mirror of
https://github.com/jimeh/update-tags-action.git
synced 2026-02-19 01:26:40 +00:00
wip(parse-version): initial test of parse_version input
This commit is contained in:
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'
|
||||
|
||||
13
README.md
13
README.md
@@ -78,12 +78,13 @@ 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 }} |
|
||||
| when_exists | What to do if the tag already exists. Must be one of 'update', 'skip', or 'fail'. | `false` | update |
|
||||
| parse_version | Version string to parse as SemVer and expose via handlebars in the tags input. | `false` | |
|
||||
| github_token | The GitHub token to use for authentication. | `false` | ${{ github.token }} |
|
||||
|
||||
<!-- action-docs-inputs -->
|
||||
|
||||
|
||||
@@ -20,6 +20,12 @@ inputs:
|
||||
'fail'.
|
||||
required: false
|
||||
default: "update"
|
||||
parse_version:
|
||||
description: >-
|
||||
Version string to parse as SemVer and expose via handlebars in the tags
|
||||
input.
|
||||
required: false
|
||||
default: ""
|
||||
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
71
index.js
71
index.js
@@ -1,21 +1,26 @@
|
||||
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 });
|
||||
let parseVersion = core.getInput("parse_version");
|
||||
const defaultRef = core.getInput("ref");
|
||||
const token = core.getInput("github_token", { required: true });
|
||||
const whenExists = core.getInput("when_exists") || "update";
|
||||
|
||||
const octokit = github.getOctokit(token);
|
||||
const tagsInput = core.getInput("tags", { required: true });
|
||||
const tagsRendered = parseVersionAndRenderTags(parseVersion, tagsInput);
|
||||
|
||||
const parsedTags = parse(tagsInput, {
|
||||
delimiter: ",",
|
||||
trim: true,
|
||||
relax_column_count: true,
|
||||
}).flat();
|
||||
const parsedTags = csv
|
||||
.parse(tagsRendered, {
|
||||
delimiter: ",",
|
||||
trim: true,
|
||||
relax_column_count: true,
|
||||
})
|
||||
.flat();
|
||||
|
||||
const { owner, repo } = github.context.repo;
|
||||
|
||||
@@ -30,6 +35,8 @@ async function run() {
|
||||
uniqueRefs.add(ref);
|
||||
}
|
||||
|
||||
const octokit = github.getOctokit(token);
|
||||
|
||||
// Pre-resolve all unique refs
|
||||
for (const ref of uniqueRefs) {
|
||||
refToSha[ref] = await resolveRefToSha(octokit, owner, repo, ref);
|
||||
@@ -68,7 +75,7 @@ async function run() {
|
||||
|
||||
core.info(
|
||||
`Tag '${tagName}' exists, updating to SHA ${sha} ` +
|
||||
`(was ${existingSHA}).`
|
||||
`(was ${existingSHA}).`
|
||||
);
|
||||
await octokit.rest.git.updateRef({
|
||||
owner,
|
||||
@@ -86,7 +93,7 @@ async function run() {
|
||||
} else {
|
||||
core.setFailed(
|
||||
`Invalid value for 'when_exists': '${whenExists}'. ` +
|
||||
`Valid values are 'update', 'skip', and 'fail'.`
|
||||
`Valid values are 'update', 'skip', and 'fail'.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -115,20 +122,50 @@ async function run() {
|
||||
}
|
||||
}
|
||||
|
||||
function parseVersionAndRenderTags(parseVersion, tags) {
|
||||
if (!parseVersion) {
|
||||
return tags;
|
||||
}
|
||||
|
||||
if (parseVersion.startsWith("refs/tags/")) {
|
||||
parseVersion = parseVersion.substring("refs/tags/".length);
|
||||
}
|
||||
|
||||
const version = semver.parse(parseVersion);
|
||||
|
||||
if (!version && tags.includes("{{")) {
|
||||
throw new Error(`Invalid version string: ${parseVersion}`);
|
||||
}
|
||||
|
||||
if (version) {
|
||||
const template = handlebars.compile(tags);
|
||||
tags = template(version);
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
async function resolveRefToSha(octokit, owner, repo, ref) {
|
||||
try {
|
||||
const {
|
||||
data: { sha },
|
||||
} = await octokit.rest.repos.getCommit({
|
||||
owner,
|
||||
repo,
|
||||
ref,
|
||||
});
|
||||
} = await octokit.rest.repos.getCommit({ owner, repo, ref });
|
||||
|
||||
return sha;
|
||||
} catch (error) {
|
||||
core.setFailed(`Failed to resolve ref '${ref}' to a SHA: ${error}`);
|
||||
const errorMessage = `Failed to resolve ref '${ref}' to a SHA: ${error}`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
// Export run function for testing
|
||||
module.exports = {
|
||||
run,
|
||||
resolveRefToSha,
|
||||
parseVersionAndRenderTags,
|
||||
};
|
||||
|
||||
// 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