From 24bd5de509e6228ad461286cf5ce5450a1b93994 Mon Sep 17 00:00:00 2001 From: Florian Fontan Date: Sat, 3 Oct 2026 20:47:39 +0200 Subject: [PATCH] Deploy the web page to Cloudflare Pages New workflow '.github/workflows/wasm.yml': it builds the WebAssembly module with Emscripten, runs 'node --test wasm/tests/', and assembles the site (the page, the module and the Web Worker). Every time the Build workflow succeeds on master, it deploys the site to Cloudflare Pages. Pull requests touching the module, the page or the libraries only build and test it. Cloudflare Pages serves 'wasm/web/_headers', which sets the headers enabling 'SharedArrayBuffer' (GitHub Pages can't set them). --- .github/workflows/wasm.yml | 109 +++++++++++++++++++++++++++++++++++++ wasm/README.md | 8 +++ wasm/web/_headers | 5 ++ 3 files changed, 122 insertions(+) create mode 100644 .github/workflows/wasm.yml create mode 100644 wasm/web/_headers diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml new file mode 100644 index 000000000..2de9254ab --- /dev/null +++ b/.github/workflows/wasm.yml @@ -0,0 +1,109 @@ +name: WebAssembly + +# Builds the WebAssembly module and the web page, runs their tests, and +# deploys the page to Cloudflare Pages every time the Build workflow succeeds +# on master (so a broken master is never deployed). +# +# Pull requests touching the module, the page or the libraries only build and +# test them. +# +# Deployment requires the repository secrets CLOUDFLARE_API_TOKEN (permission +# "Cloudflare Pages: Edit") and CLOUDFLARE_ACCOUNT_ID, and an existing +# Cloudflare Pages project (direct upload) named after the repository variable +# CLOUDFLARE_PAGES_PROJECT, "packingsolver" by default. + +on: + workflow_run: + workflows: ["Build"] + branches: [master] + types: [completed] + pull_request: + branches: [master] + paths: + - "wasm/**" + - "src/**" + - "include/**" + - "extern/**" + - "CMakeLists.txt" + - ".github/workflows/wasm.yml" + workflow_dispatch: + +# Cancel the run of a pull request when a new commit is pushed to it. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.workflow_run.head_sha || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +env: + EMSCRIPTEN_VERSION: "6.0.11" + +jobs: + + build: + # 'workflow_run' only filters on the branch name: also require a push to + # this repository, so that a pull request from a fork's 'master' branch is + # never built and deployed. + if: >- + github.event_name != 'workflow_run' || + (github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.event == 'push' && + github.event.workflow_run.head_repository.full_name == github.repository) + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + ref: ${{ github.event.workflow_run.head_sha || github.sha }} + persist-credentials: false + - name: Set up Emscripten + uses: mymindstorm/setup-emsdk@v14 + with: + version: ${{ env.EMSCRIPTEN_VERSION }} + actions-cache-folder: emsdk-cache + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Build the WebAssembly module + run: | + emcmake cmake -S . -B build_wasm -DCMAKE_BUILD_TYPE=Release -DPACKINGSOLVER_BUILD_WASM=ON + cmake --build build_wasm --target PackingSolver_wasm --parallel 4 + - name: Run the tests + run: node --test wasm/tests/ + - name: Assemble the site + run: | + mkdir site + cp -r wasm/web/. site/ + rm site/serve.py site/package.json + cp build_wasm/wasm/packingsolver.js build_wasm/wasm/packingsolver.wasm site/ + cp wasm/js/packingsolver_worker.js site/ + ls -l site + - name: Upload the site + uses: actions/upload-artifact@v4 + with: + name: site + path: site + if-no-files-found: error + + deploy: + needs: build + if: >- + github.event_name == 'workflow_run' || + (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/master') + runs-on: ubuntu-latest + environment: + name: cloudflare-pages + steps: + - name: Download the site + uses: actions/download-artifact@v5 + with: + name: site + path: site + - name: Deploy to Cloudflare Pages + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy site --project-name=${{ vars.CLOUDFLARE_PAGES_PROJECT || 'packingsolver' }} --branch=master --commit-hash=${{ github.event.workflow_run.head_sha || github.sha }} diff --git a/wasm/README.md b/wasm/README.md index 85c8ea82b..40bf6b722 100644 --- a/wasm/README.md +++ b/wasm/README.md @@ -40,3 +40,11 @@ python3 wasm/web/serve.py ``` Then open http://localhost:8000/. + +## Deployment + +The workflow `.github/workflows/wasm.yml` builds the module and the page and runs the tests. Every time the Build workflow succeeds on `master`, it also deploys the page to [Cloudflare Pages](https://pages.cloudflare.com/), which serves `web/_headers`. GitHub Pages can't set these headers. + +The deployment requires: +- a Cloudflare Pages project created for direct upload, named `packingsolver`, or set the repository variable `CLOUDFLARE_PAGES_PROJECT`; +- the repository secrets `CLOUDFLARE_API_TOKEN` (an API token with the "Cloudflare Pages: Edit" permission) and `CLOUDFLARE_ACCOUNT_ID`. diff --git a/wasm/web/_headers b/wasm/web/_headers new file mode 100644 index 000000000..b856dcba9 --- /dev/null +++ b/wasm/web/_headers @@ -0,0 +1,5 @@ +# Headers of the deployed site (Cloudflare Pages). The threads of the +# WebAssembly module need 'SharedArrayBuffer', which requires them. +/* + Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp