159 lines
3.8 KiB
Bash
159 lines
3.8 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
EXT_FILE="${SCRIPT_DIR}/extensions.txt"
|
|
TARGET="remote"
|
|
FORCE=0
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: install-vscode-extensions.sh [options]
|
|
|
|
Install VS Code extensions from extensions.txt.
|
|
|
|
Options:
|
|
--local Install to Windows VS Code only
|
|
--remote Install to WSL remote VS Code (default)
|
|
--both Install to both local Windows and WSL remote
|
|
--list Print installed extension IDs (remote by default)
|
|
--export Write installed IDs to extensions.txt
|
|
--file PATH Use a different extension list file
|
|
--force Reinstall even if already present
|
|
-h, --help Show this help
|
|
|
|
Examples:
|
|
bash install-vscode-extensions.sh
|
|
bash install-vscode-extensions.sh --both
|
|
bash install-vscode-extensions.sh --list
|
|
bash install-vscode-extensions.sh --export
|
|
EOF
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--local) TARGET="local" ;;
|
|
--remote) TARGET="remote" ;;
|
|
--both) TARGET="both" ;;
|
|
--list) TARGET="list" ;;
|
|
--export) TARGET="export" ;;
|
|
--file) shift; EXT_FILE="${1:?missing path for --file}" ;;
|
|
--force) FORCE=1 ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
find_code_cli() {
|
|
if command -v code >/dev/null 2>&1; then
|
|
command -v code
|
|
return 0
|
|
fi
|
|
local candidate
|
|
for candidate in /mnt/c/Users/*/AppData/Local/Programs/Microsoft\ VS\ Code/bin/code; do
|
|
if [[ -f "$candidate" ]]; then
|
|
echo "$candidate"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
remote_args() {
|
|
if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then
|
|
printf '%s\0%s\0' --remote "wsl+${WSL_DISTRO_NAME,,}"
|
|
fi
|
|
}
|
|
|
|
run_code() {
|
|
"$CODE_CLI" "$@"
|
|
}
|
|
|
|
list_extensions() {
|
|
local scope=$1
|
|
local -a args=(--list-extensions)
|
|
if [[ "$scope" == "remote" ]]; then
|
|
local -a remote=()
|
|
while IFS= read -r -d '' arg; do
|
|
remote+=("$arg")
|
|
done < <(remote_args)
|
|
if [[ ${#remote[@]} -eq 0 ]]; then
|
|
echo "No WSL distro detected for remote listing." >&2
|
|
return 1
|
|
fi
|
|
args+=("${remote[@]}")
|
|
fi
|
|
run_code "${args[@]}"
|
|
}
|
|
|
|
install_extensions() {
|
|
local scope=$1
|
|
local -a remote=()
|
|
if [[ "$scope" == "remote" ]]; then
|
|
while IFS= read -r -d '' arg; do
|
|
remote+=("$arg")
|
|
done < <(remote_args)
|
|
if [[ ${#remote[@]} -eq 0 ]]; then
|
|
echo "No WSL distro detected; skipping remote install." >&2
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
local line id installed=0 skipped=0 failed=0
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
line="${line%%#*}"
|
|
line="${line//[[:space:]]/}"
|
|
[[ -z "$line" ]] && continue
|
|
id="$line"
|
|
|
|
if [[ "$FORCE" -eq 0 ]] && list_extensions "$scope" | grep -Fxq "$id"; then
|
|
echo "skip [$scope] $id"
|
|
skipped=$((skipped + 1))
|
|
continue
|
|
fi
|
|
|
|
echo "install [$scope] $id"
|
|
if [[ "$scope" == "remote" ]]; then
|
|
if run_code --install-extension "$id" "${remote[@]}" --force; then
|
|
installed=$((installed + 1))
|
|
else
|
|
echo "failed [$scope] $id" >&2
|
|
failed=$((failed + 1))
|
|
fi
|
|
elif run_code --install-extension "$id" --force; then
|
|
installed=$((installed + 1))
|
|
else
|
|
echo "failed [$scope] $id" >&2
|
|
failed=$((failed + 1))
|
|
fi
|
|
done < "$EXT_FILE"
|
|
|
|
echo "done [$scope]: installed=$installed skipped=$skipped failed=$failed"
|
|
[[ "$failed" -eq 0 ]]
|
|
}
|
|
|
|
CODE_CLI=$(find_code_cli) || {
|
|
echo "VS Code CLI not found. Install VS Code and ensure 'code' is on PATH." >&2
|
|
exit 1
|
|
}
|
|
|
|
case "$TARGET" in
|
|
list)
|
|
list_extensions remote
|
|
;;
|
|
export)
|
|
list_extensions remote > "$EXT_FILE"
|
|
echo "Wrote $(wc -l < "$EXT_FILE" | tr -d ' ') extensions to $EXT_FILE"
|
|
;;
|
|
local)
|
|
install_extensions local
|
|
;;
|
|
remote)
|
|
install_extensions remote
|
|
;;
|
|
both)
|
|
install_extensions local
|
|
install_extensions remote
|
|
;;
|
|
esac |