Files
gstack/.github/workflows/free-tests.yml
T
Garry TanandOpenAI Codex 4a3c6a8a3c v1.87.0.0 feat: add verified CSO audits and replayable repair bundles (#2852)
* feat(cso): add verified audits and replayable repair bundles

* fix(cso): harden qualification and setup boundaries

* fix(cso): assemble security canaries at runtime

* fix(cso): bound release proof and maintenance work

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): require complete evaluation reports

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): replay expired snapshots from supplied source

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* test(cso): synchronize DNS cancellation assertion

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore(ship): exempt repository owner from liveness proof

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* test(cso): make recheck retention overlap deterministic

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore: bump version and changelog (v1.85.0.0)

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): pass native release gates

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore: move release to v1.86.0.0

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): resolve rechecks by finding

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore: move release to v1.87.0.0

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): pass macOS and Windows release gates

Normalize BSD wc output, compare Windows paths by filesystem identity, preserve portable snapshot race coverage, and narrow POSIX-only Windows fixtures.

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): harden native verification gates

* fix(cso): refine Windows native diagnostics

* test(cso): isolate Windows Git startup failure

* test(cso): stabilize Windows native diagnostics

* fix(cso): support hardened Git on Windows

* fix(cso): close final verification gaps

* test(cso): bound cold Docker fixture setup

* fix(cso): restore cross-platform free-suite gates

---------

Co-authored-by: OpenAI Codex <noreply@openai.com>
2026-09-14 15:14:58 -07:00

256 lines
11 KiB
YAML

