Skip to content
Closed
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
1 change: 1 addition & 0 deletions .github/workflows/reusable-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ jobs:
REPO_FULL_NAME: ${{ inputs.source_repo }}
GITHUB_ISSUE_URL: ${{ fromJSON(inputs.event_payload).issue.html_url }}
COMMENT_BODY: ${{ fromJSON(inputs.event_payload).comment.body }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
FULLSEND_DIR: ${{ inputs.install_mode == 'per-repo' && '.fullsend' || '' }}
run: bash "${FULLSEND_DIR:+$FULLSEND_DIR/}scripts/pre-code.sh"

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/reusable-fix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ jobs:
TRIGGER_SOURCE: ${{ inputs.trigger_source }}
FIX_ITERATION: ${{ steps.context.outputs.iteration }}
HUMAN_INSTRUCTION: ${{ steps.context.outputs.instruction }}
FIX_SKIP_TOOL_INSTALL: "true"
FULLSEND_DIR: ${{ inputs.install_mode == 'per-repo' && '.fullsend' || '' }}
run: bash "${FULLSEND_DIR:+$FULLSEND_DIR/}scripts/pre-fix.sh"

Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ script-test:
$(call run-timed,bash internal/scaffold/fullsend-repo/scripts/reconcile-repos-test.sh)
$(call run-timed,bash internal/scaffold/fullsend-repo/scripts/validate-output-schema-test.sh)
$(call run-timed,bash internal/scaffold/fullsend-repo/scripts/pre-code-test.sh)
$(call run-timed,bash internal/scaffold/fullsend-repo/scripts/pre-fix-test.sh)
$(call run-timed,bash internal/scaffold/fullsend-repo/scripts/pre-fetch-prior-review-test.sh)
$(call run-timed,python3 internal/scaffold/fullsend-repo/scripts/process-fix-result-test.py)
$(call run-timed,python3 skills/topissues/scripts/topissues_test.py)
Expand Down
2 changes: 2 additions & 0 deletions internal/scaffold/fullsend-repo/harness/code.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,5 @@ forge:
REPO_FULL_NAME: "${REPO_FULL_NAME}"
ISSUE_NUMBER: "${ISSUE_NUMBER}"
REPO_DIR: "${GITHUB_WORKSPACE}/target-repo"
# Skips the already-performed existing-PR check (issue #4718)
CODE_SKIP_EXISTING_PR_CHECK: "true"
88 changes: 88 additions & 0 deletions internal/scaffold/fullsend-repo/scripts/pre-code-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,94 @@ run_test_github_output "skip-output-false-on-no-token" \
0 \
"GH_TOKEN="

# --- CODE_SKIP_EXISTING_PR_CHECK tests (issue #4718) ---
#
# harness/code.yaml sets this for the second (harness pre_script) invocation
# of pre-code.sh, so the existing-human-PR search/label/comment side effects
# — already performed by the reusable workflow's inline "Validate inputs"
# step — don't run a second time.

# Human PR exists, but the flag is set → check must be bypassed entirely,
# not just short-circuited by the existing branches (no PR search, no
# "Skipping code agent", proceeds as if no PR was found).
run_test_stdout_excludes "gate-skip-flag-bypasses-check" \
"${HUMAN_PR_JSON}" \
"already performed by the workflow's inline gate" \
"Checking for existing open PRs" \
0 \
"CODE_SKIP_EXISTING_PR_CHECK=true"

run_test_stdout_excludes "gate-skip-flag-does-not-skip-agent" \
"${HUMAN_PR_JSON}" \
"already performed by the workflow's inline gate" \
"Skipping code agent" \
0 \
"CODE_SKIP_EXISTING_PR_CHECK=true"

# The flag skips the whole block, including the skipped= write — nothing
# downstream reads GITHUB_OUTPUT from this invocation, so it must stay empty.
run_test_github_output_excludes() {
local test_name="$1"
local pr_list_output="$2"
local excluded_output="$3"
local expect_exit="$4"
local extra_env="${5:-}"

local mock_bin
mock_bin="$(build_mock "${pr_list_output}")"
local gh_output="${TMPDIR}/github-output.txt"
: > "${gh_output}"

local env_cmd=(
env
PATH="${mock_bin}:${PATH}"
ISSUE_NUMBER="42"
REPO_FULL_NAME="test-org/test-repo"
GITHUB_ISSUE_URL="https://github.com/test-org/test-repo/issues/42"
GH_TOKEN="fake-token"
GITHUB_OUTPUT="${gh_output}"
)

if [[ -n "${extra_env}" ]]; then
while IFS= read -r kv; do
[[ -n "${kv}" ]] && env_cmd+=("${kv}")
done <<< "${extra_env}"
fi

local exit_code=0
"${env_cmd[@]}" bash "${PRE_SCRIPT}" > "${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 "${excluded_output}" "${gh_output}" 2>/dev/null; then
echo "FAIL: ${test_name} — GITHUB_OUTPUT unexpectedly contains '${excluded_output}'"
echo "Actual GITHUB_OUTPUT:"
cat "${gh_output}"
FAILURES=$((FAILURES + 1))
return
fi

echo "PASS: ${test_name}"
}

run_test_github_output_excludes "gate-skip-flag-writes-no-skipped-output" \
"${HUMAN_PR_JSON}" \
"skipped=" \
0 \
"CODE_SKIP_EXISTING_PR_CHECK=true"

# Flag unset (default) → behavior is completely unchanged from before #4718:
# the existing-PR check still runs and still blocks on a human PR.
run_test_stdout "gate-skip-flag-unset-check-still-runs" \
"${HUMAN_PR_JSON}" \
"Checking for existing open PRs" \
0

# --- Summary ---

echo ""
Expand Down
128 changes: 73 additions & 55 deletions internal/scaffold/fullsend-repo/scripts/pre-code.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@
# ISSUE_NUMBER — must be a positive integer
# REPO_FULL_NAME — must be owner/repo format
# GITHUB_ISSUE_URL — must be a valid GitHub issue URL
#
# Optional environment variables:
# CODE_SKIP_EXISTING_PR_CHECK
# — "true" to skip the existing-human-PR search below.
# Set via harness/code.yaml's forge.github.runner_env
# so the *second* invocation of this script (as the
# harness pre_script, run by `fullsend run` right
# before sandbox creation) doesn't repeat the GH API
# search/label/comment that the reusable workflow's
# inline "Validate inputs" step already performed
# before the expensive GCP/bot-identity/agent-env
# setup steps (issue #4718). Leave unset for that
# inline invocation — it is the one that must produce
# the skipped= output those steps gate on.
set -euo pipefail

echo "::notice::🔗 Code target: ${GITHUB_ISSUE_URL:-}"
Expand Down Expand Up @@ -54,73 +68,77 @@ echo " GITHUB_ISSUE_URL=${GITHUB_ISSUE_URL}"
# ---------------------------------------------------------------------------
# Check for existing human PRs linked to this issue
# ---------------------------------------------------------------------------
# Skip if GH_TOKEN is not available (best-effort check).
if [[ -z "${GH_TOKEN:-}" ]]; then
echo "GH_TOKEN not set — skipping existing-PR check"
echo "skipped=false" >> "${GITHUB_OUTPUT:-/dev/null}"
exit 0
fi
if [[ "${CODE_SKIP_EXISTING_PR_CHECK:-false}" == "true" ]]; then
echo "Existing-PR check already performed by the workflow's inline gate — skipping"
else
# Skip if GH_TOKEN is not available (best-effort check).
if [[ -z "${GH_TOKEN:-}" ]]; then
echo "GH_TOKEN not set — skipping existing-PR check"
echo "skipped=false" >> "${GITHUB_OUTPUT:-/dev/null}"
exit 0
fi

# Allow override when --force is in the trigger comment or CODE_FORCE is set.
echo "Evaluating force override: CODE_FORCE='${CODE_FORCE:-}' COMMENT_BODY='${COMMENT_BODY:-}'"
if [[ "${CODE_FORCE:-}" == "true" ]] || [[ "${COMMENT_BODY:-}" == *--force* ]]; then
echo "Force override — skipping existing-PR check"
echo "skipped=false" >> "${GITHUB_OUTPUT:-/dev/null}"
exit 0
fi
# Allow override when --force is in the trigger comment or CODE_FORCE is set.
echo "Evaluating force override: CODE_FORCE='${CODE_FORCE:-}' COMMENT_BODY='${COMMENT_BODY:-}'"
if [[ "${CODE_FORCE:-}" == "true" ]] || [[ "${COMMENT_BODY:-}" == *--force* ]]; then
echo "Force override — skipping existing-PR check"
echo "skipped=false" >> "${GITHUB_OUTPUT:-/dev/null}"
exit 0
fi

BOT_LOGIN="fullsend-ai[bot]"
CODER_BOT_LOGIN="fullsend-ai-coder[bot]"

echo "Checking for existing open PRs linked to issue #${ISSUE_NUMBER}..."

# Search for open PRs in the repo that mention the issue number.
# This catches PRs with "Closes #N", "Fixes #N", or "#N" in the body/title.
# Use gh's built-in --jq to filter out bot-authored PRs in one call.
HUMAN_PR_LINES="$(gh pr list --repo "${REPO_FULL_NAME}" --state open \
--search "${ISSUE_NUMBER} in:body,title" \
--json number,url,author \
--jq "[.[] | select(.author.login != \"${BOT_LOGIN}\" and .author.login != \"${CODER_BOT_LOGIN}\")] | .[] | \"\(.number)\t\(.author.login)\t\(.url)\"" \
2>/dev/null || true)"

if [[ -n "${HUMAN_PR_LINES}" ]]; then
# Parse the first PR for the notice.
FIRST_PR_NUM="$(echo "${HUMAN_PR_LINES}" | head -1 | cut -f1)"
FIRST_PR_AUTHOR="$(echo "${HUMAN_PR_LINES}" | head -1 | cut -f2)"

echo "::notice::Found existing human PR #${FIRST_PR_NUM} by @${FIRST_PR_AUTHOR}"

# Apply pr-open label to signal work is already underway.
gh label create "pr-open" --repo "${REPO_FULL_NAME}" \
--description "An open PR already addresses this issue" --color "D4C5F9" \
--force 2>/dev/null || true
gh api "repos/${REPO_FULL_NAME}/issues/${ISSUE_NUMBER}/labels" \
-f "labels[]=pr-open" --silent 2>/dev/null || true

# Build a markdown list of existing PRs.
PR_LIST_MD=""
while IFS=$'\t' read -r pr_num pr_author _pr_url; do
PR_LIST_MD="${PR_LIST_MD}
BOT_LOGIN="fullsend-ai[bot]"
CODER_BOT_LOGIN="fullsend-ai-coder[bot]"

echo "Checking for existing open PRs linked to issue #${ISSUE_NUMBER}..."

# Search for open PRs in the repo that mention the issue number.
# This catches PRs with "Closes #N", "Fixes #N", or "#N" in the body/title.
# Use gh's built-in --jq to filter out bot-authored PRs in one call.
HUMAN_PR_LINES="$(gh pr list --repo "${REPO_FULL_NAME}" --state open \
--search "${ISSUE_NUMBER} in:body,title" \
--json number,url,author \
--jq "[.[] | select(.author.login != \"${BOT_LOGIN}\" and .author.login != \"${CODER_BOT_LOGIN}\")] | .[] | \"\(.number)\t\(.author.login)\t\(.url)\"" \
2>/dev/null || true)"

if [[ -n "${HUMAN_PR_LINES}" ]]; then
# Parse the first PR for the notice.
FIRST_PR_NUM="$(echo "${HUMAN_PR_LINES}" | head -1 | cut -f1)"
FIRST_PR_AUTHOR="$(echo "${HUMAN_PR_LINES}" | head -1 | cut -f2)"

echo "::notice::Found existing human PR #${FIRST_PR_NUM} by @${FIRST_PR_AUTHOR}"

# Apply pr-open label to signal work is already underway.
gh label create "pr-open" --repo "${REPO_FULL_NAME}" \
--description "An open PR already addresses this issue" --color "D4C5F9" \
--force 2>/dev/null || true
gh api "repos/${REPO_FULL_NAME}/issues/${ISSUE_NUMBER}/labels" \
-f "labels[]=pr-open" --silent 2>/dev/null || true

# Build a markdown list of existing PRs.
PR_LIST_MD=""
while IFS=$'\t' read -r pr_num pr_author _pr_url; do
PR_LIST_MD="${PR_LIST_MD}
- #${pr_num} by @${pr_author}"
done <<< "${HUMAN_PR_LINES}"
done <<< "${HUMAN_PR_LINES}"

SKIP_COMMENT="An open PR already addresses this issue — skipping automated implementation.
SKIP_COMMENT="An open PR already addresses this issue — skipping automated implementation.
${PR_LIST_MD}

To override, comment \`/fs-code --force\` on this issue.

<sub>Posted by <a href=\"https://github.com/fullsend-ai/fullsend\">fullsend</a> pre-code check</sub>"

printf '%s' "${SKIP_COMMENT}" | gh issue comment "${ISSUE_NUMBER}" \
--repo "${REPO_FULL_NAME}" --body-file - 2>/dev/null || true
printf '%s' "${SKIP_COMMENT}" | gh issue comment "${ISSUE_NUMBER}" \
--repo "${REPO_FULL_NAME}" --body-file - 2>/dev/null || true

echo "Skipping code agent — existing PR(s) found for issue #${ISSUE_NUMBER}"
echo "skipped=true" >> "${GITHUB_OUTPUT:-/dev/null}"
exit 0
fi
echo "Skipping code agent — existing PR(s) found for issue #${ISSUE_NUMBER}"
echo "skipped=true" >> "${GITHUB_OUTPUT:-/dev/null}"
exit 0
fi

echo "No existing human PRs found — proceeding with code agent"
echo "skipped=false" >> "${GITHUB_OUTPUT:-/dev/null}"
echo "No existing human PRs found — proceeding with code agent"
echo "skipped=false" >> "${GITHUB_OUTPUT:-/dev/null}"
fi

# ---------------------------------------------------------------------------
# Auto-detect and install pre-commit tool dependencies
Expand Down
Loading
Loading