Files
pnpm/.github/workflows/ci.yml
T
Zoltan Kochan 1860c1c75e chore: move get-pnpm into the workspace and pin npm's signing keys once (#17341)
get-pnpm moves from pnpm/get.pnpm.io into pnpm/npm/get-pnpm, at 0.0.9
(get.pnpm.io be4e983, where its history stays).

- The pnpm v12 npm package depends on it with `workspace:*`, so a pnpm
  release bundles get-pnpm's source as of that release. A wrapper fix no
  longer needs a get-pnpm release, a catalog bump, and a
  minimumReleaseAge exclusion first. bundle-node-gyp builds it before
  the deploy injects its build output.
- It is released on its own: a `get-pnpm` product in bump.ts and
  create-release-pr.yml (off by default), `get-pnpm@<version>` tags,
  and build-get-pnpm/publish-get-pnpm jobs in release.yml. It is private
  in the workspace so the TypeScript release's recursive publish skips
  it; the packed manifest drops the flag.
- Its runtime dependencies stay exact, through a `get-pnpm` named
  catalog. Its lint is the root ESLint config. Its TLS tests use the
  fixture the Rust network tests use.
- The npm-wrapper CI job runs its tests on Node.js 24 and 22, beside
  the wrapper's. The forwarding proxy in its download tests forwards
  only to the servers the tests start.

npm's registry signing keys were pinned in five places: v11, v12,
get-pnpm, and get.pnpm.io's install.sh and install.ps1. Only v11's copy
was refreshed, by the release PR, and the two repositories handled a
withdrawn key in opposite ways. They now come from
.config/npm-signing-keys.json:

- .github/scripts/update-npm-signing-keys.mjs refreshes it from npm on
  every release PR, not only v11 ones. A key npm stops listing stays,
  marked withdrawn.
- The meta-updater renders it into the v11, v12, and get-pnpm copies,
  so `pnpm lint:meta` fails on a copy edited by hand. pnpm v11 and v12
  keep trusting withdrawn keys, so packages signed before a rotation
  verify. get-pnpm drops them, as get.pnpm.io's installers do.
- get.pnpm.io's update script is to read this file.
2026-10-25 17:38:04 +01:00

234 lines
9.5 KiB
YAML

