Skip to content

Commit e61bf50

Browse files
authored
feat(ci): nightly unsigned release builds on WarpBuild runners (#1170)
* feat(build): add mini_installer module for unsigned Windows packaging mini_installer.exe was only buildable inside sign_windows (sign binaries -> build installer -> sign installer), so unsigned pipelines had no way to produce the installer package_windows requires. Expose the existing build_mini_installer step as a standalone module. * feat(build): add unsigned CI release configs for all three platforms Variants of the release configs for nightly CI: no sign_* (unsigned artifacts), no upload (GitHub Actions publishes), and no clean/git_setup because CI provisions the pinned checkout itself - git_setup's 'git fetch --tags' would unshallow the CI clone against chromium's ~70k tags. Linux builds x64 (runner arch) instead of the arm64 cross-compile pin in release.linux.yaml. * feat(ci): chromium checkout provisioning and R2 cache helpers setup_chromium.py provisions depot_tools + src at the pinned tag with a depth-2 single-tag fetch (git_setup's full tag fetch is unusable on a shallow clone) and runs the same gclient sync the build expects. r2_cache.py tars the post-sync tree into R2 for Windows runners, where WarpCache is unsupported and actions/cache caps at 10GB. Missing creds or cache misses degrade to a cold checkout instead of failing. * feat(ci): nightly unsigned release builds on WarpBuild runners Daily schedule + dispatch workflow building Linux x64, Windows x64 and macOS arm64 release artifacts unsigned, with the pinned chromium checkout cached post-sync (WarpCache on linux/macos, R2 tarball on windows) keyed on CHROMIUM_VERSION. Artifacts upload per-run and a rolling 'nightly' prerelease is refreshed on scheduled main runs. Signing secrets are documented placeholders only. * fix(ci): resolve pyright findings in nightly build additions - rename validate/execute params in package/windows.py overrides to 'context' to match the CommandModule base signature - drop the dead package_universal stub (no callers) and its now-unused List import - guard Popen.stdout (typed Optional) and the lazy boto3 import with a clear run-via-uv error in r2_cache.py
1 parent 0611a1e commit e61bf50

9 files changed

Lines changed: 950 additions & 17 deletions

File tree

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
name: Nightly Release Build
2+
3+
# Daily UNSIGNED release builds of BrowserOS for all three platforms on
4+
# WarpBuild runners. Signing is intentionally not wired up yet; see
5+
# packages/browseros/build/docs/nightly-warpbuild-ci.md for the design and
6+
# the secret names that enable signing later.
7+
#
8+
# The pinned chromium checkout (packages/browseros/CHROMIUM_VERSION) is
9+
# cached post-`gclient sync`:
10+
# - Linux/macOS: WarpCache (WarpBuilds/cache, no size cap)
11+
# - Windows: R2 tarball via scripts/ci/r2_cache.py (WarpCache does
12+
# not support Windows runners; actions/cache caps at 10GB)
13+
14+
on:
15+
schedule:
16+
# Midnight US Pacific (DST). The signed self-hosted macOS nightly runs
17+
# at 05:00 UTC; keep these apart so version-bump PRs don't race.
18+
- cron: "0 8 * * *"
19+
workflow_dispatch:
20+
inputs:
21+
platforms:
22+
description: Platforms to build
23+
type: choice
24+
default: all
25+
options:
26+
- all
27+
- linux
28+
- windows
29+
- macos
30+
publish_nightly:
31+
description: Update the rolling `nightly` prerelease (main only)
32+
type: boolean
33+
default: false
34+
35+
permissions:
36+
contents: read
37+
38+
concurrency:
39+
group: nightly-release
40+
cancel-in-progress: false
41+
42+
jobs:
43+
plan:
44+
runs-on: ubuntu-latest
45+
outputs:
46+
matrix: ${{ steps.plan.outputs.matrix }}
47+
publish: ${{ steps.plan.outputs.publish }}
48+
steps:
49+
- name: Resolve build matrix and publish flag
50+
id: plan
51+
env:
52+
EVENT_NAME: ${{ github.event_name }}
53+
PLATFORMS: ${{ inputs.platforms || 'all' }}
54+
PUBLISH_INPUT: ${{ inputs.publish_nightly }}
55+
REF_NAME: ${{ github.ref_name }}
56+
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
57+
run: |
58+
set -euo pipefail
59+
60+
matrix='[
61+
{"platform":"linux","arch":"x64","runner":"warp-ubuntu-2204-x64-32x","config":"release.linux.ci.yaml","timeout":660},
62+
{"platform":"windows","arch":"x64","runner":"warp-windows-2025-x64-32x","config":"release.windows.ci.yaml","timeout":780},
63+
{"platform":"macos","arch":"arm64","runner":"warp-macos-15-arm64-12x","config":"release.macos.arm64.ci.yaml","timeout":720}
64+
]'
65+
if [ "$PLATFORMS" != "all" ]; then
66+
matrix="$(jq -c --arg p "$PLATFORMS" '[ .[] | select(.platform == $p) ]' <<<"$matrix")"
67+
else
68+
matrix="$(jq -c . <<<"$matrix")"
69+
fi
70+
71+
publish="false"
72+
if [ "$REF_NAME" = "$DEFAULT_BRANCH" ]; then
73+
if [ "$EVENT_NAME" = "schedule" ] || [ "$PUBLISH_INPUT" = "true" ]; then
74+
publish="true"
75+
fi
76+
fi
77+
78+
{
79+
echo "matrix=$matrix"
80+
echo "publish=$publish"
81+
} >> "$GITHUB_OUTPUT"
82+
83+
build:
84+
needs: plan
85+
strategy:
86+
fail-fast: false
87+
matrix:
88+
include: ${{ fromJSON(needs.plan.outputs.matrix) }}
89+
name: build (${{ matrix.platform }}-${{ matrix.arch }})
90+
runs-on: ${{ matrix.runner }}
91+
timeout-minutes: ${{ matrix.timeout }}
92+
defaults:
93+
run:
94+
shell: bash
95+
steps:
96+
- uses: actions/checkout@v6
97+
98+
- uses: astral-sh/setup-uv@v8
99+
100+
- name: Resolve chromium pin and paths
101+
id: pin
102+
run: |
103+
set -euo pipefail
104+
. packages/browseros/CHROMIUM_VERSION
105+
version="$MAJOR.$MINOR.$BUILD.$PATCH"
106+
107+
# Keep the checkout outside the workspace so actions/checkout
108+
# never touches it. pwd -W yields a native path on Windows.
109+
if [ "$RUNNER_OS" = "Windows" ]; then
110+
parent="$(cd "$GITHUB_WORKSPACE/.." && pwd -W)"
111+
else
112+
parent="$(cd "$GITHUB_WORKSPACE/.." && pwd)"
113+
fi
114+
115+
{
116+
echo "version=$version"
117+
echo "cache_key=chromium-src-${{ matrix.platform }}-${{ matrix.arch }}-v1-$version"
118+
} >> "$GITHUB_OUTPUT"
119+
{
120+
echo "CHROMIUM_ROOT=$parent/chromium"
121+
echo "CHROMIUM_SRC=$parent/chromium/src"
122+
} >> "$GITHUB_ENV"
123+
124+
- name: Restore chromium checkout (WarpCache)
125+
if: runner.os != 'Windows'
126+
id: warpcache
127+
uses: WarpBuilds/cache/restore@v1
128+
with:
129+
path: ${{ env.CHROMIUM_ROOT }}
130+
key: ${{ steps.pin.outputs.cache_key }}
131+
restore-keys: |
132+
chromium-src-${{ matrix.platform }}-${{ matrix.arch }}-v1-
133+
134+
- name: Restore chromium checkout (R2)
135+
if: runner.os == 'Windows'
136+
id: r2cache
137+
env:
138+
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
139+
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
140+
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
141+
R2_BUCKET: ${{ secrets.R2_BUCKET }}
142+
run: |
143+
set -euo pipefail
144+
uv run --project packages/browseros python scripts/ci/r2_cache.py restore \
145+
--key "${{ steps.pin.outputs.cache_key }}" \
146+
--root "$CHROMIUM_ROOT"
147+
148+
- name: Ensure chromium checkout at pinned tag
149+
run: |
150+
set -euo pipefail
151+
uv run --project packages/browseros python scripts/ci/setup_chromium.py \
152+
--chromium-root "$CHROMIUM_ROOT" --step checkout
153+
154+
- name: Reset chromium tree (clean module)
155+
working-directory: packages/browseros
156+
run: |
157+
set -euo pipefail
158+
uv run browseros build --modules clean \
159+
--chromium-src "$CHROMIUM_SRC" \
160+
--build-type release \
161+
--arch "${{ matrix.arch }}"
162+
163+
- name: Sync chromium dependencies (gclient)
164+
run: |
165+
set -euo pipefail
166+
uv run --project packages/browseros python scripts/ci/setup_chromium.py \
167+
--chromium-root "$CHROMIUM_ROOT" --step sync
168+
169+
# Save immediately after sync: the tree is pristine (no BrowserOS
170+
# patches, no out/ dir), which keeps the cache deterministic and as
171+
# small as possible.
172+
- name: Save chromium checkout (WarpCache)
173+
if: runner.os != 'Windows' && steps.warpcache.outputs.cache-hit != 'true'
174+
uses: WarpBuilds/cache/save@v1
175+
with:
176+
path: ${{ env.CHROMIUM_ROOT }}
177+
key: ${{ steps.pin.outputs.cache_key }}
178+
179+
- name: Save chromium checkout (R2)
180+
if: runner.os == 'Windows' && steps.r2cache.outputs.cache-hit != 'true'
181+
env:
182+
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
183+
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
184+
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
185+
R2_BUCKET: ${{ secrets.R2_BUCKET }}
186+
run: |
187+
set -euo pipefail
188+
uv run --project packages/browseros python scripts/ci/r2_cache.py save \
189+
--key "${{ steps.pin.outputs.cache_key }}" \
190+
--root "$CHROMIUM_ROOT"
191+
192+
- name: Install Linux build deps
193+
if: matrix.platform == 'linux'
194+
run: |
195+
set -euo pipefail
196+
sudo apt-get update
197+
# libfuse2: appimagetool is itself an AppImage and needs FUSE
198+
sudo apt-get install -y libfuse2
199+
"$CHROMIUM_SRC/build/install-build-deps.sh" --no-prompt
200+
201+
- name: Build BrowserOS (unsigned)
202+
working-directory: packages/browseros
203+
env:
204+
# download_resources pulls BrowserOS Server bundles from R2
205+
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
206+
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
207+
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
208+
R2_BUCKET: ${{ secrets.R2_BUCKET }}
209+
# --- Signing placeholders (unused until signing is wired up) ---
210+
# macOS (sign_macos + notarization):
211+
# MACOS_CERTIFICATE_P12 / MACOS_CERTIFICATE_PWD (import into a CI keychain)
212+
# MACOS_CERTIFICATE_NAME, MACOS_KEYCHAIN_PASSWORD
213+
# PROD_MACOS_NOTARIZATION_APPLE_ID / _TEAM_ID / _PWD
214+
# SPARKLE_PRIVATE_KEY (sparkle_sign)
215+
# Windows (sign_windows via SSL.com CodeSignTool):
216+
# ESIGNER_USERNAME / ESIGNER_PASSWORD / ESIGNER_TOTP_SECRET
217+
# ESIGNER_CREDENTIAL_ID, CODE_SIGN_TOOL_PATH (tool install dir)
218+
run: |
219+
set -euo pipefail
220+
uv run browseros build \
221+
--config "build/config/${{ matrix.config }}" \
222+
--chromium-src "$CHROMIUM_SRC"
223+
224+
- name: Report disk usage
225+
if: always()
226+
run: df -h || true
227+
228+
- name: Upload artifacts
229+
uses: actions/upload-artifact@v4
230+
with:
231+
name: browseros-nightly-${{ matrix.platform }}-${{ matrix.arch }}
232+
if-no-files-found: error
233+
retention-days: 14
234+
compression-level: 0
235+
path: |
236+
packages/browseros/releases/*/*.dmg
237+
packages/browseros/releases/*/*.AppImage
238+
packages/browseros/releases/*/*.deb
239+
packages/browseros/releases/*/*_installer.exe
240+
packages/browseros/releases/*/*_installer.zip
241+
242+
publish:
243+
needs: [plan, build]
244+
if: needs.plan.outputs.publish == 'true'
245+
runs-on: ubuntu-latest
246+
permissions:
247+
contents: write
248+
steps:
249+
- uses: actions/checkout@v6
250+
251+
- uses: actions/download-artifact@v4
252+
with:
253+
path: dist
254+
merge-multiple: true
255+
256+
- name: Update rolling nightly prerelease
257+
env:
258+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
259+
run: |
260+
set -euo pipefail
261+
shopt -s nullglob
262+
files=(dist/*/*.dmg dist/*/*.AppImage dist/*/*.deb dist/*/*_installer.exe dist/*/*_installer.zip dist/*.dmg dist/*.AppImage dist/*.deb dist/*_installer.exe dist/*_installer.zip)
263+
if [ "${#files[@]}" -eq 0 ]; then
264+
echo "::error::No artifacts found to publish"
265+
exit 1
266+
fi
267+
printf 'Publishing:\n%s\n' "${files[@]}"
268+
269+
notes_file="$(mktemp)"
270+
cat > "$notes_file" <<EOF
271+
Automated unsigned nightly build from \`main\` (commit $GITHUB_SHA).
272+
273+
These artifacts are NOT code signed or notarized — expect OS warnings on install. For signed builds use the regular releases.
274+
EOF
275+
276+
# The `nightly` tag is rolling: recreate it via the API each run
277+
# (release delete + create; no git force-push involved).
278+
gh release delete nightly --cleanup-tag --yes || true
279+
gh release create nightly \
280+
--prerelease \
281+
--latest=false \
282+
--target "$GITHUB_SHA" \
283+
--title "BrowserOS Nightly (unsigned) $(date -u +%Y-%m-%d)" \
284+
--notes-file "$notes_file" \
285+
"${files[@]}"

packages/browseros/build/cli/build.py

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# BrowserOS Linux CI Release Build (unsigned, no upload)
2+
#
3+
# Nightly WarpBuild variant of release.linux.yaml. Differences:
4+
# - x64 only (runner arch); arm64 cross-compile can be re-added later
5+
# - no clean/git_setup: CI runs `--modules clean` and `gclient sync`
6+
# itself so the chromium checkout cache stays a pristine post-sync tree
7+
# (git_setup's `git fetch --tags` is unusable on the shallow CI clone)
8+
# - no upload: artifacts are published via GitHub Actions
9+
#
10+
# Run (from packages/browseros, after checkout + clean + sync):
11+
# uv run browseros build --config build/config/release.linux.ci.yaml --chromium-src <src>
12+
13+
build:
14+
type: release
15+
architecture: x64
16+
17+
gn_flags:
18+
file: build/config/gn/flags.linux.release.gn
19+
20+
modules:
21+
# Patches & Resources
22+
- download_resources
23+
- resources
24+
- bundled_extensions
25+
- chromium_replace
26+
- string_replaces
27+
- series_patches
28+
- patches
29+
30+
# Build
31+
- configure
32+
- compile
33+
34+
# Package (Linux is not code signed)
35+
- package_linux
36+
37+
notifications:
38+
slack: false
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# BrowserOS macOS arm64 CI Release Build (unsigned, no upload)
2+
#
3+
# Nightly WarpBuild variant of release.macos.arm64.noupload.yaml. Differences:
4+
# - no clean/git_setup: CI runs `--modules clean` and `gclient sync`
5+
# itself so the chromium checkout cache stays a pristine post-sync tree
6+
# - no sign_macos: package_macos creates an UNSIGNED dmg when the
7+
# signed_app artifact is absent
8+
# - no required signing envs
9+
#
10+
# Run (from packages/browseros, after checkout + clean + sync):
11+
# uv run browseros build --config build/config/release.macos.arm64.ci.yaml --chromium-src <src>
12+
13+
build:
14+
type: release
15+
architecture: arm64
16+
17+
gn_flags:
18+
file: build/config/gn/flags.macos.release.gn
19+
20+
modules:
21+
# Setup (sparkle_setup re-creates third_party/sparkle itself)
22+
- sparkle_setup
23+
24+
# Patches & Resources
25+
- download_resources
26+
- resources
27+
- bundled_extensions
28+
- chromium_replace
29+
- string_replaces
30+
- series_patches
31+
- patches
32+
33+
# Build
34+
- configure
35+
- compile
36+
37+
# Package (unsigned dmg)
38+
- package_macos
39+
40+
notifications:
41+
slack: false

0 commit comments

Comments
 (0)