Skip to content

build(deps-dev): bump vitest from 4.1.11 to 5.0.0 in /packages/trilean-sql #107

build(deps-dev): bump vitest from 4.1.11 to 5.0.0 in /packages/trilean-sql

build(deps-dev): bump vitest from 4.1.11 to 5.0.0 in /packages/trilean-sql #107

Workflow file for this run

name: CI
# One workflow for the whole workspace. Every check runs its task across the workspace through turbo from the repository root, rather than against a single package directory, so adding a package needs no edit here.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
# A new push to a pull request supersedes that PR's in-flight run. A push to main never cancels: the release job publishes to npm and pushes tags mid-run, and cancelling it partway leaves real work half-done.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
env:
# On a pull request, restrict every turbo task to the packages the branch actually changed (and their dependents); on main, run the whole workspace so the caches the next run restores from are complete and the release gate covers everything. --affected compares against the base branch, so the checkouts below use fetch-depth: 0.
TURBO_FLAGS: ${{ github.event_name == 'pull_request' && '--affected' || '' }}
TURBO_SCM_BASE: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || '' }}
jobs:
commitlint:
name: Commitlint
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version-file: .tool-versions
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Validate the last commit with commitlint
if: github.event_name == 'push'
run: pnpm exec commitlint --last --verbose
- name: Validate every PR commit with commitlint
if: github.event_name == 'pull_request'
run: pnpm exec commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: ./.github/actions/setup-workspace
with:
task: lint
command: pnpm lint $TURBO_FLAGS
typecheck:
name: Typecheck
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: ./.github/actions/setup-workspace
with:
task: typecheck
# _typecheck:attw (attw --pack per published package, checking each package's declared types resolve under every module resolution mode) is one of the tasks `pnpm typecheck` runs through turbo, depending on that package's own _build -- no separate build step or bare `pnpm exec attw --pack` needed to give it a dist/ to inspect.
command: pnpm typecheck $TURBO_FLAGS
test:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
code-quality: write # to upload the cobertura coverage report below
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: ./.github/actions/setup-workspace
with:
task: test
command: pnpm test:coverage $TURBO_FLAGS
# One upload per package, each labelled by the package whose coverage it is, since the action takes a single report file rather than a set.
#
# Both are conditional on their own report existing, which is not defensive: `pnpm test:coverage` runs through turbo with $TURBO_FLAGS, which carries --affected on a pull request, so a package no commit in the PR touched legitimately does not run and legitimately writes no report. `fail-on-error` does not cover that case -- it governs the upload call, while a missing input file fails the action before it gets that far -- so without the hashFiles guard a PR confined to one package fails this job on the other package's absent report.
#
# Code Quality requires the org on GitHub Team/Enterprise Cloud, which ExaDev is not yet on, so the upload call itself will fail until that changes -- fail-on-error: false keeps that failure a log annotation instead of gating the release job below on a feature we can't turn on yet. Also guarded against fork PRs, which never hold the code-quality: write permission to upload.
- name: Upload coverage report (trilean)
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && hashFiles('packages/trilean/coverage/cobertura-coverage.xml') != ''
uses: actions/upload-code-coverage@v1
with:
file: packages/trilean/coverage/cobertura-coverage.xml
language: typescript
label: unit-trilean
fail-on-error: false
- name: Upload coverage report (trilean-sql)
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && hashFiles('packages/trilean-sql/coverage/cobertura-coverage.xml') != ''
uses: actions/upload-code-coverage@v1
with:
file: packages/trilean-sql/coverage/cobertura-coverage.xml
language: typescript
label: unit-trilean-sql
fail-on-error: false
test-integration:
name: Integration test
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: ./.github/actions/setup-workspace
with:
task: test-integration
command: pnpm test:integration $TURBO_FLAGS
test-smoke:
name: Smoke test
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: ./.github/actions/setup-workspace
with:
task: test-smoke
command: pnpm test:smoke $TURBO_FLAGS
test-workers:
name: Workers runtime test
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
# The install inside the composite action builds the workerd binary (allowBuilds in pnpm-workspace.yaml), so this runs the evaluator inside a real Cloudflare Workers isolate -- enforcing zero Node-only API usage at runtime rather than by assertion.
- uses: ./.github/actions/setup-workspace
with:
task: test-workers
command: pnpm test:workers $TURBO_FLAGS
# Single stable branch-protection target aggregating every job that runs on every PR, so the required status check doesn't need updating whenever a job is added, renamed, or split.
required-checks:
name: Required Checks
if: always()
needs:
[
commitlint,
lint,
typecheck,
test,
test-integration,
test-smoke,
test-workers,
]
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: All required jobs passed
run: |
results="${{ join(needs.*.result, ' ') }}"
for result in $results; do
if [[ "$result" == "failure" || "$result" == "cancelled" ]]; then
exit 1
fi
done
release:
name: Release
needs: [required-checks]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
# Two pushes to main close together must never run this job at the same time: the orchestrator pushes release commits, tags and dependency bumps to main mid-run, and semantic-release's own stale-checkout guard refuses to publish the moment it sees a commit on main this checkout does not have. Queue the second run behind the first rather than cancelling either -- cancelling mid-release would leave a published package with no committed version bump.
concurrency:
group: release-workspace
cancel-in-progress: false
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write # to push the release commit and tags, and create GitHub Releases
issues: write # to comment on released issues
pull-requests: write # to comment on released pull requests
id-token: write # OIDC identity for npm trusted publishing (no NPM_TOKEN)
outputs:
# A JSON array of every package this run released -- [{name, version, tag}, ...], [] when nothing released. An empty include list makes a consuming matrix job's own matrix invalid rather than skipping it, so every downstream job's `if:` checks this against '[]' explicitly rather than relying on the matrix alone.
matrix: ${{ steps.released.outputs.matrix }}
steps:
# main's ruleset requires every change to land via a pull request, and the default GITHUB_TOKEN has no bypass for that -- the orchestrator's release commit is a direct push to main, so it needs a token from an actor the ruleset explicitly allows through instead. The org-wide "exadev" GitHub App is that actor, added as an Integration bypass_actor on this repo's ruleset.
- name: Generate a token for the release push
id: app-token
uses: actions/create-github-app-token@v3
with:
app-id: ${{ secrets.GH_APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/checkout@v7
with:
# ref: main, not the bare event SHA. The orchestrator pushes to the current branch by name, so a detached HEAD stops the run with a WorkspaceStateError rather than pushing HEAD:HEAD; naming the branch is what makes checkout attach HEAD to it. fetch-depth: 0 because each package's release range is analysed from its own last matching tag.
ref: main
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version-file: .tool-versions
cache: pnpm
# registry-url is deliberately absent. Setting it makes setup-node write an .npmrc containing an _authToken line, and that line wins over the OIDC token exchange -- so the setting that looks like it configures the registry is exactly the one that would stop trusted publishing working.
- run: pnpm install --frozen-lockfile
- name: Upgrade npm for OIDC trusted publishing (needs npm CLI >=11.5.1)
run: npm install -g npm@latest
- name: Record the tags present before the orchestrator runs
# The orchestrator creates each package's `name@version` tag in this same checkout as it releases, so diffing the remote's tags across the release step is the exact record of what this run released. Written to RUNNER_TEMP, not the working tree: the single-commit strategy discovers what it touched via `git status` and requires a clean tree to start from, so a scratch file inside the checkout would fail that check every run.
run: git ls-remote --tags origin | sed 's|.*refs/tags/||' | grep -v '\^{}' | sort > "$RUNNER_TEMP/release-tags-before.txt"
- name: Release every package that changed, in dependency order
# The orchestrator discovers the workspace from pnpm-workspace.yaml, orders packages topologically, and runs semantic-release per package with commits path-filtered to that package's own directory and tags in `name@version` form. HUSKY=0 so local git hooks never fire against the automated commit.
run: HUSKY=0 pnpm release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# npm publish provenance for every package the run publishes, matching each package.json's own publishConfig.provenance.
NPM_CONFIG_PROVENANCE: "true"
# Blanked, not omitted -- an inherited NPM_TOKEN/NODE_AUTH_TOKEN from a workflow-level env block, reusable workflow, or composite action would otherwise be used in preference to the OIDC exchange.
NPM_TOKEN: ""
NODE_AUTH_TOKEN: ""
- name: Collect what this run released
id: released
# Builds the one matrix every post-release job below fans out over: one leg per package this run actually released. A new `name@version` tag is the record of a real release -- diffing tags across the release step, not grepping for one hardcoded package name, is what makes this generic across however many packages the workspace holds. `name` is everything before the tag's last `@`, matching how the orchestrator names its tags; the package.json at packages/$name is where the version comes from, since the orchestrator has already committed the released version by the time this step runs.
run: |
git ls-remote --tags origin | sed 's|.*refs/tags/||' | grep -v '\^{}' | sort > "$RUNNER_TEMP/release-tags-after.txt"
comm -13 "$RUNNER_TEMP/release-tags-before.txt" "$RUNNER_TEMP/release-tags-after.txt" > "$RUNNER_TEMP/release-tags-new.txt"
MATRIX='[]'
while read -r TAG; do
[ -n "$TAG" ] || continue
NAME=${TAG%@*}
PKG="packages/$NAME"
if [ ! -f "$PKG/package.json" ]; then
echo "::warning::tag $TAG has no package at $PKG; skipping every post-release step for it"
continue
fi
VERSION=$(jq -r '.version' "$PKG/package.json")
MATRIX=$(echo "$MATRIX" | jq -c --arg name "$NAME" --arg version "$VERSION" --arg tag "$TAG" \
'. + [{name: $name, version: $version, tag: $tag}]')
done < "$RUNNER_TEMP/release-tags-new.txt"
if [ "$MATRIX" = "[]" ]; then
echo "::notice::No new package tag; nothing was released by this run."
fi
echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"
notify-hive:
name: Notify novus-power/hive
needs: release
if: needs.release.outputs.matrix != '[]'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
steps:
# ExaSpot, unlike the org-wide 'exadev' App used for the release push above, is installed specifically on novus-power/hive (a different org from ExaDev) rather than org-wide -- owner has to be given explicitly since the App's own installation context otherwise defaults to this workflow's own repository owner (ExaDev), not novus-power.
- name: Generate a token for cross-org dispatch to novus-power/hive
id: app-token
uses: actions/create-github-app-token@v3
with:
app-id: "4816274"
private-key: ${{ secrets.HIVE_DISPATCH_APP_PRIVATE_KEY }}
owner: novus-power
repositories: |
hive
- name: Dispatch a sibling-released event per released package
# A single job looping the matrix, not `strategy: matrix` -- this is one lightweight API call per package, not a job worth a whole runner each. hive's own instant-update receiver filters this down to whichever package(s) it actually depends on, so every released package is dispatched unfiltered rather than hardcoding which one hive currently uses -- hive picking up a dependency on another package in this workspace later needs no change here.
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
RELEASE_MATRIX: ${{ needs.release.outputs.matrix }}
run: |
echo "$RELEASE_MATRIX" | jq -c '.[]' | while read -r pkg; do
name=$(echo "$pkg" | jq -r .name)
version=$(echo "$pkg" | jq -r .version)
gh api "repos/novus-power/hive/dispatches" \
-f event_type=sibling-released \
-F "client_payload[package]=$name" \
-F "client_payload[version]=$version"
done
publish-github-packages:
name: Publish mirror to GitHub Packages (${{ matrix.name }})
needs: release
if: needs.release.outputs.matrix != '[]'
strategy:
# An empty include list is an invalid matrix, not a job skip -- the `if:` above is what actually prevents this job running when nothing released; by the time the matrix itself is evaluated, needs.release.outputs.matrix is guaranteed non-empty.
fail-fast: false
matrix:
include: ${{ fromJSON(needs.release.outputs.matrix) }}
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
packages: write
# Deliberately no id-token: write here -- GitHub Packages exposes no OIDC trusted-publishing exchange to present an identity to, so this job authenticates with the GITHUB_TOKEN written to ~/.npmrc below instead. The provenance step further down removes the only other thing that would demand an OIDC token.
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v7
with:
# This leg's own tag, not main: a queued release run could have pushed further commits and tags to main between this run's release job finishing and this job starting, and the tag points at the exact release commit whose packages/<name> is the released state.
ref: ${{ matrix.tag }}
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version-file: .tool-versions
cache: pnpm
# Deliberately NOT setup-node's own registry-url/scope inputs: those write an @exadev:registry=https://npm.pkg.github.com/ *install-time* scope-to-registry mapping into .npmrc, which would redirect this workspace's own @exadev-scoped devDependency installs (@exadev/eslint-config, @exadev/semantic-release-workspace, published only to the default registry) through GitHub Packages too, breaking `pnpm install` below. publishConfig.registry (set explicitly below) already fully determines pnpm publish's *target* registry on its own; only the host-scoped auth token below is genuinely needed, added after install so install never sees any @exadev scope mapping at all.
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: Rewrite the package name for the GitHub Packages mirror
# Every package in this workspace stays unscoped on npmjs.org (see each package's own "name"), but GitHub Packages' npm registry structurally requires every package it hosts to be scoped to the owning org -- an unscoped publish there is rejected outright. This job's own package.json rewrite (never committed -- it runs against the checkout's working tree only) is what lets the same build ship under both names without the primary npm publish ever carrying the @exadev scope.
working-directory: packages/${{ matrix.name }}
run: npm pkg set name="@exadev/${{ matrix.name }}"
- name: Rewrite the registry for the GitHub Packages mirror
# publishConfig.registry has to be overridden explicitly: without it, pnpm publish would target registry.npmjs.org -- the registry the primary, unscoped npm publish already used in the release job above -- instead of GitHub Packages.
working-directory: packages/${{ matrix.name }}
run: npm pkg set publishConfig.registry="https://npm.pkg.github.com"
- name: Drop provenance for the GitHub Packages mirror
# npm honours publishConfig.provenance against whatever registry it is publishing to, and signing a provenance statement needs an OIDC token this job deliberately holds no permission to mint -- so leaving the field set fails the publish outright with 'Provenance generation in GitHub Actions requires "write" access to the "id-token" permission', before a single byte is uploaded. Granting that permission is not the fix: sigstore provenance is an npmjs.org feature GitHub Packages does not host, so the mirror would be signing an attestation none of its consumers could ever resolve. The primary npm publish in the release job above keeps provenance, which is where it means something.
working-directory: packages/${{ matrix.name }}
run: npm pkg delete publishConfig.provenance
- name: Configure the GitHub Packages auth token for publish only
run: echo "//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc
- working-directory: packages/${{ matrix.name }}
run: pnpm publish --access public --no-git-checks
attest-npm:
name: Attest SBOM and build provenance (npm, ${{ matrix.name }})
needs: release
if: needs.release.outputs.matrix != '[]'
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.release.outputs.matrix) }}
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write
attestations: write
steps:
- uses: actions/checkout@v7
with:
ref: ${{ matrix.tag }} # this leg's own release commit the orchestrator tagged, not whatever main has moved on to by now
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version-file: .tool-versions
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
# Pack into a directory of its own, separate from dist/ (tsdown's raw build output). The attestation subject has to be the artefact that actually ships -- attesting dist/ itself would mix in files that never leave the repo, producing digests that match nothing a consumer can download.
- working-directory: packages/${{ matrix.name }}
run: pnpm pack --pack-destination release-artifact
- working-directory: packages/${{ matrix.name }}
run: pnpm sbom --sbom-format spdx --prod > release-artifact/sbom.spdx.json
- name: Attest SBOM
uses: actions/attest@v4
with:
subject-path: packages/${{ matrix.name }}/release-artifact/*.tgz
sbom-path: packages/${{ matrix.name }}/release-artifact/sbom.spdx.json
- name: Attest build provenance
uses: actions/attest@v4
with:
subject-path: packages/${{ matrix.name }}/release-artifact/*.tgz
attest-github-packages:
name: Attest SBOM and build provenance (GitHub Packages, ${{ matrix.name }})
needs: release
if: needs.release.outputs.matrix != '[]'
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.release.outputs.matrix) }}
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write
attestations: write
steps:
- uses: actions/checkout@v7
with:
ref: ${{ matrix.tag }} # this leg's own release commit the orchestrator tagged, not whatever main has moved on to by now
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version-file: .tool-versions
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
# The GitHub Packages mirror ships under a different name (see publish-github-packages) and is therefore a genuinely different artefact from the one attest-npm covers above -- attesting only the unscoped tarball would leave the scoped mirror with no provenance a consumer could verify. This job's own attest@v4 attestations land in this repository's own attestation store regardless of which registry the tarball is later published to, so no id-token/OIDC conflict with publish-github-packages' own npm-registry auth exists here.
- name: Rewrite the package name to match the GitHub Packages mirror
working-directory: packages/${{ matrix.name }}
run: npm pkg set name="@exadev/${{ matrix.name }}"
- working-directory: packages/${{ matrix.name }}
run: pnpm pack --pack-destination release-artifact
- working-directory: packages/${{ matrix.name }}
run: pnpm sbom --sbom-format spdx --prod > release-artifact/sbom.spdx.json
- name: Attest SBOM
uses: actions/attest@v4
with:
subject-path: packages/${{ matrix.name }}/release-artifact/*.tgz
sbom-path: packages/${{ matrix.name }}/release-artifact/sbom.spdx.json
- name: Attest build provenance
uses: actions/attest@v4
with:
subject-path: packages/${{ matrix.name }}/release-artifact/*.tgz