feat(runtime): ship precompiled macOS artifacts #721
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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: brew install llvm@22 | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm --filter @scriptc/llvm-darwin-arm64 build:native | |
| - run: pnpm --filter @scriptc/runtime-darwin-arm64 build:native | |
| - 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/native-toolchain.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/native-toolchain.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" | |
| llvm_artifacts_macos_arm64: | |
| name: test (macOS arm64 LLVM artifacts, no clang, ${{ matrix.shard }}/3) | |
| runs-on: macos-15 | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3] | |
| env: | |
| # The helper differential uses the harness's stable per-case sharding, | |
| # keeping each macOS job below its timeout while the matrix union still | |
| # exercises every LLVM-tier program. | |
| 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: brew install llvm@22 | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm --filter @scriptc/llvm-darwin-arm64 build:native | |
| - run: pnpm --filter @scriptc/runtime-darwin-arm64 build:native | |
| - run: pnpm build | |
| - name: No-clang assembly/object contract | |
| if: matrix.shard == 1 | |
| run: pnpm test packages/cli/test/native-output.test.ts | |
| - name: Helper diagnostics and object/link parity | |
| if: matrix.shard == 1 | |
| run: >- | |
| pnpm test | |
| packages/compiler/test/native-codegen-integration.test.ts | |
| packages/compiler/src/backend/runtime-pack.test.ts | |
| packages/cli/test/native-link-info.test.ts | |
| packages/cli/test/runtime-pack.test.ts | |
| tests/harness/native-object-example.test.ts | |
| - name: LLVM-tier helper object differential (${{ matrix.shard }}/3) | |
| env: | |
| SCRIPTC_LLVM_HELPER_ONLY: "1" | |
| SCRIPTC_TEST_WORKERS: "4" | |
| run: pnpm test tests/harness/llvm-differential.test.ts | |
| - name: Packaged helper contract | |
| if: matrix.shard == 1 | |
| run: | | |
| TARBALL=$(pnpm --dir packages/llvm-darwin-arm64 pack --pack-destination "$RUNNER_TEMP" --silent) | |
| node scripts/verify-llvm-package.mjs "$RUNNER_TEMP/$(basename "$TARBALL")" | |
| - name: Packed npm installation smoke | |
| if: matrix.shard == 1 | |
| run: | | |
| pnpm --dir packages/runtime pack --pack-destination "$RUNNER_TEMP" --silent | |
| pnpm --dir packages/runtime-darwin-arm64 pack --pack-destination "$RUNNER_TEMP" --silent | |
| pnpm --dir packages/compiler pack --pack-destination "$RUNNER_TEMP" --silent | |
| pnpm --dir packages/cli pack --pack-destination "$RUNNER_TEMP" --silent | |
| PREFIX="$RUNNER_TEMP/installed-scriptc" | |
| npm install --prefix "$PREFIX" --ignore-scripts \ | |
| "$RUNNER_TEMP/scriptc-runtime-$(node -p "require('./packages/runtime/package.json').version").tgz" \ | |
| "$RUNNER_TEMP/scriptc-runtime-darwin-arm64-$(node -p "require('./packages/runtime-darwin-arm64/package.json').version").tgz" \ | |
| "$RUNNER_TEMP/scriptc-llvm-darwin-arm64-$(node -p "require('./packages/llvm-darwin-arm64/package.json').version").tgz" \ | |
| "$RUNNER_TEMP/scriptc-compiler-$(node -p "require('./packages/compiler/package.json').version").tgz" \ | |
| "$RUNNER_TEMP/scriptc-$(node -p "require('./packages/cli/package.json').version").tgz" | |
| "$PREFIX/node_modules/.bin/scriptc" build tests/corpus/001-hello.ts \ | |
| --emit=obj -o "$RUNNER_TEMP/installed.o" | |
| file "$RUNNER_TEMP/installed.o" | grep 'Mach-O 64-bit object arm64' | |
| "$PREFIX/node_modules/.bin/scriptc" build tests/corpus/001-hello.ts \ | |
| --print=native-link-info -o "$RUNNER_TEMP/installed-link.o" \ | |
| > "$RUNNER_TEMP/installed-link.json" | |
| node examples/native-object/link.mjs cc \ | |
| "$RUNNER_TEMP/installed-link.json" "$RUNNER_TEMP/installed-program" | |
| test "$("$RUNNER_TEMP/installed-program")" = 'hello world' | |
| # 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" | |
| } | |
| - name: Windows CA-store differential | |
| shell: pwsh | |
| run: | | |
| $nodeOutput = & node tests/corpus/2557-tls-ca-store.ts | |
| if ($LASTEXITCODE -ne 0) { | |
| exit $LASTEXITCODE | |
| } | |
| $nativeOutput = & node packages/cli/dist/main.js run tests/corpus/2557-tls-ca-store.ts --backend c | |
| if ($LASTEXITCODE -ne 0) { | |
| exit $LASTEXITCODE | |
| } | |
| $expected = ($nodeOutput -join "`n").Trim() | |
| $actual = ($nativeOutput -join "`n").Trim() | |
| if ($actual -ne $expected) { | |
| throw "Windows CA-store output differed:`nNODE:`n$expected`nNATIVE:`n$actual" | |
| } | |
| - name: Windows CA EKU policy | |
| run: >- | |
| pnpm exec vitest run packages/compiler/test/cc-driver.test.ts | |
| --testNamePattern "Windows CA roots honor" | |
| # 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, llvm_artifacts_macos_arm64, 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.llvm_artifacts_macos_arm64.result }}" = success | |
| test "${{ needs.windows_cli.result }}" = success |