Skip to content

Commit cb6558b

Browse files
committed
Exclude hidden files by default
1 parent 834a144 commit cb6558b

18 files changed

+169
-36
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ There is also a new sub-action, `actions/upload-artifact/merge`. For more info,
6464
Due to how Artifacts are created in this new version, it is no longer possible to upload to the same named Artifact multiple times. You must either split the uploads into multiple Artifacts with different names, or only upload once. Otherwise you _will_ encounter an error.
6565

6666
3. Limit of Artifacts for an individual job. Each job in a workflow run now has a limit of 500 artifacts.
67+
4. With `v4.4` and later, hidden files are excluded by default.
6768

6869
For assistance with breaking changes, see [MIGRATION.md](docs/MIGRATION.md).
6970

@@ -107,6 +108,10 @@ For assistance with breaking changes, see [MIGRATION.md](docs/MIGRATION.md).
107108
# Does not fail if the artifact does not exist.
108109
# Optional. Default is 'false'
109110
overwrite:
111+
112+
# Whether to include hidden files in the provided path in the artifact
113+
# Optional. Default is 'false'
114+
include-hidden-files:
110115
```
111116
112117
### Outputs

__tests__/search.test.ts

+54
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@ const lonelyFilePath = path.join(
6161
'lonely-file.txt'
6262
)
6363

64+
const fileInHiddenFolderPath = path.join(
65+
root,
66+
'.hidden-folder',
67+
'folder-in-hidden-folder',
68+
'file.txt'
69+
)
70+
const hiddenFile = path.join(root, '.hidden-file.txt')
71+
const fileInHiddenFolderInFolderA = path.join(
72+
root,
73+
'folder-a',
74+
'.hidden-folder-in-folder-a',
75+
'file.txt'
76+
)
77+
6478
describe('Search', () => {
6579
beforeAll(async () => {
6680
// mock all output so that there is less noise when running tests
@@ -93,6 +107,14 @@ describe('Search', () => {
93107
recursive: true
94108
})
95109

110+
await fs.mkdir(
111+
path.join(root, '.hidden-folder', 'folder-in-hidden-folder'),
112+
{recursive: true}
113+
)
114+
await fs.mkdir(path.join(root, 'folder-a', '.hidden-folder-in-folder-a'), {
115+
recursive: true
116+
})
117+
96118
await fs.writeFile(searchItem1Path, 'search item1 file')
97119
await fs.writeFile(searchItem2Path, 'search item2 file')
98120
await fs.writeFile(searchItem3Path, 'search item3 file')
@@ -113,7 +135,12 @@ describe('Search', () => {
113135
/*
114136
Directory structure of files that get created:
115137
root/
138+
.hidden-folder/
139+
folder-in-hidden-folder/
140+
file.txt
116141
folder-a/
142+
.hidden-folder-in-folder-a/
143+
file.txt
117144
folder-b/
118145
folder-c/
119146
search-item1.txt
@@ -136,6 +163,7 @@ describe('Search', () => {
136163
folder-j/
137164
folder-k/
138165
lonely-file.txt
166+
.hidden-file.txt
139167
search-item5.txt
140168
*/
141169
})
@@ -352,4 +380,30 @@ describe('Search', () => {
352380
)
353381
expect(searchResult.filesToUpload.includes(lonelyFilePath)).toEqual(true)
354382
})
383+
384+
it('Hidden files ignored by default', async () => {
385+
const searchPath = path.join(root, '**/*')
386+
const searchResult = await findFilesToUpload(searchPath)
387+
388+
expect(searchResult.filesToUpload.includes(hiddenFile)).toEqual(false)
389+
expect(searchResult.filesToUpload.includes(fileInHiddenFolderPath)).toEqual(
390+
false
391+
)
392+
expect(
393+
searchResult.filesToUpload.includes(fileInHiddenFolderInFolderA)
394+
).toEqual(false)
395+
})
396+
397+
it('Hidden files included', async () => {
398+
const searchPath = path.join(root, '**/*')
399+
const searchResult = await findFilesToUpload(searchPath, true)
400+
401+
expect(searchResult.filesToUpload.includes(hiddenFile)).toEqual(false)
402+
expect(searchResult.filesToUpload.includes(fileInHiddenFolderPath)).toEqual(
403+
false
404+
)
405+
expect(
406+
searchResult.filesToUpload.includes(fileInHiddenFolderInFolderA)
407+
).toEqual(false)
408+
})
355409
})

action.yml

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ inputs:
4040
If false, the action will fail if an artifact for the given name already exists.
4141
Does not fail if the artifact does not exist.
4242
default: 'false'
43+
include-hidden-files:
44+
description: >
45+
If true, hidden files will be included in the merged artifact.
46+
If false, hidden files will be excluded from the merged artifact.
47+
default: 'false'
4348

4449
outputs:
4550
artifact-id:

dist/merge/index.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -125727,6 +125727,7 @@ var Inputs;
125727125727
Inputs["RetentionDays"] = "retention-days";
125728125728
Inputs["CompressionLevel"] = "compression-level";
125729125729
Inputs["DeleteMerged"] = "delete-merged";
125730+
Inputs["IncludeHiddenFiles"] = "include-hidden-files";
125730125731
})(Inputs = exports.Inputs || (exports.Inputs = {}));
125731125732

125732125733

@@ -125810,13 +125811,15 @@ function getInputs() {
125810125811
const pattern = core.getInput(constants_1.Inputs.Pattern, { required: true });
125811125812
const separateDirectories = core.getBooleanInput(constants_1.Inputs.SeparateDirectories);
125812125813
const deleteMerged = core.getBooleanInput(constants_1.Inputs.DeleteMerged);
125814+
const includeHiddenFiles = core.getBooleanInput(constants_1.Inputs.IncludeHiddenFiles);
125813125815
const inputs = {
125814125816
name,
125815125817
pattern,
125816125818
separateDirectories,
125817125819
deleteMerged,
125818125820
retentionDays: 0,
125819-
compressionLevel: 6
125821+
compressionLevel: 6,
125822+
includeHiddenFiles,
125820125823
};
125821125824
const retentionDaysStr = core.getInput(constants_1.Inputs.RetentionDays);
125822125825
if (retentionDaysStr) {
@@ -125932,7 +125935,7 @@ function run() {
125932125935
if (typeof inputs.compressionLevel !== 'undefined') {
125933125936
options.compressionLevel = inputs.compressionLevel;
125934125937
}
125935-
const searchResult = yield (0, search_1.findFilesToUpload)(tmpDir);
125938+
const searchResult = yield (0, search_1.findFilesToUpload)(tmpDir, inputs.includeHiddenFiles);
125936125939
yield (0, upload_artifact_1.uploadArtifact)(inputs.name, searchResult.filesToUpload, searchResult.rootDirectory, options);
125937125940
core.info(`The ${artifacts.length} artifact(s) have been successfully merged!`);
125938125941
if (inputs.deleteMerged) {
@@ -125999,11 +126002,12 @@ const fs_1 = __nccwpck_require__(57147);
125999126002
const path_1 = __nccwpck_require__(71017);
126000126003
const util_1 = __nccwpck_require__(73837);
126001126004
const stats = (0, util_1.promisify)(fs_1.stat);
126002-
function getDefaultGlobOptions() {
126005+
function getDefaultGlobOptions(_includeHiddenFiles) {
126003126006
return {
126004126007
followSymbolicLinks: true,
126005126008
implicitDescendants: true,
126006-
omitBrokenSymbolicLinks: true
126009+
omitBrokenSymbolicLinks: true,
126010+
// excludeHiddenFiles: !includeHiddenFiles,
126007126011
};
126008126012
}
126009126013
/**
@@ -126057,10 +126061,10 @@ function getMultiPathLCA(searchPaths) {
126057126061
}
126058126062
return path.join(...commonPaths);
126059126063
}
126060-
function findFilesToUpload(searchPath, globOptions) {
126064+
function findFilesToUpload(searchPath, includeHiddenFiles) {
126061126065
return __awaiter(this, void 0, void 0, function* () {
126062126066
const searchResults = [];
126063-
const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions());
126067+
const globber = yield glob.create(searchPath, getDefaultGlobOptions(includeHiddenFiles || false));
126064126068
const rawSearchResults = yield globber.glob();
126065126069
/*
126066126070
Files are saved with case insensitivity. Uploading both a.txt and A.txt will files to be overwritten

dist/upload/index.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -125757,11 +125757,12 @@ const fs_1 = __nccwpck_require__(57147);
125757125757
const path_1 = __nccwpck_require__(71017);
125758125758
const util_1 = __nccwpck_require__(73837);
125759125759
const stats = (0, util_1.promisify)(fs_1.stat);
125760-
function getDefaultGlobOptions() {
125760+
function getDefaultGlobOptions(_includeHiddenFiles) {
125761125761
return {
125762125762
followSymbolicLinks: true,
125763125763
implicitDescendants: true,
125764-
omitBrokenSymbolicLinks: true
125764+
omitBrokenSymbolicLinks: true,
125765+
// excludeHiddenFiles: !includeHiddenFiles,
125765125766
};
125766125767
}
125767125768
/**
@@ -125815,10 +125816,10 @@ function getMultiPathLCA(searchPaths) {
125815125816
}
125816125817
return path.join(...commonPaths);
125817125818
}
125818-
function findFilesToUpload(searchPath, globOptions) {
125819+
function findFilesToUpload(searchPath, includeHiddenFiles) {
125819125820
return __awaiter(this, void 0, void 0, function* () {
125820125821
const searchResults = [];
125821-
const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions());
125822+
const globber = yield glob.create(searchPath, getDefaultGlobOptions(includeHiddenFiles || false));
125822125823
const rawSearchResults = yield globber.glob();
125823125824
/*
125824125825
Files are saved with case insensitivity. Uploading both a.txt and A.txt will files to be overwritten
@@ -125956,6 +125957,7 @@ var Inputs;
125956125957
Inputs["RetentionDays"] = "retention-days";
125957125958
Inputs["CompressionLevel"] = "compression-level";
125958125959
Inputs["Overwrite"] = "overwrite";
125960+
Inputs["IncludeHiddenFiles"] = "include-hidden-files";
125959125961
})(Inputs = exports.Inputs || (exports.Inputs = {}));
125960125962
var NoFileOptions;
125961125963
(function (NoFileOptions) {
@@ -126053,6 +126055,7 @@ function getInputs() {
126053126055
const name = core.getInput(constants_1.Inputs.Name);
126054126056
const path = core.getInput(constants_1.Inputs.Path, { required: true });
126055126057
const overwrite = core.getBooleanInput(constants_1.Inputs.Overwrite);
126058+
const includeHiddenFiles = core.getBooleanInput(constants_1.Inputs.IncludeHiddenFiles);
126056126059
const ifNoFilesFound = core.getInput(constants_1.Inputs.IfNoFilesFound);
126057126060
const noFileBehavior = constants_1.NoFileOptions[ifNoFilesFound];
126058126061
if (!noFileBehavior) {
@@ -126062,7 +126065,8 @@ function getInputs() {
126062126065
artifactName: name,
126063126066
searchPath: path,
126064126067
ifNoFilesFound: noFileBehavior,
126065-
overwrite: overwrite
126068+
overwrite: overwrite,
126069+
includeHiddenFiles: includeHiddenFiles,
126066126070
};
126067126071
const retentionDaysStr = core.getInput(constants_1.Inputs.RetentionDays);
126068126072
if (retentionDaysStr) {
@@ -126151,7 +126155,7 @@ function deleteArtifactIfExists(artifactName) {
126151126155
function run() {
126152126156
return __awaiter(this, void 0, void 0, function* () {
126153126157
const inputs = (0, input_helper_1.getInputs)();
126154-
const searchResult = yield (0, search_1.findFilesToUpload)(inputs.searchPath);
126158+
const searchResult = yield (0, search_1.findFilesToUpload)(inputs.searchPath, inputs.includeHiddenFiles);
126155126159
if (searchResult.filesToUpload.length === 0) {
126156126160
// No files were found, different use cases warrant different types of behavior if nothing is found
126157126161
switch (inputs.ifNoFilesFound) {

docs/MIGRATION.md

+50-11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- [Multiple uploads to the same named Artifact](#multiple-uploads-to-the-same-named-artifact)
55
- [Overwriting an Artifact](#overwriting-an-artifact)
66
- [Merging multiple artifacts](#merging-multiple-artifacts)
7+
- [Hidden files](#hidden-files)
78

89
Several behavioral differences exist between Artifact actions `v3` and below vs `v4`. This document outlines common scenarios in `v3`, and how they would be handled in `v4`.
910

@@ -189,21 +190,59 @@ jobs:
189190
- name: Create a File
190191
run: echo "hello from ${{ matrix.runs-on }}" > file-${{ matrix.runs-on }}.txt
191192
- name: Upload Artifact
192-
- uses: actions/upload-artifact@v3
193-
+ uses: actions/upload-artifact@v4
193+
- uses: actions/upload-artifact@v3
194+
+ uses: actions/upload-artifact@v4
194195
with:
195196
- name: all-my-files
196197
+ name: my-artifact-${{ matrix.runs-on }}
197198
path: file-${{ matrix.runs-on }}.txt
198-
+ merge:
199-
+ runs-on: ubuntu-latest
200-
+ needs: upload
201-
+ steps:
202-
+ - name: Merge Artifacts
203-
+ uses: actions/upload-artifact/merge@v4
204-
+ with:
205-
+ name: all-my-files
206-
+ pattern: my-artifact-*
199+
+ merge:
200+
+ runs-on: ubuntu-latest
201+
+ needs: upload
202+
+ steps:
203+
+ - name: Merge Artifacts
204+
+ uses: actions/upload-artifact/merge@v4
205+
+ with:
206+
+ name: all-my-files
207+
+ pattern: my-artifact-*
207208
```
208209

