add follow symbolic links options

This commit is contained in:
Pepe Franco 2024-03-15 16:27:05 +11:00
parent ef09cdac3e
commit 264caab452
No known key found for this signature in database
GPG Key ID: E08F34018F21BD9C
10 changed files with 133 additions and 7 deletions

View File

@ -84,6 +84,11 @@ For assistance with breaking changes, see [MIGRATION.md](docs/MIGRATION.md).
# Optional. Default is 'warn'
if-no-files-found:
# If true, symlink directories will be used to search files.
# If false, symlink directories will be ignored to search files.
# Optional. Default is 'true'
followSymbolicLinks:
# Duration after which artifact will expire in days. 0 means using default retention.
# Minimum 1 day.
# Maximum 90 days unless changed from the repository settings page.

View File

@ -61,6 +61,18 @@ const lonelyFilePath = path.join(
'lonely-file.txt'
)
const symbolicLinkExtraSearchItem4Path = path.join(
root,
'folder-l',
'extraSearch-item4.txt'
)
const symbolicLinkExtraSearchItem5Path = path.join(
root,
'folder-l',
'extraSearch-item5.txt'
)
describe('Search', () => {
beforeAll(async () => {
// mock all output so that there is less noise when running tests
@ -110,6 +122,12 @@ describe('Search', () => {
await fs.writeFile(amazingFileInFolderHPath, 'amazing file')
await fs.writeFile(lonelyFilePath, 'all by itself')
await fs.symlink(
path.join(root, 'folder-h', 'folder-i'),
path.join(root, 'folder-l'),
'dir'
)
/*
Directory structure of files that get created:
root/
@ -136,6 +154,7 @@ describe('Search', () => {
folder-j/
folder-k/
lonely-file.txt
folder-l/ (symbolic link to folder-i)
search-item5.txt
*/
})
@ -168,9 +187,36 @@ describe('Search', () => {
)
})
it('Single file search - Symbolic Link', async () => {
const relativePath = path.join(
'__tests__',
'_temp',
'search',
'folder-l',
'extraSearch-item4.txt'
)
const searchResult = await findFilesToUpload(relativePath)
expect(searchResult.filesToUpload.length).toEqual(1)
expect(searchResult.filesToUpload[0]).toEqual(
symbolicLinkExtraSearchItem4Path
)
expect(searchResult.rootDirectory).toEqual(path.join(root, 'folder-l'))
})
it('Single file search - Symbolic Link - Ignore symbolic links', async () => {
const relativePath = path.join('__tests__', '_temp', 'search', 'folder-l')
const searchResult = await findFilesToUpload(relativePath, {
followSymbolicLinks: false
})
expect(searchResult.filesToUpload.length).toEqual(0)
})
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)
@ -224,10 +270,33 @@ describe('Search', () => {
expect(searchResult.rootDirectory).toEqual(expectedRootDirectory)
})
it('Directory search - Absolute Path - Symbolic Link', async () => {
const relativePath = path.join('__tests__', '_temp', 'search', 'folder-l')
const searchResult = await findFilesToUpload(relativePath)
expect(searchResult.filesToUpload.length).toEqual(2)
expect(
searchResult.filesToUpload.includes(symbolicLinkExtraSearchItem4Path)
).toEqual(true)
expect(
searchResult.filesToUpload.includes(symbolicLinkExtraSearchItem5Path)
).toEqual(true)
})
it('Directory search - Absolute Path - Symbolic Link - Ignore symbolic links', async () => {
const relativePath = path.join('__tests__', '_temp', 'search', 'folder-l')
const searchResult = await findFilesToUpload(relativePath, {
followSymbolicLinks: false
})
expect(searchResult.filesToUpload.length).toEqual(0)
})
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.length).toEqual(12)
expect(searchResult.filesToUpload.includes(searchItem1Path)).toEqual(true)
expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true)
@ -249,6 +318,12 @@ describe('Search', () => {
expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual(
true
)
expect(
searchResult.filesToUpload.includes(symbolicLinkExtraSearchItem4Path)
).toEqual(true)
expect(
searchResult.filesToUpload.includes(symbolicLinkExtraSearchItem5Path)
).toEqual(true)
expect(searchResult.rootDirectory).toEqual(root)
})
@ -261,7 +336,7 @@ describe('Search', () => {
'**/*[Ss]earch*'
)
const searchResult = await findFilesToUpload(searchPath)
expect(searchResult.filesToUpload.length).toEqual(10)
expect(searchResult.filesToUpload.length).toEqual(12)
expect(searchResult.filesToUpload.includes(searchItem1Path)).toEqual(true)
expect(searchResult.filesToUpload.includes(searchItem2Path)).toEqual(true)
@ -283,6 +358,12 @@ describe('Search', () => {
expect(searchResult.filesToUpload.includes(extraSearchItem5Path)).toEqual(
true
)
expect(
searchResult.filesToUpload.includes(symbolicLinkExtraSearchItem4Path)
).toEqual(true)
expect(
searchResult.filesToUpload.includes(symbolicLinkExtraSearchItem5Path)
).toEqual(true)
expect(searchResult.rootDirectory).toEqual(root)
})

View File

@ -17,6 +17,11 @@ inputs:
error: Fail the action with an error message
ignore: Do not output any warnings or errors, the action does not fail
default: 'warn'
follow-symbolic-links:
description: >
If true, symlink directories will be used to search files.
If false, symlink directories will be ignored to search files.
default: 'true'
retention-days:
description: >
Duration after which artifact will expire in days. 0 means using default retention.

7
dist/merge/index.js vendored
View File

@ -129675,7 +129675,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.findFilesToUpload = void 0;
exports.findFilesToUpload = exports.getGlobOptions = void 0;
const glob = __importStar(__nccwpck_require__(28090));
const path = __importStar(__nccwpck_require__(71017));
const core_1 = __nccwpck_require__(42186);
@ -129690,6 +129690,10 @@ function getDefaultGlobOptions() {
omitBrokenSymbolicLinks: true
};
}
function getGlobOptions(followSymbolicLinks) {
return Object.assign({ followSymbolicLinks }, getDefaultGlobOptions());
}
exports.getGlobOptions = getGlobOptions;
/**
* If multiple paths are specific, the least common ancestor (LCA) of the search paths is used as
* the delimiter to control the directory structure for the artifact. This function returns the LCA
@ -129746,6 +129750,7 @@ function findFilesToUpload(searchPath, globOptions) {
const searchResults = [];
const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions());
const rawSearchResults = yield globber.glob();
console.log(rawSearchResults);
/*
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

12
dist/upload/index.js vendored
View File

@ -129433,7 +129433,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.findFilesToUpload = void 0;
exports.findFilesToUpload = exports.getGlobOptions = void 0;
const glob = __importStar(__nccwpck_require__(28090));
const path = __importStar(__nccwpck_require__(71017));
const core_1 = __nccwpck_require__(42186);
@ -129448,6 +129448,10 @@ function getDefaultGlobOptions() {
omitBrokenSymbolicLinks: true
};
}
function getGlobOptions(followSymbolicLinks) {
return Object.assign({ followSymbolicLinks }, getDefaultGlobOptions());
}
exports.getGlobOptions = getGlobOptions;
/**
* If multiple paths are specific, the least common ancestor (LCA) of the search paths is used as
* the delimiter to control the directory structure for the artifact. This function returns the LCA
@ -129504,6 +129508,7 @@ function findFilesToUpload(searchPath, globOptions) {
const searchResults = [];
const globber = yield glob.create(searchPath, globOptions || getDefaultGlobOptions());
const rawSearchResults = yield globber.glob();
console.log(rawSearchResults);
/*
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
@ -129637,6 +129642,7 @@ var Inputs;
Inputs["Name"] = "name";
Inputs["Path"] = "path";
Inputs["IfNoFilesFound"] = "if-no-files-found";
Inputs["FollowSymbolicLinks"] = "follow-symbolic-links";
Inputs["RetentionDays"] = "retention-days";
Inputs["CompressionLevel"] = "compression-level";
Inputs["Overwrite"] = "overwrite";
@ -129739,6 +129745,7 @@ function getInputs() {
const overwrite = core.getBooleanInput(constants_1.Inputs.Overwrite);
const ifNoFilesFound = core.getInput(constants_1.Inputs.IfNoFilesFound);
const noFileBehavior = constants_1.NoFileOptions[ifNoFilesFound];
const followSymbolicLinks = core.getBooleanInput(constants_1.Inputs.FollowSymbolicLinks);
if (!noFileBehavior) {
core.setFailed(`Unrecognized ${constants_1.Inputs.IfNoFilesFound} input. Provided: ${ifNoFilesFound}. Available options: ${Object.keys(constants_1.NoFileOptions)}`);
}
@ -129746,6 +129753,7 @@ function getInputs() {
artifactName: name,
searchPath: path,
ifNoFilesFound: noFileBehavior,
followSymbolicLinks: followSymbolicLinks,
overwrite: overwrite
};
const retentionDaysStr = core.getInput(constants_1.Inputs.RetentionDays);
@ -129835,7 +129843,7 @@ function deleteArtifactIfExists(artifactName) {
function run() {
return __awaiter(this, void 0, void 0, function* () {
const inputs = (0, input_helper_1.getInputs)();
const searchResult = yield (0, search_1.findFilesToUpload)(inputs.searchPath);
const searchResult = yield (0, search_1.findFilesToUpload)(inputs.searchPath, (0, search_1.getGlobOptions)(inputs.followSymbolicLinks));
if (searchResult.filesToUpload.length === 0) {
// No files were found, different use cases warrant different types of behavior if nothing is found
switch (inputs.ifNoFilesFound) {

View File

@ -19,6 +19,15 @@ function getDefaultGlobOptions(): glob.GlobOptions {
}
}
export function getGlobOptions(
followSymbolicLinks?: boolean
): glob.GlobOptions {
return {
followSymbolicLinks,
...getDefaultGlobOptions()
}
}
/**
* If multiple paths are specific, the least common ancestor (LCA) of the search paths is used as
* the delimiter to control the directory structure for the artifact. This function returns the LCA
@ -88,6 +97,7 @@ export async function findFilesToUpload(
globOptions || getDefaultGlobOptions()
)
const rawSearchResults: string[] = await globber.glob()
console.log(rawSearchResults)
/*
Files are saved with case insensitivity. Uploading both a.txt and A.txt will files to be overwritten

View File

@ -3,6 +3,7 @@ export enum Inputs {
Name = 'name',
Path = 'path',
IfNoFilesFound = 'if-no-files-found',
FollowSymbolicLinks = 'follow-symbolic-links',
RetentionDays = 'retention-days',
CompressionLevel = 'compression-level',
Overwrite = 'overwrite'

View File

@ -13,6 +13,8 @@ export function getInputs(): UploadInputs {
const ifNoFilesFound = core.getInput(Inputs.IfNoFilesFound)
const noFileBehavior: NoFileOptions = NoFileOptions[ifNoFilesFound]
const followSymbolicLinks = core.getBooleanInput(Inputs.FollowSymbolicLinks)
if (!noFileBehavior) {
core.setFailed(
`Unrecognized ${
@ -27,6 +29,7 @@ export function getInputs(): UploadInputs {
artifactName: name,
searchPath: path,
ifNoFilesFound: noFileBehavior,
followSymbolicLinks: followSymbolicLinks,
overwrite: overwrite
} as UploadInputs

View File

@ -3,7 +3,7 @@ import artifact, {
UploadArtifactOptions,
ArtifactNotFoundError
} from '@actions/artifact'
import {findFilesToUpload} from '../shared/search'
import {findFilesToUpload, getGlobOptions} from '../shared/search'
import {getInputs} from './input-helper'
import {NoFileOptions} from './constants'
import {uploadArtifact} from '../shared/upload-artifact'
@ -24,7 +24,10 @@ async function deleteArtifactIfExists(artifactName: string): Promise<void> {
export async function run(): Promise<void> {
const inputs = getInputs()
const searchResult = await findFilesToUpload(inputs.searchPath)
const searchResult = await findFilesToUpload(
inputs.searchPath,
getGlobOptions(inputs.followSymbolicLinks)
)
if (searchResult.filesToUpload.length === 0) {
// No files were found, different use cases warrant different types of behavior if nothing is found
switch (inputs.ifNoFilesFound) {

View File

@ -16,6 +16,11 @@ export interface UploadInputs {
*/
ifNoFilesFound: NoFileOptions
/**
* Wether or not to follow symbolic links when searching for files to upload
*/
followSymbolicLinks?: boolean
/**
* Duration after which artifact will expire in days
*/