Skip to content

Commit

Permalink
Allow inputs to accept stringified JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
BToersche committed May 13, 2021
1 parent 0b90c2e commit 9b8a266
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 45 deletions.
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions action.yml
Expand Up @@ -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
Expand Down
48 changes: 29 additions & 19 deletions dist/index.js
Expand Up @@ -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) {
Expand All @@ -6595,23 +6599,31 @@ 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) {
// 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 {
return JSON.parse(jsonStr);
}
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) {
const retentionDays = parseInt(retentionDaysStr);
Expand All @@ -6620,9 +6632,7 @@ function parseRetentionDays(retentionDaysStr) {
}
return retentionDays;
}
else {
return undefined;
}
return undefined;
}


Expand Down
78 changes: 54 additions & 24 deletions src/input-helper.ts
Expand Up @@ -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]
Expand All @@ -37,26 +48,46 @@ 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
)
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) {
// 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)
)
} else {
const retention = parseRetentionDays(retentionDays)
inputs.retentionDays = new Array(searchPath.length).fill(retention)
)
}
// If a string is provided, fill the array with that value
return Array.from({length: Math.max(0, requiredLength)}, (_, index) =>
defaultFunc(input || defaultInput, index)
)
}

return inputs
function parseFromJSON(jsonStr: string): string[] | undefined {
try {
return <string[]>JSON.parse(jsonStr)
} catch (_err) {
// Input wasn't a stringified JSON array (string[]), return undefined to signal an invalid JSON was provided
}
return undefined
}

function parseRetentionDays(
Expand All @@ -68,7 +99,6 @@ function parseRetentionDays(
core.setFailed('Invalid retention-days')
}
return retentionDays
} else {
return undefined
}
return undefined
}

0 comments on commit 9b8a266

Please sign in to comment.