name: TS CI on: [push, pull_request] permissions: contents: read # to fetch code (actions/checkout) jobs: changes: name: TS CI / Detect Changes runs-on: ubuntu-latest permissions: contents: read pull-requests: read outputs: ts: ${{ steps.filter.outputs.ts }} steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 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/**' - '__patches__/**' - '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' compile-and-lint: needs: changes # Run only when TypeScript-relevant files changed. Also skip # pull_request events from PRs in the same repo to prevent duplicate # build jobs (the push event already covers them). # Exception: chore/update-lockfile PRs (created by automation with GITHUB_TOKEN, which doesn't trigger push events) if: ${{ !cancelled() && needs.changes.outputs.ts == 'true' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository || github.head_ref == 'chore/update-lockfile') }} name: TS CI / Compile & Lint runs-on: ubuntu-latest steps: - name: Checkout Commit uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Install pnpm uses: pnpm/setup@b1cac37306e39c21283b9dd6cb0ac288fb35ba6b - name: Compile TypeScript run: pn compile-only - name: Lint run: pn lint - 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: compiled-packages path: compiled.tar.gz retention-days: 1 test-smoke: name: TS CI / Test / ubuntu needs: compile-and-lint uses: ./.github/workflows/test.yml with: node: '24.0.0' node_major: '24' platform: ubuntu-latest garnet: true secrets: GARNET_API_TOKEN: ${{ secrets.GARNET_API_TOKEN }} test: name: TS CI / Test / ${{ matrix.platform_label }} needs: test-smoke strategy: fail-fast: false matrix: node: ['22.13.0', '24.0.0', '26.3.1'] platform: [ubuntu-latest, windows-latest] include: - node: '22.13.0' node_major: '22' - node: '24.0.0' node_major: '24' - node: '26.3.1' node_major: '26' - platform: ubuntu-latest platform_label: ubuntu - platform: windows-latest platform_label: windows exclude: - node: '24.0.0' platform: ubuntu-latest # On branches, only run Windows with the lowest supported Node.js. # Exception: main and release branches (release/x, release/x.x) run the full matrix. - node: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && 'none' || '24.0.0' }} platform: windows-latest - node: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && 'none' || '26.3.1' }} platform: windows-latest uses: ./.github/workflows/test.yml with: node: ${{ matrix.node }} node_major: ${{ matrix.node_major }} platform: ${{ matrix.platform }} # 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 - test-smoke - test runs-on: ubuntu-latest steps: - name: Fail if any dependency failed or was cancelled if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} run: exit 1