diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml index 41a03c905..e0cab80de 100644 --- a/.github/workflows/sync-upstream.yml +++ b/.github/workflows/sync-upstream.yml @@ -109,91 +109,95 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" + AUTO_RESOLVED="" + # Attempt the merge — allow failure so we can auto-resolve conflicts echo "Merging upstream/main into fork branch..." if git merge upstream/main -m "Merge upstream TanStack Pacer v${VERSION} into fork"; then echo "Merge completed cleanly" - echo "auto_resolved=" >> "$GITHUB_OUTPUT" - git push origin "$BRANCH" - exit 0 - fi - - echo "Merge has conflicts — attempting auto-resolution..." - CONFLICTED=$(git diff --name-only --diff-filter=U) - - # ── Auto-resolution rules ───────────────────────────────────── - # Each rule handles a category of files that we never customize - # in the fork and can safely resolve automatically. - AUTO_RESOLVED="" - UNRESOLVED="" - - for FILE in $CONFLICTED; do - case "$FILE" in - # Generated docs — take upstream; autofix CI regenerates them - docs/*) - git checkout --theirs -- "$FILE" && git add "$FILE" - AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (took upstream — generated docs)\n" - ;; - # CI workflows deleted in fork (e.g. release.yml) — keep deleted - .github/workflows/release.yml) - git rm -f "$FILE" 2>/dev/null || true - AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (kept deleted — fork uses publish-fork.yml)\n" - ;; - # PR workflow — keep fork's simplified version (no NX Cloud, no - # nx-set-shas, no preview job, uses test:ci). The fork has - # intentionally diverged here; upstream changes are not adopted - # automatically and can be cherry-picked manually if desired. - .github/workflows/pr.yml) - git checkout --ours -- "$FILE" && git add "$FILE" - AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (kept fork version — simplified PR workflow)\n" - ;; - # Upstream changelogs — take upstream; fork doesn't add entries - packages/*/CHANGELOG.md) - git checkout --theirs -- "$FILE" && git add "$FILE" - AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (took upstream — changelog)\n" - ;; - # Lockfile — take upstream, then reconcile with fork's root package.json - pnpm-lock.yaml) - git checkout --theirs -- "$FILE" && git add "$FILE" - AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (took upstream + pnpm install to reconcile)\n" - NEEDS_LOCKFILE_RECONCILE=true - ;; - # Example app package.json — take upstream; fork doesn't modify examples - examples/*/package.json | examples/*/*/package.json) - git checkout --theirs -- "$FILE" && git add "$FILE" - AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (took upstream — example dep versions)\n" - ;; - # Everything else requires manual review - *) - UNRESOLVED="${UNRESOLVED} ${FILE}\n" - ;; - esac - done + else + echo "Merge has conflicts — attempting auto-resolution..." + CONFLICTED=$(git diff --name-only --diff-filter=U) + + # ── Auto-resolution rules ───────────────────────────────────── + # Each rule handles a category of files that we never customize + # in the fork and can safely resolve automatically. + UNRESOLVED="" + + for FILE in $CONFLICTED; do + case "$FILE" in + # Generated docs — take upstream; autofix CI regenerates them + docs/*) + git checkout --theirs -- "$FILE" && git add "$FILE" + AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (took upstream — generated docs)\n" + ;; + # CI workflows — always keep fork version. GITHUB_TOKEN cannot + # push workflow file changes anyway, and the fork intentionally + # diverges on workflows. Upstream workflow changes can be + # cherry-picked manually with a PAT that has `workflow` scope. + .github/workflows/*) + if git cat-file -e ":2:$FILE" 2>/dev/null; then + git checkout --ours -- "$FILE" && git add "$FILE" + AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (kept fork version — workflows token cannot push)\n" + else + # Deleted in fork (modify/delete conflict) — keep deleted + git rm -f "$FILE" 2>/dev/null || true + AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (kept deleted — workflows token cannot push)\n" + fi + ;; + # Upstream changelogs — take upstream; fork doesn't add entries + packages/*/CHANGELOG.md) + git checkout --theirs -- "$FILE" && git add "$FILE" + AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (took upstream — changelog)\n" + ;; + # Lockfile — take upstream, then reconcile with fork's root package.json + pnpm-lock.yaml) + git checkout --theirs -- "$FILE" && git add "$FILE" + AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (took upstream + pnpm install to reconcile)\n" + NEEDS_LOCKFILE_RECONCILE=true + ;; + # Example app package.json — take upstream; fork doesn't modify examples + examples/*/package.json | examples/*/*/package.json) + git checkout --theirs -- "$FILE" && git add "$FILE" + AUTO_RESOLVED="${AUTO_RESOLVED} ${FILE} (took upstream — example dep versions)\n" + ;; + # Everything else requires manual review + *) + UNRESOLVED="${UNRESOLVED} ${FILE}\n" + ;; + esac + done + + if [ -n "$AUTO_RESOLVED" ]; then + echo "Auto-resolved conflicts:" + echo -e "$AUTO_RESOLVED" + fi - if [ -n "$AUTO_RESOLVED" ]; then - echo "Auto-resolved conflicts:" - echo -e "$AUTO_RESOLVED" - fi + # If any conflicts remain that we can't auto-resolve, abort + REMAINING=$(git diff --name-only --diff-filter=U) + if [ -n "$REMAINING" ]; then + echo "::error::Unable to auto-resolve all conflicts. Manual intervention required:" + echo -e "$UNRESOLVED" + git merge --abort + exit 1 + fi - # If any conflicts remain that we can't auto-resolve, abort - REMAINING=$(git diff --name-only --diff-filter=U) - if [ -n "$REMAINING" ]; then - echo "::error::Unable to auto-resolve all conflicts. Manual intervention required:" - echo -e "$UNRESOLVED" - git merge --abort - exit 1 - fi + # Reconcile lockfile if it was conflict-resolved + if [ "${NEEDS_LOCKFILE_RECONCILE:-}" = "true" ]; then + echo "Running pnpm install to reconcile lockfile with fork's package.json..." + pnpm install --no-frozen-lockfile + git add pnpm-lock.yaml + fi - # Reconcile lockfile if it was conflict-resolved - if [ "${NEEDS_LOCKFILE_RECONCILE:-}" = "true" ]; then - echo "Running pnpm install to reconcile lockfile with fork's package.json..." - pnpm install --no-frozen-lockfile - git add pnpm-lock.yaml + # All conflicts resolved — complete the merge + git commit --no-edit fi - # All conflicts resolved — complete the merge - git commit --no-edit echo "auto_resolved=$(echo -e "$AUTO_RESOLVED" | base64 -w 0)" >> "$GITHUB_OUTPUT" + + # If the push fails because the merge touches .github/workflows/* (the + # default GITHUB_TOKEN lacks the `workflow` scope), pull the branch + # locally and finish the merge by hand. git push origin "$BRANCH" - name: Create PR