Detect case insensitive uploads + Bump @actions/artifact to version 0.3.3 (#106)
* Detect case insensitive uploads * PR feedback
This commit is contained in:
		
							parent
							
								
									5ba29a7d5b
								
							
						
					
					
						commit
						c8879bf5ae
					
				
							
								
								
									
										27
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								README.md
									
									
									
									
									
								
							| @ -203,7 +203,32 @@ Environment variables along with context expressions can also be used for input. | ||||
| 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"> | ||||
| 
 | ||||
| There is a trashcan 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 trashcan icon that can be used to delete the artifact. This icon will only appear for users who have write permissions to the repository. | ||||
| 
 | ||||
| # Limitations | ||||
| 
 | ||||
| ### Permission Loss | ||||
| 
 | ||||
| :exclamation: File permissions are not maintained during artifact upload :exclamation: For example, if you make a file executable using `chmod` and then upload that file, post-download the file is no longer guaranteed to be set as an executable. | ||||
| 
 | ||||
| ### Case Insensitive Uploads | ||||
| 
 | ||||
| :exclamation: File uploads are case insensitive :exclamation: If you upload `A.txt` and `a.txt` with the same root path, only a single file will be saved and available during download. | ||||
| 
 | ||||
| ### Maintaining file permissions and case sensitive files | ||||
| 
 | ||||
| If file permissions and case sensitivity are required, you can `tar` all of your files together before artifact upload. Post download, the `tar` file will maintain file permissions and case sensitivity. | ||||
| 
 | ||||
| ```yaml | ||||
|   - name: 'Tar files' | ||||
|     run: tar -cvf my_files.tar /path/to/my/directory | ||||
| 
 | ||||
|   - name: 'Upload Artifact' | ||||
|     uses: actions/upload-artifact@v2 | ||||
|     with: | ||||
|       name: my-artifact | ||||
|       path: my_files.tar     | ||||
| ``` | ||||
| 
 | ||||
| ## Additional Documentation | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										29
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @ -4992,11 +4992,12 @@ const utils_1 = __webpack_require__(870); | ||||
|  * Used for managing http clients during either upload or download | ||||
|  */ | ||||
| class HttpManager { | ||||
|     constructor(clientCount) { | ||||
|     constructor(clientCount, userAgent) { | ||||
|         if (clientCount < 1) { | ||||
|             throw new Error('There must be at least one client'); | ||||
|         } | ||||
|         this.clients = new Array(clientCount).fill(utils_1.createHttpClient()); | ||||
|         this.userAgent = userAgent; | ||||
|         this.clients = new Array(clientCount).fill(utils_1.createHttpClient(userAgent)); | ||||
|     } | ||||
|     getClient(index) { | ||||
|         return this.clients[index]; | ||||
| @ -5005,7 +5006,7 @@ class HttpManager { | ||||
|     // for more information see: https://github.com/actions/http-client/blob/04e5ad73cd3fd1f5610a32116b0759eddf6570d2/index.ts#L292
 | ||||
|     disposeAndReplaceClient(index) { | ||||
|         this.clients[index].dispose(); | ||||
|         this.clients[index] = utils_1.createHttpClient(); | ||||
|         this.clients[index] = utils_1.createHttpClient(this.userAgent); | ||||
|     } | ||||
|     disposeAndReplaceAllClients() { | ||||
|         for (const [index] of this.clients.entries()) { | ||||
| @ -6288,7 +6289,7 @@ function getMultiPathLCA(searchPaths) { | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|     // Loop over all the search paths until there is a non-common ancestor or we go out of bounds
 | ||||
|     // loop over all the search paths until there is a non-common ancestor or we go out of bounds
 | ||||
|     while (splitIndex < smallestPathLength) { | ||||
|         if (!isPathTheSame()) { | ||||
|             break; | ||||
| @ -6304,6 +6305,11 @@ function findFilesToUpload(searchPath, globOptions) { | ||||
|         const searchResults = []; | ||||
|         const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions()); | ||||
|         const rawSearchResults = yield globber.glob(); | ||||
|         /* | ||||
|           Files are saved with case insensitivity. Uploading both a.txt and A.txt will files to be overwritten | ||||
|           Detect any files that could be overwritten for user awareness | ||||
|         */ | ||||
|         const set = new Set(); | ||||
|         /* | ||||
|           Directories will be rejected if attempted to be uploaded. This includes just empty | ||||
|           directories so filter any directories out from the raw search results | ||||
| @ -6314,6 +6320,13 @@ function findFilesToUpload(searchPath, globOptions) { | ||||
|             if (!fileStats.isDirectory()) { | ||||
|                 core_1.debug(`File:${searchResult} was found using the provided searchPath`); | ||||
|                 searchResults.push(searchResult); | ||||
|                 // detect any files that would be overwritten because of case insensitivity
 | ||||
|                 if (set.has(searchResult.toLowerCase())) { | ||||
|                     core_1.info(`Uploads are case insensitive: ${searchResult} was detected that it will be overwritten by another file with the same path`); | ||||
|                 } | ||||
|                 else { | ||||
|                     set.add(searchResult.toLowerCase()); | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 core_1.debug(`Removing ${searchResult} from rawSearchResults because it is a directory`); | ||||
| @ -6645,7 +6658,7 @@ const upload_gzip_1 = __webpack_require__(647); | ||||
| const stat = util_1.promisify(fs.stat); | ||||
| class UploadHttpClient { | ||||
|     constructor() { | ||||
|         this.uploadHttpManager = new http_manager_1.HttpManager(config_variables_1.getUploadFileConcurrency()); | ||||
|         this.uploadHttpManager = new http_manager_1.HttpManager(config_variables_1.getUploadFileConcurrency(), 'actions/upload-artifact'); | ||||
|         this.statusReporter = new status_reporter_1.StatusReporter(10000); | ||||
|     } | ||||
|     /** | ||||
| @ -7399,7 +7412,7 @@ const http_manager_1 = __webpack_require__(452); | ||||
| const config_variables_1 = __webpack_require__(401); | ||||
| class DownloadHttpClient { | ||||
|     constructor() { | ||||
|         this.downloadHttpManager = new http_manager_1.HttpManager(config_variables_1.getDownloadFileConcurrency()); | ||||
|         this.downloadHttpManager = new http_manager_1.HttpManager(config_variables_1.getDownloadFileConcurrency(), 'actions/download-artifact'); | ||||
|         // downloads are usually significantly faster than uploads so display status information every second
 | ||||
|         this.statusReporter = new status_reporter_1.StatusReporter(1000); | ||||
|     } | ||||
| @ -8034,8 +8047,8 @@ function getUploadHeaders(contentType, isKeepAlive, isGzip, uncompressedLength, | ||||
|     return requestOptions; | ||||
| } | ||||
| exports.getUploadHeaders = getUploadHeaders; | ||||
| function createHttpClient() { | ||||
|     return new http_client_1.HttpClient('actions/artifact', [ | ||||
| function createHttpClient(userAgent) { | ||||
|     return new http_client_1.HttpClient(userAgent, [ | ||||
|         new auth_1.BearerCredentialHandler(config_variables_1.getRuntimeToken()) | ||||
|     ]); | ||||
| } | ||||
|  | ||||
							
								
								
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -5,9 +5,9 @@ | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
|     "@actions/artifact": { | ||||
|       "version": "0.3.2", | ||||
|       "resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-0.3.2.tgz", | ||||
|       "integrity": "sha512-KzUe5DEeVXprAodxfGKtx9f7ukuVKE6V6pge6t5GDGk0cdkfiMEfahoq7HfBsOsmVy4J7rr1YZQPUTvXveYinw==", | ||||
|       "version": "0.3.3", | ||||
|       "resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-0.3.3.tgz", | ||||
|       "integrity": "sha512-sKC1uA5p6064C6Qypmmt6O8iKlpDyMTfqqDlS4/zfJX1Hs8NbbzPLLN81RpewuJPWQNnroeF52w4VCWypbSNaA==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "@actions/core": "^1.2.1", | ||||
|  | ||||
| @ -29,7 +29,7 @@ | ||||
|   }, | ||||
|   "homepage": "https://github.com/actions/upload-artifact#readme", | ||||
|   "devDependencies": { | ||||
|     "@actions/artifact": "^0.3.2", | ||||
|     "@actions/artifact": "^0.3.3", | ||||
|     "@actions/core": "^1.2.3", | ||||
|     "@actions/glob": "^0.1.0", | ||||
|     "@actions/io": "^1.0.2", | ||||
|  | ||||
| @ -66,7 +66,7 @@ function getMultiPathLCA(searchPaths: string[]): string { | ||||
|     return true | ||||
|   } | ||||
| 
 | ||||
|   // Loop over all the search paths until there is a non-common ancestor or we go out of bounds
 | ||||
|   // loop over all the search paths until there is a non-common ancestor or we go out of bounds
 | ||||
|   while (splitIndex < smallestPathLength) { | ||||
|     if (!isPathTheSame()) { | ||||
|       break | ||||
| @ -89,6 +89,12 @@ export async function findFilesToUpload( | ||||
|   ) | ||||
|   const rawSearchResults: string[] = await globber.glob() | ||||
| 
 | ||||
|   /* | ||||
|     Files are saved with case insensitivity. Uploading both a.txt and A.txt will files to be overwritten | ||||
|     Detect any files that could be overwritten for user awareness | ||||
|   */ | ||||
|   const set = new Set<string>() | ||||
| 
 | ||||
|   /* | ||||
|     Directories will be rejected if attempted to be uploaded. This includes just empty | ||||
|     directories so filter any directories out from the raw search results | ||||
| @ -99,6 +105,15 @@ export async function findFilesToUpload( | ||||
|     if (!fileStats.isDirectory()) { | ||||
|       debug(`File:${searchResult} was found using the provided searchPath`) | ||||
|       searchResults.push(searchResult) | ||||
| 
 | ||||
|       // detect any files that would be overwritten because of case insensitivity
 | ||||
|       if (set.has(searchResult.toLowerCase())) { | ||||
|         info( | ||||
|           `Uploads are case insensitive: ${searchResult} was detected that it will be overwritten by another file with the same path` | ||||
|         ) | ||||
|       } else { | ||||
|         set.add(searchResult.toLowerCase()) | ||||
|       } | ||||
|     } else { | ||||
|       debug( | ||||
|         `Removing ${searchResult} from rawSearchResults because it is a directory` | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user