Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions internal/scaffold/fullsend-repo/scripts/post-code-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,90 @@ run_signoff_test "signoff-variant-casing-passes" \
signed-off-by: bot <bot@noreply.github.com>" \
"pass"

# ---------------------------------------------------------------------------
# Test helper — reimplements the pre-commit auto-fix retry decision logic
# from post-code.sh section 5. Given a pre-commit exit code and whether
# unstaged changes exist, returns the action the script would take.
# ---------------------------------------------------------------------------
decide_precommit_retry() {
local precommit_rc="$1" # 0 = passed, 1 = failed
local has_unstaged="$2" # "yes" or "no"
local retry_precommit_rc="$3" # 0 = passed on retry, 1 = still fails (ignored if no retry)
local retry_has_unstaged="${4:-no}" # "yes" if retry left unstaged changes

if [ "${precommit_rc}" -eq 0 ]; then
echo "pass:clean"
return 0
fi

# Pre-commit failed — check for auto-fixed files
if [ "${has_unstaged}" = "yes" ]; then
if [ "${retry_precommit_rc}" -eq 0 ]; then
if [ "${retry_has_unstaged}" = "yes" ]; then
echo "blocked:retry-left-unstaged"
else
echo "pass:auto-fixed"
fi
else
echo "blocked:retry-failed"
fi
else
echo "blocked:no-auto-fix"
fi
}

run_precommit_retry_test() {
local test_name="$1"
local precommit_rc="$2"
local has_unstaged="$3"
local retry_precommit_rc="$4"
local expected="$5"
local retry_has_unstaged="${6:-no}"

local actual
actual="$(decide_precommit_retry "${precommit_rc}" "${has_unstaged}" "${retry_precommit_rc}" "${retry_has_unstaged}")"

if [ "${actual}" != "${expected}" ]; then
echo "FAIL: ${test_name}"
echo " precommit_rc: '${precommit_rc}'"
echo " has_unstaged: '${has_unstaged}'"
echo " retry_precommit_rc: '${retry_precommit_rc}'"
echo " retry_has_unstaged: '${retry_has_unstaged}'"
echo " expected: '${expected}'"
echo " actual: '${actual}'"
FAILURES=$((FAILURES + 1))
return
fi

echo "PASS: ${test_name}"
}

# --- Pre-commit auto-fix retry test cases ---

# Pre-commit passes on first run → no retry needed
run_precommit_retry_test "precommit-passes-first-run" \
"0" "no" "0" "pass:clean"

# Pre-commit fails, hooks auto-fixed files, retry succeeds
run_precommit_retry_test "precommit-auto-fix-retry-succeeds" \
"1" "yes" "0" "pass:auto-fixed"

# Pre-commit fails, hooks auto-fixed files, retry still fails
run_precommit_retry_test "precommit-auto-fix-retry-fails" \
"1" "yes" "1" "blocked:retry-failed"

# Pre-commit fails, no unstaged changes (genuine failure)
run_precommit_retry_test "precommit-genuine-failure" \
"1" "no" "0" "blocked:no-auto-fix"

# Pre-commit passes but unstaged changes exist (e.g. hook wrote a log file)
run_precommit_retry_test "precommit-passes-with-unstaged" \
"0" "yes" "0" "pass:clean"

# Pre-commit fails, auto-fix retry passes, but retry left unstaged changes
run_precommit_retry_test "precommit-retry-passes-but-left-unstaged" \
"1" "yes" "0" "blocked:retry-left-unstaged" "yes"

# --- Summary ---

echo ""
Expand Down
57 changes: 53 additions & 4 deletions internal/scaffold/fullsend-repo/scripts/post-code.sh
Original file line number Diff line number Diff line change
Expand Up @@ -306,13 +306,62 @@ if [ -f .pre-commit-config.yaml ]; then

if command -v pre-commit >/dev/null 2>&1; then
mapfile -t changed_array <<< "${CHANGED_FILES}"
# SYNC: parallel retry block in post-fix.sh section 3 — keep structure
# in sync (variable names differ: CHANGED_FILES here vs
# BRANCH_CHANGED_FILES there; SCAN_RANGE scopes differ by design).
if pre-commit run --files "${changed_array[@]}"; then
echo "Pre-commit passed — all hooks clean"
else
echo "::error::BLOCKED — pre-commit hooks failed on agent's changes" >&2
echo "::error::The agent's code does not pass the repo's pre-commit hooks." >&2
echo "::error::Fix the issues and re-run, or update the pre-commit config." >&2
exit 1
# Single retry only — do not convert to a loop without adding a cap.
# Scope detection/staging to changed_array so hooks can't inject files
# outside the pre-commit scope into the commit.
if git diff --name-only -- "${changed_array[@]}" | grep -q .; then
echo "::warning::Pre-commit hooks auto-fixed files — re-staging and retrying"
echo "Auto-fixed files:"
git diff --name-only -- "${changed_array[@]}" | sed 's/^/ /'
git diff --name-only -z -- "${changed_array[@]}" | xargs -0 -r git add --
git commit --amend --no-edit

