From f0e10d6d2648e9d74587026f6bb2cf4c4ee9d06c Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Thu, 14 May 2026 15:46:13 +0700 Subject: [PATCH] fix(ci): narrow conflict resolution to bot-generated files only The blanket git pull --rebase -X theirs in the push-retry loop silently discarded conflicting changes in any file, so a maintainer push to data/agents.yml racing with the daily run could be dropped without warning. Replace it with a selective resolver: plain rebase first, then inspect git diff --name-only --diff-filter=U; if every conflicting path is data/history.jsonl or README.md (both bot-generated), resolve those with --theirs and continue; otherwise abort the rebase and fail the run loudly so the human edit is preserved and visible. Also leave a comment at the permissions block warning future maintainers not to swap GITHUB_TOKEN for a PAT, since the no-recursion invariant is what currently prevents the auto-commit from triggering its own workflow. --- .github/workflows/update.yml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 74cd3c2..cb0fa36 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -14,6 +14,9 @@ on: - '.github/workflows/update.yml' permissions: + # Uses GITHUB_TOKEN intentionally — a PAT would cause infinite trigger loops + # on the auto-commit ("chore: daily ranking refresh") because GITHUB_TOKEN-authored + # pushes do NOT re-trigger workflows, whereas a PAT would. contents: write concurrency: @@ -46,11 +49,30 @@ jobs: exit 0 fi git commit -m "chore: daily ranking refresh" - # rebase + retry guards against the race where two runs commit near-simultaneously. - # -X theirs makes our run's data/history.jsonl line win conflicts (our snapshot is the freshest). + # Rebase + retry guards against the race where two runs commit near-simultaneously. + # Strategy: plain rebase first; only force-resolve bot-generated files (README.md, + # data/history.jsonl) when they are the sole conflicts. If any human-maintained file + # (e.g. data/agents.yml) conflicts, abort and fail loudly so a human can investigate. for attempt in 1 2 3; do if git push; then exit 0; fi echo "push attempt $attempt rejected, rebasing on origin/main" - git pull --rebase -X theirs origin main + if git pull --rebase origin main; then + # Clean rebase — no conflicts; loop back to try push again. + continue + fi + # Rebase stopped on conflicts. Inspect which files are unresolved. + conflicted=$(git diff --name-only --diff-filter=U) + echo "conflicted files: $conflicted" + # Only auto-resolve if ALL conflicts are in bot-generated files. + non_bot=$(echo "$conflicted" | grep -v -E '^(README\.md|data/history\.jsonl)$' || true) + if [ -n "$non_bot" ]; then + echo "ERROR: conflict in human-maintained file(s): $non_bot — aborting rebase" + git rebase --abort + exit 1 + fi + # Safe to force-resolve: keep our (freshest) bot-generated content. + git checkout --theirs README.md data/history.jsonl 2>/dev/null || true + git add README.md data/history.jsonl + GIT_EDITOR=true git rebase --continue done exit 1