From 756587e3535113ef43c75744f8fc046467a4e7b2 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 May 2026 10:49:38 +0000 Subject: [PATCH 1/4] ci(cli): add setup-cli smoke tests for beta releases Adds two post-publish jobs to release-shared.yml that exercise supabase/setup-cli against the just-published beta across the runners real users hit: ubuntu-latest, macos-latest, windows-latest, and an Alpine container (node:20-alpine to provide a musl-linked Node for the JS action to launch). Gated to beta releases (`channel == 'beta' && !dry_run`) so PR smoke runs and stable cuts don't pay the wall-clock cost. The Alpine leg exists to catch regressions like supabase/setup-cli#427 where musl libc + archive layout changes broke the action silently. Refs CLI-1507. --- .github/workflows/release-shared.yml | 77 ++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/.github/workflows/release-shared.yml b/.github/workflows/release-shared.yml index aed96350a..7ffd0fbb5 100644 --- a/.github/workflows/release-shared.yml +++ b/.github/workflows/release-shared.yml @@ -329,6 +329,83 @@ jobs: GH_TOKEN: ${{ github.token }} run: gh release edit "v${VERSION}" --draft=false + # Post-publish smoke test for the `supabase/setup-cli` GitHub Action against + # the just-released beta. The action fetches the binary from the GitHub + # release we just published, so it must run after `publish`. It exists to + # catch regressions like supabase/setup-cli#427 where the action silently + # stopped working on Alpine-based containers (musl libc + archive layout + # changes broke PATH resolution and binary execution). + # + # Gated to beta releases only: beta is where setup-cli regressions need to + # be caught fast (it's the channel CI users typically pin via + # `version: latest` on `develop`), and running the full matrix on stable + # would double the post-publish wall-clock for no extra signal. + setup-cli-smoke-test: + needs: publish + if: ${{ !inputs.dry_run && inputs.channel == 'beta' }} + strategy: + fail-fast: false + matrix: + runner: + - ubuntu-latest + - macos-latest + - windows-latest + runs-on: ${{ matrix.runner }} + env: + VERSION: ${{ inputs.version }} + steps: + - name: Install Supabase CLI via setup-cli + uses: supabase/setup-cli@v1 + with: + version: ${{ inputs.version }} + - name: Verify supabase --version matches the published beta + shell: bash + run: | + set -euo pipefail + actual="$(supabase --version | tr -d '\r' | head -n1)" + echo "supabase --version: ${actual}" + if [ "${actual}" != "${VERSION}" ]; then + echo "Version mismatch: expected ${VERSION}, got ${actual}" >&2 + exit 1 + fi + + # Alpine leg of the setup-cli smoke test. Kept as a separate job because + # GitHub Actions only honours the `container:` field on Linux runners, and + # mixing container + non-container entries in a single matrix via + # `container: ${{ matrix.container }}` is fragile when the value is empty. + # + # The musl-vs-glibc + archive layout regressions tracked in + # supabase/setup-cli#427 only reproduce inside a real Alpine container, so + # this job is the actual signal the task in CLI-1507 was created for. + setup-cli-smoke-test-alpine: + needs: publish + if: ${{ !inputs.dry_run && inputs.channel == 'beta' }} + runs-on: ubuntu-latest + # `node:20-alpine` ships a musl-linked Node, which is required for + # JavaScript-based actions (setup-cli included) to launch inside an + # Alpine container — the runner's mounted glibc Node won't execute here. + container: + image: node:20-alpine + env: + VERSION: ${{ inputs.version }} + steps: + - name: Install Alpine prerequisites + run: apk add --no-cache bash curl tar + - name: Install Supabase CLI via setup-cli + uses: supabase/setup-cli@v1 + with: + version: ${{ inputs.version }} + - name: Verify supabase --version matches the published beta + shell: bash + run: | + set -euo pipefail + actual="$(supabase --version | tr -d '\r' | head -n1)" + echo "supabase --version: ${actual}" + if [ "${actual}" != "${VERSION}" ]; then + echo "Version mismatch: expected ${VERSION}, got ${actual}" >&2 + exit 1 + fi + publish-homebrew: needs: publish if: ${{ !inputs.dry_run && inputs.publish_brew_scoop }} From fe5d4e18368c21c89348b6afd49ede58531303ff Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 May 2026 10:54:02 +0000 Subject: [PATCH 2/4] ci(cli): make setup-cli smoke test manually dispatchable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracts the post-beta setup-cli smoke jobs into a standalone reusable workflow (`setup-cli-smoke-test.yml`) that supports both `workflow_call` (invoked from release-shared.yml after a beta publish) and `workflow_dispatch` with a `version` input. This lets us re-run the smoke against any already-published CLI version on demand — useful when debugging setup-cli regressions or confirming a previously broken environment (Alpine) is back to green without cutting a new release. release-shared.yml's two inline jobs collapse to a single nested workflow call. --- .github/workflows/release-shared.yml | 76 ++---------------- .github/workflows/setup-cli-smoke-test.yml | 90 ++++++++++++++++++++++ 2 files changed, 98 insertions(+), 68 deletions(-) create mode 100644 .github/workflows/setup-cli-smoke-test.yml diff --git a/.github/workflows/release-shared.yml b/.github/workflows/release-shared.yml index 7ffd0fbb5..84e4c3818 100644 --- a/.github/workflows/release-shared.yml +++ b/.github/workflows/release-shared.yml @@ -331,80 +331,20 @@ jobs: # Post-publish smoke test for the `supabase/setup-cli` GitHub Action against # the just-released beta. The action fetches the binary from the GitHub - # release we just published, so it must run after `publish`. It exists to - # catch regressions like supabase/setup-cli#427 where the action silently - # stopped working on Alpine-based containers (musl libc + archive layout - # changes broke PATH resolution and binary execution). + # release we just published, so it must run after `publish`. # # Gated to beta releases only: beta is where setup-cli regressions need to # be caught fast (it's the channel CI users typically pin via # `version: latest` on `develop`), and running the full matrix on stable - # would double the post-publish wall-clock for no extra signal. - setup-cli-smoke-test: + # would double the post-publish wall-clock for no extra signal. The same + # reusable workflow can be dispatched manually against an arbitrary + # already-published version when debugging setup-cli regressions. + setup-cli-smoke: needs: publish if: ${{ !inputs.dry_run && inputs.channel == 'beta' }} - strategy: - fail-fast: false - matrix: - runner: - - ubuntu-latest - - macos-latest - - windows-latest - runs-on: ${{ matrix.runner }} - env: - VERSION: ${{ inputs.version }} - steps: - - name: Install Supabase CLI via setup-cli - uses: supabase/setup-cli@v1 - with: - version: ${{ inputs.version }} - - name: Verify supabase --version matches the published beta - shell: bash - run: | - set -euo pipefail - actual="$(supabase --version | tr -d '\r' | head -n1)" - echo "supabase --version: ${actual}" - if [ "${actual}" != "${VERSION}" ]; then - echo "Version mismatch: expected ${VERSION}, got ${actual}" >&2 - exit 1 - fi - - # Alpine leg of the setup-cli smoke test. Kept as a separate job because - # GitHub Actions only honours the `container:` field on Linux runners, and - # mixing container + non-container entries in a single matrix via - # `container: ${{ matrix.container }}` is fragile when the value is empty. - # - # The musl-vs-glibc + archive layout regressions tracked in - # supabase/setup-cli#427 only reproduce inside a real Alpine container, so - # this job is the actual signal the task in CLI-1507 was created for. - setup-cli-smoke-test-alpine: - needs: publish - if: ${{ !inputs.dry_run && inputs.channel == 'beta' }} - runs-on: ubuntu-latest - # `node:20-alpine` ships a musl-linked Node, which is required for - # JavaScript-based actions (setup-cli included) to launch inside an - # Alpine container — the runner's mounted glibc Node won't execute here. - container: - image: node:20-alpine - env: - VERSION: ${{ inputs.version }} - steps: - - name: Install Alpine prerequisites - run: apk add --no-cache bash curl tar - - name: Install Supabase CLI via setup-cli - uses: supabase/setup-cli@v1 - with: - version: ${{ inputs.version }} - - name: Verify supabase --version matches the published beta - shell: bash - run: | - set -euo pipefail - actual="$(supabase --version | tr -d '\r' | head -n1)" - echo "supabase --version: ${actual}" - if [ "${actual}" != "${VERSION}" ]; then - echo "Version mismatch: expected ${VERSION}, got ${actual}" >&2 - exit 1 - fi + uses: ./.github/workflows/setup-cli-smoke-test.yml + with: + version: ${{ inputs.version }} publish-homebrew: needs: publish diff --git a/.github/workflows/setup-cli-smoke-test.yml b/.github/workflows/setup-cli-smoke-test.yml new file mode 100644 index 000000000..7c45b07d3 --- /dev/null +++ b/.github/workflows/setup-cli-smoke-test.yml @@ -0,0 +1,90 @@ +name: setup-cli Smoke Test + +# Smoke test for the `supabase/setup-cli` GitHub Action against a specific +# published CLI version. Run automatically after every beta release (called +# from release-shared.yml's `setup-cli-smoke` job) and also manually via +# workflow_dispatch when debugging setup-cli regressions or verifying that +# a previously broken environment (e.g. Alpine) is back to green. +# +# Exists primarily to catch regressions like supabase/setup-cli#427 where +# musl libc + archive layout changes broke the action on Alpine silently. + +on: + workflow_call: + inputs: + version: + description: Supabase CLI version to install via setup-cli (must already be published to GitHub Releases) + required: true + type: string + workflow_dispatch: + inputs: + version: + description: Supabase CLI version to install via setup-cli (must already be published to GitHub Releases) + required: true + type: string + +permissions: + contents: read + +jobs: + setup-cli-smoke-test: + strategy: + fail-fast: false + matrix: + runner: + - ubuntu-latest + - macos-latest + - windows-latest + runs-on: ${{ matrix.runner }} + env: + VERSION: ${{ inputs.version }} + steps: + - name: Install Supabase CLI via setup-cli + uses: supabase/setup-cli@v1 + with: + version: ${{ inputs.version }} + - name: Verify supabase --version matches the expected version + shell: bash + run: | + set -euo pipefail + actual="$(supabase --version | tr -d '\r' | head -n1)" + echo "supabase --version: ${actual}" + if [ "${actual}" != "${VERSION}" ]; then + echo "Version mismatch: expected ${VERSION}, got ${actual}" >&2 + exit 1 + fi + + # Alpine leg of the setup-cli smoke test. Kept as a separate job because + # GitHub Actions only honours the `container:` field on Linux runners, and + # mixing container + non-container entries in a single matrix via + # `container: ${{ matrix.container }}` is fragile when the value is empty. + # + # The musl-vs-glibc + archive layout regressions tracked in + # supabase/setup-cli#427 only reproduce inside a real Alpine container, so + # this job is the actual signal the workflow was created for. + setup-cli-smoke-test-alpine: + runs-on: ubuntu-latest + # `node:20-alpine` ships a musl-linked Node, which is required for + # JavaScript-based actions (setup-cli included) to launch inside an + # Alpine container — the runner's mounted glibc Node won't execute here. + container: + image: node:20-alpine + env: + VERSION: ${{ inputs.version }} + steps: + - name: Install Alpine prerequisites + run: apk add --no-cache bash curl tar + - name: Install Supabase CLI via setup-cli + uses: supabase/setup-cli@v1 + with: + version: ${{ inputs.version }} + - name: Verify supabase --version matches the expected version + shell: bash + run: | + set -euo pipefail + actual="$(supabase --version | tr -d '\r' | head -n1)" + echo "supabase --version: ${actual}" + if [ "${actual}" != "${VERSION}" ]; then + echo "Version mismatch: expected ${VERSION}, got ${actual}" >&2 + exit 1 + fi From b859cad3a855f3b308140f28e6456b32ab07d06f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 May 2026 10:56:53 +0000 Subject: [PATCH 3/4] ci(cli): run setup-cli smoke last, after homebrew/scoop publish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves the setup-cli smoke job to the end of release-shared.yml and hangs it off publish, publish-homebrew, and publish-scoop so it can never block the brew/scoop pushes — those run first and the smoke is a post-release signal that fires regardless of channel. `if: always() && !inputs.dry_run && needs.publish.result == 'success'` keeps the smoke running when publish-homebrew / publish-scoop skip (alpha) or fail, while still requiring the GitHub release the action needs to download from. --- .github/workflows/release-shared.yml | 36 +++++++++++++++------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/.github/workflows/release-shared.yml b/.github/workflows/release-shared.yml index 84e4c3818..6de5ddc0c 100644 --- a/.github/workflows/release-shared.yml +++ b/.github/workflows/release-shared.yml @@ -329,23 +329,6 @@ jobs: GH_TOKEN: ${{ github.token }} run: gh release edit "v${VERSION}" --draft=false - # Post-publish smoke test for the `supabase/setup-cli` GitHub Action against - # the just-released beta. The action fetches the binary from the GitHub - # release we just published, so it must run after `publish`. - # - # Gated to beta releases only: beta is where setup-cli regressions need to - # be caught fast (it's the channel CI users typically pin via - # `version: latest` on `develop`), and running the full matrix on stable - # would double the post-publish wall-clock for no extra signal. The same - # reusable workflow can be dispatched manually against an arbitrary - # already-published version when debugging setup-cli regressions. - setup-cli-smoke: - needs: publish - if: ${{ !inputs.dry_run && inputs.channel == 'beta' }} - uses: ./.github/workflows/setup-cli-smoke-test.yml - with: - version: ${{ inputs.version }} - publish-homebrew: needs: publish if: ${{ !inputs.dry_run && inputs.publish_brew_scoop }} @@ -429,3 +412,22 @@ jobs: run: pnpm exec bun apps/cli/scripts/update-scoop.ts --version "${VERSION}" --name "${SCOOP_NAME}" env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + + # Post-publish smoke test for the `supabase/setup-cli` GitHub Action against + # the just-released CLI. Runs last and intentionally does not gate + # publish-homebrew / publish-scoop — by the time the smoke runs, the npm + # package and GitHub release are already live and the brew/scoop pushes + # have either succeeded or skipped, so a setup-cli regression surfaces as + # a red post-release signal without holding back the rest of the channel. + # + # Depends on the publish jobs only via `needs` for ordering; the `if` + # uses `always() && needs.publish.result == 'success'` so the smoke still + # runs when publish-homebrew / publish-scoop are skipped (alpha) or fail. + # The reusable workflow can also be dispatched manually against any + # already-published version when debugging setup-cli regressions. + setup-cli-smoke: + needs: [publish, publish-homebrew, publish-scoop] + if: ${{ always() && !inputs.dry_run && needs.publish.result == 'success' }} + uses: ./.github/workflows/setup-cli-smoke-test.yml + with: + version: ${{ inputs.version }} From 86b686d2a77a9ce039cd3e408af73ba2cb5b1b6f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 May 2026 11:06:34 +0000 Subject: [PATCH 4/4] ci(cli): smoke setup-cli @v1 and @v2 side by side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub Actions disallows expressions in `uses:`, so the action ref is selected via two `if:`-gated install steps keyed off a new `major-version` matrix dimension. The cross-runner matrix now fans out to 6 legs (ubuntu/macos/windows × v1/v2) and the Alpine job to 2 legs (v1/v2), covering both the v1 archive-layout path and the v2 glibc/musl bun-binary path called out in supabase/setup-cli#427. --- .github/workflows/setup-cli-smoke-test.yml | 30 ++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/.github/workflows/setup-cli-smoke-test.yml b/.github/workflows/setup-cli-smoke-test.yml index 7c45b07d3..1ef028315 100644 --- a/.github/workflows/setup-cli-smoke-test.yml +++ b/.github/workflows/setup-cli-smoke-test.yml @@ -28,6 +28,7 @@ permissions: jobs: setup-cli-smoke-test: + name: setup-cli ${{ matrix.major-version }} (${{ matrix.runner }}) strategy: fail-fast: false matrix: @@ -35,14 +36,26 @@ jobs: - ubuntu-latest - macos-latest - windows-latest + # GitHub Actions doesn't allow expressions in `uses:`, so the action + # ref is selected via two `if:`-gated install steps below rather than + # interpolating `matrix.major-version` into a single `uses:` line. + major-version: + - v1 + - v2 runs-on: ${{ matrix.runner }} env: VERSION: ${{ inputs.version }} steps: - - name: Install Supabase CLI via setup-cli + - name: Install Supabase CLI via setup-cli@v1 + if: matrix.major-version == 'v1' uses: supabase/setup-cli@v1 with: version: ${{ inputs.version }} + - name: Install Supabase CLI via setup-cli@v2 + if: matrix.major-version == 'v2' + uses: supabase/setup-cli@v2 + with: + version: ${{ inputs.version }} - name: Verify supabase --version matches the expected version shell: bash run: | @@ -63,6 +76,13 @@ jobs: # supabase/setup-cli#427 only reproduce inside a real Alpine container, so # this job is the actual signal the workflow was created for. setup-cli-smoke-test-alpine: + name: setup-cli ${{ matrix.major-version }} (alpine) + strategy: + fail-fast: false + matrix: + major-version: + - v1 + - v2 runs-on: ubuntu-latest # `node:20-alpine` ships a musl-linked Node, which is required for # JavaScript-based actions (setup-cli included) to launch inside an @@ -74,10 +94,16 @@ jobs: steps: - name: Install Alpine prerequisites run: apk add --no-cache bash curl tar - - name: Install Supabase CLI via setup-cli + - name: Install Supabase CLI via setup-cli@v1 + if: matrix.major-version == 'v1' uses: supabase/setup-cli@v1 with: version: ${{ inputs.version }} + - name: Install Supabase CLI via setup-cli@v2 + if: matrix.major-version == 'v2' + uses: supabase/setup-cli@v2 + with: + version: ${{ inputs.version }} - name: Verify supabase --version matches the expected version shell: bash run: |