vsts-task-lib
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -16,2 +16,3 @@ { | ||
"LIB_InputRequired": "Input required: %s", | ||
"LIB_InvalidPattern": "Invalid pattern: '%s'", | ||
"LIB_EndpointNotExist": "Endpoint not present: %s", | ||
@@ -18,0 +19,0 @@ "LIB_EndpointDataNotExist": "Endpoint data parameter %s not present: %s", |
{ | ||
"name": "vsts-task-lib", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "VSTS Task SDK", | ||
@@ -5,0 +5,0 @@ "main": "./task.js", |
@@ -337,2 +337,13 @@ import Q = require('q'); | ||
/** | ||
* Prefer tl.find() and tl.match() instead. This function is for backward compatibility | ||
* when porting tasks to Node from the PowerShell or PowerShell3 execution handler. | ||
* | ||
* @param rootDirectory path to root unrooted patterns with | ||
* @param pattern include and exclude patterns | ||
* @param includeFiles whether to include files in the result. defaults to true when includeFiles and includeDirectories are both false | ||
* @param includeDirectories whether to include directories in the result | ||
* @returns string[] | ||
*/ | ||
export declare function legacyFindFiles(rootDirectory: string, pattern: string, includeFiles?: boolean, includeDirectories?: boolean): string[]; | ||
/** | ||
* Remove a path recursively with force | ||
@@ -339,0 +350,0 @@ * Returns whether it succeeds |
272
task.js
@@ -905,4 +905,6 @@ "use strict"; | ||
var item = stack.pop(); | ||
debug(" " + item.path); | ||
result.push(item.path); | ||
result.push(item.path); // todo: fix inconsistency on Windows when findPath contains '/'. the findPath | ||
// is returned as part of the result array, and child paths are constructed using | ||
// path.join(). path.join() converts to backslashes, so the first result only will | ||
// have '/' - inconsistent with the rest of the results. | ||
// stat the item. the stat info is used further below to determine whether to traverse deeper | ||
@@ -927,3 +929,3 @@ // | ||
if (stats_2.isDirectory()) { | ||
debug(' is a directory'); | ||
debug(" " + item.path + " (directory)"); | ||
if (options.followSymbolicLinks) { | ||
@@ -951,3 +953,3 @@ // get the realpath | ||
else { | ||
debug(' is a file'); | ||
debug(" " + item.path + " (file)"); | ||
} | ||
@@ -975,2 +977,264 @@ }; | ||
/** | ||
* Prefer tl.find() and tl.match() instead. This function is for backward compatibility | ||
* when porting tasks to Node from the PowerShell or PowerShell3 execution handler. | ||
* | ||
* @param rootDirectory path to root unrooted patterns with | ||
* @param pattern include and exclude patterns | ||
* @param includeFiles whether to include files in the result. defaults to true when includeFiles and includeDirectories are both false | ||
* @param includeDirectories whether to include directories in the result | ||
* @returns string[] | ||
*/ | ||
function legacyFindFiles(rootDirectory, pattern, includeFiles, includeDirectories) { | ||
if (!pattern) { | ||
throw new Error('pattern parameter cannot be empty'); | ||
} | ||
debug("legacyFindFiles rootDirectory: '" + rootDirectory + "'"); | ||
debug("pattern: '" + pattern + "'"); | ||
debug("includeFiles: '" + includeFiles + "'"); | ||
debug("includeDirectories: '" + includeDirectories + "'"); | ||
if (!includeFiles && !includeDirectories) { | ||
includeFiles = true; | ||
} | ||
// organize the patterns into include patterns and exclude patterns | ||
var includePatterns = []; | ||
var excludePatterns = []; | ||
pattern = pattern.replace(/;;/g, '\0'); | ||
for (var _i = 0, _a = pattern.split(';'); _i < _a.length; _i++) { | ||
var pat = _a[_i]; | ||
if (!pat) { | ||
continue; | ||
} | ||
pat = pat.replace(/\0/g, ';'); | ||
// determine whether include pattern and remove any include/exclude prefix. | ||
// include patterns start with +: or anything other than -: | ||
// exclude patterns start with -: | ||
var isIncludePattern = void 0; | ||
if (pat.startsWith('+:')) { | ||
pat = pat.substring(2); | ||
isIncludePattern = true; | ||
} | ||
else if (pat.startsWith('-:')) { | ||
pat = pat.substring(2); | ||
isIncludePattern = false; | ||
} | ||
else { | ||
isIncludePattern = true; | ||
} | ||
// validate pattern does not end with a slash | ||
if (pat.endsWith('/') || (process.platform == 'win32' && pat.endsWith('\\'))) { | ||
throw new Error(loc('LIB_InvalidPattern', pat)); | ||
} | ||
// root the pattern | ||
if (rootDirectory && !path.isAbsolute(pat)) { | ||
pat = path.join(rootDirectory, pat); | ||
// remove trailing slash sometimes added by path.join() on Windows, e.g. | ||
// path.join('\\\\hello', 'world') => '\\\\hello\\world\\' | ||
// path.join('//hello', 'world') => '\\\\hello\\world\\' | ||
if (pat.endsWith('\\')) { | ||
pat = pat.substring(0, pat.length - 1); | ||
} | ||
} | ||
if (isIncludePattern) { | ||
includePatterns.push(pat); | ||
} | ||
else { | ||
excludePatterns.push(legacyFindFiles_convertPatternToRegExp(pat)); | ||
} | ||
} | ||
// find and apply patterns | ||
var count = 0; | ||
var result = legacyFindFiles_getMatchingItems(includePatterns, excludePatterns, !!includeFiles, !!includeDirectories); | ||
debug('all matches:'); | ||
for (var _b = 0, result_1 = result; _b < result_1.length; _b++) { | ||
var resultItem = result_1[_b]; | ||
debug(' ' + resultItem); | ||
} | ||
debug('total matched: ' + result.length); | ||
return result; | ||
} | ||
exports.legacyFindFiles = legacyFindFiles; | ||
function legacyFindFiles_convertPatternToRegExp(pattern) { | ||
pattern = (process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern) // normalize separator on Windows | ||
.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // regex escape - from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript | ||
.replace(/\\\/\\\*\\\*\\\//g, '((\/.+/)|(\/))') // replace directory globstar, e.g. /hello/**/world | ||
.replace(/\\\*\\\*/g, '.*') // replace remaining globstars with a wildcard that can span directory separators, e.g. /hello/**dll | ||
.replace(/\\\*/g, '[^\/]*') // replace asterisks with a wildcard that cannot span directory separators, e.g. /hello/*.dll | ||
.replace(/\\\?/g, '[^\/]'); // replace single character wildcards, e.g. /hello/log?.dll | ||
pattern = "^" + pattern + "$"; | ||
var flags = process.platform == 'win32' ? 'i' : ''; | ||
return new RegExp(pattern, flags); | ||
} | ||
legacyFindFiles['legacyFindFiles_convertPatternToRegExp'] = legacyFindFiles_convertPatternToRegExp; // for unit testing | ||
function legacyFindFiles_getMatchingItems(includePatterns, excludePatterns, includeFiles, includeDirectories) { | ||
debug('getMatchingItems()'); | ||
for (var _i = 0, includePatterns_1 = includePatterns; _i < includePatterns_1.length; _i++) { | ||
var pattern = includePatterns_1[_i]; | ||
debug("includePattern: '" + pattern + "'"); | ||
} | ||
for (var _a = 0, excludePatterns_1 = excludePatterns; _a < excludePatterns_1.length; _a++) { | ||
var pattern = excludePatterns_1[_a]; | ||
debug("excludePattern: " + pattern); | ||
} | ||
debug('includeFiles: ' + includeFiles); | ||
debug('includeDirectories: ' + includeDirectories); | ||
var allFiles = {}; | ||
var _loop_2 = function(pattern) { | ||
// determine the directory to search | ||
// | ||
// note, getDirectoryName removes redundant path separators | ||
var findPath = void 0; | ||
var starIndex = pattern.indexOf('*'); | ||
var questionIndex = pattern.indexOf('?'); | ||
if (starIndex < 0 && questionIndex < 0) { | ||
// if no wildcards are found, use the directory name portion of the path. | ||
// if there is no directory name (file name only in pattern or drive root), | ||
// this will return empty string. | ||
findPath = legacyFindFiles_getDirectoryName(pattern); | ||
} | ||
else { | ||
// extract the directory prior to the first wildcard | ||
var index = Math.min(starIndex >= 0 ? starIndex : questionIndex, questionIndex >= 0 ? questionIndex : starIndex); | ||
findPath = legacyFindFiles_getDirectoryName(pattern.substring(0, index)); | ||
} | ||
// note, due to this short-circuit and the above usage of getDirectoryName, this | ||
// function has the same limitations regarding drive roots as the powershell | ||
// implementation. | ||
// | ||
// also note, since getDirectoryName eliminates slash redundancies, some additional | ||
// work may be required if removal of this limitation is attempted. | ||
if (!findPath) { | ||
return "continue"; | ||
} | ||
var patternRegex = legacyFindFiles_convertPatternToRegExp(pattern); | ||
// todo: remove after inconsistency in find() is fixed. | ||
// workaround for issue with find() on Windows where findPath contains '/'. the findPath | ||
// is returned as part of the result, all child paths are constructed using path.join(), | ||
// which converts to backslashes. | ||
findPath = process.platform == 'win32' ? findPath.replace(/\//g, '\\') : findPath; | ||
// find files/directories | ||
var items = find(findPath, { followSymbolicLinks: true }) | ||
.filter(function (item) { | ||
if (includeFiles && includeDirectories) { | ||
return true; | ||
} | ||
var isDir = fs.statSync(item).isDirectory(); | ||
return (includeFiles && !isDir) || (includeDirectories && isDir); | ||
}) | ||
.forEach(function (item) { | ||
var normalizedPath = process.platform == 'win32' ? item.replace(/\\/g, '/') : item; // normalize separators | ||
// **/times/** will not match C:/fun/times because there isn't a trailing slash | ||
// so try both if including directories | ||
var alternatePath = normalizedPath + "/"; // potential bug: it looks like this will result in a false | ||
// positive if the item is a regular file and not a directory | ||
var isMatch = false; | ||
if (patternRegex.test(normalizedPath) || (includeDirectories && patternRegex.test(alternatePath))) { | ||
isMatch = true; | ||
// test whether the path should be excluded | ||
for (var _i = 0, excludePatterns_2 = excludePatterns; _i < excludePatterns_2.length; _i++) { | ||
var regex = excludePatterns_2[_i]; | ||
if (regex.test(normalizedPath) || (includeDirectories && regex.test(alternatePath))) { | ||
isMatch = false; | ||
break; | ||
} | ||
} | ||
} | ||
if (isMatch) { | ||
allFiles[item] = item; | ||
} | ||
}); | ||
}; | ||
for (var _b = 0, includePatterns_2 = includePatterns; _b < includePatterns_2.length; _b++) { | ||
var pattern = includePatterns_2[_b]; | ||
var state_2 = _loop_2(pattern); | ||
if (state_2 === "continue") continue; | ||
} | ||
return Object.keys(allFiles).sort(); | ||
} | ||
function legacyFindFiles_getDirectoryName(p) { | ||
// legacyFindFiles is a port of the powershell implementation and carries the same | ||
// quirks and limitations searching drive roots | ||
// short-circuit if empty | ||
if (!p) { | ||
return ''; | ||
} | ||
// convert slashes on Windows | ||
p = process.platform == 'win32' ? p.replace(/\\/g, '/') : p; | ||
// remove redundant slashes since Path.GetDirectoryName() does | ||
var isUnc = process.platform == 'win32' && /^\/\/+[^\/]/.test(p); // e.g. //hello | ||
p = (isUnc ? '/' : '') + p.replace(/\/\/+/g, '/'); // preserve leading // for UNC on Windows | ||
// on Windows, the goal of this function is to match the behavior of | ||
// [System.IO.Path]::GetDirectoryName(), e.g. | ||
// C:/ => | ||
// C:/hello => C:\ | ||
// C:/hello/ => C:\hello | ||
// C:/hello/world => C:\hello | ||
// C:/hello/world/ => C:\hello\world | ||
// C: => | ||
// C:hello => C: | ||
// C:hello/ => C:hello | ||
// / => | ||
// /hello => \ | ||
// /hello/ => \hello | ||
// //hello => | ||
// //hello/ => | ||
// //hello/world => | ||
// //hello/world/ => \\hello\world | ||
// | ||
// unfortunately, path.dirname() can't simply be used. for example, on Windows | ||
// it yields different results from Path.GetDirectoryName: | ||
// C:/ => C:/ | ||
// C:/hello => C:/ | ||
// C:/hello/ => C:/ | ||
// C:/hello/world => C:/hello | ||
// C:/hello/world/ => C:/hello | ||
// C: => C: | ||
// C:hello => C: | ||
// C:hello/ => C: | ||
// / => / | ||
// /hello => / | ||
// /hello/ => / | ||
// //hello => / | ||
// //hello/ => / | ||
// //hello/world => //hello/world | ||
// //hello/world/ => //hello/world/ | ||
// //hello/world/again => //hello/world/ | ||
// //hello/world/again/ => //hello/world/ | ||
// //hello/world/again/again => //hello/world/again | ||
// //hello/world/again/again/ => //hello/world/again | ||
if (process.platform == 'win32') { | ||
if (/^[A-Z]:\/?[^\/]+$/i.test(p)) { | ||
return p.charAt(2) == '/' ? p.substring(0, 3) : p.substring(0, 2); | ||
} | ||
else if (/^[A-Z]:\/?$/i.test(p)) { | ||
return ''; | ||
} | ||
var lastSlashIndex = p.lastIndexOf('/'); | ||
if (lastSlashIndex < 0) { | ||
return ''; | ||
} | ||
else if (p == '/') { | ||
return ''; | ||
} | ||
else if (lastSlashIndex == 0) { | ||
return '/'; | ||
} | ||
else if (/^\/\/[^\/]+(\/[^\/]*)?$/.test(p)) { | ||
return ''; | ||
} | ||
return p.substring(0, lastSlashIndex); // e.g. hello/world => hello or hello/world/ => hello/world | ||
} | ||
// OSX/Linux | ||
if (p.indexOf('/') < 0) { | ||
return ''; | ||
} | ||
else if (p == '/') { | ||
return ''; | ||
} | ||
else if (p.endsWith('/')) { | ||
return p.substring(0, p.length - 1); | ||
} | ||
return path.dirname(p); | ||
} | ||
legacyFindFiles['legacyFindFiles_getDirectoryName'] = legacyFindFiles_getDirectoryName; // for unit testing | ||
/** | ||
* Remove a path recursively with force | ||
@@ -977,0 +1241,0 @@ * Returns whether it succeeds |
Sorry, the diff of this file is not supported yet
176490
4003