From 03a6dff9d462887b323ceafbd50932488755ec44 Mon Sep 17 00:00:00 2001 From: Charlie Croom Date: Tue, 13 Feb 2024 12:16:53 -0500 Subject: [PATCH] Add if-no-files-found option to merge action --- __tests__/merge.test.ts | 38 +++++++++++++++++++++++-- dist/merge/index.js | 52 ++++++++++++++++++++++++++++++++++- dist/upload/index.js | 50 ++++++++++++++++++++------------- merge/README.md | 8 ++++++ merge/action.yml | 9 ++++++ src/merge/constants.ts | 1 + src/merge/input-helper.ts | 15 ++++++++++ src/merge/merge-artifacts.ts | 24 +++++++++++++++- src/merge/merge-inputs.ts | 7 +++++ src/shared/constants.ts | 16 +++++++++++ src/upload/constants.ts | 19 +------------ src/upload/input-helper.ts | 3 +- src/upload/upload-artifact.ts | 2 +- src/upload/upload-inputs.ts | 2 +- 14 files changed, 202 insertions(+), 44 deletions(-) create mode 100644 src/shared/constants.ts diff --git a/__tests__/merge.test.ts b/__tests__/merge.test.ts index e4deba2..f0b6eb4 100644 --- a/__tests__/merge.test.ts +++ b/__tests__/merge.test.ts @@ -57,6 +57,7 @@ const mockInputs = (overrides?: Partial<{[K in Inputs]?: any}>) => { const inputs = { [Inputs.Name]: 'my-merged-artifact', [Inputs.Pattern]: '*', + [Inputs.IfNoFilesFound]: 'error', [Inputs.SeparateDirectories]: false, [Inputs.RetentionDays]: 0, [Inputs.CompressionLevel]: 6, @@ -122,11 +123,44 @@ describe('merge', () => { ) }) - it('fails if no artifacts found', async () => { + it('supports error (by default) if no artifacts found', async () => { mockInputs({[Inputs.Pattern]: 'this-does-not-match'}) - expect(run()).rejects.toThrow() + await run() + expect(core.setFailed).toHaveBeenCalledWith( + `No artifacts were found with the provided pattern: this-does-not-match.` + ) + expect(artifact.uploadArtifact).not.toBeCalled() + expect(artifact.downloadArtifact).not.toBeCalled() + }) + + it('supports warn if no artifacts found', async () => { + mockInputs({ + [Inputs.Pattern]: 'this-does-not-match', + [Inputs.IfNoFilesFound]: 'warn' + }) + + await run() + + expect(core.warning).toHaveBeenCalledWith( + `No artifacts were found with the provided pattern: this-does-not-match.` + ) + expect(artifact.uploadArtifact).not.toBeCalled() + expect(artifact.downloadArtifact).not.toBeCalled() + }) + + it('supports ignore if no artifacts found', async () => { + mockInputs({ + [Inputs.Pattern]: 'this-does-not-match', + [Inputs.IfNoFilesFound]: 'ignore' + }) + + await run() + + expect(core.info).toHaveBeenCalledWith( + `No artifacts were found with the provided pattern: this-does-not-match.` + ) expect(artifact.uploadArtifact).not.toBeCalled() expect(artifact.downloadArtifact).not.toBeCalled() }) diff --git a/dist/merge/index.js b/dist/merge/index.js index 64b7b97..da3217d 100644 --- a/dist/merge/index.js +++ b/dist/merge/index.js @@ -129407,6 +129407,7 @@ var Inputs; (function (Inputs) { Inputs["Name"] = "name"; Inputs["Pattern"] = "pattern"; + Inputs["IfNoFilesFound"] = "if-no-files-found"; Inputs["SeparateDirectories"] = "separate-directories"; Inputs["RetentionDays"] = "retention-days"; Inputs["CompressionLevel"] = "compression-level"; @@ -129486,6 +129487,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getInputs = void 0; const core = __importStar(__nccwpck_require__(42186)); const constants_1 = __nccwpck_require__(80746); +const constants_2 = __nccwpck_require__(64068); /** * Helper to get all the inputs for the action */ @@ -129494,9 +129496,15 @@ function getInputs() { const pattern = core.getInput(constants_1.Inputs.Pattern, { required: true }); const separateDirectories = core.getBooleanInput(constants_1.Inputs.SeparateDirectories); const deleteMerged = core.getBooleanInput(constants_1.Inputs.DeleteMerged); + const ifNoFilesFound = core.getInput(constants_1.Inputs.IfNoFilesFound); + const noFileBehavior = constants_2.NoFileOptions[ifNoFilesFound]; + if (!noFileBehavior) { + core.setFailed(`Unrecognized ${constants_1.Inputs.IfNoFilesFound} input. Provided: ${ifNoFilesFound}. Available options: ${Object.keys(constants_2.NoFileOptions)}`); + } const inputs = { name, pattern, + ifNoFilesFound: noFileBehavior, separateDirectories, deleteMerged, retentionDays: 0, @@ -129574,6 +129582,7 @@ const core = __importStar(__nccwpck_require__(42186)); const minimatch_1 = __nccwpck_require__(61953); const artifact_1 = __importDefault(__nccwpck_require__(79450)); const input_helper_1 = __nccwpck_require__(17661); +const constants_1 = __nccwpck_require__(64068); const upload_artifact_1 = __nccwpck_require__(56680); const search_1 = __nccwpck_require__(8725); const PARALLEL_DOWNLOADS = 5; @@ -129594,7 +129603,22 @@ function run() { const artifacts = listArtifactResponse.artifacts.filter(artifact => matcher.match(artifact.name)); core.debug(`Filtered from ${listArtifactResponse.artifacts.length} to ${artifacts.length} artifacts`); if (artifacts.length === 0) { - throw new Error(`No artifacts found matching pattern '${inputs.pattern}'`); + // No files were found, different use cases warrant different types of behavior if nothing is found + switch (inputs.ifNoFilesFound) { + case constants_1.NoFileOptions.warn: { + core.warning(`No artifacts were found with the provided pattern: ${inputs.pattern}.`); + break; + } + case constants_1.NoFileOptions.error: { + core.setFailed(`No artifacts were found with the provided pattern: ${inputs.pattern}.`); + break; + } + case constants_1.NoFileOptions.ignore: { + core.info(`No artifacts were found with the provided pattern: ${inputs.pattern}.`); + break; + } + } + return; } core.info(`Preparing to download the following artifacts:`); artifacts.forEach(artifact => { @@ -129635,6 +129659,32 @@ function run() { exports.run = run; +/***/ }), + +/***/ 64068: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.NoFileOptions = void 0; +var NoFileOptions; +(function (NoFileOptions) { + /** + * Default. Output a warning but do not fail the action + */ + NoFileOptions["warn"] = "warn"; + /** + * Fail the action with an error message + */ + NoFileOptions["error"] = "error"; + /** + * Do not output any warnings or errors, the action does not fail + */ + NoFileOptions["ignore"] = "ignore"; +})(NoFileOptions = exports.NoFileOptions || (exports.NoFileOptions = {})); + + /***/ }), /***/ 8725: diff --git a/dist/upload/index.js b/dist/upload/index.js index 3b3b208..0a05d9b 100644 --- a/dist/upload/index.js +++ b/dist/upload/index.js @@ -129393,6 +129393,32 @@ function regExpEscape (s) { } +/***/ }), + +/***/ 64068: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.NoFileOptions = void 0; +var NoFileOptions; +(function (NoFileOptions) { + /** + * Default. Output a warning but do not fail the action + */ + NoFileOptions["warn"] = "warn"; + /** + * Fail the action with an error message + */ + NoFileOptions["error"] = "error"; + /** + * Do not output any warnings or errors, the action does not fail + */ + NoFileOptions["ignore"] = "ignore"; +})(NoFileOptions = exports.NoFileOptions || (exports.NoFileOptions = {})); + + /***/ }), /***/ 8725: @@ -129630,7 +129656,7 @@ exports.uploadArtifact = uploadArtifact; "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.NoFileOptions = exports.Inputs = void 0; +exports.Inputs = void 0; /* eslint-disable no-unused-vars */ var Inputs; (function (Inputs) { @@ -129641,21 +129667,6 @@ var Inputs; Inputs["CompressionLevel"] = "compression-level"; Inputs["Overwrite"] = "overwrite"; })(Inputs = exports.Inputs || (exports.Inputs = {})); -var NoFileOptions; -(function (NoFileOptions) { - /** - * Default. Output a warning but do not fail the action - */ - NoFileOptions["warn"] = "warn"; - /** - * Fail the action with an error message - */ - NoFileOptions["error"] = "error"; - /** - * Do not output any warnings or errors, the action does not fail - */ - NoFileOptions["ignore"] = "ignore"; -})(NoFileOptions = exports.NoFileOptions || (exports.NoFileOptions = {})); /***/ }), @@ -129730,6 +129741,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getInputs = void 0; const core = __importStar(__nccwpck_require__(42186)); const constants_1 = __nccwpck_require__(86154); +const constants_2 = __nccwpck_require__(64068); /** * Helper to get all the inputs for the action */ @@ -129738,9 +129750,9 @@ function getInputs() { const path = core.getInput(constants_1.Inputs.Path, { required: true }); const overwrite = core.getBooleanInput(constants_1.Inputs.Overwrite); const ifNoFilesFound = core.getInput(constants_1.Inputs.IfNoFilesFound); - const noFileBehavior = constants_1.NoFileOptions[ifNoFilesFound]; + const noFileBehavior = constants_2.NoFileOptions[ifNoFilesFound]; if (!noFileBehavior) { - core.setFailed(`Unrecognized ${constants_1.Inputs.IfNoFilesFound} input. Provided: ${ifNoFilesFound}. Available options: ${Object.keys(constants_1.NoFileOptions)}`); + core.setFailed(`Unrecognized ${constants_1.Inputs.IfNoFilesFound} input. Provided: ${ifNoFilesFound}. Available options: ${Object.keys(constants_2.NoFileOptions)}`); } const inputs = { artifactName: name, @@ -129815,7 +129827,7 @@ const core = __importStar(__nccwpck_require__(42186)); const artifact_1 = __importStar(__nccwpck_require__(79450)); const search_1 = __nccwpck_require__(8725); const input_helper_1 = __nccwpck_require__(67022); -const constants_1 = __nccwpck_require__(86154); +const constants_1 = __nccwpck_require__(64068); const upload_artifact_1 = __nccwpck_require__(56680); function deleteArtifactIfExists(artifactName) { return __awaiter(this, void 0, void 0, function* () { diff --git a/merge/README.md b/merge/README.md index b2f390e..985599c 100644 --- a/merge/README.md +++ b/merge/README.md @@ -36,6 +36,14 @@ For most cases, this may not be the most efficient solution. See [the migration # Optional. Default is '*' pattern: + # The desired behavior if no artifacts are found using the provided pattern. + # Available Options: + # warn: Output a warning but do not fail the action + # error: Fail the action with an error message + # ignore: Do not output any warnings or errors, the action does not fail + # Optional. Default is 'error' + if-no-files-found: + # If true, the artifacts will be merged into separate directories. # If false, the artifacts will be merged into the root of the destination. # Optional. Default is 'false' diff --git a/merge/action.yml b/merge/action.yml index 8d85864..8e4fd3b 100644 --- a/merge/action.yml +++ b/merge/action.yml @@ -9,6 +9,15 @@ inputs: pattern: description: 'A glob pattern matching the artifact names that should be merged.' default: '*' + if-no-files-found: + description: > + The desired behavior if no artifacts are found using the provided pattern. + + Available Options: + warn: Output a warning but do not fail the action + error: Fail the action with an error message + ignore: Do not output any warnings or errors, the action does not fail + default: 'error' separate-directories: description: 'When multiple artifacts are matched, this changes the behavior of how they are merged in the archive. If true, the matched artifacts will be extracted into individual named directories within the specified path. diff --git a/src/merge/constants.ts b/src/merge/constants.ts index 8bc9539..ca692ba 100644 --- a/src/merge/constants.ts +++ b/src/merge/constants.ts @@ -2,6 +2,7 @@ export enum Inputs { Name = 'name', Pattern = 'pattern', + IfNoFilesFound = 'if-no-files-found', SeparateDirectories = 'separate-directories', RetentionDays = 'retention-days', CompressionLevel = 'compression-level', diff --git a/src/merge/input-helper.ts b/src/merge/input-helper.ts index de53a2f..a769315 100644 --- a/src/merge/input-helper.ts +++ b/src/merge/input-helper.ts @@ -1,5 +1,6 @@ import * as core from '@actions/core' import {Inputs} from './constants' +import {NoFileOptions} from '../shared/constants' import {MergeInputs} from './merge-inputs' /** @@ -11,9 +12,23 @@ export function getInputs(): MergeInputs { const separateDirectories = core.getBooleanInput(Inputs.SeparateDirectories) const deleteMerged = core.getBooleanInput(Inputs.DeleteMerged) + const ifNoFilesFound = core.getInput(Inputs.IfNoFilesFound) + const noFileBehavior: NoFileOptions = NoFileOptions[ifNoFilesFound] + + if (!noFileBehavior) { + core.setFailed( + `Unrecognized ${ + Inputs.IfNoFilesFound + } input. Provided: ${ifNoFilesFound}. Available options: ${Object.keys( + NoFileOptions + )}` + ) + } + const inputs = { name, pattern, + ifNoFilesFound: noFileBehavior, separateDirectories, deleteMerged, retentionDays: 0, diff --git a/src/merge/merge-artifacts.ts b/src/merge/merge-artifacts.ts index b45ef9c..968a21e 100644 --- a/src/merge/merge-artifacts.ts +++ b/src/merge/merge-artifacts.ts @@ -4,6 +4,7 @@ import * as core from '@actions/core' import {Minimatch} from 'minimatch' import artifactClient, {UploadArtifactOptions} from '@actions/artifact' import {getInputs} from './input-helper' +import {NoFileOptions} from '../shared/constants' import {uploadArtifact} from '../shared/upload-artifact' import {findFilesToUpload} from '../shared/search' @@ -32,7 +33,28 @@ export async function run(): Promise { ) if (artifacts.length === 0) { - throw new Error(`No artifacts found matching pattern '${inputs.pattern}'`) + // No files were found, different use cases warrant different types of behavior if nothing is found + switch (inputs.ifNoFilesFound) { + case NoFileOptions.warn: { + core.warning( + `No artifacts were found with the provided pattern: ${inputs.pattern}.` + ) + break + } + case NoFileOptions.error: { + core.setFailed( + `No artifacts were found with the provided pattern: ${inputs.pattern}.` + ) + break + } + case NoFileOptions.ignore: { + core.info( + `No artifacts were found with the provided pattern: ${inputs.pattern}.` + ) + break + } + } + return; } core.info(`Preparing to download the following artifacts:`) diff --git a/src/merge/merge-inputs.ts b/src/merge/merge-inputs.ts index def507a..f729e31 100644 --- a/src/merge/merge-inputs.ts +++ b/src/merge/merge-inputs.ts @@ -1,3 +1,5 @@ +import {NoFileOptions} from '../shared/constants' + export interface MergeInputs { /** * The name of the artifact that the artifacts will be merged into @@ -9,6 +11,11 @@ export interface MergeInputs { */ pattern: string + /** + * The desired behavior if no files are found with the provided search path + */ + ifNoFilesFound: NoFileOptions + /** * Duration after which artifact will expire in days */ diff --git a/src/shared/constants.ts b/src/shared/constants.ts new file mode 100644 index 0000000..d5bb1c1 --- /dev/null +++ b/src/shared/constants.ts @@ -0,0 +1,16 @@ +export enum NoFileOptions { + /** + * Default. Output a warning but do not fail the action + */ + warn = 'warn', + + /** + * Fail the action with an error message + */ + error = 'error', + + /** + * Do not output any warnings or errors, the action does not fail + */ + ignore = 'ignore' +} diff --git a/src/upload/constants.ts b/src/upload/constants.ts index 272f842..b1568ed 100644 --- a/src/upload/constants.ts +++ b/src/upload/constants.ts @@ -6,21 +6,4 @@ export enum Inputs { RetentionDays = 'retention-days', CompressionLevel = 'compression-level', Overwrite = 'overwrite' -} - -export enum NoFileOptions { - /** - * Default. Output a warning but do not fail the action - */ - warn = 'warn', - - /** - * Fail the action with an error message - */ - error = 'error', - - /** - * Do not output any warnings or errors, the action does not fail - */ - ignore = 'ignore' -} +} \ No newline at end of file diff --git a/src/upload/input-helper.ts b/src/upload/input-helper.ts index 3e24f25..bab48e3 100644 --- a/src/upload/input-helper.ts +++ b/src/upload/input-helper.ts @@ -1,5 +1,6 @@ import * as core from '@actions/core' -import {Inputs, NoFileOptions} from './constants' +import {Inputs} from './constants' +import {NoFileOptions} from '../shared/constants' import {UploadInputs} from './upload-inputs' /** diff --git a/src/upload/upload-artifact.ts b/src/upload/upload-artifact.ts index 8c77543..9d71793 100644 --- a/src/upload/upload-artifact.ts +++ b/src/upload/upload-artifact.ts @@ -5,7 +5,7 @@ import artifact, { } from '@actions/artifact' import {findFilesToUpload} from '../shared/search' import {getInputs} from './input-helper' -import {NoFileOptions} from './constants' +import {NoFileOptions} from '../shared/constants' import {uploadArtifact} from '../shared/upload-artifact' async function deleteArtifactIfExists(artifactName: string): Promise { diff --git a/src/upload/upload-inputs.ts b/src/upload/upload-inputs.ts index 1e7a46f..ed80e53 100644 --- a/src/upload/upload-inputs.ts +++ b/src/upload/upload-inputs.ts @@ -1,4 +1,4 @@ -import {NoFileOptions} from './constants' +import {NoFileOptions} from '../shared/constants' export interface UploadInputs { /**