name: CI on: push: branches: [main] pull_request: jobs: tests: name: test (${{ matrix.flavor }}, ${{ matrix.shard }}/3) # arm64 macOS: same clang/libc the differential + number-formatting # oracles were verified against. runs-on: macos-15 strategy: fail-fast: false matrix: flavor: [plain, san] shard: [1, 2, 3] env: # Capped workers: the shared runner's cores oversubscribe under the # corpus compile fan-out, and vitest's worker RPC times out ("Timeout # calling onTaskUpdate") with every test green — the same contention # artifact the harness README documents for local parallel suites. SCRIPTC_TEST_WORKERS: "4" # san = ASan + RC audit over the same suite; anything but "1" is plain. SCRIPTC_SAN: ${{ matrix.flavor == 'san' && '1' || '' }} # Within-file partition for the large case-driven harnesses # (differential, llvm-differential, npm, server, coverage, and Vercel # E2E): they hash stable case names into slice i of 3 # (tests/harness/shard.ts), so the three shards' unions run every case # exactly once. vitest's own --shard is FILE-granular and cannot split # these monster files (differential.test.ts alone is ~9 min # sequential), hence the case-sharded steps below. SCRIPTC_TEST_SHARD: ${{ matrix.shard }}/3 steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: version: 11.1.3 - uses: actions/setup-node@v4 with: node-version-file: .node-version cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm build # Separate vitest invocations because the shard axes must not mix: a file # lands in exactly ONE --shard slice, so an env-sharded file behind # --shard would run only one of its three case slices and silently # drop the rest. Files split by --shard here; cases split by env below. - name: Tests (file-sharded, case-sharded files excluded) run: >- pnpm test --shard=${{ matrix.shard }}/3 --exclude=tests/harness/differential.test.ts --exclude=tests/harness/llvm-differential.test.ts --exclude=tests/harness/npm.test.ts --exclude=tests/harness/server.test.ts --exclude=tests/harness/coverage.test.ts --exclude=tests/harness/vercel-e2e.test.ts --exclude=packages/compiler/src/backend/cc-cache.test.ts # Cache invalidation tests mutate compiler inputs process-wide and must # stay serial within a worker. They do not consume SCRIPTC_SAN, so divide # them across the three plain jobs instead of running the 6+ minute file # in full in one shard of both flavors. - name: Tests (native-cache correctness, slice ${{ matrix.shard }}/3) if: matrix.flavor == 'plain' env: SCRIPTC_CACHE_TEST_SHARD: ${{ matrix.shard }}/3 SCRIPTC_TEST_STABLE_TOOLCHAIN: "0" run: pnpm test packages/compiler/src/backend/cc-cache.test.ts - name: Tests (case-sharded, slice ${{ matrix.shard }}/3) run: >- pnpm test tests/harness/differential.test.ts tests/harness/llvm-differential.test.ts tests/harness/npm.test.ts tests/harness/server.test.ts tests/harness/vercel-e2e.test.ts # Coverage analysis is frontend-only: SCRIPTC_SAN cannot change its # result, so the three plain slices cover the full corpus once. - name: Tests (coverage sweep, slice ${{ matrix.shard }}/3) if: matrix.flavor == 'plain' run: pnpm test tests/harness/coverage.test.ts # A real host-clang/glibc build, not the zig cross-target lane: catches # Linux feature-test macro omissions at compile time and separate-libm # omissions/order errors at link time. linux_host_clang: name: test (linux host clang/glibc) runs-on: ubuntu-24.04 timeout-minutes: 10 env: # The verdict must come from a live clang invocation, never a cached # binary copied from a previous build. SCRIPTC_NO_CACHE: "1" steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: version: 11.1.3 - uses: actions/setup-node@v4 with: node-version-file: .node-version cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm build - name: Host clang static-build regression run: >- pnpm test packages/compiler/test/cc-driver.test.ts --testNamePattern "host-native clang static build" # Exercises the supported Windows GNU target and the built CLI end to end: # TS7 must open its synthetic project, ambient files must resolve across # slash styles, and the default executable must use the .exe suffix. windows_cli: name: test (windows CLI smoke) runs-on: windows-latest timeout-minutes: 15 env: SCRIPTC_NO_CACHE: "1" SCRIPTC_CC: zigcc SCRIPTC_TARGET: x86_64-windows-gnu steps: - uses: actions/checkout@v4 - uses: vercel-labs/setup-zig@v1 with: version: 0.16.0 - uses: pnpm/action-setup@v4 with: version: 11.1.3 - uses: actions/setup-node@v4 with: node-version-file: .node-version cache: pnpm - name: Install dependencies shell: pwsh run: | pnpm install --frozen-lockfile if ($LASTEXITCODE -ne 0) { Write-Host "pnpm exited without a diagnostic; retrying once" pnpm install --frozen-lockfile } - run: pnpm build - name: Windows path regressions run: pnpm exec vitest run packages/compiler/test/ts7/program.test.ts packages/cli/test/paths.test.ts - name: Windows Sandbox path regression run: >- pnpm exec vitest run tests/harness/worktree-files.test.ts --testNamePattern "workspace reset keeps Linux Sandbox paths POSIX" - name: Windows scriptc run smoke shell: pwsh run: | zig version $output = & node packages/cli/dist/main.js run tests/corpus/001-hello.ts --backend c if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } $actual = ($output -join "`n").Trim() if ($actual -ne "hello world") { throw "unexpected scriptc output: $actual" } # Aggregate gate with the pre-matrix job's name, so anything keyed on the # single "test" check (branch protection, badges) keeps resolving. Fails # unless every matrix shard and both platform integration jobs succeeded. test: needs: [tests, linux_host_clang, windows_cli] if: always() runs-on: ubuntu-latest steps: - name: All lanes green run: | test "${{ needs.tests.result }}" = success test "${{ needs.linux_host_clang.result }}" = success test "${{ needs.windows_cli.result }}" = success