Skip to content

chore(stack): bump storage to v1.89.7-r0 (#7489) #12643

chore(stack): bump storage to v1.89.7-r0 (#7489)

chore(stack): bump storage to v1.89.7-r0 (#7489) #12643

Workflow file for this run

name: Release
on:
push:
branches:
- develop
- main
- next
# workflow_dispatch is the manual re-cut path. Use it when:
# - a previous release published stale bytes under a version that
# semantic-release keeps re-computing (cut a fresh version forward), or
# - a downstream step (GH release, brew, scoop) failed after npm published
# and you need to rerun the whole pipeline against an explicit version.
# An empty version lets semantic-release compute it from the dispatched ref. `maintenance`
# releases a v<N>.x line and must be dispatched on that ref.
workflow_dispatch:
inputs:
channel:
description: Release channel
required: true
type: choice
options:
- beta
- stable
- next
- maintenance
version:
description: npm package version to publish (must be unique on npm). Leave empty to let semantic-release compute it from the dispatched ref.
required: false
type: string
default: ""
# Defaults to `true` so a stray "Run workflow" click can't accidentally publish; operators
# must consciously untick this when dispatching.
dry_run:
description: Dry run (skip actual publishing)
required: false
type: boolean
default: true
# The release pipeline mutates external registries, git tags, GitHub Releases, Homebrew, and
# Scoop, so keep one release per ref active at a time to avoid duplicate push events racing.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
jobs:
# semantic-release reads channel notes per commit (semantic-release/semantic-release#4073), so
# releasing `next` on a commit develop already tagged misreads both tags.
next-ahead:
name: Check next has its own commits
if: github.ref_name == 'next'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
outputs:
ahead: ${{ steps.compare.outputs.ahead }}
steps:
- id: compare
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
run: |
set -euo pipefail
ahead_by="$(gh api "repos/${REPO}/compare/develop...${SHA}" --jq .ahead_by)"
if [[ "$ahead_by" -gt 0 ]]; then
echo "ahead=true" >> "$GITHUB_OUTPUT"
else
echo "ahead=false" >> "$GITHUB_OUTPUT"
fi
test-next:
name: Test next
needs: next-ahead
if: github.event_name == 'push' && github.ref_name == 'next' && needs.next-ahead.outputs.ahead == 'true'
permissions:
contents: read
actions: read
uses: ./.github/workflows/test.yml
with:
force: true
secrets:
DF_FIREWALL_TOKEN: ${{ secrets.DF_FIREWALL_TOKEN }}
plan:
name: Plan release
needs: [next-ahead, test-next]
# Skipped next-ahead and test-next jobs (every non-next trigger) make the implicit success()
# false, so their results are checked explicitly. A next push with no commits beyond develop
# plans nothing.
if: >-
${{ !cancelled() &&
contains(fromJSON('["success","skipped"]'), needs.next-ahead.result) &&
contains(fromJSON('["success","skipped"]'), needs.test-next.result) &&
!(github.event_name == 'push' && github.ref_name == 'next' && needs.next-ahead.outputs.ahead != 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 135
permissions:
contents: read
actions: read
outputs:
should_release: ${{ steps.compute.outputs.should_release }}
version: ${{ steps.compute.outputs.version }}
npm_tag: ${{ steps.compute.outputs.npm_tag }}
prerelease: ${{ steps.compute.outputs.prerelease }}
brew_name: ${{ steps.compute.outputs.brew_name }}
scoop_name: ${{ steps.compute.outputs.scoop_name }}
publish_brew_scoop: ${{ steps.compute.outputs.publish_brew_scoop }}
dry_run: ${{ steps.compute.outputs.dry_run }}
channel: ${{ steps.compute.outputs.channel }}
steps:
- name: Refuse a next dispatch with no commits beyond develop
if: ${{ github.event_name == 'workflow_dispatch' && github.ref_name == 'next' && needs.next-ahead.outputs.ahead != 'true' }}
run: |
echo "::error::next has no commits beyond develop, so a next release would tag a commit develop already released"
exit 1
# The stable tag is pushed by main's publish job, so a prerelease planned before that job
# finishes misses it. The rest of the run (brew, scoop, smoke tests) does not affect the plan.
- name: Wait for an in-flight stable release
if: ${{ (github.ref_name == 'develop' || github.ref_name == 'next') && (github.event_name == 'push' || inputs.version == '') }}
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
deadline=$((SECONDS + 7200))
while true; do
pending=0
if run_ids="$(gh run list --repo "$REPO" --workflow release.yml --branch main --json databaseId,status --jq '.[] | select(.status != "completed") | .databaseId')"; then
for run_id in $run_ids; do
publish_status="$(gh api "repos/${REPO}/actions/runs/${run_id}/jobs" --paginate --jq '.jobs[] | select(.name == "Release / publish") | .status')" || publish_status=unknown
if [[ "$publish_status" != completed ]]; then
pending=$((pending + 1))
fi
done
else
echo "::warning::Could not list Release runs on main; retrying."
pending=1
fi
if [[ "$pending" -eq 0 ]]; then
break
fi
if (( SECONDS >= deadline )); then
echo "::error::The publish job of a Release run on main is still in progress after 120 minutes. Re-run this job once it finishes."
exit 1
fi
echo "Waiting for the publish job of ${pending} Release run(s) on main to finish..."
sleep 30
done
# A major cut can fast-forward develop onto next while the tests and the wait above run.
- id: next-still-ahead
if: ${{ github.ref_name == 'next' }}
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
run: |
set -euo pipefail
ahead_by="$(gh api "repos/${REPO}/compare/develop...${SHA}" --jq .ahead_by)"
if [[ "$ahead_by" -gt 0 ]]; then
echo "ahead=true" >> "$GITHUB_OUTPUT"
else
echo "ahead=false" >> "$GITHUB_OUTPUT"
fi
# semantic-release runs `git push --dry-run HEAD:<branch>` as part of verifyAuth even in
# `dry_run: true` mode, so the token needs push access to the protected develop/main
# branches — the default GITHUB_TOKEN doesn't have it.
- id: app-token
if: github.event_name == 'push' || inputs.version == ''
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
# `persist-credentials: false` is required: otherwise checkout caches the default
# GITHUB_TOKEN in git config, and its Authorization header overrides the App token
# semantic-release puts in the push URL, making the dry-push get rejected by branch protection.
- uses: useblacksmith/checkout@6fd481652155169ed4d2f25ebaf97464f685175f # v1
with:
fetch-depth: 0
persist-credentials: false
- id: semantic-release
if: github.event_name == 'push' || inputs.version == ''
uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6.0.0
with:
working_directory: apps/cli
dry_run: true
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
- id: compute
env:
EVENT: ${{ github.event_name }}
REF_NAME: ${{ github.ref_name }}
DISPATCH_CHANNEL: ${{ inputs.channel }}
DISPATCH_VERSION: ${{ inputs.version }}
DISPATCH_DRY_RUN: ${{ inputs.dry_run }}
NEXT_AHEAD: ${{ steps.next-still-ahead.outputs.ahead }}
SR_PUBLISHED: ${{ steps.semantic-release.outputs.new_release_published }}
SR_VERSION: ${{ steps.semantic-release.outputs.new_release_version }}
run: |
set -euo pipefail
maint_re='^v([0-9]+)\.x$'
version_re='^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$'
plain_re='^[0-9]+\.[0-9]+\.[0-9]+$'
next_re='^[0-9]+\.[0-9]+\.[0-9]+-next\.[0-9]+$'
beta_re='^[0-9]+\.[0-9]+\.[0-9]+-beta\.[0-9]+$'
natural=""
case "$REF_NAME" in
develop) natural=beta ;;
main) natural=stable ;;
next) natural=next ;;
*) [[ "$REF_NAME" =~ $maint_re ]] && natural=maintenance ;;
esac
if [[ "$REF_NAME" == next && "$NEXT_AHEAD" != true ]]; then
if [[ "$EVENT" == "workflow_dispatch" ]]; then
echo "::error::next has no commits beyond develop, so a next release would tag a commit develop already released"
exit 1
fi
echo "::notice::next has no commits beyond develop, so there is nothing to release"
echo "should_release=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [[ "$EVENT" == "workflow_dispatch" ]]; then
channel="$DISPATCH_CHANNEL"
dry_run="$DISPATCH_DRY_RUN"
if [[ "$natural" == maintenance && "$channel" != maintenance ]] || [[ "$natural" != maintenance && "$channel" == maintenance ]]; then
echo "::error::channel=maintenance is only valid on v<N>.x refs, and v<N>.x refs only release the maintenance channel"
exit 1
fi
if [[ "$channel" == next && "$REF_NAME" != next ]]; then
echo "::error::channel=next must be dispatched on the next branch"
exit 1
fi
if [[ "$natural" == next && "$channel" != next ]]; then
echo "::error::${REF_NAME} only releases the next channel"
exit 1
fi
if [[ "$channel" == stable && "$dry_run" != true && "$REF_NAME" != main ]]; then
echo "::error::A stable release must be dispatched on main; other refs may only dry-run"
exit 1
fi
if [[ -n "$DISPATCH_VERSION" ]]; then
if [[ ! "$DISPATCH_VERSION" =~ $version_re ]]; then
echo "::error::version ${DISPATCH_VERSION} is not a semver version"
exit 1
fi
case "$channel" in
next) channel_re="$next_re"; shape="X.Y.Z-next.N" ;;
beta) channel_re="$beta_re"; shape="X.Y.Z-beta.N" ;;
*) channel_re="$plain_re"; shape="X.Y.Z" ;;
esac
if [[ ! "$DISPATCH_VERSION" =~ $channel_re ]]; then
echo "::error::version ${DISPATCH_VERSION} does not match channel ${channel} (expected ${shape})"
exit 1
fi
version="$DISPATCH_VERSION"
else
if [[ "$channel" != "$natural" ]]; then
echo "::error::An empty version uses semantic-release, so channel must match ${REF_NAME} (${natural:-unconfigured})"
exit 1
fi
if [[ "$SR_PUBLISHED" != "true" ]]; then
echo "::error::semantic-release computed no release for ${REF_NAME}"
exit 1
fi
version="$SR_VERSION"
fi
should_release=true
else
dry_run=false
if [[ "$SR_PUBLISHED" != "true" ]]; then
echo "should_release=false" >> "$GITHUB_OUTPUT"
exit 0
fi
version="$SR_VERSION"
channel="$natural"
should_release=true
fi
case "$channel" in
beta)
npm_tag=beta
prerelease=true
brew_name=supabase-beta
scoop_name=supabase-beta
publish_brew_scoop=true
;;
stable)
npm_tag=latest
prerelease=false
brew_name=supabase
scoop_name=supabase
publish_brew_scoop=true
;;
next)
npm_tag=next
prerelease=true
brew_name=""
scoop_name=""
publish_brew_scoop=false
;;
maintenance)
[[ "$REF_NAME" =~ $maint_re ]]
major="${BASH_REMATCH[1]}"
if [[ "${version%%.*}" != "$major" ]]; then
echo "::error::version ${version} is not on the v${major} line"
exit 1
fi
npm_tag="v${major}.stable"
prerelease=false
brew_name="supabase@${major}"
scoop_name="supabase-v${major}-stable"
publish_brew_scoop=true
;;
*)
echo "Unknown channel: $channel" >&2
exit 1
;;
esac
{
echo "should_release=$should_release"
echo "version=$version"
echo "npm_tag=$npm_tag"
echo "prerelease=$prerelease"
echo "brew_name=$brew_name"
echo "scoop_name=$scoop_name"
echo "publish_brew_scoop=$publish_brew_scoop"
echo "dry_run=$dry_run"
echo "channel=$channel"
} >> "$GITHUB_OUTPUT"
release:
name: Release
needs: plan
if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.should_release == 'true' }}
# pull-requests: write is required by the nested propose-release-notes workflow
# (release-shared.yml -> propose-release-notes.yml): a called job's permissions can't exceed
# those granted to the calling job, so this must be declared here too.
permissions:
actions: read
attestations: write
contents: write
id-token: write
pull-requests: write
uses: ./.github/workflows/release-shared.yml
with:
version: ${{ needs.plan.outputs.version }}
npm_tag: ${{ needs.plan.outputs.npm_tag }}
prerelease: ${{ needs.plan.outputs.prerelease == 'true' }}
publish_brew_scoop: ${{ needs.plan.outputs.publish_brew_scoop == 'true' }}
brew_name: ${{ needs.plan.outputs.brew_name }}
scoop_name: ${{ needs.plan.outputs.scoop_name }}
dry_run: ${{ needs.plan.outputs.dry_run == 'true' }}
channel: ${{ needs.plan.outputs.channel }}
secrets:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
POSTHOG_ENDPOINT: ${{ secrets.POSTHOG_ENDPOINT }}
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
DF_FIREWALL_TOKEN: ${{ secrets.DF_FIREWALL_TOKEN }}
LINEAR_CLI_STABLE_RELEASE_ACCESS_KEY: ${{ secrets.LINEAR_CLI_STABLE_RELEASE_ACCESS_KEY }}
LINEAR_CLI_BETA_RELEASE_ACCESS_KEY: ${{ secrets.LINEAR_CLI_BETA_RELEASE_ACCESS_KEY }}
SUPABASE_E2E_CLI_LIVE_STAGING_ACCESS_TOKEN: ${{ secrets.SUPABASE_E2E_CLI_LIVE_STAGING_ACCESS_TOKEN }}
# Republishes the CLI reference and the config schemas to supabase.com. Stable only: both
# document the CLI users actually install, so pre-releases and maintenance lines are skipped.
# Nothing depends on this job, so a docs-site failure cannot affect the already-completed
# release; a failed publish reaches Slack through notify-slack-publish-failure.
docs:
name: Publish to supabase.com
needs: [plan, release]
if: ${{ !cancelled() && needs.release.result == 'success' && needs.plan.outputs.channel == 'stable' }}
runs-on: ubuntu-latest
timeout-minutes: 25
continue-on-error: true
outputs:
failed: ${{ steps.report.outputs.failed }}
steps:
# Scoped to supabase/supabase: this job pushes a branch and opens a PR
# there, and needs nothing beyond a plain read-only checkout here.
- 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 }}
owner: ${{ github.repository_owner }}
repositories: |
supabase
permission-contents: write
permission-pull-requests: write
- uses: useblacksmith/checkout@6fd481652155169ed4d2f25ebaf97464f685175f # v1
with:
persist-credentials: false
- name: Setup
uses: ./.github/actions/setup
with:
dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }}
- name: Authenticate git for the docs push
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh auth setup-git
- name: Publish the CLI reference
id: reference
continue-on-error: true
working-directory: apps/cli
# `shell: bash` for `-o pipefail`: the default `run:` shell takes only the publisher's
# exit status, so a generator that died part-way would look like a successful run.
shell: bash
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
VERSION: ${{ needs.plan.outputs.version }}
DRY_RUN: ${{ needs.plan.outputs.dry_run }}
run: |
args=()
if [[ "$DRY_RUN" == "true" ]]; then
args+=(--dry-run)
fi
bun scripts/generate-docs-spec.ts "$VERSION" \
| bun scripts/publish-docs-spec.ts --version "$VERSION" "${args[@]}"
- name: Publish the config schemas
id: schemas
continue-on-error: true
working-directory: apps/cli
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
VERSION: ${{ needs.plan.outputs.version }}
DRY_RUN: ${{ needs.plan.outputs.dry_run }}
run: |
args=()
if [[ "$DRY_RUN" == "true" ]]; then
args+=(--dry-run)
fi
bun scripts/publish-schemas.ts --version "$VERSION" "${args[@]}"
- name: Report failed publishes
id: report
if: ${{ !cancelled() }}
env:
REFERENCE_OUTCOME: ${{ steps.reference.outcome }}
SCHEMAS_OUTCOME: ${{ steps.schemas.outcome }}
run: |
failed=false
report() {
if [[ "$2" != "success" ]]; then
failed=true
echo "::error title=Publishing the $1 failed::The $1 was not published to supabase.com (step outcome: $2)."
echo "- The $1 was not published to supabase.com (step outcome: $2)." >> "$GITHUB_STEP_SUMMARY"
fi
}
report "CLI reference" "$REFERENCE_OUTCOME"
report "config schemas" "$SCHEMAS_OUTCOME"
echo "failed=$failed" >> "$GITHUB_OUTPUT"
if [[ "$failed" == "true" ]]; then
exit 1
fi
# Notifies real (non-dry-run) stable and maintenance cuts. Nothing depends on this job, so a
# Slack failure can't affect the already-completed release.
notify-slack:
name: Notify Slack
needs: [plan, release]
if: >-
${{ !cancelled() &&
needs.release.result == 'success' &&
needs.plan.outputs.dry_run != 'true' &&
(needs.plan.outputs.channel == 'stable' || needs.plan.outputs.channel == 'maintenance') }}
uses: ./.github/workflows/slack-notify.yml
with:
package: Supabase CLI
status: success
version: ${{ needs.plan.outputs.version }}
channel: ${{ needs.plan.outputs.channel }}
secrets:
SLACK_RELEASE_WEBHOOK: ${{ secrets.SLACK_RELEASE_WEBHOOK }}
# Pages when the release shipped but the supabase.com publish did not. The docs job's
# continue-on-error hides its own failures, so its report step exports `failed`; a job that ran
# without exporting `failed=false` (timeout, crash before the report step) counts as failed.
notify-slack-publish-failure:
name: Notify Slack (publish failure)
needs: [plan, docs]
if: ${{ !cancelled() && needs.plan.outputs.dry_run != 'true' && needs.docs.result != 'skipped' && needs.docs.outputs.failed != 'false' }}
uses: ./.github/workflows/slack-notify.yml
with:
package: Supabase CLI
status: publish-failure
version: ${{ needs.plan.outputs.version }}
channel: ${{ needs.plan.outputs.channel }}
secrets:
SLACK_RELEASE_WEBHOOK: ${{ secrets.SLACK_RELEASE_WEBHOOK }}
# Reports a broken release on every channel. `failure()` evaluates against the full `needs`
# chain, so this fires whenever `next-ahead`, `test-next`, `plan` or `release` (including the
# reusable release-shared workflow) fails. Skipped jobs don't count as failures. Dry runs are
# excluded via the dispatch input directly, so even a plan job that dies before recording its
# `dry_run` output stays quiet. A failed `next-ahead` or `test-next` skips `plan`, so the
# channel falls back to the ref.
notify-slack-failure:
name: Notify Slack (failure)
needs: [next-ahead, test-next, plan, release]
if: ${{ failure() && !(github.event_name == 'workflow_dispatch' && inputs.dry_run) }}
uses: ./.github/workflows/slack-notify.yml
with:
package: Supabase CLI
status: failure
version: ${{ needs.plan.outputs.version }}
channel: ${{ needs.plan.outputs.channel || (github.ref_name == 'next' && 'next') || '' }}
secrets:
SLACK_RELEASE_WEBHOOK: ${{ secrets.SLACK_RELEASE_WEBHOOK }}