Compare commits

..

4 Commits

Author SHA1 Message Date
Han Qiao
a35ea70530 Update README.md 2025-07-23 15:45:46 +08:00
Han Qiao
d3753c9dbc chore: comment on local port 2025-07-23 15:45:11 +08:00
Han Qiao
d9e25d90f6 Merge branch 'main' into bump-default 2025-07-23 15:07:34 +08:00
Qiao Han
dadb6ee5a7 fix: bump default cli version to 2.33.0 2025-07-23 00:35:05 +08:00
52 changed files with 46796 additions and 1602 deletions

View File

@@ -1 +0,0 @@
1.3.10

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
* text=auto eol=lf
dist/** -diff linguist-generated=true

2
.github/CODEOWNERS vendored
View File

@@ -1 +1 @@
* @supabase/cli
* @supabase/dev-workflows

5
.github/codeql/codeql-config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
name: JavaScript CodeQL Configuration
paths-ignore:
- node_modules
- dist

View File

@@ -4,33 +4,28 @@ updates:
directory: /
schedule:
interval: weekly
day: tuesday
time: "09:00"
timezone: Europe/Paris
open-pull-requests-limit: 1
groups:
actions-minor-patch:
patterns:
- "*"
actions-minor:
update-types:
- minor
- patch
- package-ecosystem: bun
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: tuesday
time: "09:00"
timezone: Europe/Paris
open-pull-requests-limit: 1
cooldown:
semver-minor-days: 7
semver-patch-days: 2
interval: daily
groups:
bun-minor-patch:
patterns:
- "*"
npm-development:
dependency-type: development
update-types:
- minor
- patch
npm-production:
dependency-type: production
update-types:
- patch
ignore:
# nodejs types is pinned to runtime version
- dependency-name: '@types/node'
update-types:
- version-update:semver-major

View File

@@ -1,69 +0,0 @@
name: CI
on:
pull_request:
types:
- opened
- synchronize
- reopened
- ready_for_review
- converted_to_draft
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
defaults:
run:
shell: bash
jobs:
validate:
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }}
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- run: bun install --frozen-lockfile
- run: bun run ci
test:
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }}
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
version: [1.0.0, latest]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./
with:
version: ${{ matrix.version }}
github-token: ${{ github.token }}
- run: supabase -h
ci:
if: ${{ always() && github.event_name == 'pull_request' }}
name: CI
runs-on: ubuntu-latest
needs: [validate, test]
timeout-minutes: 5
steps:
- run: |
validate_result="${{ needs.validate.result }}"
test_result="${{ needs.test.result }}"
[[ "$validate_result" == "success" || "$validate_result" == "skipped" ]]
[[ "$test_result" == "success" || "$test_result" == "skipped" ]]

47
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: CodeQL
on:
pull_request:
push:
branches:
- main
schedule:
- cron: '31 7 * * 3'
permissions:
actions: read
checks: write
contents: read
security-events: write
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language:
- typescript
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
- name: Initialize CodeQL
id: initialize
uses: github/codeql-action/init@v3
with:
config-file: .github/codeql/codeql-config.yml
languages: ${{ matrix.language }}
source-root: src
- name: Autobuild
id: autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
id: analyze
uses: github/codeql-action/analyze@v3

View File

@@ -3,10 +3,6 @@ name: Dependabot auto-merge
on: pull_request
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
pull-requests: write
contents: write
@@ -14,33 +10,29 @@ permissions:
jobs:
dependabot:
runs-on: ubuntu-latest
timeout-minutes: 10
# Only act on PRs opened by Dependabot from branches in this repository.
if: github.actor == 'dependabot[bot]' && github.repository == github.event.pull_request.head.repo.full_name
# Checking the actor will prevent your Action run failing on non-Dependabot
# PRs but also ensures that it only does work for Dependabot PRs.
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
# Metadata drives the non-major gating used for approval and auto-merge.
# This first step will fail if there's no metadata and so the approval
# will not occur.
- id: meta
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0
uses: dependabot/fetch-metadata@v2
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Generate token
id: app-token
if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor' }}
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
github-token: '${{ secrets.GITHUB_TOKEN }}'
# Here the PR gets approved.
- name: Approve a PR
if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor' }}
if: ${{steps.meta.outputs.update-type != 'version-update:semver-major'}}
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Finally, this sets the PR to allow auto-merging for patch and minor
# updates if all checks pass
- name: Enable auto-merge for Dependabot PRs
if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor' }}
if: ${{steps.meta.outputs.update-type != 'version-update:semver-major'}}
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}

View File

@@ -1,71 +0,0 @@
name: E2E
on:
pull_request:
types:
- opened
- synchronize
- reopened
- ready_for_review
- converted_to_draft
push:
branches:
- main
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
schedule:
# * is a special character in YAML so you have to quote this string
- cron: "30 1,9 * * *"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
permissions:
contents: read
jobs:
e2e: # make sure the action works on a clean machine without building
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }}
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
version:
- 1.178.2
- 2.33.0
- latest
pg_major:
- 14
- 15
- 17
exclude:
- version: 1.178.2
pg_major: 17
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: ./
with:
version: ${{ matrix.version }}
- run: supabase init
- run: |
sed -i -E "s|^(major_version) .*|\\1 = ${{ matrix.pg_major }}|" supabase/config.toml
- run: supabase start
e2e-check:
if: ${{ always() && github.event_name == 'pull_request' }}
name: E2E
runs-on: ubuntu-latest
needs: [e2e]
timeout-minutes: 5
steps:
- run: |
e2e_result="${{ needs.e2e.result }}"
[[ "$e2e_result" == "success" || "$e2e_result" == "skipped" ]]

View File

@@ -1,6 +1,6 @@
# This workflow refreshes and checks dependency license records used in this
# action with the help of the Licensed tool. If any licenses are invalid or
# missing, this workflow will fail. See: https://github.com/licensee/licensed
# This workflow checks the statuses of cached dependencies used in this action
# with the help of the Licensed tool. If any licenses are invalid or missing,
# this workflow will fail. See: https://github.com/licensee/licensed
name: Licensed
@@ -9,139 +9,61 @@ on:
push:
branches:
- main
paths:
- .github/workflows/licensed.yml
- .licensed.yml
- .licenses/**
- bun.lock
- package.json
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check-licenses:
if: ${{ github.event_name != 'workflow_dispatch' }}
name: Licensed
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
pull-requests: read
steps:
- name: Detect license inputs
id: license-inputs
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPOSITORY: ${{ github.repository }}
run: |
if [[ "${{ github.event_name }}" != "pull_request" ]]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
exit 0
fi
gh api "repos/${REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate --jq '.[].filename' > changed-files.txt
if grep -Eq '^(\.github/workflows/licensed\.yml|\.licensed\.yml|\.licenses/.*|bun\.lock|package\.json)$' changed-files.txt; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
fi
- name: Checkout
id: checkout
if: steps.license-inputs.outputs.changed == 'true'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Bun
id: setup-bun
if: steps.license-inputs.outputs.changed == 'true'
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- name: Install Dependencies
id: bun-install
if: steps.license-inputs.outputs.changed == 'true'
run: bun install --frozen-lockfile
- name: Setup Ruby
id: setup-ruby
if: steps.license-inputs.outputs.changed == 'true'
uses: ruby/setup-ruby@97ecb7b512899eb71ab1bf2310a624c6f1589ac6 # v1.308.0
with:
ruby-version: ruby
- uses: licensee/setup-licensed@0d52e575b3258417672be0dff2f115d7db8771d8 # v1.3.2
if: steps.license-inputs.outputs.changed == 'true'
with:
version: 4.x
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Refresh License Cache
id: refresh-license-cache
if: steps.license-inputs.outputs.changed == 'true'
run: licensed cache
- name: Check Licenses
id: check-licenses
if: steps.license-inputs.outputs.changed == 'true'
run: licensed status
update-licenses:
if: ${{ github.event_name == 'workflow_dispatch' }}
name: Update Licenses
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
permissions:
contents: write
jobs:
licensed:
name: Check Licenses
runs-on: ubuntu-latest
steps:
- name: Checkout
id: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@v4
- name: Setup Bun
id: setup-bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
- name: Setup Node.js
id: setup-node
uses: actions/setup-node@v4
with:
bun-version-file: .bun-version
node-version-file: .node-version
cache: npm
- name: Install Dependencies
id: bun-install
run: bun install --frozen-lockfile
id: npm-ci
run: npm ci
- name: Setup Ruby
id: setup-ruby
uses: ruby/setup-ruby@97ecb7b512899eb71ab1bf2310a624c6f1589ac6 # v1.308.0
uses: ruby/setup-ruby@v1
with:
ruby-version: ruby
- uses: licensee/setup-licensed@0d52e575b3258417672be0dff2f115d7db8771d8 # v1.3.2
- uses: licensee/setup-licensed@v1.3.2
with:
version: 4.x
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Update License Cache
# If this is a workflow_dispatch event, update the cached licenses.
- if: ${{ github.event_name == 'workflow_dispatch' }}
name: Update Licenses
id: update-licenses
run: licensed cache
- name: Format License Files
id: format-licenses
run: bun x oxfmt --write .licensed.yml .licenses
- name: Commit Licenses
# Then, commit the updated licenses to the repository.
- if: ${{ github.event_name == 'workflow_dispatch' }}
name: Commit Licenses
id: commit-licenses
run: |
git config --local user.email "licensed-ci@users.noreply.github.com"
git config --local user.name "licensed-ci"
git add .licenses .licensed.yml
if git diff --cached --quiet; then
echo "No license cache changes to commit."
exit 0
fi
git add .
git commit -m "Auto-update license files"
git push
# Last, check the status of the cached licenses.
- name: Check Licenses
id: check-licenses
run: licensed status

56
.github/workflows/linter.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
# This workflow will lint the entire codebase using the
# `super-linter/super-linter` action.
#
# For more information, see the super-linter repository:
# https://github.com/super-linter/super-linter
name: Lint Codebase
on:
pull_request:
push:
branches:
- main
permissions:
contents: read
packages: read
statuses: write
jobs:
lint:
name: Lint Codebase
runs-on: ubuntu-latest
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
id: setup-node
uses: actions/setup-node@v4
with:
node-version-file: .node-version
cache: npm
- name: Install Dependencies
id: install
run: npm ci
- name: Lint Codebase
id: super-linter
uses: super-linter/super-linter/slim@v8
env:
DEFAULT_BRANCH: main
FILTER_REGEX_EXCLUDE: dist/**/*
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LINTER_RULES_PATH: ${{ github.workspace }}
VALIDATE_ALL_CODEBASE: true
VALIDATE_JAVASCRIPT_ES: false
VALIDATE_JAVASCRIPT_STANDARD: false
VALIDATE_JSCPD: false
VALIDATE_TYPESCRIPT_ES: false
VALIDATE_JSON: false
VALIDATE_TYPESCRIPT_STANDARD: false

45
.github/workflows/start.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: CLI Start
on:
push:
branches:
- main
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
schedule:
# * is a special character in YAML so you have to quote this string
- cron: '30 1,9 * * *'
workflow_dispatch:
defaults:
run:
shell: bash
permissions:
contents: read
jobs:
e2e: # make sure the action works on a clean machine without building
runs-on: ubuntu-latest
strategy:
matrix:
version:
- 1.178.2
- 2.33.0
- latest
pg_major:
- 14
- 15
- 17
exclude:
- version: 1.178.2
pg_major: 17
steps:
- uses: actions/checkout@v4
- uses: ./
with:
version: ${{ matrix.version }}
- run: supabase init
- run:
sed -i -E "s|^(major_version) .*|\1 = ${{ matrix.pg_major }}|"
supabase/config.toml
- run: supabase start

68
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,68 @@
name: 'build-test'
on:
pull_request:
push:
branches:
- main
permissions:
contents: read
defaults:
run:
shell: bash
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .node-version
cache: npm
- run: npm ci
- run: npm run all
- id: diff
run: |
if [ ! -d dist/ ]; then
echo "Expected dist/ directory does not exist. See status below:"
ls -la ./
exit 1
fi
if [ "$(git diff --ignore-space-at-eol --text dist/ | wc -l)" -gt "0" ]; then
echo "Detected uncommitted changes after build. See status below:"
git diff --ignore-space-at-eol --text dist/
exit 1
fi
# Upload the mismatched version as a workflow artifact.
- if: ${{ failure() && steps.diff.outcome == 'failure' }}
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
version: [1.0.0, latest]
steps:
- uses: actions/checkout@v4
- uses: ./
with:
version: ${{ matrix.version }}
- run: supabase -h
check:
if: ${{ always() && github.event.pull_request }}
runs-on: ubuntu-latest
needs: [test]
steps:
- run: |
result="${{ needs.test.result }}"
[[ $result == "success" || $result == "skipped" ]]

3
.gitignore vendored
View File

@@ -1,6 +1,7 @@
# Dependency directory
node_modules
bun.lockb
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs
logs
*.log

View File

@@ -13,4 +13,6 @@ allowed:
- other
ignored:
npm: []
npm:
# Used by Rollup.js when building in GitHub Actions
- '@rollup/rollup-linux-x64-gnu'

View File

@@ -1,12 +1,12 @@
---
name: "@actions/core"
version: 3.0.0
version: 1.11.1
type: npm
summary: Actions core lib
homepage: https://github.com/actions/toolkit/tree/main/packages/core
license: mit
licenses:
- sources: LICENSE.md
- sources: LICENSE.md
text: |-
The MIT License (MIT)

View File

@@ -1,12 +1,12 @@
---
name: "@actions/exec"
version: 3.0.0
version: 1.1.1
type: npm
summary: Actions exec lib
homepage: https://github.com/actions/toolkit/tree/main/packages/exec
license: mit
licenses:
- sources: LICENSE.md
- sources: LICENSE.md
text: |-
The MIT License (MIT)

View File

@@ -1,12 +1,12 @@
---
name: "@actions/http-client"
version: 4.0.0
version: 2.2.3
type: npm
summary: Actions Http Client
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
license: other
licenses:
- sources: LICENSE
- sources: LICENSE
text: |
Actions Http Client for Node.js

View File

@@ -1,12 +1,12 @@
---
name: "@actions/io"
version: 3.0.2
version: 1.1.3
type: npm
summary: Actions io lib
homepage: https://github.com/actions/toolkit/tree/main/packages/io
license: mit
licenses:
- sources: LICENSE.md
- sources: LICENSE.md
text: |-
The MIT License (MIT)

View File

@@ -1,12 +1,12 @@
---
name: "@actions/tool-cache"
version: 4.0.0
version: 2.0.2
type: npm
summary: Actions tool-cache lib
homepage: https://github.com/actions/toolkit/tree/main/packages/tool-cache
license: mit
licenses:
- sources: LICENSE.md
- sources: LICENSE.md
text: |-
The MIT License (MIT)

View File

@@ -0,0 +1,30 @@
---
name: "@fastify/busboy"
version: 2.1.1
type: npm
summary: A streaming parser for HTML form data for node.js
homepage:
license: mit
licenses:
- sources: LICENSE
text: |-
Copyright Brian White. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
notices: []

View File

@@ -0,0 +1,26 @@
---
name: semver
version: 6.3.1
type: npm
summary: The semantic version parser used by npm.
homepage:
license: isc
licenses:
- sources: LICENSE
text: |
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
notices: []

View File

@@ -0,0 +1,26 @@
---
name: semver
version: 7.7.2
type: npm
summary: The semantic version parser used by npm.
homepage:
license: isc
licenses:
- sources: LICENSE
text: |
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
notices: []

View File

@@ -1,26 +0,0 @@
---
name: semver
version: 7.7.4
type: npm
summary: The semantic version parser used by npm.
homepage:
license: isc
licenses:
- sources: LICENSE
text: |
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
notices: []

View File

