diff --git a/deploy/sync-mirror.sh b/deploy/sync-mirror.sh index 7b3a4e2..4446c49 100755 --- a/deploy/sync-mirror.sh +++ b/deploy/sync-mirror.sh @@ -204,7 +204,39 @@ sync_github_to_forgejo_with_prs() { local FORGEJO_TOKEN FORGEJO_TOKEN=$(cat /opt/teleo-eval/secrets/forgejo-admin-token 2>/dev/null) + + # Lazy schema for sync-mirror's auto-create tracker. Records (branch, sha) + # pairs we've already auto-created PRs for, so the loop below can skip + # redundant creates after pipeline merge → _delete_remote_branch → + # GitHub-only re-discovery → re-push. Cheap CREATE IF NOT EXISTS on each + # cycle; no migration needed because this table is private to sync-mirror. + sqlite3 "$PIPELINE_DB" "CREATE TABLE IF NOT EXISTS sync_autocreate_tracker (branch TEXT NOT NULL, sha TEXT NOT NULL, pr_number INTEGER, created_at TEXT DEFAULT (datetime('now')), PRIMARY KEY (branch, sha));" 2>/dev/null || true + for branch in $GITHUB_ONLY; do + # Already-tracked gate: if we've previously auto-created a PR for + # this exact (branch, sha), skip the entire push+create sequence. + # Closes the empty-PR loop (research and reweave both observed): + # pipeline merges PR → _delete_remote_branch on Forgejo → next sync + # sees branch GitHub-only (origin still has it) → re-pushes to + # Forgejo → HAS_PR misses (Forgejo ?head= broken; closed PRs scroll + # past 50-item paginated window) → auto-creates fresh PR → pipeline + # merges (empty no-op via cherry-pick / reweave union) → repeat. + # Tracker keys on SHA, so legitimate new commits on the same branch + # produce a new SHA → tracker miss → auto-create proceeds normally. + local BRANCH_SHA TRACKED_PR + if [[ "$branch" == gh-pr-* ]]; then + BRANCH_SHA=$(git rev-parse "refs/heads/$branch" 2>/dev/null || true) + else + BRANCH_SHA=$(git rev-parse "refs/remotes/origin/$branch" 2>/dev/null || true) + fi + if [ -n "$BRANCH_SHA" ]; then + TRACKED_PR=$(sqlite3 "$PIPELINE_DB" "SELECT pr_number FROM sync_autocreate_tracker WHERE branch=$(printf "'%s'" "${branch//\'/\'\'}") AND sha=$(printf "'%s'" "$BRANCH_SHA") LIMIT 1;" 2>/dev/null || true) + if [ -n "$TRACKED_PR" ]; then + log "Skip auto-create: $branch SHA $BRANCH_SHA already tracked (PR #$TRACKED_PR)" + continue + fi + fi + log "New from GitHub: $branch -> Forgejo" # Fork PR branches live as local refs (from Step 2.1), not on origin remote if [[ "$branch" == gh-pr-* ]]; then @@ -275,6 +307,13 @@ print('no') fi log "Auto-created PR #$PR_NUM on Forgejo for $branch" + # Record (branch, sha, pr_number) so the tracker gate above can short- + # circuit the next time we see this exact (branch, sha) combination. + # INSERT OR IGNORE: idempotent if a concurrent run already inserted. + if [ -n "$BRANCH_SHA" ] && [[ "$PR_NUM" =~ ^[0-9]+$ ]]; then + sqlite3 "$PIPELINE_DB" "INSERT OR IGNORE INTO sync_autocreate_tracker (branch, sha, pr_number) VALUES ($(printf "'%s'" "${branch//\'/\'\'}"), $(printf "'%s'" "$BRANCH_SHA"), $PR_NUM);" 2>/dev/null || true + fi + # Step 4.5: Link GitHub PR to Forgejo PR in pipeline DB if [[ "$branch" == gh-pr-* ]]; then GH_PR_NUM=$(echo "$branch" | sed 's|gh-pr-\([0-9]*\)/.*|\1|')