Skip to content

build-and-release

build-and-release #13

Workflow file for this run

name: build-and-release
# Builds the full Gecko->wasm engine and the gecko.js package, then attaches the
# packaged library (dist/gecko.js + dist/gecko.wasm + the wasm/ engine artifacts)
# to a GitHub Release. (embed-xul/ + embed-chrome/ are legacy stubs and are NOT built
# here -- the maintained package is gecko.js/.)
#
# !!! HEAVY BUILD β€” READ FIRST !!!
# A full Gecko->wasm build is enormous: ~26 GB objdir, a ~3.4 GB (relocatable) libxul.so,
# a ~245 MB gecko.wasm, and 25-50 min on 12 cores. On a standard hosted runner
# (4 vCPU / 16 GB RAM / ~14 GB free disk) this is marginal on ALL THREE axes:
# - disk: we free ~30 GB below; still tight. Prefer a larger/self-hosted runner.
# - ram: the final emcc relink (wasm-opt over the 245 MB module) + engine LTO link
# spike past 16 GB and OOM-kill, so we add a 32 GB swapfile on /mnt below.
# - time: on 4 vCPU expect ~2-4 h (job cap is 6 h).
# If it doesn't fit, switch `runs-on` to a larger runner (e.g. a self-hosted one
# or GitHub larger runners) β€” the steps are otherwise unchanged.
#
# This workflow is a STARTING POINT; the local upgrade to emscripten 6.0.1 was
# verified with a DEBUG build (the RELEASE/LTO path here is not yet CI-verified).
on:
push:
tags: ['v*']
workflow_dispatch:
permissions:
contents: write # create releases / upload assets
env:
EMSDK_VERSION: '6.0.1' # emcc 6.0.1 / clang 23 (bundles binaryen v130)
RUST_VERSION: '1.95.0'
PNPM_VERSION: '9.12.0' # matches root package.json "packageManager"
MOZBUILD_STATE_PATH: ${{ github.workspace }}/.mozbuild
MACH_NO_TERMINAL_FOOTER: '1'
jobs:
build:
runs-on: firefox
timeout-minutes: 350
steps:
- name: Checkout the embedder + build harness (this repo)
uses: actions/checkout@v4
# - name: Free up disk space (Gecko objdir is ~26 GB)
# uses: jlumbroso/free-disk-space@main
# with:
# tool-cache: true
# android: true
# dotnet: true
# haskell: true
# large-packages: true
# swap-storage: true
- name: Install build prerequisites (clang/libclang for bindgen, etc.)
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang llvm-dev libclang-dev libpulse-dev python3 mercurial unzip zstd
# mozconfig.full.emscripten reads $LIBCLANG_PATH for --with-libclang-path.
echo "LIBCLANG_PATH=$(llvm-config --libdir)" >> "$GITHUB_ENV"
# Install emscripten from a FRESH emsdk clone (its bundled tags know 6.0.x; an
# older/cached emsdk may not). em_config reads $EMSDK and resolves binaryen at
# $EMSDK/upstream -- emsdk 6.0.1 bundles binaryen v130, which accepts the wasm
# features rust/LLVM emit, so NO separate binaryen install is needed anymore.
- name: Set up emscripten ${{ env.EMSDK_VERSION }}
run: |
git clone --depth 1 https://github.com/emscripten-core/emsdk.git "$HOME/emsdk"
"$HOME/emsdk/emsdk" install "${EMSDK_VERSION}"
"$HOME/emsdk/emsdk" activate "${EMSDK_VERSION}"
echo "EMSDK=$HOME/emsdk" >> "$GITHUB_ENV"
echo "$HOME/emsdk/upstream/emscripten" >> "$GITHUB_PATH"
echo "$HOME/emsdk" >> "$GITHUB_PATH"
"$HOME/emsdk/upstream/emscripten/emcc" --version | head -1
"$HOME/emsdk/upstream/bin/wasm-opt" --version | head -1
- name: Set up Rust ${{ env.RUST_VERSION }} (+ rust-src, wasm target)
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
components: rust-src
targets: wasm32-unknown-emscripten
# gecko.js's package build (rspack bundle) runs under pnpm; the repo pins
# pnpm via package.json "packageManager", so let corepack provide it.
- name: Set up Node + pnpm ${{ env.PNPM_VERSION }}
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Enable corepack/pnpm
run: |
corepack enable
corepack prepare "pnpm@${PNPM_VERSION}" --activate
pnpm --version
# em_config is committed and portable (reads $EMSDK; binaryen defaults to the
# emsdk's bundled $EMSDK/upstream), so no shim or config generation is needed.
- name: Clone the pinned Gecko engine fork (depth 1)
run: make firefox
# No path rewriting needed: mozconfig.full.emscripten derives its objdir and
# FreeType include from $topsrcdir, and libclang from $LIBCLANG_PATH (set above).
- name: "Build the engine + gecko.js (release: LTO + zstd; wasm-opt TEMPORARILY disabled) β€” long"
env:
NO_WASM_OPT: '1' # TEMP: skip the whole-module wasm-opt pass (OOMs/SIGSEGVs in CI); remove to re-enable
run: |
# wasm-opt's recursive passes overflow the default 8MB stack on the
# ~250MB module and die with SIGSEGV. Raise the stack limit for the whole
# make -> mach -> em++ -> wasm-opt subprocess tree (ulimit is inherited).
# Also needed for the libxul.so relink: emscripten 6.0.1's wasm-ld SIGSEGVs
# on the `-shared` link, so `make build` relinks the engine libs as `-r`
# relocatable objects (gecko.js/build/relink-engine-r.sh, run between two
# `mach build` passes) -- handled inside `make`, no extra step here.
ulimit -s unlimited || ulimit -s 524288 || true
make libxul RELEASE=1 # firefox cloned; vendor -> mach build (+relink) -> build-lib.sh -> rspack
- name: Show link errors on failure
if: failure()
run: |
echo "=== gecko.js/build/link.err (tail) ==="
tail -n 60 gecko.js/build/link.err 2>/dev/null || echo "(no link.err)"
- name: Package the gecko.js library (the runnable ESM bundle)
run: |
PKG=stage/gecko.js
mkdir -p "$PKG"
# The runnable/publishable artifact is dist/ (the ESM bundle + the single
# served gecko.wasm[.zst]) + package.json. The wasm/ dir is build intermediates
# -- gecko.js/worker.js/gecko.data are INLINED into dist/gecko.js, so only
# dist/gecko.wasm[.zst] is served separately; no need to ship wasm/.
cp -r gecko.js/dist gecko.js/package.json "$PKG/"
cp gecko.js/README.md "$PKG/" 2>/dev/null || true
cat > "$PKG/RELEASE.txt" <<'TXT'
gecko.js β€” embeddable Gecko (the Firefox engine) compiled to WebAssembly (emscripten 6.0.1).
The ESM bundle (dist/gecko.js) inlines the glue + gecko.data; consumers serve only the wasm
(dist/gecko.wasm or, in this release build, dist/gecko.wasm.zst β€” decompressed in-browser).
Must be served cross-origin-isolated (COOP: same-origin + COEP: require-corp) for SharedArrayBuffer.
See README / the embed-demo for usage: `new Gecko({ canvas, wispUrl }); await g.init(); g.load(url)`.
TXT
tar -C stage -czf gecko.js-${{ github.ref_name }}.tar.gz gecko.js
ls -la gecko.js-*.tar.gz
- name: Upload build artifact (always)
uses: actions/upload-artifact@v4
with:
name: gecko.js-${{ github.ref_name }}
path: gecko.js-*.tar.gz
if-no-files-found: error
- name: Publish GitHub Release (tag pushes only)
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
files: gecko.js-*.tar.gz
generate_release_notes: true