Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
01d2d63
chore(ci): bump jactionlint to 2.3.0
kanadgupta Oct 23, 2026
eb74f6c
chore(ci): migrate the jactionlint config to the 2.x rules key
kanadgupta Oct 23, 2026
b7eff25
chore(ci): keep jactionlint out of the vendored submodules
kanadgupta Oct 23, 2026
84f7717
chore(ci): turn off the jactionlint missing-timeout rule
kanadgupta Oct 23, 2026
e8cb0c6
chore(ci): turn off the jactionlint concurrency-limits rule
kanadgupta Oct 23, 2026
958d297
ci: grant write scopes per job instead of per workflow
kanadgupta Oct 23, 2026
c408fbd
ci: request only the scopes each release-notes App token uses
kanadgupta Oct 23, 2026
c992304
ci: create GitHub releases with gh instead of action-gh-release
kanadgupta Oct 23, 2026
8f19bf3
ci: stop contribution-gate checkouts persisting the token
kanadgupta Oct 23, 2026
5eb9c91
ci: drop job outputs no other job reads
kanadgupta Oct 23, 2026
3e249aa
ci: fail pipelines on the first broken command
kanadgupta Oct 23, 2026
7e9a683
ci: pass the Claude model through a step output
kanadgupta Oct 23, 2026
1e8378e
ci: accept the sender check in bot-pr-auto-merge
kanadgupta Oct 23, 2026
a81eba4
ci: accept the pinned Claude Code CLI installs
kanadgupta Oct 23, 2026
136ab16
ci: accept the privileged triggers the automation workflows need
kanadgupta Oct 23, 2026
3ece4e1
ci: accept running the published installer in its own test
kanadgupta Oct 23, 2026
5178f1c
ci: accept the run-scoped build cache in the publish job
kanadgupta Oct 23, 2026
66c23aa
ci: accept the tag-pinned smoke-test container image
kanadgupta Oct 23, 2026
78ad0f0
ci: say plainly why each jactionlint finding is accepted
kanadgupta Oct 23, 2026
b8087a5
ci: read issues with the release-notes proposal token
kanadgupta Oct 23, 2026
37bca2c
ci: drop the resolver's head_ref output with the job output
kanadgupta Oct 23, 2026
f4d989f
ci: keep the workflows scope off the release-notes apply token
kanadgupta Oct 23, 2026
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
10 changes: 9 additions & 1 deletion .github/jactionlint.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
require-commit-hash: true # actions, reusable workflows and docker:// must be pinned to a full SHA / @sha256 digest
rules:
missing-timeout: off
concurrency-limits: off
unpinned-uses: error # actions, reusable workflows and docker:// must be pinned to a full SHA / @sha256 digest
self-hosted-runner:
labels:
- arm-runner
Expand All @@ -8,3 +11,8 @@ self-hosted-runner:
- blacksmith-8vcpu-ubuntu-2404
- blacksmith-8vcpu-windows-2025
- blacksmith-32vcpu-ubuntu-2404
paths:
# Other projects' repositories, pulled in as git submodules. CI never checks them out.
".repos/**":
ignore:
- ".*"
3 changes: 1 addition & 2 deletions .github/scripts/ai-review/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
* `resolveDecision` is the pure orchestration function (I/O injected) a test
* can drive without the network; `main()` wires up the real GitHub I/O,
* writes `should_run`/`pr_number`/`head_ref`/`trigger` to `$GITHUB_OUTPUT`,
* writes `should_run`/`pr_number`/`head_sha`/`trigger` to `$GITHUB_OUTPUT`,
* and surfaces the skip reason (if any) in `$GITHUB_STEP_SUMMARY`.
*
* Run in CI as: `bun .github/scripts/ai-review/resolve.ts`.
Expand Down Expand Up @@ -374,7 +374,6 @@ function writeOutputs(result: ResolveResult, prNumber: number): void {
const entries: Record<string, string> = {
should_run: String(result.shouldRun),
pr_number: String(prNumber),
head_ref: `refs/pull/${prNumber}/head`,
head_sha: result.headSha ?? "",
trigger: result.trigger,
};
Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/ai-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ on:
description: "PR number to review"
required: true
type: string
# A `/ai-review` comment on a pull request starts a review. The resolve job checks who
# commented, and the one job that can write to the pull request runs code from the default
# branch only, never from the pull request.
# jactionlint ignore=dangerous-triggers
issue_comment:
types:
- created
Expand Down Expand Up @@ -65,7 +69,6 @@ jobs:
outputs:
should_run: ${{ steps.resolve.outputs.should_run }}
pr_number: ${{ steps.resolve.outputs.pr_number }}
head_ref: ${{ steps.resolve.outputs.head_ref }}
# Every job reviews this commit, so a push during the run is never reported as reviewed.
head_sha: ${{ steps.resolve.outputs.head_sha }}
trigger: ${{ steps.resolve.outputs.trigger }}
Expand Down Expand Up @@ -161,7 +164,10 @@ jobs:
# behavior mid-rollout. Installed from the trusted checkout with npm config isolation so a
# PR-supplied `.npmrc` in the untrusted `pr` checkout can never redirect this install to a
# hostile registry.
# 2.1.280 is the first release that accepts claude-opus-5-5.
# 2.1.280 is the first release that accepts claude-opus-5-5. The CLI is installed on its
# own, not through the repository's lockfile, so nothing in the pull request's checkout
# can change what gets installed.
# jactionlint ignore=adhoc-packages
- name: Install Claude Code CLI
working-directory: trusted
run: |
Expand Down Expand Up @@ -544,7 +550,7 @@ jobs:
steps:
# Security-critical: this is the only job with write permission, so it must only ever
# execute trusted base-branch code — never the PR head. Checking out `develop` explicitly
# (never `needs.resolve.outputs.head_ref`) keeps a malicious PR from smuggling a script
# (never the PR's own ref) keeps a malicious PR from smuggling a script
# change into the one job that can write back to the PR. (For `pull_request` events GitHub
# runs the workflow file from the PR's own ref; acceptable since the auto path only admits
# same-repo PRs, whose authors hold write access anyway, and fork PRs run with a read-only
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/apply-release-notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ jobs:
outputs:
authorized: ${{ steps.check.outputs.authorized }}
steps:
# App token needs org membership read, repo write to edit the release, and PR write to
# comment and close.
# The token reads the approver's team membership and comments on the pull request.
- id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.GH_APP_CLIENT_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
permission-members: read
permission-pull-requests: write

- name: Authorize approver against supabase/cli team
id: check
Expand Down Expand Up @@ -66,11 +67,16 @@ jobs:
contents: write
pull-requests: write
steps:
# The token edits the GitHub release, deletes the branch, and comments on and closes the
# pull request. The script below runs from the pull request's own checkout, so the token
# must not be able to change workflow files.
- id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.GH_APP_CLIENT_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write

# Checkout the PR head so reviewer edits made before approval are captured;
# apply-release-notes.ts reads from the working tree.
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/bot-pr-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: Bot PR auto-merge

on:
# Only this event lets the job use the auto-approve environment's secret. The job never
# checks out or runs code from the pull request.
# jactionlint ignore=dangerous-triggers
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review]
branches: [develop]
Expand All @@ -14,6 +17,9 @@ jobs:
# Other apps (coding agents, CI vendors) also open PRs here and still need a maintainer.
# Release-notes PRs are published by a maintainer approval and must never merge. Branch sync
# PRs land by fast-forward on a maintainer approval, never by a squash.
# The first condition checks who opened the pull request. The second checks who caused this
# event, so a person pushing to a bot's pull request does not get it approved.
# jactionlint ignore=bot-conditions
if: >-
contains(fromJSON('["dependabot[bot]", "supabase-cli-releaser[bot]"]'), github.event.pull_request.user.login) &&
contains(fromJSON('["dependabot[bot]", "supabase-cli-releaser[bot]"]'), github.event.sender.login) &&
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/cli-go-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
# Required by: internal/utils/credentials/keyring_test.go
- uses: t1m0thyj/unlock-keyring@cbcf205c879ebd86add70bab3a6abfcce59a5cae # v1.2.0
- run: |
set -o pipefail
pkgs=$(go list ./pkg/... | grep -Ev 'pkg/api' | paste -sd ',' -)
go tool gotestsum -- -race -v -count=1 ./... \
-coverpkg="./cmd/...,./internal/...,${pkgs}" -coverprofile=coverage.out
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/cli-go-mirror-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ on:
required: true
type: string

