Skip to content

Commit c3d1cec

Browse files
committed
Update the latest release
1 parent 4dd3095 commit c3d1cec

6 files changed

Lines changed: 90 additions & 35 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ __tests__/runner/*
33
# but its recommended not to check these in https://github.com/actions/toolkit/blob/master/docs/action-versioning.md#recommendations
44
node_modules
55
coverage
6+
.env

action.yml

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
# https://help.github.com/en/articles/metadata-syntax-for-github-actions
2-
name: "GH Release"
3-
description: "Github Action for creating Github Releases"
4-
author: "softprops"
2+
name: 'GH Release'
3+
description: 'Github Action for creating Github Releases'
4+
author: 'softprops'
55
inputs:
66
body:
7-
description: "Note-worthy description of changes in release"
7+
description: 'Note-worthy description of changes in release'
88
required: false
99
body_path:
10-
description: "Path to load note-worthy description of changes in release from"
10+
description: 'Path to load note-worthy description of changes in release from'
1111
required: false
1212
name:
13-
description: "Gives the release a custom name. Defaults to tag name"
13+
description: 'Gives the release a custom name. Defaults to tag name'
1414
required: false
1515
tag_name:
16-
description: "Gives a tag name. Defaults to github.ref_name. refs/tags/<name> values are normalized to <name>."
16+
description: 'Gives a tag name. Defaults to github.ref_name. refs/tags/<name> values are normalized to <name>.'
1717
required: false
1818
draft:
19-
description: "Keeps the release as a draft. Defaults to false. When reusing an existing draft release, set this to true to keep it draft; omit it to publish after upload. On immutable-release repositories, use this for prereleases that upload assets and publish the draft later."
19+
description: 'Keeps the release as a draft. Defaults to false. When reusing an existing draft release, set this to true to keep it draft; omit it to publish after upload. On immutable-release repositories, use this for prereleases that upload assets and publish the draft later.'
2020
required: false
2121
prerelease:
22-
description: "Identify the release as a prerelease. Defaults to false"
22+
description: 'Identify the release as a prerelease. Defaults to false'
2323
required: false
2424
preserve_order:
25-
description: "Upload artifacts sequentially in the provided order. This does not control the final display order GitHub uses for release assets."
25+
description: 'Upload artifacts sequentially in the provided order. This does not control the final display order GitHub uses for release assets.'
2626
required: false
2727
files:
2828
description: "Newline-delimited list of path globs for asset files to upload. Escape glob metacharacters when matching literal filenames that contain them. `~/...` expands to the runner home directory. On Windows, both \\ and / path separators are accepted. GitHub may normalize raw asset filenames that contain special characters; the action restores the asset label when possible, but the final download name remains GitHub-controlled."
@@ -31,52 +31,55 @@ inputs:
3131
description: "Base directory to resolve 'files' globs against. Defaults to the workspace root used by the action step."
3232
required: false
3333
overwrite_files:
34-
description: "Overwrite existing files with the same name. Defaults to true"
34+
description: 'Overwrite existing files with the same name. Defaults to true'
3535
required: false
3636
default: 'true'
3737
fail_on_unmatched_files:
38-
description: "Fails if any of the `files` globs match nothing. Defaults to false"
38+
description: 'Fails if any of the `files` globs match nothing. Defaults to false'
3939
required: false
4040
repository:
41-
description: "Repository to make releases against, in <owner>/<repo> format"
41+
description: 'Repository to make releases against, in <owner>/<repo> format'
4242
required: false
4343
token:
44-
description: "Authorized GitHub token or PAT. Defaults to github.token when omitted. A non-empty explicit token overrides GITHUB_TOKEN. Passing an empty string treats the token as unset."
44+
description: 'Authorized GitHub token or PAT. Defaults to github.token when omitted. A non-empty explicit token overrides GITHUB_TOKEN. Passing an empty string treats the token as unset.'
4545
required: false
4646
default: ${{ github.token }}
4747
target_commitish:
48-
description: "Commitish value that determines where the Git tag is created from. Can be any branch or commit SHA. When creating a new tag for an older commit, `github.token` may not have permission to create the ref; use a PAT or another token with sufficient contents permissions if you hit 403 `Resource not accessible by integration`."
48+
description: 'Commitish value that determines where the Git tag is created from. Can be any branch or commit SHA. When creating a new tag for an older commit, `github.token` may not have permission to create the ref; use a PAT or another token with sufficient contents permissions if you hit 403 `Resource not accessible by integration`.'
4949
required: false
5050
discussion_category_name:
51-
description: "If specified, a discussion of the specified category is created and linked to the release. The value must be a category that already exists in the repository. If there is already a discussion linked to the release, this parameter is ignored."
51+
description: 'If specified, a discussion of the specified category is created and linked to the release. The value must be a category that already exists in the repository. If there is already a discussion linked to the release, this parameter is ignored.'
5252
required: false
5353
generate_release_notes:
54-
description: "Whether to automatically generate the name and body for this release. If name is specified, the specified name will be used; otherwise, a name will be automatically generated. If body is specified, the body will be pre-pended to the automatically generated notes."
54+
description: 'Whether to automatically generate the name and body for this release. If name is specified, the specified name will be used; otherwise, a name will be automatically generated. If body is specified, the body will be pre-pended to the automatically generated notes.'
5555
required: false
5656
previous_tag:
5757
description: "Optional. When generate_release_notes is enabled, use this tag as GitHub's previous_tag_name comparison base. If omitted, GitHub chooses the comparison base automatically."
5858
required: false
59-
default: ""
59+
default: ''
6060
append_body:
61-
description: "Append to existing body instead of overwriting it. Default is false."
61+
description: 'Append to existing body instead of overwriting it. Default is false.'
6262
required: false
6363
make_latest:
64-
description: "Specifies whether this release should be set as the latest release for the repository. Drafts and prereleases cannot be set as latest. Can be `true`, `false`, or `legacy`. Uses GitHub api default if not provided"
64+
description: 'Specifies whether this release should be set as the latest release for the repository. Drafts and prereleases cannot be set as latest. Can be `true`, `false`, or `legacy`. Uses GitHub api default if not provided'
65+
required: false
66+
latest:
67+
description: 'Update the latest release. Default is false.'
6568
required: false
6669
env:
67-
GITHUB_TOKEN: "As provided by Github Actions"
70+
GITHUB_TOKEN: 'As provided by Github Actions'
6871
outputs:
6972
url:
70-
description: "URL to the Release HTML Page"
73+
description: 'URL to the Release HTML Page'
7174
id:
72-
description: "Release ID"
75+
description: 'Release ID'
7376
upload_url:
74-
description: "URL for uploading assets to the release"
77+
description: 'URL for uploading assets to the release'
7578
assets:
76-
description: "JSON array containing information about each uploaded asset, in the format given [here](https://docs.github.com/en/rest/reference/repos#upload-a-release-asset--code-samples) (minus the `uploader` field)"
79+
description: 'JSON array containing information about each uploaded asset, in the format given [here](https://docs.github.com/en/rest/reference/repos#upload-a-release-asset--code-samples) (minus the `uploader` field)'
7780
runs:
78-
using: "node24"
79-
main: "dist/index.js"
81+
using: 'node24'
82+
main: 'dist/index.js'
8083
branding:
81-
color: "green"
82-
icon: "package"
84+
color: 'green'
85+
icon: 'package'

src/github.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ type ReleaseMutationParams = {
5555
};
5656

5757
export interface Releaser {
58+
getLatestRelease(params: { owner: string; repo: string }): Promise<{ data: Release }>;
59+
5860
getReleaseByTag(params: { owner: string; repo: string; tag: string }): Promise<{ data: Release }>;
5961

6062
createRelease(params: ReleaseMutationParams): Promise<{ data: Release }>;
@@ -109,6 +111,10 @@ export class GitHubReleaser implements Releaser {
109111
this.github = github;
110112
}
111113

114+
getLatestRelease(params: { owner: string; repo: string }): Promise<{ data: Release }> {
115+
return this.github.rest.repos.getLatestRelease(params);
116+
}
117+
112118
getReleaseByTag(params: {
113119
owner: string;
114120
repo: string;
@@ -506,7 +512,7 @@ export const release = async (
506512
}
507513

508514
const [owner, repo] = config.github_repository.split('/');
509-
const tag =
515+
let tag =
510516
normalizeTagName(config.input_tag_name) ||
511517
(isTag(config.github_ref) ? config.github_ref.replace('refs/tags/', '') : '');
512518

@@ -518,7 +524,15 @@ export const release = async (
518524
console.log(`📝 Generating release notes using previous tag ${previous_tag_name}`);
519525
}
520526
try {
521-
const _release: Release | undefined = await findTagFromReleases(releaser, owner, repo, tag);
527+
let _release: Release | undefined;
528+
if (config.input_latest) {
529+
_release = await findLatestRelease(releaser, owner, repo);
530+
if (_release !== undefined) {
531+
tag = _release.tag_name;
532+
}
533+
} else {
534+
_release = await findTagFromReleases(releaser, owner, repo, tag);
535+
}
522536

523537
if (_release === undefined) {
524538
return await createRelease(
@@ -716,6 +730,31 @@ export const listReleaseAssets = async (
716730
}
717731
};
718732

733+
/**
734+
* Find the latest release.
735+
*
736+
* @param releaser - The GitHub API wrapper for release operations
737+
* @param owner - The owner of the repository
738+
* @param repo - The name of the repository
739+
* @returns The release with the given tag name, or undefined if no release with that tag name is found
740+
*/
741+
export async function findLatestRelease(
742+
releaser: Releaser,
743+
owner: string,
744+
repo: string,
745+
): Promise<Release | undefined> {
746+
try {
747+
const { data: release } = await releaser.getLatestRelease({ owner, repo });
748+
return release;
749+
} catch (error) {
750+
// Release not found (404) or other error - return undefined to allow creation
751+
if (error.status === 404) {
752+
return undefined;
753+
}
754+
// Re-throw unexpected errors
755+
throw error;
756+
}
757+
}
719758
/**
720759
* Finds a release by tag name.
721760
*

src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* This file is the entrypoint for the action
3+
*/
4+
import { run } from './main';
5+
6+
// It calls the actual logic of the action
7+
run();

