chore: prepare for v2.0.0 (#405)

## Summary

This PR prepares `supabase/setup-cli` for `v2.0.0`.

The main goal of this release is to simplify the action and modernize
the repo/tooling around a Bun-based implementation, while tightening
workflows, tests, and documentation.

## What Changed

### Action runtime
- switched the action from a Node/compiled `dist` runtime to a Bun-based
composite action
- removed the checked-in `dist/` output entirely
- simplified the action source down to a single runtime file in
`src/main.ts`
- kept the public action interface the same:
  - `with.version`
  - `outputs.version`

### Tooling
- switched package management and local tooling from npm to Bun
- removed Rollup and the build step
- replaced Jest with Bun’s native test runner
- replaced Prettier with `oxfmt`
- replaced ESLint with `oxlint`
- enabled type-aware/type-check linting with `oxlint-tsgolint`
- simplified TypeScript config to a single `tsconfig.json` extending
`@tsconfig/bun`

### Tests
- moved tests next to the runtime source
- rewrote tests to focus on meaningful user-facing action behavior
- added coverage for:
  - default entrypoint execution
  - latest version installs
  - legacy version installs
  - modern pinned version installs
  - failure when the installed CLI cannot report a version
- action code coverage is now `100%`

### Workflows
- renamed workflow files for clarity:
  - `test.yml` -> `ci.yml`
  - `start.yml` -> `e2e.yml`
- updated workflow/job naming so required checks are clean and stable:
  - `CI`
  - `E2E`
  - `CodeQL`
  - `Licensed`
- added aggregate PR-facing checks so branch protection does not need
matrix legs
- made CI and E2E skip heavy jobs on draft PRs
- made E2E run automatically on ready PRs and new commits
- simplified CodeQL config by removing the separate config file
- updated action pins to current releases using commit SHAs
- refined Dependabot for Bun-era updates and non-major auto-merge

### Docs
- refreshed `README.md` and `docs/index.md` for the new v2 behavior
- updated examples to use `@v2`
- added a practical example for exporting local Supabase env vars after
`supabase start`
- removed stale references to old local/dev flows

## Breaking / Notable Changes

- the action now runs as a Bun-based composite action instead of a
prebuilt JavaScript action
- no checked-in `dist/` artifacts anymore
- self-hosted runners now need the prerequisites expected by the
composite action path:
  - `bash`
- network access to install Bun/dependencies and download the Supabase
CLI

## Validation

Verified locally with:
- `bun run format:check`
- `bun run lint`
- `bun test`
- `bun run ci`

Also updated workflows and branch-protection-friendly check names so PR
validation is cleaner going forward.

## Follow-up

After merge, branch protection should require only:
- `CI`
- `E2E`
- `CodeQL`
- `Licensed`

---------

Co-authored-by: licensed-ci <licensed-ci@users.noreply.github.com>
This commit is contained in:
Julien Goux
2026-04-03 17:51:37 +02:00
committed by GitHub
parent 60645042c4
commit 2eca1b4d35
52 changed files with 1262 additions and 46740 deletions

1
.bun-version Normal file
View File

@@ -0,0 +1 @@
1.3.10

3
.gitattributes vendored
View File

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

2
.github/CODEOWNERS vendored
View File

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

View File

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

View File

@@ -4,30 +4,26 @@ updates:
directory: / directory: /
schedule: schedule:
interval: weekly interval: weekly
cooldown: open-pull-requests-limit: 2
default-days: 7
groups: groups:
actions-minor: actions-minor:
update-types: update-types:
- minor - minor
- patch - patch
- package-ecosystem: npm - package-ecosystem: bun
directory: / directory: /
schedule: schedule:
interval: daily interval: weekly
open-pull-requests-limit: 2
groups: groups:
npm-development: bun-development:
dependency-type: development dependency-type: development
update-types: update-types:
- minor - minor
- patch - patch
npm-production: bun-production:
dependency-type: production dependency-type: production
update-types: update-types:
- minor
- patch - patch
ignore:
# nodejs types is pinned to runtime version
- dependency-name: '@types/node'
update-types:
- version-update:semver-major

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

@@ -0,0 +1,68 @@
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 }}
- 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" ]]

View File

@@ -1,49 +0,0 @@
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Initialize CodeQL
id: initialize
uses: github/codeql-action/init@ebcb5b36ded6beda4ceefea6a8bc4cc885255bb3 # v3.34.1
with:
config-file: .github/codeql/codeql-config.yml
languages: ${{ matrix.language }}
source-root: src
- name: Autobuild
id: autobuild
uses: github/codeql-action/autobuild@ebcb5b36ded6beda4ceefea6a8bc4cc885255bb3 # v3.34.1
- name: Perform CodeQL Analysis
id: analyze
uses: github/codeql-action/analyze@ebcb5b36ded6beda4ceefea6a8bc4cc885255bb3 # v3.34.1

View File

@@ -3,6 +3,10 @@ name: Dependabot auto-merge
on: pull_request on: pull_request
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions: permissions:
pull-requests: write pull-requests: write
contents: write contents: write
@@ -10,29 +14,25 @@ permissions:
jobs: jobs:
dependabot: dependabot:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Checking the actor will prevent your Action run failing on non-Dependabot timeout-minutes: 10
# PRs but also ensures that it only does work for Dependabot PRs. # 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 if: github.actor == 'dependabot[bot]' && github.repository == github.event.pull_request.head.repo.full_name
steps: steps:
# This first step will fail if there's no metadata and so the approval # Metadata drives the non-major gating used for approval and auto-merge.
# will not occur.
- id: meta - id: meta
uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v3.0.0 uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v3.0.0
with: with:
github-token: '${{ secrets.GITHUB_TOKEN }}' github-token: "${{ secrets.GITHUB_TOKEN }}"
# Here the PR gets approved.
- name: Approve a PR - name: Approve a PR
if: ${{steps.meta.outputs.update-type != 'version-update:semver-major'}} if: ${{ steps.meta.outputs.update-type != 'version-update:semver-major' }}
run: gh pr review --approve "$PR_URL" run: gh pr review --approve "$PR_URL"
env: env:
PR_URL: ${{ github.event.pull_request.html_url }} PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.GITHUB_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 - name: Enable auto-merge for Dependabot PRs
if: ${{steps.meta.outputs.update-type != 'version-update:semver-major'}} if: ${{ steps.meta.outputs.update-type != 'version-update:semver-major' }}
run: gh pr merge --auto --squash "$PR_URL" run: gh pr merge --auto --squash "$PR_URL"
env: env:
PR_URL: ${{ github.event.pull_request.html_url }} PR_URL: ${{ github.event.pull_request.html_url }}

71
.github/workflows/e2e.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
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

@@ -6,39 +6,54 @@ name: Licensed
on: on:
pull_request: pull_request:
paths:
- .github/workflows/licensed.yml
- .licensed.yml
- .licenses/**
- bun.lock
- package.json
push: push:
branches: branches:
- main - main
paths:
- .github/workflows/licensed.yml
- .licensed.yml
- .licenses/**
- bun.lock
- package.json
workflow_dispatch: workflow_dispatch:
permissions: concurrency:
contents: write group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
licensed: check-licenses:
name: Check Licenses if: ${{ github.event_name != 'workflow_dispatch' }}
name: Licensed
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps: steps:
- name: Checkout - name: Checkout
id: checkout id: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Bun
id: setup-bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
persist-credentials: false bun-version-file: .bun-version
- name: Setup Node.js
id: setup-node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: npm
- name: Install Dependencies - name: Install Dependencies
id: npm-ci id: bun-install
run: npm ci run: bun install --frozen-lockfile
- name: Setup Ruby - name: Setup Ruby
id: setup-ruby id: setup-ruby
uses: ruby/setup-ruby@4dc28cf14d77b0afa6832d9765ac422dbf0dfedd # v1.298.0 uses: ruby/setup-ruby@3ff19f5e2baf30647122352b96108b1fbe250c64 # v1.299.0
with: with:
ruby-version: ruby ruby-version: ruby
@@ -47,24 +62,61 @@ jobs:
version: 4.x version: 4.x
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
# If this is a workflow_dispatch event, update the cached licenses. - name: Check Licenses
- if: ${{ github.event_name == 'workflow_dispatch' }} id: check-licenses
run: licensed status
update-licenses:
if: ${{ github.event_name == 'workflow_dispatch' }}
name: Update Licenses name: Update Licenses
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
steps:
- name: Checkout
id: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Bun
id: setup-bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version
- name: Install Dependencies
id: bun-install
run: bun install --frozen-lockfile
- name: Setup Ruby
id: setup-ruby
uses: ruby/setup-ruby@3ff19f5e2baf30647122352b96108b1fbe250c64 # v1.299.0
with:
ruby-version: ruby
- uses: licensee/setup-licensed@0d52e575b3258417672be0dff2f115d7db8771d8 # v1.3.2
with:
version: 4.x
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Update License Cache
id: update-licenses id: update-licenses
run: licensed cache run: licensed cache
# Then, commit the updated licenses to the repository. - name: Format License Files
- if: ${{ github.event_name == 'workflow_dispatch' }} id: format-licenses
name: Commit Licenses run: bun x oxfmt --write .licensed.yml .licenses
- name: Commit Licenses
id: commit-licenses id: commit-licenses
run: | run: |
git config --local user.email "licensed-ci@users.noreply.github.com" git config --local user.email "licensed-ci@users.noreply.github.com"
git config --local user.name "licensed-ci" git config --local user.name "licensed-ci"
git add . git add .licenses .licensed.yml
if git diff --cached --quiet; then
echo "No license cache changes to commit."
exit 0
fi
git commit -m "Auto-update license files" git commit -m "Auto-update license files"
git push git push
# Last, check the status of the cached licenses.
- name: Check Licenses
id: check-licenses
run: licensed status

View File

@@ -1,57 +0,0 @@
# 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
id: setup-node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
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@61abc07d755095a68f4987d1c2c3d1d64408f1f9 # v8.5.0
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

View File

@@ -1,47 +0,0 @@
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@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

View File

@@ -1,70 +0,0 @@
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- 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,7 +1,6 @@
# Dependency directory # Dependency directory
node_modules node_modules
bun.lockb
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs # Logs
logs logs
*.log *.log

View File

@@ -13,6 +13,4 @@ allowed:
- other - other
ignored: 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" name: "@actions/core"
version: 1.11.1 version: 3.0.0
type: npm type: npm
summary: Actions core lib summary: Actions core lib
homepage: https://github.com/actions/toolkit/tree/main/packages/core homepage: https://github.com/actions/toolkit/tree/main/packages/core
license: mit license: mit
licenses: licenses:
- sources: LICENSE.md - sources: LICENSE.md
text: |- text: |-
The MIT License (MIT) The MIT License (MIT)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,30 +0,0 @@
---
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

@@ -1,26 +0,0 @@
---
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

@@ -1,26 +0,0 @@
---
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

@@ -0,0 +1,26 @@
---
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/ homepage: https://github.com/koichik/node-tunnel/
license: mit license: mit
licenses: licenses:
- sources: LICENSE - sources: LICENSE
text: | text: |
The MIT License (MIT) The MIT License (MIT)
@@ -29,7 +29,7 @@ licenses:
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
- sources: README.md - sources: README.md
text: Licensed under the [MIT](https://github.com/koichik/node-tunnel/blob/master/LICENSE) text: Licensed under the [MIT](https://github.com/koichik/node-tunnel/blob/master/LICENSE)
license. license.
notices: [] notices: []

View File

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

View File

@@ -1,24 +0,0 @@
# 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

View File

@@ -1 +0,0 @@
20.19.4

View File

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

View File

@@ -1,16 +0,0 @@
# 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

View File

@@ -1,14 +0,0 @@
# 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/

View File

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

103
README.md
View File

@@ -1,13 +1,12 @@
# :gear: Supabase CLI Action # :gear: Supabase CLI Action
[![CI](https://github.com/supabase/setup-cli/actions/workflows/start.yml/badge.svg)](https://github.com/supabase/setup-cli/actions/workflows/start.yml) [![CI](https://github.com/supabase/setup-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/supabase/setup-cli/actions/workflows/ci.yml)
[![Linter](https://github.com/supabase/setup-cli/actions/workflows/linter.yml/badge.svg)](https://github.com/supabase/setup-cli/actions/workflows/linter.yml) [![E2E](https://github.com/supabase/setup-cli/actions/workflows/e2e.yml/badge.svg)](https://github.com/supabase/setup-cli/actions/workflows/e2e.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) [![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 ## About
This action sets up the Supabase CLI, This composite action sets up the Supabase CLI,
[`supabase`](https://github.com/supabase/cli), on GitHub's hosted Actions [`supabase`](https://github.com/supabase/cli), on GitHub's hosted Actions
runners. Other CI runners like runners. Other CI runners like
[Bitbucket](https://bitbucket.org/supabase-cli/setup-cli/src/master/bitbucket-pipelines.yml) [Bitbucket](https://bitbucket.org/supabase-cli/setup-cli/src/master/bitbucket-pipelines.yml)
@@ -25,23 +24,27 @@ Setup the `supabase` CLI:
```yaml ```yaml
steps: steps:
- uses: supabase/setup-cli@v1 - uses: supabase/setup-cli@v2
``` ```
If `version` is omitted, the action checks the repository root for `bun.lock`,
`pnpm-lock.yaml`, or `package-lock.json` and uses the declared `supabase`
version. If no supported lockfile is present, it falls back to `latest`.
A specific version of the `supabase` CLI can be installed: A specific version of the `supabase` CLI can be installed:
```yaml ```yaml
steps: steps:
- uses: supabase/setup-cli@v1 - uses: supabase/setup-cli@v2
with: with:
version: 2.20.3 version: 2.84.2
``` ```
Run `supabase db start` to execute all migrations on a fresh database: Run `supabase db start` to execute all migrations on a fresh database:
```yaml ```yaml
steps: steps:
- uses: supabase/setup-cli@v1 - uses: supabase/setup-cli@v2
with: with:
version: latest version: latest
- run: supabase init - run: supabase init
@@ -53,11 +56,11 @@ on Windows and macOS runners.
## Inputs ## Inputs
The actions supports the following inputs: The action supports the following inputs:
| Name | Type | Description | Default | Required | | Name | Type | Description | Default | Required |
| --------- | ------ | ---------------------------------- | -------- | -------- | | --------- | ------ | ---------------------------------- | --------------------------------- | -------- |
| `version` | String | Supabase CLI version (or `latest`) | `2.20.3` | false | | `version` | String | Supabase CLI version (or `latest`) | Root lockfile version or `latest` | false |
## Advanced Usage ## Advanced Usage
@@ -65,7 +68,7 @@ Check generated TypeScript types are up-to-date with Postgres schema:
```yaml ```yaml
steps: steps:
- uses: supabase/setup-cli@v1 - uses: supabase/setup-cli@v2
- run: supabase init - run: supabase init
- run: supabase db start - run: supabase db start
- name: Verify generated types match Postgres schema - name: Verify generated types match Postgres schema
@@ -87,59 +90,62 @@ env:
# Retrieve <project-id> from dashboard url: https://app.supabase.com/project/<project-id> # Retrieve <project-id> from dashboard url: https://app.supabase.com/project/<project-id>
PROJECT_ID: <project-id> PROJECT_ID: <project-id>
steps: steps:
- uses: supabase/setup-cli@v1 - uses: supabase/setup-cli@v2
- run: supabase link --project-ref $PROJECT_ID - run: supabase link --project-ref $PROJECT_ID
- run: supabase db push - 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 ## Develop
After you've cloned the repository to your local machine or codespace, you'll After you've cloned the repository to your local machine or codespace, you'll
need to perform some initial setup steps before you can develop your action. need to perform a few setup steps before you can work on the action.
> [!NOTE] > [!NOTE]
> >
> You'll need to have a reasonably modern version of > You'll need a recent version of [Bun](https://bun.sh) for local development.
> [Node.js](https://nodejs.org) handy (20.x or later should work!). If you are > This repository includes a `.bun-version` file for tools that can auto-switch
> using a version manager like [`nodenv`](https://github.com/nodenv/nodenv) or > Bun versions.
> [`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 1. :hammer_and_wrench: Install the dependencies
```bash ```bash
npm ci bun install
```
1. :building_construction: Package the TypeScript for distribution
```bash
npm run bundle
``` ```
1. :white_check_mark: Run the tests 1. :white_check_mark: Run the tests
```bash ```bash
$ npm test bun test
PASS ./index.test.js
✓ gets download url to binary (3 ms)
✓ runs main action (891 ms)
...
``` ```
## Publish to a distribution branch 1. :mag: Run the full local CI suite
Actions are run from this GitHub repository so we will checkin the packed `dist` ```bash
folder. bun run ci
```
## Publish
1. Create a new GitHub release 1. Create a new GitHub release
2. Rebase `v1` branch on `main` 2. Rebase `v2` branch on `main`
Your action is now published! :rocket: Your action is now published! :rocket:
@@ -148,14 +154,17 @@ See the
## Validate ## Validate
You can now validate the action by referencing `./` in a workflow in your Validate changes by exercising the action from a workflow in this repository
repository (see [test.yml](.github/workflows/test.yml)) (see [ci.yml](.github/workflows/ci.yml) and [e2e.yml](.github/workflows/e2e.yml)).
```yaml ```yaml
uses: ./ steps:
with: - uses: ./
with:
version: latest version: latest
``` ```
See the [actions tab](https://github.com/supabase/setup-cli/actions) for runs of The CI workflow provides fast smoke coverage across GitHub-hosted runners, and
this action! :rocket: 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.

View File

@@ -1,61 +0,0 @@
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,12 +3,29 @@ description: Setup Supabase CLI, supabase, on GitHub Actions runners
author: Supabase author: Supabase
inputs: inputs:
version: version:
description: Version of Supabase CLI to install description: Version of Supabase CLI to install. If omitted, detect from the root lockfile and otherwise use latest.
required: false required: false
default: 2.20.3
outputs: outputs:
version: version:
description: Version of installed Supabase CLI description: Version of installed Supabase CLI
value: ${{ steps.setup-cli.outputs.version }}
runs: runs:
using: node24 using: composite
main: dist/index.js steps:
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: ${{ github.action_path }}/.bun-version
- name: Install Action Dependencies
shell: bash
working-directory: ${{ github.action_path }}
run: bun install --frozen-lockfile --production
- id: setup-cli
name: Setup Supabase CLI
shell: bash
working-directory: ${{ github.action_path }}
env:
INPUT_VERSION: ${{ inputs.version }}
run: bun src/main.ts

View File

@@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 1.1 KiB

160
bun.lock Normal file
View File

@@ -0,0 +1,160 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "setup-cli",
"dependencies": {
"@actions/core": "^3.0.0",
"@actions/tool-cache": "^4.0.0",
},
"devDependencies": {
"@tsconfig/bun": "^1.0.10",
"@types/bun": "^1.3.11",
"@typescript/native-preview": "^7.0.0-dev.20260401.1",
"oxfmt": "^0.43.0",
"oxlint": "^1.58.0",
"oxlint-tsgolint": "^0.19.0",
},
},
},
"packages": {
"@actions/core": ["@actions/core@3.0.0", "", { "dependencies": { "@actions/exec": "^3.0.0", "@actions/http-client": "^4.0.0" } }, "sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg=="],
"@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=="],
"@actions/tool-cache": ["@actions/tool-cache@4.0.0", "", { "dependencies": { "@actions/core": "^3.0.0", "@actions/exec": "^3.0.0", "@actions/http-client": "^4.0.0", "@actions/io": "^3.0.0", "semver": "^7.7.3" } }, "sha512-L8P9HbXvpvqjZDveb/fdsa55IVC0trfPgQ4ZwGo6r5af6YDVdM9vMGPZ7rgY2fAT9gGj4PSYd6bYlg3p3jD78A=="],
"@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.43.0", "", { "os": "android", "cpu": "arm" }, "sha512-CgU2s+/9hHZgo0IxVxrbMPrMj+tJ6VM3mD7Mr/4oiz4FNTISLoCvRmB5nk4wAAle045RtRjd86m673jwPyb1OQ=="],
"@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.43.0", "", { "os": "android", "cpu": "arm64" }, "sha512-T9OfRwjA/EdYxAqbvR7TtqLv5nIrwPXuCtTwOHtS7aR9uXyn74ZYgzgTo6/ZwvTq9DY4W+DsV09hB2EXgn9EbA=="],
"@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.43.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-o3i49ZUSJWANzXMAAVY1wnqb65hn4JVzwlRQ5qfcwhRzIA8lGVaud31Q3by5ALHPrksp5QEaKCQF9aAS3TXpZA=="],
"@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.43.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-vWECzzCFkb0kK6jaHjbtC5sC3adiNWtqawFCxhpvsWlzVeKmv5bNvkB4nux+o4JKWTpHCM57NDK/MeXt44txmA=="],
"@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.43.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-rgz8JpkKiI/umOf7fl9gwKyQasC8bs5SYHy6g7e4SunfLBY3+8ATcD5caIg8KLGEtKFm5ujKaH8EfjcmnhzTLg=="],
"@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.43.0", "", { "os": "linux", "cpu": "arm" }, "sha512-nWYnF3vIFzT4OM1qL/HSf1Yuj96aBuKWSaObXHSWliwAk2rcj7AWd6Lf7jowEBQMo4wCZVnueIGw/7C4u0KTBQ=="],
"@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.43.0", "", { "os": "linux", "cpu": "arm" }, "sha512-sFg+NWJbLfupYTF4WELHAPSnLPOn1jiDZ33Z1jfDnTaA+cC3iB35x0FMMZTFdFOz3icRIArncwCcemJFGXu6TQ=="],
"@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-MelWqv68tX6wZEILDrTc9yewiGXe7im62+5x0bNXlCYFOZdA+VnYiJfAihbROsZ5fm90p9C3haFrqjj43XnlAA=="],
"@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ROaWfYh+6BSJ1Arwy5ujijTlwnZetxDxzBpDc1oBR4d7rfrPBqzeyjd5WOudowzQUgyavl2wEpzn1hw3jWcqLA=="],
"@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.43.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-PJRs/uNxmFipJJ8+SyKHh7Y7VZIKQicqrrBzvfyM5CtKi8D7yZKTwUOZV3ffxmiC2e7l1SDJpkBEOyue5NAFsg=="],
"@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.43.0", "", { "os": "linux", "cpu": "none" }, "sha512-j6biGAgzIhj+EtHXlbNumvwG7XqOIdiU4KgIWRXAEj/iUbHKukKW8eXa4MIwpQwW1YkxovduKtzEAPnjlnAhVQ=="],
"@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.43.0", "", { "os": "linux", "cpu": "none" }, "sha512-RYWxAcslKxvy7yri24Xm9cmD0RiANaiEPs007EFG6l9h1ChM69Q5SOzACaCoz4Z9dEplnhhneeBaTWMEdpgIbA=="],
"@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.43.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-DT6Q8zfQQy3jxpezAsBACEHNUUixKSYTwdXeXojNHe4DQOoxjPdjr3Szu6BRNjxLykZM/xMNmp9ElOIyDppwtw=="],
"@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-R8Yk7iYcuZORXmCfFZClqbDxRZgZ9/HEidUuBNdoX8Ptx07cMePnMVJ/woB84lFIDjh2ROHVaOP40Ds3rBXFqg=="],
"@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-F2YYqyvnQNvi320RWZNAvsaWEHwmW3k4OwNJ1hZxRKXupY63expbBaNp6jAgvYs7y/g546vuQnGHQuCBhslhLQ=="],
"@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.43.0", "", { "os": "none", "cpu": "arm64" }, "sha512-OE6TdietLXV3F6c7pNIhx/9YC1/2YFwjU9DPc/fbjxIX19hNIaP1rS0cFjCGJlGX+cVJwIKWe8Mos+LdQ1yAJw=="],
"@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.43.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-0nWK6a7pGkbdoypfVicmV9k/N1FwjPZENoqhlTU+5HhZnAhpIO3za30nEE33u6l6tuy9OVfpdXUqxUgZ+4lbZw=="],
"@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.43.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-9aokTR4Ft+tRdvgN/pKzSkVy2ksc4/dCpDm9L/xFrbIw0yhLtASLbvoG/5WOTUh/BRPPnfGTsWznEqv0dlOmhA=="],
"@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.43.0", "", { "os": "win32", "cpu": "x64" }, "sha512-4bPgdQux2ZLWn3bf2TTXXMHcJB4lenmuxrLqygPmvCJ104Yqzj1UctxSRzR31TiJ4MLaG22RK8dUsVpJtrCz5g=="],
"@oxlint-tsgolint/darwin-arm64": ["@oxlint-tsgolint/darwin-arm64@0.19.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-FVOIp5Njte8Z6PpINz7sL5blqSro0pAL8VAHYQ+K5Xm4cOrPQ6DGIhH14oXnbRjzn8Kl69qjz8TPteyn8EqwsQ=="],
"@oxlint-tsgolint/darwin-x64": ["@oxlint-tsgolint/darwin-x64@0.19.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-GakDTDACePvqOFq3N4oQCl8SyMMa7VBnqV0gDcXPuK50jdWCUqlxM9tgRJarjyIVvmDEJRGYOen+4uBtVwg4Aw=="],
"@oxlint-tsgolint/linux-arm64": ["@oxlint-tsgolint/linux-arm64@0.19.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ya0R7somo+KDhhkPtENJ9Q28Fost+aqA3MPe86pEqgmukHFc/KO65PgShOSbIFjZNptELEQvsWL8gDxYZWhH3w=="],
"@oxlint-tsgolint/linux-x64": ["@oxlint-tsgolint/linux-x64@0.19.0", "", { "os": "linux", "cpu": "x64" }, "sha512-yFH378jWc1k/oJmpk+TKpWbKvFieJJvsOHxVMSNFc+ukqs44ZSHVt4HFfAhXAt/bzVK2f7EIDTGp8Hm1OjoJ6Q=="],
"@oxlint-tsgolint/win32-arm64": ["@oxlint-tsgolint/win32-arm64@0.19.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-R6NyAtha7OWxh7NGBeFxqDTGAVl1Xj4xLa8Qj39PKbIDqBeVW8BIb+1nEnRp+Mo/VpRoeoFAcqlBsuMcUMd26Q=="],
"@oxlint-tsgolint/win32-x64": ["@oxlint-tsgolint/win32-x64@0.19.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2ePvxcbS5tPOmrQvxR8Kc+IqzdTtlrGeMDv+jjTYfkTFPmh2rF9yxVchi/4WM6js3gt2UauQeMV/tfnZNemENQ=="],
"@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.58.0", "", { "os": "android", "cpu": "arm" }, "sha512-1T7UN3SsWWxpWyWGn1cT3ASNJOo+pI3eUkmEl7HgtowapcV8kslYpFQcYn431VuxghXakPNlbjRwhqmR37PFOg=="],
"@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.58.0", "", { "os": "android", "cpu": "arm64" }, "sha512-GryzujxuiRv2YFF7bRy8mKcxlbuAN+euVUtGJt9KKbLT8JBUIosamVhcthLh+VEr6KE6cjeVMAQxKAzJcoN7dg=="],
"@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.58.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7/bRSJIwl4GxeZL9rPZ11anNTyUO9epZrfEJH/ZMla3+/gbQ6xZixh9nOhsZ0QwsTW7/5J2A/fHbD1udC5DQQA=="],
"@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.58.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-EqdtJSiHweS2vfILNrpyJ6HUwpEq2g7+4Zx1FPi4hu3Hu7tC3znF6ufbXO8Ub2LD4mGgznjI7kSdku9NDD1Mkg=="],
"@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.58.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-VQt5TH4M42mY20F545G637RKxV/yjwVtKk2vfXuazfReSIiuvWBnv+FVSvIV5fKVTJNjt3GSJibh6JecbhGdBw=="],
"@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.58.0", "", { "os": "linux", "cpu": "arm" }, "sha512-fBYcj4ucwpAtjJT3oeBdFBYKvNyjRSK+cyuvBOTQjh0jvKp4yeA4S/D0IsCHus/VPaNG5L48qQkh+Vjy3HL2/Q=="],
"@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.58.0", "", { "os": "linux", "cpu": "arm" }, "sha512-0BeuFfwlUHlJ1xpEdSD1YO3vByEFGPg36uLjK1JgFaxFb4W6w17F8ET8sz5cheZ4+x5f2xzdnRrrWv83E3Yd8g=="],
"@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.58.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-TXlZgnPTlxrQzxG9ZXU7BNwx1Ilrr17P3GwZY0If2EzrinqRH3zXPc3HrRcBJgcsoZNMuNL5YivtkJYgp467UQ=="],
"@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.58.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-zSoYRo5dxHLcUx93Stl2hW3hSNjPt99O70eRVWt5A1zwJ+FPjeCCANCD2a9R4JbHsdcl11TIQOjyigcRVOH2mw=="],
"@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.58.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NQ0U/lqxH2/VxBYeAIvMNUK1y0a1bJ3ZicqkF2c6wfakbEciP9jvIE4yNzCFpZaqeIeRYaV7AVGqEO1yrfVPjA=="],
"@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.58.0", "", { "os": "linux", "cpu": "none" }, "sha512-X9J+kr3gIC9FT8GuZt0ekzpNUtkBVzMVU4KiKDSlocyQuEgi3gBbXYN8UkQiV77FTusLDPsovjo95YedHr+3yg=="],
"@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.58.0", "", { "os": "linux", "cpu": "none" }, "sha512-CDze3pi1OO3Wvb/QsXjmLEY4XPKGM6kIo82ssNOgmcl1IdndF9VSGAE38YLhADWmOac7fjqhBw82LozuUVxD0Q=="],
"@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.58.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-b/89glbxFaEAcA6Uf1FvCNecBJEgcUTsV1quzrqXM/o4R1M4u+2KCVuyGCayN2UpsRWtGGLb+Ver0tBBpxaPog=="],
"@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.58.0", "", { "os": "linux", "cpu": "x64" }, "sha512-0/yYpkq9VJFCEcuRlrViGj8pJUFFvNS4EkEREaN7CB1EcLXJIaVSSa5eCihwBGXtOZxhnblWgxks9juRdNQI7w=="],
"@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.58.0", "", { "os": "linux", "cpu": "x64" }, "sha512-hr6FNvmcAXiH+JxSvaJ4SJ1HofkdqEElXICW9sm3/Rd5eC3t7kzvmLyRAB3NngKO2wzXRCAm4Z/mGWfrsS4X8w=="],
"@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.58.0", "", { "os": "none", "cpu": "arm64" }, "sha512-R+O368VXgRql1K6Xar+FEo7NEwfo13EibPMoTv3sesYQedRXd6m30Dh/7lZMxnrQVFfeo4EOfYIP4FpcgWQNHg=="],
"@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.58.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q0FZiAY/3c4YRj4z3h9K1PgaByrifrfbBoODSeX7gy97UtB7pySPUQfC2B/GbxWU6k7CzQrRy5gME10PltLAFQ=="],
"@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.58.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-Y8FKBABrSPp9H0QkRLHDHOSUgM/309a3IvOVgPcVxYcX70wxJrk608CuTg7w+C6vEd724X5wJoNkBcGYfH7nNQ=="],
"@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.58.0", "", { "os": "win32", "cpu": "x64" }, "sha512-bCn5rbiz5My+Bj7M09sDcnqW0QJyINRVxdZ65x1/Y2tGrMwherwK/lpk+HRQCKvXa8pcaQdF5KY5j54VGZLwNg=="],
"@tsconfig/bun": ["@tsconfig/bun@1.0.10", "", {}, "sha512-5AV5YknQjNyoYzZ/8NG0dawqew/wH+x7ANiCfCIn29qo0cdbd1EryvFD1k5NSZWLBMOI/fGqMIaxi58GPIP9Cg=="],
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
"@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.20260401.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260401.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260401.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260401.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260401.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260401.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260401.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260401.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-xJcN9WlY/P6xKjCMH4+DTzZSj/EKR6H9avuqUKs4eKyPEvaQ4bX+9Ys3Vl2qhlUaD7IRWY7HN7db0LHAGlWRSA=="],
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260401.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9PCc1D4/zLic30g1upOw6ZmUl98fnrXYRv5wIZ6fxm1zZAObieRKUX3Jbr8M9N8iQQFxPIZPniIScsxAbmbJvw=="],
"@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260401.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-wwzca1KrjSVC6ApXfITsg/wF4GGbhVYebc7zChpuyi+phrHfw6ThTPB5XFUH4nA32vqw0Hn/6KACipMgzg8GPA=="],
"@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260401.1", "", { "os": "linux", "cpu": "arm" }, "sha512-bbIkRZYjtyoyCJ3wFES7qn3EwYO5Go1hxArL5X5oWiBmUHq5gMIxTZDv5mpWxopVf9Eyh4ErHefXjf1s4J+6Ag=="],
"@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260401.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-1hgKibGi4QZF1J0hKltgY4nj4yKDmI4Ang5ar80I+YeUdGxV/fP2kU3bJang7QtHuSso6W+a52SF62zgqbzdow=="],
"@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260401.1", "", { "os": "linux", "cpu": "x64" }, "sha512-1ysZ4c/Wa3RYIlrwVceYlhb1m1hxQ4P2x92valZXH0bNWEPb+oiQ4Yf35O/vi5h8zDdX/ZQ59vivYl27cF1VVA=="],
"@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260401.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-fZYLCRe36y1BuzRFFpU2/RQ212l6Y1dccRMh8oTB8HlAVAAwtbkb6cjEn0Ablj4Dy16+Ih8R9uHsxPLNhtKw1Q=="],
"@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260401.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I6ses4SjWvpbvSpm1BPFRrDeqrzu7JTchJG/a26iwwmTLv4fAGLc5/o6Kv9Naygozop1W3KOcVM5i3A9oxiIjQ=="],
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
"oxfmt": ["oxfmt@0.43.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.43.0", "@oxfmt/binding-android-arm64": "0.43.0", "@oxfmt/binding-darwin-arm64": "0.43.0", "@oxfmt/binding-darwin-x64": "0.43.0", "@oxfmt/binding-freebsd-x64": "0.43.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.43.0", "@oxfmt/binding-linux-arm-musleabihf": "0.43.0", "@oxfmt/binding-linux-arm64-gnu": "0.43.0", "@oxfmt/binding-linux-arm64-musl": "0.43.0", "@oxfmt/binding-linux-ppc64-gnu": "0.43.0", "@oxfmt/binding-linux-riscv64-gnu": "0.43.0", "@oxfmt/binding-linux-riscv64-musl": "0.43.0", "@oxfmt/binding-linux-s390x-gnu": "0.43.0", "@oxfmt/binding-linux-x64-gnu": "0.43.0", "@oxfmt/binding-linux-x64-musl": "0.43.0", "@oxfmt/binding-openharmony-arm64": "0.43.0", "@oxfmt/binding-win32-arm64-msvc": "0.43.0", "@oxfmt/binding-win32-ia32-msvc": "0.43.0", "@oxfmt/binding-win32-x64-msvc": "0.43.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-KTYNG5ISfHSdmeZ25Xzb3qgz9EmQvkaGAxgBY/p38+ZiAet3uZeu7FnMwcSQJg152Qwl0wnYAxDc+Z/H6cvrwA=="],
"oxlint": ["oxlint@1.58.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.58.0", "@oxlint/binding-android-arm64": "1.58.0", "@oxlint/binding-darwin-arm64": "1.58.0", "@oxlint/binding-darwin-x64": "1.58.0", "@oxlint/binding-freebsd-x64": "1.58.0", "@oxlint/binding-linux-arm-gnueabihf": "1.58.0", "@oxlint/binding-linux-arm-musleabihf": "1.58.0", "@oxlint/binding-linux-arm64-gnu": "1.58.0", "@oxlint/binding-linux-arm64-musl": "1.58.0", "@oxlint/binding-linux-ppc64-gnu": "1.58.0", "@oxlint/binding-linux-riscv64-gnu": "1.58.0", "@oxlint/binding-linux-riscv64-musl": "1.58.0", "@oxlint/binding-linux-s390x-gnu": "1.58.0", "@oxlint/binding-linux-x64-gnu": "1.58.0", "@oxlint/binding-linux-x64-musl": "1.58.0", "@oxlint/binding-openharmony-arm64": "1.58.0", "@oxlint/binding-win32-arm64-msvc": "1.58.0", "@oxlint/binding-win32-ia32-msvc": "1.58.0", "@oxlint/binding-win32-x64-msvc": "1.58.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.18.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-t4s9leczDMqlvOSjnbCQe7gtoLkWgBGZ7sBdCJ9EOj5IXFSG/X7OAzK4yuH4iW+4cAYe8kLFbC8tuYMwWZm+Cg=="],
"oxlint-tsgolint": ["oxlint-tsgolint@0.19.0", "", { "optionalDependencies": { "@oxlint-tsgolint/darwin-arm64": "0.19.0", "@oxlint-tsgolint/darwin-x64": "0.19.0", "@oxlint-tsgolint/linux-arm64": "0.19.0", "@oxlint-tsgolint/linux-x64": "0.19.0", "@oxlint-tsgolint/win32-arm64": "0.19.0", "@oxlint-tsgolint/win32-x64": "0.19.0" }, "bin": { "tsgolint": "bin/tsgolint.js" } }, "sha512-pSzUmDjMyjC8iUUZ7fCLo0D1iUaYIfodd/WIQ6Zra11YkjkUQk3BOFoW4I5ec6uZ/0s2FEmxtiZ7hiTXFRp1cg=="],
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"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 vendored

File diff suppressed because one or more lines are too long

1
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -1,81 +0,0 @@
// 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'
}
}
]

View File

@@ -1,40 +0,0 @@
// 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
}

13147
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,72 +1,41 @@
{ {
"name": "setup-cli", "name": "setup-cli",
"description": "Supabase CLI GitHub Action", "version": "2.0.0",
"version": "1.6.0",
"author": "",
"type": "module",
"private": true, "private": true,
"description": "Supabase CLI GitHub Action",
"keywords": [
"actions"
],
"license": "MIT",
"author": "",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/supabase/setup-cli.git" "url": "git+https://github.com/supabase/setup-cli.git"
}, },
"keywords": [ "type": "module",
"actions"
],
"exports": {
".": "./dist/index.js"
},
"engines": {
"node": ">=20"
},
"scripts": { "scripts": {
"bundle": "npm run format:write && npm run package", "all": "bun run format && bun run lint && bun run coverage",
"ci-test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest", "ci": "bun run format:check && bun run lint && bun run coverage",
"coverage": "npx make-coverage-badge --output-path ./badges/coverage.svg", "coverage": "bun test --coverage --coverage-reporter=text --coverage-reporter=lcov",
"format:write": "npx prettier --write .", "format": "bun x oxfmt --write . '!coverage/**'",
"format:check": "npx prettier --check .", "format:check": "bun x oxfmt --check . '!coverage/**'",
"lint": "npx eslint .", "lint": "bun x oxlint --deny-warnings --type-aware --type-check --tsconfig tsconfig.json src",
"local-action": "npx @github/local-action . src/main.ts .env", "test": "bun test",
"package": "npx rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript", "typecheck": "bun x tsgo -p tsconfig.json --noEmit"
"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": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^3.0.0",
"@actions/tool-cache": "^2.0.2", "@actions/tool-cache": "^4.0.0"
"semver": "^7.7.2"
}, },
"devDependencies": { "devDependencies": {
"@eslint/compat": "^1.3.2", "@tsconfig/bun": "^1.0.10",
"@github/local-action": "^5.1.0", "@types/bun": "^1.3.11",
"@jest/globals": "^30.0.5", "@typescript/native-preview": "^7.0.0-dev.20260401.1",
"@rollup/plugin-commonjs": "^28.0.6", "oxfmt": "^0.43.0",
"@rollup/plugin-node-resolve": "^16.0.1", "oxlint": "^1.58.0",
"@rollup/plugin-typescript": "^12.1.4", "oxlint-tsgolint": "^0.19.0"
"@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.40.0",
"@typescript-eslint/parser": "^8.38.0",
"eslint": "^9.34.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.4",
"jest": "^30.0.5",
"js-yaml": "^4.1.1",
"make-coverage-badge": "^1.2.0",
"prettier": "^3.6.2",
"prettier-eslint": "^16.4.2",
"rollup": "^4.47.1",
"ts-jest": "^29.4.1",
"ts-jest-resolver": "^2.0.1",
"typescript": "^5.8.3"
}, },
"optionalDependencies": { "engines": {
"@rollup/rollup-linux-x64-gnu": "*" "bun": ">=1.3.10"
} }
} }

View File

@@ -1,18 +0,0 @@
// 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

View File

@@ -1,8 +0,0 @@
/**
* 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()

407
src/main.test.ts Normal file
View File

@@ -0,0 +1,407 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
import { afterEach, expect, mock, spyOn, test } from "bun:test";
import * as core from "@actions/core";
import * as tc from "@actions/tool-cache";
const repo = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
const defaultEntrypoint = fileURLToPath(new URL("./main.ts", import.meta.url));
const CLI_CONFIG_REGISTRY = "SUPABASE_INTERNAL_IMAGE_REGISTRY";
const originalWorkspace = process.env.GITHUB_WORKSPACE;
const tempDirs = new Set<string>();
let mainModule: typeof import("./main.ts") | null = null;
afterEach(() => {
mock.restore();
process.env.GITHUB_WORKSPACE = originalWorkspace;
for (const dir of tempDirs) {
rmSync(dir, { force: true, recursive: true });
}
tempDirs.clear();
});
function createFakeCli(versionOutput: string): string {
const dir = mkdtempSync(path.join(os.tmpdir(), "setup-cli-"));
tempDirs.add(dir);
if (process.platform === "win32") {
writeFileSync(
path.join(dir, "supabase.cmd"),
versionOutput ? `@echo off\r\necho ${versionOutput}\r\n` : "@echo off\r\n",
);
return dir;
}
const escapedOutput = versionOutput.replaceAll("'", "'\"'\"'");
writeFileSync(
path.join(dir, "supabase"),
versionOutput
? `#!/usr/bin/env bash\nprintf '%s\\n' '${escapedOutput}'\n`
: "#!/usr/bin/env bash\n",
);
Bun.spawnSync(["chmod", "+x", path.join(dir, "supabase")]);
return dir;
}
function createWorkspace(files: Record<string, string>): string {
const dir = mkdtempSync(path.join(os.tmpdir(), "setup-cli-workspace-"));
tempDirs.add(dir);
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;
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}",
"",
{},
"sha512-test"
]`
: ""
}
}
}
`;
}
function createPnpmLock(
version: string,
options: { asString?: boolean; includeVersion?: boolean; 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: sha512-test
`;
}
function createPackageLock(version: string): string {
return JSON.stringify(
{
name: "app",
lockfileVersion: 3,
packages: {
"": {
dependencies: {
supabase: `^${version}`,
},
},
"node_modules/supabase": {
version,
},
},
},
null,
2,
);
}
function createActionSpies(inputVersion: string, cliDir: string, expectedUrlFragment: string) {
return {
getInput: spyOn(core, "getInput").mockReturnValue(inputVersion),
setOutput: spyOn(core, "setOutput").mockImplementation(() => {}),
addPath: spyOn(core, "addPath").mockImplementation(() => {}),
exportVariable: spyOn(core, "exportVariable").mockImplementation(() => {}),
setFailed: spyOn(core, "setFailed").mockImplementation(() => {}),
downloadTool: spyOn(tc, "downloadTool").mockImplementation(async (url: string) => {
expect(url).toContain(expectedUrlFragment);
return path.join(os.tmpdir(), "supabase-cli.tar.gz");
}),
extractTar: spyOn(tc, "extractTar").mockImplementation(async () => cliDir),
};
}
async function getMainModule(): Promise<typeof import("./main.ts")> {
if (!mainModule) {
mainModule = await import("./main.ts");
}
return mainModule;
}
async function waitForCalls(assertion: () => void): Promise<void> {
let failure: Error | null = null;
for (let attempt = 0; attempt < 50; attempt += 1) {
try {
assertion();
return;
} catch (error) {
failure = error instanceof Error ? error : new Error(String(error));
await Bun.sleep(10);
}
}
throw failure ?? new Error("Timed out waiting for action side effects");
}
test("runs the action entrypoint with omitted version and latest fallback", async () => {
process.env.GITHUB_WORKSPACE = repo;
const cliDir = createFakeCli("supabase 2.84.2");
const spies = createActionSpies("", cliDir, "/latest/download/");
const originalArgv1 = process.argv[1];
process.argv[1] = defaultEntrypoint;
try {
await import(`./main.ts?entrypoint=${Date.now()}`);
await waitForCalls(() => {
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.84.2");
expect(spies.addPath).toHaveBeenCalledWith(cliDir);
});
} finally {
process.argv[1] = originalArgv1 ?? "";
}
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("uses the root bun.lock version when version is omitted", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"bun.lock": createBunLock("2.41.0"),
});
const cliDir = createFakeCli("supabase 2.41.0");
const spies = createActionSpies("", cliDir, "/download/v2.41.0/supabase_");
const { run } = await getMainModule();
await run();
expect(spies.downloadTool).not.toHaveBeenCalledWith(expect.stringContaining("/latest/download/"));
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.41.0");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("uses the root pnpm-lock.yaml version when version is omitted", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"pnpm-lock.yaml": createPnpmLock("2.42.0"),
});
const cliDir = createFakeCli("supabase 2.42.0");
const spies = createActionSpies("", cliDir, "/download/v2.42.0/supabase_");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.42.0");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("uses the root package-lock.json version when version is omitted", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"package-lock.json": createPackageLock("2.43.0"),
});
const cliDir = createFakeCli("supabase 2.43.0");
const spies = createActionSpies("", cliDir, "/download/v2.43.0/supabase_");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.43.0");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
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 cliDir = createFakeCli("supabase 2.44.0");
const spies = createActionSpies("", cliDir, "/download/v2.44.0/supabase_");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.44.0");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
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 cliDir = createFakeCli("supabase 2.84.2");
const spies = createActionSpies("", cliDir, "/latest/download/");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.84.2");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("falls back to latest when version is omitted and no workspace is available", async () => {
delete process.env.GITHUB_WORKSPACE;
const cliDir = createFakeCli("supabase 2.84.2");
const spies = createActionSpies("", cliDir, "/latest/download/");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.84.2");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
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 cliDir = createFakeCli("supabase 2.44.1");
const spies = createActionSpies("", cliDir, "/download/v2.44.1/supabase_");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.44.1");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
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 cliDir = createFakeCli("supabase 2.47.0");
const spies = createActionSpies("", cliDir, "/download/v2.47.0/supabase_");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.47.0");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("falls through malformed pnpm lockfiles and uses the next supported root lockfile", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"pnpm-lock.yaml": "not: [valid",
"package-lock.json": createPackageLock("2.48.0"),
});
const cliDir = createFakeCli("supabase 2.48.0");
const spies = createActionSpies("", cliDir, "/download/v2.48.0/supabase_");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.48.0");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("falls through unreadable bun.lock paths and malformed package-lock files to latest", async () => {
const workspace = createWorkspace({
"package-lock.json": "{ invalid",
});
mkdirSync(path.join(workspace, "bun.lock"), { recursive: true });
process.env.GITHUB_WORKSPACE = workspace;
const cliDir = createFakeCli("supabase 2.84.2");
const spies = createActionSpies("", cliDir, "/latest/download/");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.84.2");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
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 cliDir = createFakeCli("supabase 2.84.2");
const spies = createActionSpies("", cliDir, "/latest/download/");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.84.2");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("explicit version overrides detected root lockfiles", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"bun.lock": createBunLock("2.45.0"),
});
const cliDir = createFakeCli("supabase 1.0.0");
const spies = createActionSpies("1.0.0", cliDir, "/download/v1.0.0/supabase_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 () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"package-lock.json": createPackageLock("2.46.0"),
});
const cliDir = createFakeCli("");
const spies = createActionSpies("", cliDir, "/download/v2.46.0/supabase_");
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,39 +1,208 @@
import * as core from '@actions/core' import { $, semver } from "bun";
import * as tc from '@actions/tool-cache' import * as core from "@actions/core";
import { gte } from 'semver' import * as tc from "@actions/tool-cache";
import { getDownloadUrl, determineInstalledVersion } from './utils.js' import { existsSync, readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
export const CLI_CONFIG_REGISTRY = 'SUPABASE_INTERNAL_IMAGE_REGISTRY' export const CLI_CONFIG_REGISTRY = "SUPABASE_INTERNAL_IMAGE_REGISTRY";
const REGISTRY_VERSION = "1.28.0";
const DEFAULT_VERSION = "latest";
/** type BunLock = {
* The main function for the action. workspaces?: {
* "": {
* @returns Resolves when the action is complete. dependencies?: Record<string, string>;
*/ devDependencies?: Record<string, string>;
export async function run(): Promise<void> { };
try { };
// Get version of tool to be installed packages?: Record<string, unknown>;
const version = core.getInput('version') };
// Download the specific version of the tool, e.g. as a tarball/zipball type PnpmDependency =
const download = await getDownloadUrl(version) | string
const pathToTarball = await tc.downloadTool(download) | {
version?: string;
};
// Extract the tarball/zipball onto host runner type PnpmLock = {
const pathToCLI = await tc.extractTar(pathToTarball) importers?: {
".": {
dependencies?: Record<string, PnpmDependency>;
devDependencies?: Record<string, PnpmDependency>;
};
};
};
// Expose the tool by adding it to the PATH type PackageLock = {
core.addPath(pathToCLI) packages?: Record<string, { version?: string }>;
dependencies?: Record<string, { version?: string }>;
};
// Expose installed tool version function getArchivePlatform(platform: NodeJS.Platform): string {
const determinedVersion = await determineInstalledVersion() return platform === "win32" ? "windows" : platform;
core.setOutput('version', determinedVersion) }
// Use GHCR mirror by default function getArchiveArch(arch: NodeJS.Architecture): string {
if (version.toLowerCase() === 'latest' || gte(version, '1.28.0')) { return arch === "x64" ? "amd64" : arch;
core.exportVariable(CLI_CONFIG_REGISTRY, 'ghcr.io') }
function extractConcreteVersion(raw: string | undefined): string | null {
if (!raw) {
return null;
} }
} catch (error) {
if (error instanceof Error) core.setFailed(error.message) const match = raw.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?/);
return match?.[0] ?? null;
}
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 detectVersionFromBunLock(workspaceRoot: string): string | 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?.supabase ?? rootWorkspace?.devDependencies?.supabase;
if (!declaredVersion) {
return null;
}
const resolvedPackage = lockfile.packages?.supabase;
if (Array.isArray(resolvedPackage) && typeof resolvedPackage[0] === "string") {
return extractConcreteVersion(resolvedPackage[0]);
}
return extractConcreteVersion(declaredVersion);
} catch {
return null;
}
}
function detectVersionFromPnpmLock(workspaceRoot: string): string | 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?.supabase ?? rootImporter?.devDependencies?.supabase;
if (typeof dependency === "string") {
return extractConcreteVersion(dependency);
}
return extractConcreteVersion(dependency?.version);
} catch {
return null;
}
}
function detectVersionFromPackageLock(workspaceRoot: string): string | null {
const text = readWorkspaceLockfile(workspaceRoot, "package-lock.json");
if (!text) {
return null;
}
try {
const lockfile = JSON.parse(text) as PackageLock;
return (
extractConcreteVersion(lockfile.packages?.["node_modules/supabase"]?.version) ??
extractConcreteVersion(lockfile.dependencies?.supabase?.version)
);
} catch {
return null;
}
}
function resolveVersion(inputVersion: string): string {
const requestedVersion = inputVersion.trim();
if (requestedVersion) {
return requestedVersion;
}
const workspaceRoot = process.env.GITHUB_WORKSPACE?.trim();
if (!workspaceRoot) {
return DEFAULT_VERSION;
}
return (
detectVersionFromBunLock(workspaceRoot) ??
detectVersionFromPnpmLock(workspaceRoot) ??
detectVersionFromPackageLock(workspaceRoot) ??
DEFAULT_VERSION
);
}
export function getDownloadUrl(version: string): string {
const platform = getArchivePlatform(process.platform);
const arch = getArchiveArch(process.arch);
const filename = `supabase_${platform}_${arch}.tar.gz`;
if (version.toLowerCase() === "latest") {
return `https://github.com/supabase/cli/releases/latest/download/${filename}`;
}
if (semver.order(version, REGISTRY_VERSION) === -1) {
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 async function determineInstalledVersion(cliPath: string): Promise<string> {
const version = (await $`${path.join(cliPath, "supabase")} --version`.text()).trim();
if (!version) {
throw new Error("Could not determine installed Supabase CLI version");
}
return version;
}
export async function run(): Promise<void> {
try {
const version = resolveVersion(core.getInput("version"));
const tarball = await tc.downloadTool(getDownloadUrl(version));
const cliPath = await tc.extractTar(tarball);
const installedVersion = await determineInstalledVersion(cliPath);
core.setOutput("version", installedVersion);
core.addPath(cliPath);
if (version.toLowerCase() === "latest" || semver.order(version, REGISTRY_VERSION) >= 0) {
core.exportVariable(CLI_CONFIG_REGISTRY, "ghcr.io");
}
} catch (error) {
core.setFailed(error instanceof Error ? error.message : String(error));
}
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
void run();
}

View File

@@ -1,48 +0,0 @@
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
}

View File

@@ -1,23 +0,0 @@
{
"$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"
}
}

View File

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