From 5442a4e8a3867a1c14be1ee3a04c4f47b261d632 Mon Sep 17 00:00:00 2001 From: BToersche Date: Thu, 13 May 2021 22:27:39 +0200 Subject: [PATCH] Allow inputs to accept stringified JSON --- README.md | 8 +++++ action.yml | 3 +- dist/index.js | 53 ++++++++++++++++----------- src/input-helper.ts | 87 +++++++++++++++++++++++++++++++-------------- 4 files changed, 102 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 32fe99a..fd2f161 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,14 @@ steps: !path/**/*.tmp ``` +### Upload multiple artifacts using a JSON string +```yaml +- uses: actions/upload-artifact@v2 + with: + name: '["my-artifact", "my-artifact-2"]' + path: '["path/to/artifact/1/", "path/to/artifact/2/"]' +``` + For supported wildcards along with behavior and documentation, see [@actions/glob](https://github.com/actions/toolkit/tree/main/packages/glob) which is used internally to search for files. If a wildcard pattern is used, the path hierarchy will be preserved after the first wildcard pattern. diff --git a/action.yml b/action.yml index 2003cdd..2733633 100644 --- a/action.yml +++ b/action.yml @@ -3,8 +3,7 @@ description: 'Upload a build artifact that can be used by subsequent workflow st author: 'GitHub' inputs: name: - description: 'Artifact name' - default: 'artifact' + description: 'Artifact name, default is "artifact"' path: description: 'A file, directory or wildcard pattern that describes what to upload' required: true diff --git a/dist/index.js b/dist/index.js index d2d85be..ec17608 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6581,12 +6581,16 @@ const constants_1 = __webpack_require__(694); function getInputs() { const name = core.getInput(constants_1.Inputs.Name); const path = core.getInput(constants_1.Inputs.Path, { required: true }); - const searchPath = Array.isArray(path) ? path : [path]; + const searchPath = parseFromJSON(path) || [path]; const defaultArtifactName = 'artifact'; // Accepts an individual value or an array as input, if array sizes don't match, use default value instead - const artifactName = Array.isArray(name) - ? name.concat(new Array(Math.max(0, searchPath.length - name.length)).fill(defaultArtifactName)) - : new Array(searchPath.length).fill(name || defaultArtifactName); + const artifactName = parseParamaterToArrayFromInput(name, searchPath.length, defaultArtifactName, (defaultInput, index) => { + const artifactIndexStr = index == 0 ? '' : `_${index + 1}`; + return `${defaultInput}${artifactIndexStr}`; + }); + // Accepts an individual value or an array as input + const retention = core.getInput(constants_1.Inputs.RetentionDays); + const retentionDays = parseParamaterToArrayFromInput(retention, searchPath.length, undefined, defaultInput => defaultInput).map(parseRetentionDays); const ifNoFilesFound = core.getInput(constants_1.Inputs.IfNoFilesFound); const noFileBehavior = constants_1.NoFileOptions[ifNoFilesFound]; if (!noFileBehavior) { @@ -6595,34 +6599,43 @@ function getInputs() { const inputs = { artifactName, searchPath, + retentionDays, ifNoFilesFound: noFileBehavior }; - // Accepts an individual value or an array as input - const retentionDays = core.getInput(constants_1.Inputs.RetentionDays); - if (Array.isArray(retentionDays)) { - // If array sizes don't match, use default value instead - inputs.retentionDays = retentionDays - .map(parseRetentionDays) - .concat(new Array(Math.max(0, searchPath.length - retentionDays.length)).fill(undefined)); - } - else { - const retention = parseRetentionDays(retentionDays); - inputs.retentionDays = new Array(searchPath.length).fill(retention); - } return inputs; } exports.getInputs = getInputs; +function parseParamaterToArrayFromInput(input, requiredLength, defaultInput, defaultFunc) { + // Accepts an individual value or an array as input, if array size doesn't match the required length, fill the rest with a default value + const inputArray = parseFromJSON(input || '[]'); + if (inputArray != null) { + // If a stringified JSON array is provided, use it and concat it with the default when required + return inputArray.concat(Array.from({ length: Math.max(0, requiredLength - inputArray.length) }, (_, index) => defaultFunc(defaultInput, index))); + } + // If a string is provided, fill the array with that value + return Array.from({ length: Math.max(0, requiredLength) }, (_, index) => defaultFunc(input || defaultInput, index)); +} +function parseFromJSON(jsonStr) { + try { + const json = JSON.parse(jsonStr); + if (Array.isArray(json)) { + return json; + } + } + catch (_err) { + // Input wasn't a stringified JSON array (string[]), return undefined to signal an invalid JSON was provided + } + return undefined; +} function parseRetentionDays(retentionDaysStr) { - if (retentionDaysStr) { + if (retentionDaysStr != null) { const retentionDays = parseInt(retentionDaysStr); if (isNaN(retentionDays)) { core.setFailed('Invalid retention-days'); } return retentionDays; } - else { - return undefined; - } + return undefined; } diff --git a/src/input-helper.ts b/src/input-helper.ts index 2397ca1..c770137 100644 --- a/src/input-helper.ts +++ b/src/input-helper.ts @@ -9,17 +9,28 @@ export function getInputs(): UploadInputs { const name = core.getInput(Inputs.Name) const path = core.getInput(Inputs.Path, {required: true}) - const searchPath = Array.isArray(path) ? path : [path] + const searchPath = parseFromJSON(path) || [path] const defaultArtifactName = 'artifact' // Accepts an individual value or an array as input, if array sizes don't match, use default value instead - const artifactName = Array.isArray(name) - ? name.concat( - new Array(Math.max(0, searchPath.length - name.length)).fill( - defaultArtifactName - ) - ) - : new Array(searchPath.length).fill(name || defaultArtifactName) + const artifactName = parseParamaterToArrayFromInput( + name, + searchPath.length, + defaultArtifactName, + (defaultInput, index) => { + const artifactIndexStr = index == 0 ? '' : `_${index + 1}` + return `${defaultInput}${artifactIndexStr}` + } + ) + + // Accepts an individual value or an array as input + const retention = core.getInput(Inputs.RetentionDays) + const retentionDays = parseParamaterToArrayFromInput( + retention, + searchPath.length, + undefined, + defaultInput => defaultInput + ).map(parseRetentionDays) const ifNoFilesFound = core.getInput(Inputs.IfNoFilesFound) const noFileBehavior: NoFileOptions = NoFileOptions[ifNoFilesFound] @@ -37,38 +48,60 @@ export function getInputs(): UploadInputs { const inputs = { artifactName, searchPath, + retentionDays, ifNoFilesFound: noFileBehavior } as UploadInputs - // Accepts an individual value or an array as input - const retentionDays = core.getInput(Inputs.RetentionDays) - if (Array.isArray(retentionDays)) { - // If array sizes don't match, use default value instead - inputs.retentionDays = retentionDays - .map(parseRetentionDays) - .concat( - new Array(Math.max(0, searchPath.length - retentionDays.length)).fill( - undefined - ) - ) - } else { - const retention = parseRetentionDays(retentionDays) - inputs.retentionDays = new Array(searchPath.length).fill(retention) - } - return inputs } +function parseParamaterToArrayFromInput( + input: string | undefined, + requiredLength: number, + defaultInput: string | undefined, + defaultFunc: ( + defaultInput: string | undefined, + index: number + ) => string | undefined +): (string | undefined)[] { + // Accepts an individual value or an array as input, if array size doesn't match the required length, fill the rest with a default value + const inputArray = parseFromJSON(input || '[]') + if (inputArray != null) { + // If a stringified JSON array is provided, use it and concat it with the default when required + return (<(string | undefined)[]>inputArray).concat( + Array.from( + {length: Math.max(0, requiredLength - inputArray.length)}, + (_, index) => defaultFunc(defaultInput, index) + ) + ) + } + // If a string is provided, fill the array with that value + return Array.from({length: Math.max(0, requiredLength)}, (_, index) => + defaultFunc(input || defaultInput, index) + ) +} + +function parseFromJSON(jsonStr: string): string[] | undefined { + try { + const json = JSON.parse(jsonStr) + if (Array.isArray(json)) { + return json + } + } catch (_err) { + // Input wasn't a stringified JSON array (string[]), return undefined to signal an invalid JSON was provided + } + return undefined +} + function parseRetentionDays( retentionDaysStr: string | undefined ): number | undefined { - if (retentionDaysStr) { + if (retentionDaysStr != null) { const retentionDays = parseInt(retentionDaysStr) if (isNaN(retentionDays)) { core.setFailed('Invalid retention-days') } return retentionDays - } else { - return undefined } + return undefined }