From c6295c1401fdfd4e6d442c5f7578679029d1ce09 Mon Sep 17 00:00:00 2001 From: Vianney MORAIN Date: Mon, 20 Jul 2026 22:03:20 +0200 Subject: [PATCH] ci(sync): auto-resolve fixture-stamp conflicts; unignore fixture snapshots The weekly upstream rebase conflicts on fixtures/*/.webstudio/data.json almost every time: the bundleVersion is a schema hash both sides regenerate, so it is never a real content conflict. Until now any conflict aborted the rebase and opened a manual issue, forcing a hand rebase each week. - sync-upstream.yml: auto-resolve rebase conflicts that touch ONLY the fixture stamp files (take upstream's stamp via --ours, which is upstream during a rebase; the following re-stamp step recomputes the correct fork hash). Any conflict on a real code file still aborts and opens an issue. The patch-series sanity check now ignores the re-stamp commit, which legitimately drops as empty during the rebase. - .gitignore: force-keep fixtures/*/.webstudio/data.json (tracked despite the broad .webstudio ignore) so the pre-commit hook stops choking on re-stamps. - CLAUDE.md: document both. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/sync-upstream.yml | 58 +++++++++++++++++++++++++---- .gitignore | 4 ++ CLAUDE.md | 2 + 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml index 333f28f4374b..44c239d77372 100644 --- a/.github/workflows/sync-upstream.yml +++ b/.github/workflows/sync-upstream.yml @@ -84,14 +84,55 @@ jobs: run: | git fetch origin main - # Snapshot the patch series (titles) before rewrite - git log origin/main..HEAD --format="%s" > /tmp/titles-before + # The fixture bundleVersion stamp is a schema-hash that both sides + # regenerate, so it conflicts on nearly every sync even though it is + # not real content — the re-stamp step below recomputes it. Auto-take + # upstream for those files ONLY; anything else needs a human. + fixture_stamp='^fixtures/[^/]+/\.webstudio/data\.json$' + resolve_fixture_conflicts() { + while true; do + conflicts="$(git diff --name-only --diff-filter=U)" + # Any conflicted path that is NOT a fixture stamp (or no conflict + # at all) -> stop, let the caller abort and open an issue. + non_fixture="$(printf '%s\n' "$conflicts" \ + | grep -vE "$fixture_stamp" || true)" + if [ -z "$conflicts" ] || [ -n "$non_fixture" ]; then + return 1 + fi + # NOTE: during a rebase --ours/--theirs are inverted vs a merge: + # --ours = the new base (upstream), --theirs = the replayed fork + # commit. We want upstream's stamp here, so --ours. + printf '%s\n' "$conflicts" | while IFS= read -r f; do + git checkout --ours -- "$f" && git add -- "$f" + done + # Taking upstream can leave the stamp-only commit empty -> skip it. + if git diff --cached --quiet; then + GIT_EDITOR=true git rebase --skip && return 0 + else + GIT_EDITOR=true git rebase --continue && return 0 + fi + # Rebase paused again on a later commit -> loop and re-evaluate. + done + } + + # Snapshot the patch series (titles) before rewrite, ignoring the + # fixture re-stamp commit (it is regenerated below and may be dropped + # as empty during the rebase — that is expected, not a lost patch). + git log origin/main..HEAD --format="%s" | grep -viE 're-?stamp' \ + > /tmp/titles-before || true echo "Patch series before rebase:" cat /tmp/titles-before - # Never auto-resolve: any conflict aborts (handled in next step) - if ! git rebase origin/main; then + if git rebase origin/main; then + rebase_ok=1 + elif resolve_fixture_conflicts; then + rebase_ok=1 + else + rebase_ok=0 + fi + + if [ "$rebase_ok" -ne 1 ]; then git diff --name-only --diff-filter=U > /tmp/conflict-files || true { echo "conflict_files< /tmp/titles-after + # Sanity check: same patch titles before and after (re-stamp commit + # excluded above). A mismatch usually means git dropped a real patch + # that became empty (change accepted upstream) — a human decision. + git log origin/main..HEAD --format="%s" | grep -viE 're-?stamp' \ + > /tmp/titles-after || true if ! diff -u /tmp/titles-before /tmp/titles-after; then echo "ERROR: patch series changed during rebase (see diff above)." echo "If a patch was upstreamed, drop it explicitly and update COMMUNITY_CHANGES.md." diff --git a/.gitignore b/.gitignore index d5617e7eac61..7ecd680a80fe 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,10 @@ yarn-error.log* # data /data .webstudio +# ...but keep the tracked fixture snapshots (checked by packages/protocol +# fixtures.test.ts); without this the pre-commit hook chokes on re-stamps. +!fixtures/*/.webstudio/ +!fixtures/*/.webstudio/data.json *.db *.db-journal diff --git a/CLAUDE.md b/CLAUDE.md index fad5c4012f98..67d84851cea5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,6 +10,8 @@ This is a community fork. `develop` is maintained as `upstream/main` + a small p `bundleVersion` (in `packages/protocol/src/schema.ts`) is a contract hash of the schema. The fork's self-hosting patches change the schema, so its hash differs from the (upstream-generated) stamps committed in `fixtures/*/.webstudio/data.json`, failing `fixtures.test.ts`. After changing the bundle schema — or after an upstream sync — run `pnpm fixtures:restamp` and commit. The `sync-upstream` workflow already does this automatically after a clean rebase; `pnpm fixtures:restamp --check` fails on drift (CI). +Because both sides re-generate that stamp, it conflicts on most syncs even though it is not real content. The `sync-upstream` workflow therefore **auto-resolves rebase conflicts that touch only `fixtures/*/.webstudio/data.json`** (takes the upstream stamp, then re-stamps), and only opens a manual-conflict issue when a real code file also conflicts. These fixture files are force-tracked despite the `.webstudio` ignore rule — the negations in `.gitignore` keep the pre-commit hook from choking on re-stamps. + ## Overview Webstudio is an Open Source Visual Development Platform. This is a pnpm monorepo with three workspace types: `apps/`, `packages/`, and `fixtures/`.