Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/fetch-test-artifacts/action.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Restores the two fetched, gitignored test artifacts (trimmed OpenAPI spec, GraphQL schema)
# from cache, fetching each on a miss: the ONE place an artifact is cached (the drift-tripwire
# nightlies fetch uncached by design). Needs setup-bun and `bun install` earlier in the job.
# nightlies fetch uncached by design). Needs ./.github/actions/setup (bun and the install) earlier in the job.
name: Fetch the test artifacts
description: Restore the trimmed OpenAPI spec and the GraphQL schema from cache, fetching each on a miss

Expand Down
32 changes: 32 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# The bun setup and the dependency install of every repo-owned workflow job; the caller keeps its own checkout and
# setup-node, whose options differ per job.
# install -> --ignore-scripts: scripts.prepare installs lefthook, a git hook no runner uses; "false" (any letter
# case, as GitHub compares) skips the install for a job that runs a bare script or resolves the lockfile itself
# yamllint -> "true" installs the pinned yamllint behind `bun run lint:yaml`, which fails a CI run where it is missing
name: Set up bun
description: The pinned bun, the locked dependencies unless told not to, and yamllint for the jobs that lint YAML

inputs:
install:
description: '"false" sets up bun alone; anything else runs bun install --frozen-lockfile --ignore-scripts'
default: "true"
yamllint:
description: '"true" installs the pinned yamllint for lint:yaml'
default: "false"

runs:
using: composite
steps:
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- name: Install dependencies
if: inputs.install != 'false'
shell: bash
run: bun install --frozen-lockfile --ignore-scripts
# pipx ships on the ubuntu runner image with its bin directory on PATH; a venv of its own leaves the runner's
# python untouched.
- name: Install yamllint
if: inputs.yamllint == 'true'
shell: bash
run: pipx install yamllint==1.38.0
6 changes: 1 addition & 5 deletions .github/workflows/auto-fix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,7 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.ref }}
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- name: Install dependencies
run: bun install --frozen-lockfile --ignore-scripts
- uses: ./.github/actions/setup
- name: Graduate upstream gaps octokit now ships
shell: bash
run: bun .github/scripts/graduate-upstream-gaps.ts
Expand Down
94 changes: 75 additions & 19 deletions .github/workflows/auto-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
# commit to the PR branch, then removes the label. Generated once by
# the fleet sync and never overwritten by a later
# sync: adjust the format commands to this repository's tooling.
#
# Formatting runs PR-controlled code (the locked biome, the lint:fix script), so it happens in a job without a write
# token and crosses to the push job as a git patch; applying a patch executes nothing, as in auto-fix.yml.

name: Auto Format

Expand All @@ -13,42 +16,95 @@ on:
permissions:
contents: read

# One run per PR at a time: the label removed and re-applied while a run is pushing would start a second run against
# the same branch, and two formatting pushes race. The later run queues and formats the pushed head instead.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: false

jobs:
format:
# Same-repo PRs only: GITHUB_TOKEN cannot push to a fork's branch, and
# PR-controlled tool configuration should not run next to write
# credentials (checkout below persists none; the push step scopes them).
# Same-repo PRs only: GITHUB_TOKEN cannot push to a fork's branch.
if: github.event.label.name == 'fix-lint' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 15
outputs:
changed: ${{ steps.format.outputs.changed }}
head: ${{ steps.format.outputs.head }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.ref }}
persist-credentials: false
# The install puts the locked biome in node_modules, so the format runs the gate's biome, not the newest one.
- uses: ./.github/actions/setup
- name: Format (biome) and stage the patch
id: format
run: |
# The patch belongs to this commit alone; the push job refuses any other head.
echo "head=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
bun run lint:fix
# Anything the formatter left staged is not this workflow's fix: start from an empty index, then stage
# modifications and deletions of tracked files alone (a formatter adds nothing).
git reset -q
git add -u
git diff --cached --binary > "$RUNNER_TEMP/format.patch"
Comment thread
Vivswan marked this conversation as resolved.
if [ -s "$RUNNER_TEMP/format.patch" ]; then
git diff --cached --stat
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "nothing to format"
echo "changed=false" >> "$GITHUB_OUTPUT"
fi
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: steps.format.outputs.changed == 'true'
with:
name: format-patch
path: ${{ runner.temp }}/format.patch
if-no-files-found: error

# Runs whenever the format job ran, so the label comes off even after a failed format: the label is a one-shot
# request, and a broken run must not loop.
push:
needs: format
if: always() && needs.format.result != 'skipped'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
if: needs.format.outputs.changed == 'true'
with:
ref: ${{ github.event.pull_request.head.ref }}
persist-credentials: false

- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
- name: Format (biome)
run: bun x @biomejs/biome check --write .

