mirror of
https://github.com/HeyPuter/node.git
synced 2026-11-13 18:34:30 -10:00
node-worker's prepare-node.js ends by running the Rust crate's build.sh, which wants cargo and a wasm-bindgen CLI matching the crate version exactly. The runner has cargo but not wasm-bindgen, so the build died an hour in, after the emsdk and Node work was already paid for. Set up ahead of that build, with the version read out of build.sh so it cannot drift. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
170 lines
7.8 KiB
YAML
170 lines
7.8 KiB
YAML
name: Continuous build
|
|
|
|
on:
|
|
push:
|
|
branches: [master]
|
|
workflow_dispatch:
|
|
|
|
# The `continuous` tag is recreated on every run, so two runs must not overlap: the loser of the
|
|
# race would leave its assets attached to the other one's commit. Newer wins — a build of an older
|
|
# commit has nothing left to say once a newer one is underway.
|
|
concurrency:
|
|
group: continuous
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
# Dominated by the node-worker build below, which is long enough that the default limit is not
|
|
# obviously sufficient.
|
|
timeout-minutes: 180
|
|
|
|
steps:
|
|
# Into a subdirectory rather than the workspace root: `node-worker` is a `link:../node-worker`
|
|
# dependency, so it has to end up *beside* this checkout, and actions/checkout cannot write
|
|
# outside the workspace.
|
|
- name: Check out the app
|
|
uses: actions/checkout@v5
|
|
with:
|
|
path: app
|
|
|
|
- name: Check out node-worker
|
|
uses: actions/checkout@v5
|
|
with:
|
|
repository: heyputer/node-worker
|
|
path: node-worker
|
|
# GITHUB_TOKEN can read a public repository, which is all this needs. The secret is the
|
|
# fallback for as long as node-worker is still private: GITHUB_TOKEN is scoped to this
|
|
# repository alone and cannot read another private one.
|
|
token: ${{ secrets.NODE_WORKER_TOKEN || github.token }}
|
|
|
|
# An Emscripten toolchain, a full Node checkout and the object files from a wasm link of Node
|
|
# do not comfortably fit alongside everything the runner image ships with. These two are the
|
|
# largest trees this build will never touch.
|
|
- name: Reclaim runner disk
|
|
run: sudo rm -rf /usr/share/dotnet /usr/local/lib/android
|
|
|
|
# Pinned rather than read from a `packageManager` field, which this repo does not set. Both
|
|
# lockfiles are lockfileVersion 9, so one pnpm serves both checkouts.
|
|
- name: Set up pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
version: 11.3.0
|
|
|
|
- name: Set up Node
|
|
uses: actions/setup-node@v5
|
|
with:
|
|
node-version: 24
|
|
|
|
# node-worker's build also compiles a Rust crate to wasm, via
|
|
# src/worker/node-rust/wasm/build.sh. That script wants cargo and a wasm-bindgen CLI whose
|
|
# version matches the crate's exactly — it checks, and refuses to run on a mismatch.
|
|
#
|
|
# Ahead of the node-worker build rather than folded into it, because build.sh runs at the
|
|
# *end* of prepare-node.js: a toolchain problem would otherwise surface an hour in, after the
|
|
# emsdk and Node work is already done and paid for.
|
|
- name: Set up Rust for the rewriter wasm build
|
|
run: |
|
|
# Read the version out of build.sh's own check rather than restating it here, so this
|
|
# cannot drift from whatever the sibling repo demands at the commit being built.
|
|
build_sh=node-worker/src/worker/node-rust/wasm/build.sh
|
|
version="$(sed -n 's/^WBG="wasm-bindgen \([0-9.]*\)"$/\1/p' "$build_sh")"
|
|
if [ -z "$version" ]; then
|
|
echo "::error::could not read the required wasm-bindgen version out of $build_sh"
|
|
exit 1
|
|
fi
|
|
echo "build.sh wants wasm-bindgen $version"
|
|
|
|
# nightly with rust-src, because build.sh builds std from source (`-Z build-std`) and
|
|
# passes unstable rustc flags. The crate's rust-toolchain.toml asks for exactly this;
|
|
# installing it up front beats leaving rustup to fetch it lazily mid-build.
|
|
rustup toolchain install nightly --profile minimal \
|
|
--component rust-src --target wasm32-unknown-unknown
|
|
|
|
# The prebuilt binary: `cargo install wasm-bindgen-cli` would compile it from source for
|
|
# several minutes to reach the same place.
|
|
tarball="wasm-bindgen-$version-x86_64-unknown-linux-musl"
|
|
base="https://github.com/wasm-bindgen/wasm-bindgen/releases/download/$version"
|
|
curl -sSfL -O "$base/$tarball.tar.gz" -O "$base/$tarball.tar.gz.sha256sum"
|
|
# Compared by hand rather than with `sha256sum -c`, whose input names a path that only
|
|
# ever existed on the machine that cut the release.
|
|
expected="$(awk '{print $1}' "$tarball.tar.gz.sha256sum")"
|
|
actual="$(sha256sum "$tarball.tar.gz" | awk '{print $1}')"
|
|
if [ "$expected" != "$actual" ]; then
|
|
echo "::error::wasm-bindgen $version checksum mismatch: expected $expected, got $actual"
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$HOME/.local/bin"
|
|
tar -xzf "$tarball.tar.gz" --strip-components=1 \
|
|
-C "$HOME/.local/bin" "$tarball/wasm-bindgen"
|
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
|
|
# `$GITHUB_PATH` only takes effect in later steps, so this is the first point at which the
|
|
# two binaries build.sh looks for can be confirmed to resolve. Cheap, and it fails here
|
|
# instead of at the very end of a long build.
|
|
- name: Check cargo and wasm-bindgen resolve
|
|
run: |
|
|
cargo --version
|
|
wasm-bindgen --version
|
|
|
|
# The long pole by a wide margin: clones emsdk and the Node v25.9.0 source, builds OpenSSL's
|
|
# libcrypto with emcc, links Node itself to wasm, and finally builds the Rust rewriter above.
|
|
# Deliberately uncached — see the note at the end of this file.
|
|
- name: Build node-worker
|
|
working-directory: node-worker
|
|
run: |
|
|
pnpm install --frozen-lockfile
|
|
pnpm build
|
|
|
|
- name: Install
|
|
working-directory: app
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
# `prebuild` bakes the project templates first, fetching create-vite and its dependency tree
|
|
# from the npm registry, so this needs network as well as CPU.
|
|
- name: Build
|
|
working-directory: app
|
|
run: pnpm build
|
|
|
|
# `-C app dist` rather than `-C app/dist .`, so every entry in the archive is prefixed
|
|
# `dist/`. Asserted rather than assumed: getting this wrong produces an archive that looks
|
|
# fine until it is unpacked over somebody's web root.
|
|
- name: Package dist
|
|
run: |
|
|
tar -czf dist.tar.gz -C app dist
|
|
tar -tzf dist.tar.gz | grep -qx 'dist/index.html'
|
|
|
|
# Deleted and recreated rather than edited, because the tag has to move to this commit and
|
|
# `gh release edit --target` will not move a tag that already exists. `--repo` throughout
|
|
# because the workspace root is not itself a git checkout.
|
|
- name: Publish the continuous release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
# Read from the checkout rather than from `github.event`, which has no commit message on
|
|
# a workflow_dispatch run, and passed to printf as an argument so nothing in it is
|
|
# interpreted as shell.
|
|
subject="$(git -C app log -1 --pretty=%s)"
|
|
{
|
|
printf 'Built from `%s` — %s\n\n' "$GITHUB_SHA" "$subject"
|
|
printf '`dist.tar.gz` unpacks to `dist/`.\n\n'
|
|
printf 'The build uses a relative base, so it can be served from any path — `/`,\n'
|
|
printf '`/labs/node/`, anywhere — provided the URL it is served at ends in a slash.\n'
|
|
} > notes.md
|
|
|
|
gh release delete continuous --repo "$GITHUB_REPOSITORY" --cleanup-tag --yes || true
|
|
gh release create continuous dist.tar.gz \
|
|
--repo "$GITHUB_REPOSITORY" \
|
|
--target "$GITHUB_SHA" \
|
|
--prerelease \
|
|
--title "Continuous build" \
|
|
--notes-file notes.md
|
|
|
|
# On caching: nothing here is cached, so every run pays for the full node-worker build. The
|
|
# alternative is caching `node-worker/{dist,emsdk,node_core}` keyed on that repo's commit SHA,
|
|
# which would turn most runs into a cache restore. Worth revisiting if the wait starts to hurt.
|