-
Notifications
You must be signed in to change notification settings - Fork 25
292 lines (266 loc) · 14.7 KB
/
Copy pathbuild-release.yml
File metadata and controls
292 lines (266 loc) · 14.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
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/.)
#
# Releases: every master push replaces the rolling `latest` prerelease; bumping
# gecko.js/package.json "version" on master also tags + publishes v<version>;
# pushing a v* tag by hand still publishes a release for that tag.
#
# !!! 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) spikes hard, so we
# add a best-effort swapfile below. Engine LTO is OFF by default: the libxul
# LTO link needs >54 GiB (it OOM-killed an 8-core/31 GiB runner even with
# ThinLTO + a --thinlto-jobs cap + 23 GiB swap). Re-enable only on a ~64 GiB+
# host with `make ... LTO=1` (see mozconfig + Makefile LTO knob).
# - 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 path here runs optimized + wasm-opt but
# WITHOUT engine LTO (LTO doesn't fit this runner — see above).
on:
push:
branches: ['master']
tags: ['v*']
workflow_dispatch:
permissions:
contents: write # create releases / upload assets / push tags
# One build at a time per ref; a new master push supersedes the running one
# (the build is hours long on this runner -- don't queue stale builds).
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
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
# Release naming + auto-tag decision. Artifacts are named after the pushed
# tag, or after the gecko.js package version for branch pushes (which feed
# the rolling `latest` prerelease + the auto version release below).
# `new_version` is true when no v<version> tag exists yet -- bumping
# gecko.js/package.json "version" on master is what cuts a versioned release.
- name: Compute the release version/tag
id: version
run: |
PKG_VERSION=$(sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' gecko.js/package.json | head -1)
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
TAG="${GITHUB_REF_NAME}"
else
TAG="v${PKG_VERSION}"
fi
if git ls-remote --exit-code --tags origin "refs/tags/v${PKG_VERSION}" >/dev/null 2>&1; then
NEW_VERSION=false
else
NEW_VERSION=true
fi
echo "version=${PKG_VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
echo ">> version=${PKG_VERSION} tag=${TAG} new_version=${NEW_VERSION}"
# - 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
# Host toolchain + misc build deps. `clang`/`clang++` here only serve
# mozconfig's HOST_CC/HOST_CXX (host-side codegen tools) -- their version
# is irrelevant. The bindgen libclang is installed separately below.
sudo apt-get install -y --no-install-recommends \
clang llvm-dev libpulse-dev python3 mercurial unzip zstd rsync \
wget lsb-release gnupg software-properties-common
# bindgen (gecko-profiler + other rust crates) parses emscripten 6.0.1's
# libc++ headers, which are LLVM 23 and use clang>=19 builtins: __is_convertible
# (older clang errors "'_Tp' does not refer to a value") and __builtin_ctzg/
# __builtin_clzg ("use of undeclared identifier"). Ubuntu's stock libclang is
# 18 -- too old -- so bindgen fails with "Unable to generate bindings" and the
# whole engine build dies at force-cargo-library-build long before libxul links.
# Install a matching-era libclang (21, same as mach-bootstrap's clang, which is
# what works locally) from apt.llvm.org and point bindgen at it via LIBCLANG_PATH.
LLVM_VER=21
wget -qO /tmp/llvm.sh https://apt.llvm.org/llvm.sh
chmod +x /tmp/llvm.sh
sudo /tmp/llvm.sh ${LLVM_VER}
sudo apt-get install -y --no-install-recommends libclang-${LLVM_VER}-dev
# mozconfig.full.emscripten reads $LIBCLANG_PATH for --with-libclang-path.
echo "LIBCLANG_PATH=/usr/lib/llvm-${LLVM_VER}/lib" >> "$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
# Memory headroom safety net. Engine LTO is OFF by default (it needs >54 GiB and
# OOM-killed this 8-core/~31 GiB runner), so the big remaining spike is wasm-opt
# -O3 over the ~250 MB module at the final relink. Adding swap lets a peak above
# RAM spill instead of getting OOM-killed (SIGTERM / exit 143). Fully best-effort
# -- never fails the job (a runner may already have swap, lack disk, or forbid swapon).
- name: Add swap headroom (best-effort)
run: |
if swapon --show 2>/dev/null | grep -q .; then
echo ">> swap already present:"; swapon --show; free -h; exit 0
fi
SW=/swapfile
( sudo fallocate -l 24G "$SW" || sudo dd if=/dev/zero of="$SW" bs=1M count=24576 ) 2>/dev/null \
&& sudo chmod 600 "$SW" && sudo mkswap "$SW" >/dev/null 2>&1 && sudo swapon "$SW" 2>/dev/null \
&& echo ">> added 24G swap at $SW" || echo "!! could not add swap (continuing without it)"
free -h || true
# 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: zstd + wasm-opt, no engine LTO) — long"
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/relink-engine-r.sh, run between two
# `mach build` passes) -- handled inside `make`, no extra step here.
# Engine LTO is intentionally OFF (LTO=1 would need a ~64 GiB+ runner; this one
# OOM-kills the LTO link). RELEASE still gives -O3 + wasm-opt over the module.
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-${{ steps.version.outputs.tag }}.tar.gz gecko.js
ls -la gecko.js-*.tar.gz
- name: Upload build artifact (always)
uses: actions/upload-artifact@v4
with:
name: gecko.js-${{ steps.version.outputs.tag }}
path: gecko.js-*.tar.gz
if-no-files-found: error
# Build the chrome-demo as a static Vite site (the full Firefox front-end demo).
# Runs AFTER the gecko.js artifact upload so that primary artifact is published
# even if the demo build trips. `make chrome-assets` stages the GRE resource set
# from the engine objdir via rsync into public/chrome-assets.tar.zst; the vite
# build then emits the served gecko.wasm[.zst]. GECKO_OBJDIR points the staging
# at the RELEASE objdir (its default is the local debug objdir). gecko.js
# (workspace dep) + node_modules already exist from `make libxul` above.
# NOTE: chrome-demo/dist is a static site that still must be served
# cross-origin-isolated (COOP/COEP) WITH a WISP proxy at /wisp (the demo's
# dev/preview server provides both; a plain static host does not).
- name: Build the chrome-demo (static Vite build)
run: |
export GECKO_OBJDIR="${{ github.workspace }}/obj-full-emscripten-release"
make chrome-assets
VITE_PUTER_BRANDING=1 pnpm --filter chrome-demo build
tar -C demo/chrome -czf chrome-demo-${{ steps.version.outputs.tag }}.tar.gz dist
ls -la chrome-demo-*.tar.gz
- name: Upload chrome-demo artifact (always)
uses: actions/upload-artifact@v4
with:
name: chrome-demo-${{ steps.version.outputs.tag }}
path: chrome-demo-*.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
chrome-demo-*.tar.gz
generate_release_notes: true
# --- rolling `latest` + auto version tag (master pushes) ---------------
# anuraOS-style continuous releases: every master build replaces the
# `latest` prerelease (delete + recreate so the tag always points at this
# commit), and bumping gecko.js/package.json "version" additionally cuts a
# permanent v<version> tag + release from the same artifacts.
- name: Delete the previous rolling release + tag
if: github.ref == 'refs/heads/master'
uses: dev-drprasad/delete-tag-and-release@v1.0.1
with:
delete_release: true
tag_name: latest
github_token: ${{ github.token }}
- name: Publish the rolling `latest` prerelease
if: github.ref == 'refs/heads/master'
uses: ncipollo/release-action@v1
with:
name: Continuous Build
tag: latest
commit: ${{ github.sha }}
body: "${{ github.event.head_commit.url }} ${{ github.event.head_commit.message }}"
artifacts: 'gecko.js-*.tar.gz,chrome-demo-*.tar.gz'
prerelease: true
- name: Tag + release ${{ steps.version.outputs.tag }} (version bumped)
if: github.ref == 'refs/heads/master' && steps.version.outputs.new_version == 'true'
uses: ncipollo/release-action@v1
with:
name: ${{ steps.version.outputs.version }}
tag: v${{ steps.version.outputs.version }}
commit: ${{ github.sha }}
body: "${{ github.event.head_commit.url }} ${{ github.event.head_commit.message }}"
artifacts: 'gecko.js-*.tar.gz,chrome-demo-*.tar.gz'
generateReleaseNotes: true