echo "Re-running secret scan on amended commit..."
if ! gitleaks detect --source . --log-opts="${SCAN_RANGE}" --redact; then
echo "::error::BLOCKED — secret detected in amended commit after auto-fix" >&2
exit 1
fi
if git log --format='%b' "${SCAN_RANGE}" | grep -q '^Signed-off-by:'; then
echo "::error::BLOCKED — amended commit contains a Signed-off-by trailer" >&2
exit 1
fi

if [ -n "${MERGE_BASE}" ]; then
CHANGED_FILES="$(git diff --name-only "${MERGE_BASE}..HEAD")"
else
CHANGED_FILES="$(git diff --name-only "origin/${TARGET_BRANCH}..HEAD" 2>/dev/null \
|| git diff --name-only HEAD~1..HEAD 2>/dev/null || true)"
fi
if [ -z "${CHANGED_FILES}" ]; then
echo "::error::BLOCKED — pre-commit hooks removed all changes; commit is now empty" >&2
exit 1
fi
mapfile -t changed_array <<< "${CHANGED_FILES}"
if pre-commit run --files "${changed_array[@]}"; then
if git diff --name-only -- "${changed_array[@]}" | grep -q .; then
echo "::error::BLOCKED — retry pre-commit left additional unstaged changes" >&2
echo "::error::Committed content would diverge from what pre-commit validated." >&2
exit 1
fi
echo "Pre-commit passed after auto-fix re-stage"
else
echo "::error::BLOCKED — pre-commit hooks still fail after auto-fix" >&2
echo "::error::The agent's code does not pass the repo's pre-commit hooks." >&2
echo "::error::Fix the issues and re-run, or update the pre-commit config." >&2
exit 1
fi
else
echo "::error::BLOCKED — pre-commit hooks failed on agent's changes" >&2
echo "::error::The agent's code does not pass the repo's pre-commit hooks." >&2
echo "::error::Fix the issues and re-run, or update the pre-commit config." >&2
exit 1
fi
fi
else
echo "::warning::pre-commit not available on runner — skipping authoritative check"
Expand Down
84 changes: 84 additions & 0 deletions internal/scaffold/fullsend-repo/scripts/post-fix-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,90 @@ run_push_retry_test "push-rejected" \
run_push_retry_test "push-unexpected-error" \
"1" "fatal: repository not found" "fail:unexpected-error"

# ---------------------------------------------------------------------------
# Test helper — reimplements the pre-commit auto-fix retry decision logic
# from post-fix.sh section 3. Given a pre-commit exit code and whether
# unstaged changes exist, returns the action the script would take.
# ---------------------------------------------------------------------------
decide_precommit_retry() {
local precommit_rc="$1" # 0 = passed, 1 = failed
local has_unstaged="$2" # "yes" or "no"
local retry_precommit_rc="$3" # 0 = passed on retry, 1 = still fails (ignored if no retry)
local retry_has_unstaged="${4:-no}" # "yes" if retry left unstaged changes

if [ "${precommit_rc}" -eq 0 ]; then
echo "pass:clean"
return 0
fi

# Pre-commit failed — check for auto-fixed files
if [ "${has_unstaged}" = "yes" ]; then
if [ "${retry_precommit_rc}" -eq 0 ]; then
if [ "${retry_has_unstaged}" = "yes" ]; then
echo "blocked:retry-left-unstaged"
else
echo "pass:auto-fixed"
fi
else
echo "blocked:retry-failed"
fi
else
echo "blocked:no-auto-fix"
fi
}

run_precommit_retry_test() {
local test_name="$1"
local precommit_rc="$2"
local has_unstaged="$3"
local retry_precommit_rc="$4"
local expected="$5"
local retry_has_unstaged="${6:-no}"

local actual
actual="$(decide_precommit_retry "${precommit_rc}" "${has_unstaged}" "${retry_precommit_rc}" "${retry_has_unstaged}")"

if [ "${actual}" != "${expected}" ]; then
echo "FAIL: ${test_name}"
echo " precommit_rc: '${precommit_rc}'"
echo " has_unstaged: '${has_unstaged}'"
echo " retry_precommit_rc: '${retry_precommit_rc}'"
echo " retry_has_unstaged: '${retry_has_unstaged}'"
echo " expected: '${expected}'"
echo " actual: '${actual}'"
FAILURES=$((FAILURES + 1))
return
fi

echo "PASS: ${test_name}"
}

