mirror of
				https://github.com/actions/upload-artifact.git
				synced 2025-10-23 08:56:38 +08:00 
			
		
		
		
	V2 Preview (#54)
* V2 Upload Artifact * Improve logs * Update release * Update test.yml * Update test.yml * Update test.yml * @actions/artifact v0.2.0 package * Add extra YAML test
This commit is contained in:
		
							
								
								
									
										3
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| node_modules/ | ||||
| lib/ | ||||
| dist/ | ||||
							
								
								
									
										19
									
								
								.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| { | ||||
|     "env": { "node": true, "jest": true }, | ||||
|     "parser": "@typescript-eslint/parser", | ||||
|     "parserOptions": { "ecmaVersion": 9, "sourceType": "module" }, | ||||
|     "extends": [ | ||||
|       "eslint:recommended", | ||||
|       "plugin:@typescript-eslint/eslint-recommended", | ||||
|       "plugin:@typescript-eslint/recommended", | ||||
|       "plugin:import/errors", | ||||
|       "plugin:import/warnings", | ||||
|       "plugin:import/typescript", | ||||
|       "plugin:prettier/recommended", | ||||
|       "prettier/@typescript-eslint" | ||||
|     ], | ||||
|     "rules": { | ||||
|        "@typescript-eslint/no-empty-function": "off" | ||||
|     }, | ||||
|     "plugins": ["@typescript-eslint", "jest"] | ||||
| } | ||||
							
								
								
									
										142
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| name: Test | ||||
| on: | ||||
|   push: | ||||
|     branches:	 | ||||
|       - v2-preview | ||||
|     paths-ignore: | ||||
|       - '**.md' | ||||
|   pull_request: | ||||
|     branches:	 | ||||
|       - v2-preview | ||||
|     paths-ignore: | ||||
|       - '**.md' | ||||
|  | ||||
| jobs: | ||||
|  | ||||
|   build: | ||||
|     name: Build | ||||
|  | ||||
|     strategy: | ||||
|       matrix: | ||||
|         runs-on: [ubuntu-latest, macOS-latest, windows-latest] | ||||
|       fail-fast: false | ||||
|  | ||||
|     runs-on: ${{ matrix.runs-on }} | ||||
|  | ||||
|     steps: | ||||
|     - name: Checkout | ||||
|       uses: actions/checkout@v2 | ||||
|  | ||||
|     - name: Set Node.js 12.x | ||||
|       uses: actions/setup-node@v1 | ||||
|       with: | ||||
|         node-version: 12.x | ||||
|  | ||||
|     - name: npm install | ||||
|       run: npm install | ||||
|  | ||||
|     - name: Compile | ||||
|       run: npm run build | ||||
|  | ||||
|     - name: npm test | ||||
|       run: npm test | ||||
|  | ||||
|     - name: Lint | ||||
|       run: npm run lint | ||||
|  | ||||
|     - name: Format | ||||
|       run: npm run format-check | ||||
|  | ||||
|     # Test end-to-end by uploading two artifacts and then downloading them | ||||
|     - name: Create artifact files | ||||
|       run: | | ||||
|         mkdir -p path/to/dir-1 | ||||
|         mkdir -p path/to/dir-2 | ||||
|         mkdir -p path/to/dir-3     | ||||
|         echo "Lorem ipsum dolor sit amet" > path/to/dir-1/file1.txt | ||||
|         echo "Hello world from file #2" > path/to/dir-2/file2.txt | ||||
|         echo "This is a going to be a test for a large enough file that should get compressed with GZip. The @actions/artifact package uses GZip to upload files. This text should have a compression ratio greater than 100% so it should get uploaded using GZip" > path/to/dir-3/gzip.txt | ||||
|  | ||||
|     # Upload a single file artifact | ||||
|     - name: 'Upload artifact #1' | ||||
|       uses: ./ | ||||
|       with: | ||||
|         name: 'Artifact-A' | ||||
|         path: path/to/dir-1/file1.txt | ||||
|  | ||||
|     # Upload using a wildcard pattern, name should default to 'artifact' if not provided | ||||
|     - name: 'Upload artifact #2' | ||||
|       uses: ./ | ||||
|       with: | ||||
|         path: path/**/dir*/ | ||||
|  | ||||
|     # Upload a directory that contains a file that will be uploaded with GZip | ||||
|     - name: 'Upload artifact #3' | ||||
|       uses: ./ | ||||
|       with: | ||||
|         name: 'GZip-Artifact' | ||||
|         path: path/to/dir-3/ | ||||
|  | ||||
|     # Verify artifacts. Switch to download-artifact@v2 once it's out of preview | ||||
|  | ||||
|     # Download Artifact #1 and verify the correctness of the content | ||||
|     - name: 'Download artifact #1' | ||||
|       uses: actions/download-artifact@v1 | ||||
|       with: | ||||
|         name: 'Artifact-A' | ||||
|         path: some/new/path | ||||
|  | ||||
|     - name: 'Verify Artifact #1' | ||||
|       run: | | ||||
|         $file = "some/new/path/file1.txt" | ||||
|         if(!(Test-Path -path $file)) | ||||
|         { | ||||
|             Write-Error "Expected file does not exist" | ||||
|         } | ||||
|         if(!((Get-Content $file) -ceq "Lorem ipsum dolor sit amet")) | ||||
|         { | ||||
|             Write-Error "File contents of downloaded artifact are incorrect" | ||||
|         } | ||||
|       shell: pwsh | ||||
|  | ||||
|     # Download Artifact #2 and verify the correctness of the content | ||||
|     - name: 'Download artifact #2' | ||||
|       uses: actions/download-artifact@v1 | ||||
|       with: | ||||
|         name: 'artifact' | ||||
|         path: some/other/path | ||||
|  | ||||
|     - name: 'Verify Artifact #2' | ||||
|       run: | | ||||
|         $file1 = "some/other/path/to/dir-1/file1.txt" | ||||
|         $file2 = "some/other/path/to/dir-2/file2.txt" | ||||
|         if(!(Test-Path -path $file1) -or !(Test-Path -path $file2)) | ||||
|         { | ||||
|             Write-Error "Expected files do not exist" | ||||
|         } | ||||
|         if(!((Get-Content $file1) -ceq "Lorem ipsum dolor sit amet") -or !((Get-Content $file2) -ceq "Hello world from file #2")) | ||||
|         { | ||||
|             Write-Error "File contents of downloaded artifacts are incorrect" | ||||
|         } | ||||
|       shell: pwsh | ||||
|      | ||||
|     # Download Artifact #3 and verify the correctness of the content | ||||
|     - name: 'Download artifact #3' | ||||
|       uses: actions/download-artifact@v1 | ||||
|       with: | ||||
|         name: 'GZip-Artifact' | ||||
|         path: gzip/artifact/path | ||||
|  | ||||
|     # Because a directory was used as input during the upload the parent directories, path/to/dir-3/, should not be included in the uploaded artifact | ||||
|     - name: 'Verify Artifact #3' | ||||
|       run: | | ||||
|         $gzipFile = "gzip/artifact/path/gzip.txt" | ||||
|         if(!(Test-Path -path $gzipFile)) | ||||
|         { | ||||
|             Write-Error "Expected file do not exist" | ||||
|         } | ||||
|         if(!((Get-Content $gzipFile) -ceq "This is a going to be a test for a large enough file that should get compressed with GZip. The @actions/artifact package uses GZip to upload files. This text should have a compression ratio greater than 100% so it should get uploaded using GZip")) | ||||
|         { | ||||
|             Write-Error "File contents of downloaded artifact is incorrect" | ||||
|         } | ||||
|       shell: pwsh | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| node_modules/ | ||||
| lib/ | ||||
| __tests__/_temp/ | ||||
							
								
								
									
										3
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.prettierignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| dist/ | ||||
| lib/ | ||||
| node_modules/ | ||||
							
								
								
									
										11
									
								
								.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| { | ||||
|     "printWidth": 80, | ||||
|     "tabWidth": 2, | ||||
|     "useTabs": false, | ||||
|     "semi": false, | ||||
|     "singleQuote": true, | ||||
|     "trailingComma": "none", | ||||
|     "bracketSpacing": false, | ||||
|     "arrowParens": "avoid", | ||||
|     "parser": "typescript" | ||||
|   } | ||||
							
								
								
									
										75
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| # upload-artifact | ||||
| # Upload-Artifact v2 Preview | ||||
|  | ||||
| This uploads artifacts from your workflow. | ||||
| This uploads artifacts from your workflow allowing you to share data between jobs and store data once a workflow is complete. | ||||
|  | ||||
| See also [download-artifact](https://github.com/actions/download-artifact). | ||||
|  | ||||
| @@ -8,31 +8,86 @@ See also [download-artifact](https://github.com/actions/download-artifact). | ||||
|  | ||||
| See [action.yml](action.yml) | ||||
|  | ||||
| Basic: | ||||
| ### Upload an Individual File | ||||
| ```yaml | ||||
| steps: | ||||
| - uses: actions/checkout@v1 | ||||
| - uses: actions/checkout@v2 | ||||
|  | ||||
| - run: mkdir -p path/to/artifact | ||||
|  | ||||
| - run: echo hello > path/to/artifact/world.txt | ||||
|  | ||||
| - uses: actions/upload-artifact@v1 | ||||
| - uses: actions/upload-artifact@v2-preview | ||||
|   with: | ||||
|     name: my-artifact | ||||
|     path: path/to/artifact | ||||
|     path: path/to/artifact/world.txt | ||||
| ``` | ||||
|  | ||||
| ### Upload an Entire Directory | ||||
|  | ||||
| ```yaml | ||||
| - uses: actions/upload-artifact@v2-preview | ||||
|   with: | ||||
|     name: my-artifact | ||||
|     path: path/to/artifact/ # or path/to/artifact | ||||
| ``` | ||||
|  | ||||
| ### Upload using a Wildcard Pattern: | ||||
| ```yaml | ||||
| - uses: actions/upload-artifact@v2-preview | ||||
|   with: | ||||
|     name: my-artifact | ||||
|     path: path/**/[abc]rtifac?/* | ||||
| ``` | ||||
|  | ||||
| For supported wildcards along with behavior and documentation, see [@actions/glob](https://github.com/actions/toolkit/tree/master/packages/glob) which is used internally to search for files. | ||||
|  | ||||
| Relative and absolute file paths are both allowed. Relative paths are rooted against the current working directory. | ||||
|  | ||||
| ### Conditional Artifact Upload | ||||
|  | ||||
| To upload artifacts only when the previous step of a job failed, use [`if: failure()`](https://help.github.com/en/articles/contexts-and-expression-syntax-for-github-actions#job-status-check-functions): | ||||
|  | ||||
| ```yaml | ||||
| - uses: actions/upload-artifact@v1 | ||||
| - uses: actions/upload-artifact@v2-preview | ||||
|   if: failure() | ||||
|   with: | ||||
|     name: my-artifact | ||||
|     path: path/to/artifact | ||||
|     path: path/to/artifact/ | ||||
| ``` | ||||
|  | ||||
| ### Uploading without an artifact name | ||||
|  | ||||
| You can upload an artifact without specifying a name | ||||
| ```yaml | ||||
| - uses: actions/upload-artifact@v2-preview | ||||
|   with: | ||||
|     path: path/to/artifact/world.txt | ||||
| ``` | ||||
|  | ||||
| If not provided, `artifact` will be used as the default name which will manifest itself in the UI after upload. | ||||
|  | ||||
| ### Uploading to the same artifact | ||||
|  | ||||
| Each artifact behaves as a file share. Uploading to the same artifact multiple times in the same workflow can overwrite and append already uploaded files | ||||
|  | ||||
| ```yaml | ||||
| - run: echo hi > world.txt | ||||
| - uses: actions/upload-artifact@v2-preview | ||||
|   with: | ||||
|     path: world.txt | ||||
|  | ||||
| - run: echo howdy > extra-file.txt | ||||
| - uses: actions/upload-artifact@v2-preview | ||||
|   with: | ||||
|     path: extra-file.txt | ||||
|  | ||||
| - run: echo hello > world.txt | ||||
| - uses: actions/upload-artifact@v2-preview | ||||
|   with: | ||||
|     path: world.txt | ||||
| ``` | ||||
| With the following example, the available artifact (named `artifact`) would contain both `world.txt` (`hello`) and `extra-file.txt` (`howdy`). | ||||
|  | ||||
| ## Where does the upload go? | ||||
| In the top right corner of a workflow run, once the run is over, if you used this action, there will be a `Artifacts` dropdown which you can download items from. Here's a screenshot of what it looks like<br/> | ||||
| @@ -40,6 +95,10 @@ In the top right corner of a workflow run, once the run is over, if you used thi | ||||
|  | ||||
| There is a trash can icon that can be used to delete the artifact. This icon will only appear for users who have write permissions to the repository. | ||||
|  | ||||
| ## Additional Documentation | ||||
|  | ||||
| See [persisting workflow data using artifacts](https://help.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts) for additional examples and tips.  | ||||
|  | ||||
|  | ||||
| # License | ||||
|  | ||||
|   | ||||
							
								
								
									
										289
									
								
								__tests__/search.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								__tests__/search.test.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,289 @@ | ||||
| import * as core from '@actions/core' | ||||
| import * as path from 'path' | ||||
| import * as io from '@actions/io' | ||||
| import {promises as fs} from 'fs' | ||||
| import {findFilesToUpload} from '../src/search' | ||||
|  | ||||
| const root = path.join(__dirname, '_temp', 'search') | ||||
| const searchItem1Path = path.join( | ||||
|   root, | ||||
|   'folder-a', | ||||
|   'folder-b', | ||||
|   'folder-c', | ||||
|   'search-item1.txt' | ||||
| ) | ||||
| const searchItem2Path = path.join(root, 'folder-d', 'search-item2.txt') | ||||
| const searchItem3Path = path.join(root, 'folder-d', 'search-item3.txt') | ||||
| const searchItem4Path = path.join(root, 'folder-d', 'search-item4.txt') | ||||
| const searchItem5Path = path.join(root, 'search-item5.txt') | ||||
| const extraSearchItem1Path = path.join( | ||||
|   root, | ||||
|   'folder-a', | ||||
|   'folder-b', | ||||
|   'folder-c', | ||||
|   'extraSearch-item1.txt' | ||||
| ) | ||||
| const extraSearchItem2Path = path.join( | ||||
|   root, | ||||
|   'folder-d', | ||||
|   'extraSearch-item2.txt' | ||||
| ) | ||||
| const extraSearchItem3Path = path.join( | ||||
|   root, | ||||
|   'folder-f', | ||||
|   'extraSearch-item3.txt' | ||||
| ) | ||||
| const extraSearchItem4Path = path.join( | ||||
|   root, | ||||
|   'folder-h', | ||||
|   'folder-i', | ||||
|   'extraSearch-item4.txt' | ||||
| ) | ||||
| const extraSearchItem5Path = path.join( | ||||
|   root, | ||||
|   'folder-h', | ||||
|   'folder-i', | ||||
|   'extraSearch-item5.txt' | ||||
| ) | ||||
| const extraFileInFolderCPath = path.join( | ||||
|   root, | ||||
|   'folder-a', | ||||
|   'folder-b', | ||||
|   'folder-c', | ||||
|   'extra-file-in-folder-c.txt' | ||||
| ) | ||||
| const amazingFileInFolderHPath = path.join(root, 'folder-h', 'amazing-item.txt') | ||||
| const lonelyFilePath = path.join( | ||||
|   root, | ||||
|   'folder-h', | ||||
|   'folder-j', | ||||
|   'folder-k', | ||||
|   'lonely-file.txt' | ||||
| ) | ||||
|  | ||||
| describe('Search', () => { | ||||
|   beforeAll(async () => { | ||||
|     // mock all output so that there is less noise when running tests | ||||
|     jest.spyOn(console, 'log').mockImplementation(() => {}) | ||||
|     jest.spyOn(core, 'debug').mockImplementation(() => {}) | ||||
|     jest.spyOn(core, 'info').mockImplementation(() => {}) | ||||
|     jest.spyOn(core, 'warning').mockImplementation(() => {}) | ||||
|  | ||||
|     // clear temp directory | ||||
|     await io.rmRF(root) | ||||
|     await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-c'), { | ||||
|       recursive: true | ||||
|     }) | ||||
|     await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-e'), { | ||||
|       recursive: true | ||||
|     }) | ||||
|     await fs.mkdir(path.join(root, 'folder-d'), { | ||||
|       recursive: true | ||||
|     }) | ||||
|     await fs.mkdir(path.join(root, 'folder-f'), { | ||||
|       recursive: true | ||||
|     }) | ||||
|     await fs.mkdir(path.join(root, 'folder-g'), { | ||||
|       recursive: true | ||||
|     }) | ||||
|     await fs.mkdir(path.join(root, 'folder-h', 'folder-i'), { | ||||
|       recursive: true | ||||
|     }) | ||||
|     await fs.mkdir(path.join(root, 'folder-h', 'folder-j', 'folder-k'), { | ||||
|       recursive: true | ||||
|     }) | ||||
|  | ||||
|     await fs.writeFile(searchItem1Path, 'search item1 file') | ||||
|     await fs.writeFile(searchItem2Path, 'search item2 file') | ||||
|     await fs.writeFile(searchItem3Path, 'search item3 file') | ||||
|     await fs.writeFile(searchItem4Path, 'search item4 file') | ||||
|     await fs.writeFile(searchItem5Path, 'search item5 file') | ||||
|  | ||||
|     await fs.writeFile(extraSearchItem1Path, 'extraSearch item1 file') | ||||
|     await fs.writeFile(extraSearchItem2Path, 'extraSearch item2 file') | ||||
|     await fs.writeFile(extraSearchItem3Path, 'extraSearch item3 file') | ||||
|     await fs.writeFile(extraSearchItem4Path, 'extraSearch item4 file') | ||||
|     await fs.writeFile(extraSearchItem5Path, 'extraSearch item5 file') | ||||
|  | ||||
|     await fs.writeFile(extraFileInFolderCPath, 'extra file') | ||||
|  | ||||
|     await fs.writeFile(amazingFileInFolderHPath, 'amazing file') | ||||
|  | ||||
|     await fs.writeFile(lonelyFilePath, 'all by itself') | ||||
|     /* | ||||
|       Directory structure of files that get created: | ||||
|       root/ | ||||
|           folder-a/ | ||||
|               folder-b/ | ||||
|                   folder-c/ | ||||
|                       search-item1.txt | ||||
|                       extraSearch-item1.txt | ||||
|                       extra-file-in-folder-c.txt | ||||
|                   folder-e/ | ||||
|           folder-d/ | ||||
|               search-item2.txt | ||||
|               search-item3.txt | ||||
|               search-item4.txt | ||||
|               extraSearch-item2.txt | ||||
|           folder-f/ | ||||
|               extraSearch-item3.txt | ||||
|           folder-g/ | ||||
|           folder-h/ | ||||
|               amazing-item.txt | ||||
|               folder-i/ | ||||
|                   extraSearch-item4.txt | ||||
|                   extraSearch-item5.txt | ||||
|               folder-j/ | ||||
|                   folder-k/ | ||||
|                       lonely-file.txt | ||||
|           search-item5.txt | ||||
|     */ | ||||
|   }) | ||||
|  | ||||
|   it('Single file search - Absolute Path', async () => { | ||||
|     const searchResult = await findFilesToUpload(extraFileInFolderCPath) | ||||
|     expect(searchResult.filesToUpload.length).toEqual(1) | ||||
|     expect(searchResult.filesToUpload[0]).toEqual(extraFileInFolderCPath) | ||||
|     expect(searchResult.rootDirectory).toEqual( | ||||
|       path.join(root, 'folder-a', 'folder-b', 'folder-c') | ||||
|     ) | ||||
|   }) | ||||
|  | ||||
|   it('Single file search - Relative Path', async () => { | ||||
|     const relativePath = path.join( | ||||
|       '__tests__', | ||||
|       '_temp', | ||||
|       'search', | ||||
|       'folder-a', | ||||
|       'folder-b', | ||||
|       'folder-c', | ||||
|       'search-item1.txt' | ||||
|     ) | ||||
|  | ||||
|     const searchResult = await findFilesToUpload(relativePath) | ||||
|     expect(searchResult.filesToUpload.length).toEqual(1) | ||||
|     expect(searchResult.filesToUpload[0]).toEqual(searchItem1Path) | ||||
|     expect(searchResult.rootDirectory).toEqual( | ||||
|       path.join(root, 'folder-a', 'folder-b', 'folder-c') | ||||
|     ) | ||||
|   }) | ||||
|  | ||||
|   it('Single file using wildcard', async () => { | ||||
|     const expectedRoot = path.join(root, 'folder-h') | ||||
|     const searchPath = path.join(root, 'folder-h', '**/*lonely*') | ||||
|     const searchResult = await findFilesToUpload(searchPath) | ||||
|     expect(searchResult.filesToUpload.length).toEqual(1) | ||||
|     expect(searchResult.filesToUpload[0]).toEqual(lonelyFilePath) | ||||
|     expect(searchResult.rootDirectory).toEqual(expectedRoot) | ||||
|   }) | ||||
|  | ||||
|   it('Single file using directory', async () => { | ||||
|     const searchPath = path.join(root, 'folder-h', 'folder-j') | ||||
|     const searchResult = await findFilesToUpload(searchPath) | ||||
|     expect(searchResult.filesToUpload.length).toEqual(1) | ||||
|     expect(searchResult.filesToUpload[0]).toEqual(lonelyFilePath) | ||||
|     expect(searchResult.rootDirectory).toEqual(searchPath) | ||||
|   }) | ||||
|  | ||||
|   it('Directory search - Absolute Path', async () => { | ||||
|     const searchPath = path.join(root, 'folder-h') | ||||
|     const searchResult = await findFilesToUpload(searchPath) | ||||
|     expect(searchResult.filesToUpload.length).toEqual(4) | ||||
|  | ||||
|     expect( | ||||
|       searchResult.filesToUpload.includes(amazingFileInFolderHPath) | ||||
|     ).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem4Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(lonelyFilePath)).toEqual(true) | ||||
|  | ||||
|     expect(searchResult.rootDirectory).toEqual(searchPath) | ||||
|   }) | ||||
|  | ||||
|   it('Directory search - Relative Path', async () => { | ||||
|     const searchPath = path.join('__tests__', '_temp', 'search', 'folder-h') | ||||
|     const expectedRootDirectory = path.join(root, 'folder-h') | ||||
|     const searchResult = await findFilesToUpload(searchPath) | ||||
|     expect(searchResult.filesToUpload.length).toEqual(4) | ||||
|  | ||||
|     expect( | ||||
|       searchResult.filesToUpload.includes(amazingFileInFolderHPath) | ||||
|     ).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem4Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(lonelyFilePath)).toEqual(true) | ||||
|  | ||||
|     expect(searchResult.rootDirectory).toEqual(expectedRootDirectory) | ||||
|   }) | ||||
|  | ||||
|   it('Wildcard search - Absolute Path', async () => { | ||||
|     const searchPath = path.join(root, '**/*[Ss]earch*') | ||||
|     const searchResult = await findFilesToUpload(searchPath) | ||||
|     expect(searchResult.filesToUpload.length).toEqual(10) | ||||
|  | ||||
|     expect(searchResult.filesToUpload.includes(searchItem1Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(searchItem4Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(searchItem5Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem1Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem2Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem3Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem4Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|  | ||||
|     expect(searchResult.rootDirectory).toEqual(root) | ||||
|   }) | ||||
|  | ||||
|   it('Wildcard search - Relative Path', async () => { | ||||
|     const searchPath = path.join( | ||||
|       '__tests__', | ||||
|       '_temp', | ||||
|       'search', | ||||
|       '**/*[Ss]earch*' | ||||
|     ) | ||||
|     const searchResult = await findFilesToUpload(searchPath) | ||||
|     expect(searchResult.filesToUpload.length).toEqual(10) | ||||
|  | ||||
|     expect(searchResult.filesToUpload.includes(searchItem1Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(searchItem4Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(searchItem5Path)).toEqual(true) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem1Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem2Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem3Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem4Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|     expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual( | ||||
|       true | ||||
|     ) | ||||
|  | ||||
|     expect(searchResult.rootDirectory).toEqual(root) | ||||
|   }) | ||||
| }) | ||||
| @@ -4,10 +4,10 @@ author: 'GitHub' | ||||
| inputs:  | ||||
|   name: | ||||
|     description: 'Artifact name' | ||||
|     required: true | ||||
|     required: false | ||||
|   path: | ||||
|     description: 'Directory containing files to upload' | ||||
|     description: 'A file, directory or wildcard pattern that describes what to upload' | ||||
|     required: true | ||||
| runs: | ||||
|   # Plugins live on the runner and are only available to a certain set of first party actions. | ||||
|   plugin: 'publish' | ||||
|   using: 'node12' | ||||
|   main: 'dist/index.js' | ||||
							
								
								
									
										8314
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8314
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										12
									
								
								jest.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								jest.config.js
									
									
									
									
									
										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 | ||||
|   } | ||||
							
								
								
									
										6671
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6671
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										51
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| { | ||||
|   "name": "upload-artifact", | ||||
|   "version": "2.0.0", | ||||
|   "description": "Upload a build artifact that can be used by subsequent workflow steps", | ||||
|   "main": "dist/index.js", | ||||
|   "scripts": { | ||||
|     "build": "tsc", | ||||
|     "release": "ncc build src/upload-artifact.ts && git add -f dist/", | ||||
|     "check-all": "concurrently \"npm:format-check\" \"npm:lint\" \"npm:test\" \"npm:build\"", | ||||
|     "format": "prettier --write **/*.ts", | ||||
|     "format-check": "prettier --check **/*.ts", | ||||
|     "lint": "eslint **/*.ts", | ||||
|     "test": "jest --testTimeout 10000" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "git+https://github.com/actions/upload-artifact.git" | ||||
|   }, | ||||
|   "keywords": [ | ||||
|     "Actions", | ||||
|     "GitHub", | ||||
|     "Artifacts", | ||||
|     "Upload" | ||||
|   ], | ||||
|   "author": "GitHub", | ||||
|   "license": "MIT", | ||||
|   "bugs": { | ||||
|     "url": "https://github.com/actions/upload-artifact/issues" | ||||
|   }, | ||||
|   "homepage": "https://github.com/actions/upload-artifact#readme", | ||||
|   "devDependencies": { | ||||
|     "@actions/artifact": "^0.2.0", | ||||
|     "@actions/core": "^1.2.3", | ||||
|     "@actions/glob": "^0.1.0", | ||||
|     "@actions/io": "^1.0.2", | ||||
|     "@types/jest": "^25.1.4", | ||||
|     "@types/node": "^12.12.30", | ||||
|     "@typescript-eslint/parser": "^2.23.0", | ||||
|     "@zeit/ncc": "^0.20.5", | ||||
|     "concurrently": "^5.1.0", | ||||
|     "eslint": "^6.8.0", | ||||
|     "eslint-plugin-github": "^3.4.1", | ||||
|     "eslint-plugin-jest": "^23.8.2", | ||||
|     "glob": "^7.1.6", | ||||
|     "jest": "^25.1.0", | ||||
|     "jest-circus": "^25.1.0", | ||||
|     "prettier": "^1.19.1", | ||||
|     "ts-jest": "^25.2.1", | ||||
|     "typescript": "^3.8.3" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										8
									
								
								src/constants.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/constants.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| export enum Inputs { | ||||
|   Name = 'name', | ||||
|   Path = 'path' | ||||
| } | ||||
|  | ||||
| export function getDefaultArtifactName(): string { | ||||
|   return 'artifact' | ||||
| } | ||||
							
								
								
									
										69
									
								
								src/search.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/search.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| import * as glob from '@actions/glob' | ||||
| import {debug} from '@actions/core' | ||||
| import {lstatSync} from 'fs' | ||||
| import {dirname} from 'path' | ||||
|  | ||||
| export interface SearchResult { | ||||
|   filesToUpload: string[] | ||||
|   rootDirectory: string | ||||
| } | ||||
|  | ||||
| function getDefaultGlobOptions(): glob.GlobOptions { | ||||
|   return { | ||||
|     followSymbolicLinks: true, | ||||
|     implicitDescendants: true, | ||||
|     omitBrokenSymbolicLinks: true | ||||
|   } | ||||
| } | ||||
|  | ||||
| export async function findFilesToUpload( | ||||
|   searchPath: string, | ||||
|   globOptions?: glob.GlobOptions | ||||
| ): Promise<SearchResult> { | ||||
|   const searchResults: string[] = [] | ||||
|   const globber = await glob.create( | ||||
|     searchPath, | ||||
|     globOptions || getDefaultGlobOptions() | ||||
|   ) | ||||
|   const rawSearchResults: string[] = await globber.glob() | ||||
|  | ||||
|   /* | ||||
|     Directories will be rejected if attempted to be uploaded. This includes just empty | ||||
|     directories so filter any directories out from the raw search results | ||||
|   */ | ||||
|   for (const searchResult of rawSearchResults) { | ||||
|     if (!lstatSync(searchResult).isDirectory()) { | ||||
|       debug(`File:${searchResult} was found using the provided searchPath`) | ||||
|       searchResults.push(searchResult) | ||||
|     } else { | ||||
|       debug( | ||||
|         `Removing ${searchResult} from rawSearchResults because it is a directory` | ||||
|       ) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|     Only a single search pattern is being included so only 1 searchResult is expected. In the future if multiple search patterns are | ||||
|     simultaneously supported this will change | ||||
|   */ | ||||
|   const searchPaths: string[] = globber.getSearchPaths() | ||||
|   if (searchPaths.length > 1) { | ||||
|     throw new Error('Only 1 search path should be returned') | ||||
|   } | ||||
|  | ||||
|   /* | ||||
|     Special case for a single file artifact that is uploaded without a directory or wildcard pattern. The directory structure is | ||||
|     not preserved and the root directory will be the single files parent directory | ||||
|   */ | ||||
|   if (searchResults.length === 1 && searchPaths[0] === searchResults[0]) { | ||||
|     return { | ||||
|       filesToUpload: searchResults, | ||||
|       rootDirectory: dirname(searchResults[0]) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return { | ||||
|     filesToUpload: searchResults, | ||||
|     rootDirectory: searchPaths[0] | ||||
|   } | ||||
| } | ||||
							
								
								
									
										40
									
								
								src/upload-artifact.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/upload-artifact.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| import * as core from '@actions/core' | ||||
| import {create, UploadOptions} from '@actions/artifact' | ||||
| import {Inputs, getDefaultArtifactName} from './constants' | ||||
| import {findFilesToUpload} from './search' | ||||
|  | ||||
| async function run(): Promise<void> { | ||||
|   try { | ||||
|     const name = core.getInput(Inputs.Name, {required: false}) | ||||
|     const path = core.getInput(Inputs.Path, {required: true}) | ||||
|  | ||||
|     const searchResult = await findFilesToUpload(path) | ||||
|     if (searchResult.filesToUpload.length === 0) { | ||||
|       core.warning( | ||||
|         `No files were found for the provided path: ${path}. No artifacts will be uploaded.` | ||||
|       ) | ||||
|     } else { | ||||
|       core.info( | ||||
|         `With the provided path, there will be ${searchResult.filesToUpload.length} files uploaded` | ||||
|       ) | ||||
|       core.debug(`Root artifact directory is ${searchResult.rootDirectory}`) | ||||
|  | ||||
|       const artifactClient = create() | ||||
|       const options: UploadOptions = { | ||||
|         continueOnError: true | ||||
|       } | ||||
|       await artifactClient.uploadArtifact( | ||||
|         name || getDefaultArtifactName(), | ||||
|         searchResult.filesToUpload, | ||||
|         searchResult.rootDirectory, | ||||
|         options | ||||
|       ) | ||||
|  | ||||
|       core.info('Artifact upload has finished successfully!') | ||||
|     } | ||||
|   } catch (err) { | ||||
|     core.setFailed(err.message) | ||||
|   } | ||||
| } | ||||
|  | ||||
| run() | ||||
							
								
								
									
										17
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| { | ||||
|     "compilerOptions": { | ||||
|       "target": "es6", | ||||
|       "module": "commonjs", | ||||
|       "outDir": "./lib", | ||||
|       "rootDir": "./src", | ||||
|       "strict": true, | ||||
|       "noImplicitAny": false, | ||||
|       "moduleResolution": "node", | ||||
|       "allowSyntheticDefaultImports": true, | ||||
|       "esModuleInterop": true, | ||||
|       "declaration": false, | ||||
|       "sourceMap": true, | ||||
|       "lib": ["es6"] | ||||
|     }, | ||||
|     "exclude": ["node_modules", "**/*.test.ts"] | ||||
|   } | ||||
		Reference in New Issue
	
	Block a user