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) -->
|
<!-- i.e. something like chezmoi forget --interactive ~/$(chezmoi managed | fzf) -->
|
||||||
- [ ] make a command to 'forget' files using fzf as selector
|
- [ ] 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