mirror of
				https://gitea.com/actions/upload-artifact.git
				synced 2025-10-31 00:58:14 +07: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:
		
							parent
							
								
									8eb149d680
								
							
						
					
					
						commit
						c9be818b8a
					
				
							
								
								
									
										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" | ||||||
|  |   } | ||||||
							
								
								
									
										77
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								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). | See also [download-artifact](https://github.com/actions/download-artifact). | ||||||
| 
 | 
 | ||||||
| @ -8,37 +8,96 @@ See also [download-artifact](https://github.com/actions/download-artifact). | |||||||
| 
 | 
 | ||||||
| See [action.yml](action.yml) | See [action.yml](action.yml) | ||||||
| 
 | 
 | ||||||
| Basic: | ### Upload an Individual File | ||||||
| ```yaml | ```yaml | ||||||
| steps: | steps: | ||||||
| - uses: actions/checkout@v1 | - uses: actions/checkout@v2 | ||||||
| 
 | 
 | ||||||
| - run: mkdir -p path/to/artifact | - run: mkdir -p path/to/artifact | ||||||
| 
 | 
 | ||||||
| - run: echo hello > path/to/artifact/world.txt | - run: echo hello > path/to/artifact/world.txt | ||||||
| 
 | 
 | ||||||
| - uses: actions/upload-artifact@v1 | - uses: actions/upload-artifact@v2-preview | ||||||
|   with: |   with: | ||||||
|     name: my-artifact |     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): | 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 | ```yaml | ||||||
| - uses: actions/upload-artifact@v1 | - uses: actions/upload-artifact@v2-preview | ||||||
|   if: failure() |   if: failure() | ||||||
|   with: |   with: | ||||||
|     name: my-artifact |     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? | ## 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/> | 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/> | ||||||
| <img src="https://user-images.githubusercontent.com/16109154/72556687-20235a80-386d-11ea-9e2a-b534faa77083.png" width="375" height="140"> | <img src="https://user-images.githubusercontent.com/16109154/72556687-20235a80-386d-11ea-9e2a-b534faa77083.png" width="375" height="140"> | ||||||
| 
 | 
 | ||||||
| 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.  | 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 | # 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:  | inputs:  | ||||||
|   name: |   name: | ||||||
|     description: 'Artifact name' |     description: 'Artifact name' | ||||||
|     required: true |     required: false | ||||||
|   path: |   path: | ||||||
|     description: 'Directory containing files to upload' |     description: 'A file, directory or wildcard pattern that describes what to upload' | ||||||
|     required: true |     required: true | ||||||
| runs: | runs: | ||||||
|   # Plugins live on the runner and are only available to a certain set of first party actions. |   using: 'node12' | ||||||
|   plugin: 'publish' |   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"] | ||||||
|  |   } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user