diff --git a/README.md b/README.md index 2b0c7a282..d134a67cd 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ The following are optional as `step.with` keys | `files` | String | Newline-delimited globs of paths to assets to upload for release | | `name` | String | Name of the release. defaults to tag name | | `tag_name` | String | Name of a tag. defaults to `github.ref` | +| `move_existing_tag` | Boolean | Indicator of whether to delete and recreate a tag if there is an existing release, with at a different `target_commitish`. Some projects consider tags to be immutable, in which case this should be kept unset/`false`. But some projects allow for some tags to be moved (e.g., `nightly`, etc.) | | `fail_on_unmatched_files` | Boolean | Indicator of whether to fail if any of the `files` globs match nothing | | `repository` | String | Name of a target repository in `/` format. Defaults to GITHUB_REPOSITORY env variable | | `target_commitish` | String | Commitish value that determines where the Git tag is created from. Can be any branch or commit SHA. | diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index e6ae387bd..e3365ac79 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -49,6 +49,7 @@ describe("util", () => { input_files: [], input_name: undefined, input_tag_name: undefined, + input_move_existing_tag: undefined, input_target_commitish: undefined, input_discussion_category_name: undefined }) @@ -68,6 +69,7 @@ describe("util", () => { input_files: [], input_name: undefined, input_tag_name: undefined, + input_move_existing_tag: undefined, input_target_commitish: undefined, input_discussion_category_name: undefined }) @@ -87,6 +89,7 @@ describe("util", () => { input_files: [], input_name: undefined, input_tag_name: undefined, + input_move_existing_tag: undefined, input_target_commitish: undefined, input_discussion_category_name: undefined }) @@ -117,6 +120,7 @@ describe("util", () => { input_files: [], input_name: undefined, input_tag_name: undefined, + input_move_existing_tag: undefined, input_fail_on_unmatched_files: false, input_target_commitish: undefined, input_discussion_category_name: undefined @@ -140,6 +144,7 @@ describe("util", () => { input_files: [], input_name: undefined, input_tag_name: undefined, + input_move_existing_tag: undefined, input_fail_on_unmatched_files: false, input_target_commitish: "affa18ef97bc9db20076945705aba8c516139abd", input_discussion_category_name: undefined @@ -162,6 +167,7 @@ describe("util", () => { input_files: [], input_name: undefined, input_tag_name: undefined, + input_move_existing_tag: undefined, input_fail_on_unmatched_files: false, input_target_commitish: undefined, input_discussion_category_name: "releases" @@ -187,6 +193,7 @@ describe("util", () => { input_files: [], input_name: undefined, input_tag_name: undefined, + input_move_existing_tag: undefined, input_fail_on_unmatched_files: false, input_target_commitish: undefined, input_discussion_category_name: undefined @@ -211,6 +218,7 @@ describe("util", () => { input_files: [], input_name: undefined, input_tag_name: undefined, + input_move_existing_tag: undefined, input_fail_on_unmatched_files: false, input_target_commitish: undefined, input_discussion_category_name: undefined @@ -234,6 +242,30 @@ describe("util", () => { input_files: [], input_name: undefined, input_tag_name: undefined, + input_move_existing_tag: undefined, + input_fail_on_unmatched_files: false, + input_target_commitish: undefined, + input_discussion_category_name: undefined + } + ); + }); + it("parses basic config with input_move_existing_tag", () => { + assert.deepStrictEqual( + parseConfig({ + INPUT_MOVE_EXISTING_TAG: "true", + }), + { + github_ref: "", + github_repository: "", + github_token: "", + input_body: undefined, + input_body_path: undefined, + input_draft: undefined, + input_prerelease: undefined, + input_files: [], + input_name: undefined, + input_tag_name: undefined, + input_move_existing_tag: undefined, input_fail_on_unmatched_files: false, input_target_commitish: undefined, input_discussion_category_name: undefined diff --git a/src/github.ts b/src/github.ts index 21265d6c0..72e513d20 100644 --- a/src/github.ts +++ b/src/github.ts @@ -34,6 +34,12 @@ export interface Releaser { tag: string; }): Promise<{ data: Release }>; + deleteTag(params: { + owner: string; + repo: string; + ref: string; + }): void; + createRelease(params: { owner: string; repo: string; @@ -79,6 +85,14 @@ export class GitHubReleaser implements Releaser { return this.github.rest.repos.getReleaseByTag(params); } + deleteTag(params: { + owner: string; + repo: string; + ref: string; + }): void { + return this.github.rest.git.deleteRef(params); + } + createRelease(params: { owner: string; repo: string; @@ -219,9 +233,34 @@ export const release = async ( config.input_target_commitish && config.input_target_commitish !== existingRelease.data.target_commitish ) { - console.log( - `Updating commit from "${existingRelease.data.target_commitish}" to "${config.input_target_commitish}"` - ); + + if (config.input_move_existing_tag) { + console.log( + `Deleting and recreating tag "${tag}". Moving it from "${existingRelease.data.target_commitish}" to "${config.input_target_commitish}"` + ); + try { + await releaser.deleteTag({ + owner, + repo, + ref: tag, + }); + } catch (error) { + console.log( + `⚠️ Failed to delete tag "${tag}" with status: ${ + error.status + }\n${JSON.stringify( + error.response.data.errors + )}\nretrying... (${maxRetries - 1} retries remaining)` + ); + return release(config, releaser, maxRetries - 1); + } + } else if (existingRelease.data.target_commitish != undefined) { + console.log( + `⚠️ Release tag "${tag}" already points at "${existingRelease.data.target_commitish}"; will not update the tag to point at target_commitish "${config.input_target_commitish}". \ + Set move_existing_tag to true if you want to delete and recreate the "${tag}" existing tag at the new target_commitish.` + ); + } + target_commitish = config.input_target_commitish; } else { target_commitish = existingRelease.data.target_commitish; diff --git a/src/util.ts b/src/util.ts index 271fac840..7ac2b9355 100644 --- a/src/util.ts +++ b/src/util.ts @@ -8,6 +8,7 @@ export interface Config { // user provided input_name?: string; input_tag_name?: string; + input_move_existing_tag?: boolean; input_repository?: string; input_body?: string; input_body_path?: string; @@ -55,6 +56,7 @@ export const parseConfig = (env: Env): Config => { github_repository: env.INPUT_REPOSITORY || env.GITHUB_REPOSITORY || "", input_name: env.INPUT_NAME, input_tag_name: env.INPUT_TAG_NAME?.trim(), + input_move_existing_tag: env.INPUT_MOVE_EXISTING_TAG ? env.INPUT_MOVE_EXISTING_TAG === "true" : undefined, input_body: env.INPUT_BODY, input_body_path: env.INPUT_BODY_PATH, input_files: parseInputFiles(env.INPUT_FILES || ""),