diff --git a/internal/scaffold/fullsend-repo/scripts/pre-retro-test.sh b/internal/scaffold/fullsend-repo/scripts/pre-retro-test.sh new file mode 100644 index 000000000..b70b37157 --- /dev/null +++ b/internal/scaffold/fullsend-repo/scripts/pre-retro-test.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# pre-retro-test.sh — Test pre-retro.sh with mock gh to verify token validation. +# +# Uses a mock gh command to capture calls without hitting GitHub. +# Run from the repo root: bash internal/scaffold/fullsend-repo/scripts/pre-retro-test.sh + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PRE_SCRIPT="${SCRIPT_DIR}/pre-retro.sh" +FAILURES=0 + +# Create a temp directory for mock state. +TMPDIR="$(mktemp -d)" +trap 'rm -rf "${TMPDIR}"' EXIT + +# --- Helpers --- + +# build_mock creates a mock gh binary that returns a preconfigured exit code +# for "gh auth status" calls. +# Arguments: +# $1 — exit code for "gh auth status" (0 = valid token, 1 = invalid token). +build_mock() { + local auth_exit="$1" + local mock_bin="${TMPDIR}/bin" + + rm -rf "${mock_bin}" + mkdir -p "${mock_bin}" + + cat > "${mock_bin}/gh" < "${TMPDIR}/stdout.log" 2>&1 || exit_code=$? + + if [[ ${exit_code} -ne ${expect_exit} ]]; then + echo "FAIL: ${test_name} — expected exit ${expect_exit}, got ${exit_code}" + cat "${TMPDIR}/stdout.log" + FAILURES=$((FAILURES + 1)) + return + fi + + if ! grep -qF "${expected_stdout}" "${TMPDIR}/stdout.log" 2>/dev/null; then + echo "FAIL: ${test_name} — expected stdout '${expected_stdout}' not found" + echo "Actual stdout:" + cat "${TMPDIR}/stdout.log" + FAILURES=$((FAILURES + 1)) + return + fi + + echo "PASS: ${test_name}" +} + +# --- Test cases --- + +# Valid token → auth status exits 0 → script succeeds. +run_test_stdout "valid-token-succeeds" \ + 0 \ + "GH_TOKEN validated successfully." \ + 0 \ + "GH_TOKEN=fake-token" + +# Invalid token → auth status exits 1 → script fails with error annotation. +run_test_stdout "invalid-token-fails" \ + 1 \ + "::error::GH_TOKEN is invalid" \ + 1 \ + "GH_TOKEN=bad-token" + +# Absent token → script fails with error annotation (no sandbox wasted). +run_test_stdout "absent-token-fails" \ + 0 \ + "::error::GH_TOKEN is not set" \ + 1 + +# Valid token with retro comment → script succeeds with on-demand message. +run_test_stdout "retro-comment-on-demand" \ + 0 \ + "Retro triggered on-demand with comment." \ + 0 \ + "$(printf '%s\n%s' 'GH_TOKEN=fake-token' 'RETRO_COMMENT=look at this')" + +# Valid token without retro comment → script succeeds with automatic message. +run_test_stdout "retro-automatic-trigger" \ + 0 \ + "Retro triggered automatically (PR close)." \ + 0 \ + "GH_TOKEN=fake-token" + +# --- Summary --- + +echo "" +if [[ ${FAILURES} -gt 0 ]]; then + echo "${FAILURES} test(s) failed" + exit 1 +fi +echo "All tests passed" diff --git a/internal/scaffold/fullsend-repo/scripts/pre-retro.sh b/internal/scaffold/fullsend-repo/scripts/pre-retro.sh index 9ee8b9eab..c64a902ad 100755 --- a/internal/scaffold/fullsend-repo/scripts/pre-retro.sh +++ b/internal/scaffold/fullsend-repo/scripts/pre-retro.sh @@ -6,6 +6,7 @@ # # Required env vars: # ORIGINATING_URL — HTML URL of the PR or issue that triggered retro +# GH_TOKEN — GitHub token; validated before sandbox starts # # Optional env vars: # RETRO_COMMENT — The /retro comment text (empty for automatic triggers) @@ -22,6 +23,23 @@ fi echo "::notice::Retro target: ${ORIGINATING_URL}" +# --------------------------------------------------------------------------- +# Validate GH_TOKEN before starting the sandbox +# --------------------------------------------------------------------------- +# ponytail: gh auth status validates auth, not scopes — a token with only +# contents:read would pass here but fail in post-retro.sh. Still catches the +# #256 failure mode (expired/revoked token). Scope checks if needed later. +if [[ -z "${GH_TOKEN:-}" ]]; then + echo "::error::GH_TOKEN is not set — retro agent requires GitHub API access" + exit 1 +fi +echo "::add-mask::${GH_TOKEN}" +if ! gh auth status >/dev/null 2>&1; then + echo "::error::GH_TOKEN is invalid — retro agent requires GitHub API access" + exit 1 +fi +echo "GH_TOKEN validated successfully." + if [[ -n "${RETRO_COMMENT:-}" ]]; then echo "Retro triggered on-demand with comment." else