mirror of
https://github.com/goreleaser/goreleaser-action
synced 2026-06-29 21:29:42 +00:00
feat: resolve nightly to latest vX.Y.Z-<sha>-nightly release (#558)
* feat: resolve nightly to latest vX.Y.Z-<sha>-nightly release
Query GitHub releases API to resolve the 'nightly' version input to the
latest immutable nightly tag, replacing the moving 'nightly' tag that is
being removed for supply-chain hardening.
Refs goreleaser/goreleaser#6550
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: keep legacy 'nightly' tag working during transition
Fall back to the moving 'nightly' tag when no immutable
vX.Y.Z-<sha>-nightly release is found, so the action keeps working
between this release and the goreleaser nightly switchover.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: assert isNightlyTag accepts legacy fallback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: accept nightly tags without 'v' prefix
goreleaser-pro publishes nightly releases as e.g. 2.16.0-eaeb08c50-nightly
(no 'v' prefix). Make the nightly tag regex tolerate either form, and
split the integration tests so OSS asserts the legacy fallback while
Pro asserts the new <version>-<sha>-nightly format.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert "fix: accept nightly tags without 'v' prefix"
The missing 'v' prefix on the goreleaser-pro nightly was a release
mistake; new nightlies will keep the 'v' prefix.
This reverts commit 7673f7f.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ci: pass GITHUB_TOKEN to tests
The new nightly resolution hits api.github.com/repos/.../releases,
which is rate-limited for unauthenticated requests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: note GITHUB_TOKEN need for nightly resolution
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
4f96abf297
commit
4c6ab561ad
@@ -39,6 +39,8 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Test
|
name: Test
|
||||||
run: npm test
|
run: npm test
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
-
|
-
|
||||||
name: Upload coverage
|
name: Upload coverage
|
||||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
|
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
|
||||||
|
|||||||
@@ -96,6 +96,11 @@ checksums file against the GoReleaser release workflow's OIDC identity. If
|
|||||||
> versions the cosign step is silently skipped — only the `checksums.txt`
|
> versions the cosign step is silently skipped — only the `checksums.txt`
|
||||||
> SHA-256 verification runs.
|
> SHA-256 verification runs.
|
||||||
|
|
||||||
|
> **Note**: when `version: nightly` is used, the action resolves the
|
||||||
|
> latest immutable `vX.Y.Z-<sha>-nightly` release from the GitHub
|
||||||
|
> Releases API. Pass `GITHUB_TOKEN` to the action step (as in the example
|
||||||
|
> above) to avoid unauthenticated API rate limits.
|
||||||
|
|
||||||
To enable signature verification, install cosign before running the action:
|
To enable signature verification, install cosign before running the action:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -56,16 +56,18 @@ describe('getRelease', () => {
|
|||||||
expect(release?.tag_name).not.toEqual('');
|
expect(release?.tag_name).not.toEqual('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns nightly GoReleaser GitHub release', async () => {
|
it('resolves nightly to the legacy nightly tag for OSS GoReleaser', async () => {
|
||||||
|
// No <version>-<sha>-nightly release exists in goreleaser/goreleaser yet,
|
||||||
|
// so this should fall back to the legacy moving `nightly` tag.
|
||||||
const release = await github.getRelease('goreleaser', 'nightly');
|
const release = await github.getRelease('goreleaser', 'nightly');
|
||||||
expect(release).not.toBeNull();
|
expect(release).not.toBeNull();
|
||||||
expect(release?.tag_name).not.toEqual('');
|
expect(release.tag_name).toEqual('nightly');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns nightly GoReleaser Pro GitHub release', async () => {
|
it('resolves nightly to a <version>-<sha>-nightly release for GoReleaser Pro', async () => {
|
||||||
const release = await github.getRelease('goreleaser-pro', 'nightly');
|
const release = await github.getRelease('goreleaser-pro', 'nightly');
|
||||||
expect(release).not.toBeNull();
|
expect(release).not.toBeNull();
|
||||||
expect(release?.tag_name).not.toEqual('');
|
expect(release.tag_name).toMatch(github.nightlyTagRegex);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns v0.182.0 GoReleaser Pro GitHub release', async () => {
|
it('returns v0.182.0 GoReleaser Pro GitHub release', async () => {
|
||||||
|
|||||||
@@ -104,17 +104,29 @@ describe('getCertificateIdentity', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses nightly-oss.yml@refs/heads/main for OSS nightly', () => {
|
it('uses nightly-oss.yml@refs/heads/main for OSS legacy nightly tag', () => {
|
||||||
expect(goreleaser.getCertificateIdentity('goreleaser', 'nightly')).toEqual(
|
expect(goreleaser.getCertificateIdentity('goreleaser', 'nightly')).toEqual(
|
||||||
'https://github.com/goreleaser/goreleaser/.github/workflows/nightly-oss.yml@refs/heads/main'
|
'https://github.com/goreleaser/goreleaser/.github/workflows/nightly-oss.yml@refs/heads/main'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses nightly-pro.yml@refs/heads/main for Pro nightly', () => {
|
it('uses nightly-pro.yml@refs/heads/main for Pro legacy nightly tag', () => {
|
||||||
expect(goreleaser.getCertificateIdentity('goreleaser-pro', 'nightly')).toEqual(
|
expect(goreleaser.getCertificateIdentity('goreleaser-pro', 'nightly')).toEqual(
|
||||||
'https://github.com/goreleaser/goreleaser-pro-internal/.github/workflows/nightly-pro.yml@refs/heads/main'
|
'https://github.com/goreleaser/goreleaser-pro-internal/.github/workflows/nightly-pro.yml@refs/heads/main'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses nightly-oss.yml@refs/heads/main for OSS nightly tag', () => {
|
||||||
|
expect(goreleaser.getCertificateIdentity('goreleaser', 'v2.16.0-abc1234-nightly')).toEqual(
|
||||||
|
'https://github.com/goreleaser/goreleaser/.github/workflows/nightly-oss.yml@refs/heads/main'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses nightly-pro.yml@refs/heads/main for Pro nightly tag', () => {
|
||||||
|
expect(goreleaser.getCertificateIdentity('goreleaser-pro', 'v2.16.0-eaeb08c50-nightly')).toEqual(
|
||||||
|
'https://github.com/goreleaser/goreleaser-pro-internal/.github/workflows/nightly-pro.yml@refs/heads/main'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('verifyChecksum', () => {
|
describe('verifyChecksum', () => {
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+45
-1
@@ -30,6 +30,13 @@ export interface GitHubRelease {
|
|||||||
tag_name: string;
|
tag_name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matches the new-style nightly release tag pattern: vX.Y.Z-<sha>-nightly
|
||||||
|
export const nightlyTagRegex = /^v\d+\.\d+\.\d+-[0-9a-f]+-nightly$/i;
|
||||||
|
|
||||||
|
export const isNightlyTag = (tag: string): boolean => {
|
||||||
|
return tag === 'nightly' || nightlyTagRegex.test(tag);
|
||||||
|
};
|
||||||
|
|
||||||
export const getRelease = async (distribution: string, version: string): Promise<GitHubRelease> => {
|
export const getRelease = async (distribution: string, version: string): Promise<GitHubRelease> => {
|
||||||
if (version === 'latest') {
|
if (version === 'latest') {
|
||||||
core.warning("You are using 'latest' as default version. Will lock to '~> v2'.");
|
core.warning("You are using 'latest' as default version. Will lock to '~> v2'.");
|
||||||
@@ -40,7 +47,7 @@ export const getRelease = async (distribution: string, version: string): Promise
|
|||||||
|
|
||||||
export const getReleaseTag = async (distribution: string, version: string): Promise<GitHubRelease> => {
|
export const getReleaseTag = async (distribution: string, version: string): Promise<GitHubRelease> => {
|
||||||
if (version === 'nightly') {
|
if (version === 'nightly') {
|
||||||
return {tag_name: version};
|
return resolveNightly(distribution);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If version is a specific version (not a range), skip the JSON check
|
// If version is a specific version (not a range), skip the JSON check
|
||||||
@@ -81,6 +88,43 @@ export const getReleaseTag = async (distribution: string, version: string): Prom
|
|||||||
throw new Error(`Cannot find GoReleaser release ${version} in ${url}`);
|
throw new Error(`Cannot find GoReleaser release ${version} in ${url}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// resolveNightly looks up the latest immutable nightly release of the form
|
||||||
|
// `vX.Y.Z-<sha>-nightly` on the GitHub releases of the given distribution.
|
||||||
|
const resolveNightly = async (distribution: string): Promise<GitHubRelease> => {
|
||||||
|
const url = `https://api.github.com/repos/goreleaser/${distribution}/releases?per_page=100`;
|
||||||
|
core.debug(`Resolving latest nightly release from ${url}`);
|
||||||
|
|
||||||
|
const releases = await withRetry(async () => {
|
||||||
|
const http: httpm.HttpClient = new httpm.HttpClient('goreleaser-action');
|
||||||
|
const headers: {[name: string]: string} = {
|
||||||
|
Accept: 'application/vnd.github+json',
|
||||||
|
'X-GitHub-Api-Version': '2022-11-28'
|
||||||
|
};
|
||||||
|
const token = process.env.GITHUB_TOKEN;
|
||||||
|
if (token) {
|
||||||
|
headers['Authorization'] = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
const resp: httpm.HttpClientResponse = await http.get(url, headers);
|
||||||
|
const body = await resp.readBody();
|
||||||
|
const statusCode = resp.message.statusCode || 500;
|
||||||
|
if (statusCode >= 400) {
|
||||||
|
throw new Error(`Failed to list releases from ${url} with status code ${statusCode}: ${body}`);
|
||||||
|
}
|
||||||
|
return <Array<GitHubRelease>>JSON.parse(body);
|
||||||
|
});
|
||||||
|
|
||||||
|
const match = releases.find(r => nightlyTagRegex.test(r.tag_name));
|
||||||
|
if (match) {
|
||||||
|
core.info(`Resolved nightly to ${match.tag_name}`);
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to the legacy moving `nightly` tag during the transition period,
|
||||||
|
// until goreleaser stops publishing it.
|
||||||
|
core.warning(`No '<version>-<sha>-nightly' release found in ${url}, falling back to 'nightly' tag`);
|
||||||
|
return {tag_name: 'nightly'};
|
||||||
|
};
|
||||||
|
|
||||||
const resolveVersion = async (distribution: string, version: string): Promise<string | null> => {
|
const resolveVersion = async (distribution: string, version: string): Promise<string | null> => {
|
||||||
const allTags: Array<string> | null = await getAllTags(distribution);
|
const allTags: Array<string> | null = await getAllTags(distribution);
|
||||||
if (!allTags) {
|
if (!allTags) {
|
||||||
|
|||||||
+1
-1
@@ -120,7 +120,7 @@ async function verifyCosignSignature(
|
|||||||
|
|
||||||
export const getCertificateIdentity = (distribution: string, tag: string): string => {
|
export const getCertificateIdentity = (distribution: string, tag: string): string => {
|
||||||
const pro = isPro(distribution);
|
const pro = isPro(distribution);
|
||||||
if (tag === 'nightly') {
|
if (github.isNightlyTag(tag)) {
|
||||||
const workflow = pro ? 'nightly-pro.yml' : 'nightly-oss.yml';
|
const workflow = pro ? 'nightly-pro.yml' : 'nightly-oss.yml';
|
||||||
const repo = pro ? 'goreleaser-pro-internal' : 'goreleaser';
|
const repo = pro ? 'goreleaser-pro-internal' : 'goreleaser';
|
||||||
return `https://github.com/goreleaser/${repo}/.github/workflows/${workflow}@refs/heads/main`;
|
return `https://github.com/goreleaser/${repo}/.github/workflows/${workflow}@refs/heads/main`;
|
||||||
|
|||||||
Reference in New Issue
Block a user