mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-28 15:41:57 +02:00
* perf: remove repeated test work and preserve AUQ execution budgets * fix: validate native evaluation fixture evidence at its actual boundaries * fix: clarify deployment approval and recovery state transitions * chore: document coverage and release v1.90.2.0 * test: preserve Windows scheduling and native no-change consent * test: recognize verified reads through fixture symlinks * test: isolate alias-name installation from runtime assets
268 lines
12 KiB
YAML
268 lines
12 KiB
YAML
name: Windows Free Tests
|
|
|
|
# Curated subset of the free test suite that runs on a paid faster Windows runner.
|
|
#
|
|
# Codex's v1.18.0.0 review flagged that the existing evals.yml workflow uses
|
|
# a Linux container, so a windows-latest matrix entry there isn't a drop-in.
|
|
# This workflow is non-container, runs the curated Windows-safe subset, plus
|
|
# targeted resolver tests that exercise the Bun.which-based claude binary
|
|
# resolution + the GSTACK_CLAUDE_BIN override path on Windows.
|
|
#
|
|
# Runner: GitHub-hosted free `windows-latest`. The whole rest of CI runs on
|
|
# Ubicloud (Linux), but Ubicloud doesn't ship Windows runners and we don't
|
|
# want to flip on GitHub's org-level larger-runner billing for just this one
|
|
# job. 4 cores, ~60s spin-up, $0. The wave-coverage tests this runs are
|
|
# small enough that total job time stays under 2 minutes.
|
|
#
|
|
# What this DOES NOT do (still out of scope, tracked as follow-up):
|
|
# - Run the full free suite on Windows. The 24 tests that hardcode /bin/sh,
|
|
# spawn('sh',...), or raw /tmp/ paths are excluded by scripts/test-free-shards.ts
|
|
# --windows-only. They need POSIX-bound surfaces to be ported off shell
|
|
# primitives before they can run on Windows.
|
|
# - Run Playwright/browser-backed tests. Browse server bring-up on Windows is
|
|
# a separate concern (PR #1238 windows-pty-bun-pty-fix is in flight).
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
inputs:
|
|
dia_native_only:
|
|
description: Run disposable ARM64 macOS Dia qualification instead of Windows
|
|
type: boolean
|
|
default: false
|
|
native_diagnostics_only:
|
|
description: Run Windows launch diagnostics and credential regressions without qualification
|
|
type: boolean
|
|
default: false
|
|
dia_launch_comparison:
|
|
description: Compare protected Dia launch under Bun and Node in separate fresh Mac jobs
|
|
type: boolean
|
|
default: false
|
|
dia_gui_readiness:
|
|
description: Inspect disposable Mac GUI-session readiness without launching browsers
|
|
type: boolean
|
|
default: false
|
|
|
|
concurrency:
|
|
group: windows-free-${{ github.event.pull_request.number || github.run_id }}
|
|
cancel-in-progress: true
|
|
|
|
# Test-only lane — no token writes.
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
windows-free-tests:
|
|
if: ${{ !inputs.dia_native_only && !inputs.dia_launch_comparison && !inputs.dia_gui_readiness }}
|
|
# Ubicloud Windows runner (same provider as the Linux evals workflow).
|
|
# To revert: swap to `windows-latest` (GitHub's free 4-core Windows runner).
|
|
runs-on: windows-latest
|
|
timeout-minutes: 15
|
|
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: 1.4.0
|
|
|
|
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38
|
|
with:
|
|
node-version: 24.18.0
|
|
|
|
# bun install was 35s of a 55s job, all network. Cache keyed on the
|
|
# lockfile; bun's install cache lives under ~/.bun/install/cache on
|
|
# every platform.
|
|
- uses: actions/cache@v6
|
|
with:
|
|
path: ~/.bun/install/cache
|
|
key: windows-bun-${{ hashFiles('bun.lock') }}
|
|
# A lockfile bump starts from the previous cache instead of cold
|
|
# (restore alone costs ~26s; without this a bump pays it for nothing).
|
|
restore-keys: |
|
|
windows-bun-
|
|
|
|
- name: Configure git identity (required by tests that init temp repos)
|
|
run: |
|
|
git config --global user.email "windows-ci@gstack.test"
|
|
git config --global user.name "Windows CI"
|
|
git config --global init.defaultBranch main
|
|
shell: bash
|
|
|
|
- name: Install dependencies
|
|
run: bun install --frozen-lockfile
|
|
|
|
- name: Build server-node.mjs (required by Windows browse path)
|
|
# browse/src/cli.ts module-level throws on Windows if server-node.mjs
|
|
# is missing — Bun can't drive Playwright's Chromium on Windows
|
|
# (oven-sh/bun#4253). The bundle must exist for any test that
|
|
# transitively loads cli.ts to even import. We build only the
|
|
# Node-compatible server bundle here; full `bun run build` would
|
|
# also compile every binary which is slow and unnecessary for tests.
|
|
run: bash browse/scripts/build-node-server.sh
|
|
shell: bash
|
|
|
|
- name: Generate host SKILL.md outputs (.agents, .factory)
|
|
if: ${{ !inputs.native_diagnostics_only }}
|
|
# The golden-file regression tests in test/gen-skill-docs.test.ts read
|
|
# .agents/skills/gstack-ship/SKILL.md and .factory/skills/gstack-ship/
|
|
# SKILL.md. Both are gitignored — generated on demand by gen:skill-docs.
|
|
# On Mac/Linux CI the existing eval workflow regenerates these as part
|
|
# of its own pipeline; the windows-free-tests lane doesn't share that
|
|
# so it must regenerate explicitly.
|
|
run: bun run gen:skill-docs --host all
|
|
shell: bash
|
|
|
|
- name: Install Chromium for the Node worker smoke
|
|
run: bunx playwright install chromium
|
|
|
|
# The Windows job verifies the new portability work this PR delivers,
|
|
# not the entire free suite. After v1.20.0.0 ships, full-suite Windows
|
|
# parity is a P4 follow-up TODO that depends on porting many tests off
|
|
# POSIX-bound surfaces (raw /tmp paths, /bin/bash hardcodes, bash
|
|
# shebang spawns, mode-bit assertions, deleted v1.14 sidebar refs, etc).
|
|
#
|
|
# The curated subset enumeration in scripts/test-free-shards.ts is
|
|
# retained for future expansion — `bun run test:windows --list` gives
|
|
# contributors a starting point to grow Windows coverage incrementally.
|
|
#
|
|
# What we verify here is exactly the new code paths v1.20.0.0 ships:
|
|
# - bin/gstack-paths state-root resolution (test/gstack-paths.test.ts)
|
|
# - browse/src/claude-bin.ts Bun.which wrapper + override + arg-prefix
|
|
# resolution including the GSTACK_CLAUDE_BIN=wsl PATHEXT path
|
|
# (browse/test/claude-bin.test.ts)
|
|
# - scripts/test-free-shards.ts curation logic itself
|
|
# (test/test-free-shards.test.ts)
|
|
|
|
- name: Run curated Windows-safe suite
|
|
if: ${{ !inputs.native_diagnostics_only }}
|
|
# Replaces the previous hand-listed 13-file subset, which drifted from
|
|
# the curation registry it was supposed to sample. The runner's
|
|
# --windows-only curation (scripts/test-free-shards.ts) is the single
|
|
# source of truth: POSIX-bound tests are excluded by pattern there, so
|
|
# growing/pruning Windows coverage is one list, not two. If a test is
|
|
# red here because it's genuinely POSIX-bound, add it to the curation
|
|
# exclusions — don't resurrect a hand list in this file.
|
|
env:
|
|
GSTACK_FREE_JOBS: '2'
|
|
# Point os.tmpdir() at the runner temp so the shard logs land
|
|
# somewhere the artifact step below can glob.
|
|
TEMP: ${{ runner.temp }}
|
|
TMP: ${{ runner.temp }}
|
|
run: bun run test:windows
|
|
shell: bash
|
|
|
|
- name: Run focused native launch and credential diagnostics
|
|
if: inputs.native_diagnostics_only
|
|
shell: bash
|
|
run: |
|
|
set -o pipefail
|
|
status=0
|
|
bun test browse/test/cookie-import-native-job.test.ts --test-name-pattern 'native Windows launch diagnostics|a locked real Edge profile|real Edge synthetic profile' 2>&1 | tee "$RUNNER_TEMP/gstack-free-test-native-diagnostics.log" || status=1
|
|
bun test browse/test/cookie-credential-deadline.test.ts browse/test/cookie-import-node.test.ts browse/test/bun-polyfill.test.ts 2>&1 | tee "$RUNNER_TEMP/gstack-free-test-credential-diagnostics.log" || status=1
|
|
exit "$status"
|
|
|
|
# Same diagnosability contract as free-tests.yml: a red lane must
|
|
# carry the WHY (the runner's quiet console names files, not causes).
|
|
# (#2561 was written against the old hand-listed subset; its two new
|
|
# test files are pure-TS and flow into the --windows-only curation
|
|
# automatically, so no per-file entry is needed here.)
|
|
- name: Upload full shard logs
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: windows-free-test-shard-logs
|
|
path: ${{ runner.temp }}/gstack-free-test-*.log
|
|
if-no-files-found: ignore
|
|
|
|
cookie-native-qualification:
|
|
if: github.event_name == 'workflow_dispatch' && !inputs.dia_native_only && !inputs.native_diagnostics_only && !inputs.dia_launch_comparison && !inputs.dia_gui_readiness
|
|
runs-on: windows-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
with:
|
|
persist-credentials: false
|
|
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
|
|
with:
|
|
bun-version: 1.4.0
|
|
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38
|
|
with:
|
|
node-version: 24.18.0
|
|
- name: Install pinned dependencies
|
|
run: bun install --frozen-lockfile
|
|
- name: Build the qualified Node server inputs
|
|
run: bash browse/scripts/build-node-server.sh
|
|
shell: bash
|
|
- name: Qualify owned native cookie extraction
|
|
run: ./.github/scripts/run-cookie-native-qualification.ps1 -OutputRoot "$env:RUNNER_TEMP"
|
|
- name: Preserve qualification evidence
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
|
|
with:
|
|
name: cookie-native-qualification
|
|
path: ${{ runner.temp }}/cookie-native-qualification-*/
|
|
if-no-files-found: error
|
|
|
|
dia-native-qualification:
|
|
if: github.event_name == 'workflow_dispatch' && (inputs.dia_native_only || inputs.dia_launch_comparison || inputs.dia_gui_readiness)
|
|
runs-on: macos-15
|
|
timeout-minutes: 20
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
runtime: ${{ fromJSON(inputs.dia_launch_comparison && !inputs.dia_gui_readiness && '["bun","node"]' || '["bun"]') }}
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
with:
|
|
persist-credentials: false
|
|
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
|
|
with:
|
|
bun-version: 1.4.0
|
|
- name: Validate GUI readiness selection
|
|
if: inputs.dia_gui_readiness
|
|
env:
|
|
OTHER_DIA_MODES: ${{ inputs.dia_native_only || inputs.dia_launch_comparison || inputs.native_diagnostics_only }}
|
|
run: |
|
|
bun --no-env-file --no-install --no-macros --config=/dev/null -e '
|
|
if (process.env.OTHER_DIA_MODES !== "false") {
|
|
console.error("dia_gui_readiness must be selected alone");
|
|
process.exit(1);
|
|
}
|
|
'
|
|
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38
|
|
if: inputs.dia_launch_comparison && !inputs.dia_gui_readiness
|
|
with:
|
|
node-version: 24.18.0
|
|
architecture: arm64
|
|
- name: Install pinned dependencies
|
|
if: ${{ !inputs.dia_gui_readiness }}
|
|
run: bun install --frozen-lockfile
|
|
- name: Install the synthetic destination browser
|
|
if: ${{ !inputs.dia_gui_readiness }}
|
|
run: bunx --no-install playwright install chromium
|
|
- name: Inspect GUI readiness without browser or Keychain access
|
|
if: inputs.dia_gui_readiness
|
|
env:
|
|
GSTACK_DIA_NATIVE_QUALIFY: '1'
|
|
run: bun --no-env-file --no-install --no-macros --config=/dev/null .github/scripts/run-dia-native-qualification.ts --gui-readiness-only
|
|
- name: Qualify native Dia discovery, decryption, and import
|
|
if: ${{ !inputs.dia_launch_comparison && !inputs.dia_gui_readiness }}
|
|
env:
|
|
GSTACK_DIA_NATIVE_QUALIFY: '1'
|
|
run: bun --no-env-file --no-install --no-macros --config=/dev/null .github/scripts/run-dia-native-qualification.ts
|
|
- name: Compare protected native Dia launch without qualification credit
|
|
if: inputs.dia_launch_comparison && !inputs.dia_gui_readiness
|
|
env:
|
|
GSTACK_DIA_NATIVE_QUALIFY: '1'
|
|
COMPARISON_RUNTIME: ${{ matrix.runtime }}
|
|
run: bun --no-env-file --no-install --no-macros --config=/dev/null .github/scripts/run-dia-native-qualification.ts --launch-comparison "$COMPARISON_RUNTIME"
|
|
- name: Preserve only the sanitized qualification receipt
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
|
|
with:
|
|
name: ${{ inputs.dia_gui_readiness && 'dia-gui-readiness' || inputs.dia_launch_comparison && format('dia-launch-comparison-{0}', matrix.runtime) || 'dia-native-qualification' }}
|
|
path: ${{ runner.temp }}/dia-native-qualification.json
|
|
if-no-files-found: error
|