Add and configure ESLint and update configuration for Prettier (#26)

* Add ESLint and Prettier

* Rebuild action

* Update package.json

* Update licenses

* Fix review points
This commit is contained in:
Ivan 2023-03-08 10:50:45 +02:00 committed by GitHub
parent 87579b14ff
commit 9169aa7609
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2490 additions and 439 deletions

6
.eslintignore Normal file
View File

@ -0,0 +1,6 @@
# Ignore list
/*
# Do not ignore these folders:
!__tests__/
!src/

49
.eslintrc.js Normal file
View File

@ -0,0 +1,49 @@
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:eslint-plugin-jest/recommended',
'eslint-config-prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'eslint-plugin-jest'],
rules: {
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-ignore': 'allow-with-description'
}
],
'no-console': 'error',
'yoda': 'error',
'prefer-const': [
'error',
{
destructuring: 'all'
}
],
'no-control-regex': 'off',
'no-constant-condition': ['error', {checkLoops: false}]
},
overrides: [
{
files: ['**/*{test,spec}.ts'],
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'jest/no-standalone-expect': 'off',
'jest/no-conditional-expect': 'off',
'no-console': 'off',
}
}
],
env: {
node: true,
es6: true,
'jest/globals': true
}
};

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
* text=auto eol=lf
.licenses/** -diff linguist-generated=true

View File

@ -9,4 +9,4 @@ on:
jobs: jobs:
call-basic-validation: call-basic-validation:
name: Basic validation name: Basic validation
uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main

View File

@ -2,13 +2,13 @@ name: CodeQL analysis
on: on:
push: push:
branches: [ main ] branches: [main]
pull_request: pull_request:
branches: [ main ] branches: [main]
schedule: schedule:
- cron: '0 3 * * 0' - cron: '0 3 * * 0'
jobs: jobs:
call-codeQL-analysis: call-codeQL-analysis:
name: CodeQL analysis name: CodeQL analysis
uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main

View File

@ -11,4 +11,4 @@ on:
jobs: jobs:
call-licensed: call-licensed:
name: Licensed name: Licensed
uses: actions/reusable-workflows/.github/workflows/licensed.yml@main uses: actions/reusable-workflows/.github/workflows/licensed.yml@main

View File

@ -25,11 +25,11 @@ jobs:
permissions: permissions:
contents: write contents: write
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Update the ${{ env.TAG_NAME }} tag - name: Update the ${{ env.TAG_NAME }} tag
id: update-major-tag id: update-major-tag
uses: ./ uses: ./
with: with:
source-tag: ${{ env.TAG_NAME }} source-tag: ${{ env.TAG_NAME }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }} slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

Binary file not shown.

7
.prettierignore Normal file
View File

@ -0,0 +1,7 @@
# Ignore list
/*
# Do not ignore these folders:
!__tests__/
!.github/
!src/

10
.prettierrc.js Normal file
View File

@ -0,0 +1,10 @@
module.exports = {
printWidth: 80,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
trailingComma: 'none',
bracketSpacing: false,
arrowParens: 'avoid'
};

View File

@ -1,39 +1,43 @@
import * as github from "@actions/github"; import * as github from '@actions/github';
import * as apiUtils from "../src/api-utils"; import * as apiUtils from '../src/api-utils';
const prereleaseData = require("./data/pre-release.json"); import prereleaseData from './data/pre-release.json';
const releaseData = require("./data/release.json"); import releaseData from './data/release.json';
const token = "faketoken"; const token = 'faketoken';
const octokitClient = github.getOctokit(token); const octokitClient = github.getOctokit(token);
let getReleaseSpy: jest.SpyInstance; let getReleaseSpy: jest.SpyInstance;
process.env.GITHUB_REPOSITORY = "test/repository"; process.env.GITHUB_REPOSITORY = 'test/repository';
describe("validateIfReleaseIsPublished", () => { describe('validateIfReleaseIsPublished', () => {
beforeEach(() => { beforeEach(() => {
getReleaseSpy = jest.spyOn(octokitClient.repos, "getReleaseByTag"); getReleaseSpy = jest.spyOn(octokitClient.repos, 'getReleaseByTag');
}); });
it("throw if release is marked as pre-release", async () => { it('throw if release is marked as pre-release', async () => {
getReleaseSpy.mockReturnValue(prereleaseData); getReleaseSpy.mockReturnValue(prereleaseData);
expect.assertions(1);
await expect(apiUtils.validateIfReleaseIsPublished("v1.0.0", octokitClient)).rejects.toThrowError(
"The 'v1.0.0' release is marked as pre-release. Updating tags for pre-release is not supported"
);
});
it("validate that release is published", async () => { expect.assertions(1);
getReleaseSpy.mockReturnValue(releaseData); await expect(
apiUtils.validateIfReleaseIsPublished('v1.0.0', octokitClient)
).rejects.toThrow(
"The 'v1.0.0' release is marked as pre-release. Updating tags for pre-release is not supported"
);
});
expect.assertions(1); it('validate that release is published', async () => {
await expect(apiUtils.validateIfReleaseIsPublished("v1.1.0", octokitClient)).resolves.not.toThrow(); getReleaseSpy.mockReturnValue(releaseData);
});
afterEach(() => { expect.assertions(1);
jest.resetAllMocks(); await expect(
jest.clearAllMocks(); apiUtils.validateIfReleaseIsPublished('v1.1.0', octokitClient)
}); ).resolves.not.toThrow();
}); });
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
});

View File

@ -1,69 +1,93 @@
import * as versionUtils from "../src/version-utils"; import * as versionUtils from '../src/version-utils';
import stableSemver from './data/stable-semver.json';
import stableBuildSemver from './data/stable-build-semver.json';
import prereleaseSemver from './data/prerelease-semver.json';
import prereleaseBuildSemver from './data/prerelease-build-semver.json';
describe("isStableSemverVersion", () => { describe('isStableSemverVersion', () => {
it("validate if a version is stable", () => { it('validate if a version is stable', () => {
const semverVersion = require("./data/stable-semver.json"); expect(
expect(versionUtils.isStableSemverVersion(semverVersion)).toBeTruthy(); versionUtils.isStableSemverVersion(stableSemver as any)
}); ).toBeTruthy();
});
it("validate if a version with build metadata is stable", () => {
const semverVersion = require("./data/stable-build-semver.json");
expect(versionUtils.isStableSemverVersion(semverVersion)).toBeTruthy();
});
it("validate if a pre-release version is not stable", () => { it('validate if a version with build metadata is stable', () => {
const semverVersion = require("./data/prerelease-semver.json"); expect(
expect(versionUtils.isStableSemverVersion(semverVersion)).toBeFalsy(); versionUtils.isStableSemverVersion(stableBuildSemver as any)
}); ).toBeTruthy();
});
it("validate if a pre-release version with build metadata is not stable", () => { it('validate if a pre-release version is not stable', () => {
const semverVersion = require("./data/prerelease-build-semver.json"); expect(
expect(versionUtils.isStableSemverVersion(semverVersion)).toBeFalsy(); versionUtils.isStableSemverVersion(prereleaseSemver as any)
}); ).toBeFalsy();
});
it('validate if a pre-release version with build metadata is not stable', () => {
expect(
versionUtils.isStableSemverVersion(prereleaseBuildSemver as any)
).toBeFalsy();
});
}); });
describe("validateSemverVersionFromTag", () => { describe('validateSemverVersionFromTag', () => {
it("validate a tag containing an valid semantic version", () => { it('validate a tag containing a valid semantic version', () => {
expect(() => versionUtils.validateSemverVersionFromTag("1.0.0")).not.toThrow(); expect(() =>
}); versionUtils.validateSemverVersionFromTag('1.0.0')
).not.toThrow();
});
it("validate a tag containing an valid semantic version with 'v' prefix", () => { it("validate a tag containing a valid semantic version with 'v' prefix", () => {
expect(() => versionUtils.validateSemverVersionFromTag("v1.0.0")).not.toThrow(); expect(() =>
}); versionUtils.validateSemverVersionFromTag('v1.0.0')
).not.toThrow();
});
it("validate a tag containing an valid semantic version with build metadata", () => { it('validate a tag containing a valid semantic version with build metadata', () => {
expect(() => versionUtils.validateSemverVersionFromTag("v1.0.0+20130313144700")).not.toThrow(); expect(() =>
}); versionUtils.validateSemverVersionFromTag('v1.0.0+20130313144700')
).not.toThrow();
});
it("throw when a tag contains an invalid semantic version", () => { it('throw when a tag contains an invalid semantic version', () => {
expect(() => versionUtils.validateSemverVersionFromTag("1.0.0invalid")).toThrowError( expect(() =>
"The '1.0.0invalid' doesn't satisfy semantic versioning specification" versionUtils.validateSemverVersionFromTag('1.0.0invalid')
); ).toThrow(
}); "The '1.0.0invalid' doesn't satisfy semantic versioning specification"
);
});
it("throw when a tag contains an valid unstable semantic version", () => { it('throw when a tag contains a valid unstable semantic version', () => {
expect(() => versionUtils.validateSemverVersionFromTag("v1.0.0-beta.1")).toThrowError( expect(() =>
"It is not allowed to specify pre-release version to update the major tag" versionUtils.validateSemverVersionFromTag('v1.0.0-beta.1')
); ).toThrow(
}); 'It is not allowed to specify pre-release version to update the major tag'
);
});
it("throw when a tag contains an valid unstable semantic version with build metadata", () => { it('throw when a tag contains a valid unstable semantic version with build metadata', () => {
expect(() => versionUtils.validateSemverVersionFromTag("v1.0.0-beta.1+20130313144700")).toThrowError( expect(() =>
"It is not allowed to specify pre-release version to update the major tag" versionUtils.validateSemverVersionFromTag('v1.0.0-beta.1+20130313144700')
); ).toThrow(
}); 'It is not allowed to specify pre-release version to update the major tag'
);
});
}); });
describe("getMajorTagFromFullTag", () => { describe('getMajorTagFromFullTag', () => {
describe("get a valid major tag from full tag", () => { describe('get a valid major tag from full tag', () => {
it.each([ it.each([
["1.0.0", "1"], ['1.0.0', '1'],
["v1.0.0", "v1"], ['v1.0.0', 'v1'],
["v1.0.0-beta.1", "v1"], ['v1.0.0-beta.1', 'v1'],
["v1.0.0+20130313144700", "v1"], ['v1.0.0+20130313144700', 'v1']
] as [string, string][])("%s -> %s", (sourceTag: string, expectedMajorTag: string) => { ] as [string, string][])(
const resultantMajorTag = versionUtils.getMajorTagFromFullTag(sourceTag); '%s -> %s',
expect(resultantMajorTag).toBe(expectedMajorTag); (sourceTag: string, expectedMajorTag: string) => {
}); const resultantMajorTag =
}); versionUtils.getMajorTagFromFullTag(sourceTag);
}); expect(resultantMajorTag).toBe(expectedMajorTag);
}
);
});
});

375
dist/index.js vendored
View File

@ -5,104 +5,104 @@
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict"; "use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) { }) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
o[k2] = m[k]; o[k2] = m[k];
})); }));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v }); Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) { }) : function(o, v) {
o["default"] = v; o["default"] = v;
}); });
var __importStar = (this && this.__importStar) || function (mod) { var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod; if (mod && mod.__esModule) return mod;
var result = {}; var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod); __setModuleDefault(result, mod);
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.postMessageToSlack = exports.updateTag = exports.validateIfReleaseIsPublished = void 0; exports.postMessageToSlack = exports.updateTag = exports.validateIfReleaseIsPublished = void 0;
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const github_1 = __nccwpck_require__(5438); const github_1 = __nccwpck_require__(5438);
const http_client_1 = __nccwpck_require__(9925); const http_client_1 = __nccwpck_require__(9925);
async function findTag(tag, octokitClient) { async function findTag(tag, octokitClient) {
try { try {
const { data: foundTag } = await octokitClient.git.getRef({ const { data: foundTag } = await octokitClient.git.getRef({
...github_1.context.repo, ...github_1.context.repo,
ref: `tags/${tag}` ref: `tags/${tag}`
}); });
return foundTag; return foundTag;
} }
catch (err) { catch (err) {
if (err.status === 404) { if (err.status === 404) {
return null; return null;
} }
else { else {
throw new Error(`Retrieving refs failed with the following error: ${err}`); throw new Error(`Retrieving refs failed with the following error: ${err}`);
} }
} }
} }
async function getTagSHA(tag, octokitClient) { async function getTagSHA(tag, octokitClient) {
const foundTag = await findTag(tag, octokitClient); const foundTag = await findTag(tag, octokitClient);
if (!foundTag) { if (!foundTag) {
throw new Error(`The '${tag}' tag does not exist in the remote repository`); throw new Error(`The '${tag}' tag does not exist in the remote repository`);
} }
return foundTag.object.sha; return foundTag.object.sha;
} }
async function validateIfReleaseIsPublished(tag, octokitClient) { async function validateIfReleaseIsPublished(tag, octokitClient) {
try { try {
const { data: foundRelease } = await octokitClient.repos.getReleaseByTag({ const { data: foundRelease } = await octokitClient.repos.getReleaseByTag({
...github_1.context.repo, ...github_1.context.repo,
tag, tag
}); });
if (foundRelease.prerelease) { if (foundRelease.prerelease) {
throw new Error(`The '${foundRelease.name}' release is marked as pre-release. Updating tags for pre-release is not supported`); throw new Error(`The '${foundRelease.name}' release is marked as pre-release. Updating tags for pre-release is not supported`);
} }
} }
catch (err) { catch (err) {
if (err.status === 404) { if (err.status === 404) {
throw new Error(`No GitHub release found for the ${tag} tag`); throw new Error(`No GitHub release found for the ${tag} tag`);
} }
else { else {
throw new Error(`Retrieving releases failed with the following error: ${err}`); throw new Error(`Retrieving releases failed with the following error: ${err}`);
} }
} }
} }
exports.validateIfReleaseIsPublished = validateIfReleaseIsPublished; exports.validateIfReleaseIsPublished = validateIfReleaseIsPublished;
async function updateTag(sourceTag, targetTag, octokitClient) { async function updateTag(sourceTag, targetTag, octokitClient) {
const sourceTagSHA = await getTagSHA(sourceTag, octokitClient); const sourceTagSHA = await getTagSHA(sourceTag, octokitClient);
const foundTargetTag = await findTag(targetTag, octokitClient); const foundTargetTag = await findTag(targetTag, octokitClient);
const refName = `tags/${targetTag}`; const refName = `tags/${targetTag}`;
if (foundTargetTag) { if (foundTargetTag) {
core.info(`Updating the '${targetTag}' tag to point to the '${sourceTag}' tag`); core.info(`Updating the '${targetTag}' tag to point to the '${sourceTag}' tag`);
await octokitClient.git.updateRef({ await octokitClient.git.updateRef({
...github_1.context.repo, ...github_1.context.repo,
ref: refName, ref: refName,
sha: sourceTagSHA, sha: sourceTagSHA,
force: true force: true
}); });
} }
else { else {
core.info(`Creating the '${targetTag}' tag from the '${sourceTag}' tag`); core.info(`Creating the '${targetTag}' tag from the '${sourceTag}' tag`);
await octokitClient.git.createRef({ await octokitClient.git.createRef({
...github_1.context.repo, ...github_1.context.repo,
ref: `refs/${refName}`, ref: `refs/${refName}`,
sha: sourceTagSHA sha: sourceTagSHA
}); });
} }
} }
exports.updateTag = updateTag; exports.updateTag = updateTag;
async function postMessageToSlack(slackWebhook, message) { async function postMessageToSlack(slackWebhook, message) {
const jsonData = { text: message }; const jsonData = { text: message };
const http = new http_client_1.HttpClient(); const http = new http_client_1.HttpClient();
await http.postJson(slackWebhook, jsonData); await http.postJson(slackWebhook, jsonData);
} }
exports.postMessageToSlack = postMessageToSlack; exports.postMessageToSlack = postMessageToSlack;
/***/ }), /***/ }),
@ -111,60 +111,59 @@ exports.postMessageToSlack = postMessageToSlack;
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict"; "use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) { }) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
o[k2] = m[k]; o[k2] = m[k];
})); }));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v }); Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) { }) : function(o, v) {
o["default"] = v; o["default"] = v;
}); });
var __importStar = (this && this.__importStar) || function (mod) { var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod; if (mod && mod.__esModule) return mod;
var result = {}; var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod); __setModuleDefault(result, mod);
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const github = __importStar(__nccwpck_require__(5438)); const github = __importStar(__nccwpck_require__(5438));
const github_1 = __nccwpck_require__(5438); const github_1 = __nccwpck_require__(5438);
const api_utils_1 = __nccwpck_require__(2430); const api_utils_1 = __nccwpck_require__(2430);
const version_utils_1 = __nccwpck_require__(1534); const version_utils_1 = __nccwpck_require__(1534);
async function run() { async function run() {
try { try {
const token = core.getInput('token'); const token = core.getInput('token');
const octokitClient = github.getOctokit(token); const octokitClient = github.getOctokit(token);
const sourceTagName = core.getInput('source-tag'); const sourceTagName = core.getInput('source-tag');
version_utils_1.validateSemverVersionFromTag(sourceTagName); version_utils_1.validateSemverVersionFromTag(sourceTagName);
await api_utils_1.validateIfReleaseIsPublished(sourceTagName, octokitClient); await api_utils_1.validateIfReleaseIsPublished(sourceTagName, octokitClient);
const majorTag = version_utils_1.getMajorTagFromFullTag(sourceTagName); const majorTag = version_utils_1.getMajorTagFromFullTag(sourceTagName);
await api_utils_1.updateTag(sourceTagName, majorTag, octokitClient); await api_utils_1.updateTag(sourceTagName, majorTag, octokitClient);
core.setOutput('major-tag', majorTag); core.setOutput('major-tag', majorTag);
core.info(`The '${majorTag}' major tag now points to the '${sourceTagName}' tag`); core.info(`The '${majorTag}' major tag now points to the '${sourceTagName}' tag`);
const slackMessage = `The ${majorTag} tag has been successfully updated for the ${github_1.context.repo.repo} action to include changes from ${sourceTagName}`; const slackMessage = `The ${majorTag} tag has been successfully updated for the ${github_1.context.repo.repo} action to include changes from ${sourceTagName}`;
await reportStatusToSlack(slackMessage); await reportStatusToSlack(slackMessage);
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
const slackMessage = `Failed to update a major tag for the ${github_1.context.repo.repo} action`; const slackMessage = `Failed to update a major tag for the ${github_1.context.repo.repo} action`;
await reportStatusToSlack(slackMessage); await reportStatusToSlack(slackMessage);
} }
} }
; async function reportStatusToSlack(message) {
async function reportStatusToSlack(message) { const slackWebhook = core.getInput('slack-webhook');
const slackWebhook = core.getInput('slack-webhook'); if (slackWebhook) {
if (slackWebhook) { await api_utils_1.postMessageToSlack(slackWebhook, message);
await api_utils_1.postMessageToSlack(slackWebhook, message); }
} }
} run();
run();
/***/ }), /***/ }),
@ -173,31 +172,31 @@ run();
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict"; "use strict";
var __importDefault = (this && this.__importDefault) || function (mod) { var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.validateSemverVersionFromTag = exports.getMajorTagFromFullTag = exports.isStableSemverVersion = void 0; exports.validateSemverVersionFromTag = exports.getMajorTagFromFullTag = exports.isStableSemverVersion = void 0;
const parse_1 = __importDefault(__nccwpck_require__(5925)); const parse_1 = __importDefault(__nccwpck_require__(5925));
function isStableSemverVersion(version) { function isStableSemverVersion(version) {
return version.prerelease.length === 0; return version.prerelease.length === 0;
} }
exports.isStableSemverVersion = isStableSemverVersion; exports.isStableSemverVersion = isStableSemverVersion;
function getMajorTagFromFullTag(fullTag) { function getMajorTagFromFullTag(fullTag) {
return fullTag.split('.')[0]; return fullTag.split('.')[0];
} }
exports.getMajorTagFromFullTag = getMajorTagFromFullTag; exports.getMajorTagFromFullTag = getMajorTagFromFullTag;
function validateSemverVersionFromTag(tag) { function validateSemverVersionFromTag(tag) {
const semverVersion = parse_1.default(tag); const semverVersion = parse_1.default(tag);
if (!semverVersion) { if (!semverVersion) {
throw new Error(`The '${tag}' doesn't satisfy semantic versioning specification`); throw new Error(`The '${tag}' doesn't satisfy semantic versioning specification`);
} }
if (!isStableSemverVersion(semverVersion)) { if (!isStableSemverVersion(semverVersion)) {
throw new Error('It is not allowed to specify pre-release version to update the major tag'); throw new Error('It is not allowed to specify pre-release version to update the major tag');
} }
} }
exports.validateSemverVersionFromTag = validateSemverVersionFromTag; exports.validateSemverVersionFromTag = validateSemverVersionFromTag;
/***/ }), /***/ }),
@ -9593,7 +9592,7 @@ class SemVer {
if (identifier) { if (identifier) {
// 1.2.0-beta.1 bumps to 1.2.0-beta.2, // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
// 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
if (this.prerelease[0] === identifier) { if (compareIdentifiers(this.prerelease[0], identifier) === 0) {
if (isNaN(this.prerelease[1])) { if (isNaN(this.prerelease[1])) {
this.prerelease = [identifier, 0] this.prerelease = [identifier, 0]
} }
@ -9620,7 +9619,7 @@ module.exports = SemVer
/***/ 5925: /***/ 5925:
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
const {MAX_LENGTH} = __nccwpck_require__(2293) const { MAX_LENGTH } = __nccwpck_require__(2293)
const { re, t } = __nccwpck_require__(9523) const { re, t } = __nccwpck_require__(9523)
const SemVer = __nccwpck_require__(8088) const SemVer = __nccwpck_require__(8088)
@ -9666,7 +9665,7 @@ const SEMVER_SPEC_VERSION = '2.0.0'
const MAX_LENGTH = 256 const MAX_LENGTH = 256
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
/* istanbul ignore next */ 9007199254740991 /* istanbul ignore next */ 9007199254740991
// Max safe segment length for coercion. // Max safe segment length for coercion.
const MAX_SAFE_COMPONENT_LENGTH = 16 const MAX_SAFE_COMPONENT_LENGTH = 16
@ -9675,7 +9674,7 @@ module.exports = {
SEMVER_SPEC_VERSION, SEMVER_SPEC_VERSION,
MAX_LENGTH, MAX_LENGTH,
MAX_SAFE_INTEGER, MAX_SAFE_INTEGER,
MAX_SAFE_COMPONENT_LENGTH MAX_SAFE_COMPONENT_LENGTH,
} }
@ -9721,7 +9720,7 @@ const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a)
module.exports = { module.exports = {
compareIdentifiers, compareIdentifiers,
rcompareIdentifiers rcompareIdentifiers,
} }
@ -9736,9 +9735,9 @@ const opts = ['includePrerelease', 'loose', 'rtl']
const parseOptions = options => const parseOptions = options =>
!options ? {} !options ? {}
: typeof options !== 'object' ? { loose: true } : typeof options !== 'object' ? { loose: true }
: opts.filter(k => options[k]).reduce((options, k) => { : opts.filter(k => options[k]).reduce((o, k) => {
options[k] = true o[k] = true
return options return o
}, {}) }, {})
module.exports = parseOptions module.exports = parseOptions
@ -9760,7 +9759,7 @@ let R = 0
const createToken = (name, value, isGlobal) => { const createToken = (name, value, isGlobal) => {
const index = R++ const index = R++
debug(index, value) debug(name, index, value)
t[name] = index t[name] = index
src[index] = value src[index] = value
re[index] = new RegExp(value, isGlobal ? 'g' : undefined) re[index] = new RegExp(value, isGlobal ? 'g' : undefined)
@ -9928,8 +9927,8 @@ createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +
// Star ranges basically just allow anything at all. // Star ranges basically just allow anything at all.
createToken('STAR', '(<|>)?=?\\s*\\*') createToken('STAR', '(<|>)?=?\\s*\\*')
// >=0.0.0 is like a star // >=0.0.0 is like a star
createToken('GTE0', '^\\s*>=\\s*0\.0\.0\\s*$') createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$')
createToken('GTE0PRE', '^\\s*>=\\s*0\.0\.0-0\\s*$') createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$')
/***/ }), /***/ }),

1979
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,10 @@
"scripts": { "scripts": {
"build": "tsc && ncc build", "build": "tsc && ncc build",
"test": "jest", "test": "jest",
"format-check": "echo \"Fake command that does nothing. It is used in reusable workflows\"", "format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write **/*.{ts,yml,yaml}",
"lint": "echo \"Fake command that does nothing. It is used in reusable workflows\"" "format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check **/*.{ts,yml,yaml}",
"lint": "eslint --config ./.eslintrc.js **/*.ts",
"lint:fix": "eslint --config ./.eslintrc.js **/*.ts --fix"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -25,16 +27,22 @@
"homepage": "https://github.com/actions/publish-action#readme", "homepage": "https://github.com/actions/publish-action#readme",
"dependencies": { "dependencies": {
"@actions/core": "^1.2.7", "@actions/core": "^1.2.7",
"@actions/http-client": "^1.0.11",
"@actions/github": "^4.0.0", "@actions/github": "^4.0.0",
"@actions/http-client": "^1.0.11",
"semver": "^7.3.5" "semver": "^7.3.5"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^27.0.2", "@types/jest": "^27.0.2",
"@types/semver": "^7.3.6", "@types/semver": "^7.3.6",
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"@vercel/ncc": "^0.28.5", "@vercel/ncc": "^0.28.5",
"eslint": "^8.35.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-jest": "^27.2.1",
"jest": "^27.2.5", "jest": "^27.2.5",
"jest-circus": "^27.2.5", "jest-circus": "^27.2.5",
"prettier": "^2.8.4",
"ts-jest": "^27.0.5", "ts-jest": "^27.0.5",
"typescript": "^4.2.4" "typescript": "^4.2.4"
} }

View File

@ -1,114 +1,115 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import { context } from '@actions/github'; import {context} from '@actions/github';
import { GitHub } from '@actions/github/lib/utils'; import {GitHub} from '@actions/github/lib/utils';
import { HttpClient } from '@actions/http-client'; import {HttpClient} from '@actions/http-client';
interface GitRef { interface GitRef {
ref: string; ref: string;
node_id: string; node_id: string;
url: string;
object: {
type: string;
sha: string;
url: string; url: string;
object: { };
type: string;
sha: string;
url: string;
};
} }
async function findTag( async function findTag(
tag: string, tag: string,
octokitClient: InstanceType<typeof GitHub> octokitClient: InstanceType<typeof GitHub>
): Promise<GitRef | null> { ): Promise<GitRef | null> {
try { try {
const { data: foundTag } = await octokitClient.git.getRef({ const {data: foundTag} = await octokitClient.git.getRef({
...context.repo, ...context.repo,
ref: `tags/${tag}` ref: `tags/${tag}`
}); });
return foundTag; return foundTag;
} catch (err) { } catch (err) {
if (err.status === 404) { if (err.status === 404) {
return null; return null;
} else { } else {
throw new Error( throw new Error(
`Retrieving refs failed with the following error: ${err}` `Retrieving refs failed with the following error: ${err}`
); );
}
} }
}
} }
async function getTagSHA( async function getTagSHA(
tag: string, tag: string,
octokitClient: InstanceType<typeof GitHub> octokitClient: InstanceType<typeof GitHub>
): Promise<string> { ): Promise<string> {
const foundTag = await findTag(tag, octokitClient); const foundTag = await findTag(tag, octokitClient);
if (!foundTag) { if (!foundTag) {
throw new Error( throw new Error(`The '${tag}' tag does not exist in the remote repository`);
`The '${tag}' tag does not exist in the remote repository` }
);
}
return foundTag.object.sha; return foundTag.object.sha;
} }
export async function validateIfReleaseIsPublished( export async function validateIfReleaseIsPublished(
tag: string, tag: string,
octokitClient: InstanceType<typeof GitHub> octokitClient: InstanceType<typeof GitHub>
): Promise<void> { ): Promise<void> {
try { try {
const { data: foundRelease } = await octokitClient.repos.getReleaseByTag({ const {data: foundRelease} = await octokitClient.repos.getReleaseByTag({
...context.repo, ...context.repo,
tag, tag
}); });
if (foundRelease.prerelease) { if (foundRelease.prerelease) {
throw new Error( throw new Error(
`The '${foundRelease.name}' release is marked as pre-release. Updating tags for pre-release is not supported` `The '${foundRelease.name}' release is marked as pre-release. Updating tags for pre-release is not supported`
); );
}
} catch (err) {
if (err.status === 404) {
throw new Error(
`No GitHub release found for the ${tag} tag`
);
} else {
throw new Error(
`Retrieving releases failed with the following error: ${err}`
);
}
} }
} catch (err) {
if (err.status === 404) {
throw new Error(`No GitHub release found for the ${tag} tag`);
} else {
throw new Error(
`Retrieving releases failed with the following error: ${err}`
);
}
}
} }
export async function updateTag( export async function updateTag(
sourceTag: string, sourceTag: string,
targetTag: string, targetTag: string,
octokitClient: InstanceType<typeof GitHub> octokitClient: InstanceType<typeof GitHub>
): Promise<void> { ): Promise<void> {
const sourceTagSHA = await getTagSHA(sourceTag, octokitClient); const sourceTagSHA = await getTagSHA(sourceTag, octokitClient);
const foundTargetTag = await findTag(targetTag, octokitClient); const foundTargetTag = await findTag(targetTag, octokitClient);
const refName = `tags/${targetTag}`; const refName = `tags/${targetTag}`;
if (foundTargetTag) { if (foundTargetTag) {
core.info(`Updating the '${targetTag}' tag to point to the '${sourceTag}' tag`); core.info(
`Updating the '${targetTag}' tag to point to the '${sourceTag}' tag`
);
await octokitClient.git.updateRef({ await octokitClient.git.updateRef({
...context.repo, ...context.repo,
ref: refName, ref: refName,
sha: sourceTagSHA, sha: sourceTagSHA,
force: true force: true
}); });
} else { } else {
core.info(`Creating the '${targetTag}' tag from the '${sourceTag}' tag`); core.info(`Creating the '${targetTag}' tag from the '${sourceTag}' tag`);
await octokitClient.git.createRef({ await octokitClient.git.createRef({
...context.repo, ...context.repo,
ref: `refs/${refName}`, ref: `refs/${refName}`,
sha: sourceTagSHA sha: sourceTagSHA
}); });
} }
} }
export async function postMessageToSlack(slackWebhook: string, message: string): Promise<void> { export async function postMessageToSlack(
const jsonData = {text: message} slackWebhook: string,
const http = new HttpClient(); message: string
await http.postJson(slackWebhook, jsonData); ): Promise<void> {
} const jsonData = {text: message};
const http = new HttpClient();
await http.postJson(slackWebhook, jsonData);
}

View File

@ -1,40 +1,49 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as github from '@actions/github'; import * as github from '@actions/github';
import { context } from '@actions/github'; import {context} from '@actions/github';
import { updateTag, validateIfReleaseIsPublished, postMessageToSlack } from './api-utils'; import {
import { validateSemverVersionFromTag, getMajorTagFromFullTag } from './version-utils'; updateTag,
validateIfReleaseIsPublished,
postMessageToSlack
} from './api-utils';
import {
validateSemverVersionFromTag,
getMajorTagFromFullTag
} from './version-utils';
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const token = core.getInput('token'); const token = core.getInput('token');
const octokitClient = github.getOctokit(token); const octokitClient = github.getOctokit(token);
const sourceTagName = core.getInput('source-tag'); const sourceTagName = core.getInput('source-tag');
validateSemverVersionFromTag(sourceTagName); validateSemverVersionFromTag(sourceTagName);
await validateIfReleaseIsPublished(sourceTagName, octokitClient); await validateIfReleaseIsPublished(sourceTagName, octokitClient);
const majorTag = getMajorTagFromFullTag(sourceTagName); const majorTag = getMajorTagFromFullTag(sourceTagName);
await updateTag(sourceTagName, majorTag, octokitClient); await updateTag(sourceTagName, majorTag, octokitClient);
core.setOutput('major-tag', majorTag); core.setOutput('major-tag', majorTag);
core.info(`The '${majorTag}' major tag now points to the '${sourceTagName}' tag`); core.info(
`The '${majorTag}' major tag now points to the '${sourceTagName}' tag`
);
const slackMessage = `The ${majorTag} tag has been successfully updated for the ${context.repo.repo} action to include changes from ${sourceTagName}`; const slackMessage = `The ${majorTag} tag has been successfully updated for the ${context.repo.repo} action to include changes from ${sourceTagName}`;
await reportStatusToSlack(slackMessage); await reportStatusToSlack(slackMessage);
} catch (error) { } catch (error) {
core.setFailed((error as Error).message); core.setFailed((error as Error).message);
const slackMessage = `Failed to update a major tag for the ${context.repo.repo} action`; const slackMessage = `Failed to update a major tag for the ${context.repo.repo} action`;
await reportStatusToSlack(slackMessage); await reportStatusToSlack(slackMessage);
} }
};
async function reportStatusToSlack(message: string): Promise<void> {
const slackWebhook = core.getInput('slack-webhook');
if (slackWebhook) {
await postMessageToSlack(slackWebhook, message);
}
} }
run(); async function reportStatusToSlack(message: string): Promise<void> {
const slackWebhook = core.getInput('slack-webhook');
if (slackWebhook) {
await postMessageToSlack(slackWebhook, message);
}
}
run();

