mirror of
https://github.com/jimeh/update-tags-action.git
synced 2026-02-19 09:36:41 +00:00
Compare commits
2 Commits
v2.1.1
...
parse-vers
| Author | SHA1 | Date | |
|---|---|---|---|
|
ef25cc7b7d
|
|||
|
77c38f4b74
|
22
.github/workflows/ci.yml
vendored
22
.github/workflows/ci.yml
vendored
@@ -18,6 +18,28 @@ jobs:
|
||||
run: |
|
||||
git diff --exit-code
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: "npm"
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: "npm"
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
|
||||
release-please:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
|
||||
15
README.md
15
README.md
@@ -78,12 +78,15 @@ jobs:
|
||||
|
||||
## Inputs
|
||||
|
||||
| parameter | description | required | default |
|
||||
| ------------ | --------------------------------------------------------------------------------- | -------- | ------------------- |
|
||||
| tags | List/CSV of tags to create/update. | `true` | |
|
||||
| ref | The SHA or ref to tag. Defaults to SHA of current commit. | `false` | ${{ github.sha }} |
|
||||
| when_exists | What to do if the tag already exists. Must be one of 'update', 'skip', or 'fail'. | `false` | update |
|
||||
| github_token | The GitHub token to use for authentication. | `false` | ${{ github.token }} |
|
||||
| parameter | description | required | default |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------------------- | -------- | ------------------- |
|
||||
| tags | List/CSV of tags to create/update. | `true` | |
|
||||
| ref | The SHA or ref to tag. Defaults to SHA of current commit. | `false` | ${{ github.sha }} |
|
||||
| parse | Version string to parse as SemVer and expose in the tags input via templating. | `false` | |
|
||||
| when_exists | What to do if the tag already exists. Must be one of 'update', 'skip', 'warn', or 'fail'. | `false` | update |
|
||||
| when_parse_fails | What to do with non-empty `parse` input that fails to parse as a Semantic Version. Must be one of 'warn' or 'fail'. | `false` | fail |
|
||||
| skip_prerelease | When `parse` input is pre-release version, should templated tags be skipped? | `false` | true |
|
||||
| github_token | The GitHub token to use for authentication. | `false` | ${{ github.token }} |
|
||||
|
||||
<!-- action-docs-inputs -->
|
||||
|
||||
|
||||
22
action.yml
22
action.yml
@@ -14,12 +14,30 @@ inputs:
|
||||
description: "The SHA or ref to tag. Defaults to SHA of current commit."
|
||||
required: false
|
||||
default: "${{ github.sha }}"
|
||||
parse:
|
||||
description: >-
|
||||
Version string to parse as SemVer and expose in the tags input via
|
||||
templating.
|
||||
required: false
|
||||
default: ""
|
||||
when_exists:
|
||||
description: >-
|
||||
What to do if the tag already exists. Must be one of 'update', 'skip', or
|
||||
'fail'.
|
||||
What to do if the tag already exists. Must be one of 'update', 'skip',
|
||||
'warn', or 'fail'.
|
||||
required: false
|
||||
default: "update"
|
||||
when_parse_fails:
|
||||
description: >-
|
||||
What to do with non-empty `parse` input that fails to parse as a Semantic
|
||||
Version. Must be one of 'warn' or 'fail'.
|
||||
required: false
|
||||
default: "fail"
|
||||
skip_prerelease:
|
||||
description: >-
|
||||
When `parse` input is pre-release version, should templated tags be
|
||||
skipped?
|
||||
required: false
|
||||
default: "true"
|
||||
github_token:
|
||||
description: "The GitHub token to use for authentication."
|
||||
required: false
|
||||
|
||||
5
dist/index.js
vendored
5
dist/index.js
vendored
File diff suppressed because one or more lines are too long
1
dist/index.js.map
vendored
1
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
112
dist/licenses.txt
vendored
112
dist/licenses.txt
vendored
@@ -478,6 +478,29 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
handlebars
|
||||
MIT
|
||||
Copyright (C) 2011-2019 by Yehuda Katz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
is-plain-object
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
@@ -503,6 +526,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
lru-cache
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
node-fetch
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
@@ -548,6 +590,57 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
semver
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
source-map
|
||||
BSD-3-Clause
|
||||
|
||||
Copyright (c) 2009-2011, Mozilla Foundation and contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the Mozilla Foundation nor the names of project
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
tr46
|
||||
MIT
|
||||
|
||||
@@ -658,3 +751,22 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
yallist
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
1
dist/sourcemap-register.js
vendored
1
dist/sourcemap-register.js
vendored
File diff suppressed because one or more lines are too long
364
index.js
364
index.js
@@ -1,109 +1,61 @@
|
||||
const core = require("@actions/core");
|
||||
const github = require("@actions/github");
|
||||
const { parse } = require("csv-parse/sync");
|
||||
const csv = require("csv-parse/sync");
|
||||
const semver = require("semver");
|
||||
const handlebars = require("handlebars");
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const tagsInput = core.getInput("tags", { required: true });
|
||||
const defaultRef = core.getInput("ref");
|
||||
const token = core.getInput("github_token", { required: true });
|
||||
const inputVersion = core.getInput("parse");
|
||||
const whenExists = core.getInput("when_exists") || "update";
|
||||
const whenParseFails = core.getInput("when_parse_fails") || "fail";
|
||||
const skipPrerelease = core.getInput("skip_prerelease") === "true";
|
||||
const token = core.getInput("github_token", { required: true });
|
||||
|
||||
const octokit = github.getOctokit(token);
|
||||
|
||||
const parsedTags = parse(tagsInput, {
|
||||
delimiter: ",",
|
||||
trim: true,
|
||||
relax_column_count: true,
|
||||
}).flat();
|
||||
validateInput("when_exists", whenExists, [
|
||||
"update",
|
||||
"skip",
|
||||
"warn",
|
||||
"fail",
|
||||
]);
|
||||
validateInput("when_parse_fails", whenParseFails, ["warn", "fail"]);
|
||||
|
||||
const { owner, repo } = github.context.repo;
|
||||
const octokit = github.getOctokit(token);
|
||||
|
||||
const uniqueRefs = new Set();
|
||||
const refToSha = {};
|
||||
const tags = {};
|
||||
|
||||
for (const tag of parsedTags) {
|
||||
const [t, tagRef] = tag.split(":").map((s) => s.trim());
|
||||
const ref = tagRef || defaultRef;
|
||||
tags[t] = ref;
|
||||
uniqueRefs.add(ref);
|
||||
}
|
||||
|
||||
// Pre-resolve all unique refs
|
||||
for (const ref of uniqueRefs) {
|
||||
refToSha[ref] = await resolveRefToSha(octokit, owner, repo, ref);
|
||||
}
|
||||
const tagsWithRefs = buildTags(
|
||||
core,
|
||||
tagsInput,
|
||||
defaultRef,
|
||||
inputVersion,
|
||||
whenParseFails,
|
||||
skipPrerelease
|
||||
);
|
||||
const tags = await resolveTags(core, octokit, owner, repo, tagsWithRefs);
|
||||
|
||||
const created = [];
|
||||
const updated = [];
|
||||
|
||||
// Create or update all tags by looping through tags
|
||||
// Create or update all tags
|
||||
for (const tagName in tags) {
|
||||
if (!tagName) {
|
||||
core.setFailed(`Invalid tag: '${tagName}'`);
|
||||
return;
|
||||
}
|
||||
const sha = tags[tagName];
|
||||
|
||||
const tagRef = tags[tagName];
|
||||
const sha = refToSha[tagRef];
|
||||
const res = await createOrUpdateTag(
|
||||
core,
|
||||
octokit,
|
||||
owner,
|
||||
repo,
|
||||
tagName,
|
||||
sha,
|
||||
whenExists
|
||||
);
|
||||
|
||||
try {
|
||||
// Check if the ref exists
|
||||
const existing = await octokit.rest.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `tags/${tagName}`,
|
||||
});
|
||||
|
||||
// If the ref exists, decide action based on 'when_exists'
|
||||
if (whenExists === "update") {
|
||||
const existingSHA = existing.data.object.sha;
|
||||
if (existingSHA === sha) {
|
||||
core.info(
|
||||
`Tag '${tagName}' already exists with desired SHA ${sha}.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
core.info(
|
||||
`Tag '${tagName}' exists, updating to SHA ${sha} ` +
|
||||
`(was ${existingSHA}).`
|
||||
);
|
||||
await octokit.rest.git.updateRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `tags/${tagName}`,
|
||||
sha,
|
||||
force: true,
|
||||
});
|
||||
updated.push(tagName);
|
||||
} else if (whenExists === "skip") {
|
||||
core.info(`Tag '${tagName}' exists, skipping.`);
|
||||
} else if (whenExists === "fail") {
|
||||
core.setFailed(`Tag '${tagName}' already exists.`);
|
||||
return;
|
||||
} else {
|
||||
core.setFailed(
|
||||
`Invalid value for 'when_exists': '${whenExists}'. ` +
|
||||
`Valid values are 'update', 'skip', and 'fail'.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// If the ref doesn't exist, create it
|
||||
core.info(`Tag '${tagName}' does not exist, creating with SHA ${sha}.`);
|
||||
await octokit.rest.git.createRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `refs/tags/${tagName}`,
|
||||
sha,
|
||||
});
|
||||
if (res === "created") {
|
||||
created.push(tagName);
|
||||
} else if (res === "updated") {
|
||||
updated.push(tagName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,20 +67,232 @@ async function run() {
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveRefToSha(octokit, owner, repo, ref) {
|
||||
try {
|
||||
const {
|
||||
data: { sha },
|
||||
} = await octokit.rest.repos.getCommit({
|
||||
owner,
|
||||
repo,
|
||||
ref,
|
||||
});
|
||||
|
||||
return sha;
|
||||
} catch (error) {
|
||||
core.setFailed(`Failed to resolve ref '${ref}' to a SHA: ${error}`);
|
||||
function validateInput(name, value, allowedValues) {
|
||||
if (!allowedValues.includes(value)) {
|
||||
throw new Error(
|
||||
`Invalid value '${value}' for input '${name}'. ` +
|
||||
`Allowed values are: ${allowedValues.join(", ")}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
function buildTags(
|
||||
core,
|
||||
tags,
|
||||
defaultRef,
|
||||
inputVersion,
|
||||
whenParseFail,
|
||||
skipPrerelease
|
||||
) {
|
||||
const list = csv
|
||||
.parse(tags, { delimiter: ",", trim: true, relax_column_count: true })
|
||||
.flat();
|
||||
|
||||
const parsedTags = {};
|
||||
const version = parseVersion(core, inputVersion, whenParseFail);
|
||||
|
||||
for (const item of list) {
|
||||
const [tag, ref] = item.split(":").map((s) => s.trim());
|
||||
const renderedTag = renderTag(core, tag, version, skipPrerelease);
|
||||
|
||||
if (renderedTag) {
|
||||
parsedTags[renderedTag] = ref || defaultRef;
|
||||
}
|
||||
}
|
||||
|
||||
return parsedTags;
|
||||
}
|
||||
|
||||
function parseVersion(core, input, whenParseFail) {
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
const originalInput = input;
|
||||
|
||||
if (input.includes("/")) {
|
||||
input = input.split("/").pop();
|
||||
}
|
||||
|
||||
const version = semver.parse(input);
|
||||
if (version) {
|
||||
core.info(
|
||||
`Parsed input '${originalInput}' as semantic version: ${version.version}`
|
||||
);
|
||||
return version;
|
||||
}
|
||||
|
||||
if (whenParseFail === "fail") {
|
||||
throw new Error(`Failed to parse '${input}' as semantic version.`);
|
||||
}
|
||||
|
||||
core.warning(
|
||||
`Failed to parse '${input}'. Template-based tags will be skipped.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
function renderTag(core, tag, version, skipPrerelease) {
|
||||
if (!version) {
|
||||
if (!tag.includes("{{")) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
core.warning(
|
||||
`Skipping templated tag '${tag}'. No version information is available.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (version && version.includePrerelease && !skipPrerelease) {
|
||||
core.info(
|
||||
`Skipping templated tag '${tag}'. ` +
|
||||
`Parsed version '${version.version}' is a prerelease.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const template = handlebars.compile(tag);
|
||||
const emptyTag = template(semver.parse(""));
|
||||
const renderedTag = template(version);
|
||||
|
||||
if (emptyTag === renderedTag) {
|
||||
core.info(
|
||||
`Skipping templated tag '${tag}', all used template variables are empty.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (renderedTag.includes("{{")) {
|
||||
throw new Error(
|
||||
`Templated tag '${tag}' could not be renderd, some template ` +
|
||||
`variables could be resolved. Rendered to '${renderedTag}'.`
|
||||
);
|
||||
}
|
||||
|
||||
return renderedTag;
|
||||
}
|
||||
|
||||
async function resolveTags(core, octokit, owner, repo, tags) {
|
||||
const uniqueRefs = new Set();
|
||||
for (const tagName in tags) {
|
||||
uniqueRefs.add(tags[tagName]);
|
||||
}
|
||||
|
||||
core.info(
|
||||
`Looking up commit details for: '${Array.from(uniqueRefs).join("', '")}'`
|
||||
);
|
||||
const refToSha = {};
|
||||
for (const ref of uniqueRefs) {
|
||||
const sha = await resolveRefToSha(core, octokit, owner, repo, ref);
|
||||
if (sha) {
|
||||
refToSha[ref] = sha;
|
||||
}
|
||||
}
|
||||
|
||||
const tagShas = {};
|
||||
for (const tagName in tags) {
|
||||
const ref = tags[tagName];
|
||||
const sha = refToSha[ref];
|
||||
|
||||
if (!sha) {
|
||||
core.warning(
|
||||
`Skipping tag '${tagName}'. No commit details found for '${ref}'.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
tagShas[tagName] = sha;
|
||||
}
|
||||
|
||||
return tagShas;
|
||||
}
|
||||
|
||||
async function createOrUpdateTag(
|
||||
core,
|
||||
octokit,
|
||||
owner,
|
||||
repo,
|
||||
tagName,
|
||||
sha,
|
||||
whenExists
|
||||
) {
|
||||
try {
|
||||
const existing = await octokit.rest.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `tags/${tagName}`,
|
||||
});
|
||||
|
||||
if (whenExists === "update") {
|
||||
const existingSHA = existing.data.object.sha;
|
||||
if (existingSHA === sha) {
|
||||
core.info(`Tag '${tagName}' already exists with desired SHA ${sha}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
core.info(
|
||||
`Tag '${tagName}' exists, updating to SHA ${sha} ` +
|
||||
`(was ${existingSHA}).`
|
||||
);
|
||||
await octokit.rest.git.updateRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `tags/${tagName}`,
|
||||
sha,
|
||||
force: true,
|
||||
});
|
||||
|
||||
return "updated";
|
||||
} else if (whenExists === "skip") {
|
||||
core.info(`Tag '${tagName}' exists, skipping.`);
|
||||
return "skipped";
|
||||
} else if (whenExists === "warn") {
|
||||
core.warning(`Tag '${tagName}' exists, skipping.`);
|
||||
return "skipped";
|
||||
} else {
|
||||
throw new Error(`Tag '${tagName}' already exists.`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
core.info(`Tag '${tagName}' does not exist, creating with SHA ${sha}.`);
|
||||
await octokit.rest.git.createRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `refs/tags/${tagName}`,
|
||||
sha,
|
||||
});
|
||||
|
||||
return "created";
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveRefToSha(core, octokit, owner, repo, ref) {
|
||||
try {
|
||||
const {
|
||||
data: { sha },
|
||||
} = await octokit.rest.repos.getCommit({ owner, repo, ref });
|
||||
|
||||
return sha;
|
||||
} catch (error) {
|
||||
core.warning(`Failed to fetch commit details for '${ref}'.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Export run function for testing
|
||||
module.exports = {
|
||||
run,
|
||||
parseVersion,
|
||||
buildTags,
|
||||
renderTag,
|
||||
resolveTags,
|
||||
createOrUpdateTag,
|
||||
resolveRefToSha,
|
||||
};
|
||||
|
||||
// Call run function to start action only if this file is being run directly.
|
||||
if (require.main === module) {
|
||||
run();
|
||||
}
|
||||
|
||||
1077
package-lock.json
generated
1077
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,11 @@
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "npm run dist && npm run update-readme",
|
||||
"dist": "ncc build index.js --source-map --minify --license licenses.txt",
|
||||
"dist": "ncc build index.js --minify --license licenses.txt",
|
||||
"lint": "eslint *.js",
|
||||
"format": "eslint --fix *.js",
|
||||
"update-readme": "action-docs --update-readme && prettier -w README.md"
|
||||
"update-readme": "action-docs --update-readme && prettier -w README.md",
|
||||
"test": "jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
@@ -16,7 +17,11 @@
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"handlebars": "^4.7.7",
|
||||
"jest": "^29.5.0",
|
||||
"nock": "^13.3.1",
|
||||
"prettier": "^2.8.8",
|
||||
"semver": "^7.5.1",
|
||||
"ts-jest": "^29.1.0",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
|
||||
38
test/parseVersionAndRenderTags.test.js
Normal file
38
test/parseVersionAndRenderTags.test.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const semver = require("semver");
|
||||
const handlebars = require("handlebars");
|
||||
const { parseVersionAndRenderTags } = require("../index"); // replace 'your-file' with the actual file name
|
||||
|
||||
describe("parseVersionAndRenderTags", () => {
|
||||
it("handles empty parseVersion input", () => {
|
||||
const tags = "v1.0.0";
|
||||
expect(parseVersionAndRenderTags("", tags)).toBe(tags);
|
||||
});
|
||||
|
||||
it('removes "refs/tags/" prefix from parseVersion', () => {
|
||||
const parseVersion = "refs/tags/v1.2.3";
|
||||
const tags = "M={{major}}, m={{minor}}, p={{patch}}";
|
||||
const expectedTags = "M=1, m=2, p=3";
|
||||
expect(parseVersionAndRenderTags(parseVersion, tags)).toBe(expectedTags);
|
||||
});
|
||||
|
||||
it("throws an error if parseVersion is invalid and tags contains placeholders", () => {
|
||||
const parseVersion = "invalid-version-string";
|
||||
const tags = "{{major}}.{{minor}}.{{patch}}";
|
||||
expect(() => parseVersionAndRenderTags(parseVersion, tags)).toThrow(
|
||||
`Invalid version string: ${parseVersion}`
|
||||
);
|
||||
});
|
||||
|
||||
it("does not throw an error if parseVersion is invalid and tags does not contain placeholders", () => {
|
||||
const parseVersion = "invalid-version-string";
|
||||
const tags = "v1.0.0";
|
||||
expect(parseVersionAndRenderTags(parseVersion, tags)).toBe(tags);
|
||||
});
|
||||
|
||||
it("renders the tags template with the parsed version", () => {
|
||||
const parseVersion = "v1.2.3";
|
||||
const tags = "M={{major}}, m={{minor}}, p={{patch}}";
|
||||
const expectedTags = "M=1, m=2, p=3";
|
||||
expect(parseVersionAndRenderTags(parseVersion, tags)).toBe(expectedTags);
|
||||
});
|
||||
});
|
||||
54
test/resolveRefToSha.test.js
Normal file
54
test/resolveRefToSha.test.js
Normal file
@@ -0,0 +1,54 @@
|
||||
const nock = require("nock");
|
||||
const { resolveRefToSha } = require("../index");
|
||||
|
||||
describe("resolveRefToSha", () => {
|
||||
const octokit = { rest: { repos: { getCommit: jest.fn() } } };
|
||||
const owner = "testOwner";
|
||||
const repo = "testRepo";
|
||||
const ref = "testRef";
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
||||
test("it returns the correct SHA when the API call is successful", async () => {
|
||||
const expectedSha = "abc123";
|
||||
octokit.rest.repos.getCommit.mockResolvedValueOnce({
|
||||
data: { sha: expectedSha },
|
||||
});
|
||||
|
||||
const sha = await resolveRefToSha(octokit, owner, repo, ref);
|
||||
|
||||
expect(octokit.rest.repos.getCommit).toHaveBeenCalledWith({
|
||||
owner,
|
||||
repo,
|
||||
ref,
|
||||
});
|
||||
expect(sha).toEqual(expectedSha);
|
||||
});
|
||||
|
||||
test("it throws an error when the API call fails with a 404", async () => {
|
||||
octokit.rest.repos.getCommit.mockRejectedValueOnce({
|
||||
status: 404,
|
||||
message: "Not Found",
|
||||
});
|
||||
|
||||
await expect(resolveRefToSha(octokit, owner, repo, ref)).rejects.toThrow(
|
||||
"Failed to resolve ref"
|
||||
);
|
||||
});
|
||||
|
||||
test("it throws an error when the API call fails with a 500", async () => {
|
||||
octokit.rest.repos.getCommit.mockRejectedValueOnce({
|
||||
status: 500,
|
||||
message: "Internal Server Error",
|
||||
});
|
||||
|
||||
await expect(resolveRefToSha(octokit, owner, repo, ref)).rejects.toThrow(
|
||||
"Failed to resolve ref"
|
||||
);
|
||||
});
|
||||
|
||||
// Add more test cases as needed for other error scenarios
|
||||
});
|
||||
Reference in New Issue
Block a user