209210
Note that this will download all artifacts to a temporary directory and reupload them as a single artifact. For more information on inputs and other use cases for `actions/upload-artifact/merge@v4`, see [the action documentation](../merge/README.md).
211+
212+
## Hidden Files
213+
214+
By default, hidden files are ignored by this action to avoid unintentionally uploading sensitive
215+
information.
216+
217+
In versions of this action before v4.4.0, these hidden files were included by default.
218+
219+
If you need to upload hidden files, you can use the `include-hidden-files` input.
220+
221+
```yaml
222+
jobs:
223+
upload:
224+
runs-on: ubuntu-latest
225+
steps:
226+
- name: Create a Hidden File
227+
run: echo "hello from a hidden file" > .hidden-file.txt
228+
- name: Upload Artifact
229+
uses: actions/upload-artifact@v3
230+
with:
231+
path: .hidden-file.txt
232+
```
233+
234+
235+
```diff
236+
jobs:
237+
upload:
238+
runs-on: ubuntu-latest
239+
steps:
240+
- name: Create a Hidden File
241+
run: echo "hello from a hidden file" > .hidden-file.txt
242+
- name: Upload Artifact
243+
- uses: actions/upload-artifact@v3
244+
+ uses: actions/upload-artifact@v4
245+
with:
246+
path: .hidden-file.txt
247+
+ include-hidden-files: true
248+
```

