Compare commits

...

1 Commits

Author SHA1 Message Date
Julien Goux
ad077b4817 fix: v1 setup on Linux musl (#432)
## Summary

- Detect Linux musl runners in the v1 action and download the Supabase
CLI `.apk` asset for CLI versions `>= 2.99.0`.
- Add the extracted `usr/bin` directory to `PATH` for `.apk` archives.
- Backport the optional `github-token` input for authenticated `latest`
release lookup, because the test matrix hit unauthenticated GitHub API
rate limits.
- Rebuild `dist/index.js` for the Node action.

## Validation

- `npm run format:check`
- `npm run lint`
- `npm test`
- `npm run package`
- Local Docker smoke test in `node:20-alpine` with
`INPUT_VERSION=2.100.1`
- setup-cli-testing workflow:
https://github.com/jgoux/setup-cli-testing/actions/runs/26165593808

The external workflow passed Alpine `2.100.1`, Alpine `latest`, and
Ubuntu/macOS/Windows with both `2.100.1` and `latest`.
2026-05-20 16:22:46 +02:00
22 changed files with 43754 additions and 15606 deletions

View File

@@ -1,7 +1,6 @@
name: CodeQL name: CodeQL
on: on:
pull_request:
push: push:
branches: branches:
- main - main

View File

@@ -48,9 +48,11 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LINTER_RULES_PATH: ${{ github.workspace }} LINTER_RULES_PATH: ${{ github.workspace }}
VALIDATE_ALL_CODEBASE: true VALIDATE_ALL_CODEBASE: true
VALIDATE_BIOME_FORMAT: false
VALIDATE_JAVASCRIPT_ES: false VALIDATE_JAVASCRIPT_ES: false
VALIDATE_JAVASCRIPT_STANDARD: false VALIDATE_JAVASCRIPT_STANDARD: false
VALIDATE_JSCPD: false VALIDATE_JSCPD: false
VALIDATE_TYPESCRIPT_ES: false VALIDATE_TYPESCRIPT_ES: false
VALIDATE_JSON: false VALIDATE_JSON: false
VALIDATE_GITHUB_ACTIONS_ZIZMOR: false
VALIDATE_TYPESCRIPT_STANDARD: false VALIDATE_TYPESCRIPT_STANDARD: false

View File

@@ -38,6 +38,7 @@ jobs:
- uses: ./ - uses: ./
with: with:
version: ${{ matrix.version }} version: ${{ matrix.version }}
github-token: ${{ github.token }}
- run: supabase init - run: supabase init
- run: - run:
sed -i -E "s|^(major_version) .*|\1 = ${{ matrix.pg_major }}|" sed -i -E "s|^(major_version) .*|\1 = ${{ matrix.pg_major }}|"

View File

@@ -56,6 +56,7 @@ jobs:
- uses: ./ - uses: ./
with: with:
version: ${{ matrix.version }} version: ${{ matrix.version }}
github-token: ${{ github.token }}
- run: supabase -h - run: supabase -h
check: check:

View File

@@ -1,6 +1,6 @@
--- ---
name: "@actions/core" name: "@actions/core"
version: 1.11.1 version: 3.0.1
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

View File

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

View File

@@ -1,6 +1,6 @@
--- ---
name: "@actions/http-client" name: "@actions/http-client"
version: 2.2.3 version: 4.0.1
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

View File

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

View File

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

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: 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,6 +1,6 @@
--- ---
name: semver name: semver
version: 6.3.1 version: 7.8.0
type: npm type: npm
summary: The semantic version parser used by npm. summary: The semantic version parser used by npm.
homepage: homepage:

View File

@@ -1,6 +1,6 @@
--- ---
name: undici name: undici
version: 5.29.0 version: 6.25.0
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

View File

@@ -44,6 +44,7 @@ steps:
- uses: supabase/setup-cli@v1 - uses: supabase/setup-cli@v1
with: with:
version: latest version: latest
github-token: ${{ github.token }}
- run: supabase init - run: supabase init
- run: supabase db start - run: supabase db start
``` ```
@@ -55,9 +56,10 @@ on Windows and macOS runners.
The actions supports the following inputs: The actions 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`) | `2.20.3` | false |
| `github-token` | String | GitHub token used to resolve `latest` without unauthenticated API limiting | | false |
## Advanced Usage ## Advanced Usage
@@ -135,8 +137,8 @@ need to perform some initial setup steps before you can develop your action.
## Publish to a distribution branch ## Publish to a distribution branch
Actions are run from this GitHub repository so we will checkin the packed `dist` Actions are run from this GitHub repository so we will check in the packed
folder. `dist` folder.
1. Create a new GitHub release 1. Create a new GitHub release
2. Rebase `v1` branch on `main` 2. Rebase `v1` branch on `main`
@@ -155,6 +157,7 @@ repository (see [test.yml](.github/workflows/test.yml))
uses: ./ uses: ./
with: with:
version: latest version: latest
github-token: ${{ github.token }}
``` ```
See the [actions tab](https://github.com/supabase/setup-cli/actions) for runs of See the [actions tab](https://github.com/supabase/setup-cli/actions) for runs of

View File

@@ -1,4 +1,4 @@
import { getDownloadArchive, getDownloadUrl } from '../src/utils' import { getCliPath, getDownloadArchive, getDownloadUrl } from '../src/utils'
import { CLI_CONFIG_REGISTRY } from '../src/main' import { CLI_CONFIG_REGISTRY } from '../src/main'
import * as os from 'os' import * as os from 'os'
import * as process from 'process' import * as process from 'process'
@@ -47,6 +47,48 @@ test('gets download url to latest version', async () => {
expect(url).toMatch(/\.tar\.gz$|\.zip$/) expect(url).toMatch(/\.tar\.gz$|\.zip$/)
}) })
test('authenticates latest version lookup when a GitHub token is provided', async () => {
const fetchMock = jest.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(JSON.stringify({ tag_name: 'v2.99.0' }), {
status: 200,
statusText: 'OK'
})
)
await getDownloadUrl('latest', 'github-token')
expect(fetchMock).toHaveBeenCalledWith(
'https://api.github.com/repos/supabase/cli/releases/latest',
expect.objectContaining({
headers: expect.objectContaining({
Accept: 'application/vnd.github+json',
Authorization: 'Bearer github-token',
'X-GitHub-Api-Version': '2022-11-28'
})
})
)
})
test('omits authorization from latest version lookup without a GitHub token', async () => {
const fetchMock = jest.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(JSON.stringify({ tag_name: 'v2.99.0' }), {
status: 200,
statusText: 'OK'
})
)
await getDownloadUrl('latest')
expect(fetchMock).toHaveBeenCalledWith(
'https://api.github.com/repos/supabase/cli/releases/latest',
expect.objectContaining({
headers: expect.not.objectContaining({
Authorization: expect.any(String)
})
})
)
})
test('gets versioned archive url to binary from Supabase CLI v2.99.0', async () => { test('gets versioned archive url to binary from Supabase CLI v2.99.0', async () => {
const archive = await getDownloadArchive('2.99.0', 'linux', 'x64') const archive = await getDownloadArchive('2.99.0', 'linux', 'x64')
@@ -56,6 +98,30 @@ test('gets versioned archive url to binary from Supabase CLI v2.99.0', async ()
}) })
}) })
test('gets apk archive url on Linux musl from Supabase CLI v2.99.0', async () => {
const archive = await getDownloadArchive('2.99.0', 'linux', 'x64', true)
expect(archive).toEqual({
url: 'https://github.com/supabase/cli/releases/download/v2.99.0/supabase_2.99.0_linux_amd64.apk',
format: 'apk'
})
})
test('keeps tar archives before Supabase CLI v2.99.0 on Linux musl', async () => {
const archive = await getDownloadArchive('2.98.2', 'linux', 'x64', true)
expect(archive).toEqual({
url: 'https://github.com/supabase/cli/releases/download/v2.98.2/supabase_linux_amd64.tar.gz',
format: 'tar'
})
})
test('uses usr/bin as the CLI path for apk archives', () => {
expect(getCliPath('/tmp/extracted', 'apk')).toBe('/tmp/extracted/usr/bin')
expect(getCliPath('/tmp/extracted', 'tar')).toBe('/tmp/extracted')
expect(getCliPath('/tmp/extracted', 'zip')).toBe('/tmp/extracted')
})
test('gets versioned zip archive url on Windows from Supabase CLI v2.99.0', async () => { test('gets versioned zip archive url on Windows from Supabase CLI v2.99.0', async () => {
const archive = await getDownloadArchive('2.99.0', 'win32', 'x64') const archive = await getDownloadArchive('2.99.0', 'win32', 'x64')

View File

@@ -6,6 +6,11 @@ inputs:
description: Version of Supabase CLI to install description: Version of Supabase CLI to install
required: false required: false
default: 2.20.3 default: 2.20.3
github-token:
description:
GitHub token used to resolve the latest Supabase CLI release without
hitting unauthenticated API limits.
required: false
outputs: outputs:
version: version:
description: Version of installed Supabase CLI description: Version of installed Supabase CLI

58948
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

136
package-lock.json generated
View File

@@ -9,8 +9,8 @@
"version": "1.6.0", "version": "1.6.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^3.0.1",
"@actions/tool-cache": "^2.0.2", "@actions/tool-cache": "^4.0.0",
"semver": "^7.7.2" "semver": "^7.7.2"
}, },
"devDependencies": { "devDependencies": {
@@ -70,6 +70,17 @@
"unzip-stream": "^0.3.1" "unzip-stream": "^0.3.1"
} }
}, },
"node_modules/@actions/artifact/node_modules/@actions/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1"
}
},
"node_modules/@actions/artifact/node_modules/@actions/github": { "node_modules/@actions/artifact/node_modules/@actions/github": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
@@ -252,19 +263,54 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/@actions/core": { "node_modules/@actions/core": {
"version": "1.11.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-3.0.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", "integrity": "sha512-a6d/Nwahm9fliVGRhdhofo40HjHQasUPusmc7vBfyky+7Z+P2A1J68zyFVaNcEclc/Se+eO595oAr5nwEIoIUA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/exec": "^1.1.1", "@actions/exec": "^3.0.0",
"@actions/http-client": "^2.0.1" "@actions/http-client": "^4.0.0"
}
},
"node_modules/@actions/core/node_modules/@actions/exec": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-3.0.0.tgz",
"integrity": "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==",
"license": "MIT",
"dependencies": {
"@actions/io": "^3.0.2"
}
},
"node_modules/@actions/core/node_modules/@actions/http-client": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.1.tgz",
"integrity": "sha512-+Nvd1ImaOZBSoPbsUtEhv+1z99H12xzncCkz0a3RuehINE81FZSe2QTj3uvAPTcJX/SCzUQHQ0D1GrPMbrPitg==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^6.23.0"
}
},
"node_modules/@actions/core/node_modules/@actions/io": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz",
"integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==",
"license": "MIT"
},
"node_modules/@actions/core/node_modules/undici": {
"version": "6.25.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz",
"integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
"license": "MIT",
"engines": {
"node": ">=18.17"
} }
}, },
"node_modules/@actions/exec": { "node_modules/@actions/exec": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/io": "^1.0.1" "@actions/io": "^1.0.1"
@@ -431,6 +477,7 @@
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tunnel": "^0.0.6", "tunnel": "^0.0.6",
@@ -441,28 +488,54 @@
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==", "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@actions/tool-cache": { "node_modules/@actions/tool-cache": {
"version": "2.0.2", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-4.0.0.tgz",
"integrity": "sha512-fBhNNOWxuoLxztQebpOaWu6WeVmuwa77Z+DxIZ1B+OYvGkGQon6kTVg6Z32Cb13WCuw0szqonK+hh03mJV7Z6w==", "integrity": "sha512-L8P9HbXvpvqjZDveb/fdsa55IVC0trfPgQ4ZwGo6r5af6YDVdM9vMGPZ7rgY2fAT9gGj4PSYd6bYlg3p3jD78A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^3.0.0",
"@actions/exec": "^1.0.0", "@actions/exec": "^3.0.0",
"@actions/http-client": "^2.0.1", "@actions/http-client": "^4.0.0",
"@actions/io": "^1.1.1", "@actions/io": "^3.0.0",
"semver": "^6.1.0" "semver": "^7.7.3"
} }
}, },
"node_modules/@actions/tool-cache/node_modules/semver": { "node_modules/@actions/tool-cache/node_modules/@actions/exec": {
"version": "6.3.1", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-3.0.0.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "integrity": "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==",
"license": "ISC", "license": "MIT",
"bin": { "dependencies": {
"semver": "bin/semver.js" "@actions/io": "^3.0.2"
}
},
"node_modules/@actions/tool-cache/node_modules/@actions/http-client": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.1.tgz",
"integrity": "sha512-+Nvd1ImaOZBSoPbsUtEhv+1z99H12xzncCkz0a3RuehINE81FZSe2QTj3uvAPTcJX/SCzUQHQ0D1GrPMbrPitg==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^6.23.0"
}
},
"node_modules/@actions/tool-cache/node_modules/@actions/io": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz",
"integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==",
"license": "MIT"
},
"node_modules/@actions/tool-cache/node_modules/undici": {
"version": "6.25.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz",
"integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
"license": "MIT",
"engines": {
"node": ">=18.17"
} }
}, },
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
@@ -1903,6 +1976,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=14" "node": ">=14"
@@ -1948,6 +2022,17 @@
"node": "^20 || ^22" "node": "^20 || ^22"
} }
}, },
"node_modules/@github/local-action/node_modules/@actions/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1"
}
},
"node_modules/@github/local-action/node_modules/undici": { "node_modules/@github/local-action/node_modules/undici": {
"version": "7.12.0", "version": "7.12.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz",
@@ -11316,9 +11401,9 @@
} }
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.2", "version": "7.8.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@@ -12596,6 +12681,7 @@
"version": "5.29.0", "version": "5.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/busboy": "^2.0.0" "@fastify/busboy": "^2.0.0"

View File

@@ -33,8 +33,8 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^3.0.1",
"@actions/tool-cache": "^2.0.2", "@actions/tool-cache": "^4.0.0",
"semver": "^7.7.2" "semver": "^7.7.2"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -1,7 +1,11 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as tc from '@actions/tool-cache' import * as tc from '@actions/tool-cache'
import { gte } from 'semver' import { gte } from 'semver'
import { getDownloadArchive, determineInstalledVersion } from './utils.js' import {
getDownloadArchive,
determineInstalledVersion,
getCliPath
} from './utils.js'
export const CLI_CONFIG_REGISTRY = 'SUPABASE_INTERNAL_IMAGE_REGISTRY' export const CLI_CONFIG_REGISTRY = 'SUPABASE_INTERNAL_IMAGE_REGISTRY'
@@ -14,16 +18,24 @@ export async function run(): Promise<void> {
try { try {
// Get version of tool to be installed // Get version of tool to be installed
const version = core.getInput('version') const version = core.getInput('version')
const githubToken = core.getInput('github-token')
// Download the specific version of the tool, e.g. as a tarball/zipball // Download the specific version of the tool, e.g. as a tarball/zipball
const download = await getDownloadArchive(version) const download = await getDownloadArchive(
version,
undefined,
undefined,
undefined,
githubToken
)
const pathToArchive = await tc.downloadTool(download.url) const pathToArchive = await tc.downloadTool(download.url)
// Extract the tarball/zipball onto host runner // Extract the tarball/zipball onto host runner
const pathToCLI = const extractedPath =
download.format === 'zip' download.format === 'zip'
? await tc.extractZip(pathToArchive) ? await tc.extractZip(pathToArchive)
: await tc.extractTar(pathToArchive) : await tc.extractTar(pathToArchive)
const pathToCLI = getCliPath(extractedPath, download.format)
// Expose the tool by adding it to the PATH // Expose the tool by adding it to the PATH
core.addPath(pathToCLI) core.addPath(pathToCLI)

View File

@@ -1,4 +1,5 @@
import { exec } from 'child_process' import { exec } from 'child_process'
import { existsSync } from 'fs'
import os from 'os' import os from 'os'
import { gte, lt } from 'semver' import { gte, lt } from 'semver'
import { promisify } from 'util' import { promisify } from 'util'
@@ -8,7 +9,7 @@ const VERSIONED_ARCHIVE_VERSION = '2.99.0'
const LATEST_RELEASE_URL = const LATEST_RELEASE_URL =
'https://api.github.com/repos/supabase/cli/releases/latest' 'https://api.github.com/repos/supabase/cli/releases/latest'
export type ArchiveFormat = 'tar' | 'zip' export type ArchiveFormat = 'apk' | 'tar' | 'zip'
export type DownloadArchive = { export type DownloadArchive = {
url: string url: string
@@ -35,8 +36,18 @@ const mapOS = (platform: string): string => {
const normalizeVersion = (version: string): string => version.replace(/^v/i, '') const normalizeVersion = (version: string): string => version.replace(/^v/i, '')
const resolveLatestVersion = async (): Promise<string> => { const resolveLatestVersion = async (githubToken?: string): Promise<string> => {
const response = await fetch(LATEST_RELEASE_URL) const headers: Record<string, string> = {
Accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28'
}
const token = githubToken?.trim()
if (token) {
headers.Authorization = `Bearer ${token}`
}
const response = await fetch(LATEST_RELEASE_URL, { headers })
if (!response.ok) { if (!response.ok) {
throw new Error( throw new Error(
`Failed to resolve latest Supabase CLI release: ${response.statusText}` `Failed to resolve latest Supabase CLI release: ${response.statusText}`
@@ -53,7 +64,37 @@ const resolveLatestVersion = async (): Promise<string> => {
return normalizeVersion(release.tag_name) return normalizeVersion(release.tag_name)
} }
const getArchiveFormat = (version: string, platform: string): ArchiveFormat => { const detectMuslLinux = async (platform = os.platform()): Promise<boolean> => {
if (platform !== 'linux') {
return false
}
if (existsSync('/etc/alpine-release')) {
return true
}
try {
const { stdout, stderr } = await doExec('ldd --version')
return `${stdout}\n${stderr}`.toLowerCase().includes('musl')
} catch (error) {
const output = error instanceof Error ? error.message : String(error)
return output.toLowerCase().includes('musl')
}
}
const getArchiveFormat = (
version: string,
platform: string,
isMuslLinux: boolean
): ArchiveFormat => {
if (
platform === 'linux' &&
isMuslLinux &&
gte(version, VERSIONED_ARCHIVE_VERSION)
) {
return 'apk'
}
if (platform === 'win32' && gte(version, VERSIONED_ARCHIVE_VERSION)) { if (platform === 'win32' && gte(version, VERSIONED_ARCHIVE_VERSION)) {
return 'zip' return 'zip'
} }
@@ -64,7 +105,8 @@ const getArchiveFormat = (version: string, platform: string): ArchiveFormat => {
const getArchiveFilename = ( const getArchiveFilename = (
version: string, version: string,
platform: string, platform: string,
arch: string arch: string,
format: ArchiveFormat
): string => { ): string => {
const archivePlatform = mapOS(platform) const archivePlatform = mapOS(platform)
const archiveArch = mapArch(arch) const archiveArch = mapArch(arch)
@@ -72,6 +114,10 @@ const getArchiveFilename = (
return `supabase_${version}_${archivePlatform}_${archiveArch}.tar.gz` return `supabase_${version}_${archivePlatform}_${archiveArch}.tar.gz`
} }
if (platform === 'linux' && format === 'apk') {
return `supabase_${version}_${archivePlatform}_${archiveArch}.apk`
}
if (gte(version, VERSIONED_ARCHIVE_VERSION)) { if (gte(version, VERSIONED_ARCHIVE_VERSION)) {
const extension = platform === 'win32' ? 'zip' : 'tar.gz' const extension = platform === 'win32' ? 'zip' : 'tar.gz'
return `supabase_${version}_${archivePlatform}_${archiveArch}.${extension}` return `supabase_${version}_${archivePlatform}_${archiveArch}.${extension}`
@@ -83,22 +129,45 @@ const getArchiveFilename = (
export const getDownloadArchive = async ( export const getDownloadArchive = async (
version: string, version: string,
platform = os.platform(), platform = os.platform(),
arch = os.arch() arch = os.arch(),
isMuslLinux?: boolean,
githubToken?: string
): Promise<DownloadArchive> => { ): Promise<DownloadArchive> => {
const resolvedVersion = const resolvedVersion =
version.toLowerCase() === 'latest' version.toLowerCase() === 'latest'
? await resolveLatestVersion() ? await resolveLatestVersion(githubToken)
: normalizeVersion(version) : normalizeVersion(version)
const filename = getArchiveFilename(resolvedVersion, platform, arch) const format = getArchiveFormat(
resolvedVersion,
platform,
isMuslLinux ?? (await detectMuslLinux(platform))
)
const filename = getArchiveFilename(resolvedVersion, platform, arch, format)
return { return {
url: `https://github.com/supabase/cli/releases/download/v${resolvedVersion}/${filename}`, url: `https://github.com/supabase/cli/releases/download/v${resolvedVersion}/${filename}`,
format: getArchiveFormat(resolvedVersion, platform) format
} }
} }
export const getDownloadUrl = async (version: string): Promise<string> => { export const getCliPath = (
const archive = await getDownloadArchive(version) extractedPath: string,
archiveFormat: ArchiveFormat
): string => {
return archiveFormat === 'apk' ? `${extractedPath}/usr/bin` : extractedPath
}
export const getDownloadUrl = async (
version: string,
githubToken?: string
): Promise<string> => {
const archive = await getDownloadArchive(
version,
os.platform(),
os.arch(),
undefined,
githubToken
)
return archive.url return archive.url
} }