From ae4b81ae37903f1d9d2c076be03627ec80c32b6c Mon Sep 17 00:00:00 2001 From: thsaravana Date: Fri, 19 Jul 2024 06:48:23 +0200 Subject: [PATCH] Add typescript recommended lint --- .eslintrc.json | 11 +- dist/index.js | 2453 ++++++++++++++++++++++++------------------------ src/util.ts | 2 +- 3 files changed, 1231 insertions(+), 1235 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 95010d1..0f39724 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,9 @@ { "plugins": ["jest", "@typescript-eslint"], - "extends": ["plugin:github/recommended"], + "extends": [ + "plugin:github/recommended", + "plugin:@typescript-eslint/recommended" + ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 9, @@ -37,13 +40,13 @@ "@typescript-eslint/no-inferrable-types": "error", "@typescript-eslint/no-misused-new": "error", "@typescript-eslint/no-namespace": "error", - "@typescript-eslint/no-non-null-assertion": "warn", + "@typescript-eslint/no-non-null-assertion": "error", "@typescript-eslint/no-unnecessary-qualifier": "error", "@typescript-eslint/no-unnecessary-type-assertion": "error", "@typescript-eslint/no-useless-constructor": "error", "@typescript-eslint/no-var-requires": "error", - "@typescript-eslint/prefer-for-of": "warn", - "@typescript-eslint/prefer-function-type": "warn", + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-function-type": "error", "@typescript-eslint/prefer-includes": "error", "@typescript-eslint/prefer-string-starts-ends-with": "error", "@typescript-eslint/promise-function-async": "error", diff --git a/dist/index.js b/dist/index.js index 21b9901..5e446c7 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,18 +1,14 @@ -require('./sourcemap-register.js');/******/ (() => { // webpackBootstrap +/******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 2966: +/***/ 5350: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; @@ -25,658 +21,434 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; 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.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.action = void 0; -/* eslint-disable @typescript-eslint/no-explicit-any */ -const core = __importStar(__nccwpck_require__(6024)); -const github = __importStar(__nccwpck_require__(5016)); -const fs = __importStar(__nccwpck_require__(7147)); -const xml2js_1 = __importDefault(__nccwpck_require__(9253)); -const processors_1 = __nccwpck_require__(6434); -const glob = __importStar(__nccwpck_require__(2723)); -const process_1 = __nccwpck_require__(4355); -const render_1 = __nccwpck_require__(6015); -const util_1 = __nccwpck_require__(9410); -function action() { - var _a, _b, _c, _d; - return __awaiter(this, void 0, void 0, function* () { - let continueOnError = true; - try { - const token = core.getInput('token'); - if (!token) { - core.setFailed("'token' is missing"); - return; - } - const pathsString = core.getInput('paths'); - if (!pathsString) { - core.setFailed("'paths' is missing"); - return; - } - const reportPaths = pathsString.split(','); - const minCoverageOverall = parseFloat(core.getInput('min-coverage-overall')); - const minCoverageChangedFiles = parseFloat(core.getInput('min-coverage-changed-files')); - const title = core.getInput('title'); - const updateComment = (0, processors_1.parseBooleans)(core.getInput('update-comment')); - if (updateComment) { - if (!title) { - core.info("'title' is not set. 'update-comment' does not work without 'title'"); - } - } - const skipIfNoChanges = (0, processors_1.parseBooleans)(core.getInput('skip-if-no-changes')); - const passEmoji = core.getInput('pass-emoji'); - const failEmoji = core.getInput('fail-emoji'); - continueOnError = (0, processors_1.parseBooleans)(core.getInput('continue-on-error')); - const debugMode = (0, processors_1.parseBooleans)(core.getInput('debug-mode')); - const event = github.context.eventName; - core.info(`Event is ${event}`); - if (debugMode) { - core.info(`passEmoji: ${passEmoji}`); - core.info(`failEmoji: ${failEmoji}`); - } - let base; - let head; - let prNumber; - switch (event) { - case 'pull_request': - case 'pull_request_target': - base = (_a = github.context.payload.pull_request) === null || _a === void 0 ? void 0 : _a.base.sha; - head = (_b = github.context.payload.pull_request) === null || _b === void 0 ? void 0 : _b.head.sha; - prNumber = (_c = github.context.payload.pull_request) === null || _c === void 0 ? void 0 : _c.number; - break; - case 'push': - base = github.context.payload.before; - head = github.context.payload.after; - break; - default: - core.setFailed(`Only pull requests and pushes are supported, ${github.context.eventName} not supported.`); - return; - } - core.info(`base sha: ${base}`); - core.info(`head sha: ${head}`); - const client = github.getOctokit(token); - if (debugMode) - core.info(`reportPaths: ${reportPaths}`); - const reportsJsonAsync = getJsonReports(reportPaths, debugMode); - const changedFiles = yield getChangedFiles(base, head, client, debugMode); - if (debugMode) - core.info(`changedFiles: ${(0, util_1.debug)(changedFiles)}`); - const reportsJson = yield reportsJsonAsync; - const reports = reportsJson.map(report => report['report']); - const project = (0, process_1.getProjectCoverage)(reports, changedFiles); - if (debugMode) - core.info(`project: ${(0, util_1.debug)(project)}`); - core.setOutput('coverage-overall', parseFloat(((_d = project.overall.percentage) !== null && _d !== void 0 ? _d : 0).toFixed(2))); - core.setOutput('coverage-changed-files', parseFloat(project['coverage-changed-files'].toFixed(2))); - const skip = skipIfNoChanges && project.modules.length === 0; - if (debugMode) - core.info(`skip: ${skip}`); - if (debugMode) - core.info(`prNumber: ${prNumber}`); - if (prNumber != null && !skip) { - const emoji = { - pass: passEmoji, - fail: failEmoji, - }; - yield addComment(prNumber, updateComment, (0, render_1.getTitle)(title), (0, render_1.getPRComment)(project, { - overall: minCoverageOverall, - changed: minCoverageChangedFiles, - }, title, emoji), client, debugMode); - } +exports.issue = exports.issueCommand = void 0; +const os = __importStar(__nccwpck_require__(2037)); +const utils_1 = __nccwpck_require__(7369); +/** + * Commands + * + * Command Format: + * ::name key=value,key=value::message + * + * Examples: + * ::warning::This is the message + * ::set-env name=MY_VAR::some value + */ +function issueCommand(command, properties, message) { + const cmd = new Command(command, properties, message); + process.stdout.write(cmd.toString() + os.EOL); +} +exports.issueCommand = issueCommand; +function issue(name, message = '') { + issueCommand(name, {}, message); +} +exports.issue = issue; +const CMD_STRING = '::'; +class Command { + constructor(command, properties, message) { + if (!command) { + command = 'missing.command'; } - catch (error) { - if (error instanceof Error) { - if (continueOnError) { - core.error(error); - } - else { - core.setFailed(error); + this.command = command; + this.properties = properties; + this.message = message; + } + toString() { + let cmdStr = CMD_STRING + this.command; + if (this.properties && Object.keys(this.properties).length > 0) { + cmdStr += ' '; + let first = true; + for (const key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + const val = this.properties[key]; + if (val) { + if (first) { + first = false; + } + else { + cmdStr += ','; + } + cmdStr += `${key}=${escapeProperty(val)}`; + } } } } - }); -} -exports.action = action; -function getJsonReports(xmlPaths, debugMode) { - return __awaiter(this, void 0, void 0, function* () { - const globber = yield glob.create(xmlPaths.join('\n')); - const files = yield globber.glob(); - if (debugMode) - core.info(`Resolved files: ${files}`); - return Promise.all(files.map((path) => __awaiter(this, void 0, void 0, function* () { - const reportXml = yield fs.promises.readFile(path.trim(), 'utf-8'); - return yield xml2js_1.default.parseStringPromise(reportXml); - }))); - }); + cmdStr += `${CMD_STRING}${escapeData(this.message)}`; + return cmdStr; + } } -function getChangedFiles(base, head, client, debugMode) { - return __awaiter(this, void 0, void 0, function* () { - const response = yield client.rest.repos.compareCommits({ - base, - head, - owner: github.context.repo.owner, - repo: github.context.repo.repo, - }); - const changedFiles = []; - for (const file of response.data.files) { - if (debugMode) - core.info(`file: ${(0, util_1.debug)(file)}`); - const changedFile = { - filePath: file.filename, - url: file.blob_url, - lines: (0, util_1.getChangedLines)(file.patch), - }; - changedFiles.push(changedFile); - } - return changedFiles; - }); +function escapeData(s) { + return utils_1.toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A'); } -function addComment(prNumber, update, title, body, client, debugMode) { - return __awaiter(this, void 0, void 0, function* () { - let commentUpdated = false; - if (debugMode) - core.info(`update: ${update}`); - if (debugMode) - core.info(`title: ${title}`); - if (debugMode) - core.info(`JaCoCo Comment: ${body}`); - if (update && title) { - if (debugMode) - core.info('Listing all comments'); - const comments = yield client.rest.issues.listComments(Object.assign({ issue_number: prNumber }, github.context.repo)); - const comment = comments.data.find((it) => it.body.startsWith(title)); - if (comment) { - if (debugMode) - core.info(`Updating existing comment: id=${comment.id} \n body=${comment.body}`); - yield client.rest.issues.updateComment(Object.assign({ comment_id: comment.id, body }, github.context.repo)); - commentUpdated = true; - } - } - if (!commentUpdated) { - if (debugMode) - core.info('Creating a new comment'); - yield client.rest.issues.createComment(Object.assign({ issue_number: prNumber, body }, github.context.repo)); - } - }); +function escapeProperty(s) { + return utils_1.toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C'); } - +//# sourceMappingURL=command.js.map /***/ }), -/***/ 4355: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { +/***/ 6024: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getProjectCoverage = void 0; -const util_1 = __nccwpck_require__(9410); -/* eslint-disable @typescript-eslint/no-explicit-any */ -function getProjectCoverage(reports, changedFiles) { - const moduleCoverages = []; - const modules = getModulesFromReports(reports); - for (const module of modules) { - const files = getFileCoverageFromPackages([].concat(...module.packages), changedFiles); - if (files.length !== 0) { - const moduleCoverage = getModuleCoverage(module.root); - const changedMissed = files - .map(file => file.changed.missed) - .reduce(sumReducer, 0.0); - const changedCovered = files - .map(file => file.changed.covered) - .reduce(sumReducer, 0.0); - moduleCoverages.push({ - name: module.name, - files, - overall: { - percentage: moduleCoverage.percentage, - covered: moduleCoverage.covered, - missed: moduleCoverage.missed, - }, - changed: { - covered: changedCovered, - missed: changedMissed, - percentage: calculatePercentage(changedCovered, changedMissed), - }, - }); - } +exports.getIDToken = exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0; +const command_1 = __nccwpck_require__(5350); +const file_command_1 = __nccwpck_require__(8466); +const utils_1 = __nccwpck_require__(7369); +const os = __importStar(__nccwpck_require__(2037)); +const path = __importStar(__nccwpck_require__(1017)); +const oidc_utils_1 = __nccwpck_require__(7557); +/** + * The code to exit an action + */ +var ExitCode; +(function (ExitCode) { + /** + * A code indicating that the action was successful + */ + ExitCode[ExitCode["Success"] = 0] = "Success"; + /** + * A code indicating that the action was a failure + */ + ExitCode[ExitCode["Failure"] = 1] = "Failure"; +})(ExitCode = exports.ExitCode || (exports.ExitCode = {})); +//----------------------------------------------------------------------- +// Variables +//----------------------------------------------------------------------- +/** + * Sets env variable for this action and future actions in the job + * @param name the name of the variable to set + * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function exportVariable(name, val) { + const convertedVal = utils_1.toCommandValue(val); + process.env[name] = convertedVal; + const filePath = process.env['GITHUB_ENV'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('ENV', file_command_1.prepareKeyValueMessage(name, val)); } - moduleCoverages.sort((a, b) => { var _a, _b; return ((_a = b.overall.percentage) !== null && _a !== void 0 ? _a : 0) - ((_b = a.overall.percentage) !== null && _b !== void 0 ? _b : 0); }); - const totalFiles = moduleCoverages.flatMap(module => { - return module.files; - }); - const changedMissed = moduleCoverages - .map(module => module.changed.missed) - .reduce(sumReducer, 0.0); - const changedCovered = moduleCoverages - .map(module => module.changed.covered) - .reduce(sumReducer, 0.0); - const projectCoverage = getOverallProjectCoverage(reports); - const totalPercentage = getTotalPercentage(totalFiles); - return { - modules: moduleCoverages, - isMultiModule: reports.length > 1 || modules.length > 1, - overall: { - covered: projectCoverage.covered, - missed: projectCoverage.missed, - percentage: projectCoverage.percentage, - }, - changed: { - covered: changedCovered, - missed: changedMissed, - percentage: calculatePercentage(changedCovered, changedMissed), - }, - 'coverage-changed-files': totalPercentage !== null && totalPercentage !== void 0 ? totalPercentage : 100, - }; -} -exports.getProjectCoverage = getProjectCoverage; -function sumReducer(total, value) { - return total + value; -} -function toFloat(value) { - return parseFloat(value.toFixed(2)); -} -function getModulesFromReports(reports) { - const modules = []; - for (const report of reports) { - const groupTag = report[util_1.TAG.GROUP]; - if (groupTag) { - const groups = groupTag.filter((group) => group !== undefined); - for (const group of groups) { - const module = getModuleFromParent(group); - modules.push(module); - } - } - const module = getModuleFromParent(report); - if (module) { - modules.push(module); - } - } - return modules; -} -function getModuleFromParent(parent) { - const packageTag = parent[util_1.TAG.PACKAGE]; - if (packageTag) { - const packages = packageTag.filter((pacage) => pacage !== undefined); - if (packages.length !== 0) { - return { - name: parent['$'].name, - packages, - root: parent, // TODO just pass array of 'counters' - }; - } - } - return null; + command_1.issueCommand('set-env', { name }, convertedVal); } -function getFileCoverageFromPackages(packages, files) { - const resultFiles = []; - const jacocoFiles = (0, util_1.getFilesWithCoverage)(packages); - for (const jacocoFile of jacocoFiles) { - const name = jacocoFile.name; - const packageName = jacocoFile.packageName; - const githubFile = files.find(function (f) { - return f.filePath.endsWith(`${packageName}/${name}`); - }); - if (githubFile) { - const instruction = jacocoFile.counters.find(counter => counter.name === 'instruction'); - if (instruction) { - const missed = instruction.missed; - const covered = instruction.covered; - const lines = []; - for (const lineNumber of githubFile.lines) { - const jacocoLine = jacocoFile.lines.find(line => line.number === lineNumber); - if (jacocoLine) { - lines.push(Object.assign({}, jacocoLine)); - } - } - const changedMissed = lines - .map(line => toFloat(line.instruction.missed)) - .reduce(sumReducer, 0.0); - const changedCovered = lines - .map(line => toFloat(line.instruction.covered)) - .reduce(sumReducer, 0.0); - resultFiles.push({ - name, - url: githubFile.url, - overall: { - missed, - covered, - percentage: calculatePercentage(covered, missed), - }, - changed: { - missed: changedMissed, - covered: changedCovered, - percentage: calculatePercentage(changedCovered, changedMissed), - }, - lines, - }); - } - } - } - resultFiles.sort((a, b) => { var _a, _b; return ((_a = b.overall.percentage) !== null && _a !== void 0 ? _a : 0) - ((_b = a.overall.percentage) !== null && _b !== void 0 ? _b : 0); }); - return resultFiles; +exports.exportVariable = exportVariable; +/** + * Registers a secret which will get masked from logs + * @param secret value of the secret + */ +function setSecret(secret) { + command_1.issueCommand('add-mask', {}, secret); } -function calculatePercentage(covered, missed) { - const total = covered + missed; - if (total !== 0) { - return parseFloat(((covered / total) * 100).toFixed(2)); +exports.setSecret = setSecret; +/** + * Prepends inputPath to the PATH (for this action and future actions) + * @param inputPath + */ +function addPath(inputPath) { + const filePath = process.env['GITHUB_PATH'] || ''; + if (filePath) { + file_command_1.issueFileCommand('PATH', inputPath); } else { - return undefined; + command_1.issueCommand('add-path', {}, inputPath); } + process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; } -function getTotalPercentage(files) { - let missed = 0; - let covered = 0; - if (files.length !== 0) { - for (const file of files) { - missed += file.overall.missed; - covered += file.overall.covered; - } - return parseFloat(((covered / (covered + missed)) * 100).toFixed(2)); +exports.addPath = addPath; +/** + * Gets the value of an input. + * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. + * Returns an empty string if the value is not defined. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string + */ +function getInput(name, options) { + const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; + if (options && options.required && !val) { + throw new Error(`Input required and not supplied: ${name}`); } - else { - return null; + if (options && options.trimWhitespace === false) { + return val; } + return val.trim(); } -function getModuleCoverage(report) { - const counters = report['counter']; - return getDetailedCoverage(counters, 'INSTRUCTION'); -} -function getOverallProjectCoverage(reports) { - const coverages = reports.map(report => getDetailedCoverage(report['counter'], 'INSTRUCTION')); - const covered = coverages.reduce((acc, coverage) => acc + coverage.covered, 0); - const missed = coverages.reduce((acc, coverage) => acc + coverage.missed, 0); - return { - covered, - missed, - percentage: parseFloat(((covered / (covered + missed)) * 100).toFixed(2)), - }; -} -function getDetailedCoverage(counters, type) { - const counterTag = counters.find(counter => counter[util_1.TAG.SELF].type === type); - if (counterTag) { - const attr = counterTag[util_1.TAG.SELF]; - const missed = parseFloat(attr.missed); - const covered = parseFloat(attr.covered); - return { - missed, - covered, - percentage: parseFloat(((covered / (covered + missed)) * 100).toFixed(2)), - }; +exports.getInput = getInput; +/** + * Gets the values of an multiline input. Each value is also trimmed. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string[] + * + */ +function getMultilineInput(name, options) { + const inputs = getInput(name, options) + .split('\n') + .filter(x => x !== ''); + if (options && options.trimWhitespace === false) { + return inputs; } - return { missed: 0, covered: 0, percentage: 100 }; + return inputs.map(input => input.trim()); } - - -/***/ }), - -/***/ 6015: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getTitle = exports.getPRComment = void 0; -function getPRComment(project, minCoverage, title, emoji) { - const heading = getTitle(title); - const overallTable = getOverallTable(project, minCoverage, emoji); - const moduleTable = getModuleTable(project.modules, minCoverage, emoji); - const filesTable = getFileTable(project, minCoverage, emoji); - const tables = project.modules.length === 0 - ? '> There is no coverage information present for the Files changed' - : project.isMultiModule - ? `${moduleTable}\n\n${filesTable}` - : filesTable; - return `${heading + overallTable}\n\n${tables}`; +exports.getMultilineInput = getMultilineInput; +/** + * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. + * Support boolean input list: `true | True | TRUE | false | False | FALSE` . + * The return value is also in boolean type. + * ref: https://yaml.org/spec/1.2/spec.html#id2804923 + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns boolean + */ +function getBooleanInput(name, options) { + const trueValue = ['true', 'True', 'TRUE']; + const falseValue = ['false', 'False', 'FALSE']; + const val = getInput(name, options); + if (trueValue.includes(val)) + return true; + if (falseValue.includes(val)) + return false; + throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + + `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); } -exports.getPRComment = getPRComment; -function getModuleTable(modules, minCoverage, emoji) { - const tableHeader = '|Module|Coverage||'; - const tableStructure = '|:-|:-|:-:|'; - let table = `${tableHeader}\n${tableStructure}`; - for (const module of modules) { - const coverageDifference = getCoverageDifference(module.overall, module.changed); - renderRow(module.name, module.overall.percentage, coverageDifference, module.changed.percentage); - } - return table; - function renderRow(name, overallCoverage, coverageDiff, changedCoverage) { - const status = getStatus(changedCoverage, minCoverage.changed, emoji); - let coveragePercentage = `${formatCoverage(overallCoverage)}`; - if (shouldShow(coverageDiff)) { - coveragePercentage += ` **\`${formatCoverage(coverageDiff)}\`**`; - } - const row = `|${name}|${coveragePercentage}|${status}|`; - table = `${table}\n${row}`; +exports.getBooleanInput = getBooleanInput; +/** + * Sets the value of an output. + * + * @param name name of the output to set + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function setOutput(name, value) { + const filePath = process.env['GITHUB_OUTPUT'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('OUTPUT', file_command_1.prepareKeyValueMessage(name, value)); } + process.stdout.write(os.EOL); + command_1.issueCommand('set-output', { name }, utils_1.toCommandValue(value)); } -function getFileTable(project, minCoverage, emoji) { - const tableHeader = project.isMultiModule - ? '|Module|File|Coverage||' - : '|File|Coverage||'; - const tableStructure = project.isMultiModule - ? '|:-|:-|:-|:-:|' - : '|:-|:-|:-:|'; - let table = `${tableHeader}\n${tableStructure}`; - for (const module of project.modules) { - for (let index = 0; index < module.files.length; index++) { - const file = module.files[index]; - let moduleName = module.name; - if (index !== 0) { - moduleName = ''; - } - const coverageDifference = getCoverageDifference(file.overall, file.changed); - renderRow(moduleName, `[${file.name}](${file.url})`, file.overall.percentage, coverageDifference, file.changed.percentage, project.isMultiModule); - } - } - return project.isMultiModule - ? `
\nFiles\n\n${table}\n\n
` - : table; - function renderRow(moduleName, fileName, overallCoverage, coverageDiff, changedCoverage, isMultiModule) { - const status = getStatus(changedCoverage, minCoverage.changed, emoji); - let coveragePercentage = `${formatCoverage(overallCoverage)}`; - if (shouldShow(coverageDiff)) { - coveragePercentage += ` **\`${formatCoverage(coverageDiff)}\`**`; - } - const row = isMultiModule - ? `|${moduleName}|${fileName}|${coveragePercentage}|${status}|` - : `|${fileName}|${coveragePercentage}|${status}|`; - table = `${table}\n${row}`; - } +exports.setOutput = setOutput; +/** + * Enables or disables the echoing of commands into stdout for the rest of the step. + * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * + */ +function setCommandEcho(enabled) { + command_1.issue('echo', enabled ? 'on' : 'off'); } -function getCoverageDifference(overall, changed) { - const totalInstructions = overall.covered + overall.missed; - const missed = changed.missed; - return -(missed / totalInstructions) * 100; +exports.setCommandEcho = setCommandEcho; +//----------------------------------------------------------------------- +// Results +//----------------------------------------------------------------------- +/** + * Sets the action status to failed. + * When the action exits it will be with an exit code of 1 + * @param message add error issue message + */ +function setFailed(message) { + process.exitCode = ExitCode.Failure; + error(message); } -function getOverallTable(project, minCoverage, emoji) { - const overallStatus = getStatus(project.overall.percentage, minCoverage.overall, emoji); - const coverageDifference = getCoverageDifference(project.overall, project.changed); - let coveragePercentage = `${formatCoverage(project.overall.percentage)}`; - if (shouldShow(coverageDifference)) { - coveragePercentage += ` **\`${formatCoverage(coverageDifference)}\`**`; - } - const tableHeader = `|Overall Project|${coveragePercentage}|${overallStatus}|`; - const tableStructure = '|:-|:-|:-:|'; - const missedLines = project.changed.missed; - const coveredLines = project.changed.covered; - const totalChangedLines = missedLines + coveredLines; - let changedCoverageRow = ''; - if (totalChangedLines !== 0) { - const changedLinesPercentage = (coveredLines / totalChangedLines) * 100; - const filesChangedStatus = getStatus(changedLinesPercentage, minCoverage.changed, emoji); - changedCoverageRow = - '\n' + - `|Files changed|${formatCoverage(changedLinesPercentage)}|${filesChangedStatus}|` + - '\n
'; - } - return `${tableHeader}\n${tableStructure}${changedCoverageRow}`; +exports.setFailed = setFailed; +//----------------------------------------------------------------------- +// Logging Commands +//----------------------------------------------------------------------- +/** + * Gets whether Actions Step Debug is on or not + */ +function isDebug() { + return process.env['RUNNER_DEBUG'] === '1'; } -function round(value) { - return Math.round((value + Number.EPSILON) * 100) / 100; +exports.isDebug = isDebug; +/** + * Writes debug message to user log + * @param message debug message + */ +function debug(message) { + command_1.issueCommand('debug', {}, message); } -function shouldShow(value) { - const rounded = Math.abs(round(value)); - return rounded !== 0 && rounded !== 100; +exports.debug = debug; +/** + * Adds an error issue + * @param message error issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function error(message, properties = {}) { + command_1.issueCommand('error', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); } -function getTitle(title) { - if (title != null && title.trim().length > 0) { - const trimmed = title.trim(); - return trimmed.startsWith('#') ? `${trimmed}\n` : `### ${trimmed}\n`; - } - else { - return ''; - } +exports.error = error; +/** + * Adds a warning issue + * @param message warning issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function warning(message, properties = {}) { + command_1.issueCommand('warning', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); } -exports.getTitle = getTitle; -function getStatus(coverage, minCoverage, emoji) { - let status = emoji.pass; - if (coverage != null && coverage < minCoverage) { - status = emoji.fail; +exports.warning = warning; +/** + * Adds a notice issue + * @param message notice issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function notice(message, properties = {}) { + command_1.issueCommand('notice', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +exports.notice = notice; +/** + * Writes info to log with console.log. + * @param message info message + */ +function info(message) { + process.stdout.write(message + os.EOL); +} +exports.info = info; +/** + * Begin an output group. + * + * Output until the next `groupEnd` will be foldable in this group + * + * @param name The name of the output group + */ +function startGroup(name) { + command_1.issue('group', name); +} +exports.startGroup = startGroup; +/** + * End an output group. + */ +function endGroup() { + command_1.issue('endgroup'); +} +exports.endGroup = endGroup; +/** + * Wrap an asynchronous function call in a group. + * + * Returns the same type as the function itself. + * + * @param name The name of the group + * @param fn The function to wrap in the group + */ +function group(name, fn) { + return __awaiter(this, void 0, void 0, function* () { + startGroup(name); + let result; + try { + result = yield fn(); + } + finally { + endGroup(); + } + return result; + }); +} +exports.group = group; +//----------------------------------------------------------------------- +// Wrapper action state +//----------------------------------------------------------------------- +/** + * Saves state for current action, the state can only be retrieved by this action's post job execution. + * + * @param name name of the state to store + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function saveState(name, value) { + const filePath = process.env['GITHUB_STATE'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('STATE', file_command_1.prepareKeyValueMessage(name, value)); } - return status; + command_1.issueCommand('save-state', { name }, utils_1.toCommandValue(value)); } -function formatCoverage(coverage) { - if (coverage == null) - return 'NaN%'; - return `${toFloat(coverage)}%`; +exports.saveState = saveState; +/** + * Gets the value of an state set by this action's main execution. + * + * @param name name of the state to get + * @returns string + */ +function getState(name) { + return process.env[`STATE_${name}`] || ''; } -function toFloat(value) { - return parseFloat(value.toFixed(2)); +exports.getState = getState; +function getIDToken(aud) { + return __awaiter(this, void 0, void 0, function* () { + return yield oidc_utils_1.OidcClient.getIDToken(aud); + }); } - +exports.getIDToken = getIDToken; +/** + * Summary exports + */ +var summary_1 = __nccwpck_require__(9029); +Object.defineProperty(exports, "summary", ({ enumerable: true, get: function () { return summary_1.summary; } })); +/** + * @deprecated use core.summary + */ +var summary_2 = __nccwpck_require__(9029); +Object.defineProperty(exports, "markdownSummary", ({ enumerable: true, get: function () { return summary_2.markdownSummary; } })); +/** + * Path exports + */ +var path_utils_1 = __nccwpck_require__(8064); +Object.defineProperty(exports, "toPosixPath", ({ enumerable: true, get: function () { return path_utils_1.toPosixPath; } })); +Object.defineProperty(exports, "toWin32Path", ({ enumerable: true, get: function () { return path_utils_1.toWin32Path; } })); +Object.defineProperty(exports, "toPlatformPath", ({ enumerable: true, get: function () { return path_utils_1.toPlatformPath; } })); +//# sourceMappingURL=core.js.map /***/ }), -/***/ 9410: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getFilesWithCoverage = exports.getChangedLines = exports.debug = exports.TAG = void 0; -exports.TAG = { - SELF: '$', - SOURCE_FILE: 'sourcefile', - LINE: 'line', - COUNTER: 'counter', - PACKAGE: 'package', - GROUP: 'group', -}; -function debug(obj) { - return JSON.stringify(obj, null, 4); -} -exports.debug = debug; -const pattern = /^@@ -([0-9]*),?\S* \+([0-9]*),?/; -function getChangedLines(patch) { - const lineNumbers = new Set(); - if (patch) { - const lines = patch.split('\n'); - const groups = getDiffGroups(lines); - for (const group of groups) { - const firstLine = group.shift(); - if (firstLine) { - const diffGroup = firstLine.match(pattern); - if (diffGroup) { - let bX = parseInt(diffGroup[2]); - for (const line of group) { - bX++; - if (line.startsWith('+')) { - lineNumbers.add(bX - 1); - } - else if (line.startsWith('-')) { - bX--; - } - } - } - } - } - } - return [...lineNumbers]; -} -exports.getChangedLines = getChangedLines; -function getDiffGroups(lines) { - const groups = []; - let group = []; - for (const line of lines) { - if (line.startsWith('@@')) { - group = []; - groups.push(group); - } - group.push(line); - } - return groups; -} -/* eslint-disable @typescript-eslint/no-explicit-any */ -function getFilesWithCoverage(packages) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; - const files = []; - for (const item of packages) { - const packageName = item[exports.TAG.SELF].name; - const sourceFiles = (_a = item[exports.TAG.SOURCE_FILE]) !== null && _a !== void 0 ? _a : []; - for (const sourceFile of sourceFiles) { - const sourceFileName = sourceFile[exports.TAG.SELF].name; - const file = { - name: sourceFileName, - packageName, - lines: [], - counters: [], - }; - const counters = (_b = sourceFile[exports.TAG.COUNTER]) !== null && _b !== void 0 ? _b : []; - for (const counter of counters) { - const counterSelf = counter[exports.TAG.SELF]; - const type = counterSelf.type; - file.counters.push({ - name: type.toLowerCase(), - missed: (_c = parseInt(counterSelf.missed)) !== null && _c !== void 0 ? _c : 0, - covered: (_d = parseInt(counterSelf.covered)) !== null && _d !== void 0 ? _d : 0, - }); - } - const lines = (_e = sourceFile[exports.TAG.LINE]) !== null && _e !== void 0 ? _e : []; - for (const line of lines) { - const lineSelf = line[exports.TAG.SELF]; - file.lines.push({ - number: (_f = parseInt(lineSelf.nr)) !== null && _f !== void 0 ? _f : 0, - instruction: { - missed: (_g = parseInt(lineSelf.mi)) !== null && _g !== void 0 ? _g : 0, - covered: (_h = parseInt(lineSelf.ci)) !== null && _h !== void 0 ? _h : 0, - }, - branch: { - missed: (_j = parseInt(lineSelf.mb)) !== null && _j !== void 0 ? _j : 0, - covered: (_k = parseInt(lineSelf.cb)) !== null && _k !== void 0 ? _k : 0, - }, - }); - } - files.push(file); - } - } - return files; -} -exports.getFilesWithCoverage = getFilesWithCoverage; - - -/***/ }), - -/***/ 5350: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +/***/ 8466: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; +// For internal use, subject to change. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); @@ -697,104 +469,50 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.issue = exports.issueCommand = void 0; +exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ +const fs = __importStar(__nccwpck_require__(7147)); const os = __importStar(__nccwpck_require__(2037)); +const uuid_1 = __nccwpck_require__(2574); const utils_1 = __nccwpck_require__(7369); -/** - * Commands - * - * Command Format: - * ::name key=value,key=value::message - * - * Examples: - * ::warning::This is the message - * ::set-env name=MY_VAR::some value - */ -function issueCommand(command, properties, message) { - const cmd = new Command(command, properties, message); - process.stdout.write(cmd.toString() + os.EOL); -} -exports.issueCommand = issueCommand; -function issue(name, message = '') { - issueCommand(name, {}, message); -} -exports.issue = issue; -const CMD_STRING = '::'; -class Command { - constructor(command, properties, message) { - if (!command) { - command = 'missing.command'; - } - this.command = command; - this.properties = properties; - this.message = message; +function issueFileCommand(command, message) { + const filePath = process.env[`GITHUB_${command}`]; + if (!filePath) { + throw new Error(`Unable to find environment variable for file command ${command}`); } - toString() { - let cmdStr = CMD_STRING + this.command; - if (this.properties && Object.keys(this.properties).length > 0) { - cmdStr += ' '; - let first = true; - for (const key in this.properties) { - if (this.properties.hasOwnProperty(key)) { - const val = this.properties[key]; - if (val) { - if (first) { - first = false; - } - else { - cmdStr += ','; - } - cmdStr += `${key}=${escapeProperty(val)}`; - } - } - } - } - cmdStr += `${CMD_STRING}${escapeData(this.message)}`; - return cmdStr; + if (!fs.existsSync(filePath)) { + throw new Error(`Missing file at path: ${filePath}`); } + fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, { + encoding: 'utf8' + }); } -function escapeData(s) { - return utils_1.toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A'); -} -function escapeProperty(s) { - return utils_1.toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A') - .replace(/:/g, '%3A') - .replace(/,/g, '%2C'); +exports.issueFileCommand = issueFileCommand; +function prepareKeyValueMessage(key, value) { + const delimiter = `ghadelimiter_${uuid_1.v4()}`; + const convertedValue = utils_1.toCommandValue(value); + // These should realistically never happen, but just in case someone finds a + // way to exploit uuid generation let's not allow keys or values that contain + // the delimiter. + if (key.includes(delimiter)) { + throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); + } + if (convertedValue.includes(delimiter)) { + throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); + } + return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; } -//# sourceMappingURL=command.js.map +exports.prepareKeyValueMessage = prepareKeyValueMessage; +//# sourceMappingURL=file-command.js.map /***/ }), -/***/ 6024: +/***/ 7557: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -805,381 +523,141 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getIDToken = exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0; -const command_1 = __nccwpck_require__(5350); -const file_command_1 = __nccwpck_require__(8466); -const utils_1 = __nccwpck_require__(7369); -const os = __importStar(__nccwpck_require__(2037)); -const path = __importStar(__nccwpck_require__(1017)); -const oidc_utils_1 = __nccwpck_require__(7557); -/** - * The code to exit an action - */ -var ExitCode; -(function (ExitCode) { - /** - * A code indicating that the action was successful - */ - ExitCode[ExitCode["Success"] = 0] = "Success"; - /** - * A code indicating that the action was a failure - */ - ExitCode[ExitCode["Failure"] = 1] = "Failure"; -})(ExitCode = exports.ExitCode || (exports.ExitCode = {})); -//----------------------------------------------------------------------- -// Variables -//----------------------------------------------------------------------- -/** - * Sets env variable for this action and future actions in the job - * @param name the name of the variable to set - * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function exportVariable(name, val) { - const convertedVal = utils_1.toCommandValue(val); - process.env[name] = convertedVal; - const filePath = process.env['GITHUB_ENV'] || ''; - if (filePath) { - return file_command_1.issueFileCommand('ENV', file_command_1.prepareKeyValueMessage(name, val)); +exports.OidcClient = void 0; +const http_client_1 = __nccwpck_require__(2745); +const auth_1 = __nccwpck_require__(2834); +const core_1 = __nccwpck_require__(6024); +class OidcClient { + static createHttpClient(allowRetry = true, maxRetry = 10) { + const requestOptions = { + allowRetries: allowRetry, + maxRetries: maxRetry + }; + return new http_client_1.HttpClient('actions/oidc-client', [new auth_1.BearerCredentialHandler(OidcClient.getRequestToken())], requestOptions); } - command_1.issueCommand('set-env', { name }, convertedVal); -} -exports.exportVariable = exportVariable; -/** - * Registers a secret which will get masked from logs - * @param secret value of the secret - */ -function setSecret(secret) { - command_1.issueCommand('add-mask', {}, secret); -} -exports.setSecret = setSecret; -/** - * Prepends inputPath to the PATH (for this action and future actions) - * @param inputPath - */ -function addPath(inputPath) { - const filePath = process.env['GITHUB_PATH'] || ''; - if (filePath) { - file_command_1.issueFileCommand('PATH', inputPath); - } - else { - command_1.issueCommand('add-path', {}, inputPath); + static getRequestToken() { + const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']; + if (!token) { + throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'); + } + return token; } - process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; -} -exports.addPath = addPath; -/** - * Gets the value of an input. - * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. - * Returns an empty string if the value is not defined. - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns string - */ -function getInput(name, options) { - const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; - if (options && options.required && !val) { - throw new Error(`Input required and not supplied: ${name}`); + static getIDTokenUrl() { + const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']; + if (!runtimeUrl) { + throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'); + } + return runtimeUrl; } - if (options && options.trimWhitespace === false) { - return val; + static getCall(id_token_url) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const httpclient = OidcClient.createHttpClient(); + const res = yield httpclient + .getJson(id_token_url) + .catch(error => { + throw new Error(`Failed to get ID Token. \n + Error Code : ${error.statusCode}\n + Error Message: ${error.result.message}`); + }); + const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; + if (!id_token) { + throw new Error('Response json body do not have ID Token field'); + } + return id_token; + }); } - return val.trim(); -} -exports.getInput = getInput; -/** - * Gets the values of an multiline input. Each value is also trimmed. - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns string[] - * - */ -function getMultilineInput(name, options) { - const inputs = getInput(name, options) - .split('\n') - .filter(x => x !== ''); - if (options && options.trimWhitespace === false) { - return inputs; + static getIDToken(audience) { + return __awaiter(this, void 0, void 0, function* () { + try { + // New ID Token is requested from action service + let id_token_url = OidcClient.getIDTokenUrl(); + if (audience) { + const encodedAudience = encodeURIComponent(audience); + id_token_url = `${id_token_url}&audience=${encodedAudience}`; + } + core_1.debug(`ID token url is ${id_token_url}`); + const id_token = yield OidcClient.getCall(id_token_url); + core_1.setSecret(id_token); + return id_token; + } + catch (error) { + throw new Error(`Error message: ${error.message}`); + } + }); } - return inputs.map(input => input.trim()); } -exports.getMultilineInput = getMultilineInput; +exports.OidcClient = OidcClient; +//# sourceMappingURL=oidc-utils.js.map + +/***/ }), + +/***/ 8064: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = void 0; +const path = __importStar(__nccwpck_require__(1017)); /** - * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. - * Support boolean input list: `true | True | TRUE | false | False | FALSE` . - * The return value is also in boolean type. - * ref: https://yaml.org/spec/1.2/spec.html#id2804923 + * toPosixPath converts the given path to the posix form. On Windows, \\ will be + * replaced with /. * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns boolean + * @param pth. Path to transform. + * @return string Posix path. */ -function getBooleanInput(name, options) { - const trueValue = ['true', 'True', 'TRUE']; - const falseValue = ['false', 'False', 'FALSE']; - const val = getInput(name, options); - if (trueValue.includes(val)) - return true; - if (falseValue.includes(val)) - return false; - throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + - `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); +function toPosixPath(pth) { + return pth.replace(/[\\]/g, '/'); } -exports.getBooleanInput = getBooleanInput; +exports.toPosixPath = toPosixPath; /** - * Sets the value of an output. + * toWin32Path converts the given path to the win32 form. On Linux, / will be + * replaced with \\. * - * @param name name of the output to set - * @param value value to store. Non-string values will be converted to a string via JSON.stringify + * @param pth. Path to transform. + * @return string Win32 path. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function setOutput(name, value) { - const filePath = process.env['GITHUB_OUTPUT'] || ''; - if (filePath) { - return file_command_1.issueFileCommand('OUTPUT', file_command_1.prepareKeyValueMessage(name, value)); - } - process.stdout.write(os.EOL); - command_1.issueCommand('set-output', { name }, utils_1.toCommandValue(value)); +function toWin32Path(pth) { + return pth.replace(/[/]/g, '\\'); } -exports.setOutput = setOutput; +exports.toWin32Path = toWin32Path; /** - * Enables or disables the echoing of commands into stdout for the rest of the step. - * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * toPlatformPath converts the given path to a platform-specific path. It does + * this by replacing instances of / and \ with the platform-specific path + * separator. * + * @param pth The path to platformize. + * @return string The platform-specific path. */ -function setCommandEcho(enabled) { - command_1.issue('echo', enabled ? 'on' : 'off'); -} -exports.setCommandEcho = setCommandEcho; -//----------------------------------------------------------------------- -// Results -//----------------------------------------------------------------------- -/** - * Sets the action status to failed. - * When the action exits it will be with an exit code of 1 - * @param message add error issue message - */ -function setFailed(message) { - process.exitCode = ExitCode.Failure; - error(message); -} -exports.setFailed = setFailed; -//----------------------------------------------------------------------- -// Logging Commands -//----------------------------------------------------------------------- -/** - * Gets whether Actions Step Debug is on or not - */ -function isDebug() { - return process.env['RUNNER_DEBUG'] === '1'; -} -exports.isDebug = isDebug; -/** - * Writes debug message to user log - * @param message debug message - */ -function debug(message) { - command_1.issueCommand('debug', {}, message); -} -exports.debug = debug; -/** - * Adds an error issue - * @param message error issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function error(message, properties = {}) { - command_1.issueCommand('error', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -exports.error = error; -/** - * Adds a warning issue - * @param message warning issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function warning(message, properties = {}) { - command_1.issueCommand('warning', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -exports.warning = warning; -/** - * Adds a notice issue - * @param message notice issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function notice(message, properties = {}) { - command_1.issueCommand('notice', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -exports.notice = notice; -/** - * Writes info to log with console.log. - * @param message info message - */ -function info(message) { - process.stdout.write(message + os.EOL); +function toPlatformPath(pth) { + return pth.replace(/[/\\]/g, path.sep); } -exports.info = info; -/** - * Begin an output group. - * - * Output until the next `groupEnd` will be foldable in this group - * - * @param name The name of the output group - */ -function startGroup(name) { - command_1.issue('group', name); -} -exports.startGroup = startGroup; -/** - * End an output group. - */ -function endGroup() { - command_1.issue('endgroup'); -} -exports.endGroup = endGroup; -/** - * Wrap an asynchronous function call in a group. - * - * Returns the same type as the function itself. - * - * @param name The name of the group - * @param fn The function to wrap in the group - */ -function group(name, fn) { - return __awaiter(this, void 0, void 0, function* () { - startGroup(name); - let result; - try { - result = yield fn(); - } - finally { - endGroup(); - } - return result; - }); -} -exports.group = group; -//----------------------------------------------------------------------- -// Wrapper action state -//----------------------------------------------------------------------- -/** - * Saves state for current action, the state can only be retrieved by this action's post job execution. - * - * @param name name of the state to store - * @param value value to store. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function saveState(name, value) { - const filePath = process.env['GITHUB_STATE'] || ''; - if (filePath) { - return file_command_1.issueFileCommand('STATE', file_command_1.prepareKeyValueMessage(name, value)); - } - command_1.issueCommand('save-state', { name }, utils_1.toCommandValue(value)); -} -exports.saveState = saveState; -/** - * Gets the value of an state set by this action's main execution. - * - * @param name name of the state to get - * @returns string - */ -function getState(name) { - return process.env[`STATE_${name}`] || ''; -} -exports.getState = getState; -function getIDToken(aud) { - return __awaiter(this, void 0, void 0, function* () { - return yield oidc_utils_1.OidcClient.getIDToken(aud); - }); -} -exports.getIDToken = getIDToken; -/** - * Summary exports - */ -var summary_1 = __nccwpck_require__(9029); -Object.defineProperty(exports, "summary", ({ enumerable: true, get: function () { return summary_1.summary; } })); -/** - * @deprecated use core.summary - */ -var summary_2 = __nccwpck_require__(9029); -Object.defineProperty(exports, "markdownSummary", ({ enumerable: true, get: function () { return summary_2.markdownSummary; } })); -/** - * Path exports - */ -var path_utils_1 = __nccwpck_require__(8064); -Object.defineProperty(exports, "toPosixPath", ({ enumerable: true, get: function () { return path_utils_1.toPosixPath; } })); -Object.defineProperty(exports, "toWin32Path", ({ enumerable: true, get: function () { return path_utils_1.toWin32Path; } })); -Object.defineProperty(exports, "toPlatformPath", ({ enumerable: true, get: function () { return path_utils_1.toPlatformPath; } })); -//# sourceMappingURL=core.js.map - -/***/ }), - -/***/ 8466: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -// For internal use, subject to change. -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; -// We use any as a valid input type -/* eslint-disable @typescript-eslint/no-explicit-any */ -const fs = __importStar(__nccwpck_require__(7147)); -const os = __importStar(__nccwpck_require__(2037)); -const uuid_1 = __nccwpck_require__(2574); -const utils_1 = __nccwpck_require__(7369); -function issueFileCommand(command, message) { - const filePath = process.env[`GITHUB_${command}`]; - if (!filePath) { - throw new Error(`Unable to find environment variable for file command ${command}`); - } - if (!fs.existsSync(filePath)) { - throw new Error(`Missing file at path: ${filePath}`); - } - fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, { - encoding: 'utf8' - }); -} -exports.issueFileCommand = issueFileCommand; -function prepareKeyValueMessage(key, value) { - const delimiter = `ghadelimiter_${uuid_1.v4()}`; - const convertedValue = utils_1.toCommandValue(value); - // These should realistically never happen, but just in case someone finds a - // way to exploit uuid generation let's not allow keys or values that contain - // the delimiter. - if (key.includes(delimiter)) { - throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); - } - if (convertedValue.includes(delimiter)) { - throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); - } - return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; -} -exports.prepareKeyValueMessage = prepareKeyValueMessage; -//# sourceMappingURL=file-command.js.map +exports.toPlatformPath = toPlatformPath; +//# sourceMappingURL=path-utils.js.map /***/ }), -/***/ 7557: +/***/ 9029: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -1194,188 +672,39 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.OidcClient = void 0; -const http_client_1 = __nccwpck_require__(2745); -const auth_1 = __nccwpck_require__(2834); -const core_1 = __nccwpck_require__(6024); -class OidcClient { - static createHttpClient(allowRetry = true, maxRetry = 10) { - const requestOptions = { - allowRetries: allowRetry, - maxRetries: maxRetry - }; - return new http_client_1.HttpClient('actions/oidc-client', [new auth_1.BearerCredentialHandler(OidcClient.getRequestToken())], requestOptions); - } - static getRequestToken() { - const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']; - if (!token) { - throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'); - } - return token; - } - static getIDTokenUrl() { - const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']; - if (!runtimeUrl) { - throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'); - } - return runtimeUrl; +exports.summary = exports.markdownSummary = exports.SUMMARY_DOCS_URL = exports.SUMMARY_ENV_VAR = void 0; +const os_1 = __nccwpck_require__(2037); +const fs_1 = __nccwpck_require__(7147); +const { access, appendFile, writeFile } = fs_1.promises; +exports.SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'; +exports.SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'; +class Summary { + constructor() { + this._buffer = ''; } - static getCall(id_token_url) { - var _a; + /** + * Finds the summary file path from the environment, rejects if env var is not found or file does not exist + * Also checks r/w permissions. + * + * @returns step summary file path + */ + filePath() { return __awaiter(this, void 0, void 0, function* () { - const httpclient = OidcClient.createHttpClient(); - const res = yield httpclient - .getJson(id_token_url) - .catch(error => { - throw new Error(`Failed to get ID Token. \n - Error Code : ${error.statusCode}\n - Error Message: ${error.result.message}`); - }); - const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; - if (!id_token) { - throw new Error('Response json body do not have ID Token field'); + if (this._filePath) { + return this._filePath; + } + const pathFromEnv = process.env[exports.SUMMARY_ENV_VAR]; + if (!pathFromEnv) { + throw new Error(`Unable to find environment variable for $${exports.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`); } - return id_token; - }); - } - static getIDToken(audience) { - return __awaiter(this, void 0, void 0, function* () { try { - // New ID Token is requested from action service - let id_token_url = OidcClient.getIDTokenUrl(); - if (audience) { - const encodedAudience = encodeURIComponent(audience); - id_token_url = `${id_token_url}&audience=${encodedAudience}`; - } - core_1.debug(`ID token url is ${id_token_url}`); - const id_token = yield OidcClient.getCall(id_token_url); - core_1.setSecret(id_token); - return id_token; + yield access(pathFromEnv, fs_1.constants.R_OK | fs_1.constants.W_OK); } - catch (error) { - throw new Error(`Error message: ${error.message}`); + catch (_a) { + throw new Error(`Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.`); } - }); - } -} -exports.OidcClient = OidcClient; -//# sourceMappingURL=oidc-utils.js.map - -/***/ }), - -/***/ 8064: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = void 0; -const path = __importStar(__nccwpck_require__(1017)); -/** - * toPosixPath converts the given path to the posix form. On Windows, \\ will be - * replaced with /. - * - * @param pth. Path to transform. - * @return string Posix path. - */ -function toPosixPath(pth) { - return pth.replace(/[\\]/g, '/'); -} -exports.toPosixPath = toPosixPath; -/** - * toWin32Path converts the given path to the win32 form. On Linux, / will be - * replaced with \\. - * - * @param pth. Path to transform. - * @return string Win32 path. - */ -function toWin32Path(pth) { - return pth.replace(/[/]/g, '\\'); -} -exports.toWin32Path = toWin32Path; -/** - * toPlatformPath converts the given path to a platform-specific path. It does - * this by replacing instances of / and \ with the platform-specific path - * separator. - * - * @param pth The path to platformize. - * @return string The platform-specific path. - */ -function toPlatformPath(pth) { - return pth.replace(/[/\\]/g, path.sep); -} -exports.toPlatformPath = toPlatformPath; -//# sourceMappingURL=path-utils.js.map - -/***/ }), - -/***/ 9029: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.summary = exports.markdownSummary = exports.SUMMARY_DOCS_URL = exports.SUMMARY_ENV_VAR = void 0; -const os_1 = __nccwpck_require__(2037); -const fs_1 = __nccwpck_require__(7147); -const { access, appendFile, writeFile } = fs_1.promises; -exports.SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'; -exports.SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'; -class Summary { - constructor() { - this._buffer = ''; - } - /** - * Finds the summary file path from the environment, rejects if env var is not found or file does not exist - * Also checks r/w permissions. - * - * @returns step summary file path - */ - filePath() { - return __awaiter(this, void 0, void 0, function* () { - if (this._filePath) { - return this._filePath; - } - const pathFromEnv = process.env[exports.SUMMARY_ENV_VAR]; - if (!pathFromEnv) { - throw new Error(`Unable to find environment variable for $${exports.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`); - } - try { - yield access(pathFromEnv, fs_1.constants.R_OK | fs_1.constants.W_OK); - } - catch (_a) { - throw new Error(`Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.`); - } - this._filePath = pathFromEnv; - return this._filePath; + this._filePath = pathFromEnv; + return this._filePath; }); } /** @@ -17396,7 +16725,7 @@ function wrappy (fn, cb) { XMLProcessingInstruction = __nccwpck_require__(8951); XMLDummy = __nccwpck_require__(9230); NodeType = __nccwpck_require__(7777); - XMLNodeList = __nccwpck_require__(7289); + XMLNodeList = __nccwpck_require__(8902); XMLNamedNodeMap = __nccwpck_require__(4533); DocumentPosition = __nccwpck_require__(2273); } @@ -18131,7 +17460,7 @@ function wrappy (fn, cb) { /***/ }), -/***/ 7289: +/***/ 8902: /***/ (function(module) { // Generated by CoffeeScript 1.12.7 @@ -19317,6 +18646,671 @@ function wrappy (fn, cb) { }).call(this); +/***/ }), + +/***/ 7289: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.action = void 0; +/* eslint-disable @typescript-eslint/no-explicit-any */ +const core = __importStar(__nccwpck_require__(6024)); +const github = __importStar(__nccwpck_require__(5016)); +const fs = __importStar(__nccwpck_require__(7147)); +const xml2js_1 = __importDefault(__nccwpck_require__(9253)); +const processors_1 = __nccwpck_require__(6434); +const glob = __importStar(__nccwpck_require__(2723)); +const process_1 = __nccwpck_require__(3709); +const render_1 = __nccwpck_require__(6197); +const util_1 = __nccwpck_require__(2090); +async function action() { + let continueOnError = true; + try { + const token = core.getInput('token'); + if (!token) { + core.setFailed("'token' is missing"); + return; + } + const pathsString = core.getInput('paths'); + if (!pathsString) { + core.setFailed("'paths' is missing"); + return; + } + const reportPaths = pathsString.split(','); + const minCoverageOverall = parseFloat(core.getInput('min-coverage-overall')); + const minCoverageChangedFiles = parseFloat(core.getInput('min-coverage-changed-files')); + const title = core.getInput('title'); + const updateComment = (0, processors_1.parseBooleans)(core.getInput('update-comment')); + if (updateComment) { + if (!title) { + core.info("'title' is not set. 'update-comment' does not work without 'title'"); + } + } + const skipIfNoChanges = (0, processors_1.parseBooleans)(core.getInput('skip-if-no-changes')); + const passEmoji = core.getInput('pass-emoji'); + const failEmoji = core.getInput('fail-emoji'); + continueOnError = (0, processors_1.parseBooleans)(core.getInput('continue-on-error')); + const debugMode = (0, processors_1.parseBooleans)(core.getInput('debug-mode')); + const event = github.context.eventName; + core.info(`Event is ${event}`); + if (debugMode) { + core.info(`passEmoji: ${passEmoji}`); + core.info(`failEmoji: ${failEmoji}`); + } + let base; + let head; + let prNumber; + switch (event) { + case 'pull_request': + case 'pull_request_target': + base = github.context.payload.pull_request?.base.sha; + head = github.context.payload.pull_request?.head.sha; + prNumber = github.context.payload.pull_request?.number; + break; + case 'push': + base = github.context.payload.before; + head = github.context.payload.after; + break; + default: + core.setFailed(`Only pull requests and pushes are supported, ${github.context.eventName} not supported.`); + return; + } + core.info(`base sha: ${base}`); + core.info(`head sha: ${head}`); + const client = github.getOctokit(token); + if (debugMode) + core.info(`reportPaths: ${reportPaths}`); + const reportsJsonAsync = getJsonReports(reportPaths, debugMode); + const changedFiles = await getChangedFiles(base, head, client, debugMode); + if (debugMode) + core.info(`changedFiles: ${(0, util_1.debug)(changedFiles)}`); + const reportsJson = await reportsJsonAsync; + const reports = reportsJson.map(report => report['report']); + const project = (0, process_1.getProjectCoverage)(reports, changedFiles); + if (debugMode) + core.info(`project: ${(0, util_1.debug)(project)}`); + core.setOutput('coverage-overall', parseFloat((project.overall.percentage ?? 0).toFixed(2))); + core.setOutput('coverage-changed-files', parseFloat(project['coverage-changed-files'].toFixed(2))); + const skip = skipIfNoChanges && project.modules.length === 0; + if (debugMode) + core.info(`skip: ${skip}`); + if (debugMode) + core.info(`prNumber: ${prNumber}`); + if (prNumber != null && !skip) { + const emoji = { + pass: passEmoji, + fail: failEmoji, + }; + await addComment(prNumber, updateComment, (0, render_1.getTitle)(title), (0, render_1.getPRComment)(project, { + overall: minCoverageOverall, + changed: minCoverageChangedFiles, + }, title, emoji), client, debugMode); + } + } + catch (error) { + if (error instanceof Error) { + if (continueOnError) { + core.error(error); + } + else { + core.setFailed(error); + } + } + } +} +exports.action = action; +async function getJsonReports(xmlPaths, debugMode) { + const globber = await glob.create(xmlPaths.join('\n')); + const files = await globber.glob(); + if (debugMode) + core.info(`Resolved files: ${files}`); + return Promise.all(files.map(async (path) => { + const reportXml = await fs.promises.readFile(path.trim(), 'utf-8'); + return await xml2js_1.default.parseStringPromise(reportXml); + })); +} +async function getChangedFiles(base, head, client, debugMode) { + const response = await client.rest.repos.compareCommits({ + base, + head, + owner: github.context.repo.owner, + repo: github.context.repo.repo, + }); + const changedFiles = []; + for (const file of response.data.files) { + if (debugMode) + core.info(`file: ${(0, util_1.debug)(file)}`); + const changedFile = { + filePath: file.filename, + url: file.blob_url, + lines: (0, util_1.getChangedLines)(file.patch), + }; + changedFiles.push(changedFile); + } + return changedFiles; +} +async function addComment(prNumber, update, title, body, client, debugMode) { + let commentUpdated = false; + if (debugMode) + core.info(`update: ${update}`); + if (debugMode) + core.info(`title: ${title}`); + if (debugMode) + core.info(`JaCoCo Comment: ${body}`); + if (update && title) { + if (debugMode) + core.info('Listing all comments'); + const comments = await client.rest.issues.listComments({ + issue_number: prNumber, + ...github.context.repo, + }); + const comment = comments.data.find((it) => it.body.startsWith(title)); + if (comment) { + if (debugMode) + core.info(`Updating existing comment: id=${comment.id} \n body=${comment.body}`); + await client.rest.issues.updateComment({ + comment_id: comment.id, + body, + ...github.context.repo, + }); + commentUpdated = true; + } + } + if (!commentUpdated) { + if (debugMode) + core.info('Creating a new comment'); + await client.rest.issues.createComment({ + issue_number: prNumber, + body, + ...github.context.repo, + }); + } +} + + +/***/ }), + +/***/ 3709: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getProjectCoverage = void 0; +const util_1 = __nccwpck_require__(2090); +/* eslint-disable @typescript-eslint/no-explicit-any */ +function getProjectCoverage(reports, changedFiles) { + const moduleCoverages = []; + const modules = getModulesFromReports(reports); + for (const module of modules) { + const files = getFileCoverageFromPackages([].concat(...module.packages), changedFiles); + if (files.length !== 0) { + const moduleCoverage = getModuleCoverage(module.root); + const changedMissed = files + .map(file => file.changed.missed) + .reduce(sumReducer, 0.0); + const changedCovered = files + .map(file => file.changed.covered) + .reduce(sumReducer, 0.0); + moduleCoverages.push({ + name: module.name, + files, + overall: { + percentage: moduleCoverage.percentage, + covered: moduleCoverage.covered, + missed: moduleCoverage.missed, + }, + changed: { + covered: changedCovered, + missed: changedMissed, + percentage: calculatePercentage(changedCovered, changedMissed), + }, + }); + } + } + moduleCoverages.sort((a, b) => (b.overall.percentage ?? 0) - (a.overall.percentage ?? 0)); + const totalFiles = moduleCoverages.flatMap(module => { + return module.files; + }); + const changedMissed = moduleCoverages + .map(module => module.changed.missed) + .reduce(sumReducer, 0.0); + const changedCovered = moduleCoverages + .map(module => module.changed.covered) + .reduce(sumReducer, 0.0); + const projectCoverage = getOverallProjectCoverage(reports); + const totalPercentage = getTotalPercentage(totalFiles); + return { + modules: moduleCoverages, + isMultiModule: reports.length > 1 || modules.length > 1, + overall: { + covered: projectCoverage.covered, + missed: projectCoverage.missed, + percentage: projectCoverage.percentage, + }, + changed: { + covered: changedCovered, + missed: changedMissed, + percentage: calculatePercentage(changedCovered, changedMissed), + }, + 'coverage-changed-files': totalPercentage ?? 100, + }; +} +exports.getProjectCoverage = getProjectCoverage; +function sumReducer(total, value) { + return total + value; +} +function toFloat(value) { + return parseFloat(value.toFixed(2)); +} +function getModulesFromReports(reports) { + const modules = []; + for (const report of reports) { + const groupTag = report[util_1.TAG.GROUP]; + if (groupTag) { + const groups = groupTag.filter((group) => group !== undefined); + for (const group of groups) { + const module = getModuleFromParent(group); + modules.push(module); + } + } + const module = getModuleFromParent(report); + if (module) { + modules.push(module); + } + } + return modules; +} +function getModuleFromParent(parent) { + const packageTag = parent[util_1.TAG.PACKAGE]; + if (packageTag) { + const packages = packageTag.filter((pacage) => pacage !== undefined); + if (packages.length !== 0) { + return { + name: parent['$'].name, + packages, + root: parent, // TODO just pass array of 'counters' + }; + } + } + return null; +} +function getFileCoverageFromPackages(packages, files) { + const resultFiles = []; + const jacocoFiles = (0, util_1.getFilesWithCoverage)(packages); + for (const jacocoFile of jacocoFiles) { + const name = jacocoFile.name; + const packageName = jacocoFile.packageName; + const githubFile = files.find(function (f) { + return f.filePath.endsWith(`${packageName}/${name}`); + }); + if (githubFile) { + const instruction = jacocoFile.counters.find(counter => counter.name === 'instruction'); + if (instruction) { + const missed = instruction.missed; + const covered = instruction.covered; + const lines = []; + for (const lineNumber of githubFile.lines) { + const jacocoLine = jacocoFile.lines.find(line => line.number === lineNumber); + if (jacocoLine) { + lines.push({ + ...jacocoLine, + }); + } + } + const changedMissed = lines + .map(line => toFloat(line.instruction.missed)) + .reduce(sumReducer, 0.0); + const changedCovered = lines + .map(line => toFloat(line.instruction.covered)) + .reduce(sumReducer, 0.0); + resultFiles.push({ + name, + url: githubFile.url, + overall: { + missed, + covered, + percentage: calculatePercentage(covered, missed), + }, + changed: { + missed: changedMissed, + covered: changedCovered, + percentage: calculatePercentage(changedCovered, changedMissed), + }, + lines, + }); + } + } + } + resultFiles.sort((a, b) => (b.overall.percentage ?? 0) - (a.overall.percentage ?? 0)); + return resultFiles; +} +function calculatePercentage(covered, missed) { + const total = covered + missed; + if (total !== 0) { + return parseFloat(((covered / total) * 100).toFixed(2)); + } + else { + return undefined; + } +} +function getTotalPercentage(files) { + let missed = 0; + let covered = 0; + if (files.length !== 0) { + for (const file of files) { + missed += file.overall.missed; + covered += file.overall.covered; + } + return parseFloat(((covered / (covered + missed)) * 100).toFixed(2)); + } + else { + return null; + } +} +function getModuleCoverage(report) { + const counters = report['counter']; + return getDetailedCoverage(counters, 'INSTRUCTION'); +} +function getOverallProjectCoverage(reports) { + const coverages = reports.map(report => getDetailedCoverage(report['counter'], 'INSTRUCTION')); + const covered = coverages.reduce((acc, coverage) => acc + coverage.covered, 0); + const missed = coverages.reduce((acc, coverage) => acc + coverage.missed, 0); + return { + covered, + missed, + percentage: parseFloat(((covered / (covered + missed)) * 100).toFixed(2)), + }; +} +function getDetailedCoverage(counters, type) { + const counterTag = counters.find(counter => counter[util_1.TAG.SELF].type === type); + if (counterTag) { + const attr = counterTag[util_1.TAG.SELF]; + const missed = parseFloat(attr.missed); + const covered = parseFloat(attr.covered); + return { + missed, + covered, + percentage: parseFloat(((covered / (covered + missed)) * 100).toFixed(2)), + }; + } + return { missed: 0, covered: 0, percentage: 100 }; +} + + +/***/ }), + +/***/ 6197: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getTitle = exports.getPRComment = void 0; +function getPRComment(project, minCoverage, title, emoji) { + const heading = getTitle(title); + const overallTable = getOverallTable(project, minCoverage, emoji); + const moduleTable = getModuleTable(project.modules, minCoverage, emoji); + const filesTable = getFileTable(project, minCoverage, emoji); + const tables = project.modules.length === 0 + ? '> There is no coverage information present for the Files changed' + : project.isMultiModule + ? `${moduleTable}\n\n${filesTable}` + : filesTable; + return `${heading + overallTable}\n\n${tables}`; +} +exports.getPRComment = getPRComment; +function getModuleTable(modules, minCoverage, emoji) { + const tableHeader = '|Module|Coverage||'; + const tableStructure = '|:-|:-|:-:|'; + let table = `${tableHeader}\n${tableStructure}`; + for (const module of modules) { + const coverageDifference = getCoverageDifference(module.overall, module.changed); + renderRow(module.name, module.overall.percentage, coverageDifference, module.changed.percentage); + } + return table; + function renderRow(name, overallCoverage, coverageDiff, changedCoverage) { + const status = getStatus(changedCoverage, minCoverage.changed, emoji); + let coveragePercentage = `${formatCoverage(overallCoverage)}`; + if (shouldShow(coverageDiff)) { + coveragePercentage += ` **\`${formatCoverage(coverageDiff)}\`**`; + } + const row = `|${name}|${coveragePercentage}|${status}|`; + table = `${table}\n${row}`; + } +} +function getFileTable(project, minCoverage, emoji) { + const tableHeader = project.isMultiModule + ? '|Module|File|Coverage||' + : '|File|Coverage||'; + const tableStructure = project.isMultiModule + ? '|:-|:-|:-|:-:|' + : '|:-|:-|:-:|'; + let table = `${tableHeader}\n${tableStructure}`; + for (const module of project.modules) { + for (let index = 0; index < module.files.length; index++) { + const file = module.files[index]; + let moduleName = module.name; + if (index !== 0) { + moduleName = ''; + } + const coverageDifference = getCoverageDifference(file.overall, file.changed); + renderRow(moduleName, `[${file.name}](${file.url})`, file.overall.percentage, coverageDifference, file.changed.percentage, project.isMultiModule); + } + } + return project.isMultiModule + ? `
\nFiles\n\n${table}\n\n
` + : table; + function renderRow(moduleName, fileName, overallCoverage, coverageDiff, changedCoverage, isMultiModule) { + const status = getStatus(changedCoverage, minCoverage.changed, emoji); + let coveragePercentage = `${formatCoverage(overallCoverage)}`; + if (shouldShow(coverageDiff)) { + coveragePercentage += ` **\`${formatCoverage(coverageDiff)}\`**`; + } + const row = isMultiModule + ? `|${moduleName}|${fileName}|${coveragePercentage}|${status}|` + : `|${fileName}|${coveragePercentage}|${status}|`; + table = `${table}\n${row}`; + } +} +function getCoverageDifference(overall, changed) { + const totalInstructions = overall.covered + overall.missed; + const missed = changed.missed; + return -(missed / totalInstructions) * 100; +} +function getOverallTable(project, minCoverage, emoji) { + const overallStatus = getStatus(project.overall.percentage, minCoverage.overall, emoji); + const coverageDifference = getCoverageDifference(project.overall, project.changed); + let coveragePercentage = `${formatCoverage(project.overall.percentage)}`; + if (shouldShow(coverageDifference)) { + coveragePercentage += ` **\`${formatCoverage(coverageDifference)}\`**`; + } + const tableHeader = `|Overall Project|${coveragePercentage}|${overallStatus}|`; + const tableStructure = '|:-|:-|:-:|'; + const missedLines = project.changed.missed; + const coveredLines = project.changed.covered; + const totalChangedLines = missedLines + coveredLines; + let changedCoverageRow = ''; + if (totalChangedLines !== 0) { + const changedLinesPercentage = (coveredLines / totalChangedLines) * 100; + const filesChangedStatus = getStatus(changedLinesPercentage, minCoverage.changed, emoji); + changedCoverageRow = + '\n' + + `|Files changed|${formatCoverage(changedLinesPercentage)}|${filesChangedStatus}|` + + '\n
'; + } + return `${tableHeader}\n${tableStructure}${changedCoverageRow}`; +} +function round(value) { + return Math.round((value + Number.EPSILON) * 100) / 100; +} +function shouldShow(value) { + const rounded = Math.abs(round(value)); + return rounded !== 0 && rounded !== 100; +} +function getTitle(title) { + if (title != null && title.trim().length > 0) { + const trimmed = title.trim(); + return trimmed.startsWith('#') ? `${trimmed}\n` : `### ${trimmed}\n`; + } + else { + return ''; + } +} +exports.getTitle = getTitle; +function getStatus(coverage, minCoverage, emoji) { + let status = emoji.pass; + if (coverage != null && coverage < minCoverage) { + status = emoji.fail; + } + return status; +} +function formatCoverage(coverage) { + if (coverage == null) + return 'NaN%'; + return `${toFloat(coverage)}%`; +} +function toFloat(value) { + return parseFloat(value.toFixed(2)); +} + + +/***/ }), + +/***/ 2090: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getFilesWithCoverage = exports.getChangedLines = exports.debug = exports.TAG = void 0; +exports.TAG = { + SELF: '$', + SOURCE_FILE: 'sourcefile', + LINE: 'line', + COUNTER: 'counter', + PACKAGE: 'package', + GROUP: 'group', +}; +function debug(obj) { + return JSON.stringify(obj, null, 4); +} +exports.debug = debug; +const pattern = /^@@ -([0-9]*),?\S* \+([0-9]*),?/; +function getChangedLines(patch) { + const lineNumbers = new Set(); + if (patch) { + const lines = patch.split('\n'); + const groups = getDiffGroups(lines); + for (const group of groups) { + const firstLine = group.shift(); + if (firstLine) { + const diffGroup = firstLine.match(pattern); + if (diffGroup) { + let bX = parseInt(diffGroup[2]); + for (const line of group) { + bX++; + if (line.startsWith('+')) { + lineNumbers.add(bX - 1); + } + else if (line.startsWith('-')) { + bX--; + } + } + } + } + } + } + return [...lineNumbers]; +} +exports.getChangedLines = getChangedLines; +function getDiffGroups(lines) { + const groups = []; + let group = []; + for (const line of lines) { + if (line.startsWith('@@')) { + group = []; + groups.push(group); + } + group.push(line); + } + return groups; +} +/* eslint-disable @typescript-eslint/no-explicit-any */ +function getFilesWithCoverage(packages) { + const files = []; + for (const item of packages) { + const packageName = item[exports.TAG.SELF].name; + const sourceFiles = item[exports.TAG.SOURCE_FILE] ?? []; + for (const sourceFile of sourceFiles) { + const sourceFileName = sourceFile[exports.TAG.SELF].name; + const file = { + name: sourceFileName, + packageName, + lines: [], + counters: [], + }; + const counters = sourceFile[exports.TAG.COUNTER] ?? []; + for (const counter of counters) { + const counterSelf = counter[exports.TAG.SELF]; + const type = counterSelf.type; + file.counters.push({ + name: type.toLowerCase(), + missed: parseInt(counterSelf.missed) ?? 0, + covered: parseInt(counterSelf.covered) ?? 0, + }); + } + const lines = sourceFile[exports.TAG.LINE] ?? []; + for (const line of lines) { + const lineSelf = line[exports.TAG.SELF]; + file.lines.push({ + number: parseInt(lineSelf.nr) ?? 0, + instruction: { + missed: parseInt(lineSelf.mi) ?? 0, + covered: parseInt(lineSelf.ci) ?? 0, + }, + branch: { + missed: parseInt(lineSelf.mb) ?? 0, + covered: parseInt(lineSelf.cb) ?? 0, + }, + }); + } + files.push(file); + } + } + return files; +} +exports.getFilesWithCoverage = getFilesWithCoverage; + + /***/ }), /***/ 5347: @@ -19516,12 +19510,11 @@ var __webpack_exports__ = {}; var exports = __webpack_exports__; Object.defineProperty(exports, "__esModule", ({ value: true })); -const action_1 = __nccwpck_require__(2966); +const action_1 = __nccwpck_require__(7289); (0, action_1.action)(); })(); module.exports = __webpack_exports__; /******/ })() -; -//# sourceMappingURL=index.js.map \ No newline at end of file +; \ No newline at end of file diff --git a/src/util.ts b/src/util.ts index aadbae7..89215ea 100644 --- a/src/util.ts +++ b/src/util.ts @@ -9,7 +9,7 @@ export const TAG = { GROUP: 'group', } -export function debug(obj: Object): string { +export function debug(obj: object): string { return JSON.stringify(obj, null, 4) }