@@ -6,7 +6,7 @@ summary: Node HTTP/HTTPS Agents for tunneling proxies
homepage: https://github.com/koichik/node-tunnel/
license: mit
licenses:
- sources: LICENSE
- sources: LICENSE
text: |
The MIT License (MIT)
@@ -29,7 +29,7 @@ licenses:
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
- sources: README.md
- sources: README.md
text: Licensed under the [MIT](https://github.com/koichik/node-tunnel/blob/master/LICENSE)
license.
notices: []

View File

@@ -1,12 +1,12 @@
---
name: undici
version: 6.24.1
version: 5.29.0
type: npm
summary: An HTTP/1.1 client, written from scratch for Node.js
homepage: https://undici.nodejs.org
license: mit
licenses:
- sources: LICENSE
- sources: LICENSE
text: |
MIT License
@@ -29,6 +29,6 @@ licenses:
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- sources: README.md
- sources: README.md
text: MIT
notices: []

24
.markdown-lint.yml Normal file
View File

@@ -0,0 +1,24 @@
# See: https://github.com/DavidAnson/markdownlint
# Unordered list style
MD004:
style: dash
# Disable line length for tables
MD013:
tables: false
# Ordered list item prefix
MD029:
style: one
# Spaces after list markers
MD030:
ul_single: 1
ol_single: 1
ul_multi: 1
ol_multi: 1
# Code block style
MD046:
style: fenced

1
.node-version Normal file
View File

@@ -0,0 +1 @@
20.19.4

5
.prettierignore Normal file
View File

@@ -0,0 +1,5 @@
.DS_Store
.licenses/
dist/
node_modules/
coverage/

16
.prettierrc.yml Normal file
View File

@@ -0,0 +1,16 @@
# See: https://prettier.io/docs/en/configuration
printWidth: 80
tabWidth: 2
useTabs: false
semi: false
singleQuote: true
quoteProps: as-needed
jsxSingleQuote: false
trailingComma: none
bracketSpacing: true
bracketSameLine: true
arrowParens: always
proseWrap: always
htmlWhitespaceSensitivity: css
endOfLine: lf

14
.yaml-lint.yml Normal file
View File

@@ -0,0 +1,14 @@
# See: https://yamllint.readthedocs.io/en/stable/
rules:
document-end: disable
document-start:
level: warning
present: false
line-length:
level: warning
max: 80
allow-non-breakable-words: true
allow-non-breakable-inline-mappings: true
ignore:
- .licenses/

1
CODEOWNERS Normal file
View File

@@ -0,0 +1 @@
* @sweatybridge

114
README.md
View File

@@ -1,12 +1,13 @@
# :gear: Supabase CLI Action
[![CI](https://github.com/supabase/setup-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/supabase/setup-cli/actions/workflows/ci.yml)
[![E2E](https://github.com/supabase/setup-cli/actions/workflows/e2e.yml/badge.svg)](https://github.com/supabase/setup-cli/actions/workflows/e2e.yml)
[![CI](https://github.com/supabase/setup-cli/actions/workflows/start.yml/badge.svg)](https://github.com/supabase/setup-cli/actions/workflows/start.yml)
[![Linter](https://github.com/supabase/setup-cli/actions/workflows/linter.yml/badge.svg)](https://github.com/supabase/setup-cli/actions/workflows/linter.yml)
[![CodeQL](https://github.com/supabase/setup-cli/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/supabase/setup-cli/actions/workflows/codeql-analysis.yml)
[![Coverage](./badges/coverage.svg)](https://github.com/supabase/setup-cli/actions/workflows/test.yml)
## About
This composite action sets up the Supabase CLI,
This action sets up the Supabase CLI,
[`supabase`](https://github.com/supabase/cli), on GitHub's hosted Actions
runners. Other CI runners like
[Bitbucket](https://bitbucket.org/supabase-cli/setup-cli/src/master/bitbucket-pipelines.yml)
@@ -24,33 +25,28 @@ Setup the `supabase` CLI:
```yaml
steps:
- uses: supabase/setup-cli@v2
- uses: supabase/setup-cli@v1
```
If `version` is omitted, the action checks the repository root for `bun.lock`,
`pnpm-lock.yaml`, or `package-lock.json` and installs the declared `supabase`
package version through npm. If the lockfile includes package integrity
metadata, the action verifies it against the npm registry before installing. If
no supported lockfile is present, it falls back to `latest`.
A specific version of the `supabase` CLI can be installed:
```yaml
steps:
- uses: supabase/setup-cli@v2
- uses: supabase/setup-cli@v1
with:
version: 2.84.2
version: 2.33.0
```
Run `supabase db start` to execute all migrations on a fresh database:
```yaml
steps:
- uses: supabase/setup-cli@v2
- uses: supabase/setup-cli@v1
with:
version: latest
- run: supabase init
- run: supabase db start
# Optionally connect to the local database using psql
- run: psql 'postgres://postgres:postgres@localhost:54322/postgres' -c ''
```
Since Supabase CLI relies on Docker Engine API, additional setup may be required
@@ -58,12 +54,11 @@ on Windows and macOS runners.
## Inputs
The action supports the following inputs:
The actions supports the following inputs:
| Name | Type | Description | Default | Required |
| -------------- | ------ | ---------------------------------------------------------------- | --------------------------------- | -------- |
| `version` | String | Supabase CLI version (or `latest`) | Root lockfile version or `latest` | false |
| `github-token` | String | Deprecated; no longer used now that installs resolve through npm | | false |
| --------- | ------ | ---------------------------------- | -------- | -------- |
| `version` | String | Supabase CLI version (or `latest`) | `2.33.0` | false |
## Advanced Usage
@@ -71,12 +66,11 @@ Check generated TypeScript types are up-to-date with Postgres schema:
```yaml
steps:
- uses: supabase/setup-cli@v2
- run: supabase init
- uses: supabase/setup-cli@v1
- run: supabase db start
- name: Verify generated types match Postgres schema
run: |
supabase gen types typescript --local > schema.gen.ts
supabase gen types --local > schema.gen.ts
if ! git diff --ignore-space-at-eol --exit-code --quiet schema.gen.ts; then
echo "Detected uncommitted changes after build. See status below:"
git diff
@@ -89,66 +83,64 @@ Release job to push schema changes to a Supabase project:
```yaml
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
# Optionally set the postgres password for linking project database
SUPABASE_DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
# Retrieve <project-id> from dashboard url: https://app.supabase.com/project/<project-id>
PROJECT_ID: <project-id>
steps:
- uses: supabase/setup-cli@v2
steps:
- uses: supabase/setup-cli@v1
- run: supabase link --project-ref $PROJECT_ID
- run: supabase db push
```
Export local Supabase env vars for app tests:
```yaml
steps:
- uses: supabase/setup-cli@v2
- run: supabase init
- run: supabase start
- name: Export local Supabase env vars
run: |
# Customize the variable names as needed for your app.
supabase status -o env \
--override-name api.url=SUPABASE_URL \
--override-name auth.service_role_key=SUPABASE_SERVICE_ROLE_KEY \
>> .env.test
- run: bun test
```
## Develop
After you've cloned the repository to your local machine or codespace, you'll
need to perform a few setup steps before you can work on the action.
need to perform some initial setup steps before you can develop your action.
> [!NOTE]
>
> You'll need a recent version of [Bun](https://bun.sh) for local development.
> This repository includes a `.bun-version` file for tools that can auto-switch
> Bun versions.
> You'll need to have a reasonably modern version of
> [Node.js](https://nodejs.org) handy (20.x or later should work!). If you are
> using a version manager like [`nodenv`](https://github.com/nodenv/nodenv) or
> [`fnm`](https://github.com/Schniz/fnm), this template has a `.node-version`
> file at the root of the repository that can be used to automatically switch to
> the correct version when you `cd` into the repository. Additionally, this
> `.node-version` file is used by GitHub Actions in any `actions/setup-node`
> actions.
1. :hammer_and_wrench: Install the dependencies
```bash
bun install
npm ci
```
1. :building_construction: Package the TypeScript for distribution
```bash
npm run bundle
```
1. :white_check_mark: Run the tests
```bash
bun test
$ npm test
PASS ./index.test.js
✓ gets download url to binary (3 ms)
✓ runs main action (891 ms)
...
```
1. :mag: Run the full local CI suite
## Publish to a distribution branch
```bash
bun run ci
```
## Publish
Actions are run from this GitHub repository so we will checkin the packed `dist`
folder.
1. Create a new GitHub release
2. Rebase `v2` branch on `main`
2. Rebase `v1` branch on `main`
Your action is now published! :rocket:
@@ -157,18 +149,14 @@ See the
## Validate
Validate changes by exercising the action from a workflow in this repository
(see [ci.yml](.github/workflows/ci.yml) and [e2e.yml](.github/workflows/e2e.yml)).
You can now validate the action by referencing `./` in a workflow in your
repository (see [test.yml](.github/workflows/test.yml))
```yaml
steps:
- uses: ./
with:
uses: ./
with:
version: latest
github-token: ${{ github.token }}
```
The CI workflow provides fast smoke coverage across GitHub-hosted runners, and
the E2E workflow verifies `supabase init` and `supabase start` against supported
Postgres versions. See the [actions tab](https://github.com/supabase/setup-cli/actions)
for recent runs.
See the [actions tab](https://github.com/supabase/setup-cli/actions) for runs of
this action! :rocket:

61
__tests__/main.test.ts Normal file
View File

@@ -0,0 +1,61 @@
import { getDownloadUrl } from '../src/utils'
import { CLI_CONFIG_REGISTRY } from '../src/main'
import * as os from 'os'
import * as process from 'process'
import * as cp from 'child_process'
import * as path from 'path'
import * as fs from 'fs'
import * as yaml from 'js-yaml'
import * as url from 'url'
import { expect, test } from '@jest/globals'
test('gets download url to binary', async () => {
const url = await getDownloadUrl('1.28.0')
expect(
url.startsWith(
'https://github.com/supabase/cli/releases/download/v1.28.0/supabase_'
)
).toBeTruthy()
expect(url.endsWith('.tar.gz')).toBeTruthy()
expect(url).not.toContain('_1.28.0_')
})
test('gets legacy download url to binary', async () => {
const url = await getDownloadUrl('0.1.0')
expect(
url.startsWith(
`https://github.com/supabase/cli/releases/download/v0.1.0/supabase_0.1.0_`
)
).toBeTruthy()
expect(url.endsWith('.tar.gz')).toBeTruthy()
})
test('gets download url to latest version', async () => {
const url = await getDownloadUrl('latest')
expect(url).toMatch(
'https://github.com/supabase/cli/releases/latest/download/'
)
})
// shows how the runner will run a javascript action with env / stdout protocol
test('runs main action', () => {
const { env, execPath } = process
const repo = path.dirname(path.dirname(url.fileURLToPath(import.meta.url)))
const config = path.join(repo, 'action.yml')
const action = yaml.load(fs.readFileSync(config, 'utf8')) as {
inputs: { version: { default: string } }
}
const ip = path.join(repo, 'dist', 'index.js')
const stdout = cp
.execFileSync(execPath, [ip], {
env: {
...env,
RUNNER_TEMP: os.tmpdir(),
INPUT_VERSION: action.inputs.version.default
}
})
.toString()
expect
.stringContaining(`::set-env name=${CLI_CONFIG_REGISTRY}::`)
.asymmetricMatch(stdout)
})

View File

@@ -3,113 +3,12 @@ description: Setup Supabase CLI, supabase, on GitHub Actions runners
author: Supabase
inputs:
version:
description: Version of Supabase CLI to install. If omitted, detect from the root lockfile and otherwise use latest.
required: false
github-token:
description: Deprecated. The action now installs through npm and does not use GitHub release API requests.
description: Version of Supabase CLI to install
required: false
default: 2.33.0
outputs:
version:
description: Version of installed Supabase CLI
value: ${{ steps.setup-cli.outputs.version }}
runs:
using: composite
steps:
- id: bun-download
name: Resolve Bun Download URL
shell: sh
working-directory: ${{ github.action_path }}
run: |
set -eu
if [ "${RUNNER_OS}" != "Linux" ]; then
exit 0
fi
# setup-bun does not detect Linux musl yet, so Alpine-like containers need the musl asset explicitly.
is_musl=false
if [ -f /etc/alpine-release ]; then
is_musl=true
elif command -v ldd >/dev/null 2>&1 && ldd --version 2>&1 | grep -qi musl; then
is_musl=true
fi
if [ "${is_musl}" != "true" ]; then
exit 0
fi
version="$(cat .bun-version)"
case "$(uname -m)" in
x86_64) arch="x64" ;;
aarch64|arm64) arch="aarch64" ;;
*)
echo "Unsupported Linux musl architecture: $(uname -m)" >&2
exit 1
;;
esac
echo "url=https://github.com/oven-sh/bun/releases/download/bun-v${version}/bun-linux-${arch}-musl.zip" >> "$GITHUB_OUTPUT"
- name: Install Alpine Runtime Dependencies
shell: sh
run: |
set -eu
if [ "${RUNNER_OS}" != "Linux" ]; then
exit 0
fi
is_musl=false
if [ -f /etc/alpine-release ]; then
is_musl=true
elif command -v ldd >/dev/null 2>&1 && ldd --version 2>&1 | grep -qi musl; then
is_musl=true
fi
if [ "${is_musl}" != "true" ]; then
exit 0
fi
# Bun's musl binary and the Supabase CLI shim both dynamically link libstdc++ and libgcc.
if command -v apk >/dev/null 2>&1; then
missing_packages=""
for package in libstdc++ libgcc; do
if ! apk info -e "${package}" >/dev/null 2>&1; then
missing_packages="${missing_packages} ${package}"
fi
done
if [ -z "${missing_packages}" ]; then
exit 0
fi
if [ "$(id -u)" != "0" ]; then
echo "::error::Alpine/musl containers need${missing_packages} to run Supabase CLI. Add 'apk add --no-cache${missing_packages}' before supabase/setup-cli, or run this job container as root."
exit 1
fi
apk add --no-cache ${missing_packages}
exit 0
fi
echo "::error::Linux musl containers need libstdc++ and libgcc to run Supabase CLI. Install them before supabase/setup-cli."
exit 1
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: ${{ github.action_path }}/.bun-version
bun-download-url: ${{ steps.bun-download.outputs.url }}
- name: Install Action Dependencies
shell: sh
working-directory: ${{ github.action_path }}
run: bun install --frozen-lockfile --production
- id: setup-cli
name: Setup Supabase CLI
shell: sh
working-directory: ${{ github.action_path }}
env:
INPUT_VERSION: ${{ inputs.version }}
run: bun src/main.ts
using: node20
main: dist/index.js

1
badges/coverage.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20" role="img" aria-label="Coverage: 51.42%"><title>Coverage: 51.42%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#e05d44"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">51.42%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">51.42%</text></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

155
bun.lock
View File

@@ -1,155 +0,0 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "setup-cli",
"dependencies": {
"@actions/core": "^3.0.1",
},
"devDependencies": {
"@tsconfig/bun": "^1.0.10",
"@types/bun": "^1.3.14",
"@typescript/native-preview": "^7.0.0-dev.20260410.1",
"oxfmt": "^0.49.0",
"oxlint": "^1.64.0",
"oxlint-tsgolint": "^0.22.1",
},
},
},
"packages": {
"@actions/core": ["@actions/core@3.0.1", "", { "dependencies": { "@actions/exec": "^3.0.0", "@actions/http-client": "^4.0.0" } }, "sha512-a6d/Nwahm9fliVGRhdhofo40HjHQasUPusmc7vBfyky+7Z+P2A1J68zyFVaNcEclc/Se+eO595oAr5nwEIoIUA=="],
"@actions/exec": ["@actions/exec@3.0.0", "", { "dependencies": { "@actions/io": "^3.0.2" } }, "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw=="],
"@actions/http-client": ["@actions/http-client@4.0.0", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^6.23.0" } }, "sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g=="],
"@actions/io": ["@actions/io@3.0.2", "", {}, "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw=="],
"@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.49.0", "", { "os": "android", "cpu": "arm" }, "sha512-HbifJ84prIh9+55CTPAU35JdRQrwg47y16cGerCC+iejSKOuHXYo2WDql6l7cQlzrYVtc3f4UWY+dBj2lRmOeA=="],
"@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.49.0", "", { "os": "android", "cpu": "arm64" }, "sha512-Ef7SKJqAaH2d7E6eXZZa2OffIShbhFMxnGK0zd93p4qiyTJr75B0qf7lrPD+qQOwcf04BrjYJ0JUxq8d5+yZwg=="],
"@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.49.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8x5DN9CsFfb432sHa9NyqX5XisGUdA53LPEGSdv/VniS+v4uEOR8Orv7A9QSB98Xxgp0t6r31DzQA/wpIobGqQ=="],
"@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.49.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-e0+DSVzk4ewhMVKNYDaRTmP81jNMBWR1X9al0cVKWS+hDM/dElNqD5zjTOCuLOZc4oOdp2Gx2ldrVL+yYo9TZQ=="],
"@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.49.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-W+mjtYtrQvFbXT/uNT+221OBhGRZ8UqNsLxjTWsjZ4GsQnRdvRC/N2NCK86BcamWr7lsTxwpwN3PULnr78sgcQ=="],
"@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.49.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Rtv6UevV7czDlLqil+NZUe4d8gs8jQo/zScSpumwyf7I+fSdLc+hc8AF3MQC7ymxSMMD9+vfiqQlsIf7wOAzXA=="],
"@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.49.0", "", { "os": "linux", "cpu": "arm" }, "sha512-sBi+8C/Q/MdKa5FL8ibAUCdhFBGFH7HFN/Qoyd5xQbZ/0ky3NMPpKfIBpaH0lhK2dXkGLczVQUoZ+xuNSerCdQ=="],
"@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.49.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-JIfWenFhlzx+O8YygyZhoHFzTsdgDhxhbDRnE2iJLnnM5pWKScFvPECO2vOlA7JqJ/9S1g3uzEKuRCkHFwTjvA=="],
"@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.49.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-iNzkMPG18jPkwBOZ4/HEjwqfzAjq4RrUQ0CgId/fC1ENvYD5jLVAaU/gWgpiqP1ys07kxSsSggDd1fp3E7mQHw=="],
"@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.49.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-BPHA/NN3LvoIXiid+iz3BHt5V0Rzx0tXAqRUovwE1NsbDaLG9e8mtv7evDGRIkVQacqTDBv0XL25THHsxSJosQ=="],
"@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.49.0", "", { "os": "linux", "cpu": "none" }, "sha512-3Eroshe+s69htC9JIL0+zLGQczLtRKezkMhwqQC21VC5Z/fuLvzLfbAOLgJLUq601H8gDYjy7deYycfOBjCvWg=="],
"@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.49.0", "", { "os": "linux", "cpu": "none" }, "sha512-fnaERGgsxGm0lKAmO72EYR4BA3qBnzBTJBTi6EtUMq1D4R7EexRBMU4voXnx4TXla3SEDl9x4uNp/18SbkPjGg=="],
"@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.49.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-rBwasMl1Uul1MCCeTGEFKnOTL7VUxHf+634jWStrQAbzpBJgd5Yz5m4F7exVCsoI8PHn57dNjssXagXLCLB5yA=="],
"@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.49.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BoC/F9xHe2y/deuBGA5Aw7bes07OD2gcL2wlpzTrfImR92vPP7S/k3LBTyspQZCNIVNdagkELcqKELwMLGIfAg=="],
"@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.49.0", "", { "os": "linux", "cpu": "x64" }, "sha512-umY6jFADAo/oztFKl8D/S6vSrG6oBpEskcentiRuz42kZVU2kfDXMWCYavxyZR2bwPjqkHpcHZ6EZFiH3Qj9ZA=="],
"@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.49.0", "", { "os": "none", "cpu": "arm64" }, "sha512-J85zQMiw2pXiGPK+OusmDvSnJ/dgpgN7VgmB2zOBtgS8F+nsOUfSg9ZEBrwbQscjZ7tkPbm38CG4VF5f53MsiA=="],
"@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.49.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-38K67XR++CoFFORDd4sMFwUVAnD6msYBdGTei+qvKGrRPO6S2PbrYPNL/eQQ1RgnnxOegNba0YQwg6uRkNcw6A=="],
"@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.49.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-rXVe0HICwQF0dBgbQtBCoYf8x/SidPIdhyQl+iPuJlV7suV+qDv7yUEB3wQ4qC3nOeNxz287SwFXKzyr0kWgEg=="],
"@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.49.0", "", { "os": "win32", "cpu": "x64" }, "sha512-gwWLwSEmBBfIK/Wh7GGd658161o4RKAvHWRaRQbJm571iQXGKfyr7UKsI1vsWvDlNLc30CxJDc8mMmCvJ/kczQ=="],
"@oxlint-tsgolint/darwin-arm64": ["@oxlint-tsgolint/darwin-arm64@0.22.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4150Lpgc1YM09GcjA6GSrra1JoPjC7aOpfywLjWEY4vW0Sd1qKzqHF1WRaiw0/qUZ40OATYdv3aRd7ipPkWQbw=="],
"@oxlint-tsgolint/darwin-x64": ["@oxlint-tsgolint/darwin-x64@0.22.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-vFWcPWYOgZs4HWcgS1EjUZg33NLcNfEYU49KGImmCfZWkflENrmBYV4HN/C0YeAPum6ZZ/goPSvQrB/cOD+NfA=="],
"@oxlint-tsgolint/linux-arm64": ["@oxlint-tsgolint/linux-arm64@0.22.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-6LiUpP0Zir3+29FvBm7Y28q/dBjSHqTZ5MhG1Ckw4fGhI4cAvbcwXaKvbjx1TP7rRmBNOoq/M5xdpHjTb+GAew=="],
"@oxlint-tsgolint/linux-x64": ["@oxlint-tsgolint/linux-x64@0.22.1", "", { "os": "linux", "cpu": "x64" }, "sha512-fuX1hEQfpHauUbXADsfqVhRzrUrGabzGXbj5wsp2vKhV5uk/Rze8Mba9GdjFGECzvXudMGqHqxB4r6jGRdhxVA=="],
"@oxlint-tsgolint/win32-arm64": ["@oxlint-tsgolint/win32-arm64@0.22.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SZidAj+jrbZf9ZjBEYW0tiNZ+KasqB2zgW26qdiPpQSF/DzURnPmXz651IeA9YsmbVdHGIooEHUmev6QJdquA=="],
"@oxlint-tsgolint/win32-x64": ["@oxlint-tsgolint/win32-x64@0.22.1", "", { "os": "win32", "cpu": "x64" }, "sha512-QweSk9H5lFh5Y+WUf2Kq/OAN88V6+62ZwGhP38gqdRotI90luXSMkruFTj7Q2rYrzH4ZVNaSqx7NY8JpSfIzqg=="],
"@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.64.0", "", { "os": "android", "cpu": "arm" }, "sha512-2r6Nq3XXGLHEXKkSj8JtmJ6N4gDw431DPFOg0ZoJHlNjnG6HVMm/ksQ10m0HJ8WBvwgMe1L50UHPaYZutCRPCw=="],
"@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.64.0", "", { "os": "android", "cpu": "arm64" }, "sha512-ePJMpePgg7fBv+L/hVx1xXRU5/5gd5m0obLA6hPEfLXF3GjpR8idIDbY1dhQYhyz1ms2wdTccSboo6KEd2Oxtg=="],
"@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.64.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-U4DMLQd10gJLuoSTLSGbfv3bGjTlUNsScm9Dgb8wwBqmCzidf1pE1pXV4doGNxqwH3KtVng1AGTINA0NvkGLvQ=="],
"@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.64.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-GoRIL48QWm4/TAvjN8pB1nAG+1/uqc9EdnWT9zqHeb6wsmjZtywj8VRe5aGW47Fdb64YtLOsdLqVxOvQuz98Wg=="],
"@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.64.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5dFkv4tkg7PxJJGS9/OjrJwjhuHczrd3OQOkRE0wHcLM+ncUnULtzEPWjqGOxTXxZnLWcB91bGiIznx89TVXyQ=="],
"@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.64.0", "", { "os": "linux", "cpu": "arm" }, "sha512-jsBqMLl/uOL5+Kq/+BtK9FrmiNGUbx8SiyZXv+WlUxA45KuwcLu9BfiSIL3I3DBDgWM3yZizDITnTK9BcqNBQg=="],
"@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.64.0", "", { "os": "linux", "cpu": "arm" }, "sha512-1lrj8At/Uuc9GhjrVFBQo0NEjfBrTkzpmtHIGAhNnIXqn1CAyGL+qrztUsXb2GIluJrpl9Q7qRLJOb/NqydacQ=="],
"@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.64.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-HpSQbubwh03mMhAdy2BYtad/fsY8vDFHDAb6bUwuCYg2VD3xCQgn6ArKcO0oZyLCheacKTv4PrF3Mfu5hgoE2g=="],
"@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.64.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-00QQ0h0Y7u0G69BgiH3+ky2aaq/QvkDL6DYok8htIuJHxybiux5aQ8jwmg8qIk9wha6UagUP2BAwAzbemcJbpg=="],
"@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.64.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-2GaimTV6EMW+s5HS0An3oGbQme3BgHswvfVdGk3EB57Xe9+/gyT+Qd7lNVzb3rtir52vbIPzXfaYArzs5b5zcw=="],
"@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.64.0", "", { "os": "linux", "cpu": "none" }, "sha512-H46AtFb9wypjoVwGdlxrm0DsD809NGmtiK9HiyPKTxkSte2YjhC4S+00rOIrwCaxcyPiGid3Y3OMXp5KMAkGZw=="],
"@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.64.0", "", { "os": "linux", "cpu": "none" }, "sha512-HEgsidjjvvyzdg82icYkuFCf7REDV7B9JFwbIMbVwrKLBY0MrXX+bku3POn/hduZ2yW91IyVDUMq0Bf02KwXQw=="],
"@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.64.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-Axvm8qryotmKN00P5w4JapaSjvP2LOSbdbBJiX+2SuHd3QzhW7TUc8skqgw+ahQZ5DmzEYeHCqauvW8f32Ns6Q=="],
"@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.64.0", "", { "os": "linux", "cpu": "x64" }, "sha512-cR60vSd7+m+KRZ3GQGfDxWwahW5RMXg0qlGvAluZr0fTUYvw0H9N9AXAF/M/PMqgytyqvVNmBAkJG9l7U30Y1g=="],
"@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.64.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2u/aPZ9pEg7HnvZPDsHxUGNnrpr4qaHi+mCgLgpt+LYRzPrS4Px4wPfkIdRdr2GvKnaYyt+XSlto0Vm5sbStTg=="],
"@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.64.0", "", { "os": "none", "cpu": "arm64" }, "sha512-kfhkGfCdoXLSxEkrhDlJrvBYajGmq+ma4EMc53dsOWTq+rIBOlI0vTBmpZNnM5oH2LY/K/w1HAK+UQEgjgpVUg=="],
"@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.64.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-r/cNKBFieONoVu2bb1KkVouq9W+edDUgHumXJGphCRRj+U0xaD4nanrw8ZOqo0IsutPkEM4vCcGBpak6x5aXMg=="],
"@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.64.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-tUw0xUUwEFVZbpJoeCblkv8SJA4Xz3CdXCJbAnBsiNLyxDrk2tLcxEAS6M73Q7hHHDg3OtwI8vZVK3t5RJt4Gw=="],
"@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.64.0", "", { "os": "win32", "cpu": "x64" }, "sha512-9CBR+LO0JVST87fNTzzNxS5I29jIUO5gxT9i9+M3SDHHALElj9sY1Prf12tad3vIRC6OD7Ehtvvh+sn13vSwHw=="],
"@tsconfig/bun": ["@tsconfig/bun@1.0.10", "", {}, "sha512-5AV5YknQjNyoYzZ/8NG0dawqew/wH+x7ANiCfCIn29qo0cdbd1EryvFD1k5NSZWLBMOI/fGqMIaxi58GPIP9Cg=="],
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
"@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260410.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260410.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260410.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260410.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260410.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260410.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260410.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260410.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-K3TIwBw4XGQM33wW8KUqRU7r6ZY1IqB8chk1u1kT+CDj4iu+eQ6jCXgU7EDxmpJ++gbNcIf8iBYgWgYNssrhZQ=="],
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260410.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bpLYm6woXd8BECzV9AQvPqISVeohpekK1qwpRopvNIxydhRQ4fEjZsS7EtDYpqHAW4/u1uEv07P9/iS6TAL1fQ=="],
"@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260410.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-V8bW8g5hgu+bAwGvTqF1kilkkoDgxhxi5egrdMUeWQkR+MIisoBQeaAupqMpLoSkqZsc/kKucM0zwBNC/KRU3Q=="],
"@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260410.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NO6Ci65ADadOCr2ycTxOyCgC5kyk+Ryjl8k5c78mz9sKDxYqwEtryFFjLqitAG+rejtJbnUq897WRICjAOwslA=="],
"@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260410.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MOluRRAhv46s9ScFmePa0InMHmpZ/z0Evc11RrTKsg+bN8BR7sWoAtFq6IujEDK9WVP7YmEYtBRgEfMLuqVojw=="],
"@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260410.1", "", { "os": "linux", "cpu": "x64" }, "sha512-IofIUrMGjXmZKDEMaRgshzOne0EQZtx9vE/6URHfgmDnWLDKWzz9eQ2qWmvsFD2vOBbgc6GwVWEq6XTHMEfx2A=="],
"@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260410.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-TXmE+wovQqRo+qAhaewB0MPB9esgayvSHr6vFlCpHykHqbDl3FUucuC4F8yU6zVOA3UqXTk4/GHeLsAvU7YEgQ=="],
"@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260410.1", "", { "os": "win32", "cpu": "x64" }, "sha512-dMFT4tdHBe2vVA2WPQMjorT+fzCURRtillevQzz8/bwCEz2uXSnpu4oLRLS5045ppGE0wCFELE+Hq5z2oRddDw=="],
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
"oxfmt": ["oxfmt@0.49.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.49.0", "@oxfmt/binding-android-arm64": "0.49.0", "@oxfmt/binding-darwin-arm64": "0.49.0", "@oxfmt/binding-darwin-x64": "0.49.0", "@oxfmt/binding-freebsd-x64": "0.49.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.49.0", "@oxfmt/binding-linux-arm-musleabihf": "0.49.0", "@oxfmt/binding-linux-arm64-gnu": "0.49.0", "@oxfmt/binding-linux-arm64-musl": "0.49.0", "@oxfmt/binding-linux-ppc64-gnu": "0.49.0", "@oxfmt/binding-linux-riscv64-gnu": "0.49.0", "@oxfmt/binding-linux-riscv64-musl": "0.49.0", "@oxfmt/binding-linux-s390x-gnu": "0.49.0", "@oxfmt/binding-linux-x64-gnu": "0.49.0", "@oxfmt/binding-linux-x64-musl": "0.49.0", "@oxfmt/binding-openharmony-arm64": "0.49.0", "@oxfmt/binding-win32-arm64-msvc": "0.49.0", "@oxfmt/binding-win32-ia32-msvc": "0.49.0", "@oxfmt/binding-win32-x64-msvc": "0.49.0" }, "peerDependencies": { "svelte": "^5.0.0" }, "optionalPeers": ["svelte"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-IAHFMdlJSWe+oAr65dx22UvjCtV9DBMisAuLnKpDqMQrctzCkGnj3QRwNHm0d+uwSWPalsDF8ZYLz9rh6nH2IQ=="],
"oxlint": ["oxlint@1.64.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.64.0", "@oxlint/binding-android-arm64": "1.64.0", "@oxlint/binding-darwin-arm64": "1.64.0", "@oxlint/binding-darwin-x64": "1.64.0", "@oxlint/binding-freebsd-x64": "1.64.0", "@oxlint/binding-linux-arm-gnueabihf": "1.64.0", "@oxlint/binding-linux-arm-musleabihf": "1.64.0", "@oxlint/binding-linux-arm64-gnu": "1.64.0", "@oxlint/binding-linux-arm64-musl": "1.64.0", "@oxlint/binding-linux-ppc64-gnu": "1.64.0", "@oxlint/binding-linux-riscv64-gnu": "1.64.0", "@oxlint/binding-linux-riscv64-musl": "1.64.0", "@oxlint/binding-linux-s390x-gnu": "1.64.0", "@oxlint/binding-linux-x64-gnu": "1.64.0", "@oxlint/binding-linux-x64-musl": "1.64.0", "@oxlint/binding-openharmony-arm64": "1.64.0", "@oxlint/binding-win32-arm64-msvc": "1.64.0", "@oxlint/binding-win32-ia32-msvc": "1.64.0", "@oxlint/binding-win32-x64-msvc": "1.64.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.22.1" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-Star3SNpWPeWFPw7kRXIhXUSn6fdiAl25q15CQzH/9WaOtG6e9CWTc25vNZOCr4PE1yEP1GtKJKIKglhj3OmEQ=="],
"oxlint-tsgolint": ["oxlint-tsgolint@0.22.1", "", { "optionalDependencies": { "@oxlint-tsgolint/darwin-arm64": "0.22.1", "@oxlint-tsgolint/darwin-x64": "0.22.1", "@oxlint-tsgolint/linux-arm64": "0.22.1", "@oxlint-tsgolint/linux-x64": "0.22.1", "@oxlint-tsgolint/win32-arm64": "0.22.1", "@oxlint-tsgolint/win32-x64": "0.22.1" }, "bin": { "tsgolint": "bin/tsgolint.js" } }, "sha512-YUSGSLUnoolsu8gxISEDio3q1rtsCozwfOzASUn3DT2mR2EeQ93uEEnen7s+6LpF+lyTQFln1pQfqwBh/fsVEg=="],
"tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="],
"tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="],
"undici": ["undici@6.24.1", "", {}, "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
}
}

