| 1 | #!/usr/bin/env bash |
| 2 | # Installs the Diffinery extension into the editors built on VS Code on this computer (Linux and macOS): |
| 3 | # |
| 4 | # curl -fsSL https://diffinery.com/install-editor | bash # every editor found |
| 5 | # curl -fsSL https://diffinery.com/install-editor | bash -s -- cursor # only the ones named |
| 6 | # |
| 7 | # Names: code, code-insiders, cursor, windsurf, codium. Each editor's command-line tool is used from |
| 8 | # PATH, or from its usual place in /Applications on macOS. The .vsix comes from the server that served |
| 9 | # this script and is checked against the SHA-256 it publishes. The extension updates itself afterwards. |
| 10 | # Served by Diffinery from apps/vscode/install/. |
| 11 | if [ -z "${BASH_VERSION:-}" ]; then |
| 12 | echo "This installer needs bash: curl -fsSL __DIFFINERY_URL__/install-editor | bash" >&2 |
| 13 | exit 1 |
| 14 | fi |
| 15 | set -euo pipefail |
| 16 | |
| 17 | server=${DIFFINERY_URL:-__DIFFINERY_URL__} |
| 18 | case $server in __*) server=https://diffinery.com ;; esac |
| 19 | server=${server%/} |
| 20 | |
| 21 | if [ -t 2 ] && [ -z "${NO_COLOR:-}" ]; then |
| 22 | bold=$'\033[1m' dim=$'\033[2m' green=$'\033[32m' red=$'\033[31m' reset=$'\033[0m' |
| 23 | else |
| 24 | bold='' dim='' green='' red='' reset='' |
| 25 | fi |
| 26 | say() { printf '%s\n' "$*" >&2; } |
| 27 | die() { |
| 28 | say "${red}error${reset}: $*" |
| 29 | exit 1 |
| 30 | } |
| 31 | command -v curl >/dev/null || die "curl is required" |
| 32 | |
| 33 | # name|label|macOS app bundle |
| 34 | known="code|VS Code|Visual Studio Code |
| 35 | code-insiders|VS Code Insiders|Visual Studio Code - Insiders |
| 36 | cursor|Cursor|Cursor |
| 37 | windsurf|Windsurf|Windsurf |
| 38 | codium|VSCodium|VSCodium" |
| 39 | |
| 40 | find_cli() { |
| 41 | local name=$1 app=$2 |
| 42 | if command -v "$name" >/dev/null 2>&1; then |
| 43 | command -v "$name" |
| 44 | return |
| 45 | fi |
| 46 | for base in "/Applications" "$HOME/Applications"; do |
| 47 | local p="$base/$app.app/Contents/Resources/app/bin/$name" |
| 48 | if [ -x "$p" ]; then |
| 49 | printf '%s\n' "$p" |
| 50 | return |
| 51 | fi |
| 52 | done |
| 53 | return 1 |
| 54 | } |
| 55 | |
| 56 | wanted=" $* " |
| 57 | tmp=$(mktemp -d) |
| 58 | trap 'rm -rf "$tmp"' EXIT |
| 59 | meta=$(curl -fsSL "$server/-/vscode/version.json") || die "couldn't reach $server" |
| 60 | version=$(printf '%s' "$meta" | sed -n 's/.*"version":"\([^"]*\)".*/\1/p') |
| 61 | sha=$(printf '%s' "$meta" | sed -n 's/.*"sha256":"\([0-9a-f]*\)".*/\1/p') |
| 62 | [ -n "$version" ] && [ -n "$sha" ] || die "unexpected answer from $server/-/vscode/version.json" |
| 63 | vsix=$tmp/diffinery-$version.vsix |
| 64 | curl -fsSL "$server/-/vscode/diffinery-$version.vsix" -o "$vsix" || die "downloading the extension failed" |
| 65 | if command -v sha256sum >/dev/null; then got=$(sha256sum "$vsix" | cut -d' ' -f1); else got=$(shasum -a 256 "$vsix" | cut -d' ' -f1); fi |
| 66 | [ "$got" = "$sha" ] || die "the download didn't match the published SHA-256; nothing was installed" |
| 67 | |
| 68 | installed=0 |
| 69 | while IFS='|' read -r name label app; do |
| 70 | if [ "$wanted" != " " ] && [[ $wanted != *" $name "* ]]; then continue; fi |
| 71 | cli=$(find_cli "$name" "$app") || { |
| 72 | [ "$wanted" != " " ] && say "${red}✗${reset} $label: its command-line tool ($name) wasn't found" |
| 73 | continue |
| 74 | } |
| 75 | if "$cli" --install-extension "$vsix" --force >/dev/null 2>"$tmp/err"; then |
| 76 | say "${green}✓${reset} $label: Diffinery extension $version installed" |
| 77 | installed=$((installed + 1)) |
| 78 | else |
| 79 | say "${red}✗${reset} $label: $(tail -n 3 "$tmp/err")" |
| 80 | fi |
| 81 | done <<<"$known" |
| 82 | |
| 83 | if [ "$installed" = 0 ]; then |
| 84 | say "No editor took the extension. Install it from the editor instead: Extensions → … → Install from VSIX…" |
| 85 | say "with ${bold}$server/-/vscode/diffinery.vsix${reset}, or see $server/docs/repositories/clone-in-your-editor" |
| 86 | exit 1 |
| 87 | fi |
| 88 | say "" |
| 89 | say "Next: in the editor, run ${bold}Git: Clone${reset} and pick ${bold}Clone from Diffinery${reset}. ${dim}($server/docs/repositories/clone-in-your-editor)${reset}" |