Compare commits

..

39 Commits

Author SHA1 Message Date
Josh Gross
95815c38cf Merge pull request #391 from GhadimiR/main
Fix bug introduced in 4.2.0
2025-03-19 11:14:44 -04:00
Ryan Ghadimi
278fca438a Move log statements 2025-03-19 15:06:13 +00:00
Ryan Ghadimi
68909842a1 Merge branch 'main' into main 2025-03-19 15:04:09 +00:00
Josh Gross
f9415c0ec3 Run unit tests in CI 2025-03-19 15:01:26 +00:00
Josh Gross
76a6eb5cbc Merge pull request #392 from GhadimiR/add_unit_tests
Add unit tests
2025-03-19 10:52:21 -04:00
Josh Gross
a2426d7c45 Merge branch 'main' into add_unit_tests 2025-03-19 10:48:52 -04:00
Ryan Ghadimi
3ffa694f6f lint 2025-03-19 12:26:46 +00:00
Ryan Ghadimi
53f6aa5f93 Add extra assertion to download single artifact test 2025-03-19 12:25:37 +00:00
Ryan Ghadimi
b456700053 lint 2025-03-19 11:31:11 +00:00
Ryan Ghadimi
9eab798a98 Configure tsconfig 2025-03-19 11:25:29 +00:00
Ryan Ghadimi
a39a661f39 Unfix error 2025-03-19 11:22:27 +00:00
Ryan Ghadimi
9a869e9c49 Lint 2025-03-19 11:21:30 +00:00
Ryan Ghadimi
96a6f165f4 Add tests & test dependencies 2025-03-19 11:17:03 +00:00
Ryan Ghadimi
df4ad15cb8 Merge branch 'main' into main 2025-03-18 22:02:13 +00:00
Ryan Ghadimi
c7cfc3a2a3 dist & package.json 2025-03-18 21:28:08 +00:00
Ryan Ghadimi
2439186eed Fix bug introduced in 4.2.0 2025-03-18 21:25:22 +00:00
JoannaaKL
b14cf4c926 Merge pull request #383 from GhadimiR/main
Bump artifact version, do digest check
2025-03-18 16:31:00 +01:00
Ryan Ghadimi
c5804ef743 Update dist 2025-03-18 15:23:08 +00:00
Ryan Ghadimi
956811a503 Update artifact to 2.3.2 2025-03-18 15:21:17 +00:00
Ryan Ghadimi
af3c6d3e5b Update artifact license 2025-03-18 13:23:53 +00:00
Ryan Ghadimi
4dd97f8f21 Bump artifact package 2025-03-18 11:57:35 +00:00
Ryan Ghadimi
da9985dde6 Merge branch 'main' into main 2025-03-17 10:26:24 +00:00
Larissa Fortuna
81ba80daa4 Merge pull request #384 from actions/lkfortuna-patch-1
Update README.md
2025-03-13 08:12:46 -07:00
Larissa Fortuna
727afbf2b0 Update README.md
removing deprecation warning
2025-03-12 15:02:16 -07:00
Ryan Ghadimi
56c2d7ea8c Make work as intended 2025-03-12 16:21:59 +00:00
Ryan Ghadimi
7797bfcd59 run release 2025-03-12 16:20:09 +00:00
Ryan Ghadimi
9ff67cb2d2 Break the thing, also log the expected digest 2025-03-12 16:17:52 +00:00
Ryan Ghadimi
049eba1e9a unbreak testing code 2025-03-12 16:04:02 +00:00
Ryan Ghadimi
503e7a18ae Refactor loop, break for testing 2025-03-12 15:40:05 +00:00
Ryan Ghadimi
a8a786b097 update dist 2025-03-12 14:13:07 +00:00
Ryan Ghadimi
24aef17bbf Refactor loop 2025-03-12 14:02:51 +00:00
Ryan Ghadimi
b81a615862 Bump artifact version, do digest check 2025-03-12 13:47:20 +00:00
Yang Cao
cc20338598 Merge pull request #380 from actions/yacaovsnc/release_4_1_9
Update artifact package to 2.2.2
2025-02-25 16:23:11 -05:00
Yang Cao
1fc0fee191 Update artifact package to 2.2.2 2025-02-25 16:10:45 -05:00
Rob Herley
7fba95161a Merge pull request #372 from andyfeller/patch-1
Update MIGRATION.md
2025-01-10 12:01:23 -05:00
Andy Feller
f9ceb7763b Update MIGRATION.md
Fixing the broken link pointing to `actions/upload-artifact` content outside of the repository.

