Files
nexus/vscode/package-all.sh
T
andyandClaude Opus 4.8 ab0e3a44e6 vscode: revamp terminal UX, fix sizing, add cross-platform packaging
Nexus terminal:
- Fix CSP that silently blocked the inline <style>, so the terminal now
  fills 100% of the tab; theme xterm from VSCode colour variables and use
  the editor/terminal monospace font.
- Re-fit via ResizeObserver/visibility/font-load and re-send geometry once
  the session is connected, so the remote PTY no longer stays at 80x24.
- Remove the floating SFTP icon; SFTP now opens in the user's home folder
  (retry SFTPPwd instead of falling back to /).

Both extensions:
- Polish panels to VSCode design standards (themed buttons, focus rings,
  primary actions); redraw the activity-bar logos.
- chmod +x the bridge on activation (VSIX extraction may drop the exec bit).

Packaging:
- Stop tracking the per-host bridge binary (committed a macOS arm64 build
  that broke Linux/Windows); ignore vscode/bin, vscode/dist, vscode/*/bin.
- Add package-all.sh to cross-compile the pure-Go bridge and produce one
  platform-specific VSIX per target (win32/darwin/linux x64+arm64), with an
  opt-in macOS codesign + notarization hook.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 19:56:31 +01:00

191 lines
7.4 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Build platform-specific VSIX packages for the Nexus and Leviathan VSCode
# extensions, covering Windows, macOS and Linux on both x64 and arm64.
#
# Each extension embeds a Go "bridge" binary that is OS/arch specific, so we
# produce one VSIX per target using VSCode's platform-specific extension
# mechanism (`vsce package --target <target>`). VSCode then installs the VSIX
# matching the user's platform automatically (e.g. from the Marketplace, or via
# `code --install-extension <file>.vsix`).
#
# The bridge is pure Go (CGO_ENABLED=0), so every target cross-compiles from any
# host with just the Go toolchain — no C compilers or per-OS toolchains needed.
#
# Usage:
# vscode/package-all.sh # build every target
# vscode/package-all.sh linux-x64 ... # build only the named vsce target(s)
#
# Optional macOS code signing + notarization (darwin targets only):
# Runs only on a macOS host with Xcode command line tools, and only when the
# relevant env vars are set — otherwise it is skipped with a warning and the
# darwin VSIXs are produced unsigned (fine for local use, blocked by Gatekeeper
# when distributed to other machines).
#
# MACOS_SIGN_IDENTITY "Developer ID Application: Name (TEAMID)" — enables signing.
#
# Then, to also notarize, provide EITHER a stored notarytool profile:
# MACOS_NOTARY_PROFILE profile name from `xcrun notarytool store-credentials`
# OR an Apple ID + app-specific password:
# MACOS_NOTARY_APPLE_ID Apple ID email
# MACOS_NOTARY_TEAM_ID Apple Developer team id
# MACOS_NOTARY_PASSWORD app-specific password
#
# Example:
# MACOS_SIGN_IDENTITY="Developer ID Application: Andy Dixon (AB12CD34EF)" \
# MACOS_NOTARY_PROFILE="nexus-notary" \
# vscode/package-all.sh darwin-x64 darwin-arm64
#
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
VSCODE_DIR="$ROOT_DIR/vscode"
OUT_DIR="$VSCODE_DIR/dist"
EXTENSIONS=(nexus leviathan)
# vsce target -> GOOS/GOARCH
ALL_TARGETS=(
"win32-x64:windows/amd64"
"win32-arm64:windows/arm64"
"darwin-x64:darwin/amd64"
"darwin-arm64:darwin/arm64"
"linux-x64:linux/amd64"
"linux-arm64:linux/arm64"
)
# Optional positional filter: only build the vsce targets passed as arguments.
SELECTED=("$@")
target_selected() {
[ ${#SELECTED[@]} -eq 0 ] && return 0
local want="$1"
for t in "${SELECTED[@]}"; do [ "$t" = "$want" ] && return 0; done
return 1
}
run_vsce() {
if command -v vsce >/dev/null 2>&1; then
vsce "$@"
else
npx --yes @vscode/vsce "$@"
fi
}
# Codesign (hardened runtime) and optionally notarize a single darwin bridge
# binary. Opt-in via env vars and skipped with a warning when it can't run, so
# the cross-platform build still succeeds on non-macOS hosts. Signing the staged
# binary before it is copied into each extension means both VSIXs inherit the
# signature (and notarization, which is keyed to the binary's hash).
macos_sign() {
local bin="$1"
if [ -z "${MACOS_SIGN_IDENTITY:-}" ]; then
echo "[vscode] (skip signing: MACOS_SIGN_IDENTITY not set — shipping unsigned)"
return 0
fi
if [ "$(uname -s)" != "Darwin" ] || ! command -v codesign >/dev/null 2>&1; then
echo "[vscode] WARNING: cannot sign darwin bridge here (needs macOS + Xcode tools); shipping unsigned"
return 0
fi
echo "[vscode] codesign ($MACOS_SIGN_IDENTITY)"
codesign --force --options runtime --timestamp --sign "$MACOS_SIGN_IDENTITY" "$bin"
codesign --verify --strict --verbose=2 "$bin"
# Notarize only when credentials are provided.
local notary_args=()
if [ -n "${MACOS_NOTARY_PROFILE:-}" ]; then
notary_args=(--keychain-profile "$MACOS_NOTARY_PROFILE")
elif [ -n "${MACOS_NOTARY_APPLE_ID:-}" ] && [ -n "${MACOS_NOTARY_TEAM_ID:-}" ] && [ -n "${MACOS_NOTARY_PASSWORD:-}" ]; then
notary_args=(--apple-id "$MACOS_NOTARY_APPLE_ID" --team-id "$MACOS_NOTARY_TEAM_ID" --password "$MACOS_NOTARY_PASSWORD")
else
echo "[vscode] (skip notarization: no notary credentials set)"
return 0
fi
if ! command -v xcrun >/dev/null 2>&1; then
echo "[vscode] WARNING: xcrun not found; cannot notarize"
return 0
fi
echo "[vscode] notarize (submitting to Apple, waiting for result…)"
local zip="${bin}.notarize.zip"
# notarytool requires an archive; a bare Mach-O binary cannot be submitted directly.
ditto -c -k --keepParent "$bin" "$zip"
xcrun notarytool submit "$zip" "${notary_args[@]}" --wait
rm -f "$zip"
# A standalone executable cannot be stapled (stapler only supports app/dmg/pkg);
# Gatekeeper verifies the notarization online via the now-registered hash, which
# matches the exact bytes embedded in every VSIX.
echo "[vscode] notarized (online check; standalone binaries are not stapleable)"
}
echo "[vscode] Building frontend assets (platform independent)"
if [ ! -d "$ROOT_DIR/frontend/node_modules" ]; then
npm --prefix "$ROOT_DIR/frontend" install
fi
npm --prefix "$ROOT_DIR/frontend" run build
echo "[vscode] Staging shared media assets"
cp "$ROOT_DIR/frontend/dist/src/js/lib/xterm.min.js" "$VSCODE_DIR/nexus/media/xterm.min.js"
cp "$ROOT_DIR/frontend/dist/src/js/lib/xterm-addon-fit.min.js" "$VSCODE_DIR/nexus/media/xterm-addon-fit.min.js"
cp "$ROOT_DIR/frontend/dist/src/css/lib/xterm.css" "$VSCODE_DIR/nexus/media/xterm.css"
cp "$ROOT_DIR/frontend/dist/src/js/lib/novnc.min.js" "$VSCODE_DIR/nexus/media/novnc.min.js"
rm -rf "$OUT_DIR"
mkdir -p "$OUT_DIR"
cd "$ROOT_DIR"
VERSION="$(node -p "require('$VSCODE_DIR/nexus/package.json').version")"
for entry in "${ALL_TARGETS[@]}"; do
VSCE_TARGET="${entry%%:*}"
GOPLATFORM="${entry##*:}"
GOOS="${GOPLATFORM%%/*}"
GOARCH="${GOPLATFORM##*/}"
if ! target_selected "$VSCE_TARGET"; then
continue
fi
BIN_NAME="nexus-vscode-bridge"
[ "$GOOS" = "windows" ] && BIN_NAME="nexus-vscode-bridge.exe"
echo "[vscode] Building bridge for $VSCE_TARGET ($GOOS/$GOARCH)"
STAGE_BIN="$VSCODE_DIR/bin/$VSCE_TARGET/$BIN_NAME"
mkdir -p "$(dirname "$STAGE_BIN")"
GOOS="$GOOS" GOARCH="$GOARCH" CGO_ENABLED=0 \
go build -trimpath -o "$STAGE_BIN" ./cmd/vscode-bridge
# Sign + notarize macOS bridges before they are copied into the extensions.
if [ "$GOOS" = "darwin" ]; then
macos_sign "$STAGE_BIN"
fi
for EXT in "${EXTENSIONS[@]}"; do
EXT_BIN_DIR="$VSCODE_DIR/$EXT/bin"
# Ship exactly one binary per VSIX so the package can't carry a wrong-arch
# (or wrong-OS) bridge alongside the right one.
rm -rf "$EXT_BIN_DIR"
mkdir -p "$EXT_BIN_DIR"
cp "$STAGE_BIN" "$EXT_BIN_DIR/$BIN_NAME"
chmod 0755 "$EXT_BIN_DIR/$BIN_NAME"
OUT_VSIX="$OUT_DIR/$EXT-$VERSION-$VSCE_TARGET.vsix"
( cd "$VSCODE_DIR/$EXT" && run_vsce package --target "$VSCE_TARGET" \
--no-dependencies --allow-missing-repository -o "$OUT_VSIX" >/dev/null )
echo "[vscode] wrote $(basename "$OUT_VSIX")"
done
done
# Leave the working tree with a host-native bridge staged so a plain
# `code --install-extension` of an unpacked extension still works locally.
HOST_OS="$(go env GOOS)"; HOST_ARCH="$(go env GOARCH)"
HOST_BIN="nexus-vscode-bridge"; [ "$HOST_OS" = "windows" ] && HOST_BIN="nexus-vscode-bridge.exe"
for EXT in "${EXTENSIONS[@]}"; do
rm -rf "$VSCODE_DIR/$EXT/bin"; mkdir -p "$VSCODE_DIR/$EXT/bin"
GOOS="$HOST_OS" GOARCH="$HOST_ARCH" CGO_ENABLED=0 \
go build -trimpath -o "$VSCODE_DIR/$EXT/bin/$HOST_BIN" ./cmd/vscode-bridge
done
echo "[vscode] Done. Packages in $OUT_DIR:"
ls -1 "$OUT_DIR"