src/main.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ import { isTag, parseConfig, paths, unmatchedPatterns, uploadUrl } from './util'
55

66
import { env } from 'process';
77

8-
async function run() {
8+
export async function run() {
99
try {
1010
const config = parseConfig(env);
11-
if (!config.input_tag_name && !isTag(config.github_ref) && !config.input_draft) {
11+
if (
12+
!config.input_latest &&
13+
!config.input_tag_name &&
14+
!isTag(config.github_ref) &&
15+
!config.input_draft
16+
) {
1217
throw new Error(`⚠️ GitHub Releases requires a tag`);
1318
}
1419
if (config.input_files) {
@@ -110,5 +115,3 @@ async function run() {
110115
setFailed(error.message);
111116
}
112117
}
113-
114-
run();

src/util.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface Config {
2626
input_previous_tag?: string;
2727
input_append_body?: boolean;
2828
input_make_latest: 'true' | 'false' | 'legacy' | undefined;
29+
input_latest?: boolean;
2930
}
3031

3132
export const uploadUrl = (url: string): string => {
@@ -118,6 +119,7 @@ export const parseConfig = (env: Env): Config => {
118119
input_previous_tag: env.INPUT_PREVIOUS_TAG?.trim() || undefined,
119120
input_append_body: env.INPUT_APPEND_BODY == 'true',
120121
input_make_latest: parseMakeLatest(env.INPUT_MAKE_LATEST),
122+
input_latest: env.INPUT_LATEST == 'true',
121123
};
122124
};
123125

0 commit comments

Comments
 (0)