name: TS CI
on:
# Run push CI only on the default branch. A branch that lives in this repo
# fires both a `push` and a `pull_request` event for the same commit, and
# both runs report the `TS CI / Success` context to that commit. Branch
# protection then sees two results for the required check, so a cancellation
# or flake on the push side blocks the PR even when the pull_request run passed.
# Restricting push to `main` means PRs carry `TS CI / Success` only via
# their `pull_request` run (and the merge queue via `merge_group`); `main`
# is validated pre-merge by `merge_group` and re-checked post-merge here.
push:
branches:
- main
pull_request:
merge_group:
permissions:
contents: read # to fetch code (actions/checkout)
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
changes:
name: TS CI / Detect Changes
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
pull-requests: read
outputs:
ts: ${{ steps.force.outputs.ts || steps.filter.outputs.ts }}
changesets: ${{ steps.filter.outputs.changesets }}
steps:
# In the merge queue the full suite must run: `TS CI / Compile & Lint`
# is itself a required check, and a skipped job never reports its
# context, which would leave the queue waiting forever. Forcing
# detection true also tests the merged result, which is the point of
# the queue.
- name: Force TS CI for merge queue
if: github.event_name == 'merge_group'
id: force
run: echo "ts=true" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
if: github.event_name != 'merge_group' && github.event_name != 'pull_request'
with:
persist-credentials: false
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
if: github.event_name != 'merge_group'
id: filter
with:
# The TypeScript pnpm stack lives under pnpm11/, plus the
# workspace-root tooling that drives its build/lint/test.
filters: |
ts:
- 'pnpm11/**'
- '.meta-updater/**'
- '.config/pnpm/**'
- 'package.json'
- 'pnpm-workspace.yaml'
- 'pnpm-lock.yaml'
- '.pnpmfile.cjs'
- 'eslint.config.mjs'
- 'tsconfig.lint.json'
- 'cspell.json'
- '.github/workflows/ci.yml'
- '.github/workflows/test.yml'
- '.github/workflows/build-pnpr.yml'
- '.github/scripts/**'
# Skill scripts under .agents/ ship their own *.test.sh, run by the
# Compile & Lint job next to the publication scripts above.
- '.agents/skills/**/scripts/**'
# A pending change intent is validated by `pn lint`, which runs in
# Compile & Lint. Nothing under .changeset/ can affect a test, so
# it gates that job alone.
changesets:
- '.changeset/**'
compile-and-lint:
needs: changes
# Run when TypeScript-relevant files changed, or when the PR only touches
# .changeset/ — `pn lint` validates the pending change intents and their
# spelling, and a changeset-only PR skips every other job below. Every PR
# (same-repo or fork) is covered by its pull_request run, and push only
# fires on main, so there are no duplicate build jobs to guard against here.
if: ${{ !cancelled() && (needs.changes.outputs.ts == 'true' || needs.changes.outputs.changesets == 'true') }}
name: TS CI / Compile & Lint
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout Commit
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Initialize Rust before dependency installation
uses: $/.github/actions/rustup
with:
restore-cache: false
- name: Install pnpm
uses: pnpm/setup@fa36a5ef7dcdefc3a7cb25655c122e00cf6e636e # v3.1.2
with:
install: false
- name: Test CI and release publication scripts
run: |
.github/scripts/npm-package-publication-state.test.sh
.github/scripts/npm-staged-publication.test.sh
.github/scripts/issue-fix-intent.test.sh
.github/scripts/docker-image-tags.test.sh
.agents/skills/pull-requests/scripts/resolve-pr-conflicts.test.sh
node --test .github/scripts/ts-test-scope.test.mjs .github/scripts/ts-test-chunks.test.mjs .github/scripts/assert-windows-crt-static.test.mjs .github/scripts/wait-for-pnpr.test.mjs .github/scripts/update-npm-signing-keys.test.mjs
- name: Compile and lint TypeScript
run: pn pipeline ts-check --include-workspace-root --full --no-cache
- name: Package compiled artifacts
shell: bash
run: |
mapfile -d '' -t lib_dirs < <(find . -type d -name lib -not -path '*/node_modules/*' -print0)
mapfile -d '' -t tsbuildinfo_files < <(find . -name 'tsconfig.tsbuildinfo' -not -path '*/node_modules/*' -print0)
tar -czf compiled.tar.gz --exclude='node_modules' "${lib_dirs[@]}" "${tsbuildinfo_files[@]}" pnpm11/pnpm/dist
- name: Upload compiled artifacts
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: compiled-packages
path: compiled.tar.gz
retention-days: 1
# Build the pnpr binaries once per OS via a per-OS reusable workflow (not a
# single matrix job) so each platform's test jobs gate only on their own
# build. `needs` can only target a whole job, never an individual matrix
# leg, so a matrix build would make the ubuntu tests wait for the slower
# windows build and vice versa.
# Every push to main builds them, including Rust-only pushes that skip the
# tests, so pull requests merged with the latest main find them cached.
build-pnpr-linux:
needs: changes
if: ${{ !cancelled() && (needs.changes.outputs.ts == 'true' || github.event_name == 'push') }}
name: TS CI / Build pnpr
uses: $/.github/workflows/build-pnpr.yml
with:
os: blacksmith-16vcpu-ubuntu-2404
build-pnpr-windows:
needs: changes
if: ${{ !cancelled() && (needs.changes.outputs.ts == 'true' || github.event_name == 'push') }}
name: TS CI / Build pnpr
uses: $/.github/workflows/build-pnpr.yml
with:
os: blacksmith-16vcpu-windows-2025
# Like the Windows chunks, the Linux chunks start while the Linux pnpr
# build runs and wait for it inside the job (wait_for_pnpr).
test:
name: TS CI / Test / ubuntu
needs: [changes, compile-and-lint]
if: ${{ !cancelled() && needs.changes.outputs.ts == 'true' && needs.compile-and-lint.result == 'success' }}
permissions:
actions: read
contents: read
strategy:
fail-fast: false
matrix:
node: ['22.13.0', '24.0.0', '26.12.1']
test_chunk: ['1', '2', '3']
include:
- node: '22.13.0'
node_major: '22'
- node: '24.0.0'
node_major: '24'
# Garnet's post step costs about 100 s, so it runs on one chunk:
# the one whose tests finish first.
- node: '24.0.0'
test_chunk: '2'
garnet: true
- node: '26.12.1'
node_major: '26'
uses: $/.github/workflows/test.yml
with:
node: ${{ matrix.node }}
node_major: ${{ matrix.node_major }}
platform: blacksmith-8vcpu-ubuntu-2404
test_chunk: ${{ matrix.test_chunk }}
test_chunk_total: '3'
garnet: ${{ matrix.garnet == true }}
wait_for_pnpr: true
secrets:
GARNET_API_TOKEN: ${{ secrets.GARNET_API_TOKEN }}
# Windows chunks start while the Windows pnpr build runs and wait for it
# inside the job (wait_for_pnpr), so runner startup and installs overlap it.
test-windows:
name: TS CI / Test / windows ${{ matrix.test_chunk }}/5
needs: [changes, compile-and-lint]
if: ${{ !cancelled() && needs.changes.outputs.ts == 'true' && needs.compile-and-lint.result == 'success' }}
permissions:
actions: read
contents: read
strategy:
fail-fast: false
matrix:
test_chunk: ['1', '2', '3', '4', '5']
uses: $/.github/workflows/test.yml
with:
node: '22.13.0'
node_major: '22'
platform: blacksmith-8vcpu-windows-2025
test_chunk: ${{ matrix.test_chunk }}
test_chunk_total: '5'
wait_for_pnpr: true
# Single aggregate gate — the only TS CI context branch protection needs
# to require. Listing the individual jobs as required checks does not
# work: they skip on non-TypeScript PRs, and a matrix job skipped at the
# job level never expands its `${{ matrix.* }}` name, so the per-platform
# contexts it would report never appear and the PR blocks forever waiting
# for them. This job always runs (it reports under a static name in every
# state) and fails only if a dependency actually failed or was cancelled —
# skipped dependencies count as a pass.
success:
name: TS CI / Success
if: ${{ always() }}
needs:
- changes
- compile-and-lint
- build-pnpr-linux
- build-pnpr-windows
- test
- test-windows
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Fail if any dependency failed or was cancelled
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
run: exit 1