diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1b1cff25..d7dd69b7d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ # Format, lint, build and test on every pull request and on each push to main that changes code. # The full protected set (TestLocal shards, gate, bound runs, LSP and API oracles, np-suite) stays in # the revision pipeline (scripts/goport/candidate.sh). This workflow is a fast check that the tree -# builds and its own tests pass on Linux and macOS. +# builds and its own tests pass on Linux (x64 and arm64) and macOS. name: CI on: @@ -58,7 +58,7 @@ jobs: fail-fast: false matrix: # Blacksmith runners (the pingdotgg org has the app), as T3 Code uses. - runner: [blacksmith-16vcpu-ubuntu-2404, blacksmith-12vcpu-macos-26] + runner: [blacksmith-16vcpu-ubuntu-2404, blacksmith-16vcpu-ubuntu-2404-arm, blacksmith-12vcpu-macos-26] runs-on: ${{ matrix.runner }} timeout-minutes: 120 env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1d1b850ef..bf9648a8d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,17 +1,18 @@ -# Builds, packs and tests the tsc-rs release: the npm packages tsc-rs, @tsc-rs/linux-x64 and -# @tsc-rs/darwin-arm64, and one standalone archive per platform (tsc and the lib files). +# Builds, packs and tests the tsc-rs release: the npm packages tsc-rs, @tsc-rs/linux-x64, +# @tsc-rs/linux-arm64 and @tsc-rs/darwin-arm64, and one standalone archive per platform (tsc and +# the lib files). # # A tag v also publishes the npm packages and creates a GitHub release. A stable version # (v0.1.0) goes to the dist-tag `latest` and a normal release. A prerelease version (v0.2.0-beta.1) # goes to `next` and a GitHub prerelease. npm trusted publishing (OIDC) authenticates the -# publish: no npm token. Each of the 3 packages trusts this workflow file and the environment `npm` +# publish: no npm token. Each of the 4 packages trusts this workflow file and the environment `npm` # (npm/README.md, "Trusted publishing"). Pull requests that change the release files, and a manual # run, do everything but the release and the publish. # # These builds are the plain shipped profile (fat LTO), without the PGO and BOLT of # crates/ts_goport/scripts/build-release.sh, which needs a host with BOLT and the project inputs. -# Linux is static musl and non-PIE, so it starts on any x86-64 Linux, Alpine too (npm/README.md, -# "Linux libc"). +# Linux is static musl and non-PIE, so it starts on any x86-64 or arm64 Linux, Alpine too +# (npm/README.md, "Linux libc"). Each Linux tsc builds natively on a Blacksmith runner of its arch. name: Release on: @@ -73,6 +74,9 @@ jobs: - platform: linux-x64 target: x86_64-unknown-linux-musl runner: blacksmith-16vcpu-ubuntu-2404 + - platform: linux-arm64 + target: aarch64-unknown-linux-musl + runner: blacksmith-16vcpu-ubuntu-2404-arm # jemalloc does not cross-build for macOS with zig, so this builds on a Mac. - platform: darwin-arm64 target: aarch64-apple-darwin @@ -97,7 +101,11 @@ jobs: - name: Build run: | if [[ $RUNNER_OS == Linux ]]; then - export CC_x86_64_unknown_linux_musl=musl-gcc + target=${{ matrix.target }} + export "CC_${target//-/_}=musl-gcc" + # jemalloc stops at start when the kernel page is bigger than its build page. arm64 kernels + # use 4, 16 or 64 KiB pages; 2^16 covers all three. + [[ $target != aarch64-* ]] || export JEMALLOC_SYS_WITH_LG_PAGE=16 export RUSTFLAGS="-C target-feature=+crt-static -C relocation-model=static" JEMALLOC_SYS_WITH_MALLOC_CONF=$(sed -n 's/^const JEMALLOC_CONF: &str = "\([^"]*\)";$/\1/p' crates/ts_goport/src/bin/tsgo.rs | head -1) [[ -n $JEMALLOC_SYS_WITH_MALLOC_CONF ]] || { echo "::error::no JEMALLOC_CONF in bin/tsgo.rs"; exit 1; } @@ -179,14 +187,15 @@ jobs: bins="$RUNNER_TEMP/bins" chmod +x "$bins"/tsc-*/tsc scripts/goport/npm-pack.sh --name tsc-rs --package-version "$VERSION" \ - --also "darwin-arm64=$bins/tsc-darwin-arm64/tsc" "$RUNNER_TEMP/pkg" "$bins/tsc-linux-x64/tsc" + --also "linux-arm64=$bins/tsc-linux-arm64/tsc" --also "darwin-arm64=$bins/tsc-darwin-arm64/tsc" \ + "$RUNNER_TEMP/pkg" "$bins/tsc-linux-x64/tsc" # One archive per platform: the platform package's lib dir (tsc and the lib files, which a # noembed tsc reads next to it), LICENSE and NOTICE.txt. - name: Archives run: | pkg="$RUNNER_TEMP/pkg" dist="$RUNNER_TEMP/dist" mkdir -p "$dist" - for platform in linux-x64 darwin-arm64; do + for platform in linux-x64 linux-arm64 darwin-arm64; do name="tsc-rs-$VERSION-$platform" mkdir -p "$RUNNER_TEMP/archive/$name" cp -r "$pkg/tsc-rs-$platform/lib/." "$pkg/tsc-rs-$platform/LICENSE" "$pkg/tsc-rs-$platform/NOTICE.txt" "$RUNNER_TEMP/archive/$name/" @@ -201,7 +210,9 @@ jobs: path: ${{ runner.temp }}/dist if-no-files-found: error - # Installs the npm packages in a fresh project on each platform, as a user would. + # Installs the npm packages in a fresh project on each platform, as a user would. These are + # GitHub-hosted runners, so the check runs on another host than the build. ubuntu-24.04-arm is + # GitHub's arm64 standard runner (2 vCPU in a private repo, 4 in a public one). verify: name: verify (${{ matrix.platform }}) needs: [version, package] @@ -211,6 +222,8 @@ jobs: include: - platform: linux-x64 runner: ubuntu-latest + - platform: linux-arm64 + runner: ubuntu-24.04-arm - platform: darwin-arm64 runner: macos-latest runs-on: ${{ matrix.runner }} @@ -280,7 +293,8 @@ jobs: tag=latest [[ $VERSION != *-* ]] || tag=next echo "dist-tag $tag" - for pkg in @tsc-rs/linux-x64:tsc-rs-linux-x64 @tsc-rs/darwin-arm64:tsc-rs-darwin-arm64 tsc-rs:tsc-rs; do + for pkg in @tsc-rs/linux-x64:tsc-rs-linux-x64 @tsc-rs/linux-arm64:tsc-rs-linux-arm64 \ + @tsc-rs/darwin-arm64:tsc-rs-darwin-arm64 tsc-rs:tsc-rs; do name=${pkg%%:*} file=./dist/${pkg#*:}-$VERSION.tgz [[ -f $file ]] || { echo "::error::missing package $file"; exit 1; } if npm view "$name@$VERSION" version > /dev/null 2>&1; then diff --git a/README.md b/README.md index 1cb364e01..0c9cc74c5 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ npx tsc-rs -p tsconfig.json with the `typescript` package. Each [release](https://github.com/pingdotgg/ts-rust/releases) also has a standalone archive per platform: the `tsc` binary with the lib files next to it. -Platforms: Linux x64 (static, any distribution) and macOS arm64. Windows and Linux arm64 are not -available yet. +Platforms: Linux x64 (static, any distribution) and macOS arm64. Linux arm64 (static) comes in +the first release after 0.1.0. Windows is not available yet. To use it in VS Code, see the [npm package README](npm/tsc-rs-readme.md#vs-code). diff --git a/npm/README.md b/npm/README.md index 4deb778ab..dff5666a1 100644 --- a/npm/README.md +++ b/npm/README.md @@ -53,7 +53,7 @@ is not the default. `tsc-rs` is the same set under the port's own names: the main package `tsc-rs` (bin `tsc-rs`, so it does not clash with the `tsc` of a `typescript` install) and one platform package per platform, -`@tsc-rs/linux-x64` and `@tsc-rs/darwin-arm64`. Its `lib/getExePath.js` finds +`@tsc-rs/linux-x64`, `@tsc-rs/linux-arm64` and `@tsc-rs/darwin-arm64`. Its `lib/getExePath.js` finds `@tsc-rs/-`. The npm version (`--package-version`) is the port's own. The tsc is not stamped with it: it reports @@ -66,11 +66,13 @@ The release workflow (`.github/workflows/release.yml`) makes a release: 1. Push a tag `v`, for example `git tag v0.1.0 && git push origin v0.1.0`. The npm version is the tag without the `v`. -2. It builds the tsc on Linux (static musl, non-PIE: it starts on any x86-64 Linux) and on a Mac - (jemalloc does not cross-build for macOS with zig), from the same source. -3. It packs the set with `npm-pack.sh --name tsc-rs --package-version --also darwin-arm64=`, +2. It builds the tsc on Linux x64 and Linux arm64 (static musl, non-PIE: it starts on any Linux of + its arch; each builds natively on a Blacksmith runner of that arch) and on a Mac (jemalloc does + not cross-build for macOS with zig), from the same source. +3. It packs the set with `npm-pack.sh --name tsc-rs --package-version --also linux-arm64= --also darwin-arm64=`, makes one archive per platform (the tsc, the lib files, LICENSE and NOTICE.txt) and installs - the packages in a fresh project on each platform. + the packages in a fresh project on each platform (GitHub-hosted `ubuntu-latest`, + `ubuntu-24.04-arm` and `macos-latest`). 4. It publishes the platform packages, then `tsc-rs`, and creates a GitHub release with the archives, the .tgz files and `SHA256SUMS`. A stable version goes to the dist-tag `latest` and a normal release. A version with a prerelease part (`0.2.0-beta.1`) goes to `next` and a GitHub @@ -89,6 +91,7 @@ and pack and test it by hand: ```sh GOPORT_PIN= scripts/goport/npm-pack.sh --name tsc-rs --package-version \ + --also linux-arm64= \ --also darwin-arm64= /pkg /linux-x64/bin/tsgo scripts/goport/npm-test.sh --name tsc-rs /pkg /test ``` @@ -96,7 +99,7 @@ scripts/goport/npm-test.sh --name tsc-rs /pkg /test ### Trusted publishing npm trusts the workflow file `release.yml` of `pingdotgg/ts-rust`, in the GitHub environment -`npm`, to publish each of the 3 packages (OIDC, no token). Set it up once with +`npm`, to publish each of the 4 packages (OIDC, no token). Set it up once with `npm/trust-setup.sh`, logged in to npm with 2FA and npm 11.15.0 or later. It publishes a `0.0.0-placeholder` version of a package that is not on npm yet, because npm can only trust a workflow for a package that exists. The workflow refuses `0.0.x` tags, so a release never @@ -104,9 +107,11 @@ collides with a placeholder. npm drops a new trust that publishes nothing in 2 d shortly before the first tag. Add required reviewers to the `npm` environment (repo settings, Environments) to approve each publish by hand. -The first publish binds each trust to the repo's GitHub ID, not only its name. The repo was -recreated on 2026-10-07 (history cleanup), so before the next release run -`npm/trust-setup.sh --relink`: it revokes the old trusts and creates new ones for the new repo. +The first publish binds each trust to the repo's GitHub ID, not only its name. After the repo is +recreated, run `npm/trust-setup.sh --relink`: it revokes the old trusts and creates new ones for +the new repo. The 2026-10-07 recreation needs no relink: v0.1.0 already published from the new +repo. To add a platform package, run plain `npm/trust-setup.sh` shortly before the next tag: it +publishes the new package's placeholder, adds its trust and leaves the other packages as they are. While the repo is private, npm publishes with no provenance. From a public repo it adds provenance by itself. diff --git a/npm/trust-setup.sh b/npm/trust-setup.sh index e562357c1..5a69b4b7a 100755 --- a/npm/trust-setup.sh +++ b/npm/trust-setup.sh @@ -2,7 +2,8 @@ # One-time setup of npm trusted publishing for the tsc-rs packages. Run it by hand, logged in to # npm as an owner of tsc-rs and the @tsc-rs org, with 2FA on (npm asks for a code once). # -# For each package (tsc-rs, @tsc-rs/linux-x64, @tsc-rs/darwin-arm64) it: +# For each package (tsc-rs, @tsc-rs/linux-x64, @tsc-rs/linux-arm64, +# @tsc-rs/darwin-arm64) it: # 1. publishes a 0.0.0-placeholder version when the package is not on npm yet: npm can only # trust a workflow for a package that exists. The release workflow refuses 0.0.x tags, so a # release never collides with a placeholder (tsc-rs has a 0.0.1 placeholder). @@ -11,15 +12,16 @@ # revoke command (it does not revoke by itself). # npm drops a new trust that publishes nothing in 2 days. So run this shortly before the first tag. # The first publish binds a trust to the repo's GitHub ID, not only its name. After the repo is -# recreated (as on 2026-10-07), run it with --relink: it revokes every trust of each package first. +# recreated, run it with --relink: it revokes every trust of each package first. The 2026-10-07 +# recreation needs no relink: v0.1.0 already published from the new repo. # # usage: npm/trust-setup.sh [--relink] needs npm 11.15.0 or later (npm trust) set -euo pipefail -[[ ${1:-} != help ]] || { sed -n '2,16p' "$0" >&2; exit 2; } +[[ ${1:-} != help ]] || { sed -n '2,18p' "$0" >&2; exit 2; } relink=0 [[ ${1:-} != --relink ]] || relink=1 repo=pingdotgg/ts-rust -packages=(tsc-rs @tsc-rs/linux-x64 @tsc-rs/darwin-arm64) +packages=(tsc-rs @tsc-rs/linux-x64 @tsc-rs/linux-arm64 @tsc-rs/darwin-arm64) npm_version=$(npm --version) [[ $(printf '%s\n' 11.15.0 "$npm_version" | sort -V | head -1) == 11.15.0 ]] || diff --git a/npm/tsc-rs-readme.md b/npm/tsc-rs-readme.md index bfdd08c80..05a6edc43 100644 --- a/npm/tsc-rs-readme.md +++ b/npm/tsc-rs-readme.md @@ -42,14 +42,15 @@ allow it when VS Code asks: { "js/ts.tsdk.path": "node_modules/@tsc-rs/linux-x64/lib" } ``` -On macOS, use `@tsc-rs/darwin-arm64`. +On Linux arm64, use `@tsc-rs/linux-arm64`. On macOS, use `@tsc-rs/darwin-arm64`. ## Platforms - Linux x64 (static, any distribution) +- Linux arm64 (static, any distribution) - macOS arm64 -Windows and Linux arm64 are not available yet. +Windows is not available yet. ## Known problems