Created
February 24, 2026 17:23
-
-
Save hiway/d350399d78bd82153095476db6f2a4ab to your computer and use it in GitHub Desktop.
Revisions
-
hiway created this gist
Feb 24, 2026 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,198 @@ #!/bin/sh # shellcheck shell=sh set -eu # Why: non-interactive SSH sessions may have a limited PATH (missing /usr/sbin # or /usr/local/bin), which would make `pkg`/`npm` look unavailable. PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PATH:-}" export PATH # This script makes GitHub Copilot CLI usable on FreeBSD ARM64 hosts. # # Why this exists: # - Copilot CLI currently ships prebuilt `pty.node` binaries for common OS/arch pairs. # - FreeBSD ARM64 is not bundled, so Copilot may fail at runtime with: # "Cannot find module './prebuilds/freebsd-arm64/pty.node'" # - We fix this by building `node-pty` locally and placing its binary where # Copilot expects it. # # Idempotency goals: # - Safe to run multiple times. # - Installs only missing packages. # - Skips rebuild when Copilot already runs unless FORCE_REBUILD=1. COPILOT_NPM_SPEC="${COPILOT_NPM_SPEC:-@github/copilot}" FORCE_REBUILD="${FORCE_REBUILD:-0}" USE_PRERELEASE=0 log() { printf '[copilot-setup] %s\n' "$*" } fail() { printf '[copilot-setup] ERROR: %s\n' "$*" >&2 exit 1 } usage() { cat <<'EOF' Usage: setup-copilot-cli-freebsd-arm64.sh [--prerelease] [--help] Options: --prerelease Install @github/copilot prerelease when Copilot is not installed --help Show this help Environment: FORCE_REBUILD=1 Force rebuilding pty.node workaround COPILOT_NPM_SPEC Override npm package spec (default: @github/copilot) EOF } parse_args() { while [ "$#" -gt 0 ]; do case "$1" in --prerelease) USE_PRERELEASE=1 ;; --help|-h) usage exit 0 ;; *) fail "Unknown argument: $1 (use --help)" ;; esac shift done if [ "$USE_PRERELEASE" = "1" ]; then COPILOT_NPM_SPEC="@github/copilot@prerelease" fi } need_cmd() { command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1" } # Runs command as root when needed. # Why: FreeBSD hosts may use either `doas` or `sudo`. as_root() { if [ "$(id -u)" -eq 0 ]; then "$@" elif command -v doas >/dev/null 2>&1; then doas "$@" elif command -v sudo >/dev/null 2>&1; then sudo "$@" else fail "Need root privileges but neither doas nor sudo is available" fi } install_pkg_if_missing() { # Why: `pkg install` is noisy and unnecessary when package already exists. # This check keeps the script quick and repeatable. pkg_name="$1" if pkg info -e "$pkg_name" >/dev/null 2>&1; then log "Package already installed: $pkg_name" else log "Installing missing package: $pkg_name" as_root pkg install -y "$pkg_name" fi } ensure_copilot_installed() { # Why: some hosts may not have Copilot installed globally yet. if command -v copilot >/dev/null 2>&1; then log "Copilot CLI already present: $(command -v copilot)" else log "Installing Copilot CLI globally via npm ($COPILOT_NPM_SPEC)" as_root npm install -g "$COPILOT_NPM_SPEC" fi } copilot_works() { copilot --version >/dev/null 2>&1 } rebuild_freebsd_arm64_pty() { npm_root="" copilot_dir="" expected_bin="" tmp_dir="" npm_root="$(npm root -g)" copilot_dir="$npm_root/@github/copilot" expected_bin="$copilot_dir/prebuilds/freebsd-arm64/pty.node" [ -d "$copilot_dir" ] || fail "Copilot directory not found at: $copilot_dir" if [ "$FORCE_REBUILD" != "1" ] && [ -f "$expected_bin" ] && copilot_works; then log "FreeBSD ARM64 pty binary already present and Copilot works; skipping rebuild" return 0 fi log "Building node-pty from source for FreeBSD ARM64" tmp_dir="$(mktemp -d)" # Why temporary directory: avoids polluting current repo/host directories. ( cd "$tmp_dir" npm init -y >/dev/null 2>&1 # Why environment variable: asks npm/node-gyp to compile native module locally. # This avoids depending on unavailable prebuilt binaries for FreeBSD ARM64. npm_config_build_from_source=true npm install node-pty [ -f "node_modules/node-pty/build/Release/pty.node" ] || fail "Build succeeded but pty.node not found" as_root mkdir -p "$(dirname "$expected_bin")" as_root cp "node_modules/node-pty/build/Release/pty.node" "$expected_bin" ) rm -rf "$tmp_dir" log "Installed rebuilt pty.node to: $expected_bin" } main() { parse_args "$@" need_cmd uname need_cmd pkg need_cmd npm os="" arch="" os="$(uname -s | tr '[:upper:]' '[:lower:]')" arch="$(uname -m | tr '[:upper:]' '[:lower:]')" log "Detected platform: ${os}/${arch}" install_pkg_if_missing gmake install_pkg_if_missing python3 install_pkg_if_missing npm ensure_copilot_installed # Fast path: if Copilot works already and no forced rebuild requested, exit quickly. if [ "$FORCE_REBUILD" != "1" ] && copilot_works; then log "Copilot CLI already functional; nothing to do" copilot --version return 0 fi # Only apply the pty workaround on the platform that needs it. if [ "$os" = "freebsd" ] && [ "$arch" = "arm64" ]; then rebuild_freebsd_arm64_pty else log "Not freebsd/arm64; skipping node-pty workaround" fi if copilot_works; then log "Copilot CLI is now functional" copilot --version else fail "Copilot still fails after setup. Run with FORCE_REBUILD=1 and inspect output." fi } main "$@"