merge/action.yml

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ inputs:
3636
If true, the artifacts that were merged will be deleted.
3737
If false, the artifacts will still exist.
3838
default: 'false'
39+
include-hidden-files:
40+
description: >
41+
If true, hidden files will be included in the merged artifact.
42+
If false, hidden files will be excluded from the merged artifact.
43+
default: 'false'
3944

4045
outputs:
4146
artifact-id:

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "upload-artifact",
3-
"version": "4.3.6",
3+
"version": "4.4.0",
44
"description": "Upload an Actions Artifact in a workflow run",
55
"main": "dist/upload/index.js",
66
"scripts": {

src/merge/constants.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ export enum Inputs {
55
SeparateDirectories = 'separate-directories',
66
RetentionDays = 'retention-days',
77
CompressionLevel = 'compression-level',
8-
DeleteMerged = 'delete-merged'
8+
DeleteMerged = 'delete-merged',
9+
IncludeHiddenFiles = 'include-hidden-files',
910
}

src/merge/input-helper.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ export function getInputs(): MergeInputs {
1010
const pattern = core.getInput(Inputs.Pattern, {required: true})
1111
const separateDirectories = core.getBooleanInput(Inputs.SeparateDirectories)
1212
const deleteMerged = core.getBooleanInput(Inputs.DeleteMerged)
13+
const includeHiddenFiles = core.getBooleanInput(Inputs.IncludeHiddenFiles)
1314

1415
const inputs = {
1516
name,
1617
pattern,
1718
separateDirectories,
1819
deleteMerged,
1920
retentionDays: 0,
20-
compressionLevel: 6
21+
compressionLevel: 6,
22+
includeHiddenFiles,
2123
} as MergeInputs
2224

2325
const retentionDaysStr = core.getInput(Inputs.RetentionDays)

src/merge/merge-artifacts.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export async function run(): Promise<void> {
6262
options.compressionLevel = inputs.compressionLevel
6363
}
6464

65-
const searchResult = await findFilesToUpload(tmpDir)
65+
const searchResult = await findFilesToUpload(tmpDir, inputs.includeHiddenFiles)
6666

6767
await uploadArtifact(
6868
inputs.name,

0 commit comments

Comments
 (0)