From ae4aa90bb2e4f2979e97482446b1caa8bef04d06 Mon Sep 17 00:00:00 2001 From: A <258483684+la14-1@users.noreply.github.com> Date: Wed, 18 Feb 2026 10:22:33 -0800 Subject: [PATCH] =?UTF-8?q?fix:=20gh=20CLI=20setup=20on=20remote=20VMs=20?= =?UTF-8?q?=E2=80=94=20pass=20local=20token=20through=20(#1444)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes GitHub CLI authentication on remote VMs by passing local token through to remote installation script. Uses printf '%q' for safe shell escaping to prevent command injection. --- cli/src/__tests__/shared-github-auth.test.ts | 2 +- shared/common.sh | 33 ++++++++++++++++++-- shared/github-auth.sh | 13 +++++--- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/cli/src/__tests__/shared-github-auth.test.ts b/cli/src/__tests__/shared-github-auth.test.ts index 2fb62526..f6b59d48 100644 --- a/cli/src/__tests__/shared-github-auth.test.ts +++ b/cli/src/__tests__/shared-github-auth.test.ts @@ -500,7 +500,7 @@ describe("ensure_gh_auth", () => { ensure_gh_auth 2>&1 `); expect(result.exitCode).not.toBe(0); - expect(result.stdout + result.stderr).toContain("Failed to authenticate"); + expect(result.stdout + result.stderr).toContain("authentication failed"); }); it("should fail when post-login auth status check fails", () => { diff --git a/shared/common.sh b/shared/common.sh index 50c4f94a..83a8ba2e 100644 --- a/shared/common.sh +++ b/shared/common.sh @@ -1233,6 +1233,13 @@ prompt_github_auth() { choice=$(safe_read "Set up GitHub CLI (gh) on this machine? (y/N): ") || return 0 if [[ "${choice}" =~ ^[Yy]$ ]]; then SPAWN_GITHUB_AUTH_REQUESTED=1 + + # Capture local GitHub token for passthrough to remote VM + if [[ -n "${GITHUB_TOKEN:-}" ]]; then + SPAWN_GITHUB_TOKEN="${GITHUB_TOKEN}" + elif command -v gh &>/dev/null && gh auth status &>/dev/null 2>&1; then + SPAWN_GITHUB_TOKEN="$(gh auth token 2>/dev/null)" || true + fi fi } @@ -1248,11 +1255,19 @@ offer_github_auth() { return 0 fi + # Build the remote command with optional token export + local gh_cmd="curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/shared/github-auth.sh | bash" + if [[ -n "${SPAWN_GITHUB_TOKEN:-}" ]]; then + local escaped_token + escaped_token=$(printf '%q' "${SPAWN_GITHUB_TOKEN}") + gh_cmd="export GITHUB_TOKEN=${escaped_token}; ${gh_cmd}" + fi + # If prompt_github_auth was already called, use its stored answer if [[ "${SPAWN_GITHUB_AUTH_PROMPTED:-}" == "1" ]]; then if [[ "${SPAWN_GITHUB_AUTH_REQUESTED:-}" == "1" ]]; then log_step "Installing and authenticating GitHub CLI..." - ${run_callback} "curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/shared/github-auth.sh | bash" + ${run_callback} "${gh_cmd}" fi return 0 fi @@ -1265,8 +1280,22 @@ offer_github_auth() { return 0 fi + # Attempt token capture in fallback path too + if [[ -z "${SPAWN_GITHUB_TOKEN:-}" ]]; then + if [[ -n "${GITHUB_TOKEN:-}" ]]; then + SPAWN_GITHUB_TOKEN="${GITHUB_TOKEN}" + elif command -v gh &>/dev/null && gh auth status &>/dev/null 2>&1; then + SPAWN_GITHUB_TOKEN="$(gh auth token 2>/dev/null)" || true + fi + if [[ -n "${SPAWN_GITHUB_TOKEN:-}" ]]; then + local escaped_token + escaped_token=$(printf '%q' "${SPAWN_GITHUB_TOKEN}") + gh_cmd="export GITHUB_TOKEN=${escaped_token}; ${gh_cmd}" + fi + fi + log_step "Installing and authenticating GitHub CLI..." - ${run_callback} "curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/shared/github-auth.sh | bash" + ${run_callback} "${gh_cmd}" } # ============================================================ diff --git a/shared/github-auth.sh b/shared/github-auth.sh index a730ffe4..f6dba01c 100755 --- a/shared/github-auth.sh +++ b/shared/github-auth.sh @@ -222,10 +222,12 @@ ensure_gh_auth() { return 1 } else - # Interactive: browser-based OAuth flow - log_step "Initiating GitHub CLI authentication..." - gh auth login || { - log_error "Failed to authenticate with GitHub CLI" + # Device code flow — works on headless/remote servers + # Shows a URL + code; user opens URL in local browser and enters the code + log_step "Authenticating via device code flow..." + log_info "A URL and code will appear below. Open the URL in your browser and enter the code." + gh auth login --web -p https -h github.com || { + log_error "GitHub authentication failed" log_error "Run manually: gh auth login" return 1 } @@ -254,7 +256,8 @@ ensure_github_auth() { # ============================================================ # If executed directly (not sourced), run ensure_github_auth -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then +# When piped via curl|bash, BASH_SOURCE[0] is empty and $0 is "bash" +if [[ "${BASH_SOURCE[0]}" == "${0}" ]] || [[ -z "${BASH_SOURCE[0]:-}" ]]; then set -eo pipefail ensure_github_auth fi