mirror of
https://github.com/actions/upload-artifact.git
synced 2024-11-25 04:32:10 +00:00
Add new option to specify behavior if no files found (#104)
* Add new option to specify behavior if no files found
This commit is contained in:
parent
5f948bc1f0
commit
5ba29a7d5b
7 changed files with 188 additions and 25 deletions
12
README.md
12
README.md
|
@ -88,6 +88,18 @@ Relative and absolute file paths are both allowed. Relative paths are rooted aga
|
||||||
|
|
||||||
The [@actions/artifact](https://github.com/actions/toolkit/tree/main/packages/artifact) package is used internally to handle most of the logic around uploading an artifact. There is extra documentation around upload limitations and behavior in the toolkit repo that is worth checking out.
|
The [@actions/artifact](https://github.com/actions/toolkit/tree/main/packages/artifact) package is used internally to handle most of the logic around uploading an artifact. There is extra documentation around upload limitations and behavior in the toolkit repo that is worth checking out.
|
||||||
|
|
||||||
|
### Customization if no files are found
|
||||||
|
|
||||||
|
If a path (or paths), result in no files being found for the artifact, the action will succeed but print out a warning. In certain scenarios it may be desirable to fail the action or suppress the warning. The `if-no-files-found` option allows you to customize the behavior of the action if no files are found.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: my-artifact
|
||||||
|
path: path/to/artifact/
|
||||||
|
if-no-files-found: error # 'warn' or 'ignore' are also available, defaults to `warn`
|
||||||
|
```
|
||||||
|
|
||||||
### Conditional Artifact Upload
|
### Conditional Artifact Upload
|
||||||
|
|
||||||
To upload artifacts only when the previous step of a job failed, use [`if: failure()`](https://help.github.com/en/articles/contexts-and-expression-syntax-for-github-actions#job-status-check-functions):
|
To upload artifacts only when the previous step of a job failed, use [`if: failure()`](https://help.github.com/en/articles/contexts-and-expression-syntax-for-github-actions#job-status-check-functions):
|
||||||
|
|
11
action.yml
11
action.yml
|
@ -4,10 +4,19 @@ author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
name:
|
name:
|
||||||
description: 'Artifact name'
|
description: 'Artifact name'
|
||||||
required: false
|
default: 'artifact'
|
||||||
path:
|
path:
|
||||||
description: 'A file, directory or wildcard pattern that describes what to upload'
|
description: 'A file, directory or wildcard pattern that describes what to upload'
|
||||||
required: true
|
required: true
|
||||||
|
if-no-files-found:
|
||||||
|
description: >
|
||||||
|
The desired behavior if no files are found using the provided path.
|
||||||
|
|
||||||
|
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: 'warn'
|
||||||
runs:
|
runs:
|
||||||
using: 'node12'
|
using: 'node12'
|
||||||
main: 'dist/index.js'
|
main: 'dist/index.js'
|
85
dist/index.js
vendored
85
dist/index.js
vendored
|
@ -3987,25 +3987,39 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
const core = __importStar(__webpack_require__(470));
|
const core = __importStar(__webpack_require__(470));
|
||||||
const artifact_1 = __webpack_require__(214);
|
const artifact_1 = __webpack_require__(214);
|
||||||
const constants_1 = __webpack_require__(694);
|
|
||||||
const search_1 = __webpack_require__(575);
|
const search_1 = __webpack_require__(575);
|
||||||
|
const input_helper_1 = __webpack_require__(583);
|
||||||
|
const constants_1 = __webpack_require__(694);
|
||||||
function run() {
|
function run() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
const name = core.getInput(constants_1.Inputs.Name, { required: false });
|
const inputs = input_helper_1.getInputs();
|
||||||
const path = core.getInput(constants_1.Inputs.Path, { required: true });
|
const searchResult = yield search_1.findFilesToUpload(inputs.searchPath);
|
||||||
const searchResult = yield search_1.findFilesToUpload(path);
|
|
||||||
if (searchResult.filesToUpload.length === 0) {
|
if (searchResult.filesToUpload.length === 0) {
|
||||||
core.warning(`No files were found for the provided path: ${path}. No artifacts will be uploaded.`);
|
// 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 files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case constants_1.NoFileOptions.error: {
|
||||||
|
core.setFailed(`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case constants_1.NoFileOptions.ignore: {
|
||||||
|
core.info(`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.info(`With the provided path, there will be ${searchResult.filesToUpload.length} files uploaded`);
|
core.info(`With the provided path, there will be ${searchResult.filesToUpload.length} file(s) uploaded`);
|
||||||
core.debug(`Root artifact directory is ${searchResult.rootDirectory}`);
|
core.debug(`Root artifact directory is ${searchResult.rootDirectory}`);
|
||||||
const artifactClient = artifact_1.create();
|
const artifactClient = artifact_1.create();
|
||||||
const options = {
|
const options = {
|
||||||
continueOnError: false
|
continueOnError: false
|
||||||
};
|
};
|
||||||
const uploadResponse = yield artifactClient.uploadArtifact(name || constants_1.getDefaultArtifactName(), searchResult.filesToUpload, searchResult.rootDirectory, options);
|
const uploadResponse = yield artifactClient.uploadArtifact(inputs.artifactName, searchResult.filesToUpload, searchResult.rootDirectory, options);
|
||||||
if (uploadResponse.failedItems.length > 0) {
|
if (uploadResponse.failedItems.length > 0) {
|
||||||
core.setFailed(`An error was encountered when uploading ${uploadResponse.artifactName}. There were ${uploadResponse.failedItems.length} items that failed to upload.`);
|
core.setFailed(`An error was encountered when uploading ${uploadResponse.artifactName}. There were ${uploadResponse.failedItems.length} items that failed to upload.`);
|
||||||
}
|
}
|
||||||
|
@ -6335,6 +6349,43 @@ function findFilesToUpload(searchPath, globOptions) {
|
||||||
exports.findFilesToUpload = findFilesToUpload;
|
exports.findFilesToUpload = findFilesToUpload;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 583:
|
||||||
|
/***/ (function(__unusedmodule, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||||
|
result["default"] = mod;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const core = __importStar(__webpack_require__(470));
|
||||||
|
const constants_1 = __webpack_require__(694);
|
||||||
|
/**
|
||||||
|
* Helper to get all the inputs for the action
|
||||||
|
*/
|
||||||
|
function getInputs() {
|
||||||
|
const name = core.getInput(constants_1.Inputs.Name);
|
||||||
|
const path = core.getInput(constants_1.Inputs.Path, { required: true });
|
||||||
|
const ifNoFilesFound = core.getInput(constants_1.Inputs.IfNoFilesFound);
|
||||||
|
const noFileBehavior = constants_1.NoFileOptions[ifNoFilesFound];
|
||||||
|
if (!noFileBehavior) {
|
||||||
|
core.setFailed(`Unrecognized ${constants_1.Inputs.IfNoFilesFound} input. Provided: ${ifNoFilesFound}. Available options: ${Object.keys(constants_1.NoFileOptions)}`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
artifactName: name,
|
||||||
|
searchPath: path,
|
||||||
|
ifNoFilesFound: noFileBehavior
|
||||||
|
};
|
||||||
|
}
|
||||||
|
exports.getInputs = getInputs;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 590:
|
/***/ 590:
|
||||||
|
@ -7249,11 +7300,23 @@ var Inputs;
|
||||||
(function (Inputs) {
|
(function (Inputs) {
|
||||||
Inputs["Name"] = "name";
|
Inputs["Name"] = "name";
|
||||||
Inputs["Path"] = "path";
|
Inputs["Path"] = "path";
|
||||||
|
Inputs["IfNoFilesFound"] = "if-no-files-found";
|
||||||
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
||||||
function getDefaultArtifactName() {
|
var NoFileOptions;
|
||||||
return 'artifact';
|
(function (NoFileOptions) {
|
||||||
}
|
/**
|
||||||
exports.getDefaultArtifactName = getDefaultArtifactName;
|
* 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 = {}));
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
|
@ -1,8 +1,22 @@
|
||||||
export enum Inputs {
|
export enum Inputs {
|
||||||
Name = 'name',
|
Name = 'name',
|
||||||
Path = 'path'
|
Path = 'path',
|
||||||
|
IfNoFilesFound = 'if-no-files-found'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultArtifactName(): string {
|
export enum NoFileOptions {
|
||||||
return 'artifact'
|
/**
|
||||||
|
* 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'
|
||||||
}
|
}
|
||||||
|
|
30
src/input-helper.ts
Normal file
30
src/input-helper.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import {Inputs, NoFileOptions} from './constants'
|
||||||
|
import {UploadInputs} from './upload-inputs'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get all the inputs for the action
|
||||||
|
*/
|
||||||
|
export function getInputs(): UploadInputs {
|
||||||
|
const name = core.getInput(Inputs.Name)
|
||||||
|
const path = core.getInput(Inputs.Path, {required: true})
|
||||||
|
|
||||||
|
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
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
artifactName: name,
|
||||||
|
searchPath: path,
|
||||||
|
ifNoFilesFound: noFileBehavior
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,38 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {create, UploadOptions} from '@actions/artifact'
|
import {create, UploadOptions} from '@actions/artifact'
|
||||||
import {Inputs, getDefaultArtifactName} from './constants'
|
|
||||||
import {findFilesToUpload} from './search'
|
import {findFilesToUpload} from './search'
|
||||||
|
import {getInputs} from './input-helper'
|
||||||
|
import {NoFileOptions} from './constants'
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const name = core.getInput(Inputs.Name, {required: false})
|
const inputs = getInputs()
|
||||||
const path = core.getInput(Inputs.Path, {required: true})
|
const searchResult = await findFilesToUpload(inputs.searchPath)
|
||||||
|
|
||||||
const searchResult = await findFilesToUpload(path)
|
|
||||||
if (searchResult.filesToUpload.length === 0) {
|
if (searchResult.filesToUpload.length === 0) {
|
||||||
|
// No files were found, different use cases warrant different types of behavior if nothing is found
|
||||||
|
switch (inputs.ifNoFilesFound) {
|
||||||
|
case NoFileOptions.warn: {
|
||||||
core.warning(
|
core.warning(
|
||||||
`No files were found for the provided path: ${path}. No artifacts will be uploaded.`
|
`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`
|
||||||
)
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case NoFileOptions.error: {
|
||||||
|
core.setFailed(
|
||||||
|
`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case NoFileOptions.ignore: {
|
||||||
|
core.info(
|
||||||
|
`No files were found with the provided path: ${inputs.searchPath}. No artifacts will be uploaded.`
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
core.info(
|
core.info(
|
||||||
`With the provided path, there will be ${searchResult.filesToUpload.length} files uploaded`
|
`With the provided path, there will be ${searchResult.filesToUpload.length} file(s) uploaded`
|
||||||
)
|
)
|
||||||
core.debug(`Root artifact directory is ${searchResult.rootDirectory}`)
|
core.debug(`Root artifact directory is ${searchResult.rootDirectory}`)
|
||||||
|
|
||||||
|
@ -24,7 +41,7 @@ async function run(): Promise<void> {
|
||||||
continueOnError: false
|
continueOnError: false
|
||||||
}
|
}
|
||||||
const uploadResponse = await artifactClient.uploadArtifact(
|
const uploadResponse = await artifactClient.uploadArtifact(
|
||||||
name || getDefaultArtifactName(),
|
inputs.artifactName,
|
||||||
searchResult.filesToUpload,
|
searchResult.filesToUpload,
|
||||||
searchResult.rootDirectory,
|
searchResult.rootDirectory,
|
||||||
options
|
options
|
||||||
|
|
18
src/upload-inputs.ts
Normal file
18
src/upload-inputs.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import {NoFileOptions} from './constants'
|
||||||
|
|
||||||
|
export interface UploadInputs {
|
||||||
|
/**
|
||||||
|
* The name of the artifact that will be uploaded
|
||||||
|
*/
|
||||||
|
artifactName: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The search path used to describe what to upload as part of the artifact
|
||||||
|
*/
|
||||||
|
searchPath: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The desired behavior if no files are found with the provided search path
|
||||||
|
*/
|
||||||
|
ifNoFilesFound: NoFileOptions
|
||||||
|
}
|
Loading…
Reference in a new issue