mirror of
https://github.com/actions/upload-artifact.git
synced 2024-11-24 14:25:29 +00:00
Add if-no-files-found option to merge action
This commit is contained in:
parent
4c0ff1c489
commit
03a6dff9d4
@ -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
52
dist/merge/index.js
vendored
@ -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
50
dist/upload/index.js
vendored
@ -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* () {
|
||||
|
@ -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'
|
||||
|
@ -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.
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
@ -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:`)
|
||||
|
@ -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
16
src/shared/constants.ts
Normal 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'
|
||||
}
|
@ -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'
|
||||
}
|
||||
}
|
@ -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'
|
||||
|
||||
/**
|
||||
|
@ -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> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {NoFileOptions} from './constants'
|
||||
import {NoFileOptions} from '../shared/constants'
|
||||
|
||||
export interface UploadInputs {
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user