permissions:
contents: read
packages: write
id-token: write
permissions: {}

jobs:
mirror:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- id: strip
env:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/cli-go-pg-prove.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ jobs:
arch: arm64
runs-on: ${{ matrix.runner }}
timeout-minutes: 180
outputs:
image_digest: ${{ steps.build.outputs.digest }}
steps:
- run: docker context create builders
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/cli-go-publish-migra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ jobs:
arch: arm64
runs-on: ${{ matrix.runner }}
timeout-minutes: 180
outputs:
image_digest: ${{ steps.build.outputs.digest }}
steps:
- run: docker context create builders
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/cli-go-tag-pkg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ on:
required: true
type: string

permissions:
contents: write
permissions: {}

jobs:
tag:
name: Create pkg tag
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/close-stale-issues-and-prs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ on:
type: string
default: "security,pinned,do-not-close,keep-open,do not merge"

permissions:
contents: read
issues: write
pull-requests: write
permissions: {}

concurrency:
group: close-stale-issues-and-prs
Expand All @@ -35,6 +32,10 @@ jobs:
close-stale:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Close stale issues and PRs
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
Expand Down
20 changes: 16 additions & 4 deletions .github/workflows/contribution-gate.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: Contribution Gate

on:
# The gate has to comment on pull requests from forks, which only this event's token can do.
# It checks out the base branch, never the pull request.
# jactionlint ignore=dangerous-triggers
pull_request_target:
types:
- opened
Expand All @@ -16,10 +19,7 @@ on:
type: boolean
default: false

