Compare commits

..

3 Commits

Author SHA1 Message Date
Julien Goux
1dedf2c611 ci: skip unsupported latest CLI and Postgres 14 combination (#454)
The v1.7.2 release E2E run fails when the latest CLI starts PostgreSQL
14: the current Storage migration uses SQL unsupported by PostgreSQL 14.
This combination is already excluded from the v2 and v3 action matrices.

Exclude only `latest` with PostgreSQL 14 from the v1 release matrix.
Pinned CLI versions continue testing PostgreSQL 14, and the latest CLI
continues testing PostgreSQL 15 and 17.
2026-09-24 00:30:29 +02:00
Julien Goux
8b97e5256c fix: allow registry fallback in supported CLI versions
The action currently pins every recent CLI to GHCR, which disables the
CLI's registry fallback and leaves image pulls exposed to GHCR
throttling.

Use the installed CLI version to keep the GHCR default for versions
before v2.108.0 and allow fallback for newer versions. Preserve a
caller's explicit registry choice and update the bundled v1 action.
2026-09-24 00:17:25 +02:00
Julien Goux
ab058987d8 fix: install Alpine runtime dependencies (#434)
## Summary
- Install `libstdc++` and `libgcc` before verifying CLI versions from
apk archives
- Keep non-apk archive installs unchanged
- Rebuild the v1 bundled action artifact

## Testing
- `npm run package`
- `npm run format:check`
- `npm run lint`
- `npm run test`
- Verified `supabase_2.100.0_linux_arm64.apk` fails on plain Alpine
without `libstdc++`/`libgcc` and reports `2.100.0` after installing them
2026-05-21 09:31:24 +02:00
6 changed files with 98 additions and 38 deletions

View File

@@ -33,6 +33,8 @@ jobs:
exclude:
- version: 1.178.2
pg_major: 17
- version: latest
pg_major: 14
steps:
- uses: actions/checkout@v4
- uses: ./

View File

@@ -1,18 +1,21 @@
import { getCliPath, getDownloadArchive, 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 { shouldPinGhcrRegistry } from '../src/main'
import { afterEach, expect, jest, test } from '@jest/globals'
afterEach(() => {
jest.restoreAllMocks()
})
test('pins GHCR for legacy CLI versions until registry fallback support', () => {
expect(shouldPinGhcrRegistry('1.28.0', undefined)).toBe(true)
expect(shouldPinGhcrRegistry('2.107.0', undefined)).toBe(true)
expect(shouldPinGhcrRegistry('2.108.0', undefined)).toBe(false)
})
test('preserves a configured image registry', () => {
expect(shouldPinGhcrRegistry('2.107.0', 'registry.example.test')).toBe(false)
})
test('gets download url to binary', async () => {
const url = await getDownloadUrl('1.28.0')
expect(
@@ -138,26 +141,3 @@ test('keeps unversioned archive url to binary before Supabase CLI v2.99.0', asyn
expect(url).not.toContain('supabase_2.98.2_')
expect(url).toMatch(/\.tar\.gz$/)
})
// 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)
})

33
dist/index.js generated vendored
View File

@@ -60534,6 +60534,29 @@ const getDownloadArchive = async (version, platform = os__default.platform(), ar
const getCliPath = (extractedPath, archiveFormat) => {
return archiveFormat === 'apk' ? `${extractedPath}/usr/bin` : extractedPath;
};
const installAlpineRuntimeDependencies = async (archiveFormat) => {
if (archiveFormat !== 'apk') {
return;
}
try {
await doExec('command -v apk');
}
catch {
throw new Error('Linux musl containers need libstdc++ and libgcc to run Supabase CLI. Install them before supabase/setup-cli.');
}
try {
await doExec('apk info -e libstdc++ libgcc');
return;
}
catch {
const { stdout } = await doExec('id -u');
if (stdout.trim() !== '0') {
throw new Error("Alpine/musl containers need libstdc++ and libgcc to run Supabase CLI. Add 'apk add --no-cache libstdc++ libgcc' before supabase/setup-cli, or run this job container as root.");
}
}
// The Supabase CLI shim in the apk dynamically links these Alpine runtime libraries.
await doExec('apk add --no-cache libstdc++ libgcc');
};
const determineInstalledVersion = async () => {
const { stdout } = await doExec('supabase --version');
const version = stdout.trim();
@@ -60544,6 +60567,11 @@ const determineInstalledVersion = async () => {
};
const CLI_CONFIG_REGISTRY = 'SUPABASE_INTERNAL_IMAGE_REGISTRY';
const REGISTRY_VERSION = '1.28.0';
const FALLBACK_VERSION = '2.108.0';
const shouldPinGhcrRegistry = (installedVersion, configuredRegistry) => !configuredRegistry &&
semverExports.gte(installedVersion, REGISTRY_VERSION) &&
semverExports.lt(installedVersion, FALLBACK_VERSION);
/**
* The main function for the action.
*
@@ -60562,13 +60590,14 @@ async function run() {
? await extractZip(pathToArchive)
: await extractTar(pathToArchive);
const pathToCLI = getCliPath(extractedPath, download.format);
await installAlpineRuntimeDependencies(download.format);
// Expose the tool by adding it to the PATH
addPath(pathToCLI);
// Expose installed tool version
const determinedVersion = await determineInstalledVersion();
setOutput('version', determinedVersion);
// Use GHCR mirror by default
if (version.toLowerCase() === 'latest' || semverExports.gte(version, '1.28.0')) {
// Use GHCR for CLI versions without registry fallback support.
if (shouldPinGhcrRegistry(determinedVersion.replace(/^supabase\s+/i, '').replace(/^v/i, ''), process.env[CLI_CONFIG_REGISTRY])) {
exportVariable(CLI_CONFIG_REGISTRY, 'ghcr.io');
}
}

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,13 +1,24 @@
import * as core from '@actions/core'
import * as tc from '@actions/tool-cache'
import { gte } from 'semver'
import { gte, lt } from 'semver'
import {
getDownloadArchive,
determineInstalledVersion,
getCliPath
getCliPath,
installAlpineRuntimeDependencies
} from './utils.js'
export const CLI_CONFIG_REGISTRY = 'SUPABASE_INTERNAL_IMAGE_REGISTRY'
const REGISTRY_VERSION = '1.28.0'
const FALLBACK_VERSION = '2.108.0'
export const shouldPinGhcrRegistry = (
installedVersion: string,
configuredRegistry: string | undefined
): boolean =>
!configuredRegistry &&
gte(installedVersion, REGISTRY_VERSION) &&
lt(installedVersion, FALLBACK_VERSION)
/**
* The main function for the action.
@@ -37,6 +48,8 @@ export async function run(): Promise<void> {
: await tc.extractTar(pathToArchive)
const pathToCLI = getCliPath(extractedPath, download.format)
await installAlpineRuntimeDependencies(download.format)
// Expose the tool by adding it to the PATH
core.addPath(pathToCLI)
@@ -44,8 +57,13 @@ export async function run(): Promise<void> {
const determinedVersion = await determineInstalledVersion()
core.setOutput('version', determinedVersion)
// Use GHCR mirror by default
if (version.toLowerCase() === 'latest' || gte(version, '1.28.0')) {
// Use GHCR for CLI versions without registry fallback support.
if (
shouldPinGhcrRegistry(
determinedVersion.replace(/^supabase\s+/i, '').replace(/^v/i, ''),
process.env[CLI_CONFIG_REGISTRY]
)
) {
core.exportVariable(CLI_CONFIG_REGISTRY, 'ghcr.io')
}
} catch (error) {

View File

@@ -157,6 +157,37 @@ export const getCliPath = (
return archiveFormat === 'apk' ? `${extractedPath}/usr/bin` : extractedPath
}
export const installAlpineRuntimeDependencies = async (
archiveFormat: ArchiveFormat
): Promise<void> => {
if (archiveFormat !== 'apk') {
return
}
try {
await doExec('command -v apk')
} catch {
throw new Error(
'Linux musl containers need libstdc++ and libgcc to run Supabase CLI. Install them before supabase/setup-cli.'
)
}
try {
await doExec('apk info -e libstdc++ libgcc')
return
} catch {
const { stdout } = await doExec('id -u')
if (stdout.trim() !== '0') {
throw new Error(
"Alpine/musl containers need libstdc++ and libgcc to run Supabase CLI. Add 'apk add --no-cache libstdc++ libgcc' before supabase/setup-cli, or run this job container as root."
)
}
}
// The Supabase CLI shim in the apk dynamically links these Alpine runtime libraries.
await doExec('apk add --no-cache libstdc++ libgcc')
}
export const getDownloadUrl = async (
version: string,
githubToken?: string