Skip to content
Merged
Show file tree
Hide file tree
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
28 changes: 27 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,15 @@ jobs:
wasm-build:
name: wasm-build
runs-on: ubuntu-latest
# Pinned binaryen version — see docs/release-plan.md
# ("Pinned binaryen / wasm-opt") for the bump procedure. Older
# binaryen rejects rustc's multi-table WASM output with
# "Only 1 table definition allowed in MVP", which the runner
# image's cache state used to surface non-deterministically
# (SQLR-58). Pinning keeps wasm-opt out of "whatever's cached"
# territory.
env:
BINARYEN_VERSION: version_122
steps:
- uses: actions/checkout@v4

Expand All @@ -313,12 +322,29 @@ jobs:
workspaces: sdk/wasm
shared-key: wasm-build

- name: Install pinned binaryen (wasm-opt)
# MUST run before wasm-pack: wasm-pack picks up wasm-opt from
# PATH if present, otherwise downloads whatever binaryen its
# own internal cache happens to have. Pinning + prepending
# to PATH forces a deterministic version across runner images.
run: |
set -euo pipefail
curl -fsSL "https://github.com/WebAssembly/binaryen/releases/download/${BINARYEN_VERSION}/binaryen-${BINARYEN_VERSION}-x86_64-linux.tar.gz" \
-o "$RUNNER_TEMP/binaryen.tar.gz"
tar -xzf "$RUNNER_TEMP/binaryen.tar.gz" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/binaryen-${BINARYEN_VERSION}/bin" >> "$GITHUB_PATH"
"$RUNNER_TEMP/binaryen-${BINARYEN_VERSION}/bin/wasm-opt" --version

- name: Install wasm-pack
uses: jetli/wasm-pack-action@v0.4.0

- name: wasm-pack build --target web --release
working-directory: sdk/wasm
run: wasm-pack build --target web --release
run: |
# Sanity-check that the pinned wasm-opt is what wasm-pack sees.
which wasm-opt
wasm-opt --version
wasm-pack build --target web --release

- name: Report .wasm size
# Surfaces size regressions in PR logs. Not a hard limit yet;
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,14 @@ jobs:
if: needs.detect.outputs.should_release == 'true'
runs-on: ubuntu-latest
environment: release
# Pinned binaryen version — see docs/release-plan.md
# ("Pinned binaryen / wasm-opt") for the bump procedure. Older
# binaryen rejects rustc's multi-table WASM output with
# "Only 1 table definition allowed in MVP" (SQLR-58). The
# release pipeline can't tolerate that flake — a failed
# publish-wasm leaves the rest of the release wave inconsistent.
env:
BINARYEN_VERSION: version_122
permissions:
# OIDC: required for npm trusted-publisher token exchange.
# Same flow proven in publish-nodejs after the v0.1.5–0.1.7
Expand All @@ -1207,6 +1215,20 @@ jobs:
shared-key: publish-wasm
workspaces: 'sdk/wasm -> target'

- name: Install pinned binaryen (wasm-opt)
# MUST run before wasm-pack: wasm-pack picks up wasm-opt
# from PATH if present, otherwise downloads whatever
# binaryen its own internal cache happens to have. Pinning
# + prepending to PATH forces a deterministic version
# across runner images.
run: |
set -euo pipefail
curl -fsSL "https://github.com/WebAssembly/binaryen/releases/download/${BINARYEN_VERSION}/binaryen-${BINARYEN_VERSION}-x86_64-linux.tar.gz" \
-o "$RUNNER_TEMP/binaryen.tar.gz"
tar -xzf "$RUNNER_TEMP/binaryen.tar.gz" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/binaryen-${BINARYEN_VERSION}/bin" >> "$GITHUB_PATH"
"$RUNNER_TEMP/binaryen-${BINARYEN_VERSION}/bin/wasm-opt" --version