name: Free Tests
# The free suite (~400 files: test/, browse/test/, make-pdf/test/, design/test/)
# had ZERO Linux CI coverage before this lane — only a curated Windows subset
# ran anywhere. This job runs the whole thing through the canonical runner
# (scripts/test-free-shards.ts): N concurrent shard processes (serial within
# each, plus a trailing serial tree-mutating shard) with strict-output
# classification per shard, so a truncated or summary-less run can never
# report green.
#
# Deliberately SECRETLESS: free tests make no API calls, so this lane gets no
# provider keys at all — least privilege, and fork PRs get real test signal
# here (the eval matrix skips fork PRs because repository secrets can't reach
# them). test/free-tests-workflow-wiring.test.ts fails CI if a secret sneaks in.
#
# This is a REQUIRED check from day one (branch protection lists it). If it's
# red, fix or quarantine-with-issue — don't make it advisory; an advisory lane
# is permanent false comfort.
#
# Sizing note (decision V3): single job first. If PR runs show it slower than
# the eval matrix wall, switch to a matrix of `--shards N --shard i` jobs
# (indices are stable, empty shards no-op).
on:
pull_request:
branches: [main]
# Also on main pushes: two individually-green PRs can merge into a red
# main; without this nothing runs the free suite on main until the next PR.
push:
branches: [main]
workflow_dispatch:
# Keyed on the PR number, not head_ref: a bare branch name carries no fork
# prefix, so same-name branches from two forks would share one group and a
# push to fork B's PR would cancel fork A's in-flight REQUIRED check.
concurrency:
group: free-tests-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
# Least privilege: this job executes PR-controlled code (install lifecycle
# scripts + the test suite), so the GITHUB_TOKEN gets read-only contents and
# the checkout doesn't persist it into .git/config.
permissions:
contents: read
jobs:
cso-macos-launcher:
runs-on: macos-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.0
- name: Install helper dependencies
run: bun install --frozen-lockfile --ignore-scripts
- name: Build and sign the native macOS startup boundary
run: bun run build:cso
- name: Exercise hardened-runtime startup and environment scrubbing
run: bun run test:cso:macos
env:
GSTACK_CSO_MACOS_TESTS: "1"
cso-windows-launcher:
runs-on: windows-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.0
- name: Install helper dependencies
run: bun install --frozen-lockfile --ignore-scripts
- name: Build the native Windows startup boundary from Git Bash
shell: bash
run: bun run build:cso
- name: Exercise native startup, environment, and argument forwarding
run: bun run test:cso:windows
env:
GSTACK_CSO_WINDOWS_TESTS: "1"
cso-docker-integration:
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.0
- name: Require local Docker containment prerequisites
# A missing daemon is a failed gate, never a skipped integration test.
run: |
command -v docker
docker --host unix:///var/run/docker.sock info
command -v cc
- name: Install helper dependencies
run: bun install --frozen-lockfile --ignore-scripts
- name: Compile trusted CSO helper and watchdog
run: bun run build:cso
- name: Run CSO Docker integration gate
run: bun run test:cso:docker
env:
GSTACK_CSO_DOCKER_TESTS: "1"
DOCKER_HOST: unix:///var/run/docker.sock
free-suite:
runs-on: ubicloud-standard-8
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.0
- uses: actions/cache@v6
with:
path: ~/.bun/install/cache
key: linux-bun-${{ hashFiles('bun.lock') }}
# A lockfile bump starts from the previous cache instead of cold.
restore-keys: |
linux-bun-
- name: Install dependencies
run: bun install --frozen-lockfile
- uses: actions/cache@v6
with:
path: ~/.cache/ms-playwright
key: linux-playwright-${{ hashFiles('bun.lock') }}
restore-keys: |
linux-playwright-
# Cache restores browser binaries; install is still required for system
# deps and is a fast no-op for already-present browsers.
- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium
# Headed-browser tests (handoff, extension sidepanel DOM) need a real
# DISPLAY — first Linux run failed with Playwright's "launched a headed
# browser without an XServer" banner. xvfb-run below provides it;
# x11-utils ships xdpyinfo for display probing. poppler-utils ships
# pdftotext/pdffonts/pdftoppm for the make-pdf e2e gates;
# fonts-noto-color-emoji is the emoji-gate's render font (playwright
# --with-deps usually installs it, but the gate must not depend on a
# transitive package list). Fonts must land BEFORE the first browse
# daemon launch — Chromium snapshots fontconfig at startup.
- name: Install Xvfb + X11 utilities + gate tools
run: sudo apt-get install -y --no-install-recommends xvfb x11-utils poppler-utils fonts-noto-color-emoji
- name: Configure git identity (tests init temp repos)
run: |
git config --global user.email "free-tests-ci@gstack.test"
git config --global user.name "Free Tests CI"
git config --global init.defaultBranch main
# Some tests run git against the checkout itself; CI checkouts can be
# owned by a different uid than the runner user.
git config --global --add safe.directory '*'
- name: Generate host SKILL.md outputs (.agents, .factory)
# Golden-file tests read generated host outputs that are gitignored.
run: bun run gen:skill-docs --host all
- name: Vendor xterm assets into the extension
# extension/lib/xterm* are gitignored (vendored from npm at build
# time). Without them the sidepanel's terminal script bails and the
# sidepanel DOM tests time out waiting on init that never happens.
run: bun run vendor:xterm
- name: Build server-node bundle (loaded by browse cli imports)
run: bash browse/scripts/build-node-server.sh
# Narrowed gate build: the make-pdf e2e gates probe make-pdf/dist/pdf,
# browse/dist/browse, and the diagram-render bundle, then self-skip when
# absent — which made them silently skip on Linux for their whole life
# (this lane never built binaries). Full `bun run build` compiles five
# binaries and would add ~60-90s to the ONLY required check; the gates
# need exactly these three artifacts.
- name: Build gate binaries (make-pdf e2e gates)
run: bun run build:gates
- name: Build trusted CSO helper used by free contract tests
run: bun run build:cso
# GSTACK_EXPECT_BINARIES=1 arms make-pdf/test/e2e/ci-prereqs.test.ts:
# if a future edit drops the gate build (or poppler), the lane FAILS
# instead of the gates silently self-skipping back to false green.
- name: Run free suite
run: xvfb-run -a bun run test:free
env:
GSTACK_EXPECT_BINARIES: "1"
# WS1 flake telemetry: a single timing flake must not red the only
# required lane — the runner's attribution-gated retry pass (cap 5,
# truncation veto) re-runs failing files once, serially, and a
# clean retry downgrades to a LOUD flaky-pass. Every flaky-pass is
# appended to the ledger (single writer: the parent runner) and
# uploaded below, so repeat offenders are an enumerable series —
# recorded and ranked, never masked. Pinned by
# test/free-tests-workflow-wiring.test.ts.
GSTACK_FREE_RETRY_FLAKY: "1"
GSTACK_FLAKE_LEDGER: ${{ runner.temp }}/flake-ledger.jsonl
# Uploaded unconditionally (not just on failure): a flaky-pass run is
# GREEN — that's the point — so its evidence must survive green runs.
- name: Upload flake ledger
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: flake-ledger
path: ${{ runner.temp }}/flake-ledger.jsonl
if-no-files-found: ignore
retention-days: 90
# The runner streams the full child output to per-run logs under the OS
# tmpdir and prints only the quiet contract to the console. Without this
# upload, a red required check names WHICH test failed but the why
# (assertion detail, stack) dies with the runner — every diagnosis would
# need a local re-run, which fork contributors can't do on this image.
- name: Upload shard logs on failure
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: free-test-shard-logs
path: /tmp/gstack-free-test-*.log
if-no-files-found: ignore
# Branch protection already requires the `free-tests` context. Keep that
# stable name as an always-running aggregate so every platform-specific CSO
# gate is merge-blocking without a separate branch-protection migration.
free-tests:
if: always()
needs: [free-suite, cso-macos-launcher, cso-windows-launcher, cso-docker-integration]
runs-on: ubuntu-24.04
timeout-minutes: 2
steps:
- name: Require the free suite and every CSO platform gate
env:
FREE_SUITE_RESULT: ${{ needs.free-suite.result }}
CSO_MACOS_RESULT: ${{ needs.cso-macos-launcher.result }}
CSO_WINDOWS_RESULT: ${{ needs.cso-windows-launcher.result }}
CSO_DOCKER_RESULT: ${{ needs.cso-docker-integration.result }}
run: |
set -eu
test "$FREE_SUITE_RESULT" = success
test "$CSO_MACOS_RESULT" = success
test "$CSO_WINDOWS_RESULT" = success
test "$CSO_DOCKER_RESULT" = success