View File

@ -2,24 +2,24 @@ import semverParse from 'semver/functions/parse';
import SemVer from 'semver/classes/semver'; import SemVer from 'semver/classes/semver';
export function isStableSemverVersion(version: SemVer): boolean { export function isStableSemverVersion(version: SemVer): boolean {
return version.prerelease.length === 0 return version.prerelease.length === 0;
} }
export function getMajorTagFromFullTag(fullTag: string): string { export function getMajorTagFromFullTag(fullTag: string): string {
return fullTag.split('.')[0]; return fullTag.split('.')[0];
} }
export function validateSemverVersionFromTag(tag: string): void { export function validateSemverVersionFromTag(tag: string): void {
const semverVersion = semverParse(tag); const semverVersion = semverParse(tag);
if (!semverVersion) { if (!semverVersion) {
throw new Error( throw new Error(
`The '${tag}' doesn't satisfy semantic versioning specification` `The '${tag}' doesn't satisfy semantic versioning specification`
); );
} }
if (!isStableSemverVersion(semverVersion)) { if (!isStableSemverVersion(semverVersion)) {
throw new Error( throw new Error(
'It is not allowed to specify pre-release version to update the major tag' 'It is not allowed to specify pre-release version to update the major tag'
); );
} }
} }

View File

@ -5,6 +5,7 @@
"outDir": "./lib", "outDir": "./lib",
"rootDir": "./src", "rootDir": "./src",
"esModuleInterop": true, "esModuleInterop": true,
"resolveJsonModule": true,
"strict": true, "strict": true,
"noImplicitAny": true, "noImplicitAny": true,