Add if-no-files-found option to merge action

pull/521/head
Charlie Croom 2024-02-13 12:16:53 -05:00
parent 4c0ff1c489
commit 03a6dff9d4
No known key found for this signature in database
14 changed files with 202 additions and 44 deletions

View File

@ -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()
})

52
dist/merge/index.js vendored
View File

@ -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:

50
dist/upload/index.js vendored
View File

@ -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* () {

View File

@ -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'

View File

@ -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.

View File

@ -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',

View File

@ -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,

View File

@ -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<void> {
)
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:`)

View File

@ -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
*/

16
src/shared/constants.ts Normal file
View File

@ -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'
}

View File

@ -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'
}
}

View File

@ -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'
/**

View File

@ -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<void> {

View File

@ -1,4 +1,4 @@
import {NoFileOptions} from './constants'
import {NoFileOptions} from '../shared/constants'
export interface UploadInputs {
/**