Bash install.sh and settings.sh for Linux and macOS

Twins of the PowerShell scripts, needing only bash, curl and one of jq,
node or python3 for JSON. install.sh downloads a release (or installs
from dist/), settings.sh pushes, pulls or diffs User/, both resolve the
code CLI and user folder per platform.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0169iPWwKHZoBTNN9qwXiwqk
This commit is contained in:
max
2026-09-08 19:50:07 +02:00
co-authored by Claude Fable 5.1
parent 9085266dd3
commit c058c09050
4 changed files with 451 additions and 3 deletions
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
# Installs the extensions into VS Code, from a Gitea release or from dist/.
#
# The bash twin of install.ps1, for a Linux or macOS machine without PowerShell. By
# default it downloads the .vsix assets of the "latest" release and installs them, so a
# fresh machine needs nothing but VS Code, curl, and this repo. --local installs what is
# already in dist/ instead.
#
# Every install passes --force: without it VS Code declines to reinstall a version it
# already has, which would silently do nothing every time the same version number is
# republished.
#
# Usage:
# scripts/install.sh install the "latest" release
# scripts/install.sh --tag v0.1.0 a specific release
# scripts/install.sh --local from dist/
# scripts/install.sh --only vertical-tabs --only colored-references
# scripts/install.sh --uninstall
# scripts/install.sh --token <token> otherwise GITEA_TOKEN or ~/.gitea-token
# shellcheck source=common.sh
. "$(dirname "${BASH_SOURCE[0]}")/common.sh"
tag=latest
token=""
only=""
local_install=""
uninstall=""
while [ $# -gt 0 ]; do
case "$1" in
--tag) tag="$2"; shift 2 ;;
--token) token="$2"; shift 2 ;;
--only) only="$only $2"; shift 2 ;;
--local) local_install=1; shift ;;
--uninstall) uninstall=1; shift ;;
-h|--help) sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
*) die "unknown argument: $1 (try --help)" ;;
esac
done
load_config
code="$(code_command)"
extensions="$(configured_extensions "$only")"
if [ -n "$uninstall" ]; then
while IFS=$'\t' read -r id _path _repo; do
[ -n "$id" ] || continue
# The manifest's publisher is "local", so the installed id is local.<name>.
note "uninstalling local.$id"
"$code" --uninstall-extension "local.$id" || true
done <<<"$extensions"
echo "Reload the window for this to take effect."
exit 0
fi
packages=()
staging=""
if [ -n "$local_install" ]; then
while IFS=$'\t' read -r id _path _repo; do
[ -n "$id" ] || continue
found="$(ls -1 "$setup_dist"/"$id"-*.vsix 2>/dev/null | sort -r | head -n1 || true)"
[ -n "$found" ] || die "no package for $id in $setup_dist. Run build.ps1 or ci-release.sh first."
packages+=("$found")
done <<<"$extensions"
else
require_gitea
token="$(gitea_token "$token")"
release="$(gitea_release "$tag" "$token")"
[ -n "$release" ] || die "no release tagged '$tag' in $gitea_owner/$gitea_repo. Run publish.ps1 first."
assets="$(json_rows - assets name browser_download_url <<<"$release")"
[ -n "$assets" ] || die "release '$tag' has no assets"
# Downloads go to a temp folder, not dist/, so an install never disturbs a build.
staging="$(mktemp -d -t vsix-XXXXXX)"
trap 'rm -rf "$staging"' EXIT
while IFS=$'\t' read -r id _path _repo; do
[ -n "$id" ] || continue
match="$(grep -P "^$id-.*\.vsix\t" <<<"$assets" | sort -r | head -n1 || true)"
if [ -z "$match" ]; then
warn "release '$tag' has no asset for $id, skipping"
continue
fi
name="${match%%$'\t'*}"
url="${match#*$'\t'}"
note "downloading $name"
gitea_download "$url" "$staging/$name" "$token"
packages+=("$staging/$name")
done <<<"$extensions"
fi
[ ${#packages[@]} -gt 0 ] || die "nothing to install"
failed=0
for package in "${packages[@]}"; do
printf 'installing %s' "$(basename "$package")"
if "$code" --install-extension "$package" --force >/dev/null 2>&1; then
ok ' ok'
else
printf '\033[31m FAILED\033[0m\n'
failed=$((failed + 1))
fi
done
echo
[ "$failed" -eq 0 ] || die "$failed of ${#packages[@]} failed to install"
echo "installed ${#packages[@]} extension(s). Reload the window."