diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml index 61c319e..63034c6 100644 --- a/.gitea/workflows/docker-build.yml +++ b/.gitea/workflows/docker-build.yml @@ -36,12 +36,12 @@ jobs: id: meta run: echo "sha=${GITHUB_SHA::8}" >> "$GITHUB_OUTPUT" - # WASM artifact freshness is enforced precisely by the dedicated - # `web-wasm-freshness` workflow (rebuild-and-diff on every PR + master - # push), which supersedes the previous changed-files heuristic here. - # That heuristic only hard-failed on direct solitaire_web/ edits and - # treated solitaire_engine/_core changes as a non-blocking notice, which - # let the whole card_game migration ship a stale pkg/. + # WASM artifact freshness is owned by the `web-wasm-rebuild` workflow, + # which rebuilds pkg/ in CI on every master change to a wasm-feeding crate + # and commits it back (CI is the single source of truth — the artifacts + # aren't byte-reproducible on contributor machines). That pkg/ commit then + # triggers this workflow, so the deployed image always ships fresh wasm. + # No drift check is needed here. - name: Log in to Gitea registry uses: docker/login-action@v3 diff --git a/.gitea/workflows/web-wasm-freshness.yml b/.gitea/workflows/web-wasm-freshness.yml deleted file mode 100644 index b800455..0000000 --- a/.gitea/workflows/web-wasm-freshness.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Web WASM Freshness - -# Guards against the committed solitaire_server/web/pkg/ artifacts going stale. -# -# A fresh build on a pinned toolchain is byte-for-byte reproducible (verified), -# so this job rebuilds the artifacts and fails if they differ from what is -# committed. Unlike a changed-files heuristic, this catches *any* drift in the -# wasm-feeding crates — solitaire_core / _engine / _data / _sync / _wasm / _web — -# including gameplay-logic changes that do not alter the JS API surface (e.g. -# the v4->v5 save-schema change that the old heuristic let through as a -# non-blocking "notice", leaving the deployed web build stale for ~3 weeks). -# -# Tool versions are pinned to whatever produced the committed artifacts. When -# you regenerate the artifacts with a newer toolchain, bump these in lockstep: -# rust 1.95.0 · wasm-bindgen 0.2.120 · wasm-pack 0.14.0 · binaryen 130 - -on: - push: - branches: [master] - paths: - - 'solitaire_core/**' - - 'solitaire_engine/**' - - 'solitaire_data/**' - - 'solitaire_sync/**' - - 'solitaire_wasm/**' - - 'solitaire_web/**' - - 'solitaire_server/web/pkg/**' - - 'Cargo.toml' - - 'Cargo.lock' - - 'build_wasm.sh' - - '.gitea/workflows/web-wasm-freshness.yml' - pull_request: - paths: - - 'solitaire_core/**' - - 'solitaire_engine/**' - - 'solitaire_data/**' - - 'solitaire_sync/**' - - 'solitaire_wasm/**' - - 'solitaire_web/**' - - 'solitaire_server/web/pkg/**' - - 'Cargo.toml' - - 'Cargo.lock' - - 'build_wasm.sh' - - '.gitea/workflows/web-wasm-freshness.yml' - workflow_dispatch: - -jobs: - freshness: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Rust 1.95.0 (pinned for reproducible wasm output) - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.95.0 - targets: wasm32-unknown-unknown - - - name: Cache cargo build - uses: Swatinem/rust-cache@v2 - - - name: Install wasm-bindgen-cli + wasm-pack (pinned) - uses: taiki-e/install-action@v2 - with: - tool: wasm-bindgen-cli@0.2.120,wasm-pack@0.14.0 - - - name: Install binaryen 130 (wasm-opt, pinned) - run: | - set -euo pipefail - curl -sSL \ - https://github.com/WebAssembly/binaryen/releases/download/version_130/binaryen-version_130-x86_64-linux.tar.gz \ - | tar xz - echo "$PWD/binaryen-version_130/bin" >> "$GITHUB_PATH" - - - name: Rebuild WASM artifacts - run: ./build_wasm.sh - - - name: Fail if committed pkg differs from a fresh build - run: | - set -euo pipefail - if [ -z "$(git status --porcelain -- solitaire_server/web/pkg/)" ]; then - echo "pkg/ artifacts match a fresh build." - else - echo "::error::solitaire_server/web/pkg/ is stale — a wasm-feeding crate changed without regenerating the artifacts." - echo "Fix: run ./build_wasm.sh and commit the updated solitaire_server/web/pkg/." - git status --porcelain -- solitaire_server/web/pkg/ - git diff --stat -- solitaire_server/web/pkg/ - exit 1 - fi diff --git a/.gitea/workflows/web-wasm-rebuild.yml b/.gitea/workflows/web-wasm-rebuild.yml new file mode 100644 index 0000000..7d54aae --- /dev/null +++ b/.gitea/workflows/web-wasm-rebuild.yml @@ -0,0 +1,89 @@ +name: Web WASM Rebuild + +# CI is the single source of truth for solitaire_server/web/pkg/. +# +# The wasm artifacts cannot be reproduced byte-for-byte on an arbitrary +# contributor machine: even with identical rustc 1.95.0 / LLVM 22.1.2, the same +# flags, the same Cargo.lock and remapped source paths, the output still differs +# by host environment. So rather than police freshness with a rebuild-and-diff +# gate (which false-failed for exactly that reason), CI rebuilds the artifacts +# itself on every master change to a wasm-feeding crate and commits them back. +# +# Result: the deployed pkg/ can't silently rot, and contributors never need to +# run build_wasm.sh by hand. The commit touches only pkg/, which is not in this +# workflow's trigger paths (so it does not re-trigger here) but does match +# docker-build's, so the refreshed wasm deploys. + +on: + push: + branches: [master] + paths: + - 'solitaire_core/**' + - 'solitaire_engine/**' + - 'solitaire_data/**' + - 'solitaire_sync/**' + - 'solitaire_wasm/**' + - 'solitaire_web/**' + - 'Cargo.toml' + - 'Cargo.lock' + - 'build_wasm.sh' + - '.gitea/workflows/web-wasm-rebuild.yml' + workflow_dispatch: + +concurrency: + group: web-wasm-rebuild + cancel-in-progress: false + +jobs: + rebuild: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.CI_TOKEN }} + + - name: Install Rust 1.95.0 + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.95.0 + targets: wasm32-unknown-unknown + + - name: Cache cargo build + uses: Swatinem/rust-cache@v2 + + - name: Install wasm-bindgen-cli + wasm-pack (pinned) + uses: taiki-e/install-action@v2 + with: + tool: wasm-bindgen-cli@0.2.120,wasm-pack@0.14.0 + + - name: Install binaryen 130 (wasm-opt, pinned) + run: | + set -euo pipefail + curl -sSL \ + https://github.com/WebAssembly/binaryen/releases/download/version_130/binaryen-version_130-x86_64-linux.tar.gz \ + | tar xz + echo "$PWD/binaryen-version_130/bin" >> "$GITHUB_PATH" + + - name: Rebuild WASM artifacts + run: ./build_wasm.sh + + - name: Commit refreshed artifacts if changed + run: | + set -euo pipefail + if git diff --quiet -- solitaire_server/web/pkg/; then + echo "pkg/ already up to date — nothing to commit." + exit 0 + fi + git config user.email "ci@gitea.local" + git config user.name "Gitea CI" + git add solitaire_server/web/pkg/ + git commit -m "chore(web): regenerate wasm artifacts" + # master is unprotected; retry once if the tip moved under us. + git push origin HEAD:master || { + git fetch origin master + git rebase origin/master + git push origin HEAD:master + }