Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix stats function in the CopyFilesV2 task #16453

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions Tasks/CopyFilesV2/Tests/L0copyAllFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,44 @@ answers.find[path.normalize('/srcDir')] = [
path.normalize('/srcDir/someOtherDir3'),
];
runner.setAnswers(answers);
runner.registerMockExport('stats', (itemPath: string) => {
console.log('##vso[task.debug]stats ' + itemPath);

fs.existsSync = (itemPath: string) => {
switch (itemPath) {
case path.normalize('/srcDir'):
case path.normalize('/srcDir/someOtherDir'):
case path.normalize('/srcDir/someOtherDir/file1.file'):
case path.normalize('/srcDir/someOtherDir/file2.file'):
case path.normalize('/srcDir/someOtherDir2'):
case path.normalize('/srcDir/someOtherDir2/file1.file'):
case path.normalize('/srcDir/someOtherDir2/file2.file'):
case path.normalize('/srcDir/someOtherDir2/file3.file'):
case path.normalize('/srcDir/someOtherDir3'):
return true;
default:
return false;
}
}

fs.statSync = (itemPath: string) => {
const itemStats: fs.Stats = new fs.Stats();
switch (itemPath) {
case path.normalize('/srcDir/someOtherDir'):
case path.normalize('/srcDir/someOtherDir2'):
case path.normalize('/srcDir/someOtherDir3'):
return { isDirectory: () => true };
itemStats.isDirectory = () => true;
break;
case path.normalize('/srcDir/someOtherDir/file1.file'):
case path.normalize('/srcDir/someOtherDir/file2.file'):
case path.normalize('/srcDir/someOtherDir2/file1.file'):
case path.normalize('/srcDir/someOtherDir2/file2.file'):
case path.normalize('/srcDir/someOtherDir2/file3.file'):
return { isDirectory: () => false };
itemStats.isDirectory = () => false;
break;
default:
throw { code: 'ENOENT' };
}
});
return itemStats;
}

// as a precaution, disable fs.chmodSync. it should not be called during this scenario.
fs.chmodSync = null;
Expand Down
57 changes: 33 additions & 24 deletions Tasks/CopyFilesV2/copyfiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { RetryOptions, RetryHelper } from './retrylogichelper';
* @param err error - null if there is no error
*/
function displayTimestampChangeResults(
fileStats: tl.FsStats,
fileStats: fs.Stats,
err: NodeJS.ErrnoException
) {
if (err) {
Expand Down Expand Up @@ -38,22 +38,33 @@ function makeDirP(targetFolder: string, ignoreErrors: boolean): void {
}

/**
* Gets stats for the provided path. Will ignore ENOENT error if ignoreEnoent is true.
* If ignoreEnoent is false ENOENT will be thrown from the function.
* @param path path for which methid will try to get tl.FsStats.
* @param ignoreEnoent ignore ENOENT error during check of path stats.
* @returns
* Gets stats for the provided path.
* Will throw error if entry does not exist and `throwEnoent` is `true`.
* @param path path for which method will try to get `fs.Stats`.
* @param throwEnoent throw error if entry does not exist.
* @returns `fs.Stats` or `null`
*/
function stats(path: string, ignoreEnoent: boolean): tl.FsStats {
try {
return tl.stats(path);
} catch (err) {
if (err.code != 'ENOENT' && ignoreEnoent) {
throw err;
function stats(path: string, throwEnoent: boolean = true): fs.Stats | null {
if (fs.existsSync(path)) {
return fs.statSync(path);
} else {
const message: string = `Entry "${path}" does not exist`;
if (throwEnoent) {
tl.warning(message);
throw new Error(message);
}
tl.debug(message);
return null;
}
}

function filterOutDirectories(paths: string[]): string[] {
return paths.filter((path: string) => {
const itemStats: fs.Stats = stats(path);
return !itemStats.isDirectory();
});
}

async function main(): Promise<void> {
// we allow broken symlinks - since there could be broken symlinks found in source folder, but filtered by contents pattern
const findOptions: tl.FindOptions = {
Expand Down Expand Up @@ -97,7 +108,7 @@ async function main(): Promise<void> {
let allPaths: string[] = tl.find(sourceFolder, findOptions);
let sourceFolderPattern = sourceFolder.replace('[', '[[]'); // directories can have [] in them, and they have special meanings as a pattern, so escape them
let matchedPaths: string[] = tl.match(allPaths, contents, sourceFolderPattern); // default match options
let matchedFiles: string[] = matchedPaths.filter((itemPath: string) => !stats(itemPath, false).isDirectory()); // filter-out directories
let matchedFiles: string[] = filterOutDirectories(matchedPaths);

// copy the files to the target folder
console.log(tl.loc('FoundNFiles', matchedFiles.length));
Expand All @@ -108,9 +119,8 @@ async function main(): Promise<void> {
console.log(tl.loc('CleaningTargetFolder', targetFolder));

// stat the targetFolder path
let targetFolderStats: tl.FsStats;
targetFolderStats = await retryHelper.RunWithRetry<tl.FsStats>(
() => stats(targetFolder, true),
const targetFolderStats: fs.Stats = await retryHelper.RunWithRetry<fs.Stats>(
() => stats(targetFolder),
`stats for ${targetFolder}`
);

Expand Down Expand Up @@ -172,10 +182,10 @@ async function main(): Promise<void> {
}

// stat the target
let targetStats: tl.FsStats;
let targetStats: fs.Stats;
if (!cleanTargetFolder) { // optimization - no need to check if relative target exists when CleanTargetFolder=true
targetStats = await retryHelper.RunWithRetry<tl.FsStats>(
() => stats(targetPath, true),
targetStats = await retryHelper.RunWithRetry<fs.Stats>(
() => stats(targetPath, false),
`Stats for ${targetPath}`
);
}
Expand All @@ -196,9 +206,8 @@ async function main(): Promise<void> {
);
if (preserveTimestamp) {
try {
let fileStats;
fileStats = await retryHelper.RunWithRetry<tl.FsStats>(
() => stats(file, false),
const fileStats: fs.Stats = await retryHelper.RunWithRetry<fs.Stats>(
() => stats(file),
`stats for ${file}`
);
fs.utimes(targetPath, fileStats.atime, fileStats.mtime, (err) => {
Expand Down Expand Up @@ -241,8 +250,8 @@ async function main(): Promise<void> {

if (preserveTimestamp) {
try {
const fileStats = await retryHelper.RunWithRetry<tl.FsStats>(
() => stats(file, false),
const fileStats = await retryHelper.RunWithRetry<fs.Stats>(
() => stats(file),
`stats for ${file}`
);
fs.utimes(targetPath, fileStats.atime, fileStats.mtime, (err) => {
Expand Down
2 changes: 1 addition & 1 deletion Tasks/CopyFilesV2/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"author": "Microsoft Corporation",
"version": {
"Major": 2,
"Minor": 200,
"Minor": 208,
"Patch": 0
},
"releaseNotes": "Match pattern consistency.",
Expand Down
2 changes: 1 addition & 1 deletion Tasks/CopyFilesV2/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"author": "Microsoft Corporation",
"version": {
"Major": 2,
"Minor": 200,
"Minor": 208,
"Patch": 0
},
"releaseNotes": "ms-resource:loc.releaseNotes",
Expand Down