From 1eb259de8acbd376c25e20537ba7a639a7cbd9ba Mon Sep 17 00:00:00 2001 From: m3taversal Date: Tue, 28 Apr 2026 13:01:43 +0100 Subject: [PATCH] fix(sync-mirror): self-heal sweep for orphaned gh-pr-* github_pr links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 0 (new): runs once per cron tick before per-repo work. Selects PR rows where branch matches gh-pr-% but github_pr IS NULL, parses the PR number from the branch name, and updates github_pr + source_channel='github'. Recovers from races and transient failures in the existing Step 4.5 link UPDATE — no retry path before. The sweep IS the backfill: same SELECT/UPDATE heals historical orphans (FwazB PR 4066 picked up on first cron tick) AND future races on subsequent ticks. No separate one-shot script needed. Properties: - Idempotent: SELECT empty when clean, zero work - No API calls: branch name encodes the GitHub PR number deterministically - Bounded log volume: one line per actually-healed row - Runs before any sync_repo work, ahead of branch-mirror loop and the auto-create-PR block in Step 4 — same-cycle convergence on fresh races Closes the Bug #2 path that left FwazB's PR 4066 with github_pr=NULL, preventing on_merged() from posting comment + closing the GitHub PR. Verified end-to-end on live DB snapshot: - before: 4066 had github_pr=NULL - after sweep: 4066 has github_pr=90, source_channel='github' - second run: zero output (idempotent) Phase 1 of docs/external-contributor-merge-flow.md (v2, sweep-only). Ship architecturally approved Msg 2/2. Co-Authored-By: Claude Opus 4.7 (1M context) --- deploy/sync-mirror.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/deploy/sync-mirror.sh b/deploy/sync-mirror.sh index bd04e98..15c9cdd 100755 --- a/deploy/sync-mirror.sh +++ b/deploy/sync-mirror.sh @@ -367,6 +367,27 @@ print(json.dumps({'chat_id': sys.argv[4], 'text': msg, 'parse_mode': 'HTML'})) REPO_TAG="main" log "Starting sync cycle" +# Step 0: self-heal any gh-pr-* PR rows missing github_pr. +# Runs FIRST — before per-repo work (branch-mirror loop, auto-create-PR block). +# Recovers from races/transient failures in Step 4.5's one-shot link UPDATE. +# Idempotent: SELECT empty when clean, zero-cost path. Same SELECT/UPDATE +# heals historical orphans (PR 4066 picked up on first cron tick post-deploy) +# and future races on subsequent ticks. The branch name encodes the GitHub PR +# number deterministically (gh-pr-{N}/...) so no API call is required. +if [ -f "$PIPELINE_DB" ]; then + sqlite3 -separator '|' "$PIPELINE_DB" \ + "SELECT number, branch FROM prs WHERE branch LIKE 'gh-pr-%' AND github_pr IS NULL;" \ + 2>/dev/null | while IFS='|' read -r pr_num branch; do + gh_pr_num=$(echo "$branch" | sed -n 's|^gh-pr-\([0-9]*\)/.*|\1|p') + [ -z "$gh_pr_num" ] && continue + if sqlite3 "$PIPELINE_DB" \ + "UPDATE prs SET github_pr = $gh_pr_num, source_channel = 'github' WHERE number = $pr_num;" \ + 2>/dev/null; then + log "self-heal: linked Forgejo PR #$pr_num -> GitHub PR #$gh_pr_num" + fi + done +fi + for entry in "${MIRROR_REPOS[@]}"; do # Read the 4 fields. `read` splits on $IFS (whitespace) by default. read -r forgejo_repo github_repo bare_path mode <<< "$entry"