mirror of
				https://github.com/actions/download-artifact.git
				synced 2025-10-31 19:43:42 +00:00 
			
		
		
		
	Merge branch 'main' into main
This commit is contained in:
		
						commit
						68909842a1
					
				
							
								
								
									
										224
									
								
								__tests__/download.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								__tests__/download.test.ts
									
									
									
									
									
										Normal 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') | ||||
|     ) | ||||
|   }) | ||||
| }) | ||||
							
								
								
									
										9
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @ -118760,7 +118760,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { | ||||
|     return (mod && mod.__esModule) ? mod : { "default": mod }; | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", ({ value: true })); | ||||
| exports.chunk = void 0; | ||||
| exports.run = exports.chunk = void 0; | ||||
| const os = __importStar(__nccwpck_require__(22037)); | ||||
| const path = __importStar(__nccwpck_require__(71017)); | ||||
| const core = __importStar(__nccwpck_require__(42186)); | ||||
| @ -118857,12 +118857,13 @@ function run() { | ||||
|                     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(constants_1.Outputs.DownloadPath, resolvedPath); | ||||
|             core.info('Download artifact has finished successfully'); | ||||
|         } | ||||
|         core.info(`Total of ${artifacts.length} artifact(s) downloaded`); | ||||
|         core.setOutput(constants_1.Outputs.DownloadPath, resolvedPath); | ||||
|         core.info('Download artifact has finished successfully'); | ||||
|     }); | ||||
| } | ||||
| exports.run = run; | ||||
| run().catch(err => core.setFailed(`Unable to download artifact(s): ${err.message}`)); | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										12
									
								
								jest.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								jest.config.ts
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
							
								
								
									
										8309
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8309
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "download-artifact", | ||||
|   "version": "4.2.1", | ||||
|   "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", | ||||
| @ -34,6 +35,7 @@ | ||||
|     "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" | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -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}), | ||||
| @ -138,10 +138,10 @@ async function run(): Promise<void> { | ||||
|         ) | ||||
|       } | ||||
|     } | ||||
|     core.info(`Total of ${artifacts.length} artifact(s) downloaded`) | ||||
|     core.setOutput(Outputs.DownloadPath, resolvedPath) | ||||
|     core.info('Download artifact has finished successfully') | ||||
|   } | ||||
|   core.info(`Total of ${artifacts.length} artifact(s) downloaded`) | ||||
|   core.setOutput(Outputs.DownloadPath, resolvedPath) | ||||
|   core.info('Download artifact has finished successfully') | ||||
| } | ||||
| 
 | ||||
| run().catch(err => | ||||
|  | ||||
| @ -9,5 +9,5 @@ | ||||
|     "moduleResolution": "node", | ||||
|     "esModuleInterop": true | ||||
|   }, | ||||
|   "exclude": ["node_modules", "**/*.test.ts"] | ||||
|   "exclude": ["node_modules", "**/*.test.ts", "jest.config.ts", "__tests__"] | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user