32595
dist/index.js generated vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/index.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,43 +1,42 @@
# `supabase/setup-cli`
# `supabase-github-action`
The Supabase CLI Action provides an easy way to install the
[Supabase CLI](https://github.com/supabase/cli) on GitHub Actions runners.
The Supabase GitHub Action provides an easy way to use the Supabase CLI on
GitHub's hosted Actions runners.
The action supports `ubuntu-latest`, `windows-latest`, and `macos-latest`, and
adds the requested `supabase` version to `PATH` for the rest of the job.
The action can be run on `ubuntu-latest`, `windows-latest`, and `macos-latest`
GitHub Actions runners, and will install and expose a specified version of the
Supabase CLI on the runner environment.
If `version` is omitted, the action checks the repository root for `bun.lock`,
`pnpm-lock.yaml`, or `package-lock.json` and otherwise falls back to `latest`.
## Quick start
## Quick Start
This example shows how you can use the Supabase GitHub Action to test your
migrations on every Pull Request.
This example runs Supabase migrations on every pull request:
Inside your repository, create a new file inside the `.github/workflows` folder
called `test-migrations.yml`.
Copy this snippet inside the file, and the action will run whenever a new PR is
created:
```yaml
name: test-migrations
name: 'test-migrations'
on:
pull_request:
jobs:
test-migrations:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: supabase/setup-cli@v2
- uses: supabase/setup-cli@v1
with:
version: latest
- run: supabase init
- run: supabase db start
```
To pin a specific CLI version:
```yaml
- uses: supabase/setup-cli@v2
with:
version: 2.84.2
```
## Resources
- **Source Code**: <https://github.com/supabase/setup-cli>
- **CLI Documentation**: <https://supabase.com/docs/guides/cli>
- **Source Code**:
<a href="https://github.com/supabase/supabase-github-action" target="_blank">github.com/supabase/supabase-github-action</a>
- **CLI Documentation**:
<a href="https://supabase.com/docs/guides/cli" target="_blank">supabase.com/docs/guides/cli</a>

81
eslint.config.mjs Normal file
View File

@@ -0,0 +1,81 @@
// See: https://eslint.org/docs/latest/use/configure/configuration-files
import { fixupPluginRules } from '@eslint/compat'
import { FlatCompat } from '@eslint/eslintrc'
import js from '@eslint/js'
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import tsParser from '@typescript-eslint/parser'
import _import from 'eslint-plugin-import'
import jest from 'eslint-plugin-jest'
import prettier from 'eslint-plugin-prettier'
import globals from 'globals'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
})
export default [
{
ignores: ['**/coverage', '**/dist', '**/linter', '**/node_modules']
},
...compat.extends(
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:jest/recommended',
'plugin:prettier/recommended'
),
{
plugins: {
import: fixupPluginRules(_import),
jest,
prettier,
'@typescript-eslint': typescriptEslint
},
languageOptions: {
globals: {
...globals.node,
...globals.jest,
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
},
parser: tsParser,
ecmaVersion: 2023,
sourceType: 'module',
parserOptions: {
project: ['tsconfig.eslint.json'],
tsconfigRootDir: '.'
}
},
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: 'tsconfig.eslint.json'
}
}
},
rules: {
camelcase: 'off',
'eslint-comments/no-use': 'off',
'eslint-comments/no-unused-disable': 'off',
'i18n-text/no-en': 'off',
'import/no-namespace': 'off',
'no-console': 'off',
'no-shadow': 'off',
'no-unused-vars': 'off',
'prettier/prettier': 'error'
}
}
]

40
jest.config.js Normal file
View File

@@ -0,0 +1,40 @@
// See: https://jestjs.io/docs/configuration
/** @type {import('ts-jest').JestConfigWithTsJest} **/
export default {
clearMocks: true,
collectCoverage: true,
collectCoverageFrom: ['./src/**'],
coverageDirectory: './coverage',
coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
coverageReporters: ['json-summary', 'text', 'lcov'],
// Uncomment the below lines if you would like to enforce a coverage threshold
// for your action. This will fail the build if the coverage is below the
// specified thresholds.
// coverageThreshold: {
// global: {
// branches: 100,
// functions: 100,
// lines: 100,
// statements: 100
// }
// },
extensionsToTreatAsEsm: ['.ts'],
moduleFileExtensions: ['ts', 'js'],
preset: 'ts-jest',
reporters: ['default'],
resolver: 'ts-jest-resolver',
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testPathIgnorePatterns: ['/dist/', '/node_modules/'],
transform: {
'^.+\\.ts$': [
'ts-jest',
{
tsconfig: 'tsconfig.eslint.json',
useESM: true
}
]
},
verbose: true
}

13198
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,72 @@
{
"name": "setup-cli",
"version": "2.0.0",
"private": true,
"description": "Supabase CLI GitHub Action",
"keywords": [
"actions"
],
"license": "MIT",
"version": "1.6.0",
"author": "",
"type": "module",
"private": true,
"repository": {
"type": "git",
"url": "git+https://github.com/supabase/setup-cli.git"
},
"type": "module",
"scripts": {
"all": "bun run format && bun run lint && bun run coverage",
"ci": "bun run format:check && bun run lint && bun run coverage",
"coverage": "bun test --coverage --coverage-reporter=text --coverage-reporter=lcov",
"format": "bun x oxfmt --write . '!coverage/**'",
"format:check": "bun x oxfmt --check . '!coverage/**'",
"lint": "bun x oxlint --deny-warnings --type-aware --type-check --tsconfig tsconfig.json src",
"test": "bun test",
"typecheck": "bun x tsgo -p tsconfig.json --noEmit"
},
"dependencies": {
"@actions/core": "^3.0.1"
},
"devDependencies": {
"@tsconfig/bun": "^1.0.10",
"@types/bun": "^1.3.14",
"@typescript/native-preview": "^7.0.0-dev.20260410.1",
"oxfmt": "^0.49.0",
"oxlint": "^1.64.0",
"oxlint-tsgolint": "^0.22.1"
"keywords": [
"actions"
],
"exports": {
".": "./dist/index.js"
},
"engines": {
"bun": ">=1.3.10"
"node": ">=20"
},
"scripts": {
"bundle": "npm run format:write && npm run package",
"ci-test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest",
"coverage": "npx make-coverage-badge --output-path ./badges/coverage.svg",
"format:write": "npx prettier --write .",
"format:check": "npx prettier --check .",
"lint": "npx eslint .",
"local-action": "npx @github/local-action . src/main.ts .env",
"package": "npx rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
"package:watch": "npm run package -- --watch",
"test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest",
"all": "npm run format:write && npm run lint && npm run package && npm run test && npm run coverage"
},
"license": "MIT",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/tool-cache": "^2.0.2",
"semver": "^7.7.2"
},
"devDependencies": {
"@eslint/compat": "^1.3.1",
"@github/local-action": "^5.1.0",
"@jest/globals": "^30.0.5",
"@rollup/plugin-commonjs": "^28.0.6",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-typescript": "^12.1.4",
"@types/jest": "^30.0.0",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.19.9",
"@types/semver": "^7.7.0",
"@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0",
"eslint": "^9.31.0",
"eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-prettier": "^5.5.3",
"jest": "^30.0.5",
"js-yaml": "^4.1.0",
"make-coverage-badge": "^1.2.0",
"prettier": "^3.6.2",
"prettier-eslint": "^16.4.2",
"rollup": "^4.45.1",
"ts-jest": "^29.4.0",
"ts-jest-resolver": "^2.0.1",
"typescript": "^5.8.3"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "*"
}
}

