mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 14:34:42 +01:00
A-g: run the quick free tier on GSTACK_BUN_DEFAULT in CI; drift test pins it
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
d008757e87
commit
db9fdbd0e6
3 files changed
+90
-8
No files matched your search
@@ -227,6 +227,57 @@ jobs:
|
||||
- name: CSO source formatting
|
||||
run: bun run format:cso:check
|
||||
|
||||
# Capy machines install GSTACK_BUN_DEFAULT (bin/gstack-bun-version.sh) when
|
||||
# no project pin decides, above the 1.4.2 minimum every other lane pins, so
|
||||
# the quick tier also runs on it. Its pin equals GSTACK_BUN_DEFAULT;
|
||||
# test/bun-version-drift.test.ts checks this job exists and agrees.
|
||||
quick-bun-default:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
bun-version: 1.4.3
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
- name: Install Playwright Chromium
|
||||
run: npx playwright install --with-deps chromium
|
||||
- name: Configure the bundled Chromium sandbox helper
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chrome=$(bun -e 'import { chromium } from "playwright"; import { realpathSync } from "node:fs"; console.log(realpathSync(chromium.executablePath()))')
|
||||
case "$chrome" in
|
||||
"$HOME"/.cache/ms-playwright/chromium-*/chrome-linux*/chrome) ;;
|
||||
*) echo "Unexpected Chromium installation path" >&2; exit 1 ;;
|
||||
esac
|
||||
helper="${chrome%/*}/chrome_sandbox"
|
||||
installed="${chrome%/*}/chrome-sandbox"
|
||||
test -f "$helper" && test ! -L "$helper"
|
||||
sudo install -T -o root -g root -m 4755 "$helper" "$installed"
|
||||
- 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 zsh
|
||||
- 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
|
||||
git config --global --add safe.directory '*'
|
||||
- name: Generate host SKILL.md outputs (.agents, .factory)
|
||||
run: bun run gen:skill-docs --host all
|
||||
- name: Vendor xterm assets into the extension
|
||||
run: bun run vendor:xterm
|
||||
- name: Build server-node bundle (loaded by browse cli imports)
|
||||
run: bash browse/scripts/build-node-server.sh
|
||||
- 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
|
||||
- name: Run the quick free tier on the install-default Bun
|
||||
run: xvfb-run -a bun run test:quick
|
||||
|
||||
free-suite:
|
||||
needs: free-plan
|
||||
runs-on: ubicloud-standard-8
|
||||
@@ -396,14 +447,15 @@ jobs:
|
||||
# gate is merge-blocking without a separate branch-protection migration.
|
||||
free-tests:
|
||||
if: always()
|
||||
needs: [free-suite, typecheck, cso-macos-launcher, macos-named-regressions, cso-windows-launcher, cso-docker-integration]
|
||||
needs: [free-suite, typecheck, quick-bun-default, cso-macos-launcher, macos-named-regressions, cso-windows-launcher, cso-docker-integration]
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Require the free suite, typecheck, macOS regressions, and every CSO platform gate
|
||||
- name: Require the free suite, typecheck, the quick tier on the default Bun, macOS regressions, and every CSO platform gate
|
||||
env:
|
||||
FREE_SUITE_RESULT: ${{ needs.free-suite.result }}
|
||||
TYPECHECK_RESULT: ${{ needs.typecheck.result }}
|
||||
QUICK_BUN_DEFAULT_RESULT: ${{ needs.quick-bun-default.result }}
|
||||
CSO_MACOS_RESULT: ${{ needs.cso-macos-launcher.result }}
|
||||
MACOS_REGRESSIONS_RESULT: ${{ needs.macos-named-regressions.result }}
|
||||
CSO_WINDOWS_RESULT: ${{ needs.cso-windows-launcher.result }}
|
||||
@@ -412,6 +464,7 @@ jobs:
|
||||
set -eu
|
||||
test "$FREE_SUITE_RESULT" = success
|
||||
test "$TYPECHECK_RESULT" = success
|
||||
test "$QUICK_BUN_DEFAULT_RESULT" = success
|
||||
test "$CSO_MACOS_RESULT" = success
|
||||
test "$MACOS_REGRESSIONS_RESULT" = success
|
||||
test "$CSO_WINDOWS_RESULT" = success
|
||||
|
||||
@@ -28,8 +28,13 @@ const WORKFLOWS_DIR = path.join(ROOT, '.github', 'workflows');
|
||||
interface Pin {
|
||||
surface: string;
|
||||
version: string;
|
||||
/** The workflow job the pin sits in (empty outside a workflow). */
|
||||
job?: string;
|
||||
}
|
||||
|
||||
/** The one job that runs on GSTACK_BUN_DEFAULT instead of the minimum (plan A-g). */
|
||||
const DEFAULT_BUN_JOB = 'quick-bun-default';
|
||||
|
||||
function collectPins(): Pin[] {
|
||||
const pins: Pin[] = [];
|
||||
|
||||
@@ -37,7 +42,9 @@ function collectPins(): Pin[] {
|
||||
if (!/\.ya?ml$/.test(name)) continue;
|
||||
const source = fs.readFileSync(path.join(WORKFLOWS_DIR, name), 'utf-8');
|
||||
const lines = source.split('\n');
|
||||
let job = '';
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
job = lines[i].match(/^ {2}([A-Za-z0-9_-]+):\s*$/)?.[1] ?? job;
|
||||
if (!/uses:\s*oven-sh\/setup-bun@/.test(lines[i])) continue;
|
||||
// A pinned stanza is `with:` + `bun-version: <v>` within the next few
|
||||
// lines; an unpinned setup-bun is itself drift (installs latest).
|
||||
@@ -46,6 +53,7 @@ function collectPins(): Pin[] {
|
||||
pins.push({
|
||||
surface: `${name}:${i + 1}`,
|
||||
version: m ? m[1] : '<unpinned setup-bun — installs latest>',
|
||||
job,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -69,8 +77,8 @@ function collectPins(): Pin[] {
|
||||
}
|
||||
|
||||
describe('bun version pins', () => {
|
||||
test('every CI surface pins the same bun version', () => {
|
||||
const pins = collectPins();
|
||||
test('every CI surface pins the same bun version (the install-default job aside)', () => {
|
||||
const pins = collectPins().filter(p => p.job !== DEFAULT_BUN_JOB);
|
||||
// Sanity: the scan found the known surfaces (a regex rot that finds
|
||||
// nothing must fail loudly, not vacuously pass).
|
||||
expect(pins.length).toBeGreaterThanOrEqual(6);
|
||||
@@ -96,19 +104,40 @@ describe('bun version pins', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function bunVersionsFile(): { floor: string; tested: string } {
|
||||
function bunVersionsFile(): { floor: string; tested: string; installDefault: string; verified: string[] } {
|
||||
const src = fs.readFileSync(path.join(ROOT, 'bin', 'gstack-bun-version.sh'), 'utf-8');
|
||||
return {
|
||||
floor: src.match(/^GSTACK_BUN_FLOOR="([^"]+)"$/m)?.[1] ?? '<no GSTACK_BUN_FLOOR>',
|
||||
tested: src.match(/^GSTACK_BUN_TESTED="([^"]+)"$/m)?.[1] ?? '<no GSTACK_BUN_TESTED>',
|
||||
installDefault: src.match(/^GSTACK_BUN_DEFAULT="([^"]+)"$/m)?.[1] ?? '<no GSTACK_BUN_DEFAULT>',
|
||||
verified: (src.match(/^GSTACK_BUN_VERIFIED="([^"]+)"$/m)?.[1] ?? '').split(/\s+/).filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
// Plan A2/A-g: the Capy install default is separate from the floor. It must be
|
||||
// a verified archive at or above the minimum, and exactly one CI job (the
|
||||
// quick free tier) runs on it so the default Bun is never untested.
|
||||
describe('install default Bun (A2, A-g)', () => {
|
||||
const { tested, installDefault, verified } = bunVersionsFile();
|
||||
test('GSTACK_BUN_DEFAULT is a verified archive and not below the supported minimum', () => {
|
||||
expect(verified).toContain(installDefault);
|
||||
expect(verified).toContain(tested);
|
||||
expect(Bun.semver.order(installDefault, tested)).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
test('one CI job runs the quick free tier on GSTACK_BUN_DEFAULT, and the free-tests aggregate requires it', () => {
|
||||
const onDefault = collectPins().filter(p => p.job === DEFAULT_BUN_JOB);
|
||||
expect(onDefault.map(p => p.version)).toEqual([installDefault]);
|
||||
const workflow = Bun.YAML.parse(fs.readFileSync(path.join(WORKFLOWS_DIR, 'free-tests.yml'), 'utf-8')) as { jobs: Record<string, { needs?: string[]; steps: { run?: string }[] }> };
|
||||
expect(workflow.jobs[DEFAULT_BUN_JOB].steps.some(step => step.run?.includes('bun run test:quick'))).toBe(true);
|
||||
expect(workflow.jobs['free-tests'].needs).toContain(DEFAULT_BUN_JOB);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bun requirement surfaces agree (E1)', () => {
|
||||
const { floor, tested } = bunVersionsFile();
|
||||
|
||||
test('tested version == every CI pin == engines.bun lower bound; floor sits below it', () => {
|
||||
const ciVersions = [...new Set(collectPins().map((p) => p.version))];
|
||||
test('tested version == every CI pin (the install-default job aside) == engines.bun lower bound; floor sits below it', () => {
|
||||
const ciVersions = [...new Set(collectPins().filter((p) => p.job !== DEFAULT_BUN_JOB).map((p) => p.version))];
|
||||
expect(ciVersions).toEqual([tested]);
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf-8'));
|
||||
expect(pkg.engines?.bun).toBe(`>=${tested}`);
|
||||
|
||||
@@ -388,7 +388,7 @@ describe('CSO runtime staging gates', () => {
|
||||
expect(gate['continue-on-error']).not.toBe(true);
|
||||
const required = workflow.jobs['free-tests'];
|
||||
expect(required.if).toBe('always()');
|
||||
expect(required.needs).toEqual(['free-suite', 'typecheck', 'cso-macos-launcher', 'macos-named-regressions', 'cso-windows-launcher', 'cso-docker-integration']);
|
||||
expect(required.needs).toEqual(['free-suite', 'typecheck', 'quick-bun-default', 'cso-macos-launcher', 'macos-named-regressions', 'cso-windows-launcher', 'cso-docker-integration']);
|
||||
expect(required.steps[0].run).toContain('test "$CSO_DOCKER_RESULT" = success');
|
||||
for (const current of Object.values(workflow.jobs) as any[]) for (const step of current.steps) {
|
||||
if (step.uses?.startsWith('oven-sh/setup-bun')) expect(step.uses).toBe('oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6');
|
||||
|
||||
Reference in new issue
Block a user