Files
scriptc/RELEASING.md
T
Chris Tate d1a0bea84e Have the native CLI packages depend on their LLVM helper and runtime pack
Each @scriptc/cli-<platform> package bundled its platform's LLVM helper
and runtime pack, which are also published as @scriptc/llvm-<platform> and
@scriptc/runtime-<platform>. The CLI packages now depend on those packages
at the same version (workspace:*, which pnpm pack writes as the exact
version) and leave them out: cli-linux-x64-gnu goes from 115 MB to 60 MB
packed.

- build-native-cli.mts --platform-packages, which each CLI package's
  build:native passes, writes a manifest without llvm_package, runtime_pack
  and runtime_packs.
- loadNativeToolchain resolves a missing helper or host runtime pack as
  Node resolves the package from the compiler's directory: nested in the
  platform package, beside it (hoisted, or pnpm's virtual store) or higher
  up. It requires the compiler's version, and an absent or mismatched
  package fails with an error that names it.
- installNativeCli records where the package manager put both packages in
  the installed manifest, with the same checks.
- verify-native-cli.mjs requires the exact dependencies of a platform
  package and checks the resolved packages' versions.
- package-native-cli.mjs bundles the helper and runtime pack into the
  standalone archives, which stay self-contained.
- smoke-native-install.mjs installs the helper and runtime pack tarballs
  with the platform package; the bootstrap's exported package drops its
  bundled copies first.
- release.yml publishes the CLI packages only once every helper and
  runtime pack package resolves on npm, and scriptc only once the CLI
  packages resolve, each wait at most 25 minutes. A rerun skips the
  uploads npm already accepted and waits again.
2026-10-26 07:48:11 -05:00

5.3 KiB

Releasing

Releases are manual, single-commit affairs. The maintainer controls the changelog voice and format. @scriptc/runtime, every platform helper/runtime pack, @scriptc/compiler, @scriptc/experimental, @scriptc/threads, and scriptc always publish together at the same version.

To prepare a release:

  1. Bump the version in packages/cli/package.json
  2. Run node scripts/sync-versions.mjs to stamp the same version into the runtime, helper, compiler, experimental, and threads packages, then pnpm manifest to restamp packages/compiler/surface-manifest.json with the new version, and commit both (the test suite's staleness guard fails on a version drift)
  3. Fold the ## Unreleased section of CHANGELOG.md into a new ## <version> entry (newest first, below ## Unreleased), and leave ## Unreleased empty for the next cycle
  4. Wrap the new entry in <!-- release:start --> and <!-- release:end --> markers; this marked block is also the GitHub release body
  5. Remove the <!-- release:start --> and <!-- release:end --> markers from the previous release entry; only the latest release should have markers
  6. With Zig on PATH, run SCRIPTC_CROSS=1 pnpm exec vitest run tests/harness/library-cross.test.ts and require the cross-target library conformance lane to pass
  7. Commit to main

CI (.github/workflows/release.yml) compares the version in packages/cli/package.json to what scriptc has on npm. If it differs, it builds platform packages on matching macOS, Linux, and Windows runners, verifies the version spine, and publishes the runtime, helper, compiler, and CLI packages in dependency order. After the publish succeeds, a separate job creates the git tag v<version> and the GitHub release with the marked changelog entry as its body, and attaches surface-manifest.json — the machine-readable listing of the surface the static tier compiles at that version (stable per-entry ids, so two releases diff mechanically; see packages/compiler/src/coverage/surface-manifest.ts for the schema). The job regenerates the manifest from the tree and fails on any byte difference from the committed file before attaching, so the asset is always the manifest of the code being released. The same file ships inside the @scriptc/compiler package as @scriptc/compiler/surface-manifest.json.

The release job builds each pinned LLVM helper, precompiled runtime pack, and native CLI on its matching host. The native CLI package bundles its TypeScript checker, declarations, and comptime evaluator, and depends on its platform's LLVM helper and runtime pack packages at the same version; the compiler resolves them beside itself. The standalone archives bundle the helper and runtime pack too. npm postinstall installs the matching native executable; Node is needed for npm installation but not native compilation or execution. The user's toolchain performs the final platform link. Sanitized builds also use it to instrument program LLVM and compile the C runtime. Platform packages publish ahead of @scriptc/compiler and scriptc. The GitHub release attaches standalone compiler archives, their SHA-256 checksums, and the surface manifest after npm publication succeeds.

Release builds of the native CLI optimize the compiler with a profile collected on the release commit. The cli-profile job runs node scripts/refresh-cli-profile.mjs on Linux x64, which builds an instrumented compiler, runs it on tsc-ts, a slice of the corpus, and the runtime benchmark workloads, and writes the merged sparse profile. Every platform's CLI build uses that profile (SCRIPTC_NATIVE_PROFILE_USE), and the GitHub release attaches it as scriptc-<version>-cli.profdata.gz. Code the profile does not describe, or describes from older compiler sources, is optimized as without a profile. When the profile job fails, the CLI builds use the committed profile packages/compiler/native/cli.profdata.gz, which local and CI builds of the native compiler also use. Refresh the committed profile when compiler code has changed substantially since the last refresh: run node scripts/refresh-cli-profile.mjs --tsc-ts <tsc-ts checkout> on a quiet Linux x64 host and commit the new profile on its own.

Builds of the native compiler also order its functions by the committed layout packages/compiler/native/cli.layout.gz: the functions that --version and rebuilds of unchanged programs run come first, then the ones other builds run, ordered by the share of the layout's training builds that run each, so a run maps fewer pages of the executable. Functions the layout does not list keep their usual order. Function names of closures and generated helpers change with nearby edits, so the cli-profile job also collects a layout on the release commit (scripts/refresh-cli-layout.mjs, reusing the job's instrumented compiler), and the CLI builds use it (SCRIPTC_NATIVE_LAYOUT). Refresh the committed layout the same way as the profile, with node scripts/refresh-cli-layout.mjs, and commit it on its own.

Publishing uses npm trusted publishing (OIDC) — there is no npm token secret. Each published package must have a GitHub Actions trusted publisher for release.yml and the Release environment. A missing configuration fails before upload. Re-runs skip package versions already present on npm, so a partially published release can be resumed safely.