cc: @pedrolacerda
2025-01-10 11:56:44 -05:00
Josh Gross
533298bc57 Merge pull request #370 from froblesmartin/patch-1
docs: small migration fix
2024-12-23 13:12:54 -05:00
Francisco Robles Martín
d06289e120 docs: small migration fix 2024-12-23 14:32:00 +01:00
Joel Ambass
d0ce8fd116 Merge pull request #354 from actions/Jcambass-patch-1
Add workflow file for publishing releases to immutable action package
2024-09-26 08:20:04 +02:00
11 changed files with 9024 additions and 2652 deletions

View File

@@ -40,6 +40,9 @@ jobs:
- name: Format
run: npm run format-check
- name: Run Unit Tests
run: npm test
- name: Create artifacts
run: |
mkdir -p path/to/artifact-A

View File

@@ -1,6 +1,6 @@
---
name: "@actions/artifact"
version: 2.1.8
version: 2.3.2
type: npm
summary: Actions artifact lib
homepage: https://github.com/actions/toolkit/tree/main/packages/artifact

View File

@@ -1,11 +1,5 @@
# `@actions/download-artifact`
> [!WARNING]
> actions/download-artifact@v3 is scheduled for deprecation on **November 30, 2024**. [Learn more.](https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/)
> Similarly, v1/v2 are scheduled for deprecation on **June 30, 2024**.
> Please update your workflow to use v4 of the artifact actions.
> This deprecation will not impact any existing versions of GitHub Enterprise Server being used by customers.
Download [Actions Artifacts](https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts) from your Workflow Runs. Internally powered by the [@actions/artifact](https://github.com/actions/toolkit/tree/main/packages/artifact) package.
See also [upload-artifact](https://github.com/actions/upload-artifact).

224
__tests__/download.test.ts Normal file
View File

@@ -0,0 +1,224 @@
import * as core from '@actions/core'
import artifact, {ArtifactNotFoundError} from '@actions/artifact'
import {run} from '../src/download-artifact'
import {Inputs} from '../src/constants'
jest.mock('@actions/github', () => ({
context: {
repo: {
owner: 'actions',
repo: 'toolkit'
},
runId: 123,
serverUrl: 'https://github.com'
}
}))
jest.mock('@actions/core')
/* eslint-disable no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */
const mockInputs = (overrides?: Partial<{[K in Inputs]?: any}>) => {
const inputs = {
[Inputs.Name]: 'artifact-name',
[Inputs.Path]: '/some/artifact/path',
[Inputs.GitHubToken]: 'warn',
[Inputs.Repository]: 'owner/some-repository',
[Inputs.RunID]: 'some-run-id',
[Inputs.Pattern]: 'some-pattern',
...overrides
}
;(core.getInput as jest.Mock).mockImplementation((name: string) => {
return inputs[name]
})
;(core.getBooleanInput as jest.Mock).mockImplementation((name: string) => {
return inputs[name]
})
return inputs
}
describe('download', () => {
beforeEach(async () => {
mockInputs()
jest.clearAllMocks()
// Mock artifact client methods
jest
.spyOn(artifact, 'listArtifacts')
.mockImplementation(() => Promise.resolve({artifacts: []}))
jest.spyOn(artifact, 'getArtifact').mockImplementation(name => {
throw new ArtifactNotFoundError(`Artifact '${name}' not found`)
})
jest
.spyOn(artifact, 'downloadArtifact')
.mockImplementation(() => Promise.resolve({digestMismatch: false}))
})
test('downloads a single artifact by name', async () => {
const mockArtifact = {
id: 123,
name: 'artifact-name',
size: 1024,
digest: 'abc123'
}
jest
.spyOn(artifact, 'getArtifact')
.mockImplementation(() => Promise.resolve({artifact: mockArtifact}))
await run()
expect(artifact.downloadArtifact).toHaveBeenCalledWith(
mockArtifact.id,
expect.objectContaining({
expectedHash: mockArtifact.digest
})
)
expect(core.info).toHaveBeenCalledWith('Total of 1 artifact(s) downloaded')
expect(core.setOutput).toHaveBeenCalledWith(
'download-path',
expect.any(String)
)
expect(core.info).toHaveBeenCalledWith(
'Download artifact has finished successfully'
)
})
test('downloads multiple artifacts when no name or pattern provided', async () => {
jest.clearAllMocks()
mockInputs({
[Inputs.Name]: '',
[Inputs.Pattern]: ''
})
const mockArtifacts = [
{id: 123, name: 'artifact1', size: 1024, digest: 'abc123'},
{id: 456, name: 'artifact2', size: 2048, digest: 'def456'}
]
// Set up artifact mock after clearing mocks
jest
.spyOn(artifact, 'listArtifacts')
.mockImplementation(() => Promise.resolve({artifacts: mockArtifacts}))
// Reset downloadArtifact mock as well
jest
.spyOn(artifact, 'downloadArtifact')
.mockImplementation(() => Promise.resolve({digestMismatch: false}))
await run()
expect(core.info).toHaveBeenCalledWith(
'No input name or pattern filtered specified, downloading all artifacts'
)
expect(core.info).toHaveBeenCalledWith('Total of 2 artifact(s) downloaded')
expect(artifact.downloadArtifact).toHaveBeenCalledTimes(2)
})
test('sets download path output even when no artifacts are found', async () => {
mockInputs({[Inputs.Name]: ''})
await run()
expect(core.setOutput).toHaveBeenCalledWith(
'download-path',
expect.any(String)
)
expect(core.info).toHaveBeenCalledWith(
'Download artifact has finished successfully'
)
expect(core.info).toHaveBeenCalledWith('Total of 0 artifact(s) downloaded')
})
test('filters artifacts by pattern', async () => {
const mockArtifacts = [
{id: 123, name: 'test-artifact', size: 1024, digest: 'abc123'},
{id: 456, name: 'prod-artifact', size: 2048, digest: 'def456'}
]
jest
.spyOn(artifact, 'listArtifacts')
.mockImplementation(() => Promise.resolve({artifacts: mockArtifacts}))
mockInputs({
[Inputs.Name]: '',
[Inputs.Pattern]: 'test-*'
})
await run()
expect(artifact.downloadArtifact).toHaveBeenCalledTimes(1)
expect(artifact.downloadArtifact).toHaveBeenCalledWith(
123,
expect.anything()
)
})
test('uses token and repository information when provided', async () => {
const token = 'ghp_testtoken123'
mockInputs({
[Inputs.Name]: '',
[Inputs.GitHubToken]: token,
[Inputs.Repository]: 'myorg/myrepo',
[Inputs.RunID]: '789'
})
jest
.spyOn(artifact, 'listArtifacts')
.mockImplementation(() => Promise.resolve({artifacts: []}))
await run()
expect(artifact.listArtifacts).toHaveBeenCalledWith(
expect.objectContaining({
findBy: {
token,
workflowRunId: 789,
repositoryName: 'myrepo',
repositoryOwner: 'myorg'
}
})
)
})
test('throws error when repository format is invalid', async () => {
mockInputs({
[Inputs.GitHubToken]: 'some-token',
[Inputs.Repository]: 'invalid-format' // Missing the owner/repo format
})
await expect(run()).rejects.toThrow(
"Invalid repository: 'invalid-format'. Must be in format owner/repo"
)
})
test('warns when digest validation fails', async () => {
const mockArtifact = {
id: 123,
name: 'corrupted-artifact',
size: 1024,
digest: 'abc123'
}
jest
.spyOn(artifact, 'getArtifact')
.mockImplementation(() => Promise.resolve({artifact: mockArtifact}))
jest
.spyOn(artifact, 'downloadArtifact')
.mockImplementation(() => Promise.resolve({digestMismatch: true}))
await run()
expect(core.warning).toHaveBeenCalledWith(
expect.stringContaining('digest validation failed')
)
})
})

2707
dist/index.js vendored

File diff suppressed because it is too large Load Diff

View File

@@ -189,7 +189,8 @@ jobs:
- name: Create a File
run: echo "hello from ${{ matrix.runs-on }}" > file-${{ matrix.runs-on }}.txt
- name: Upload Artifact
uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
- name: all-my-files
+ name: my-artifact-${{ matrix.runs-on }}
@@ -205,4 +206,4 @@ jobs:
+ pattern: my-artifact-*
```
Note that this will download all artifacts to a temporary directory and reupload them as a single artifact. For more information on inputs and other use cases for `actions/upload-artifact/merge@v4`, see [the action documentation](../merge/README.md).
Note that this will download all artifacts to a temporary directory and reupload them as a single artifact. For more information on inputs and other use cases for `actions/upload-artifact/merge@v4`, see [the action documentation](https://github.com/actions/upload-artifact/blob/main/merge/README.md).

12
jest.config.ts Normal file
View File

@@ -0,0 +1,12 @@
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
roots: ['<rootDir>'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
}

8675
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "download-artifact",
"version": "4.1.7",
"version": "4.2.0",
"description": "Download an Actions Artifact from a workflow run",
"main": "dist/index.js",
"scripts": {
@@ -9,7 +9,8 @@
"check-all": "concurrently \"npm:format-check\" \"npm:lint\" \"npm:build\"",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"lint": "eslint **/*.ts"
"lint": "eslint **/*.ts",
"test": "jest"
},
"repository": {
"type": "git",
@@ -28,12 +29,13 @@
},
"homepage": "https://github.com/actions/download-artifact#readme",
"dependencies": {
"@actions/artifact": "^2.1.8",
"@actions/artifact": "^2.3.2",
"@actions/core": "^1.10.1",
"@actions/github": "^5.1.1",
"minimatch": "^9.0.3"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node": "^12.12.6",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@vercel/ncc": "^0.33.4",
@@ -41,7 +43,10 @@
"eslint": "^8.55.0",
"eslint-plugin-github": "^4.10.1",
"eslint-plugin-prettier": "^5.0.1",
"jest": "^29.7.0",
"prettier": "^3.1.1",
"ts-jest": "^29.2.6",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
}

View File

@@ -15,7 +15,7 @@ export const chunk = <T>(arr: T[], n: number): T[][] =>
return acc
}, [] as T[][])
async function run(): Promise<void> {
export async function run(): Promise<void> {
const inputs = {
name: core.getInput(Inputs.Name, {required: false}),
path: core.getInput(Inputs.Path, {required: false}),
@@ -106,26 +106,39 @@ async function run(): Promise<void> {
core.info(`Preparing to download the following artifacts:`)
artifacts.forEach(artifact => {
core.info(
`- ${artifact.name} (ID: ${artifact.id}, Size: ${artifact.size})`
`- ${artifact.name} (ID: ${artifact.id}, Size: ${artifact.size}, Expected Digest: ${artifact.digest})`
)
})
}
const downloadPromises = artifacts.map(artifact =>
artifactClient.downloadArtifact(artifact.id, {
const downloadPromises = artifacts.map(artifact => ({
name: artifact.name,
promise: artifactClient.downloadArtifact(artifact.id, {
...options,
path:
isSingleArtifactDownload || inputs.mergeMultiple
? resolvedPath
: path.join(resolvedPath, artifact.name)
: path.join(resolvedPath, artifact.name),
expectedHash: artifact.digest
})
)
}))
const chunkedPromises = chunk(downloadPromises, PARALLEL_DOWNLOADS)
for (const chunk of chunkedPromises) {
await Promise.all(chunk)
}
const chunkPromises = chunk.map(item => item.promise)
const results = await Promise.all(chunkPromises)
for (let i = 0; i < results.length; i++) {
const outcome = results[i]
const artifactName = chunk[i].name
if (outcome.digestMismatch) {
core.warning(
`Artifact '${artifactName}' digest validation failed. Please verify the integrity of the artifact.`
)
}
}
}
core.info(`Total of ${artifacts.length} artifact(s) downloaded`)
core.setOutput(Outputs.DownloadPath, resolvedPath)
core.info('Download artifact has finished successfully')

View File

@@ -9,5 +9,5 @@
"moduleResolution": "node",
"esModuleInterop": true
},
"exclude": ["node_modules", "**/*.test.ts"]
"exclude": ["node_modules", "**/*.test.ts", "jest.config.ts", "__tests__"]
}