Support for multi path upload
This commit is contained in:
parent
4347a0d55a
commit
5db5708164
30
.github/workflows/test.yml
vendored
30
.github/workflows/test.yml
vendored
@ -75,6 +75,16 @@ jobs:
|
||||
name: 'GZip-Artifact'
|
||||
path: path/to/dir-3/
|
||||
|
||||
# Upload a directory that contains a file that will be uploaded with GZip
|
||||
- name: 'Upload artifact #4'
|
||||
uses: ./
|
||||
with:
|
||||
name: 'Multi-Path-Artifact'
|
||||
path: |
|
||||
path/to/dir-1/*
|
||||
path/to/dir-[23]/*
|
||||
!path/to/dir-3/*.txt
|
||||
|
||||
# Verify artifacts. Switch to download-artifact@v2 once it's out of preview
|
||||
|
||||
# Download Artifact #1 and verify the correctness of the content
|
||||
@ -138,3 +148,23 @@ jobs:
|
||||
Write-Error "File contents of downloaded artifact is incorrect"
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
- name: 'Download artifact #4'
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: 'Multi-Path-Artifact'
|
||||
path: multi/artifact/path
|
||||
|
||||
- name: 'Verify Artifact #4'
|
||||
run: |
|
||||
$file1 = "multi/artifact/path/to/dir-1/file1.txt"
|
||||
$file2 = "multi/artifact/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
|
||||
|
@ -286,4 +286,70 @@ describe('Search', () => {
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(root)
|
||||
})
|
||||
|
||||
it('Multi path search - root directory', async () => {
|
||||
const searchPath1 = path.join(root, 'folder-a')
|
||||
const searchPath2 = path.join(root, 'folder-d')
|
||||
|
||||
const searchPaths = searchPath1 + '\n' + searchPath2
|
||||
const searchResult = await findFilesToUpload(searchPaths)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(root)
|
||||
expect(searchResult.filesToUpload.length).toEqual(7)
|
||||
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(extraSearchItem1Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraSearchItem2Path)).toEqual(
|
||||
true
|
||||
)
|
||||
expect(searchResult.filesToUpload.includes(extraFileInFolderCPath)).toEqual(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('Multi path search - with exclude character', async () => {
|
||||
const searchPath1 = path.join(root, 'folder-a')
|
||||
const searchPath2 = path.join(root, 'folder-d')
|
||||
const searchPath3 = path.join(root, 'folder-a', 'folder-b', '**/extra*.txt')
|
||||
|
||||
// negating the third search path
|
||||
const searchPaths = searchPath1 + '\n' + searchPath2 + '\n!' + searchPath3
|
||||
const searchResult = await findFilesToUpload(searchPaths)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(root)
|
||||
expect(searchResult.filesToUpload.length).toEqual(5)
|
||||
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(extraSearchItem2Path)).toEqual(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('Multi path search - non root directory', async () => {
|
||||
const searchPath1 = path.join(root, 'folder-h', 'folder-i')
|
||||
const searchPath2 = path.join(root, 'folder-h', 'folder-j', 'folder-k')
|
||||
const searchPath3 = amazingFileInFolderHPath
|
||||
|
||||
const searchPaths = searchPath1 + '\n' + searchPath2 + '\n' + searchPath3
|
||||
const searchResult = await findFilesToUpload(searchPaths)
|
||||
|
||||
expect(searchResult.rootDirectory).toEqual(path.join(root, 'folder-h'))
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
65
dist/index.js
vendored
65
dist/index.js
vendored
@ -6221,6 +6221,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const glob = __importStar(__webpack_require__(281));
|
||||
const path = __importStar(__webpack_require__(622));
|
||||
const core_1 = __webpack_require__(470);
|
||||
const fs_1 = __webpack_require__(747);
|
||||
const path_1 = __webpack_require__(622);
|
||||
@ -6231,6 +6232,57 @@ function getDefaultGlobOptions() {
|
||||
omitBrokenSymbolicLinks: true
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
* when given an array of search paths
|
||||
*
|
||||
* Example 1: The patterns `/foo/` and `/bar/` returns `/`
|
||||
*
|
||||
* Example 2: The patterns `~/foo/bar/*` and `~/foo/voo/two/*` and `~/foo/mo/` returns `~/foo`
|
||||
*/
|
||||
function getMultiPathLCA(searchPaths) {
|
||||
if (searchPaths.length < 2) {
|
||||
throw new Error('At least two search paths must be provided');
|
||||
}
|
||||
const commonPaths = new Array();
|
||||
const splitPaths = new Array();
|
||||
let smallestPathLength = Number.MAX_SAFE_INTEGER;
|
||||
// split each of the search paths using the platform specific separator
|
||||
for (const searchPath of searchPaths) {
|
||||
core_1.debug(`Using search path ${searchPath}`);
|
||||
const splitSearchPath = searchPath.split(path.sep);
|
||||
// keep track of the smallest path length so that we don't accidentally later go out of bounds
|
||||
smallestPathLength = Math.min(smallestPathLength, splitSearchPath.length);
|
||||
splitPaths.push(splitSearchPath);
|
||||
}
|
||||
// on Unix-like file systems, the file separator exists at the beginning of the file path, make sure to preserve it
|
||||
if (searchPaths[0].startsWith(path.sep)) {
|
||||
commonPaths.push(path.sep);
|
||||
}
|
||||
let splitIndex = 0;
|
||||
// function to check if the paths are the same at a specific index
|
||||
function isPathTheSame() {
|
||||
const common = splitPaths[0][splitIndex];
|
||||
for (let i = 1; i < splitPaths.length; i++) {
|
||||
if (common !== splitPaths[i][splitIndex]) {
|
||||
// a non-common index has been reached
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// if all are the same, add to the end result & increment the index
|
||||
commonPaths.push(common);
|
||||
splitIndex++;
|
||||
return true;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
return path.join(...commonPaths);
|
||||
}
|
||||
function findFilesToUpload(searchPath, globOptions) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const searchResults = [];
|
||||
@ -6249,13 +6301,16 @@ function findFilesToUpload(searchPath, globOptions) {
|
||||
core_1.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
|
||||
*/
|
||||
// Calculate the root directory for the artifact using the search paths that were utilized
|
||||
const searchPaths = globber.getSearchPaths();
|
||||
if (searchPaths.length > 1) {
|
||||
throw new Error('Only 1 search path should be returned');
|
||||
core_1.info(`Multiple search paths detected. Calculating the least common ancestor of all paths`);
|
||||
const lcaSearchPath = getMultiPathLCA(searchPaths);
|
||||
core_1.info(`The least common ancestor is ${lcaSearchPath} This will be the root directory of the artifact`);
|
||||
return {
|
||||
filesToUpload: searchResults,
|
||||
rootDirectory: lcaSearchPath
|
||||
};
|
||||
}
|
||||
/*
|
||||
Special case for a single file artifact that is uploaded without a directory or wildcard pattern. The directory structure is
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as glob from '@actions/glob'
|
||||
import {debug} from '@actions/core'
|
||||
import * as path from 'path'
|
||||
import {debug, info} from '@actions/core'
|
||||
import {lstatSync} from 'fs'
|
||||
import {dirname} from 'path'
|
||||
|
||||
@ -16,6 +17,65 @@ function getDefaultGlobOptions(): glob.GlobOptions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* when given an array of search paths
|
||||
*
|
||||
* Example 1: The patterns `/foo/` and `/bar/` returns `/`
|
||||
*
|
||||
* Example 2: The patterns `~/foo/bar/*` and `~/foo/voo/two/*` and `~/foo/mo/` returns `~/foo`
|
||||
*/
|
||||
function getMultiPathLCA(searchPaths: string[]): string {
|
||||
if (searchPaths.length < 2) {
|
||||
throw new Error('At least two search paths must be provided')
|
||||
}
|
||||
|
||||
const commonPaths = new Array<string>()
|
||||
const splitPaths = new Array<string[]>()
|
||||
let smallestPathLength = Number.MAX_SAFE_INTEGER
|
||||
|
||||
// split each of the search paths using the platform specific separator
|
||||
for (const searchPath of searchPaths) {
|
||||
debug(`Using search path ${searchPath}`)
|
||||
const splitSearchPath = searchPath.split(path.sep)
|
||||
|
||||
// keep track of the smallest path length so that we don't accidentally later go out of bounds
|
||||
smallestPathLength = Math.min(smallestPathLength, splitSearchPath.length)
|
||||
splitPaths.push(splitSearchPath)
|
||||
}
|
||||
|
||||
// on Unix-like file systems, the file separator exists at the beginning of the file path, make sure to preserve it
|
||||
if (searchPaths[0].startsWith(path.sep)) {
|
||||
commonPaths.push(path.sep)
|
||||
}
|
||||
|
||||
let splitIndex = 0
|
||||
// function to check if the paths are the same at a specific index
|
||||
function isPathTheSame(): boolean {
|
||||
const common = splitPaths[0][splitIndex]
|
||||
for (let i = 1; i < splitPaths.length; i++) {
|
||||
if (common !== splitPaths[i][splitIndex]) {
|
||||
// a non-common index has been reached
|
||||
return false
|
||||
}
|
||||
}
|
||||
// if all are the same, add to the end result & increment the index
|
||||
commonPaths.push(common)
|
||||
splitIndex++
|
||||
return true
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
return path.join(...commonPaths)
|
||||
}
|
||||
|
||||
export async function findFilesToUpload(
|
||||
searchPath: string,
|
||||
globOptions?: glob.GlobOptions
|
||||
@ -42,13 +102,22 @@ export async function findFilesToUpload(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
// Calculate the root directory for the artifact using the search paths that were utilized
|
||||
const searchPaths: string[] = globber.getSearchPaths()
|
||||
|
||||
if (searchPaths.length > 1) {
|
||||
throw new Error('Only 1 search path should be returned')
|
||||
info(
|
||||
`Multiple search paths detected. Calculating the least common ancestor of all paths`
|
||||
)
|
||||
const lcaSearchPath = getMultiPathLCA(searchPaths)
|
||||
info(
|
||||
`The least common ancestor is ${lcaSearchPath} This will be the root directory of the artifact`
|
||||
)
|
||||
|
||||
return {
|
||||
filesToUpload: searchResults,
|
||||
rootDirectory: lcaSearchPath
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user