# Install wasm-pack — the canonical tool for building +
# packaging Rust crates as npm-publishable WASM modules.
# `cargo binstall` would be faster than `cargo install`
Expand Down Expand Up @@ -1239,6 +1261,9 @@ jobs:
- name: Build WASM package
working-directory: sdk/wasm
run: |
# Sanity-check that the pinned wasm-opt is on PATH (SQLR-58).
which wasm-opt
wasm-opt --version
wasm-pack build --release --target bundler --scope joaoh82
echo "--- generated pkg/ contents ---"
ls -la pkg/
Expand Down
63 changes: 62 additions & 1 deletion docs/release-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,10 @@ The "publish" half. Auto-fires on the release commit.
GitHub Release `sqlrite-node-vX.Y.Z`.
- **publish-wasm** — `wasm-pack build --target bundler --release`,
then `wasm-pack publish` via OIDC. Creates
`sqlrite-wasm-vX.Y.Z` GitHub Release.
`sqlrite-wasm-vX.Y.Z` GitHub Release. Installs a
[pinned binaryen / wasm-opt](#pinned-binaryen--wasm-opt) before
invoking `wasm-pack`, so the published bundle is byte-stable
across runner image cache states.
- **publish-go** — nothing to build on the Go side. Verifies
`sdk/go/vX.Y.Z` was pushed correctly by `tag-all`. Pulls the
per-platform `libsqlrite_c` tarballs produced by
Expand Down Expand Up @@ -285,6 +288,64 @@ The "publish" half. Auto-fires on the release commit.
exist (because the real release happened weeks ago). Workflow
aborts with a clear "tag already exists" error. No damage.

## Pinned binaryen / wasm-opt

The WASM build paths in `ci.yml` (`wasm-build`) and `release.yml`
(`publish-wasm`) both install a **pinned version of binaryen**
(which provides `wasm-opt`) before invoking `wasm-pack`. The pin
lives in a `BINARYEN_VERSION` job-level `env:` in each workflow.

**Current pin: `version_122`** (released Feb 2025).

### Why this exists (SQLR-58)

`wasm-pack` invokes `wasm-opt` to size-optimize the published
bundle. If `wasm-opt` is already on `PATH`, `wasm-pack` uses that
one; otherwise it downloads its own copy into a per-runner cache.
That cache is keyed on the runner image and survives across
images opaquely — which means CI was getting whatever binaryen
the cache happened to hold. When the cached copy was old enough
to predate multi-table WASM support, `wasm-opt` would reject
recent rustc output with:

> `[parse exception: Only 1 table definition allowed in MVP]`
> `Fatal: error in parsing input`
> `Error: failed to execute wasm-opt: exited with exit code: 1`

The failure was non-deterministic (re-runs frequently passed,
because the new image had a different cache state), but it broke
the release pipeline at least once before [PR #135](https://github.com/joaoh82/rust_sqlite/pull/135).
Pinning binaryen + prepending it to `PATH` forces `wasm-pack`
to always see the same `wasm-opt`, regardless of runner state.

### Bump procedure

1. Look at the [binaryen releases page](https://github.com/WebAssembly/binaryen/releases)
and pick a recent stable version (avoid release candidates).
2. Locally, download that release's `x86_64-linux` tarball and
run `wasm-opt --version` to confirm it builds. Optional but
nice: also run `wasm-pack build --target web --release` in
`sdk/wasm` with the new `wasm-opt` on `PATH` and confirm the
`.wasm` artifact size is in the same ballpark as before
(regressions > 10% are worth investigating).
3. Update `BINARYEN_VERSION` in both `.github/workflows/ci.yml`
(job `wasm-build`) and `.github/workflows/release.yml` (job
`publish-wasm`). Keep the two in lockstep — a divergence
means CI and release produce subtly different artifacts.
4. Update the "Current pin" line above to match.
5. PR + merge. CI will exercise the new version on the WASM
build job before the release pipeline ever sees it.

### Why a tarball, not apt-get

Ubuntu's apt-packaged `binaryen` is reliably 1–2 years behind
upstream and pinning it requires the matching apt index, which is
itself unstable across runner image refreshes. The official
WebAssembly/binaryen GitHub release tarballs are stable URLs and
sha-pinnable (we don't currently verify the sha256 — a follow-up
if supply-chain integrity becomes a concern; the tarball is
short-lived and contained to the runner).

## Secrets / one-time setup

With lockstep + OIDC-based trusted publishing, the only long-lived
Expand Down
Loading