mirror of
https://code.forgejo.org/actions/download-artifact
synced 2025-07-13 13:43:52 +02:00
feat: support downloading multiple artifacts to different paths
There is a few use cases where we want to download a few different artifacts but not all of them. This commit implements this support, without breaking backward compatibility. A build test case was also added to the pipeline.
This commit is contained in:
parent
fc5dc36c10
commit
73f53c9b26
5 changed files with 2295 additions and 121 deletions
2
.github/workflows/check-dist.yml
vendored
2
.github/workflows/check-dist.yml
vendored
|
@ -4,7 +4,7 @@
|
|||
# For our project, we generate this file through a build process
|
||||
# from other source files.
|
||||
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
|
||||
name: Check dist/
|
||||
name: Check dist
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
53
.github/workflows/test.yml
vendored
53
.github/workflows/test.yml
vendored
|
@ -89,7 +89,56 @@ jobs:
|
|||
}
|
||||
shell: pwsh
|
||||
|
||||
# Test downloading both artifacts at once
|
||||
# Test downloading multiple artifacts to the same path
|
||||
- name: Download artifacts A and B to the same path
|
||||
uses: ./
|
||||
with:
|
||||
name: |
|
||||
Artifact-A
|
||||
Artifact-B
|
||||
path: some/path/for/multiple/files
|
||||
|
||||
- name: Verify successful download
|
||||
run: |
|
||||
$fileA = "some/path/for/multiple/files/file-A.txt"
|
||||
$fileB = "some/path/for/multiple/files/file-B.txt"
|
||||
if(!(Test-Path -path $fileA) -or !(Test-Path -path $fileB))
|
||||
{
|
||||
Write-Error "Expected files do not exist"
|
||||
}
|
||||
if(!((Get-Content $fileA) -ceq "Lorem ipsum dolor sit amet") -or !((Get-Content $fileB) -ceq "Hello world from file B"))
|
||||
{
|
||||
Write-Error "File contents of downloaded artifacts are incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
# Test downloading multiple artifacts to different paths
|
||||
- name: Download artifacts A and B to different paths
|
||||
uses: ./
|
||||
with:
|
||||
name: |
|
||||
Artifact-A
|
||||
Artifact-B
|
||||
path: |
|
||||
some/path/for/a
|
||||
some/path/for/b
|
||||
|
||||
|
||||
- name: Verify successful download
|
||||
run: |
|
||||
$fileA = "some/path/for/a/file-A.txt"
|
||||
$fileB = "some/path/for/b/file-B.txt"
|
||||
if(!(Test-Path -path $fileA) -or !(Test-Path -path $fileB))
|
||||
{
|
||||
Write-Error "Expected files do not exist"
|
||||
}
|
||||
if(!((Get-Content $fileA) -ceq "Lorem ipsum dolor sit amet") -or !((Get-Content $fileB) -ceq "Hello world from file B"))
|
||||
{
|
||||
Write-Error "File contents of downloaded artifacts are incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
# Test downloading all artifacts
|
||||
- name: Download all Artifacts
|
||||
uses: ./
|
||||
with:
|
||||
|
@ -108,5 +157,3 @@ jobs:
|
|||
Write-Error "File contents of downloaded artifacts are incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
|
2204
dist/index.js
vendored
2204
dist/index.js
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
export enum Inputs {
|
||||
Name = 'name',
|
||||
Path = 'path'
|
||||
Names = 'name',
|
||||
Paths = 'path'
|
||||
}
|
||||
export enum Outputs {
|
||||
DownloadPath = 'download-path'
|
||||
DownloadPaths = 'download-path'
|
||||
}
|
||||
|
|
|
@ -4,55 +4,118 @@ import * as os from 'os'
|
|||
import {resolve} from 'path'
|
||||
import {Inputs, Outputs} from './constants'
|
||||
|
||||
async function downloadArtifact(name: string, path: string): Promise<string> {
|
||||
let resolvedPath
|
||||
// resolve tilde expansions, path.replace only replaces the first occurrence of a pattern
|
||||
if (path.startsWith(`~`)) {
|
||||
resolvedPath = resolve(path.replace('~', os.homedir()))
|
||||
} else {
|
||||
resolvedPath = resolve(path)
|
||||
}
|
||||
core.debug(`Resolved path is ${resolvedPath}`)
|
||||
|
||||
const artifactClient = artifact.create()
|
||||
if (!name) {
|
||||
// download all artifacts
|
||||
core.info('No artifact name specified, downloading all artifacts')
|
||||
core.info(
|
||||
'Creating an extra directory for each artifact that is being downloaded'
|
||||
)
|
||||
const downloadResponse = await artifactClient.downloadAllArtifacts(
|
||||
resolvedPath
|
||||
)
|
||||
core.info(`There were ${downloadResponse.length} artifacts downloaded`)
|
||||
for (const artifact of downloadResponse) {
|
||||
core.info(
|
||||
`Artifact ${artifact.artifactName} was downloaded to ${artifact.downloadPath}`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// download a single artifact
|
||||
core.info(`Starting download for ${name}`)
|
||||
const downloadOptions = {
|
||||
createArtifactFolder: false
|
||||
}
|
||||
const downloadResponse = await artifactClient.downloadArtifact(
|
||||
name,
|
||||
resolvedPath,
|
||||
downloadOptions
|
||||
)
|
||||
core.info(
|
||||
`Artifact ${downloadResponse.artifactName} was downloaded to ${downloadResponse.downloadPath}`
|
||||
)
|
||||
}
|
||||
// output the directory that the artifact(s) was/were downloaded to
|
||||
// if no path is provided, an empty string resolves to the current working directory
|
||||
core.info('Artifact download has finished successfully')
|
||||
|
||||
return resolvedPath
|
||||
}
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
const name = core.getInput(Inputs.Name, {required: false})
|
||||
const path = core.getInput(Inputs.Path, {required: false})
|
||||
const names = core.getMultilineInput(Inputs.Names, {required: false})
|
||||
const paths = core.getMultilineInput(Inputs.Paths, {required: false})
|
||||
|
||||
let resolvedPath
|
||||
// resolve tilde expansions, path.replace only replaces the first occurrence of a pattern
|
||||
if (path.startsWith(`~`)) {
|
||||
resolvedPath = resolve(path.replace('~', os.homedir()))
|
||||
} else {
|
||||
resolvedPath = resolve(path)
|
||||
}
|
||||
core.debug(`Resolved path is ${resolvedPath}`)
|
||||
core.info(`names: '${JSON.stringify(names)}' | length: ${names.length}`)
|
||||
core.info(`paths: '${JSON.stringify(paths)}' | length: ${paths.length}`)
|
||||
|
||||
const artifactClient = artifact.create()
|
||||
if (!name) {
|
||||
// download all artifacts
|
||||
core.info('No artifact name specified, downloading all artifacts')
|
||||
core.info(
|
||||
'Creating an extra directory for each artifact that is being downloaded'
|
||||
)
|
||||
const downloadResponse = await artifactClient.downloadAllArtifacts(
|
||||
resolvedPath
|
||||
)
|
||||
core.info(`There were ${downloadResponse.length} artifacts downloaded`)
|
||||
for (const artifact of downloadResponse) {
|
||||
core.info(
|
||||
`Artifact ${artifact.artifactName} was downloaded to ${artifact.downloadPath}`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// download a single artifact
|
||||
core.info(`Starting download for ${name}`)
|
||||
const downloadOptions = {
|
||||
createArtifactFolder: false
|
||||
}
|
||||
const downloadResponse = await artifactClient.downloadArtifact(
|
||||
name,
|
||||
resolvedPath,
|
||||
downloadOptions
|
||||
)
|
||||
core.info(
|
||||
`Artifact ${downloadResponse.artifactName} was downloaded to ${downloadResponse.downloadPath}`
|
||||
let downloadPaths: string[] = []
|
||||
|
||||
// Names is set and has fewer entries than Paths
|
||||
if (names.length !== 0 && paths.length > names.length) {
|
||||
throw Error(
|
||||
`The input 'path' cannot have more entries than 'name', if 'name' is set.`
|
||||
)
|
||||
}
|
||||
// Names is NOT set and Paths has more than 1 entry
|
||||
else if (names.length === 0 && paths.length > 1) {
|
||||
throw Error(
|
||||
`The input 'path' cannot have more than one entry, if 'name' is not set.`
|
||||
)
|
||||
}
|
||||
// Names is NOT set and path has at max 1 entry: download all artifacts
|
||||
else if (names.length === 0 && paths.length <= 1) {
|
||||
const name = names.toString() // ''
|
||||
const path = paths.toString() // '' or 'some/path'
|
||||
const downloadPath = await downloadArtifact(name, path)
|
||||
downloadPaths.push(downloadPath)
|
||||
}
|
||||
// Names has one or more entries and Paths has at max 1 entry
|
||||
else if (names.length >= 1 && paths.length <= 1) {
|
||||
const path = paths.toString() // '' or 'some/path'
|
||||
names.forEach(async name => {
|
||||
const downloadPath = await downloadArtifact(name, path)
|
||||
downloadPaths.push(downloadPath)
|
||||
})
|
||||
}
|
||||
// Names and Paths have the same numbers of entries (more than 1)
|
||||
else if (
|
||||
names.length > 1 &&
|
||||
paths.length > 1 &&
|
||||
names.length === paths.length
|
||||
) {
|
||||
names.forEach(async (name, index) => {
|
||||
const path = paths[index]
|
||||
const downloadPath = await downloadArtifact(name, path)
|
||||
downloadPaths.push(downloadPath)
|
||||
})
|
||||
}
|
||||
// Unhandled exception
|
||||
else {
|
||||
throw Error(
|
||||
`Unhandled scenario. This shouldn't happen. It's very likely a bug. :-()`
|
||||
)
|
||||
}
|
||||
|
||||
// Remove duplicates and empty strings
|
||||
downloadPaths = [...new Set(downloadPaths.filter(path => path !== ''))]
|
||||
|
||||
// Returns a newline-separated list of paths
|
||||
const output = downloadPaths.join('\n')
|
||||
|
||||
// output the directory that the artifact(s) was/were downloaded to
|
||||
// if no path is provided, an empty string resolves to the current working directory
|
||||
core.setOutput(Outputs.DownloadPath, resolvedPath)
|
||||
core.info('Artifact download has finished successfully')
|
||||
core.setOutput(Outputs.DownloadPaths, output)
|
||||
} catch (err) {
|
||||
core.setFailed(err.message)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue