mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-29 07:04:09 +08:00
The sanitized native emitter peaks near 13 GB while it emits the compiler and then compiles the 348 MB module with clang under AddressSanitizer, which together exceed the 16 GB runner. Back the rebuild-emit job with a 24 GiB swap file and lift its stack limit, since the instrumented emitter's recursion can exceed 8 MiB.
792 lines
33 KiB
YAML
792 lines
33 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
|
|
concurrency:
|
|
group: ci-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# GitHub recommends stack.base.ref == base.ref for the lowest unmerged PR:
|
|
# https://docs.github.com/en/pull-requests/how-tos/merge-and-close-pull-requests/optimizing-ci-for-stacked-pull-requests
|
|
# Only trusted, same-repository upper layers defer full CI. Forks, ordinary
|
|
# PRs, pushes and incomplete metadata always retain full validation.
|
|
validation_scope:
|
|
name: Select CI scope
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
outputs:
|
|
full: ${{ steps.scope.outputs.full }}
|
|
steps:
|
|
- id: scope
|
|
# Read GitHub's event directly; do not run scripts from a PR checkout.
|
|
run: |
|
|
node <<'NODE'
|
|
const { appendFileSync, readFileSync } = require('node:fs');
|
|
const event = JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8'));
|
|
const pr = event.pull_request;
|
|
const repository = event.repository?.full_name;
|
|
const sameRepository = typeof repository === 'string' && repository.length > 0 &&
|
|
pr?.head?.repo?.full_name === repository && pr?.base?.repo?.full_name === repository;
|
|
const trustedAuthor = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(pr?.author_association);
|
|
const stackBase = pr?.stack?.base?.ref;
|
|
const prBase = pr?.base?.ref;
|
|
// Position 1 can already be merged; compare the current base branches.
|
|
const upperLayer = process.env.GITHUB_EVENT_NAME === 'pull_request' &&
|
|
sameRepository && trustedAuthor && typeof stackBase === 'string' && stackBase.length > 0 &&
|
|
typeof prBase === 'string' && prBase.length > 0 && stackBase !== prBase;
|
|
const full = !upperLayer;
|
|
appendFileSync(process.env.GITHUB_OUTPUT, `full=${full}\n`);
|
|
console.log(full ? 'Run full CI' : 'Defer full CI until this PR becomes the lowest unmerged layer');
|
|
NODE
|
|
|
|
code_quality:
|
|
name: lint and format
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 10
|
|
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 lint
|
|
- run: pnpm format:check
|
|
- name: Code quality and CI policy contracts
|
|
run: pnpm test tests/harness/code-quality.test.ts tests/harness/ci-stack-scope.test.ts
|
|
env:
|
|
SCRIPTC_TEST_WORKERS: "1"
|
|
- name: Verify mixed-source generated AST getters
|
|
run: pnpm generate:ts7-ast --check
|
|
|
|
native_macos:
|
|
needs: validation_scope
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: build (macOS arm64 native packages)
|
|
runs-on: macos-15
|
|
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
|
|
- uses: ./.github/actions/native-packages
|
|
with:
|
|
target: darwin-arm64
|
|
- name: Preserve executable permissions in shared packages
|
|
run: >-
|
|
tar -czf "$RUNNER_TEMP/native-darwin-arm64.tar.gz"
|
|
packages/llvm-darwin-arm64/bin
|
|
packages/runtime-darwin-arm64/artifacts
|
|
packages/runtime-darwin-arm64/runtime-pack.json
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: native-darwin-arm64
|
|
path: ${{ runner.temp }}/native-darwin-arm64.tar.gz
|
|
retention-days: 1
|
|
|
|
tests:
|
|
needs: [validation_scope, native_macos]
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: test (${{ matrix.flavor }}, ${{ matrix.shard }}/10)
|
|
# 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, 4, 5, 6, 7, 8, 9, 10]
|
|
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, Test262, coverage, and
|
|
# Vercel E2E): partition stable case names into slice i of 10
|
|
# (tests/harness/shard.ts). The ten 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 }}/10
|
|
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
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: native-darwin-arm64
|
|
path: ${{ runner.temp }}
|
|
- run: tar -xzf "$RUNNER_TEMP/native-darwin-arm64.tar.gz"
|
|
- run: pnpm build
|
|
# Start the corpus immediately beside a serial side queue. One shared
|
|
# worker budget covers both processes; file and case shard axes stay
|
|
# separate so every fixture still runs exactly once in each lane.
|
|
- name: Tests (corpus and integration suites)
|
|
run: node scripts/ci-test.mjs
|
|
|
|
native_linux:
|
|
needs: validation_scope
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: build (Linux x64 native packages)
|
|
runs-on: ubuntu-24.04
|
|
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
|
|
- uses: ./.github/actions/llvm-linux
|
|
- run: pnpm install --frozen-lockfile
|
|
- uses: ./.github/actions/zig
|
|
- uses: ./.github/actions/native-packages
|
|
with:
|
|
target: linux-x64-gnu
|
|
- name: Preserve executable permissions in shared packages
|
|
run: >-
|
|
tar -czf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
|
|
packages/llvm-linux-x64-gnu/bin
|
|
packages/runtime-linux-x64-gnu/artifacts
|
|
packages/runtime-linux-x64-gnu/runtime-pack.json
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: native-linux-x64-gnu
|
|
path: ${{ runner.temp }}/native-linux-x64-gnu.tar.gz
|
|
retention-days: 1
|
|
|
|
effects:
|
|
needs: [validation_scope, native_linux]
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: test (Linux, ${{ matrix.flavor }}, Effect ${{ matrix.shard }}/16)
|
|
runs-on: ubuntu-24.04
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
flavor: [plain, san]
|
|
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
|
|
env:
|
|
# One compiler at a time leaves room for its 8 GiB heap, checker and
|
|
# LLVM on a 16 GB runner. Parallelism comes from independent shards.
|
|
# Both package versions retain every Node/native comparison in both
|
|
# flavors; Effect 3 plain also retains its release optimizer coverage.
|
|
SCRIPTC_TEST_WORKERS: "1"
|
|
SCRIPTC_EFFECT_TEST_CONCURRENCY: "1"
|
|
SCRIPTC_TEST_SHARD: ${{ matrix.shard }}/16
|
|
SCRIPTC_SAN: ${{ matrix.flavor == 'san' && '1' || '' }}
|
|
ASAN_OPTIONS: detect_leaks=0
|
|
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
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: native-linux-x64-gnu
|
|
path: ${{ runner.temp }}
|
|
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
|
|
- run: pnpm test tests/harness/effect.test.ts tests/harness/effect4.test.ts
|
|
|
|
# Build each instrumented seed once. Downstream contracts retain the same
|
|
# compiler and source artifacts without repeating seed construction.
|
|
bootstrap_seed:
|
|
needs: validation_scope
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: build (Linux, ${{ matrix.flavor }}, bootstrap seed)
|
|
runs-on: ubuntu-24.04
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
flavor: [plain, san]
|
|
env:
|
|
SCRIPTC_SAN: ${{ matrix.flavor == 'san' && '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
|
|
- uses: ./.github/actions/llvm-linux
|
|
- run: pnpm install --frozen-lockfile
|
|
- uses: ./.github/actions/zig
|
|
- uses: ./.github/actions/native-packages
|
|
with:
|
|
target: linux-x64-gnu
|
|
- uses: ./.github/actions/native-packages
|
|
with:
|
|
target: wasm32-wasi
|
|
helper: "false"
|
|
- run: pnpm build
|
|
- name: Prepare bootstrap seed
|
|
run: node scripts/bootstrap-seed.mjs
|
|
env:
|
|
SCRIPTC_BOOTSTRAP_SEED_DIRECTORY: ${{ runner.temp }}/native-bootstrap
|
|
- name: Archive seed and compiler artifacts
|
|
run: tar -C "$RUNNER_TEMP" -cf "$RUNNER_TEMP/bootstrap-seed.tar" native-bootstrap
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: bootstrap-seed-${{ matrix.flavor }}-${{ github.sha }}
|
|
path: ${{ runner.temp }}/bootstrap-seed.tar
|
|
retention-days: 1
|
|
if-no-files-found: error
|
|
|
|
bootstrap:
|
|
needs: [validation_scope, bootstrap_seed]
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: test (Linux, ${{ matrix.flavor }}, native bootstrap, ${{ matrix.phase }})
|
|
# These contracts are portable. Standard Linux runners have enough memory
|
|
# for LLVM optimization alongside the compiler that produces its input.
|
|
runs-on: ubuntu-24.04
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
# The sanitized self-rebuild is split at the IR boundary: one job
|
|
# lowers the compiler and compares its IR with the seed's; the other
|
|
# emits, links and probes the next generation from the seed's IR.
|
|
include:
|
|
- { flavor: plain, phase: commands }
|
|
- { flavor: plain, phase: rebuild }
|
|
- { flavor: san, phase: commands }
|
|
- { flavor: san, phase: rebuild-frontend }
|
|
- { flavor: san, phase: rebuild-emit }
|
|
env:
|
|
SCRIPTC_TEST_WORKERS: "1"
|
|
SCRIPTC_SAN: ${{ matrix.flavor == 'san' && '1' || '' }}
|
|
SCRIPTC_BOOTSTRAP_PHASE: ${{ matrix.phase }}
|
|
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
|
|
- uses: ./.github/actions/llvm-linux
|
|
- run: pnpm install --frozen-lockfile
|
|
- uses: ./.github/actions/zig
|
|
# Rebuilds use the relocated seed's packaged helper and runtimes.
|
|
- uses: ./.github/actions/native-packages
|
|
if: matrix.phase == 'commands'
|
|
with:
|
|
target: linux-x64-gnu
|
|
- uses: ./.github/actions/native-packages
|
|
if: matrix.phase == 'commands'
|
|
with:
|
|
target: wasm32-wasi
|
|
helper: "false"
|
|
- run: pnpm build
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: bootstrap-seed-${{ matrix.flavor }}-${{ github.sha }}
|
|
path: ${{ runner.temp }}
|
|
- name: Restore bootstrap seed
|
|
run: tar -C "$RUNNER_TEMP" -xf "$RUNNER_TEMP/bootstrap-seed.tar"
|
|
# The sanitized emitter peaks near 13 GB while it emits the compiler,
|
|
# then clang compiles the 348 MB module under AddressSanitizer. Together
|
|
# they exceed the runner's 16 GB of memory, so back them with swap.
|
|
- name: Enlarge swap for the sanitized emitter
|
|
if: matrix.phase == 'rebuild-emit'
|
|
run: |
|
|
sudo swapoff -a
|
|
sudo rm -f /mnt/swapfile
|
|
sudo fallocate -l 24G /mnt/swapfile
|
|
sudo chmod 600 /mnt/swapfile
|
|
sudo mkswap /mnt/swapfile
|
|
sudo swapon /mnt/swapfile
|
|
free -h
|
|
- name: Native bootstrap and package installation checks
|
|
# The instrumented emitter's recursion can exceed the default 8 MiB stack.
|
|
run: |
|
|
if [ "$SCRIPTC_BOOTSTRAP_PHASE" = rebuild-emit ]; then ulimit -s unlimited; fi
|
|
node scripts/ci-native-bootstrap.mjs
|
|
env:
|
|
SCRIPTC_BOOTSTRAP_SEED_DIRECTORY: ${{ runner.temp }}/native-bootstrap
|
|
SCRIPTC_BOOTSTRAP_PACKAGE_DIR: ${{ runner.temp }}/native-cli-package
|
|
|
|
# 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:
|
|
needs: validation_scope
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
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 and sanitizer build regressions
|
|
run: >-
|
|
pnpm test packages/compiler/test/cc-driver.test.ts
|
|
packages/compiler/src/native/sanitizer.test.ts
|
|
--testNamePattern "host-native clang static build|sanitized vendor archives"
|
|
|
|
# The TS7 parity sweep exercises the compiler frontend, so it can run
|
|
# alongside the Linux LLVM/runtime package build instead of following it.
|
|
ts7_parity:
|
|
needs: validation_scope
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: test (TypeScript 7 parity)
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 10
|
|
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: TypeScript 7 full parity sweep
|
|
run: pnpm test:ts7
|
|
|
|
llvm_artifacts_linux_x64:
|
|
needs: validation_scope
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: test (Linux x64 LLVM artifacts, no clang)
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 30
|
|
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
|
|
- uses: ./.github/actions/llvm-linux
|
|
- run: pnpm install --frozen-lockfile
|
|
- uses: ./.github/actions/zig
|
|
- uses: ./.github/actions/native-packages
|
|
with:
|
|
target: linux-x64-gnu
|
|
- name: Build and verify Linux x64 musl release packages
|
|
run: |
|
|
pnpm --filter @scriptc/llvm-linux-x64-musl build:native
|
|
CC=zig AR=zig pnpm --filter @scriptc/runtime-linux-x64-musl build:native
|
|
helper=packages/llvm-linux-x64-musl/bin/scriptc-llvm-codegen
|
|
readelf -l "$helper" > "$RUNNER_TEMP/musl-helper-program-headers"
|
|
if grep -q INTERP "$RUNNER_TEMP/musl-helper-program-headers"; then
|
|
echo "musl helper must be a static executable" >&2
|
|
exit 1
|
|
fi
|
|
docker run --rm --volume "$GITHUB_WORKSPACE:/work:ro" alpine:3.22 \
|
|
/work/$helper version --format=json | node -e '
|
|
const value = JSON.parse(require("node:fs").readFileSync(0, "utf8"));
|
|
if (value.default_target !== "x86_64-unknown-linux-musl" || !value.supported_targets?.includes("wasm32-unknown-wasi")) process.exit(1);
|
|
'
|
|
node packages/runtime-linux-x64-musl/scripts/verify.mjs
|
|
- uses: ./.github/actions/native-packages
|
|
with:
|
|
target: wasm32-wasi
|
|
helper: "false"
|
|
- run: pnpm build
|
|
- name: Linux helper object format and no-clang output contract
|
|
run: |
|
|
PATH="$RUNNER_TEMP/traps:$PATH"
|
|
mkdir -p "$RUNNER_TEMP/traps"
|
|
for tool in clang cc gcc ar ld; do printf '#!/bin/sh\nexit 97\n' > "$RUNNER_TEMP/traps/$tool"; chmod +x "$RUNNER_TEMP/traps/$tool"; done
|
|
node packages/cli/dist/main.js build tests/corpus/001-hello.ts --emit=obj -o "$RUNNER_TEMP/hello.o"
|
|
file "$RUNNER_TEMP/hello.o" | grep 'ELF 64-bit LSB relocatable, x86-64'
|
|
- name: Linux helper/runtime-pack executable differential smoke
|
|
env:
|
|
SCRIPTC_NO_CACHE: "1"
|
|
SCRIPTC_LEGACY_C_PIPELINE: "0"
|
|
run: |
|
|
node packages/cli/dist/main.js build tests/corpus/001-hello.ts -o "$RUNNER_TEMP/hello"
|
|
test "$("$RUNNER_TEMP/hello")" = 'hello world'
|
|
- name: Linux release runtime reachability and debug payload
|
|
run: pnpm test tests/harness/runtime-tree-shaking.test.ts
|
|
- name: Debian 12 glibc 2.36 runtime-pack smoke
|
|
run: |
|
|
docker run --rm --volume "$GITHUB_WORKSPACE:/work:ro" --workdir /work node:24-bookworm-slim sh -ec '
|
|
apt-get update
|
|
apt-get install --yes --no-install-recommends clang
|
|
SCRIPTC_NO_CACHE=1 SCRIPTC_LEGACY_C_PIPELINE=0 node packages/cli/dist/main.js build tests/corpus/001-hello.ts -o /tmp/hello
|
|
test "$(/tmp/hello)" = "hello world"
|
|
'
|
|
- name: WASI helper object/runtime-pack smoke
|
|
run: pnpm test packages/cli/test/wasi-runtime-pack.test.ts tests/harness/wasm-library.test.ts
|
|
|
|
llvm_artifacts_macos_arm64:
|
|
needs: [validation_scope, native_macos]
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: test (macOS arm64 LLVM artifacts, no clang, ${{ matrix.shard }}/5)
|
|
runs-on: macos-15
|
|
# Shard 1 additionally runs package/install and link-parity contracts
|
|
# before its LLVM differential slice. The slice itself can take ~10
|
|
# minutes on a cold hosted runner, so 20 minutes cancels an otherwise
|
|
# healthy job before the differential completes.
|
|
timeout-minutes: 35
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
shard: [1, 2, 3, 4, 5]
|
|
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 }}/5
|
|
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
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: native-darwin-arm64
|
|
path: ${{ runner.temp }}
|
|
- run: tar -xzf "$RUNNER_TEMP/native-darwin-arm64.tar.gz"
|
|
- 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 release/dev differential (${{ matrix.shard }}/5)
|
|
env:
|
|
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
|
|
env:
|
|
# Release optimization is covered by the compiler bootstrap jobs.
|
|
# This lane verifies packaging and installation with a faster seed.
|
|
SCRIPTC_NATIVE_OPTIMIZATION: dev
|
|
run: |
|
|
pnpm --filter @scriptc/cli-darwin-arm64 build:native
|
|
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-darwin-arm64 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" --no-audit --no-fund \
|
|
"$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-cli-darwin-arm64-$(node -p "require('./packages/cli-darwin-arm64/package.json').version").tgz" \
|
|
"$RUNNER_TEMP/scriptc-$(node -p "require('./packages/cli/package.json').version").tgz"
|
|
PATH="" "$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'
|
|
PATH="" "$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'
|
|
|
|
llvm_artifacts_macos_x64:
|
|
needs: validation_scope
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: test (macOS x64 LLVM artifacts, no clang)
|
|
runs-on: macos-15-intel
|
|
timeout-minutes: 20
|
|
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
|
|
- uses: ./.github/actions/native-packages
|
|
with:
|
|
target: darwin-x64
|
|
- run: pnpm build
|
|
- name: Intel Mach-O no-clang object contract
|
|
run: |
|
|
mkdir -p "$RUNNER_TEMP/traps"
|
|
for tool in clang cc gcc ar ld; do printf '#!/bin/sh\nexit 97\n' > "$RUNNER_TEMP/traps/$tool"; chmod +x "$RUNNER_TEMP/traps/$tool"; done
|
|
PATH="$RUNNER_TEMP/traps:$PATH" node packages/cli/dist/main.js build tests/corpus/001-hello.ts --emit=obj -o "$RUNNER_TEMP/hello.o"
|
|
file "$RUNNER_TEMP/hello.o" | grep 'Mach-O 64-bit object x86_64'
|
|
- name: Intel helper/runtime-pack executable smoke
|
|
env:
|
|
SCRIPTC_NO_CACHE: "1"
|
|
SCRIPTC_LEGACY_C_PIPELINE: "0"
|
|
run: |
|
|
node packages/cli/dist/main.js build tests/corpus/001-hello.ts -o "$RUNNER_TEMP/hello"
|
|
test "$($RUNNER_TEMP/hello)" = '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)
|
|
needs: [validation_scope, llvm_artifacts_windows_x64]
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
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: ./.github/actions/zig
|
|
- 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
|
|
}
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: windows-native-packages
|
|
path: packages
|
|
- run: pnpm build
|
|
- name: Windows path regressions
|
|
run: pnpm exec vitest run packages/compiler/test/ts7/program-adapter.test.ts packages/compiler/test/ts7/resolver-parity.test.ts packages/compiler/src/native/toolchain.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 llvm
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
$actual = ($output -join "`n").Trim()
|
|
if ($actual -ne "hello world") {
|
|
throw "unexpected scriptc output: $actual"
|
|
}
|
|
- name: Windows PE subsystem selection
|
|
env:
|
|
SCRIPTC_NO_CACHE: "0"
|
|
run: pnpm exec vitest run packages/cli/test/windows-subsystem.test.ts
|
|
- name: Windows utility runtime linking
|
|
run: pnpm exec vitest run packages/compiler/test/util-runtime-linking.test.ts
|
|
- name: Windows filesystem links differential
|
|
run: >-
|
|
pnpm exec vitest run tests/harness/differential.test.ts tests/harness/project-config.test.ts
|
|
--testNamePattern "node24-fs-links|filesystem links"
|
|
- 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 llvm
|
|
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"
|
|
|
|
llvm_artifacts_windows_x64:
|
|
needs: validation_scope
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: test (Windows x64 MSVC LLVM artifacts)
|
|
# The official LLVM 22 Windows archives are MSVC-built and the helper
|
|
# selects CMake's VS 2022 generator. `windows-latest` can move to an
|
|
# image without that generator, so retain the hosted VS 2022 contract.
|
|
runs-on: windows-2022
|
|
timeout-minutes: 30
|
|
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
|
|
- name: Install pinned LLVM development distribution
|
|
shell: pwsh
|
|
run: |
|
|
# windows-2022 already provides CMake, Ninja, VS 2022, and 7-Zip.
|
|
# Use the complete archive because it supplies LLVM's exported CMake
|
|
# targets and static libraries. Windows tar.exe takes 25+ minutes to
|
|
# materialize it; two-stage 7-Zip extraction takes seconds.
|
|
$archive = "$env:RUNNER_TEMP\clang+llvm-22.1.8-x86_64-pc-windows-msvc.tar.xz"
|
|
$llvm = "$env:RUNNER_TEMP\llvm-22.1.8"
|
|
$expanded = "$env:RUNNER_TEMP\llvm-expanded"
|
|
$sevenZip = "$env:ProgramFiles\7-Zip\7z.exe"
|
|
if (-not (Test-Path $sevenZip)) {
|
|
choco install 7zip --no-progress -y
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
}
|
|
if (-not (Test-Path $sevenZip)) { throw "7-Zip is unavailable" }
|
|
curl.exe -fL --retry 3 -o $archive https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.8/clang%2Bllvm-22.1.8-x86_64-pc-windows-msvc.tar.xz
|
|
& $sevenZip x -y "-o$expanded" $archive
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
& $sevenZip x -y "-o$expanded" "$expanded\clang+llvm-22.1.8-x86_64-pc-windows-msvc.tar"
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
Move-Item "$expanded\clang+llvm-22.1.8-x86_64-pc-windows-msvc" $llvm
|
|
echo "LLVM_DIR=$llvm\lib\cmake\llvm" >> $env:GITHUB_ENV
|
|
echo "$llvm\bin" >> $env:GITHUB_PATH
|
|
- run: pnpm install --frozen-lockfile
|
|
- name: Build Windows helper
|
|
run: pnpm --filter @scriptc/llvm-win32-x64-msvc build:native
|
|
- uses: ./.github/actions/zig
|
|
- name: Build Windows runtime pack
|
|
run: pnpm --filter @scriptc/runtime-win32-x64-msvc build:native
|
|
- run: pnpm build
|
|
- name: Windows COFF no-clang object contract
|
|
shell: pwsh
|
|
run: |
|
|
node packages/cli/dist/main.js build tests/corpus/001-hello.ts --emit=obj -o "$env:RUNNER_TEMP\hello.obj"
|
|
$bytes = [System.IO.File]::ReadAllBytes("$env:RUNNER_TEMP\hello.obj")
|
|
if ($bytes[0] -ne 0x64 -or $bytes[1] -ne 0x86) { throw "expected AMD64 COFF object" }
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: windows-native-packages
|
|
path: |
|
|
packages/llvm-win32-x64-msvc/bin
|
|
packages/runtime-win32-x64-msvc/artifacts
|
|
packages/runtime-win32-x64-msvc/runtime-pack.json
|
|
retention-days: 1
|
|
|
|
mobile_runtime_packs:
|
|
needs: validation_scope
|
|
if: ${{ needs.validation_scope.outputs.full == 'true' }}
|
|
name: test (${{ matrix.runtime }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- runtime: runtime-ios-arm64
|
|
runner: macos-15
|
|
- runtime: runtime-ios-simulator-arm64
|
|
runner: macos-15
|
|
- runtime: runtime-android-arm64
|
|
runner: ubuntu-24.04
|
|
runs-on: ${{ matrix.runner }}
|
|
timeout-minutes: 20
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: ./.github/actions/mobile-runtime-pack
|
|
with:
|
|
runtime: ${{ matrix.runtime }}
|
|
|
|
# Retain the pre-matrix "test" check for branch protection and badges.
|
|
# Require this check on the stack base. Upper layers must not pass before
|
|
# full validation, so untested groups cannot merge. Merge bottom-up.
|
|
test:
|
|
needs: [validation_scope, code_quality, native_macos, tests, native_linux, effects, bootstrap_seed, bootstrap, linux_host_clang, ts7_parity, llvm_artifacts_linux_x64, llvm_artifacts_macos_arm64, llvm_artifacts_macos_x64, windows_cli, llvm_artifacts_windows_x64, mobile_runtime_packs]
|
|
# Run even after failed or cancelled lanes: skipped required jobs allow merging.
|
|
if: ${{ always() }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- name: All lanes green
|
|
run: |
|
|
test "${{ needs.validation_scope.result }}" = success
|
|
test "${{ needs.code_quality.result }}" = success
|
|
case "${{ needs.validation_scope.outputs.full }}" in
|
|
true)
|
|
test "${{ needs.native_macos.result }}" = success
|
|
test "${{ needs.tests.result }}" = success
|
|
test "${{ needs.native_linux.result }}" = success
|
|
test "${{ needs.effects.result }}" = success
|
|
test "${{ needs.bootstrap_seed.result }}" = success
|
|
test "${{ needs.bootstrap.result }}" = success
|
|
test "${{ needs.linux_host_clang.result }}" = success
|
|
test "${{ needs.ts7_parity.result }}" = success
|
|
test "${{ needs.llvm_artifacts_linux_x64.result }}" = success
|
|
test "${{ needs.llvm_artifacts_macos_arm64.result }}" = success
|
|
test "${{ needs.llvm_artifacts_macos_x64.result }}" = success
|
|
test "${{ needs.windows_cli.result }}" = success
|
|
test "${{ needs.llvm_artifacts_windows_x64.result }}" = success
|
|
test "${{ needs.mobile_runtime_packs.result }}" = success
|
|
;;
|
|
false)
|
|
echo "::notice::Full CI is deferred until this PR becomes the lowest unmerged layer. Merge the stack from the bottom up."
|
|
exit 1
|
|
;;
|
|
*)
|
|
echo "Invalid CI scope"
|
|
exit 1
|
|
;;
|
|
esac
|