From a2b5414405e87eb45d1bfe265b9b1859dfa434d2 Mon Sep 17 00:00:00 2001 From: Denis Rumyantsev <61472718+DenisRumyantsev@users.noreply.github.com> Date: Fri, 23 Jul 2021 22:39:17 +0300 Subject: [PATCH] Skipping the file if it was not found (the CI is fixed) (#779) A part of the code was taken out from the `find` function to the new `_getStats` function. A new field `skipMissingFiles` was added into the `findOptions` parameter. When it is true, the `find` function skips files that were not found during the `_getStats` function execution. Also, the `mocktests` timeout increased from 15 to 30 seconds. --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 97 +++++++++++++++++++++++++----------------- node/test/mocktests.ts | 6 +-- 4 files changed, 62 insertions(+), 45 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 47935c113..f89f29c4a 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.5", + "version": "3.1.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 1339ac727..282db943a 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.5", + "version": "3.1.6", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index 9646d9302..9a9f9e1f0 100644 --- a/node/task.ts +++ b/node/task.ts @@ -810,6 +810,11 @@ export interface FindOptions { * symbolic link directories. */ followSymbolicLinks: boolean; + + /** + * When true, missing files will not cause an error and will be skipped. + */ + skipMissingFiles?: boolean; } /** @@ -858,6 +863,40 @@ export function retry(func: Function, args: any[], retryOptions: RetryOptions = } } +/** + * Gets info about item stats. + * + * @param path a path to the item to be processed. + * @param followSymbolicLinks indicates whether to traverse descendants of symbolic link directories. + * @param allowBrokenSymbolicLinks when true, broken symbolic link will not cause an error. + * @returns fs.Stats + */ +function _getStats (path: string, followSymbolicLinks: boolean, allowBrokenSymbolicLinks: boolean): fs.Stats { + // stat returns info about the target of a symlink (or symlink chain), + // lstat returns info about a symlink itself + let stats: fs.Stats; + + if (followSymbolicLinks) { + try { + // use stat (following symlinks) + stats = fs.statSync(path); + } catch (err) { + if (err.code == 'ENOENT' && allowBrokenSymbolicLinks) { + // fallback to lstat (broken symlinks allowed) + stats = fs.lstatSync(path); + debug(` ${path} (broken symlink)`); + } else { + throw err; + } + } + } else { + // use lstat (not following symlinks) + stats = fs.lstatSync(path); + } + + return stats; +} + /** * Recursively finds all paths a given path. Returns an array of paths. * @@ -903,49 +942,25 @@ export function find(findPath: string, options?: FindOptions): string[] { while (stack.length) { // pop the next item and push to the result array let item = stack.pop()!; // non-null because `stack.length` was truthy - result.push(item.path); - // stat the item. the stat info is used further below to determine whether to traverse deeper - // - // stat returns info about the target of a symlink (or symlink chain), - // lstat returns info about a symlink itself let stats: fs.Stats; - if (options.followSymbolicLinks) { - try { - // use stat (following all symlinks) - stats = fs.statSync(item.path); - } - catch (err) { - if (err.code == 'ENOENT' && options.allowBrokenSymbolicLinks) { - // fallback to lstat (broken symlinks allowed) - stats = fs.lstatSync(item.path); - debug(` ${item.path} (broken symlink)`); - } - else { - throw err; - } - } - } - else if (options.followSpecifiedSymbolicLink && result.length == 1) { - try { - // use stat (following symlinks for the specified path and this is the specified path) - stats = fs.statSync(item.path); - } - catch (err) { - if (err.code == 'ENOENT' && options.allowBrokenSymbolicLinks) { - // fallback to lstat (broken symlinks allowed) - stats = fs.lstatSync(item.path); - debug(` ${item.path} (broken symlink)`); - } - else { - throw err; - } + try { + // `item.path` equals `findPath` for the first item to be processed, when the `result` array is empty + const isPathToSearch = !result.length; + + // following all symlinks OR following symlinks for the specified path AND this is the specified path + const followSymbolicLinks = options.followSymbolicLinks || options.followSpecifiedSymbolicLink && isPathToSearch; + + // stat the item. The stat info is used further below to determine whether to traverse deeper + stats = _getStats(item.path, followSymbolicLinks, options.allowBrokenSymbolicLinks); + } catch (err) { + if (err.code == 'ENOENT' && options.skipMissingFiles) { + warning(`"${item.path}" seems to be a removed file or directory / broken symlink - so skipping it.`); + continue; } + throw err; } - else { - // use lstat (not following symlinks) - stats = fs.lstatSync(item.path); - } + result.push(item.path); // note, isDirectory() returns false for the lstat of a symlink if (stats.isDirectory()) { @@ -1012,13 +1027,15 @@ function _debugFindOptions(options: FindOptions): void { debug(`findOptions.allowBrokenSymbolicLinks: '${options.allowBrokenSymbolicLinks}'`); debug(`findOptions.followSpecifiedSymbolicLink: '${options.followSpecifiedSymbolicLink}'`); debug(`findOptions.followSymbolicLinks: '${options.followSymbolicLinks}'`); + debug(`findOptions.skipMissingFiles: '${options.skipMissingFiles}'`); } function _getDefaultFindOptions(): FindOptions { return { allowBrokenSymbolicLinks: false, followSpecifiedSymbolicLink: true, - followSymbolicLinks: true + followSymbolicLinks: true, + skipMissingFiles: false }; } diff --git a/node/test/mocktests.ts b/node/test/mocktests.ts index 70a9d7638..a6cef55a1 100644 --- a/node/test/mocktests.ts +++ b/node/test/mocktests.ts @@ -299,7 +299,7 @@ describe('Mock Tests', function () { }) it('MockTest handles node 6 tasks correctly', function (done) { - this.timeout(15000); + this.timeout(30000); const runner = new mtm.MockTestRunner(path.join(__dirname, 'fakeTasks', 'node6task', 'entry.js')); const nodePath = runner.nodePath; assert(nodePath, 'node path should have been correctly set'); @@ -309,7 +309,7 @@ describe('Mock Tests', function () { }) it('MockTest handles node 10 tasks correctly', function (done) { - this.timeout(15000); + this.timeout(30000); const runner = new mtm.MockTestRunner(path.join(__dirname, 'fakeTasks', 'node10task', 'entry.js')); const nodePath = runner.nodePath; assert(nodePath, 'node path should have been correctly set'); @@ -319,7 +319,7 @@ describe('Mock Tests', function () { }) it('MockTest handles node 14 tasks correctly', function (done) { - this.timeout(15000); + this.timeout(30000); const runner = new mtm.MockTestRunner(path.join(__dirname, 'fakeTasks', 'node14task', 'entry.js')); const nodePath = runner.nodePath; assert(nodePath, 'node path should have been correctly set');