permissions:
pull-requests: write
issues: read
contents: read
permissions: {}

concurrency:
# Per-PR for pull_request_target; unique per run for manual sweeps (which
Expand All @@ -31,6 +31,10 @@ jobs:
gate-single:
name: Contribution Gate
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: read
contents: read
# Exempt maintainers/collaborators and bots. External contributors are gated.
if: >
github.event_name == 'pull_request_target' &&
Expand All @@ -42,6 +46,8 @@ jobs:
# Checks out the base repo (default for pull_request_target), so the gate
# runs trusted code from the base branch, never the untrusted PR head.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: ".bun-version"
Expand All @@ -58,11 +64,17 @@ jobs:
gate-all:
name: Contribution Gate (all open PRs)
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: read
contents: read
if: github.event_name == 'workflow_dispatch'
steps:
# Base-branch checkout only — the sweep makes API calls and never runs
# any PR's code.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: ".bun-version"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/dispatch-cli-e2e-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
CLI_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -o pipefail
echo "Dispatching cli-e2e-ci live run for PR #${PR_NUMBER} @ ${CLI_SHA}"
# Build the nested client_payload with jq — `gh api -f` sends a flat
# body and would not nest `client_payload.*` correctly.
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/fill-sticky-disks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ jobs:

- name: Pull stack package images
run: |
set -o pipefail
pnpm --silent --filter @supabase/stack prepare:test-artifacts --images |
xargs --max-procs 4 --max-args 1 docker pull --quiet

# Also runs after a failed step so leftover stack containers and volumes never persist.
- name: Prune Docker before saving its image disk
if: always()
run: |
set -o pipefail
docker ps --all --quiet | xargs --no-run-if-empty docker rm --force
docker system prune --force
docker volume prune --all --force
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/live-e2e-notify.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: Live E2E Notifications

on:
# The job only reads whether the run passed. It runs nothing the run produced.
# jactionlint ignore=dangerous-triggers
workflow_run:
workflows: [Live E2E, Release]
types: [completed]
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/propose-release-notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ jobs:
with:
client-id: ${{ vars.GH_APP_CLIENT_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
permission-contents: write
permission-issues: read
permission-pull-requests: write

- uses: useblacksmith/checkout@6fd481652155169ed4d2f25ebaf97464f685175f # v1
with:
Expand Down
29 changes: 17 additions & 12 deletions .github/workflows/release-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -355,19 +355,24 @@ jobs:
fi
echo "dist-tag ${NPM_TAG} -> ${VERSION} confirmed."

# The CLI's install scripts and setup-cli resolve releases/latest/download/..., so a
# config release must never become the repo's "latest" release. Re-runs skip this step
# when the release already exists.
- name: Create GitHub Release
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
token: ${{ github.token }}
tag_name: config-v${{ needs.plan.outputs.version }}
name: "@supabase/config v${{ needs.plan.outputs.version }}"
body_path: ${{ runner.temp }}/config-release/config-release-notes.md
draft: false
prerelease: false
# The CLI's install scripts and setup-cli resolve
# releases/latest/download/..., so a config release must never
# become the repo's "latest" release.
make_latest: "false"
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag="config-v${VERSION}"
if gh release view "${tag}" >/dev/null 2>&1; then
echo "Release ${tag} already exists; skipping."
else
gh release create "${tag}" \
--verify-tag \
--title "@supabase/config v${VERSION}" \
--notes-file "${RUNNER_TEMP}/config-release/config-release-notes.md" \
--latest=false
fi

# Pages Slack the moment a real run arms the config-release approval gate. Runs in parallel
# with the publish job's `waiting` state (needs only `plan`), so the ping and the pending
Expand Down
24 changes: 18 additions & 6 deletions .github/workflows/release-shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ jobs:
with:
dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }}

# The cache key includes this run's id, so only this run's build job can have written it.
# jactionlint ignore=cache-poisoning
- name: Restore build artifacts cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
Expand Down Expand Up @@ -377,13 +379,23 @@ jobs:
done

# Created empty; the next step uploads the assets so each one can be retried on its own.
# Re-runs skip this step when the release already exists.
- name: Create draft GitHub Release
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
tag_name: v${{ inputs.version }}
name: v${{ inputs.version }}
draft: true
prerelease: ${{ inputs.prerelease }}
env:
GH_TOKEN: ${{ github.token }}
PRERELEASE: ${{ inputs.prerelease }}
run: |
set -euo pipefail
tag="v${VERSION}"
if gh release view "${tag}" >/dev/null 2>&1; then
echo "Release ${tag} already exists; skipping."
else
args=(--draft --verify-tag --title "${tag}")
if [[ "$PRERELEASE" == true ]]; then
args+=(--prerelease)
fi
gh release create "${tag}" "${args[@]}"
fi

# One asset at a time with per-asset retries; see apps/cli/scripts/upload-release-assets.ts.
- name: Upload release assets
Expand Down
Loading
Loading