mirror of
https://github.com/actions/upload-artifact.git
synced 2024-11-26 22:19:02 +00:00
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
25
README.md
25
README.md
@ -205,6 +205,31 @@ In the top right corner of a workflow run, once the run is over, if you used thi
|
|||||||
|
|
||||||
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
|
## 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.
|
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.
|
||||||
|
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
|
* Used for managing http clients during either upload or download
|
||||||
*/
|
*/
|
||||||
class HttpManager {
|
class HttpManager {
|
||||||
constructor(clientCount) {
|
constructor(clientCount, userAgent) {
|
||||||
if (clientCount < 1) {
|
if (clientCount < 1) {
|
||||||
throw new Error('There must be at least one client');
|
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) {
|
getClient(index) {
|
||||||
return this.clients[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
|
// for more information see: https://github.com/actions/http-client/blob/04e5ad73cd3fd1f5610a32116b0759eddf6570d2/index.ts#L292
|
||||||
disposeAndReplaceClient(index) {
|
disposeAndReplaceClient(index) {
|
||||||
this.clients[index].dispose();
|
this.clients[index].dispose();
|
||||||
this.clients[index] = utils_1.createHttpClient();
|
this.clients[index] = utils_1.createHttpClient(this.userAgent);
|
||||||
}
|
}
|
||||||
disposeAndReplaceAllClients() {
|
disposeAndReplaceAllClients() {
|
||||||
for (const [index] of this.clients.entries()) {
|
for (const [index] of this.clients.entries()) {
|
||||||
@ -6288,7 +6289,7 @@ function getMultiPathLCA(searchPaths) {
|
|||||||
}
|
}
|
||||||
return true;
|
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) {
|
while (splitIndex < smallestPathLength) {
|
||||||
if (!isPathTheSame()) {
|
if (!isPathTheSame()) {
|
||||||
break;
|
break;
|
||||||
@ -6304,6 +6305,11 @@ function findFilesToUpload(searchPath, globOptions) {
|
|||||||
const searchResults = [];
|
const searchResults = [];
|
||||||
const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions());
|
const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions());
|
||||||
const rawSearchResults = yield globber.glob();
|
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 will be rejected if attempted to be uploaded. This includes just empty
|
||||||
directories so filter any directories out from the raw search results
|
directories so filter any directories out from the raw search results
|
||||||
@ -6314,6 +6320,13 @@ function findFilesToUpload(searchPath, globOptions) {
|
|||||||
if (!fileStats.isDirectory()) {
|
if (!fileStats.isDirectory()) {
|
||||||
core_1.debug(`File:${searchResult} was found using the provided searchPath`);
|
core_1.debug(`File:${searchResult} was found using the provided searchPath`);
|
||||||
searchResults.push(searchResult);
|
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 {
|
else {
|
||||||
core_1.debug(`Removing ${searchResult} from rawSearchResults because it is a directory`);
|
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);
|
const stat = util_1.promisify(fs.stat);
|
||||||
class UploadHttpClient {
|
class UploadHttpClient {
|
||||||
constructor() {
|
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);
|
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);
|
const config_variables_1 = __webpack_require__(401);
|
||||||
class DownloadHttpClient {
|
class DownloadHttpClient {
|
||||||
constructor() {
|
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
|
// downloads are usually significantly faster than uploads so display status information every second
|
||||||
this.statusReporter = new status_reporter_1.StatusReporter(1000);
|
this.statusReporter = new status_reporter_1.StatusReporter(1000);
|
||||||
}
|
}
|
||||||
@ -8034,8 +8047,8 @@ function getUploadHeaders(contentType, isKeepAlive, isGzip, uncompressedLength,
|
|||||||
return requestOptions;
|
return requestOptions;
|
||||||
}
|
}
|
||||||
exports.getUploadHeaders = getUploadHeaders;
|
exports.getUploadHeaders = getUploadHeaders;
|
||||||
function createHttpClient() {
|
function createHttpClient(userAgent) {
|
||||||
return new http_client_1.HttpClient('actions/artifact', [
|
return new http_client_1.HttpClient(userAgent, [
|
||||||
new auth_1.BearerCredentialHandler(config_variables_1.getRuntimeToken())
|
new auth_1.BearerCredentialHandler(config_variables_1.getRuntimeToken())
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -5,9 +5,9 @@
|
|||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/artifact": {
|
"@actions/artifact": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-0.3.3.tgz",
|
||||||
"integrity": "sha512-KzUe5DEeVXprAodxfGKtx9f7ukuVKE6V6pge6t5GDGk0cdkfiMEfahoq7HfBsOsmVy4J7rr1YZQPUTvXveYinw==",
|
"integrity": "sha512-sKC1uA5p6064C6Qypmmt6O8iKlpDyMTfqqDlS4/zfJX1Hs8NbbzPLLN81RpewuJPWQNnroeF52w4VCWypbSNaA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@actions/core": "^1.2.1",
|
"@actions/core": "^1.2.1",
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/actions/upload-artifact#readme",
|
"homepage": "https://github.com/actions/upload-artifact#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/artifact": "^0.3.2",
|
"@actions/artifact": "^0.3.3",
|
||||||
"@actions/core": "^1.2.3",
|
"@actions/core": "^1.2.3",
|
||||||
"@actions/glob": "^0.1.0",
|
"@actions/glob": "^0.1.0",
|
||||||
"@actions/io": "^1.0.2",
|
"@actions/io": "^1.0.2",
|
||||||
|
@ -66,7 +66,7 @@ function getMultiPathLCA(searchPaths: string[]): string {
|
|||||||
return true
|
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) {
|
while (splitIndex < smallestPathLength) {
|
||||||
if (!isPathTheSame()) {
|
if (!isPathTheSame()) {
|
||||||
break
|
break
|
||||||
@ -89,6 +89,12 @@ export async function findFilesToUpload(
|
|||||||
)
|
)
|
||||||
const rawSearchResults: string[] = await globber.glob()
|
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 will be rejected if attempted to be uploaded. This includes just empty
|
||||||
directories so filter any directories out from the raw search results
|
directories so filter any directories out from the raw search results
|
||||||
@ -99,6 +105,15 @@ export async function findFilesToUpload(
|
|||||||
if (!fileStats.isDirectory()) {
|
if (!fileStats.isDirectory()) {
|
||||||
debug(`File:${searchResult} was found using the provided searchPath`)
|
debug(`File:${searchResult} was found using the provided searchPath`)
|
||||||
searchResults.push(searchResult)
|
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 {
|
} else {
|
||||||
debug(
|
debug(
|
||||||
`Removing ${searchResult} from rawSearchResults because it is a directory`
|
`Removing ${searchResult} from rawSearchResults because it is a directory`
|
||||||
|
Loading…
Reference in New Issue
Block a user