18
rollup.config.ts Normal file
View File

@@ -0,0 +1,18 @@
// See: https://rollupjs.org/introduction/
import commonjs from '@rollup/plugin-commonjs'
import nodeResolve from '@rollup/plugin-node-resolve'
import typescript from '@rollup/plugin-typescript'
const config = {
input: 'src/index.ts',
output: {
esModule: true,
file: 'dist/index.js',
format: 'es',
sourcemap: true
},
plugins: [typescript(), nodeResolve({ preferBuiltins: true }), commonjs()]
}
export default config

8
src/index.ts Normal file
View File

@@ -0,0 +1,8 @@
/**
* The entrypoint for the action. This file simply imports and runs the action's
* main logic.
*/
import { run } from './main.js'
/* istanbul ignore next */
run()

View File

@@ -1,488 +0,0 @@
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { afterEach, expect, mock, spyOn, test } from "bun:test";
import * as core from "@actions/core";
const CLI_CONFIG_REGISTRY = "SUPABASE_INTERNAL_IMAGE_REGISTRY";
const originalPath = process.env.PATH;
const originalRunnerTemp = process.env.RUNNER_TEMP;
const originalWorkspace = process.env.GITHUB_WORKSPACE;
const tempDirs = new Set<string>();
let mainModule: typeof import("./main.ts") | null = null;
afterEach(() => {
mock.restore();
process.env.PATH = originalPath;
process.env.RUNNER_TEMP = originalRunnerTemp;
process.env.GITHUB_WORKSPACE = originalWorkspace;
delete process.env.FAKE_CLI_VERSION;
delete process.env.FAKE_NPM_INTEGRITY;
delete process.env.FAKE_NPM_LOG;
delete process.env.SUPABASE_SETUP_CLI_NPM;
for (const dir of tempDirs) {
rmSync(dir, { force: true, recursive: true });
}
tempDirs.clear();
});
function createTempDir(prefix: string): string {
const dir = mkdtempSync(path.join(os.tmpdir(), prefix));
tempDirs.add(dir);
return dir;
}
function createWorkspace(files: Record<string, string>): string {
const dir = createTempDir("setup-cli-workspace-");
for (const [relativePath, content] of Object.entries(files)) {
const filePath = path.join(dir, relativePath);
mkdirSync(path.dirname(filePath), { recursive: true });
writeFileSync(filePath, content);
}
return dir;
}
function createBunLock(
version: string,
options: {
includeDependency?: boolean;
includePackageEntry?: boolean;
integrity?: string;
useDevDependency?: boolean;
} = {},
): string {
const includeDependency = options.includeDependency ?? true;
const includePackageEntry = options.includePackageEntry ?? true;
const dependencyKey = options.useDevDependency ? "devDependencies" : "dependencies";
return `{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "app",
"${dependencyKey}": {
${includeDependency ? ` "supabase": "^${version}"` : ""}
}
}
},
"packages": {
${
includePackageEntry
? ` "supabase": [
"supabase@${version}",
"",
{},
"${options.integrity ?? "sha512-bun"}"
]`
: ""
}
}
}
`;
}
function createPnpmLock(
version: string,
options: {
asString?: boolean;
includeVersion?: boolean;
integrity?: string;
useDevDependency?: boolean;
} = {},
): string {
const dependencyKey = options.useDevDependency ? "devDependencies" : "dependencies";
return `lockfileVersion: "9.0"
importers:
.:
${dependencyKey}:
${
options.asString
? ` supabase: ${version}`
: ` supabase:
specifier: ^${version}
${options.includeVersion === false ? "" : ` version: ${version}`}`
}
packages:
supabase@${version}:
resolution:
integrity: ${options.integrity ?? "sha512-pnpm"}
`;
}
function createPackageLock(version: string, integrity = "sha512-package-lock"): string {
return JSON.stringify(
{
name: "app",
lockfileVersion: 3,
packages: {
"": {
dependencies: {
supabase: `^${version}`,
},
},
"node_modules/supabase": {
integrity,
version,
},
},
},
null,
2,
);
}
function createFakeNpm(): string {
const root = createTempDir("setup-cli-fake-npm-");
const binDir = path.join(root, "bin");
const scriptPath = path.join(root, "fake-npm.js");
mkdirSync(binDir, { recursive: true });
writeFileSync(
scriptPath,
`import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
import path from "node:path";
const args = process.argv.slice(2);
appendFileSync(process.env.FAKE_NPM_LOG, JSON.stringify(args) + "\\n");
if (args[0] === "view") {
console.log(JSON.stringify(process.env.FAKE_NPM_INTEGRITY ?? "sha512-test"));
process.exit(0);
}
if (args[0] !== "install") {
console.error("Unexpected npm command: " + args.join(" "));
process.exit(1);
}
const prefixIndex = args.indexOf("--prefix");
const prefix = prefixIndex === -1 ? undefined : args[prefixIndex + 1];
if (!prefix) {
console.error("Missing --prefix");
process.exit(1);
}
const binDir = path.join(prefix, "node_modules", ".bin");
mkdirSync(binDir, { recursive: true });
if (process.platform === "win32") {
writeFileSync(
path.join(binDir, "supabase.cmd"),
process.env.FAKE_CLI_VERSION ? "@echo off\\r\\necho " + process.env.FAKE_CLI_VERSION + "\\r\\n" : "@echo off\\r\\n",
);
} else {
writeFileSync(
path.join(binDir, "supabase"),
process.env.FAKE_CLI_VERSION
? "#!/usr/bin/env bash\\nprintf '%s\\\\n' '" + process.env.FAKE_CLI_VERSION.replaceAll("'", "'\\\\''") + "'\\n"
: "#!/usr/bin/env bash\\n",
{ mode: 0o755 },
);
}
`,
);
if (process.platform === "win32") {
writeFileSync(
path.join(binDir, "npm.cmd"),
`@echo off\r\n"${process.execPath}" "${scriptPath}" %*\r\n`,
);
} else {
writeFileSync(
path.join(binDir, "npm"),
`#!/usr/bin/env bash\nexec "${process.execPath}" "${scriptPath}" "$@"\n`,
{ mode: 0o755 },
);
}
return binDir;
}
function installFakeNpm(versionOutput = "supabase 2.101.0", integrity = "sha512-test"): string {
const binDir = createFakeNpm();
const logPath = path.join(createTempDir("setup-cli-fake-npm-log-"), "npm.log");
writeFileSync(logPath, "");
process.env.FAKE_CLI_VERSION = versionOutput;
process.env.FAKE_NPM_INTEGRITY = integrity;
process.env.FAKE_NPM_LOG = logPath;
process.env.PATH = `${binDir}${path.delimiter}${originalPath ?? ""}`;
process.env.RUNNER_TEMP = createTempDir("setup-cli-runner-temp-");
process.env.SUPABASE_SETUP_CLI_NPM = path.join(
binDir,
process.platform === "win32" ? "npm.cmd" : "npm",
);
return logPath;
}
function readNpmCalls(logPath: string): string[][] {
return readFileSync(logPath, "utf8")
.trim()
.split("\n")
.filter(Boolean)
.map((line) => JSON.parse(line) as string[]);
}
function createActionSpies(inputVersion: string) {
return {
addPath: spyOn(core, "addPath").mockImplementation(() => {}),
exportVariable: spyOn(core, "exportVariable").mockImplementation(() => {}),
getInput: spyOn(core, "getInput").mockReturnValue(inputVersion),
setFailed: spyOn(core, "setFailed").mockImplementation(() => {}),
setOutput: spyOn(core, "setOutput").mockImplementation(() => {}),
};
}
async function getMainModule(): Promise<typeof import("./main.ts")> {
if (!mainModule) {
mainModule = await import("./main.ts");
}
return mainModule;
}
test("uses an explicit npm package version when provided", async () => {
const { resolvePackage } = await getMainModule();
expect(resolvePackage("v2.101.0")).toEqual({
spec: "supabase@2.101.0",
version: "2.101.0",
});
});
test("uses the root bun.lock resolution when version is omitted", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"bun.lock": createBunLock("2.41.0", { integrity: "sha512-bun-lock" }),
});
const { resolvePackage } = await getMainModule();
expect(resolvePackage("")).toEqual({
integrity: "sha512-bun-lock",
spec: "supabase@2.41.0",
version: "2.41.0",
});
});
test("uses the root pnpm-lock.yaml resolution when version is omitted", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"pnpm-lock.yaml": createPnpmLock("2.42.0", { integrity: "sha512-pnpm-lock" }),
});
const { resolvePackage } = await getMainModule();
expect(resolvePackage("")).toEqual({
integrity: "sha512-pnpm-lock",
spec: "supabase@2.42.0",
version: "2.42.0",
});
});
test("uses the root package-lock.json resolution when version is omitted", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"package-lock.json": createPackageLock("2.43.0", "sha512-package-lock"),
});
const { resolvePackage } = await getMainModule();
expect(resolvePackage("")).toEqual({
integrity: "sha512-package-lock",
spec: "supabase@2.43.0",
version: "2.43.0",
});
});
test("falls through malformed lockfiles and uses the next supported root lockfile", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"bun.lock": "{ not valid",
"package-lock.json": createPackageLock("2.44.0"),
});
const { resolvePackage } = await getMainModule();
expect(resolvePackage("")).toEqual({
integrity: "sha512-package-lock",
spec: "supabase@2.44.0",
version: "2.44.0",
});
});
test("falls back to latest when version is omitted and no supported root lockfile is present", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"README.md": "# app\n",
});
const { resolvePackage } = await getMainModule();
expect(resolvePackage("")).toEqual({
spec: "supabase@latest",
version: "latest",
});
});
test("falls back to latest when version is omitted and no workspace is available", async () => {
delete process.env.GITHUB_WORKSPACE;
const { resolvePackage } = await getMainModule();
expect(resolvePackage("")).toEqual({
spec: "supabase@latest",
version: "latest",
});
});
test("uses the declared bun.lock version when the resolved package entry is missing", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"bun.lock": createBunLock("2.44.1", { includePackageEntry: false, useDevDependency: true }),
});
const { resolvePackage } = await getMainModule();
expect(resolvePackage("")).toEqual({
spec: "supabase@2.44.1",
version: "2.44.1",
});
});
test("falls through bun.lock without supabase and uses a pnpm string dependency version", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"bun.lock": createBunLock("2.47.0", { includeDependency: false }),
"pnpm-lock.yaml": createPnpmLock("2.47.0", { asString: true }),
});
const { resolvePackage } = await getMainModule();
expect(resolvePackage("")).toEqual({
integrity: "sha512-pnpm",
spec: "supabase@2.47.0",
version: "2.47.0",
});
});
test("falls back to latest when a pnpm dependency entry has no concrete version", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"pnpm-lock.yaml": createPnpmLock("2.49.0", { includeVersion: false }),
});
const { resolvePackage } = await getMainModule();
expect(resolvePackage("")).toEqual({
spec: "supabase@latest",
version: "latest",
});
});
test("installs the CLI with npm into an isolated prefix", async () => {
const logPath = installFakeNpm();
const { installCli } = await getMainModule();
const cliPath = await installCli({
spec: "supabase@2.101.0",
version: "2.101.0",
});
expect(cliPath).toContain(`${path.sep}node_modules${path.sep}.bin`);
expect(readNpmCalls(logPath)).toEqual([
[
"install",
"--prefix",
expect.any(String),
"--omit=dev",
"--no-audit",
"--no-fund",
"--no-package-lock",
"--ignore-scripts",
"supabase@2.101.0",
],
]);
});
test("verifies lockfile integrity before installing", async () => {
const logPath = installFakeNpm("supabase 2.101.0", "sha512-lock");
const { installCli } = await getMainModule();
await installCli({
integrity: "sha512-lock",
spec: "supabase@2.101.0",
version: "2.101.0",
});
expect(readNpmCalls(logPath)).toEqual([
["view", "supabase@2.101.0", "dist.integrity", "--json"],
[
"install",
"--prefix",
expect.any(String),
"--omit=dev",
"--no-audit",
"--no-fund",
"--no-package-lock",
"--ignore-scripts",
"supabase@2.101.0",
],
]);
});
test("fails when lockfile integrity does not match the registry", async () => {
installFakeNpm("supabase 2.101.0", "sha512-registry");
const { installCli } = await getMainModule();
try {
await installCli({
integrity: "sha512-lock",
spec: "supabase@2.101.0",
version: "2.101.0",
});
throw new Error("Expected installCli to reject");
} catch (error) {
expect(error).toEqual(
new Error("Lockfile integrity for supabase@2.101.0 does not match the npm registry"),
);
}
});
test("runs the action with a package-lock resolution", async () => {
const logPath = installFakeNpm("supabase 2.43.0", "sha512-package-lock");
process.env.GITHUB_WORKSPACE = createWorkspace({
"package-lock.json": createPackageLock("2.43.0", "sha512-package-lock"),
});
const spies = createActionSpies("");
const { run } = await getMainModule();
await run();
expect(readNpmCalls(logPath)[0]).toEqual(["view", "supabase@2.43.0", "dist.integrity", "--json"]);
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.43.0");
expect(spies.addPath).toHaveBeenCalledWith(expect.stringContaining("node_modules"));
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("explicit version overrides detected root lockfiles", async () => {
installFakeNpm("supabase 1.0.0");
process.env.GITHUB_WORKSPACE = createWorkspace({
"bun.lock": createBunLock("2.45.0"),
});
const spies = createActionSpies("1.0.0");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 1.0.0");
expect(spies.exportVariable).not.toHaveBeenCalled();
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("fails when the installed CLI does not report a version", async () => {
installFakeNpm("");
process.env.GITHUB_WORKSPACE = createWorkspace({
"package-lock.json": createPackageLock("2.46.0", "sha512-test"),
});
const spies = createActionSpies("");
const { run } = await getMainModule();
await run();
expect(spies.setFailed).toHaveBeenCalledWith(
"Could not determine installed Supabase CLI version",
);
expect(spies.setOutput).not.toHaveBeenCalled();
expect(spies.addPath).not.toHaveBeenCalled();
expect(spies.exportVariable).not.toHaveBeenCalled();
});

View File

@@ -1,330 +1,39 @@
import { semver } from "bun";
import * as core from "@actions/core";
import { existsSync, mkdtempSync, readFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import * as core from '@actions/core'
import * as tc from '@actions/tool-cache'
import { gte } from 'semver'
import { getDownloadUrl, determineInstalledVersion } from './utils.js'
export const CLI_CONFIG_REGISTRY = "SUPABASE_INTERNAL_IMAGE_REGISTRY";
const REGISTRY_VERSION = "1.28.0";
const DEFAULT_VERSION = "latest";
const NPM_PACKAGE = "supabase";
const NPM_EXECUTABLE_ENV = "SUPABASE_SETUP_CLI_NPM";
type PackageResolution = {
spec: string;
version: string;
integrity?: string;
};
type BunLock = {
workspaces?: {
"": {
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
};
};
packages?: Record<string, unknown>;
};
type PnpmDependency =
| string
| {
version?: string;
};
type PnpmPackage = {
resolution?: {
integrity?: string;
};
};
type PnpmLock = {
importers?: {
".": {
dependencies?: Record<string, PnpmDependency>;
devDependencies?: Record<string, PnpmDependency>;
};
};
packages?: Record<string, PnpmPackage>;
};
type PackageLock = {
packages?: Record<string, { integrity?: string; version?: string }>;
dependencies?: Record<string, { integrity?: string; version?: string }>;
};
function extractConcreteVersion(raw: string | undefined): string | null {
if (!raw) {
return null;
}
const match = raw.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?/);
return match?.[0] ?? null;
}
function normalizeVersion(version: string): string {
return version.replace(/^v/i, "");
}
function toPackageResolution(version: string, integrity?: string): PackageResolution {
const normalizedVersion = normalizeVersion(version);
return {
spec: `${NPM_PACKAGE}@${normalizedVersion}`,
version: normalizedVersion,
integrity,
};
}
function readWorkspaceLockfile(workspaceRoot: string, filename: string): string | null {
const filePath = path.join(workspaceRoot, filename);
if (!existsSync(filePath)) {
return null;
}
try {
return readFileSync(filePath, "utf8");
} catch {
return null;
}
}
function detectResolutionFromBunLock(workspaceRoot: string): PackageResolution | null {
const text = readWorkspaceLockfile(workspaceRoot, "bun.lock");
if (!text) {
return null;
}
try {
const lockfile = JSON.parse(text.replace(/,\s*([}\]])/g, "$1")) as BunLock;
const rootWorkspace = lockfile.workspaces?.[""];
const declaredVersion =
rootWorkspace?.dependencies?.[NPM_PACKAGE] ?? rootWorkspace?.devDependencies?.[NPM_PACKAGE];
if (!declaredVersion) {
return null;
}
const resolvedPackage = lockfile.packages?.[NPM_PACKAGE];
if (Array.isArray(resolvedPackage) && typeof resolvedPackage[0] === "string") {
const version = extractConcreteVersion(resolvedPackage[0]);
const integrity = typeof resolvedPackage[3] === "string" ? resolvedPackage[3] : undefined;
return version ? toPackageResolution(version, integrity) : null;
}
const version = extractConcreteVersion(declaredVersion);
return version ? toPackageResolution(version) : null;
} catch {
return null;
}
}
function detectResolutionFromPnpmLock(workspaceRoot: string): PackageResolution | null {
const text = readWorkspaceLockfile(workspaceRoot, "pnpm-lock.yaml");
if (!text) {
return null;
}
try {
const lockfile = Bun.YAML.parse(text) as PnpmLock;
const rootImporter = lockfile.importers?.["."];
const dependency =
rootImporter?.dependencies?.[NPM_PACKAGE] ?? rootImporter?.devDependencies?.[NPM_PACKAGE];
const version =
typeof dependency === "string"
? extractConcreteVersion(dependency)
: extractConcreteVersion(dependency?.version);
if (!version) {
return null;
}
const integrity = Object.entries(lockfile.packages ?? {}).find(
([packageKey]) =>
packageKey === `${NPM_PACKAGE}@${version}` ||
packageKey.startsWith(`/${NPM_PACKAGE}@${version}`),
)?.[1].resolution?.integrity;
return toPackageResolution(version, integrity);
} catch {
return null;
}
}
function detectResolutionFromPackageLock(workspaceRoot: string): PackageResolution | null {
const text = readWorkspaceLockfile(workspaceRoot, "package-lock.json");
if (!text) {
return null;
}
try {
const lockfile = JSON.parse(text) as PackageLock;
const packageEntry = lockfile.packages?.[`node_modules/${NPM_PACKAGE}`];
const dependencyEntry = lockfile.dependencies?.[NPM_PACKAGE];
const version =
extractConcreteVersion(packageEntry?.version) ??
extractConcreteVersion(dependencyEntry?.version);
return version
? toPackageResolution(version, packageEntry?.integrity ?? dependencyEntry?.integrity)
: null;
} catch {
return null;
}
}
export function resolvePackage(inputVersion: string): PackageResolution {
const requestedVersion = inputVersion.trim();
if (requestedVersion) {
return toPackageResolution(requestedVersion);
}
const workspaceRoot = process.env.GITHUB_WORKSPACE?.trim();
if (!workspaceRoot) {
return toPackageResolution(DEFAULT_VERSION);
}
return (
detectResolutionFromBunLock(workspaceRoot) ??
detectResolutionFromPnpmLock(workspaceRoot) ??
detectResolutionFromPackageLock(workspaceRoot) ??
toPackageResolution(DEFAULT_VERSION)
);
}
async function verifyExpectedIntegrity(resolution: PackageResolution): Promise<void> {
if (!resolution.integrity) {
return;
}
const output = await runNpm(["view", resolution.spec, "dist.integrity", "--json"]);
const registryIntegrity = JSON.parse(output) as unknown;
if (registryIntegrity !== resolution.integrity) {
throw new Error(`Lockfile integrity for ${resolution.spec} does not match the npm registry`);
}
}
function createInstallRoot(): string {
const tempRoot = process.env.RUNNER_TEMP?.trim() || os.tmpdir();
return mkdtempSync(path.join(tempRoot, "setup-cli-"));
}
async function runNpm(args: string[]): Promise<string> {
const executable = process.env[NPM_EXECUTABLE_ENV]?.trim() || "npm";
const proc = Bun.spawn([executable, ...args], {
env: process.env,
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
if (exitCode !== 0) {
throw new Error(stderr.trim() || `npm ${args.join(" ")} failed`);
}
return stdout;
}
export async function installCli(resolution: PackageResolution): Promise<string> {
await verifyExpectedIntegrity(resolution);
const installRoot = createInstallRoot();
await runNpm([
"install",
"--prefix",
installRoot,
"--omit=dev",
"--no-audit",
"--no-fund",
"--no-package-lock",
"--ignore-scripts",
resolution.spec,
]);
return path.join(installRoot, "node_modules", ".bin");
}
function getCliExecutablePath(cliPath: string): string {
if (process.platform !== "win32") {
return path.join(cliPath, "supabase");
}
const cmdPath = path.join(cliPath, "supabase.cmd");
if (existsSync(cmdPath)) {
return cmdPath;
}
const exePath = path.join(cliPath, "supabase.exe");
if (existsSync(exePath)) {
return exePath;
}
return path.join(cliPath, "supabase");
}
export async function determineInstalledVersion(cliPath: string): Promise<string> {
const executable = getCliExecutablePath(cliPath);
const proc = Bun.spawn([executable, "--version"], {
stderr: "pipe",
stdout: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
if (exitCode !== 0) {
throw new Error(stderr.trim() || "Could not determine installed Supabase CLI version");
}
const version = stdout.trim();
if (!version) {
throw new Error("Could not determine installed Supabase CLI version");
}
return version;
}
function shouldUseGhcrRegistry(requestedVersion: string, installedVersion: string): boolean {
if (requestedVersion.toLowerCase() === DEFAULT_VERSION) {
return true;
}
const concreteVersion = extractConcreteVersion(installedVersion);
return concreteVersion !== null && semver.order(concreteVersion, REGISTRY_VERSION) >= 0;
}
export const CLI_CONFIG_REGISTRY = 'SUPABASE_INTERNAL_IMAGE_REGISTRY'
/**
* The main function for the action.
*
* @returns Resolves when the action is complete.
*/
export async function run(): Promise<void> {
try {
const resolution = resolvePackage(core.getInput("version"));
const cliPath = await installCli(resolution);
const installedVersion = await determineInstalledVersion(cliPath);
core.setOutput("version", installedVersion);
core.addPath(cliPath);
// Get version of tool to be installed
const version = core.getInput('version')
if (shouldUseGhcrRegistry(resolution.version, installedVersion)) {
core.exportVariable(CLI_CONFIG_REGISTRY, "ghcr.io");
// Download the specific version of the tool, e.g. as a tarball/zipball
const download = await getDownloadUrl(version)
const pathToTarball = await tc.downloadTool(download)
// Extract the tarball/zipball onto host runner
const pathToCLI = await tc.extractTar(pathToTarball)
// Expose the tool by adding it to the PATH
core.addPath(pathToCLI)
// Expose installed tool version
const determinedVersion = await determineInstalledVersion()
core.setOutput('version', determinedVersion)
// Use GHCR mirror by default
if (version.toLowerCase() === 'latest' || gte(version, '1.28.0')) {
core.exportVariable(CLI_CONFIG_REGISTRY, 'ghcr.io')
}
} catch (error) {
core.setFailed(error instanceof Error ? error.message : String(error));
if (error instanceof Error) core.setFailed(error.message)
}
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
await run();
}

48
src/utils.ts Normal file
View File

@@ -0,0 +1,48 @@
import { exec } from 'child_process'
import os from 'os'
import { lt } from 'semver'
import { promisify } from 'util'
const doExec = promisify(exec)
// arch in [arm, arm64, x64...] (https://nodejs.org/docs/latest-v16.x/api/os.html#osarch)
// return value in [amd64, arm64, arm]
const mapArch = (arch: string): string => {
const mappings: Record<string, string> = {
x64: 'amd64'
}
return mappings[arch] || arch
}
// os in [darwin, linux, win32...] (https://nodejs.org/docs/latest-v16.x/api/os.html#osplatform)
// return value in [darwin, linux, windows]
const mapOS = (platform: string): string => {
const mappings: Record<string, string> = {
win32: 'windows'
}
return mappings[platform] || platform
}
export const getDownloadUrl = async (version: string): Promise<string> => {
const platform = mapOS(os.platform())
const arch = mapArch(os.arch())
const filename = `supabase_${platform}_${arch}.tar.gz`
if (version.toLowerCase() === 'latest') {
return `https://github.com/supabase/cli/releases/latest/download/${filename}`
}
if (lt(version, '1.28.0')) {
return `https://github.com/supabase/cli/releases/download/v${version}/supabase_${version}_${platform}_${arch}.tar.gz`
}
return `https://github.com/supabase/cli/releases/download/v${version}/${filename}`
}
export const determineInstalledVersion = async (): Promise<string> => {
const { stdout } = await doExec('supabase --version')
const version = stdout.trim()
if (!version) {
throw new Error('Could not determine installed Supabase CLI version')
}
return version
}

23
tsconfig.base.json Normal file
View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"declaration": false,
"declarationMap": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"newLine": "lf",
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": false,
"pretty": true,
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"target": "ES2022"
}
}

17
tsconfig.eslint.json Normal file
View File

@@ -0,0 +1,17 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"noEmit": true
},
"exclude": ["dist", "node_modules"],
"include": [
"__fixtures__",
"__tests__",
"src",
"eslint.config.mjs",
"jest.config.js",
"rollup.config.ts"
]
}

View File

@@ -1,4 +1,11 @@
{
"extends": "@tsconfig/bun/tsconfig.json",
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./tsconfig.base.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist"
},
"exclude": ["__fixtures__", "__tests__", "coverage", "dist", "node_modules"],
"include": ["src"]
}