- name: Commit and push changes
# The patch lands OUTSIDE the checkout so nothing in the work tree can shadow or stage it.
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
if: needs.format.outputs.changed == 'true'
with:
name: format-patch
path: ${{ runner.temp }}/format
- name: Commit and push the formatting
if: needs.format.outputs.changed == 'true'
env:
GH_TOKEN: ${{ github.token }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
HEAD_SHA: ${{ needs.format.outputs.head }}
run: |
if git diff --quiet; then
echo "nothing to format"
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "style: apply automated formatting"
git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:"$HEAD_REF"
# The branch can move between the two jobs; the patch was cut on HEAD_SHA and formats that tree alone, so a
# newer head gets its own run (re-apply the label) instead of a commit formatting the old one.
if [ "$(git rev-parse HEAD)" != "$HEAD_SHA" ]; then
echo "::notice::head moved since the format; skipping the stale formatting push"
exit 0
fi
# The label is a one-shot request: remove it even when a step failed,
# so a broken run cannot loop.
git apply --index --binary "$RUNNER_TEMP/format/format.patch"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit --no-verify -m "style: apply automated formatting"
# The lease pins the remote to the commit the patch was cut on, so a push that lands in between rejects this one.
git push --force-with-lease="refs/heads/${HEAD_REF}:${HEAD_SHA}" \
"https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:"refs/heads/${HEAD_REF}"
- name: Remove the fix-lint label
if: always()
env:
Expand Down
52 changes: 19 additions & 33 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@ on:
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
- uses: ./.github/actions/setup
with:
bun-version-file: .bun-version
- name: Install dependencies
run: bun install --frozen-lockfile
yamllint: "true"
# bun test loads the fetched, gitignored OpenAPI spec and GraphQL
# schema; the composite restores each from cache or fetches on a miss.
- uses: ./.github/actions/fetch-test-artifacts
- name: Lint (biome)
run: bun run lint
- name: Lint (yaml)
run: bun run lint:yaml
- name: Lint (architecture)
run: bun run lint:arch
- name: Typecheck
Expand All @@ -50,9 +51,9 @@ jobs:
with:
# The newest release merge can be arbitrarily far behind HEAD.
fetch-depth: 0
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
- uses: ./.github/actions/setup
with:
bun-version-file: .bun-version
install: "false"
- name: Confirm last-release-sha matches the newest release merge
run: bun .github/scripts/release-pipeline.ts boundary-check

Expand All @@ -63,21 +64,18 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
- uses: ./.github/actions/setup
with:
bun-version-file: .bun-version
install: "false"
- name: Require the release PR to carry this cycle's anchor
run: bun .github/scripts/release-pipeline.ts anchor-check

schema-check:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- name: Install dependencies
run: bun install --frozen-lockfile
- uses: ./.github/actions/setup
- name: Regenerate the schema and compare
run: bun run build:check

Expand All @@ -87,15 +85,14 @@ jobs:
# than the floor, so the PATH node is 24 and the floor's binary is handed to the consumer alone.
package-smoke:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
node-version: ["22.14", "24"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- uses: ./.github/actions/setup
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ matrix.node-version }}
Expand All @@ -104,22 +101,17 @@ jobs:
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build, pack, install, import, and typecheck the library
run: bun .github/scripts/package-smoke.ts

# Drift and permission skips are fine here (the default token cannot read admin surfaces); a CRASH is not, and an
# exception never sets the result output.
self-check:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- name: Install dependencies
run: bun install --frozen-lockfile --ignore-scripts
- uses: ./.github/actions/setup
- name: Build the action bundle
run: bun run build:bundle
- name: Run settings-as-code (check mode)
Expand All @@ -142,18 +134,15 @@ jobs:
# to diff against, so it runs the full corpus.
e2e-smoke:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- uses: ./.github/actions/setup
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Select sections from the diff
id: select
env:
Expand Down Expand Up @@ -188,18 +177,15 @@ jobs:
# the selector finds nothing settings-related in the diff.
endpoint-coverage:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- uses: ./.github/actions/setup
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Install dependencies
run: bun install --frozen-lockfile
# Only the selector's "none" answer matters here. It runs BEFORE the spec cache/fetch so a PR that selects
# nothing skips the download too.
- name: Check whether the diff can affect route coverage
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ permissions:
jobs:
copilot-setup-steps:
runs-on: ubuntu-latest
# The most Copilot accepts for this job (its documented ceiling); the setup itself takes seconds.
timeout-minutes: 59
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
- name: Install dependencies (bun)
run: bun install --frozen-lockfile
- uses: ./.github/actions/setup
12 changes: 7 additions & 5 deletions .github/workflows/e2e-nightly.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# The nightly run of the whole curated e2e corpus (test/e2e/run.ts) plus the endpoint-coverage tripwire, against an
# OpenAPI spec fetched fresh: PR CI restores a cached spec for speed, so this fetch is where upstream drift surfaces.
# A red night files or updates one tracking issue through the fleet's fuzz-issue action; a green one closes it.
# Repo-owned, never overwritten by sync.

name: E2E Nightly

on:
Expand All @@ -18,16 +23,13 @@ concurrency:
jobs:
nightly:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- uses: ./.github/actions/setup
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Install dependencies
run: bun install --frozen-lockfile

# The trimmed OpenAPI spec is a fetched, gitignored artifact. The nightly
# ALWAYS fetches it (no cache) on purpose: the fetch is the drift tripwire.
Expand Down
6 changes: 1 addition & 5 deletions .github/workflows/nightly-fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,10 @@ jobs:
timeout-minutes: 60
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- uses: ./.github/actions/setup
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Install dependencies
run: bun install --frozen-lockfile

# The trimmed OpenAPI spec is a fetched, gitignored artifact the mock
# validates responses against. Always fetched fresh (no cache), same as
Expand Down
Loading