update vsext workflow
This commit is contained in:
parent
3f302c5ef3
commit
5a0b9f0457
5 changed files with 215 additions and 267 deletions
155
home/dot_vscode/executable_vsext
Normal file
155
home/dot_vscode/executable_vsext
Normal file
|
@ -0,0 +1,155 @@
|
|||
#!/usr/bin/env -S viv run rich 'git+https://github.com/usu-dev/usu-py.git' -s
|
||||
|
||||
import argparse
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import usu
|
||||
from rich.align import Align
|
||||
from rich.columns import Columns
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.prompt import Confirm
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def run(cmd, capture=True, returncode=False):
|
||||
result = subprocess.run(
|
||||
shlex.split(cmd),
|
||||
stdout=subprocess.PIPE if capture else None,
|
||||
text=True,
|
||||
shell=False,
|
||||
)
|
||||
|
||||
if returncode:
|
||||
return result.returncode
|
||||
elif capture:
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def parse_exts(usu_file):
|
||||
exts = usu.loads(Path(usu_file).read_text())
|
||||
all = [v for ext_list in exts.values() for v in ext_list]
|
||||
return exts, sorted(all)
|
||||
|
||||
|
||||
class SubcommandHelpFormatter(argparse.RawDescriptionHelpFormatter):
|
||||
def _format_action(self, action):
|
||||
parts = super(argparse.RawDescriptionHelpFormatter, self)._format_action(action)
|
||||
if action.nargs == argparse.PARSER:
|
||||
parts = "\n".join(parts.split("\n")[1:])
|
||||
return parts
|
||||
|
||||
|
||||
def get_args():
|
||||
p = argparse.ArgumentParser(formatter_class=SubcommandHelpFormatter)
|
||||
sp = p.add_subparsers(title="commands", metavar="", dest="command", required=True)
|
||||
check = sp.add_parser(
|
||||
"check",
|
||||
help="check the extensions which have already been installed",
|
||||
aliases=["c"],
|
||||
)
|
||||
check.add_argument("--verbose", help="show more information", action="store_true")
|
||||
install = sp.add_parser(
|
||||
"install", help="install the extensions from the spec", aliases=["i"]
|
||||
)
|
||||
install.add_argument(
|
||||
"-f", "--force", help="force the installation of exts", action="store_true"
|
||||
)
|
||||
sp.add_parser("remove", help="remove all extra packages", aliases=["r"])
|
||||
|
||||
p.add_argument(
|
||||
"-s",
|
||||
"--spec",
|
||||
help="mardown spec list (default ./exts.usu)",
|
||||
default=Path.cwd() / "exts.usu",
|
||||
)
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
p.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def get_extra_exts(spec, installed):
|
||||
# sanitize lists
|
||||
spec = [ext.lower() for ext in spec]
|
||||
installed = [ext.lower() for ext in installed]
|
||||
|
||||
return [ext for ext in spec if ext not in installed], [
|
||||
ext for ext in installed if ext not in spec
|
||||
]
|
||||
|
||||
|
||||
def code(flag, exts, force=False):
|
||||
cmd = "code " + " ".join([f"{flag} {ext}" for ext in exts])
|
||||
if force:
|
||||
cmd += " --force"
|
||||
run(cmd, capture=False)
|
||||
|
||||
|
||||
def header():
|
||||
console.print(
|
||||
Panel(
|
||||
Align("[yellow]VS Code Extensions Installer", align="center"),
|
||||
style="magenta",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def installed_table(head, exts, not_installed):
|
||||
table = Table(title=head)
|
||||
table.add_column("extension")
|
||||
table.add_column("installed?")
|
||||
for ext in exts:
|
||||
installed = ext.lower() not in not_installed
|
||||
table.add_row(
|
||||
ext, "Y" if installed else "N", style = 'green' if installed else "red"
|
||||
)
|
||||
return table
|
||||
|
||||
|
||||
def main():
|
||||
args = get_args()
|
||||
header()
|
||||
|
||||
spec_sections, spec_list = parse_exts(args.spec)
|
||||
|
||||
not_installed, extra_installed = get_extra_exts(
|
||||
spec_list, run("code --list-extensions").splitlines()
|
||||
)
|
||||
|
||||
if args.command == "check" or args.command == "c":
|
||||
tables = (
|
||||
installed_table(head, exts, not_installed)
|
||||
for head, exts in spec_sections.items()
|
||||
)
|
||||
console.print(Columns(tables))
|
||||
if not_installed:
|
||||
console.print(f"[yellow]missing {len(not_installed)} extensions")
|
||||
if extra_installed:
|
||||
console.print(f"[red]{len(extra_installed)} extra extensions installed")
|
||||
if not args.verbose:
|
||||
console.print("[dim]run with --verbose to see extra extensions")
|
||||
else:
|
||||
console.print(Columns(extra_installed))
|
||||
|
||||
|
||||
elif args.command == "install" or args.command == "i":
|
||||
console.print("[bold]Installing extensions from spec list")
|
||||
code("--install-extension", spec_list, args.force)
|
||||
|
||||
elif args.command == "remove" or args.command == "r":
|
||||
console.print("[red]Removing unspecified extensions")
|
||||
console.print(Columns(extra_installed))
|
||||
if Confirm.ask(f"Remove the above {len(extra_installed)} packages?"):
|
||||
code("--uninstall-extension", extra_installed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
59
home/dot_vscode/exts.usu
Normal file
59
home/dot_vscode/exts.usu
Normal file
|
@ -0,0 +1,59 @@
|
|||
# VS Code Extensions
|
||||
|
||||
:general [
|
||||
ms-vscode-remote.remote-ssh
|
||||
ms-vscode-remote.remote-ssh-edit
|
||||
ms-azuretools.vscode-docker
|
||||
ms-vscode-remote.remote-containers
|
||||
ms-vscode.remote-repositories
|
||||
vscodevim.vim
|
||||
]
|
||||
|
||||
:misc-tools [
|
||||
janisdd.vscode-edit-csv
|
||||
naumovs.color-highlight
|
||||
davidhouchin.whitespace-plus
|
||||
antfu.slidev
|
||||
antfu.file-nesting
|
||||
mhutchie.git-graph
|
||||
]
|
||||
|
||||
:web-dev [
|
||||
ritwickdey.liveserver
|
||||
esbenp.prettier-vscode
|
||||
astro-build.astro-vscode
|
||||
]
|
||||
|
||||
:ui [
|
||||
johnpapa.vscode-peacock
|
||||
PKief.material-icon-theme
|
||||
catppuccin.catppuccin-vsc
|
||||
catppuccin.catppuccin-vsc-icons
|
||||
]
|
||||
|
||||
:editing [
|
||||
metaseed.metajump
|
||||
christian-kohler.path-intellisense
|
||||
aaron-bond.better-comments
|
||||
streetsidesoftware.code-spell-checker
|
||||
]
|
||||
|
||||
:python [
|
||||
ms-python.python
|
||||
ms-toolsai.jupyter
|
||||
quarto.quarto
|
||||
charliermarsh.ruff
|
||||
]
|
||||
|
||||
:languages [
|
||||
golang.go
|
||||
bbenoist.nix
|
||||
antfu.unocss
|
||||
budparr.language-hugo-vscode
|
||||
snakemake.snakemake-lang
|
||||
eww-yuck.yuck
|
||||
tamasfe.even-better-toml
|
||||
dlasagno.rasi
|
||||
nimsaem.nimvscode
|
||||
]
|
||||
|
2
todo.md
2
todo.md
|
@ -3,4 +3,4 @@
|
|||
<!-- i.e. something like chezmoi forget --interactive ~/$(chezmoi managed | fzf) -->
|
||||
- [ ] make a command to 'forget' files using fzf as selector
|
||||
|
||||
- [ ] deal with the abomination that is the ./vscode directory
|
||||
- [x] deal with the abomination that is the ./vscode directory
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
# VS Code Extensions
|
||||
|
||||
## general
|
||||
ms-vscode-remote.remote-ssh
|
||||
ms-vscode-remote.remote-ssh-edit
|
||||
ms-vscode-remote.remote-wsl
|
||||
ms-azuretools.vscode-docker
|
||||
ms-vscode-remote.remote-containers
|
||||
ms-vscode.remote-repositories
|
||||
vscodevim.vim
|
||||
|
||||
## misc-tools
|
||||
Shan.code-settings-sync
|
||||
janisdd.vscode-edit-csv
|
||||
naumovs.color-highlight
|
||||
davidhouchin.whitespace-plus
|
||||
dlasagno.rasi
|
||||
antfu.slidev
|
||||
antfu.file-nesting
|
||||
|
||||
## web-dev
|
||||
bradlc.vscode-tailwindcss
|
||||
ritwickdey.liveserver
|
||||
esbenp.prettier-vscode
|
||||
antfu.unocss
|
||||
astro-build.astro-vscode
|
||||
|
||||
## styling
|
||||
johnpapa.vscode-peacock
|
||||
PKief.material-icon-theme
|
||||
Thomaz.preparing
|
||||
catppuccin.catppuccin-vsc
|
||||
|
||||
## code-help
|
||||
metaseed.metajump
|
||||
christian-kohler.path-intellisense
|
||||
aaron-bond.better-comments
|
||||
streetsidesoftware.code-spell-checker
|
||||
eamodio.gitlens
|
||||
vsls-contrib.gistfs
|
||||
mhutchie.git-graph
|
||||
|
||||
## python
|
||||
ms-python.python
|
||||
ms-python.flake8
|
||||
ms-toolsai.jupyter
|
||||
ms-python.vscode-pylance
|
||||
ms-toolsai.jupyter-keymap
|
||||
ms-toolsai.jupyter-renderers
|
||||
njpwerner.autodocstring
|
||||
quarto.quarto
|
||||
|
||||
## language-support
|
||||
budparr.language-hugo-vscode
|
||||
skellock.just
|
||||
snakemake.snakemake-lang
|
||||
voorjaar.windicss-intellisense
|
||||
eww-yuck.yuck
|
||||
bungcip.better-toml
|
||||
|
206
vscode/vsext
206
vscode/vsext
|
@ -1,206 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from shutil import which
|
||||
|
||||
|
||||
def is_tool(name):
|
||||
"""Check whether `name` is on PATH and marked as executable."""
|
||||
if not which(name):
|
||||
print(f"ERROR: {name} is not found in your PATH")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def run(cmd, capture=True, returncode=False):
|
||||
result = subprocess.run(
|
||||
shlex.split(cmd),
|
||||
stdout=subprocess.PIPE if capture else None,
|
||||
text=True,
|
||||
shell=False,
|
||||
)
|
||||
|
||||
if returncode:
|
||||
return result.returncode
|
||||
elif capture:
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
class Gum:
|
||||
def __init__(self):
|
||||
is_tool("gum")
|
||||
|
||||
def confirm(self, question):
|
||||
return not run(
|
||||
"gum confirm "
|
||||
"--selected.bold "
|
||||
"--selected.background 11 "
|
||||
"--selected.foreground 8 "
|
||||
f"'{question}'",
|
||||
returncode=True,
|
||||
)
|
||||
|
||||
def color(self, text, color):
|
||||
return run(f'gum style --foreground {color} "{text}"')
|
||||
|
||||
def header(self, text, color=11):
|
||||
run(
|
||||
"gum style "
|
||||
"--foreground 212 --border-foreground 212 --border rounded "
|
||||
'--align center --width 30 --margin "1 4" '
|
||||
f"'{self.color(text,color)}'",
|
||||
capture=False,
|
||||
)
|
||||
|
||||
def choose(self, options):
|
||||
if isinstance(options, str):
|
||||
options = options.split("")
|
||||
if isinstance(options, list):
|
||||
options = "".join([f"'{i}'" for i in options])
|
||||
return run(f"gum choose {options}")
|
||||
|
||||
def input(self, placeholder, value):
|
||||
run(
|
||||
"gum input" + f"--placeholder {placeholder}"
|
||||
if placeholder
|
||||
else "" + f"--value {value}"
|
||||
if value
|
||||
else ""
|
||||
)
|
||||
|
||||
def spinner(self, placeholder, cmd):
|
||||
run(f"gum spin -s points --title '{placeholder}'" + cmd)
|
||||
|
||||
|
||||
gum = Gum()
|
||||
|
||||
|
||||
def pprint(text, color):
|
||||
print(gum.color(text, color))
|
||||
|
||||
|
||||
def parse_exts(mdfile):
|
||||
|
||||
with Path(mdfile).open("r") as f:
|
||||
md = f.read()
|
||||
md = md.split("##")[1:]
|
||||
exts = {}
|
||||
all = []
|
||||
for section in md:
|
||||
head = section.splitlines()[0].strip()
|
||||
exts[head] = []
|
||||
for line in section.splitlines()[1:]:
|
||||
if line == "":
|
||||
continue
|
||||
exts[head].append(line.strip())
|
||||
all.append(line.strip())
|
||||
|
||||
return exts, sorted(all)
|
||||
|
||||
|
||||
class SubcommandHelpFormatter(argparse.RawDescriptionHelpFormatter):
|
||||
def _format_action(self, action):
|
||||
parts = super(argparse.RawDescriptionHelpFormatter, self)._format_action(action)
|
||||
if action.nargs == argparse.PARSER:
|
||||
parts = "\n".join(parts.split("\n")[1:])
|
||||
return parts
|
||||
|
||||
|
||||
def get_args():
|
||||
|
||||
p = argparse.ArgumentParser(formatter_class=SubcommandHelpFormatter)
|
||||
sp = p.add_subparsers(title="commands", metavar="", dest="command",required=True)
|
||||
check = sp.add_parser(
|
||||
"check",
|
||||
help="check the extensions which have already been installed",
|
||||
aliases = ['c']
|
||||
)
|
||||
install = sp.add_parser(
|
||||
"install",
|
||||
help="install the extensions from the spec",
|
||||
aliases =['i']
|
||||
)
|
||||
install.add_argument(
|
||||
"-f", "--force", help="force the installation of exts", action="store_true"
|
||||
)
|
||||
remove = sp.add_parser(
|
||||
"remove",
|
||||
help="remove all extra packages",
|
||||
aliases = ['r']
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
"-s",
|
||||
"--spec",
|
||||
help="mardown spec list (default ~/.dotfiles/vscode/spec.md)",
|
||||
default=Path.home() / ".dotfiles" / "vscode" / "spec.md",
|
||||
)
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
# p.print_usage()
|
||||
p.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def get_extra_exts(spec, installed):
|
||||
# sanitize lists
|
||||
spec = [ext.lower() for ext in spec]
|
||||
installed = [ext.lower() for ext in installed]
|
||||
|
||||
return [ext for ext in spec if ext not in installed], [
|
||||
ext for ext in installed if ext not in spec
|
||||
]
|
||||
|
||||
|
||||
def code(flag, exts, force=False):
|
||||
cmd = "code " + " ".join([f"{flag} {ext}" for ext in exts])
|
||||
if force:
|
||||
cmd +=" --force"
|
||||
run(cmd, capture=False)
|
||||
|
||||
|
||||
def main():
|
||||
args = get_args()
|
||||
gum.header("VS Code Extensions Installer")
|
||||
|
||||
spec_sections, spec_list = parse_exts(args.spec)
|
||||
|
||||
not_installed, extra_installed = get_extra_exts(
|
||||
spec_list, run("code --list-extensions").splitlines()
|
||||
)
|
||||
|
||||
if args.command == "check" or args.command == "c":
|
||||
|
||||
for head, exts in spec_sections.items():
|
||||
pprint(f"## {head}", 11)
|
||||
for ext in exts:
|
||||
if ext.lower() in not_installed:
|
||||
pprint(ext, 9)
|
||||
else:
|
||||
print(ext)
|
||||
if extra_installed:
|
||||
pprint("\nExtra installed extensions:", 11)
|
||||
print("\n".join(extra_installed))
|
||||
|
||||
elif args.command == "install" or args.command =='i':
|
||||
|
||||
pprint("Installing extensions from spec list", 11)
|
||||
code("--install-extension", spec_list, args.force)
|
||||
|
||||
elif args.command == "remove" or args.command == "r":
|
||||
pprint("Removing extensions I don't know about", 11)
|
||||
|
||||
print("\n".join(extra_installed) + "\n")
|
||||
|
||||
if gum.confirm(f"Remove the above {len(extra_installed)} packages"):
|
||||
print("bye bye packages")
|
||||
code("--uninstall-extension", extra_installed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue