fix: authenticate latest release lookup (#430)

## Summary

- Add an optional `github-token` input to authenticate the GitHub
release lookup used by `version: latest`.
- Pass the token through the composite action as
`SUPABASE_CLI_GITHUB_TOKEN` and use it as a bearer token for the
`/repos/supabase/cli/releases/latest` request.
- Update this repository's CI smoke test and README examples to pass
`${{ github.token }}` when testing or using `latest`.

## Root Cause

CI failed in `test (macos-latest, latest)` because the action resolved
`latest` through an unauthenticated GitHub REST API request and hit the
low unauthenticated rate limit. The dependency bump in #429 was not the
cause; the validate job passed and the failure happened inside the
release lookup path.

## Impact

Pinned versions continue to work without a token. For `version: latest`,
callers can now pass `${{ github.token }}` to avoid unauthenticated API
rate limiting while keeping the input optional for backward
compatibility.

## Validation

- `bun run ci`
This commit is contained in:
Julien Goux
2026-05-20 15:06:17 +02:00
committed by GitHub
parent a4d563a017
commit 3095b000b6
5 changed files with 47 additions and 4 deletions

View File

@@ -10,13 +10,21 @@ 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 GITHUB_RELEASES_API = "https://api.github.com/repos/supabase/cli/releases/latest";
const GITHUB_TOKEN_ENV = "SUPABASE_CLI_GITHUB_TOKEN";
const originalWorkspace = process.env.GITHUB_WORKSPACE;
const originalGithubToken = process.env[GITHUB_TOKEN_ENV];
const tempDirs = new Set<string>();
let mainModule: typeof import("./main.ts") | null = null;
afterEach(() => {
mock.restore();
process.env.GITHUB_WORKSPACE = originalWorkspace;
if (originalGithubToken === undefined) {
delete process.env[GITHUB_TOKEN_ENV];
} else {
process.env[GITHUB_TOKEN_ENV] = originalGithubToken;
}
for (const dir of tempDirs) {
rmSync(dir, { force: true, recursive: true });
@@ -222,6 +230,22 @@ test("resolves latest before choosing a versioned Supabase CLI archive", async (
});
});
test("authenticates latest release lookup when a GitHub token is provided", async () => {
process.env[GITHUB_TOKEN_ENV] = "ghs_test-token";
const fetch = mockLatestRelease("v2.99.0");
const { getDownloadArchive } = await getMainModule();
await getDownloadArchive("latest", "darwin", "arm64");
expect(fetch).toHaveBeenCalledWith(GITHUB_RELEASES_API, {
headers: expect.objectContaining({
Accept: "application/vnd.github+json",
Authorization: "Bearer ghs_test-token",
"X-GitHub-Api-Version": "2022-11-28",
}),
});
});
test("awaits the action entrypoint with omitted version and latest fallback", async () => {
process.env.GITHUB_WORKSPACE = repo;
mockLatestRelease();

View File

@@ -10,6 +10,7 @@ const REGISTRY_VERSION = "1.28.0";
const VERSIONED_ARCHIVE_VERSION = "2.99.0";
const DEFAULT_VERSION = "latest";
const GITHUB_RELEASES_API = "https://api.github.com/repos/supabase/cli/releases/latest";
const GITHUB_TOKEN_ENV = "SUPABASE_CLI_GITHUB_TOKEN";
type ArchiveFormat = "tar" | "zip";
@@ -175,7 +176,17 @@ function resolveVersion(inputVersion: string): string {
}
async function resolveLatestVersion(): Promise<string> {
const response = await fetch(GITHUB_RELEASES_API);
const headers: Record<string, string> = {
Accept: "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
};
const githubToken = process.env[GITHUB_TOKEN_ENV]?.trim();
if (githubToken) {
headers.Authorization = `Bearer ${githubToken}`;
}
const response = await fetch(GITHUB_RELEASES_API, { headers });
if (!response.ok) {
throw new Error(`Failed to resolve latest Supabase CLI release: ${response.statusText}`);
}