# --- Pre-commit auto-fix retry test cases ---

# Pre-commit passes on first run → no retry needed
run_precommit_retry_test "precommit-passes-first-run" \
"0" "no" "0" "pass:clean"

# Pre-commit fails, hooks auto-fixed files, retry succeeds
run_precommit_retry_test "precommit-auto-fix-retry-succeeds" \
"1" "yes" "0" "pass:auto-fixed"

# Pre-commit fails, hooks auto-fixed files, retry still fails
run_precommit_retry_test "precommit-auto-fix-retry-fails" \
"1" "yes" "1" "blocked:retry-failed"

# Pre-commit fails, no unstaged changes (genuine failure)
run_precommit_retry_test "precommit-genuine-failure" \
"1" "no" "0" "blocked:no-auto-fix"

# Pre-commit passes but unstaged changes exist (e.g. hook wrote a log file)
run_precommit_retry_test "precommit-passes-with-unstaged" \
"0" "yes" "0" "pass:clean"

# Pre-commit fails, auto-fix retry passes, but retry left unstaged changes
run_precommit_retry_test "precommit-retry-passes-but-left-unstaged" \
"1" "yes" "0" "blocked:retry-left-unstaged" "yes"

# --- Summary ---

echo ""
Expand Down
55 changes: 53 additions & 2 deletions internal/scaffold/fullsend-repo/scripts/post-fix.sh
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,63 @@ if [ "${NO_PUSH}" = "false" ] && [ -f .pre-commit-config.yaml ]; then
fi

if command -v pre-commit >/dev/null 2>&1; then
# SYNC: parallel retry block in post-code.sh section 5 — keep structure
# in sync (variable names differ: BRANCH_CHANGED_FILES here vs
# CHANGED_FILES there; SCAN_RANGE scopes differ by design).
mapfile -t changed_array <<< "${BRANCH_CHANGED_FILES}"
if pre-commit run --files "${changed_array[@]}"; then
echo "Pre-commit passed — all hooks clean"
else
echo "::error::BLOCKED — pre-commit hooks failed on agent's changes" >&2
exit 1
# Single retry only — do not convert to a loop without adding a cap.
# Scope detection/staging to changed_array so hooks can't inject files
# outside the pre-commit scope into the commit.
if git diff --name-only -- "${changed_array[@]}" | grep -q .; then
echo "::warning::Pre-commit hooks auto-fixed files — re-staging and retrying"
echo "Auto-fixed files:"
git diff --name-only -- "${changed_array[@]}" | sed 's/^/ /'
git diff --name-only -z -- "${changed_array[@]}" | xargs -0 -r git add --
git commit --amend --no-edit

echo "Re-running secret scan on amended commit..."
if ! gitleaks detect --source . --log-opts="${SCAN_RANGE}" --redact; then
echo "::error::BLOCKED — secret detected in amended commit after auto-fix" >&2
exit 1
fi
if git log --format='%b' "${SCAN_RANGE}" | grep -q '^Signed-off-by:'; then
echo "::error::BLOCKED — amended commit contains a Signed-off-by trailer" >&2
exit 1
fi

if [ -n "${MERGE_BASE}" ]; then
BRANCH_CHANGED_FILES="$(git diff --name-only "${MERGE_BASE}..HEAD")"
else
BRANCH_CHANGED_FILES="$(git diff --name-only "origin/${TARGET_BRANCH}..HEAD" 2>/dev/null \
|| git diff --name-only HEAD~1..HEAD 2>/dev/null || true)"
fi
if [ -z "${BRANCH_CHANGED_FILES}" ]; then
echo "::error::BLOCKED — pre-commit hooks removed all changes; commit is now empty" >&2
exit 1
fi
mapfile -t changed_array <<< "${BRANCH_CHANGED_FILES}"
if pre-commit run --files "${changed_array[@]}"; then
if git diff --name-only -- "${changed_array[@]}" | grep -q .; then
echo "::error::BLOCKED — retry pre-commit left additional unstaged changes" >&2
echo "::error::Committed content would diverge from what pre-commit validated." >&2
exit 1
fi
echo "Pre-commit passed after auto-fix re-stage"
else
echo "::error::BLOCKED — pre-commit hooks still fail after auto-fix" >&2
echo "::error::The agent's code does not pass the repo's pre-commit hooks." >&2
echo "::error::Fix the issues and re-run, or update the pre-commit config." >&2
exit 1
fi
else
echo "::error::BLOCKED — pre-commit hooks failed on agent's changes" >&2
echo "::error::The agent's code does not pass the repo's pre-commit hooks." >&2
echo "::error::Fix the issues and re-run, or update the pre-commit config." >&2
exit 1
fi
fi
else
echo "::warning::pre-commit not available — skipping authoritative check"
Expand Down
Loading