Review follow-up: name the parity job in the workflow header, point the unit comment at ci.yml's Linux jobs instead of a nonexistent `checks` job, state the budget as a completion ceiling and raise it to 60 minutes so the slowest observed runner keeps headroom, and let the owning Agent Note describe the budget and the workflow's statuses without restating numbers the workflow owns.
162 lines
6.7 KiB
YAML
162 lines
6.7 KiB
YAML
# Sandbox CI: the keyless real-kernel confinement proofs, plus the darwin
|
||
# unit-parity job (unit-darwin) that needs the matrix's macOS image. This
|
||
# master-only reference stays outside the pull-request verdict; rationale lives in
|
||
# .agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.md.
|
||
# A separate workflow from ci.yml because the axis is different — the e2e jobs
|
||
# fan out over OS×runner (kernel capabilities), not node versions. The Landlock
|
||
# launcher is built from native/system on each Landlock leg and installed
|
||
# from the same tarballs the main-repository release workflow publishes.
|
||
name: Sandbox
|
||
|
||
on:
|
||
push:
|
||
branches: [master]
|
||
|
||
concurrency:
|
||
group: ${{ github.workflow }}-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
permissions:
|
||
contents: read
|
||
|
||
env:
|
||
# CI runs must never report to the production telemetry endpoint baked
|
||
# into apps/cli/cordis.yml (AppCLIEntry disables the row when set).
|
||
DSH_TELEMETRY_DISABLED: '1'
|
||
|
||
jobs:
|
||
# Keyless real-kernel sandbox proofs (sandbox Agent Note § Testing): each ladder
|
||
# rung is only provable on a host where it enforces, so this job fans out
|
||
# an OS×runner matrix — bwrap and Landlock on Linux (separate legs: the
|
||
# Landlock files force the bwrap rung off, so each leg proves exactly one
|
||
# rung; Landlock twice, once per architecture, each confining through the
|
||
# locally built launcher), Seatbelt on macOS (sandbox-exec ships with
|
||
# the OS). One node
|
||
# version only: kernel confinement does not vary by node, and ci.yml's
|
||
# node matrix already covers the node axis.
|
||
#
|
||
# The e2e files self-skip where their runner is absent, so a leg that lost
|
||
# its runner (no bwrap, kernel without Landlock, macOS without
|
||
# sandbox-exec) would otherwise pass as a false green — the same trap
|
||
# e2e.yml's key preflight guards against. Each leg therefore asserts BOTH
|
||
# its platform files actually ran: `Test Files 2 passed (2)`, no skips.
|
||
sandbox-e2e:
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
# Only the bwrap leg has a Blacksmith equivalent: the earlier
|
||
# migration dispatch runs measured that Blacksmith's Linux images do
|
||
# not enforce Landlock (the run-guard would turn the leg red) and
|
||
# its macOS image is unverified, so those legs stay on GitHub's
|
||
# images under every failover value — see
|
||
# .agents/notes/implemented/process/2026-09-09-blacksmith-failover-leg.md.
|
||
- os: ubuntu-latest
|
||
runner: bwrap
|
||
- os: ubuntu-24.04
|
||
runner: landlock
|
||
- os: ubuntu-24.04-arm
|
||
runner: landlock
|
||
- os: macos-latest
|
||
runner: seatbelt
|
||
name: sandbox e2e (${{ matrix.runner }}, ${{ matrix.os }})
|
||
runs-on: >-
|
||
${{ matrix.runner == 'bwrap' && vars.DSH_CI_FAILOVER_LINUX == 'blacksmith'
|
||
&& 'blacksmith-4vcpu-ubuntu-2404'
|
||
|| matrix.os }}
|
||
timeout-minutes: 20
|
||
steps:
|
||
- uses: actions/checkout@v6
|
||
|
||
- uses: pnpm/action-setup@v4
|
||
|
||
- uses: actions/setup-node@v6
|
||
with:
|
||
node-version: 24
|
||
|
||
- name: Install (immutable)
|
||
run: pnpm install --frozen-lockfile
|
||
|
||
# The bwrap rung needs bubblewrap on PATH and unprivileged user
|
||
# namespaces. Ubuntu 24.04 gates the latter behind an AppArmor knob;
|
||
# lift it best-effort — on images where the knob is absent the
|
||
# functional probe (and the run-guard below) is the arbiter anyway.
|
||
- name: Install bubblewrap (unrestrict userns)
|
||
if: matrix.runner == 'bwrap'
|
||
run: |
|
||
sudo apt-get update -q
|
||
sudo apt-get install -yq bubblewrap
|
||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \
|
||
|| echo "apparmor userns knob absent — the functional probe decides"
|
||
|
||
- name: Build Landlock launcher for this architecture
|
||
if: matrix.runner == 'landlock'
|
||
run: |
|
||
sudo apt-get update -q
|
||
sudo apt-get install -yq musl-tools
|
||
pnpm --dir native/system run build:ts
|
||
pnpm --dir native/system run build:native
|
||
|
||
- name: Sandbox e2e (real kernel confinement, world-verified)
|
||
# NO_COLOR: vitest force-enables ANSI color under GITHUB_ACTIONS even
|
||
# without a TTY, which would thread escape codes through the summary
|
||
# line the run-guard greps.
|
||
env:
|
||
NO_COLOR: 1
|
||
run: |
|
||
set -u +e -o pipefail
|
||
out=$(pnpm exec vitest run --config vitest.e2e.config.ts \
|
||
packages/sandbox/sandbox-local/tests/${{ matrix.runner }}.e2e.ts \
|
||
packages/shell/bash-sandbox/tests/${{ matrix.runner }}.e2e.ts 2>&1); status=$?
|
||
echo "$out"
|
||
[ "$status" -eq 0 ]
|
||
# Both platform files must have RUN — a self-skip (runner missing on
|
||
# the very platform that exists to prove it) is a failure, not a pass.
|
||
echo "$out" | grep -qE 'Test Files[[:space:]]+2 passed \(2\)'
|
||
|
||
# Publish-path rehearsal, Landlock legs only. Build the launcher on its
|
||
# native architecture, then install the local native and harness tarballs
|
||
# together so registry state cannot mask source/package drift.
|
||
- name: Build packages for the pack rehearsal
|
||
if: matrix.runner == 'landlock'
|
||
run: pnpm run build:official
|
||
|
||
- name: Packed-distribution e2e (pack → install → confine)
|
||
if: matrix.runner == 'landlock'
|
||
env:
|
||
NO_COLOR: 1
|
||
run: |
|
||
set -u +e -o pipefail
|
||
out=$(pnpm exec vitest run --config vitest.e2e.config.ts \
|
||
packages/sandbox/sandbox-local/tests/packed-install.e2e.ts 2>&1); status=$?
|
||
echo "$out"
|
||
[ "$status" -eq 0 ]
|
||
echo "$out" | grep -qE 'Test Files[[:space:]]+1 passed \(1\)'
|
||
|
||
# The unit suite runs on ubuntu in ci.yml's Linux jobs; macos-latest is this
|
||
# workflow's only darwin runner, so the platform-dependent unit expectations
|
||
# (Seatbelt path canonicalization: /tmp IS /private/tmp — sandbox semantics,
|
||
# hence this workflow rather than ci-master.yml) take their darwin branch only
|
||
# here. A job of its own so the seatbelt e2e leg keeps the matrix budget. The
|
||
# budget below is a completion ceiling, not a latency target: the full suite
|
||
# takes 16 minutes on a fast macos-latest runner and was under half done at
|
||
# 20 on the slowest observed.
|
||
unit-darwin:
|
||
name: unit tests (darwin parity, macos-latest)
|
||
runs-on: macos-latest
|
||
timeout-minutes: 60
|
||
steps:
|
||
- uses: actions/checkout@v6
|
||
|
||
- uses: pnpm/action-setup@v4
|
||
|
||
- uses: actions/setup-node@v6
|
||
with:
|
||
node-version: 24
|
||
|
||
- name: Install (immutable)
|
||
run: pnpm install --frozen-lockfile
|
||
|
||
- name: Unit tests (darwin parity)
|
||
run: pnpm run test
|