Skip to content
Closed
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
154 changes: 79 additions & 75 deletions .github/workflows/sync-upstream.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading