diff --git a/shared/github-auth.sh b/shared/github-auth.sh index 8f9e3c78..ad3c5637 100755 --- a/shared/github-auth.sh +++ b/shared/github-auth.sh @@ -213,20 +213,45 @@ _install_gh_binary() { # ============================================================ ensure_gh_auth() { - if gh auth status &>/dev/null; then - log_info "Authenticated with GitHub CLI" - return 0 - fi - - log_step "Not authenticated with GitHub CLI" - - # Non-interactive: use GITHUB_TOKEN if set + # When GITHUB_TOKEN is set, persist it to gh's credential store so it + # survives into the interactive session (where the env var is absent). + # NOTE: This writes the token to ~/.config/gh/hosts.yml in plaintext, + # which is standard gh CLI behavior (same as `gh auth login`). if [[ -n "${GITHUB_TOKEN:-}" ]]; then - log_step "Authenticating with GITHUB_TOKEN..." - printf '%s\n' "${GITHUB_TOKEN}" | gh auth login --with-token || { + # Validate token format: must start with a known GitHub prefix + case "${GITHUB_TOKEN}" in + ghp_*|gho_*|ghu_*|ghs_*|ghr_*|github_pat_*) + ;; + *) + log_error "GITHUB_TOKEN has unexpected format (expected ghp_, gho_, ghu_, ghs_, ghr_, or github_pat_ prefix)" + return 1 + ;; + esac + + # Fast path: skip persistence if gh is already authenticated with + # stored credentials (not just the env var). Temporarily unset + # GITHUB_TOKEN so gh auth status checks disk credentials only. + local _gh_token="${GITHUB_TOKEN}" + unset GITHUB_TOKEN + if gh auth status &>/dev/null; then + export GITHUB_TOKEN="${_gh_token}" + log_info "Authenticated with GitHub CLI (credentials already persisted)" + return 0 + fi + + log_step "Persisting GITHUB_TOKEN to gh credential store..." + # GITHUB_TOKEN is already unset above so gh auth login won't refuse + # with "The value of the GITHUB_TOKEN environment variable is being + # used for authentication." + printf '%s\n' "${_gh_token}" | gh auth login --with-token || { log_error "Failed to authenticate with GITHUB_TOKEN" + export GITHUB_TOKEN="${_gh_token}" return 1 } + export GITHUB_TOKEN="${_gh_token}" + elif gh auth status &>/dev/null; then + log_info "Authenticated with GitHub CLI" + return 0 else # Device code flow — works on headless/remote servers # Shows a URL + code; user opens URL in local browser and enters the code