Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ See also [upload-artifact](https://github.com/actions/upload-artifact).
- [v4 - What's new](#v4---whats-new)
- [Improvements](#improvements)
- [Breaking Changes](#breaking-changes)
- [Note](#note)
- [Usage](#usage)
- [Inputs](#inputs)
- [Outputs](#outputs)
Expand Down Expand Up @@ -89,6 +90,7 @@ You are welcome to still raise bugs in this repo.
# When multiple artifacts are matched, this changes the behavior of the destination directories.
# If true, the downloaded artifacts will be in the same directory specified by path.
# If false, the downloaded artifacts will be extracted into individual named directories within the specified path.
# Note: When downloading a single artifact (by name or ID), it will always be extracted directly to the specified path.
# Optional. Default is 'false'
merge-multiple:

Expand Down Expand Up @@ -145,6 +147,8 @@ steps:

The `artifact-ids` input allows downloading artifacts using their unique ID rather than name. This is particularly useful when working with immutable artifacts from `actions/upload-artifact@v4` which assigns a unique ID to each artifact.

Download a single artifact by ID to the current working directory (`$GITHUB_WORKSPACE`):

```yaml
steps:
- uses: actions/download-artifact@v4
Expand All @@ -154,6 +158,20 @@ steps:
run: ls -R
```

Download a single artifact by ID to a specific directory:

```yaml
steps:
- uses: actions/download-artifact@v4
with:
artifact-ids: 12345
path: your/destination/dir
- name: Display structure of downloaded files
run: ls -R your/destination/dir
```

When downloading a single artifact by ID, the behavior is identical to downloading by name - the artifact contents are extracted directly to the specified path without creating a subdirectory.

Multiple artifacts can be downloaded by providing a comma-separated list of IDs:

```yaml
Expand All @@ -166,7 +184,7 @@ steps:
run: ls -R path/to/artifacts
```

This will download multiple artifacts to separate directories (similar to downloading multiple artifacts by name).
When downloading multiple artifacts by ID, each artifact will be extracted into its own subdirectory named after the artifact (similar to downloading multiple artifacts by name).

### Download All Artifacts

Expand Down
35 changes: 35 additions & 0 deletions __tests__/download.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as core from '@actions/core'
import * as path from 'path'
import artifact, {ArtifactNotFoundError} from '@actions/artifact'
import {run} from '../src/download-artifact'
import {Inputs} from '../src/constants'
Expand Down Expand Up @@ -371,4 +372,38 @@ describe('download', () => {
"Inputs 'name' and 'artifact-ids' cannot be used together. Please specify only one."
)
})

test('downloads single artifact by ID to same path as by name', async () => {
const mockArtifact = {
id: 456,
name: 'test-artifact',
size: 1024,
digest: 'def456'
}

const testPath = '/test/path'
mockInputs({
[Inputs.Name]: '',
[Inputs.Pattern]: '',
[Inputs.ArtifactIds]: '456',
[Inputs.Path]: testPath
})

jest.spyOn(artifact, 'listArtifacts').mockImplementation(() =>
Promise.resolve({
artifacts: [mockArtifact]
})
)

await run()

// Verify it downloads directly to the specified path (not nested in artifact name subdirectory)
expect(artifact.downloadArtifact).toHaveBeenCalledWith(
456,
expect.objectContaining({
path: path.resolve(testPath), // Should be the resolved path directly, not nested
expectedHash: mockArtifact.digest
})
)
})
})
6 changes: 4 additions & 2 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118883,7 +118883,9 @@ function run() {
}
const downloadPromises = artifacts.map(artifact => ({
name: artifact.name,
promise: artifact_1.default.downloadArtifact(artifact.id, Object.assign(Object.assign({}, options), { path: isSingleArtifactDownload || inputs.mergeMultiple
promise: artifact_1.default.downloadArtifact(artifact.id, Object.assign(Object.assign({}, options), { path: isSingleArtifactDownload ||
inputs.mergeMultiple ||
artifacts.length === 1
? resolvedPath
: path.join(resolvedPath, artifact.name), expectedHash: artifact.digest }))
}));
Expand Down Expand Up @@ -128958,4 +128960,4 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"]
/******/ module.exports = __webpack_exports__;
/******/
/******/ })()
;
;
4 changes: 3 additions & 1 deletion src/download-artifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,9 @@ export async function run(): Promise<void> {
promise: artifactClient.downloadArtifact(artifact.id, {
...options,
path:
isSingleArtifactDownload || inputs.mergeMultiple
isSingleArtifactDownload ||
inputs.mergeMultiple ||
artifacts.length === 1
? resolvedPath
: path.join(resolvedPath, artifact.name),
expectedHash: artifact.digest
Expand Down
Loading