cyberchef/.github/workflows/sync_upstream.yml
J8k3 1045fcc3c5 ci(sync): auto-resolve recurring upstream conflicts, fix workflow restore
The weekly upstream sync hard-failed whenever upstream touched files the
fork diverges on (README branding) or removed a now-generated file
(tests/operations/index.mjs). It also had a latent bug: the workflow
restore ran 'git checkout HEAD -- .github/workflows/' *after* the merge
had already auto-committed, so HEAD was the merge commit and the restore
was a no-op.

- Merge with --no-commit so HEAD stays the pre-merge fork commit, making
  the workflow restore actually keep the fork's workflows.
- Auto-resolve the two recurring conflicts: keep the fork's README, and
  honour upstream deletions of generated/gitignored files.
- Fail loudly (and abort the merge) on any other, unexpected conflict
  instead of pushing a half-resolved tree.
- Exit cleanly when already up to date.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 08:21:03 -04:00

70 lines
2.6 KiB
YAML

name: Sync Upstream
on:
schedule:
- cron: '0 6 * * 1' # weekly, Monday 6am UTC
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.PAT_TOKEN }}
fetch-depth: 0
- name: Sync upstream
run: |
set -eo pipefail
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
git remote add upstream https://github.com/gchq/CyberChef.git
git fetch upstream
# Merge without committing so we can post-process the result before
# creating a single merge commit:
# - auto-resolve the conflicts this fork hits every week,
# - keep the fork's own workflow files (HEAD stays the pre-merge
# fork commit while --no-commit is in effect).
git merge --no-commit --no-ff upstream/master || true
if ! git rev-parse -q --verify MERGE_HEAD >/dev/null; then
echo "Already up to date with upstream."
exit 0
fi
# Resolve known, recurring conflicts; bail out loudly on anything
# unexpected so we never push a half-resolved tree to master.
unresolved=""
for f in $(git diff --name-only --diff-filter=U); do
if [ "$f" = "README.md" ]; then
# The fork's README intentionally diverges (jacobmarks.com
# branding + extra demo links); keep our version.
git checkout --ours -- "$f"
git add -- "$f"
echo "Resolved $f -> kept fork version"
elif ! git cat-file -e "upstream/master:$f" 2>/dev/null \
&& git check-ignore -q --no-index "$f"; then
# Generated/gitignored file that upstream has deleted (e.g.
# tests/operations/index.mjs is now produced by grunt
# configTests); honour the deletion.
git rm -q -- "$f"
echo "Resolved $f -> honoured upstream deletion (generated file)"
else
unresolved="$unresolved $f"
fi
done
if [ -n "$unresolved" ]; then
echo "::error::Unresolved merge conflicts need manual resolution:$unresolved"
git merge --abort
exit 1
fi
# Keep the fork's own workflow files instead of adopting upstream's.
git checkout HEAD -- .github/workflows/
git add -A .github/workflows/
git commit --no-edit
git push origin master