From 47ed8390b118b05143a3d58118eb33a274a4acfd Mon Sep 17 00:00:00 2001 From: Ting Date: Tue, 29 Aug 2017 10:39:35 -0400 Subject: [PATCH 001/259] certificate support (#281) --- node/README.md | 2 +- node/docs/cert.md | 92 ++++++++++++++++++++ node/docs/proxy.md | 5 +- node/internal.ts | 71 ++++++++++++++- node/task.ts | 45 ++++++++++ node/vault.ts | 2 +- powershell/Docs/README.md | 1 + powershell/VstsTaskSdk/ServerOMFunctions.ps1 | 66 ++++++++++++-- powershell/VstsTaskSdk/VstsTaskSdk.psm1 | 2 + 9 files changed, 272 insertions(+), 14 deletions(-) create mode 100644 node/docs/cert.md diff --git a/node/README.md b/node/README.md index d00755f46..f8c111169 100644 --- a/node/README.md +++ b/node/README.md @@ -17,7 +17,7 @@ Step by Step: [Create Task](docs/stepbystep.md) Documentation: [Typescript API](docs/vsts-task-lib.md) -Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/minagent.md), [Proxy](docs/proxy.md) +Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/minagent.md), [Proxy](docs/proxy.md), [Certificate](docs/cert.md) ## Reference Examples diff --git a/node/docs/cert.md b/node/docs/cert.md new file mode 100644 index 000000000..f270ae012 --- /dev/null +++ b/node/docs/cert.md @@ -0,0 +1,92 @@ +### Get certificate configuration by using [VSTS-Task-Lib](https://github.com/Microsoft/vsts-task-lib) method (Min Agent Version 2.122.0) + +#### Node.js Lib + +Method for retrieve certificate settings in node.js lib +``` typescript +export function getHttpCertConfiguration(): CertConfiguration { +} +``` +`CertConfiguration` has following fields +```typescript +export interface CertConfiguration { + caFile?: string; + certFile?: string; + keyFile?: string; + certArchiveFile?: string; + passphrase?: string; + } +``` + +In the following example, we will retrieve certificate configuration information and use VSTS-Node-Api to make a Rest Api call back to VSTS/TFS service, the Rest call will use the certificates you configured in agent. +```typescript +// MyCertExampleTask.ts +import tl = require('vsts-task-lib/task'); +import api = require('vso-node-api'); +import VsoBaseInterfaces = require('vso-node-api/interfaces/common/VsoBaseInterfaces'); + +async function run() { + + // get cert config + let cert = tl.getHttpCertConfiguration(); + + // TFS server url + let serverUrl = "https://mycompanytfs.com/tfs"; + + // Personal access token + let token = "g6zzur6bfypfwuqdxxupv3y3qfcoudlgh26bjz77t3mgylzmvjiq"; + let authHandler = api.getPersonalAccessTokenHandler(token); + + // Options for VSTS-Node-Api, + // this is not required if you want to send http request to the same TFS + // instance the agent currently connect to. + // VSTS-Node-Api will pick up certificate setting from VSTS-Task-Lib automatically + let option: VsoBaseInterfaces.IRequestOptions = { + cert: { + caFile: "C:\\ca.pem", + certFile: "C:\\client-cert.pem", + keyFile: "C:\\client-cert-key.pem", + passphrase: "test123", + } + }; + + // Make a Rest call to VSTS/TFS + let vsts: api.WebApi = new api.WebApi(serverUrl, authHandler, option); + let connData: lim.ConnectionData = await vsts.connect(); + console.log('Hello ' + connData.authenticatedUser.providerDisplayName); + + // You should only use the retrieved certificate config to call the TFS instance your agent current connect to or any resource within your cooperation that accept those certificates. +} + +run(); +``` + +#### PowerShell Lib + +On Windows the CA certificate needs to be installed into the `Trusted CA Store` of `Windows Certificate manager` first. +So the PowerShell lib will only expose the client certificate information + +Method for retrieve client certificate settings in PowerShell lib +``` powershell +function Get-ClientCertificate { + [CmdletBinding()] + param() + + # Return a new X509Certificate2 object to the client certificate + return New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 +} +``` + +In the following example, we will retrieve client certificate configuration information and print it out first, then we will use PowerShell lib method to get `VssHttpClient` and make a Rest Api call back to TFS service's `Project` endpoint and retrieve all team projects. The Rest call will use the client certificate you configured in agent. + +```powershell +# retrieve cert config +$cert = Get-VstsClientCertificate +Write-Host $cert + +# get project http client (the client will have proxy hook up by default) +$projectHttpClient = Get-VstsVssHttpClient -TypeName Microsoft.TeamFoundation.Core.WebApi.ProjectHttpClient -OMDirectory "" + +# print out all team projects +$projectHttpClient.GetProjects().Result +``` \ No newline at end of file diff --git a/node/docs/proxy.md b/node/docs/proxy.md index 46b56cc97..d5413dd1b 100644 --- a/node/docs/proxy.md +++ b/node/docs/proxy.md @@ -36,7 +36,10 @@ async function run() { let token = "g6zzur6bfypfwuqdxxupv3y3qfcoudlgh26bjz77t3mgylzmvjiq"; let authHandler = api.getPersonalAccessTokenHandler(token); - // Options for VSTS-Node-Api + // Options for VSTS-Node-Api, + // this is not required if you want to send http request to the same VSTS/TFS + // instance the agent currently connect to. + // VSTS-Node-Api will pick up proxy setting from VSTS-Task-Lib automatically let option: VsoBaseInterfaces.IRequestOptions = { proxy: { proxyUrl: proxy.proxyUrl, diff --git a/node/internal.ts b/node/internal.ts index f13fc7e91..d16b2a5a3 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -6,6 +6,7 @@ import util = require('util'); import tcm = require('./taskcommand'); import vm = require('./vault'); import semver = require('semver'); +import crypto = require('crypto'); /** * Hash table of known variable info. The formatted env var name is the lookup key. @@ -391,7 +392,7 @@ export function _which(tool: string, check?: boolean): string { // it feels like we should not do this. Checking the current directory seems like more of a use // case of a shell, and the which() function exposed by the task lib should strive for consistency // across platforms. - let directories: string[] = [ ]; + let directories: string[] = []; if (process.env['PATH']) { for (let p of process.env['PATH'].split(path.delimiter)) { if (p) { @@ -686,9 +687,9 @@ export function _ensurePatternRooted(root: string, p: string) { //------------------------------------------------------------------- export function _loadData(): void { - // in agent, workFolder is set. + // in agent, prefer TempDirectory then workFolder. // In interactive dev mode, it won't be - let keyPath: string = _getVariable("agent.workFolder") || process.cwd(); + let keyPath: string = _getVariable("agent.TempDirectory") || _getVariable("agent.workFolder") || process.cwd(); _vault = new vm.Vault(keyPath); _knownVariableMap = {}; _debug('loading inputs and endpoints'); @@ -914,3 +915,67 @@ export function _normalizeSeparators(p: string): string { // remove redundant slashes return p.replace(/\/\/+/g, '/'); } + +//----------------------------------------------------- +// Expose proxy information to vsts-node-api +//----------------------------------------------------- +export function _exposeProxySettings(): void { + let proxyUrl: string = _getVariable('Agent.ProxyUrl'); + if (proxyUrl && proxyUrl.length > 0) { + let proxyUsername: string = _getVariable('Agent.ProxyUsername'); + let proxyPassword: string = _getVariable('Agent.ProxyPassword'); + let proxyBypassHostsJson: string = _getVariable('Agent.ProxyBypassList'); + + global['_vsts_task_lib_proxy_url'] = proxyUrl; + global['_vsts_task_lib_proxy_username'] = proxyUsername; + global['_vsts_task_lib_proxy_bypass'] = proxyBypassHostsJson; + global['_vsts_task_lib_proxy_password'] = _exposeTaskLibSecret('proxy', proxyPassword); + + _debug('expose agent proxy configuration.') + global['_vsts_task_lib_proxy'] = true; + } +} + +//----------------------------------------------------- +// Expose certificate information to vsts-node-api +//----------------------------------------------------- +export function _exposeCertSettings(): void { + let ca: string = _getVariable('Agent.CAInfo'); + if (ca) { + global['_vsts_task_lib_cert_ca'] = ca; + } + + let clientCert: string = _getVariable('Agent.ClientCert'); + if (clientCert) { + let clientCertKey: string = _getVariable('Agent.ClientCertKey'); + let clientCertArchive: string = _getVariable('Agent.ClientCertArchive'); + let clientCertPassword: string = _getVariable('Agent.ClientCertPassword'); + + global['_vsts_task_lib_cert_clientcert'] = clientCert; + global['_vsts_task_lib_cert_key'] = clientCertKey; + global['_vsts_task_lib_cert_archive'] = clientCertArchive; + global['_vsts_task_lib_cert_passphrase'] = _exposeTaskLibSecret('cert', clientCertPassword); + } + + if (ca || clientCert) { + _debug('expose agent certificate configuration.') + global['_vsts_task_lib_cert'] = true; + } +} + +// We store the encryption key on disk and hold the encrypted content and key file in memory +// return base64encoded:base64encoded +// downstream vsts-node-api will retrieve the secret later +function _exposeTaskLibSecret(keyFile: string, secret: string): string { + if (secret) { + let encryptKey = crypto.randomBytes(256); + let cipher = crypto.createCipher("aes-256-ctr", encryptKey); + let encryptedContent = cipher.update(secret, "utf8", "hex"); + encryptedContent += cipher.final("hex"); + + let storageFile = path.join(_getVariable('Agent.TempDirectory') || _getVariable("agent.workFolder") || process.cwd(), keyFile); + fs.writeFileSync(storageFile, encryptKey.toString('base64'), { encoding: 'utf8' }); + + return new Buffer(storageFile).toString('base64') + ':' + new Buffer(encryptedContent).toString('base64'); + } +} \ No newline at end of file diff --git a/node/task.ts b/node/task.ts index a9a580c86..a532c0b10 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1564,6 +1564,49 @@ export function getHttpProxyConfiguration(): ProxyConfiguration { } } +//----------------------------------------------------- +// Http Certificate Helper +//----------------------------------------------------- + +export interface CertConfiguration { + caFile?: string; + certFile?: string; + keyFile?: string; + certArchiveFile?: string; + passphrase?: string; +} + +/** + * Gets http certificate configuration used by Build/Release agent + * + * @return CertConfiguration + */ +export function getHttpCertConfiguration(): CertConfiguration { + let ca: string = getVariable('Agent.CAInfo'); + let clientCert: string = getVariable('Agent.ClientCert'); + + if (ca || clientCert) { + let certConfig: CertConfiguration = {}; + certConfig.caFile = ca; + certConfig.certFile = clientCert; + + if (clientCert) { + let clientCertKey: string = getVariable('Agent.ClientCertKey'); + let clientCertArchive: string = getVariable('Agent.ClientCertArchive'); + let clientCertPassword: string = getVariable('Agent.ClientCertPassword'); + + certConfig.keyFile = clientCertKey; + certConfig.certArchiveFile = clientCertArchive; + certConfig.passphrase = clientCertPassword; + } + + return certConfig; + } + else { + return null; + } +} + //----------------------------------------------------- // Test Publisher //----------------------------------------------------- @@ -1679,4 +1722,6 @@ if (semver.lt(process.versions.node, '4.2.0')) { // avoid loading twice (overwrites .taskkey) if (!global['_vsts_task_lib_loaded']) { im._loadData(); + im._exposeProxySettings(); + im._exposeCertSettings(); } diff --git a/node/vault.ts b/node/vault.ts index d7c04a8cb..554ed3c79 100644 --- a/node/vault.ts +++ b/node/vault.ts @@ -9,7 +9,7 @@ var algorithm = "aes-256-ctr"; // // Store sensitive data in proc. -// Main goal: Protects tasks which would dump envvars from leaking secrets innadvertantly +// Main goal: Protects tasks which would dump envvars from leaking secrets inadvertently // the task lib clears after storing. // Also protects against a dump of a process getting the secrets // The secret is generated and stored externally for the lifetime of the task. diff --git a/powershell/Docs/README.md b/powershell/Docs/README.md index 377bafc80..77aadf55f 100644 --- a/powershell/Docs/README.md +++ b/powershell/Docs/README.md @@ -17,4 +17,5 @@ The [MSBuild Task](https://github.com/Microsoft/vsts-tasks/blob/master/Tasks/MSB ### [Using the VSTS .NET SDKs](UsingOM.md) ### [Minimum agent version](../../node/docs/minagent.md) ### [Proxy](../../node/docs/proxy.md) +### [Certificate](../../node/docs/cert.md) ### [Release notes](ReleaseNotes.md) diff --git a/powershell/VstsTaskSdk/ServerOMFunctions.ps1 b/powershell/VstsTaskSdk/ServerOMFunctions.ps1 index c0cddc67c..a61a6f1f9 100644 --- a/powershell/VstsTaskSdk/ServerOMFunctions.ps1 +++ b/powershell/VstsTaskSdk/ServerOMFunctions.ps1 @@ -3,7 +3,7 @@ Gets assembly reference information. .DESCRIPTION -Not supported for use during task exection. This function is only intended to help developers resolve the minimal set of DLLs that need to be bundled when consuming the VSTS REST SDK or TFS Extended Client SDK. The interface and output may change between patch releases of the VSTS Task SDK. +Not supported for use during task execution. This function is only intended to help developers resolve the minimal set of DLLs that need to be bundled when consuming the VSTS REST SDK or TFS Extended Client SDK. The interface and output may change between patch releases of the VSTS Task SDK. Only a subset of the referenced assemblies may actually be required, depending on the functionality used by your task. It is best to bundle only the DLLs required for your scenario. @@ -24,7 +24,7 @@ function Get-AssemblyReference { [string]$LiteralPath) $ErrorActionPreference = 'Stop' - Write-Warning "Not supported for use during task exection. This function is only intended to help developers resolve the minimal set of DLLs that need to be bundled when consuming the VSTS REST SDK or TFS Extended Client SDK. The interface and output may change between patch releases of the VSTS Task SDK." + Write-Warning "Not supported for use during task execution. This function is only intended to help developers resolve the minimal set of DLLs that need to be bundled when consuming the VSTS REST SDK or TFS Extended Client SDK. The interface and output may change between patch releases of the VSTS Task SDK." Write-Output '' Write-Warning "Only a subset of the referenced assemblies may actually be required, depending on the functionality used by your task. It is best to bundle only the DLLs required for your scenario." $directory = [System.IO.Path]::GetDirectoryName($LiteralPath) @@ -178,7 +178,7 @@ If not specified, defaults to the directory of the entry script for the task. URI to use when initializing the service. If not specified, defaults to System.TeamFoundationCollectionUri. .PARAMETER TfsClientCredentials -Credentials to use when intializing the service. If not specified, the default uses the agent job token to construct the credentials object. The identity associated with the token depends on the scope selected in the build/release definition (either the project collection build/release service identity, or the project build/release service identity). +Credentials to use when initializing the service. If not specified, the default uses the agent job token to construct the credentials object. The identity associated with the token depends on the scope selected in the build/release definition (either the project collection build/release service identity, or the project build/release service identity). .EXAMPLE $versionControlServer = Get-VstsTfsService -TypeName Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer @@ -322,10 +322,13 @@ If not specified, defaults to the directory of the entry script for the task. # URI to use when initializing the HTTP client. If not specified, defaults to System.TeamFoundationCollectionUri. # .PARAMETER VssCredentials -# Credentials to use when intializing the HTTP client. If not specified, the default uses the agent job token to construct the credentials object. The identity associated with the token depends on the scope selected in the build/release definition (either the project collection build/release service identity, or the project build/release service identity). +# Credentials to use when initializing the HTTP client. If not specified, the default uses the agent job token to construct the credentials object. The identity associated with the token depends on the scope selected in the build/release definition (either the project collection build/release service identity, or the project build/release service identity). # .PARAMETER WebProxy -# WebProxy to use when intializing the HTTP client. If not specified, the default uses the proxy configuration agent current has. +# WebProxy to use when initializing the HTTP client. If not specified, the default uses the proxy configuration agent current has. + +# .PARAMETER ClientCert +# ClientCert to use when initializing the HTTP client. If not specified, the default uses the client certificate agent current has. .EXAMPLE $projectHttpClient = Get-VstsVssHttpClient -TypeName Microsoft.TeamFoundation.Core.WebApi.ProjectHttpClient @@ -343,7 +346,9 @@ function Get-VssHttpClient { $VssCredentials, - $WebProxy = (Get-WebProxy)) + $WebProxy = (Get-WebProxy), + + $ClientCert = (Get-ClientCertificate)) Trace-EnteringInvocation -InvocationInfo $MyInvocation $originalErrorActionPreference = $ErrorActionPreference @@ -369,10 +374,22 @@ function Get-VssHttpClient { # Update proxy setting for vss http client [Microsoft.VisualStudio.Services.Common.VssHttpMessageHandler]::DefaultWebProxy = $WebProxy + # Update client certificate setting for vss http client + $null = Get-OMType -TypeName 'Microsoft.VisualStudio.Services.Common.VssHttpRequestSettings' -OMKind 'WebApi' -OMDirectory $OMDirectory -Require + $null = Get-OMType -TypeName 'Microsoft.VisualStudio.Services.WebApi.VssClientHttpRequestSettings' -OMKind 'WebApi' -OMDirectory $OMDirectory -Require + [Microsoft.VisualStudio.Services.Common.VssHttpRequestSettings]$Settings = [Microsoft.VisualStudio.Services.WebApi.VssClientHttpRequestSettings]::Default.Clone() + + if($ClientCert){ + $null = Get-OMType -TypeName 'Microsoft.VisualStudio.Services.WebApi.VssClientCertificateManager' -OMKind 'WebApi' -OMDirectory $OMDirectory -Require + $null = [Microsoft.VisualStudio.Services.WebApi.VssClientCertificateManager]::Instance.ClientCertificates.Add($ClientCert) + + $Settings.ClientCertificateManager = [Microsoft.VisualStudio.Services.WebApi.VssClientCertificateManager]::Instance + } + # Try to construct the HTTP client. Write-Verbose "Constructing HTTP client." try { - return New-Object $TypeName($Uri, $VssCredentials) + return New-Object $TypeName($Uri, $VssCredentials, $Settings) } catch { # Rethrow if the exception is not due to Newtonsoft.Json DLL not found. if ($_.Exception.InnerException -isnot [System.IO.FileNotFoundException] -or @@ -414,7 +431,7 @@ function Get-VssHttpClient { try { # Try again to construct the HTTP client. Write-Verbose "Trying again to construct the HTTP client." - return New-Object $TypeName($Uri, $VssCredentials) + return New-Object $TypeName($Uri, $VssCredentials, $Settings) } finally { # Unregister the assembly resolver. Write-Verbose "Removing assemlby resolver." @@ -465,6 +482,39 @@ function Get-WebProxy { } } +<# +.SYNOPSIS +Gets a client certificate for current connected TFS instance + +.DESCRIPTION +Gets an instance of a X509Certificate2 that is the client certificate Build/Release agent used. + +.EXAMPLE +$x509cert = Get-ClientCertificate +WebRequestHandler.ClientCertificates.Add(x509cert) +#> +function Get-ClientCertificate { + [CmdletBinding()] + param() + + Trace-EnteringInvocation -InvocationInfo $MyInvocation + try + { + # Min agent version that supports client certificate + Assert-Agent -Minimum '2.122.0' + + [string]$clientCert = Get-TaskVariable -Name Agent.ClientCertArchive + [string]$clientCertPassword = Get-TaskVariable -Name Agent.ClientCertPassword + + if((Test-Path -LiteralPath $clientCert -PathType Leaf)) { + return New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @($clientCert, $clientCertPassword) + } + } + finally { + Trace-LeavingInvocation -InvocationInfo $MyInvocation + } +} + ######################################## # Private functions. ######################################## diff --git a/powershell/VstsTaskSdk/VstsTaskSdk.psm1 b/powershell/VstsTaskSdk/VstsTaskSdk.psm1 index e6a5058b3..833a621cc 100644 --- a/powershell/VstsTaskSdk/VstsTaskSdk.psm1 +++ b/powershell/VstsTaskSdk/VstsTaskSdk.psm1 @@ -81,6 +81,8 @@ Export-ModuleMember -Function @( 'Trace-Path' # Proxy functions 'Get-WebProxy' + # Client cert functions + 'Get-ClientCertificate' ) # Override Out-Default globally. From f043c4f91223278827fa2612df19ed1b674822e9 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 29 Aug 2017 10:42:32 -0400 Subject: [PATCH 002/259] bump version to 2.1 for proxy and cert support --- node/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/package.json b/node/package.json index b1cc397d1..f16aeaa37 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.0.7", + "version": "2.1.0", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", From b7ee0e1d166bc2bfdc501cdf6b412a12ed1366e7 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Wed, 13 Sep 2017 08:54:19 +0000 Subject: [PATCH 003/259] Add escaping of closing bracket character (#279) --- node/package.json | 2 +- node/taskcommand.ts | 46 +++++++++++++++---- node/test/commandtests.ts | 37 +++++++++++++-- .../VstsTaskSdk/LoggingCommandFunctions.ps1 | 2 +- powershell/package.json | 2 +- 5 files changed, 72 insertions(+), 17 deletions(-) diff --git a/node/package.json b/node/package.json index f16aeaa37..24f058388 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.1.0", + "version": "2.1.1", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/taskcommand.ts b/node/taskcommand.ts index f3d96b8dc..b8aee389e 100644 --- a/node/taskcommand.ts +++ b/node/taskcommand.ts @@ -32,7 +32,7 @@ export class TaskCommand { if (this.properties.hasOwnProperty(key)) { var val = this.properties[key]; if (val) { - cmdStr += key + '=' + val + ';'; + cmdStr += key + '=' + escape(val) + ';'; } } } @@ -43,7 +43,7 @@ export class TaskCommand { // safely append the message - avoid blowing up when attempting to // call .replace() if message is not a string for some reason let message: string = '' + (this.message || ''); - cmdStr += message.replace(/\r/g, '%0D').replace(/\n/g, '%0A'); + cmdStr += escapedata(message); return cmdStr; } @@ -66,22 +66,48 @@ export function commandFromString(commandLine) { command = cmdInfo.trim().substring(0, spaceIdx); var propSection = cmdInfo.trim().substring(spaceIdx+1); - var propLines = propSection.split(';'); - propLines.forEach(function (propLine) { + var propLines: string[] = propSection.split(';'); + propLines.forEach(function (propLine: string) { propLine = propLine.trim(); if (propLine.length > 0) { - var propParts = propLine.split('='); - if (propParts.length != 2) { + var eqIndex = propLine.indexOf('='); + if (eqIndex == -1){ throw new Error('Invalid property: ' + propLine); } - properties[propParts[0]] = propParts[1]; + + var key: string = propLine.substring(0, eqIndex); + var val: string = propLine.substring(eqIndex+1); + + properties[key] = unescape(val); } }); } - var msg = commandLine.substring(rbPos + 1) - .replace(/%0D/g, '\r') - .replace(/%0A/g, '\n'); + let msg: string = unescapedata(commandLine.substring(rbPos + 1)); var cmd = new TaskCommand(command, properties, msg); return cmd; } + +function escapedata(s) : string { + return s.replace(/\r/g, '%0D') + .replace(/\n/g, '%0A'); +} + +function unescapedata(s) : string { + return s.replace(/%0D/g, '\r') + .replace(/%0A/g, '\n'); +} + +function escape(s) : string { + return s.replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/]/g, '%5D') + .replace(/;/g, '%3B'); +} + +function unescape(s) : string { + return s.replace(/%0D/g, '\r') + .replace(/%0A/g, '\n') + .replace(/%5D/g, ']') + .replace(/%3B/g, ';'); +} diff --git a/node/test/commandtests.ts b/node/test/commandtests.ts index 9aa1cdfc9..2ca72f77c 100644 --- a/node/test/commandtests.ts +++ b/node/test/commandtests.ts @@ -34,6 +34,7 @@ describe('Command Tests', function () { done(); }) + it('toStrings', function (done) { this.timeout(1000); @@ -43,15 +44,27 @@ describe('Command Tests', function () { assert.equal(cmdStr, '##vso[some.cmd foo=bar;]a message'); done(); }) + it('toString escapes message', function (done) { this.timeout(1000); - var tc = new tcm.TaskCommand('some.cmd', { foo: 'bar' }, 'cr \r lf \n crlf \r\n eom'); + var tc = new tcm.TaskCommand('some.cmd', { foo: 'bar' }, 'cr \r lf \n crlf \r\n eom ] ;'); assert(tc, 'TaskCommand constructor works'); var cmdStr = tc.toString(); - assert.equal(cmdStr, '##vso[some.cmd foo=bar;]cr %0D lf %0A crlf %0D%0A eom'); + assert.equal(cmdStr, '##vso[some.cmd foo=bar;]cr %0D lf %0A crlf %0D%0A eom ] ;'); done(); }) + + it ('toString escapes properties', function (done) { + this.timeout(1000); + + var tc = new tcm.TaskCommand('some.cmd', { foo: ';=\r=\n' }, 'dog'); + assert(tc, 'TaskCommand constructor works'); + var cmdStr = tc.toString(); + assert.equal(cmdStr, '##vso[some.cmd foo=%3B=%0D=%0A;]dog'); + done(); + }) + it('handles null properties', function (done) { this.timeout(1000); @@ -59,6 +72,7 @@ describe('Command Tests', function () { assert.equal(tc.toString(), '##vso[some.cmd]a message'); done(); }) + it('parses cmd with no properties', function (done) { var cmdStr = '##vso[basic.command]messageVal'; @@ -69,6 +83,7 @@ describe('Command Tests', function () { assert.equal(tc.message, 'messageVal', 'message is correct'); done(); }) + it('parses basic cmd with values', function (done) { var cmdStr = '##vso[basic.command prop1=val1;]messageVal'; @@ -81,6 +96,7 @@ describe('Command Tests', function () { assert.equal(tc.message, 'messageVal', 'message is correct'); done(); }) + it('parses basic cmd with multiple properties no trailing semi', function (done) { var cmdStr = '##vso[basic.command prop1=val1;prop2=val2]messageVal'; @@ -94,6 +110,7 @@ describe('Command Tests', function () { assert.equal(tc.message, 'messageVal', 'message is correct'); done(); }) + it('parses values with spaces in them', function (done) { var cmdStr = '##vso[task.setvariable variable=task variable;]task variable set value'; @@ -104,14 +121,26 @@ describe('Command Tests', function () { assert.equal(tc.message, 'task variable set value'); done(); }) + it('parses and unescapes message', function (done) { - var cmdStr = '##vso[basic.command]cr %0D lf %0A crlf %0D%0A eom'; + var cmdStr = '##vso[basic.command]cr %0D lf %0A crlf %0D%0A eom ] ;'; var tc = tcm.commandFromString(cmdStr); assert.equal(tc.command, 'basic.command', 'cmd should be basic.command'); - assert.equal(tc.message, 'cr \r lf \n crlf \r\n eom'); + assert.equal(tc.message, 'cr \r lf \n crlf \r\n eom ] ;'); done(); }) + + it ('parses and unescapes properties', function (done) { + var cmdStr = '##vso[basic.command foo=%3B=%0D=%0A;]dog'; + + var tc = tcm.commandFromString(cmdStr); + assert.equal(tc.command, 'basic.command', 'cmd should be basic.command'); + assert.equal(tc.properties['foo'], ';=\r=\n', 'property should be unescaped') + assert.equal(tc.message, 'dog'); + done(); + }) + it('handles empty properties', function (done) { this.timeout(1000); diff --git a/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 b/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 index a8462efbf..a1c8b1eb3 100644 --- a/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 +++ b/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 @@ -3,8 +3,8 @@ $script:loggingCommandEscapeMappings = @( # TODO: WHAT ABOUT "="? WHAT ABOUT "%" New-Object psobject -Property @{ Token = ';' ; Replacement = '%3B' } New-Object psobject -Property @{ Token = "`r" ; Replacement = '%0D' } New-Object psobject -Property @{ Token = "`n" ; Replacement = '%0A' } + New-Object psobject -Property @{ Token = "]" ; Replacement = '%5D' } ) -# TODO: BUG: Escape ] # TODO: BUG: Escape % ??? # TODO: Add test to verify don't need to escape "=". diff --git a/powershell/package.json b/powershell/package.json index 86531977e..e64eb4321 100644 --- a/powershell/package.json +++ b/powershell/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-sdk", - "version": "0.10.0", + "version": "0.10.1", "description": "VSTS Task SDK", "scripts": { "build": "node make.js build", From d04a3eed89a52ef5782356c69fb69b3cfe98a8f9 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 10:57:07 -0400 Subject: [PATCH 004/259] Update .vsts-ci.yml --- .vsts-ci.yml | 71 ++++++++++++++++++---------------------------------- 1 file changed, 24 insertions(+), 47 deletions(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index a397266ef..9296634d4 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -5,76 +5,53 @@ steps: ################################################################################ # npm install - - task: CmdLine@1 - name: (vsts-task-lib) npm install + - task: Npm@1.* + displayName: (vsts-task-lib) npm install inputs: - filename: npm - arguments: install - workingFolder: node - # - task: Npm@1.* - # name: npm install - # inputs: - # command: install - # workingDir: node + command: install + workingDir: node # use node 5 - task: NodeTool@0 - name: (vsts-task-lib) use node 5.10.1 + displayName: (vsts-task-lib) use node 5.10.1 condition: and(succeeded(), ne(variables.os, 'windows')) inputs: versionSpec: "5.10.1" - - task: CmdLine@1 - name: (vsts-task-lib) use node 5.10.1 + - powershell: | + & "$(build.sourcesDirectory)/res/UseNode5.ps1" + displayName: (vsts-task-lib) use node 5.10.1 condition: and(succeeded(), eq(variables.os, 'windows')) - inputs: - filename: powershell.exe - arguments: -noninteractive -noprofile -file "$(build.sourcesDirectory)/res/UseNode5.ps1" # build/test - - task: CmdLine@1 - name: (vsts-task-lib) node make.js test - inputs: - filename: node - arguments: make.js test - workingFolder: node + - script: node make.js test + workingFolder: node + displayName: (vsts-task-lib) node make.js test # use node 6 - task: NodeTool@0 - name: (vsts-task-lib) use node 6.10.3 + displayName: (vsts-task-lib) use node 6.10.3 inputs: versionSpec: "6.10.3" # build/test - - task: CmdLine@1 - name: (vsts-task-lib) node make.js test - inputs: - filename: node - arguments: make.js test - workingFolder: node + - script: node make.js test + displayName: (vsts-task-lib) node make.js test + workingFolder: node ################################################################################ # VstsTaskSdk ################################################################################ # npm install - - task: CmdLine@1 - name: (VstsTaskSdk) npm install - condition: and(succeeded(), eq(variables.os, 'windows')) - inputs: - filename: npm - arguments: install - workingFolder: powershell - # - task: Npm@1.* - # name: npm install - # inputs: - # command: install - # workingDir: powershell + - task: Npm@1.* + displayName: (VstsTaskSdk) npm install + condition: and(succeeded(), eq(variables.os, 'windows')) + inputs: + command: install + workingDir: powershell # npm test - - task: CmdLine@1 - name: (VstsTaskSdk) npm test + - script: npm test + displayName: (VstsTaskSdk) npm test condition: and(succeeded(), eq(variables.os, 'windows')) - inputs: - filename: npm - arguments: test - workingFolder: powershell + workingFolder: powershell From 842567fe30525629723942f2733c737feddcea59 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 10:57:59 -0400 Subject: [PATCH 005/259] Update .vsts-ci.yml --- .vsts-ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index 9296634d4..eda692c15 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -5,7 +5,7 @@ steps: ################################################################################ # npm install - - task: Npm@1.* + - task: Npm@1 displayName: (vsts-task-lib) npm install inputs: command: install @@ -43,12 +43,12 @@ steps: ################################################################################ # npm install - - task: Npm@1.* - displayName: (VstsTaskSdk) npm install - condition: and(succeeded(), eq(variables.os, 'windows')) - inputs: - command: install - workingDir: powershell + - task: Npm@1 + displayName: (VstsTaskSdk) npm install + condition: and(succeeded(), eq(variables.os, 'windows')) + inputs: + command: install + workingDir: powershell # npm test - script: npm test From 1d4bd2e148d27425cdd24f418348660b1e979058 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 10:58:50 -0400 Subject: [PATCH 006/259] Update .vsts-ci.yml --- .vsts-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index eda692c15..511ed9ef8 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -24,7 +24,7 @@ steps: # build/test - script: node make.js test - workingFolder: node + workingDirectory: node displayName: (vsts-task-lib) node make.js test # use node 6 @@ -36,7 +36,7 @@ steps: # build/test - script: node make.js test displayName: (vsts-task-lib) node make.js test - workingFolder: node + workingDirectory: node ################################################################################ # VstsTaskSdk @@ -54,4 +54,4 @@ steps: - script: npm test displayName: (VstsTaskSdk) npm test condition: and(succeeded(), eq(variables.os, 'windows')) - workingFolder: powershell + workingDirectory: powershell From df5abfa9483981b175ba85964d29e356fd8bef31 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 11:17:44 -0400 Subject: [PATCH 007/259] Update .vsts-ci.yml --- .vsts-ci.yml | 103 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 12 deletions(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index 511ed9ef8..385e749c5 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -1,9 +1,17 @@ -steps: +phases: +################################################# +- phase: build_on_windows +################################################# + displayName: (vsts-task-lib) Windows + queue: + name: Hosted VS2017 + + steps: ################################################################################ # vsts-task-lib ################################################################################ - + # npm install - task: Npm@1 displayName: (vsts-task-lib) npm install @@ -12,15 +20,8 @@ steps: workingDir: node # use node 5 - - task: NodeTool@0 - displayName: (vsts-task-lib) use node 5.10.1 - condition: and(succeeded(), ne(variables.os, 'windows')) - inputs: - versionSpec: "5.10.1" - - powershell: | - & "$(build.sourcesDirectory)/res/UseNode5.ps1" + - powershell: & "$(build.sourcesDirectory)/res/UseNode5.ps1" displayName: (vsts-task-lib) use node 5.10.1 - condition: and(succeeded(), eq(variables.os, 'windows')) # build/test - script: node make.js test @@ -45,7 +46,6 @@ steps: # npm install - task: Npm@1 displayName: (VstsTaskSdk) npm install - condition: and(succeeded(), eq(variables.os, 'windows')) inputs: command: install workingDir: powershell @@ -53,5 +53,84 @@ steps: # npm test - script: npm test displayName: (VstsTaskSdk) npm test - condition: and(succeeded(), eq(variables.os, 'windows')) workingDirectory: powershell + +################################################# +- phase: build_on_linux +################################################# + displayName: (vsts-task-lib) linux + queue: + name: Hosted Linux Preview + + steps: + ################################################################################ + # vsts-task-lib + ################################################################################ + + # npm install + - task: Npm@1 + displayName: (vsts-task-lib) npm install + inputs: + command: install + workingDir: node + + # use node 5 + - task: NodeTool@0 + displayName: (vsts-task-lib) use node 5.10.1 + inputs: + versionSpec: "5.10.1" + + # build/test + - script: node make.js test + workingDirectory: node + displayName: (vsts-task-lib) node make.js test + + # use node 6 + - task: NodeTool@0 + displayName: (vsts-task-lib) use node 6.10.3 + inputs: + versionSpec: "6.10.3" + + # build/test + - script: node make.js test + displayName: (vsts-task-lib) node make.js test + workingDirectory: node + +################################################# +- phase: build_on_osx +################################################# + displayName: (vsts-task-lib) osx + + steps: + ################################################################################ + # vsts-task-lib + ################################################################################ + + # npm install + - task: Npm@1 + displayName: (vsts-task-lib) npm install + inputs: + command: install + workingDir: node + + # use node 5 + - task: NodeTool@0 + displayName: (vsts-task-lib) use node 5.10.1 + inputs: + versionSpec: "5.10.1" + + # build/test + - script: node make.js test + workingDirectory: node + displayName: (vsts-task-lib) node make.js test + + # use node 6 + - task: NodeTool@0 + displayName: (vsts-task-lib) use node 6.10.3 + inputs: + versionSpec: "6.10.3" + + # build/test + - script: node make.js test + displayName: (vsts-task-lib) node make.js test + workingDirectory: node From d04d1c5694a13b7c4f45aac35916c6d2de546114 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 11:19:13 -0400 Subject: [PATCH 008/259] Update .vsts-ci.yml --- .vsts-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index 385e749c5..29bbd2516 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -20,7 +20,8 @@ phases: workingDir: node # use node 5 - - powershell: & "$(build.sourcesDirectory)/res/UseNode5.ps1" + - powershell: | + & "$(build.sourcesDirectory)/res/UseNode5.ps1" displayName: (vsts-task-lib) use node 5.10.1 # build/test From 2db48107ee0e8226aa003c650a4ea6c5659bdf51 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 13:23:33 -0400 Subject: [PATCH 009/259] bump test timeout --- node/test/toolrunnertests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 67776e5da..91d629720 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -870,7 +870,7 @@ describe('Toolrunner Tests', function () { }); it('exec .exe with space AND verbatim args (Windows)', function (done) { - this.timeout(1000); + this.timeout(5000); // this test validates the quoting that tool runner adds around the tool path // when using the windowsVerbatimArguments option. otherwise the target process From e4a64f19053b3ccaab1115e90d2915c7e25d7586 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 13:33:47 -0400 Subject: [PATCH 010/259] Update .vsts-ci.yml --- .vsts-ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index 29bbd2516..1c3f9f477 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -5,8 +5,9 @@ phases: ################################################# displayName: (vsts-task-lib) Windows queue: - name: Hosted VS2017 - + name: buildDevs + demands: agent.os -equals Windows_NT + steps: ################################################################################ # vsts-task-lib From b01b539ea7e35779a756fdb2cf71555806cebedc Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 13:39:04 -0400 Subject: [PATCH 011/259] Update toolrunnertests.ts --- node/test/toolrunnertests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 91d629720..67776e5da 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -870,7 +870,7 @@ describe('Toolrunner Tests', function () { }); it('exec .exe with space AND verbatim args (Windows)', function (done) { - this.timeout(5000); + this.timeout(1000); // this test validates the quoting that tool runner adds around the tool path // when using the windowsVerbatimArguments option. otherwise the target process From 856f53320b08edfc17810c0006735bf57daa38c8 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 13:41:57 -0400 Subject: [PATCH 012/259] Update .vsts-ci.yml --- .vsts-ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index 1c3f9f477..6a9324976 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -5,8 +5,9 @@ phases: ################################################# displayName: (vsts-task-lib) Windows queue: - name: buildDevs - demands: agent.os -equals Windows_NT + name: Hosted + #name: buildDevs + #demands: agent.os -equals Windows_NT steps: ################################################################################ From 3c96213840046a1ef68a0e23306e0676b3870997 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 2 Nov 2017 14:03:12 -0400 Subject: [PATCH 013/259] Update .vsts-ci.yml --- .vsts-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index 6a9324976..6bcafab2f 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -5,9 +5,9 @@ phases: ################################################# displayName: (vsts-task-lib) Windows queue: - name: Hosted - #name: buildDevs - #demands: agent.os -equals Windows_NT + #name: Hosted + name: buildDevs + demands: agent.os -equals Windows_NT steps: ################################################################################ From 5ba1a56b960ddb0e7ef88838af4bafeb7874a25a Mon Sep 17 00:00:00 2001 From: ericsciple Date: Thu, 2 Nov 2017 14:26:54 -0400 Subject: [PATCH 014/259] Update minagent.md --- node/docs/minagent.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/node/docs/minagent.md b/node/docs/minagent.md index ae120607b..2a0f6863d 100644 --- a/node/docs/minagent.md +++ b/node/docs/minagent.md @@ -25,8 +25,7 @@ Use the details below to determine when specific agent features were added: - `prejobexecution` and `postjobexecution` handler sections were added in 2.115.0 * `node` handler - Added in 1.95.1. Used node v5.10.1. - - Updated in 2.117.0 to use node v6.10.3 - - Updated in 2.117.0. Previously used v5.10.1 + - Updated in 2.117.0 to use node v6.10.3. * `powershell3` handler - Added in 1.95.1 - Updated in 1.97 to propagate `Data` property for endpoints From 929bc803dc60bd4e56fd83109303e5697d5affee Mon Sep 17 00:00:00 2001 From: Ting Date: Fri, 3 Nov 2017 07:54:33 -0700 Subject: [PATCH 015/259] skip ssl certificate validation to TFS if needed. (#290) --- node/internal.ts | 5 +++ powershell/CompiledHelpers/VstsTaskSdk.cs | 13 ++++++++ powershell/VstsTaskSdk/ServerOMFunctions.ps1 | 32 +++++++++++++++----- powershell/make.js | 2 +- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index d16b2a5a3..e941f37d7 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -961,6 +961,11 @@ export function _exposeCertSettings(): void { _debug('expose agent certificate configuration.') global['_vsts_task_lib_cert'] = true; } + + let skipCertValidation: string = _getVariable('Agent.SkipCertValidation') || 'false'; + if (skipCertValidation) { + global['_vsts_task_lib_skip_cert_validation'] = skipCertValidation.toUpperCase() === 'TRUE'; + } } // We store the encryption key on disk and hold the encrypted content and key file in memory diff --git a/powershell/CompiledHelpers/VstsTaskSdk.cs b/powershell/CompiledHelpers/VstsTaskSdk.cs index f9d8d8ac9..6c37f1e58 100644 --- a/powershell/CompiledHelpers/VstsTaskSdk.cs +++ b/powershell/CompiledHelpers/VstsTaskSdk.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Net; +using System.Net.Security; using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; namespace VstsTaskSdk @@ -89,6 +91,17 @@ private bool IsMatchInBypassList(Uri input) return false; } } + + public sealed class VstsHttpHandlerSettings + { + public static RemoteCertificateValidationCallback UnsafeSkipServerCertificateValidation + { + get + { + return ((object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => { return true; }); + } + } + } } namespace VstsTaskSdk.FS diff --git a/powershell/VstsTaskSdk/ServerOMFunctions.ps1 b/powershell/VstsTaskSdk/ServerOMFunctions.ps1 index a61a6f1f9..9d43b2088 100644 --- a/powershell/VstsTaskSdk/ServerOMFunctions.ps1 +++ b/powershell/VstsTaskSdk/ServerOMFunctions.ps1 @@ -330,6 +330,9 @@ If not specified, defaults to the directory of the entry script for the task. # .PARAMETER ClientCert # ClientCert to use when initializing the HTTP client. If not specified, the default uses the client certificate agent current has. +# .PARAMETER IgnoreSslError +# Skip SSL server certificate validation on all requests made by this HTTP client. If not specified, the default is to validate SSL server certificate. + .EXAMPLE $projectHttpClient = Get-VstsVssHttpClient -TypeName Microsoft.TeamFoundation.Core.WebApi.ProjectHttpClient $projectHttpClient.GetProjects().Result @@ -348,7 +351,9 @@ function Get-VssHttpClient { $WebProxy = (Get-WebProxy), - $ClientCert = (Get-ClientCertificate)) + $ClientCert = (Get-ClientCertificate), + + [switch]$IgnoreSslError) Trace-EnteringInvocation -InvocationInfo $MyInvocation $originalErrorActionPreference = $ErrorActionPreference @@ -379,13 +384,26 @@ function Get-VssHttpClient { $null = Get-OMType -TypeName 'Microsoft.VisualStudio.Services.WebApi.VssClientHttpRequestSettings' -OMKind 'WebApi' -OMDirectory $OMDirectory -Require [Microsoft.VisualStudio.Services.Common.VssHttpRequestSettings]$Settings = [Microsoft.VisualStudio.Services.WebApi.VssClientHttpRequestSettings]::Default.Clone() - if($ClientCert){ + if ($ClientCert) { $null = Get-OMType -TypeName 'Microsoft.VisualStudio.Services.WebApi.VssClientCertificateManager' -OMKind 'WebApi' -OMDirectory $OMDirectory -Require $null = [Microsoft.VisualStudio.Services.WebApi.VssClientCertificateManager]::Instance.ClientCertificates.Add($ClientCert) $Settings.ClientCertificateManager = [Microsoft.VisualStudio.Services.WebApi.VssClientCertificateManager]::Instance } + # Skip SSL server certificate validation + [bool]$SkipCertValidation = (Get-TaskVariable -Name Agent.SkipCertValidation -AsBool) -or $IgnoreSslError + if ($SkipCertValidation) { + if ($Settings.GetType().GetProperty('ServerCertificateValidationCallback')) { + Write-Verbose "Ignore any SSL server certificate validation errors."; + $Settings.ServerCertificateValidationCallback = [VstsTaskSdk.VstsHttpHandlerSettings]::UnsafeSkipServerCertificateValidation + } + else { + # OMDirectory has older version of Microsoft.VisualStudio.Services.Common.dll + Write-Verbose "The version of 'Microsoft.VisualStudio.Services.Common.dll' does not support skip SSL server certificate validation." + } + } + # Try to construct the HTTP client. Write-Verbose "Constructing HTTP client." try { @@ -416,7 +434,7 @@ function Get-VssHttpClient { # dependency on the 6.0.0.0 Newtonsoft.Json DLL, while other parts reference # the 8.0.0.0 Newtonsoft.Json DLL. Write-Verbose "Adding assembly resolver." - $onAssemblyResolve = [System.ResolveEventHandler]{ + $onAssemblyResolve = [System.ResolveEventHandler] { param($sender, $e) if ($e.Name -like 'Newtonsoft.Json, *') { @@ -464,8 +482,7 @@ function Get-WebProxy { param() Trace-EnteringInvocation -InvocationInfo $MyInvocation - try - { + try { # Min agent version that supports proxy Assert-Agent -Minimum '2.105.7' @@ -498,15 +515,14 @@ function Get-ClientCertificate { param() Trace-EnteringInvocation -InvocationInfo $MyInvocation - try - { + try { # Min agent version that supports client certificate Assert-Agent -Minimum '2.122.0' [string]$clientCert = Get-TaskVariable -Name Agent.ClientCertArchive [string]$clientCertPassword = Get-TaskVariable -Name Agent.ClientCertPassword - if((Test-Path -LiteralPath $clientCert -PathType Leaf)) { + if ($clientCert -and (Test-Path -LiteralPath $clientCert -PathType Leaf)) { return New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @($clientCert, $clientCertPassword) } } diff --git a/powershell/make.js b/powershell/make.js index 73112823b..ebcd686c8 100644 --- a/powershell/make.js +++ b/powershell/make.js @@ -28,7 +28,7 @@ target.build = function() { var minimatchPackage = util.downloadArchive('https://www.nuget.org/api/v2/package/minimatch/1.1.0'); util.cp(path.join(minimatchPackage, 'lib', 'portable-net40%2Bsl50%2Bwin%2Bwp80', 'Minimatch.dll'), path.join(buildPath, 'VstsTaskSdk')); - var compiledHelperPackage = util.downloadArchive('https://vstsagenttools.blob.core.windows.net/tools/VstsTaskSdkCompiledHelpers/2/VstsTaskSdk.zip'); + var compiledHelperPackage = util.downloadArchive('https://vstsagenttools.blob.core.windows.net/tools/VstsTaskSdkCompiledHelpers/3/VstsTaskSdk.zip'); util.cp(path.join(compiledHelperPackage, 'VstsTaskSdk.dll'), path.join(buildPath, 'VstsTaskSdk')); // stamp the version number from the package.json onto the PowerShell module definition From 80d6f89a4d7819fc26776bea2d2ad2866f344aac Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Wed, 15 Nov 2017 10:00:41 -0500 Subject: [PATCH 016/259] Allow multiple resource files. --- node/internal.ts | 19 ++++++++-------- node/package.json | 2 +- node/test/loctests.ts | 52 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index e941f37d7..5277e2398 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -66,7 +66,8 @@ export function _setErrStream(errStream): void { //----------------------------------------------------- let _locStringCache: { [key: string]: string } = {}; -let _resourceFile: string; +//let _resourceFile: string; +let _resourceFiles: { [key: string]: string } = {}; let _libResourceFileLoaded: boolean = false; let _resourceCulture: string = 'en-US'; @@ -146,20 +147,20 @@ function _loadLocStrings(resourceFile: string, culture: string): { [key: string] * @returns void */ export function _setResourcePath(path: string): void { - if (process.env['TASKLIB_INPROC_UNITS']) { - _resourceFile = null; + if (process.env['TASKLIB_INPROC_UNITS'] && process.env['TASKLIB_INPROC_UNITS'] != '0') { + _resourceFiles = {}; _libResourceFileLoaded = false; _locStringCache = {}; _resourceCulture = 'en-US'; } - if (!_resourceFile) { + if (!_resourceFiles[path]) { _checkPath(path, 'resource file path'); - _resourceFile = path; - _debug('set resource file to: ' + _resourceFile); + _resourceFiles[path] = path; + _debug('adding resource file: ' + path); _resourceCulture = _getVariable('system.culture') || _resourceCulture; - var locStrs = _loadLocStrings(_resourceFile, _resourceCulture); + var locStrs: { [key: string]: string; } = _loadLocStrings(path, _resourceCulture); for (var key in locStrs) { //cache loc string _locStringCache[key] = locStrs[key]; @@ -167,7 +168,7 @@ export function _setResourcePath(path: string): void { } else { - _warning(_loc('LIB_ResourceFileAlreadySet', _resourceFile)); + _warning(_loc('LIB_ResourceFileAlreadySet', path)); } } @@ -196,7 +197,7 @@ export function _loc(key: string, ...param: any[]): string { locString = _locStringCache[key]; } else { - if (!_resourceFile) { + if (Object.keys(_resourceFiles).length <= 0) { _warning(_loc('LIB_ResourceFileNotSet', key)); } else { diff --git a/node/package.json b/node/package.json index 24f058388..a64b541f7 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.1.1", + "version": "2.2.0", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/test/loctests.ts b/node/test/loctests.ts index 27412aa3d..0e2c370c7 100644 --- a/node/test/loctests.ts +++ b/node/test/loctests.ts @@ -13,7 +13,7 @@ import testutil = require('./testutil'); describe('Loc Tests', function () { - before(function (done) { + beforeEach(function (done) { try { testutil.initialize(); } @@ -72,6 +72,54 @@ describe('Loc Tests', function () { done(); }) + it('gets loc string from second loc resources.json', function (done) { + this.timeout(1000); + + // Don't reset values each time we call setResourcesPath for this test. + process.env['TASKLIB_INPROC_UNITS'] = '0'; + + // Arrange + var tempFolder = path.join(testutil.getTestTemp(), 'loc-str-from-loc-res-json2'); + shell.mkdir('-p', tempFolder); + + // Create first task.json and resources file + var jsonStr = "{\"messages\": {\"key6\" : \"string for key 6.\"}}"; + var jsonPath = path.join(tempFolder, 'task.json'); + fs.writeFileSync(jsonPath, jsonStr); + + var tempLocFolder = path.join(tempFolder, 'Strings', 'resources.resjson', 'zh-CN'); + shell.mkdir('-p', tempLocFolder); + var locJsonStr = "{\"loc.messages.key6\" : \"loc cn-string for key 6.\"}"; + var locJsonPath = path.join(tempLocFolder, 'resources.resjson'); + fs.writeFileSync(locJsonPath, locJsonStr); + + // Create second task.json and resources file + var nestedLocFolder = path.join(tempFolder, 'nested'); + shell.mkdir('-p', nestedLocFolder); + + var jsonStr2 = "{\"messages\": {\"keySecondFile\" : \"string for keySecondFile.\"}}"; + var jsonPath2 = path.join(nestedLocFolder, 'task.json'); + fs.writeFileSync(jsonPath2, jsonStr2); + + var tempLocFolder2 = path.join(nestedLocFolder, 'Strings', 'resources.resjson', 'zh-CN'); + shell.mkdir('-p', tempLocFolder2); + var locJsonStr2 = "{\"loc.messages.keySecondFile\" : \"loc cn-string for keySecondFile.\"}"; + var locJsonPath2 = path.join(tempLocFolder2, 'resources.resjson'); + fs.writeFileSync(locJsonPath2, locJsonStr2); + + process.env['SYSTEM_CULTURE'] = 'ZH-cn'; // Lib should handle casing differences for culture. + + // Act + tl.setResourcePath(jsonPath); + tl.setResourcePath(jsonPath2); + + // Assert + assert.equal(tl.loc('key6'), 'loc cn-string for key 6.', 'string not found for key.'); + assert.equal(tl.loc('keySecondFile'), 'loc cn-string for keySecondFile.', 'string not found for keySecondFile.'); + + done(); + }) + it('fallback to current string if culture resources.resjson not found', function (done) { this.timeout(1000); @@ -142,7 +190,7 @@ describe('Loc Tests', function () { fs.writeFileSync(jsonPath, jsonStr); tl.setResourcePath(jsonPath); - assert.equal(tl.loc('key3', 3), 'key3 3', 'key and params not return for non-exist key.'); + assert.equal(tl.loc('key9', 9), 'key9 9', 'key and params not return for non-exist key.'); done(); }) From acc2922bba58c8a38c0b740d1d0d5274e1f5e7aa Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Wed, 15 Nov 2017 10:06:18 -0500 Subject: [PATCH 017/259] Revert test change. --- node/test/loctests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/test/loctests.ts b/node/test/loctests.ts index 0e2c370c7..3a35a719b 100644 --- a/node/test/loctests.ts +++ b/node/test/loctests.ts @@ -190,7 +190,7 @@ describe('Loc Tests', function () { fs.writeFileSync(jsonPath, jsonStr); tl.setResourcePath(jsonPath); - assert.equal(tl.loc('key9', 9), 'key9 9', 'key and params not return for non-exist key.'); + assert.equal(tl.loc('key3', 3), 'key3 3', 'key and params not return for non-exist key.'); done(); }) From c41ab801607cf5c84fd133c6048a85d7d331bfda Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Wed, 15 Nov 2017 10:09:50 -0500 Subject: [PATCH 018/259] Remove commented code. --- node/internal.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/node/internal.ts b/node/internal.ts index 5277e2398..6f213c5d0 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -66,7 +66,6 @@ export function _setErrStream(errStream): void { //----------------------------------------------------- let _locStringCache: { [key: string]: string } = {}; -//let _resourceFile: string; let _resourceFiles: { [key: string]: string } = {}; let _libResourceFileLoaded: boolean = false; let _resourceCulture: string = 'en-US'; From 954f052d9d695c98b0e68a1c255ed2e4fdac0330 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Wed, 15 Nov 2017 10:11:58 -0500 Subject: [PATCH 019/259] Remove unnecessary code. --- node/internal.ts | 2 +- node/test/loctests.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index 6f213c5d0..532c81318 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -146,7 +146,7 @@ function _loadLocStrings(resourceFile: string, culture: string): { [key: string] * @returns void */ export function _setResourcePath(path: string): void { - if (process.env['TASKLIB_INPROC_UNITS'] && process.env['TASKLIB_INPROC_UNITS'] != '0') { + if (process.env['TASKLIB_INPROC_UNITS']) { _resourceFiles = {}; _libResourceFileLoaded = false; _locStringCache = {}; diff --git a/node/test/loctests.ts b/node/test/loctests.ts index 3a35a719b..ba564a7e7 100644 --- a/node/test/loctests.ts +++ b/node/test/loctests.ts @@ -76,7 +76,7 @@ describe('Loc Tests', function () { this.timeout(1000); // Don't reset values each time we call setResourcesPath for this test. - process.env['TASKLIB_INPROC_UNITS'] = '0'; + process.env['TASKLIB_INPROC_UNITS'] = ''; // Arrange var tempFolder = path.join(testutil.getTestTemp(), 'loc-str-from-loc-res-json2'); From 306ddaa9a85a84cf866761a3c5fdf4453653e2a7 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Mon, 27 Nov 2017 14:18:31 -0500 Subject: [PATCH 020/259] Add TFS 2017 Update 3 agent version. --- node/docs/minagent.md | 1 + 1 file changed, 1 insertion(+) diff --git a/node/docs/minagent.md b/node/docs/minagent.md index 2a0f6863d..c72aa5f93 100644 --- a/node/docs/minagent.md +++ b/node/docs/minagent.md @@ -47,6 +47,7 @@ The following chart details the agent versions that shipped with each on-premise | Agent | TFS | |---------|-------------------| +| 2.122.1 | TFS 2017 Update 3 | | 2.117.x | TFS 2017 Update 2 | | 2.112.0 | TFS 2017 Update 1 | | 2.105.7 | TFS 2017 RTM | From 86fbbedde8288b9088e609b6ec45f527b7f56ce5 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Mon, 27 Nov 2017 14:36:02 -0500 Subject: [PATCH 021/259] Update yml config. --- .vsts-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index 6bcafab2f..c0e6916bf 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -62,8 +62,7 @@ phases: - phase: build_on_linux ################################################# displayName: (vsts-task-lib) linux - queue: - name: Hosted Linux Preview + queue: Hosted Linux Preview steps: ################################################################################ @@ -103,6 +102,7 @@ phases: - phase: build_on_osx ################################################# displayName: (vsts-task-lib) osx + queue: Hosted Mac Preview steps: ################################################################################ From 924b02e06846c42d3bb6d11c5036406b5727bd06 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Mon, 27 Nov 2017 14:41:10 -0500 Subject: [PATCH 022/259] Fix mac pool. --- .vsts-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index c0e6916bf..efb5fcc24 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -102,7 +102,7 @@ phases: - phase: build_on_osx ################################################# displayName: (vsts-task-lib) osx - queue: Hosted Mac Preview + queue: Hosted Mac Internal steps: ################################################################################ From a4be3729c2416c90fc6876902eb60e5e062d7c63 Mon Sep 17 00:00:00 2001 From: Cheyo Jimenez Date: Tue, 28 Nov 2017 19:13:09 -0800 Subject: [PATCH 023/259] Added video (#301) Deep Dive on Building Custom Build or Deploy Tasks --- node/docs/stepbystep.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/docs/stepbystep.md b/node/docs/stepbystep.md index ed1957cd3..64958044a 100644 --- a/node/docs/stepbystep.md +++ b/node/docs/stepbystep.md @@ -9,7 +9,7 @@ Note: The tutorial was done on a mac. Attempted to make generic for all platfor ## Video -TODO: posting soon +https://youtu.be/O_34c7p4GlM?t=1173 ## Tools From 0217b3ac5756b79b13f7373c2725e9355e0c8523 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Mon, 29 Jan 2018 08:18:14 -0800 Subject: [PATCH 024/259] Add task.json schema file (#146) * Add task.json schema file * Allow any "connectedService:" type of input * Added the "id" field --- tasks.schema.json | 334 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 tasks.schema.json diff --git a/tasks.schema.json b/tasks.schema.json new file mode 100644 index 000000000..a7ff0e307 --- /dev/null +++ b/tasks.schema.json @@ -0,0 +1,334 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema", + "id": "https://aka.ms/vsts-tasks.schema.json", + "title": "VSTS Tasks schema", + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "description": "A unique guid for this task", + "pattern": "^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$" + }, + "name": { + "type": "string", + "description": "Name with no spaces", + "pattern": "^[A-Za-z0-9\\-]+$" + }, + "friendlyName": { + "type": "string", + "description": "Descriptive name (spaces allowed)" + }, + "description": { + "type": "string", + "description": "Detailed description of what your task does" + }, + "helpMarkDown": { + "type": "string" + }, + "author": { + "type": "string" + }, + "visibility": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "Build", + "Release", + "Preview" + ] + } + }, + "runsOn": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "Agent", + "MachineGroup", + "Server" + ] + } + }, + "category": { + "type": "string", + "description": "Where the task appears in VSTS", + "enum": [ + "Build", + "Utility", + "Test", + "Package", + "Deploy" + ] + }, + "groups": { + "type": "array", + "description": "Describes groups that task properties may be logically grouped by in the UI.", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "displayName" + ], + "properties": { + "name": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "isExpanded": { + "type": "boolean" + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "Preview" + ] + } + } + } + } + }, + "demands": { + "type": "array", + "description": "Allows you to define a list of demands that a build agent requires to run this build task.", + "items": { + "type": "string" + } + }, + "minimumAgentVersion": { + "type": "string", + "pattern": "^\\d+\\.\\d+(\\.\\d+)?$" + }, + "version": { + "type": "object", + "additionalProperties": false, + "description": "Always update this when you release your task, so that the agents utilise the latest code.", + "required": [ + "Major", + "Minor", + "Patch" + ], + "properties": { + "Major": { + "type": "number" + }, + "Minor": { + "type": "number" + }, + "Patch": { + "type": "number" + } + } + }, + "instanceNameFormat": { + "type": "string", + "description": "This is how the task will be displayed within the build step list - you can use variable values by using $(variablename)" + }, + "inputs": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "label", + "type" + ], + "properties": { + "name": { + "type": "string", + "description": "The variable name to use to store the user-supplied value", + "pattern": "^[A-Za-z][A-Za-z0-9]*$" + }, + "label": { + "type": "string", + "description": "The text displayed to the user for the input label" + }, + "type": { + "type": "string", + "description": "The type that dictates the control rendered to the user.", + "anyOf": [ + { + "enum": [ + "boolean", + "filePath", + "multiLine", + "pickList", + "radio", + "string" + ] + }, + { + "pattern": "^connectedService\\:.+$" + } + ] + }, + "defaultValue": { + "type": [ + "string", + "boolean" + ], + "description": "The default value to apply to this input." + }, + "required": { + "type": "boolean", + "description": "Whether the input is a required field (default is false).", + "default": false + }, + "helpMarkDown": { + "type": "string", + "description": "Help to be displayed when hovering over the help icon for the input. To display URLs use the format [Text To Display](http://Url)" + }, + "groupName": { + "type": "string", + "description": "Setting this to the name of a group defined in 'groups' will place the input into that group." + }, + "visibleRule": { + "type": "string", + "description": "Allow's you to define a rule which dictates when the input will be visible to a user, for example \"variableName = value\"" + }, + "properties": { + "type": "object", + "properties": { + "EditableOptions": { + "type": "string", + "enum": [ + "True", + "False" + ] + } + } + }, + "options": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "dataSourceBindings": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "target": { + "type": "string" + }, + "endpointId": { + "type": "string" + }, + "dataSourceName": { + "type": "string" + }, + "parameters": { + "type": "object" + }, + "resultTemplate": { + "type": "string" + } + } + } + }, + "sourceDefinitions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "target": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "selector": { + "type": "string" + }, + "keySelector": { + "type": "string" + }, + "authKey": { + "type": "string" + } + } + } + }, + "execution": { + "type": "object", + "additionalProperties": false, + "description": "Execution options for this task", + "properties": { + "Node": { + "$ref": "#/definitions/executionObject" + }, + "Bash": { + "$ref": "#/definitions/executionObject" + }, + "AzurePowerShell": { + "$ref": "#/definitions/executionObject" + }, + "PowerShell": { + "$ref": "#/definitions/executionObject" + }, + "PowerShell3": { + "$ref": "#/definitions/executionObject" + }, + "PowerShellExe": { + "$ref": "#/definitions/executionObject" + }, + "Process": { + "$ref": "#/definitions/executionObject" + }, + "RM:ManualIntervention": { + "$ref": "#/definitions/executionObject" + } + } + }, + "messages": { + "type": "object" + }, + "$schema": { + "type": "string" + } + }, + "definitions": { + "executionObject": { + "type": "object", + "additionalProperties": true, + "properties": { + "target": { + "type": "string", + "description": "The target file to be executed. You can use variables here in brackets e.g. $(currentDirectory)\filename.ps1" + }, + "argumentFormat": { + "type": "string" + }, + "workingDirectory": { + "type": "string", + "description": "The directory to execute the task from e.g. $(currentDirectory)" + }, + "modifyEnvironment": { + "type": [ + "boolean", + "string" + ] + }, + "platforms": { + "type": "array", + "items": { + "enum": [ + "windows" + ] + } + } + } + } + } +} From ad4047c221c7004c8675b9d2cdae5a35eee9a6c4 Mon Sep 17 00:00:00 2001 From: ericsciple Date: Mon, 29 Jan 2018 11:35:34 -0500 Subject: [PATCH 025/259] remove extra schema info (#308) --- tasks.schema.json | 55 ++--------------------------------------------- 1 file changed, 2 insertions(+), 53 deletions(-) diff --git a/tasks.schema.json b/tasks.schema.json index a7ff0e307..4f7c36486 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -29,17 +29,6 @@ "author": { "type": "string" }, - "visibility": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Build", - "Release", - "Preview" - ] - } - }, "runsOn": { "type": "array", "items": { @@ -81,15 +70,6 @@ }, "isExpanded": { "type": "boolean" - }, - "tags": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Preview" - ] - } } } } @@ -108,7 +88,7 @@ "version": { "type": "object", "additionalProperties": false, - "description": "Always update this when you release your task, so that the agents utilise the latest code.", + "description": "Always update this when you release your task, so that the agents utilize the latest code.", "required": [ "Major", "Minor", @@ -196,7 +176,7 @@ "properties": { "type": "object", "properties": { - "EditableOptions": { + "editableOptions": { "type": "string", "enum": [ "True", @@ -268,26 +248,8 @@ "Node": { "$ref": "#/definitions/executionObject" }, - "Bash": { - "$ref": "#/definitions/executionObject" - }, - "AzurePowerShell": { - "$ref": "#/definitions/executionObject" - }, - "PowerShell": { - "$ref": "#/definitions/executionObject" - }, "PowerShell3": { "$ref": "#/definitions/executionObject" - }, - "PowerShellExe": { - "$ref": "#/definitions/executionObject" - }, - "Process": { - "$ref": "#/definitions/executionObject" - }, - "RM:ManualIntervention": { - "$ref": "#/definitions/executionObject" } } }, @@ -307,19 +269,6 @@ "type": "string", "description": "The target file to be executed. You can use variables here in brackets e.g. $(currentDirectory)\filename.ps1" }, - "argumentFormat": { - "type": "string" - }, - "workingDirectory": { - "type": "string", - "description": "The directory to execute the task from e.g. $(currentDirectory)" - }, - "modifyEnvironment": { - "type": [ - "boolean", - "string" - ] - }, "platforms": { "type": "array", "items": { From 2ea9157a24666e8fd3a0fddb3fc3818be0463029 Mon Sep 17 00:00:00 2001 From: ericsciple Date: Tue, 6 Feb 2018 10:58:32 -0500 Subject: [PATCH 026/259] link to task json schema (#309) --- node/README.md | 2 +- node/docs/stepbystep.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/README.md b/node/README.md index f8c111169..36382cee4 100644 --- a/node/README.md +++ b/node/README.md @@ -15,7 +15,7 @@ Cross platform tasks are written in Typescript. It is the preferred way to writ Step by Step: [Create Task](docs/stepbystep.md) -Documentation: [Typescript API](docs/vsts-task-lib.md) +Documentation: [Typescript API](docs/vsts-task-lib.md), [task JSON schema](https://aka.ms/vsts-tasks.schema.json) Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/minagent.md), [Proxy](docs/proxy.md), [Certificate](docs/cert.md) diff --git a/node/docs/stepbystep.md b/node/docs/stepbystep.md index 64958044a..1d99b58f0 100644 --- a/node/docs/stepbystep.md +++ b/node/docs/stepbystep.md @@ -76,7 +76,7 @@ Change `tsconfig.json` file to ES6 to match the sample gist. ES6 is for async a Now that the scaffolding is out of the way, let's create the task! -Create a `task.json` file using `sample_task.json` as a starting point. +Create a `task.json` file using `sample_task.json` as a starting point. The full task JSON schema is [here](https://aka.ms/vsts-tasks.schema.json). Replace the `{{placeholders}}`. The most important being a [unique guid](http://www.guidgen.com/). Note: copy from web view since file needs property names in quotes (browser might strip in raw view) From b7b47e488590fba1b1940c6b05fe11ceb15b47b8 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Thu, 8 Feb 2018 13:33:42 -0500 Subject: [PATCH 027/259] Fix escaping property values. (#311) Properly escape property value to handle non string types --- node/package.json | 2 +- node/taskcommand.ts | 4 +++- node/test/commandtests.ts | 10 ++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/node/package.json b/node/package.json index a64b541f7..01beaec27 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.2.0", + "version": "2.2.1", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/taskcommand.ts b/node/taskcommand.ts index b8aee389e..1634bac0b 100644 --- a/node/taskcommand.ts +++ b/node/taskcommand.ts @@ -32,7 +32,9 @@ export class TaskCommand { if (this.properties.hasOwnProperty(key)) { var val = this.properties[key]; if (val) { - cmdStr += key + '=' + escape(val) + ';'; + // safely append the val - avoid blowing up when attempting to + // call .replace() if message is not a string for some reason + cmdStr += key + '=' + escape('' + (val || '')) + ';'; } } } diff --git a/node/test/commandtests.ts b/node/test/commandtests.ts index 2ca72f77c..4a7ea9012 100644 --- a/node/test/commandtests.ts +++ b/node/test/commandtests.ts @@ -55,6 +55,16 @@ describe('Command Tests', function () { done(); }) + it('toString handles non string value in properties', function (done) { + this.timeout(1000); + + var tc = new tcm.TaskCommand('some.cmd', { foo: ['bar', 'baz'] }, 'cr \r lf \n crlf \r\n eom ] ;'); + assert(tc, 'TaskCommand constructor works'); + var cmdStr = tc.toString(); + assert.equal(cmdStr, '##vso[some.cmd foo=bar,baz;]cr %0D lf %0A crlf %0D%0A eom ] ;'); + done(); + }) + it ('toString escapes properties', function (done) { this.timeout(1000); From bfddff3013b85ce9d9f534160d33a70e992eb9ed Mon Sep 17 00:00:00 2001 From: ericsciple Date: Thu, 22 Feb 2018 17:52:50 -0500 Subject: [PATCH 028/259] Error on multiline secret (#315) * Error on multiline secret * v2.3.0 --- .../resources.resjson/en-US/resources.resjson | 1 + node/docs/releases.md | 4 + node/lib.json | 1 + node/package.json | 2 +- node/task.ts | 20 ++++- node/test/inputtests.ts | 86 +++++++++++++++++++ node/test/toolrunnertests.ts | 2 +- 7 files changed, 113 insertions(+), 3 deletions(-) diff --git a/node/Strings/resources.resjson/en-US/resources.resjson b/node/Strings/resources.resjson/en-US/resources.resjson index 46f8afe04..8fa6a5bc1 100644 --- a/node/Strings/resources.resjson/en-US/resources.resjson +++ b/node/Strings/resources.resjson/en-US/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "Unable to create directory '%s'. Conflicting file exists: '%s'", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Unable to create directory '%s'. Root directory does not exist: '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "Unable to create directory '%s'. Unable to verify the directory exists: '%s'. If directory is a file share, please verify the share name is correct, the share is online, and the current process has permission to access the share.", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "Return code: %d", "loc.messages.LIB_ResourceFileNotExist": "Resource file doesn\\'t exist: %s", "loc.messages.LIB_ResourceFileAlreadySet": "Resource file has already set to: %s", diff --git a/node/docs/releases.md b/node/docs/releases.md index cec48e629..6c6261139 100644 --- a/node/docs/releases.md +++ b/node/docs/releases.md @@ -1,5 +1,9 @@ # VSTS-TASK-LIB RELEASES +## 2.3.0 + * Updated `setVariable` to fail when a secret contains multiple lines. + * Added `setSecret` to register a secret with the log scrubber, without registering a variable. Multi-line secrets are not supported. + ## 2.0.4-preview * Updated `ToolRunner` to validate the specified tool can be found and is executable. * Updated `which` to validate the file is executable and also on Windows to apply PATHEXT. diff --git a/node/lib.json b/node/lib.json index 5287d0990..caed8df73 100644 --- a/node/lib.json +++ b/node/lib.json @@ -6,6 +6,7 @@ "LIB_MkdirFailedFileExists": "Unable to create directory '%s'. Conflicting file exists: '%s'", "LIB_MkdirFailedInvalidDriveRoot": "Unable to create directory '%s'. Root directory does not exist: '%s'", "LIB_MkdirFailedInvalidShare": "Unable to create directory '%s'. Unable to verify the directory exists: '%s'. If directory is a file share, please verify the share name is correct, the share is online, and the current process has permission to access the share.", + "LIB_MultilineSecret": "Secrets cannot contain multiple lines", "LIB_ReturnCode": "Return code: %d", "LIB_ResourceFileNotExist": "Resource file doesn\\'t exist: %s", "LIB_ResourceFileAlreadySet": "Resource file has already set to: %s", diff --git a/node/package.json b/node/package.json index 01beaec27..f6e042401 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.2.1", + "version": "2.3.0", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index a532c0b10..783402d35 100644 --- a/node/task.ts +++ b/node/task.ts @@ -114,7 +114,7 @@ export function getVariables(): VariableInfo[] { * * @param name name of the variable to set * @param val value to set - * @param secret whether variable is secret. optional, defaults to false + * @param secret whether variable is secret. Multi-line secrets are not allowed. Optional, defaults to false * @returns void */ export function setVariable(name: string, val: string, secret: boolean = false): void { @@ -128,6 +128,10 @@ export function setVariable(name: string, val: string, secret: boolean = false): let varValue = val || ''; debug('set ' + name + '=' + (secret && varValue ? '********' : varValue)); if (secret) { + if (varValue && varValue.match(/\r|\n/) && `${process.env['SYSTEM_UNSAFEALLOWMULTILINESECRET']}`.toUpperCase() != 'TRUE') { + throw new Error(loc('LIB_MultilineSecret')); + } + im._vault.storeSecret('SECRET_' + key, varValue); delete process.env[key]; } else { @@ -141,6 +145,20 @@ export function setVariable(name: string, val: string, secret: boolean = false): command('task.setvariable', { 'variable': name || '', 'issecret': (secret || false).toString() }, varValue); } +/** + * Registers a value with the logger, so the value will be masked from the logs. Multi-line secrets are not allowed. + * + * @param val value to register + */ +export function setSecret(val: string): void { + if (val) { + if (val.match(/\r|\n/) && `${process.env['SYSTEM_UNSAFEALLOWMULTILINESECRET']}`.toUpperCase() !== 'TRUE') { + throw new Error(loc('LIB_MultilineSecret')); + } + command('task.setsecret', {}, val); + } +} + /** Snapshot of a variable at the time when getVariables was called. */ export interface VariableInfo { name: string; diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index dd187b5ed..929f43439 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -222,6 +222,92 @@ describe('Input Tests', function () { done(); }) + it('does not allow setting a multi-line secret variable', function (done) { + this.timeout(1000); + + im._loadData(); + + // test carriage return + let failed = false; + try { + tl.setVariable('my.secret', 'line1\rline2', true); + } + catch (err) { + failed = true; + } + assert(failed, 'Should have failed setting a secret variable with a carriage return'); + + // test line feed + failed = false; + try { + tl.setVariable('my.secret', 'line1\nline2', true); + } + catch (err) { + failed = true; + } + assert(failed, 'Should have failed setting a secret variable with a line feed'); + + done(); + }) + it('allows unsafe setting a multi-line secret variable', function (done) { + this.timeout(1000); + + im._loadData(); + try { + process.env['SYSTEM_UNSAFEALLOWMULTILINESECRET'] = 'true'; + tl.setVariable('my.secret', 'line1\r\nline2', true); + } + finally { + delete process.env['SYSTEM_UNSAFEALLOWMULTILINESECRET']; + } + + assert.equal(tl.getVariable('my.secret'), 'line1\r\nline2'); + + done(); + }) + + // setSecret tests + it('does not allow setting a multi-line secret', function (done) { + this.timeout(1000); + + im._loadData(); + + // test carriage return + let failed = false; + try { + tl.setSecret('line1\rline2'); + } + catch (err) { + failed = true; + } + assert(failed, 'Should have failed setting a secret with a carriage return'); + + // test line feed + failed = false; + try { + tl.setSecret('line1\nline2'); + } + catch (err) { + failed = true; + } + assert(failed, 'Should have failed setting a secret with a line feed'); + + done(); + }) + it('allows unsafe setting a multi-line secret', function (done) { + this.timeout(1000); + + im._loadData(); + try { + process.env['SYSTEM_UNSAFEALLOWMULTILINESECRET'] = 'true'; + tl.setSecret('line1\r\nline2'); + } + finally { + delete process.env['SYSTEM_UNSAFEALLOWMULTILINESECRET']; + } + + done(); + }) // getVariables tests it('gets public variables from initial load', function (done) { diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 67776e5da..f930fc113 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -329,7 +329,7 @@ describe('Toolrunner Tests', function () { } }) it('Succeeds on stderr by default', function (done) { - this.timeout(1000); + this.timeout(2000); var scriptPath = path.join(__dirname, 'scripts', 'stderroutput.js'); var ls = tl.tool(tl.which('node', true)); From c70eaca01135c0808933970c6aab8fa05a456da2 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Fri, 23 Feb 2018 11:41:44 -0500 Subject: [PATCH 029/259] Handback. (#316) --- node/Strings/resources.resjson/de-de/resources.resjson | 1 + node/Strings/resources.resjson/es-es/resources.resjson | 1 + node/Strings/resources.resjson/fr-fr/resources.resjson | 1 + node/Strings/resources.resjson/it-IT/resources.resjson | 1 + node/Strings/resources.resjson/ja-jp/resources.resjson | 3 ++- node/Strings/resources.resjson/ko-KR/resources.resjson | 1 + node/Strings/resources.resjson/ru-RU/resources.resjson | 3 ++- node/Strings/resources.resjson/zh-CN/resources.resjson | 1 + node/Strings/resources.resjson/zh-TW/resources.resjson | 1 + .../Strings/resources.resjson/de-de/resources.resjson | 1 + .../Strings/resources.resjson/es-es/resources.resjson | 1 + .../Strings/resources.resjson/fr-fr/resources.resjson | 1 + .../Strings/resources.resjson/it-IT/resources.resjson | 1 + .../Strings/resources.resjson/ja-jp/resources.resjson | 1 + .../Strings/resources.resjson/ko-KR/resources.resjson | 1 + .../Strings/resources.resjson/ru-RU/resources.resjson | 1 + .../Strings/resources.resjson/zh-CN/resources.resjson | 1 + .../Strings/resources.resjson/zh-TW/resources.resjson | 1 + 18 files changed, 20 insertions(+), 2 deletions(-) diff --git a/node/Strings/resources.resjson/de-de/resources.resjson b/node/Strings/resources.resjson/de-de/resources.resjson index 19e594b36..75a1a0379 100644 --- a/node/Strings/resources.resjson/de-de/resources.resjson +++ b/node/Strings/resources.resjson/de-de/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "Das Verzeichnis \"%s\" kann nicht erstellt werden. Eine in Konflikt stehende Datei ist vorhanden: \"%s\"", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Das Verzeichnis \"%s\" kann nicht erstellt werden. Das Stammverzeichnis ist nicht vorhanden: %s", "loc.messages.LIB_MkdirFailedInvalidShare": "Das Verzeichnis \"%s\" kann nicht erstellt werden. Es kann nicht überprüft werden, ob das Verzeichnis vorhanden ist: {%s}. Wenn das Verzeichnis eine Dateifreigabe ist, stellen Sie sicher, dass der Freigabename richtig, die Freigabe online und der aktuelle Prozess berechtigt ist, auf die Freigabe zuzugreifen.", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "Rückgabecode: %d", "loc.messages.LIB_ResourceFileNotExist": "Die Ressourcendatei ist nicht vorhanden: %s", "loc.messages.LIB_ResourceFileAlreadySet": "Die Ressourcendatei wurde bereits festgelegt auf: %s", diff --git a/node/Strings/resources.resjson/es-es/resources.resjson b/node/Strings/resources.resjson/es-es/resources.resjson index d1c9e314a..6ae378812 100644 --- a/node/Strings/resources.resjson/es-es/resources.resjson +++ b/node/Strings/resources.resjson/es-es/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "No se puede crear el directorio '%s'. Hay un conflicto entre archivos: '%s'", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "No se puede crear el directorio '%s'. El directorio raíz no existe: '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "No se puede crear el directorio '%s'. No se puede comprobar si el directorio existe: '%s'. Si el directorio es un recurso compartido de archivos, compruebe que el nombre es correcto, que está en línea y que el proceso actual tiene permiso de acceso a este.", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "Código de retorno: %d", "loc.messages.LIB_ResourceFileNotExist": "El archivo de recursos no existe: %s", "loc.messages.LIB_ResourceFileAlreadySet": "El archivo de recursos se ha establecido ya en: %s", diff --git a/node/Strings/resources.resjson/fr-fr/resources.resjson b/node/Strings/resources.resjson/fr-fr/resources.resjson index ad3cbd090..334162833 100644 --- a/node/Strings/resources.resjson/fr-fr/resources.resjson +++ b/node/Strings/resources.resjson/fr-fr/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "Impossible de créer le répertoire '%s'. Présence d'un fichier en conflit : '%s'", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Impossible de créer le répertoire '%s'. Le répertoire racine n'existe pas : '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "Impossible de créer le répertoire '%s'. Impossible de vérifier l'existence du répertoire : '%s'. Si le répertoire est un partage de fichiers, vérifiez que le nom du partage est correct, que le partage est en ligne, et que le processus actuel est autorisé à accéder au partage.", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "Code de retour : %d", "loc.messages.LIB_ResourceFileNotExist": "Le fichier de ressources n'existe pas : %s", "loc.messages.LIB_ResourceFileAlreadySet": "Le fichier de ressources est déjà défini : %s", diff --git a/node/Strings/resources.resjson/it-IT/resources.resjson b/node/Strings/resources.resjson/it-IT/resources.resjson index 064a08727..bd97a92e8 100644 --- a/node/Strings/resources.resjson/it-IT/resources.resjson +++ b/node/Strings/resources.resjson/it-IT/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "Non è possibile creare la directory '%s'. Esiste un file in conflitto: '%s'", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Non è possibile creare la directory '%s'. La directory radice non esiste: '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "Non è possibile creare la directory '%s' perché non è possibile verificarne l'esistenza: '%s'. Se la directory è una condivisione file, verificare che il nome della condivisione sia corretto, che la condivisione sia online e che il processo corrente sia autorizzato ad accedervi.", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "Codice restituito: %d", "loc.messages.LIB_ResourceFileNotExist": "Il file di risorse non esiste: %s", "loc.messages.LIB_ResourceFileAlreadySet": "Il file di risorse è già stato impostato su %s", diff --git a/node/Strings/resources.resjson/ja-jp/resources.resjson b/node/Strings/resources.resjson/ja-jp/resources.resjson index 7532d97c5..18bbd0d29 100644 --- a/node/Strings/resources.resjson/ja-jp/resources.resjson +++ b/node/Strings/resources.resjson/ja-jp/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "ディレクトリ '%s' を作成できません。競合するファイルが存在します: '%s'", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "ディレクトリ '%s' を作成できません。ルート ディレクトリが存在しません: '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "ディレクトリ '%s' を作成できません。ディレクトリが存在することを確認できません: '%s'。ディレクトリがファイル共有である場合、その共有名が正しいこと、その共有がオンラインであること、そして現在のプロセスにその共有へのアクセス許可があることをご確認ください。", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "リターン コード: %d", "loc.messages.LIB_ResourceFileNotExist": "リソース ファイルが存在しません: %s", "loc.messages.LIB_ResourceFileAlreadySet": "リソース ファイルは既に %s に設定されています", @@ -19,7 +20,7 @@ "loc.messages.LIB_EndpointDataNotExist": "エンドポイントのデータ パラメーター %s がありません: %s", "loc.messages.LIB_EndpointAuthNotExist": "エンドポイントの認証データがありません: %s", "loc.messages.LIB_InvalidEndpointAuth": "エンドポイントの認証が無効です: %s", - "loc.messages.LIB_InvalidSecureFilesInput": "セキュリティ保護された無効なファイル入力: %s", + "loc.messages.LIB_InvalidSecureFilesInput": "無効なセキュア ファイル入力: %s", "loc.messages.LIB_PathNotFound": "見つかりませんでした %s: %s", "loc.messages.LIB_PathHasNullByte": "パスに null バイトを含めることはできません", "loc.messages.LIB_OperationFailed": "失敗しました %s: %s", diff --git a/node/Strings/resources.resjson/ko-KR/resources.resjson b/node/Strings/resources.resjson/ko-KR/resources.resjson index 4d5dc0bb1..98f410084 100644 --- a/node/Strings/resources.resjson/ko-KR/resources.resjson +++ b/node/Strings/resources.resjson/ko-KR/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "'%s' 디렉터리를 만들 수 없습니다. 충돌하는 파일 '%s'이(가) 있습니다.", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "'%s' 디렉터리를 만들 수 없습니다. 루트 디렉터리 '%s'이(가) 없습니다.", "loc.messages.LIB_MkdirFailedInvalidShare": "'%s' 디렉터리를 만들 수 없습니다. '%s' 디렉터리가 있는지 확인할 수 없습니다. 디렉터리가 파일 공유인 경우 공유 이름이 올바르고, 공유가 온라인 상태이며, 현재 프로세스에 공유에 액세스할 수 있는 권한이 있는지 확인하세요.", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "반환 코드: %d", "loc.messages.LIB_ResourceFileNotExist": "리소스 파일이 없음: %s", "loc.messages.LIB_ResourceFileAlreadySet": "리소스 파일이 이미 다음으로 설정됨: %s", diff --git a/node/Strings/resources.resjson/ru-RU/resources.resjson b/node/Strings/resources.resjson/ru-RU/resources.resjson index d2ae77920..2949128f1 100644 --- a/node/Strings/resources.resjson/ru-RU/resources.resjson +++ b/node/Strings/resources.resjson/ru-RU/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "Не удается создать каталог \"%s\". Существует конфликтующий файл: \"%s\"", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Не удалось создать каталог \"%s\". Корневой каталог не существует: \"%s\"", "loc.messages.LIB_MkdirFailedInvalidShare": "Не удалось создать каталог \"%s\". Не удалось проверить, существует ли каталог \"%s\". Если каталог является файловым ресурсом, убедитесь, что имя ресурса указано правильно, он работает и текущий процесс имеет разрешение на доступ к нему.", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "Код возврата: %d", "loc.messages.LIB_ResourceFileNotExist": "Файл ресурсов не существует: %s.", "loc.messages.LIB_ResourceFileAlreadySet": "Файл ресурсов уже задан: %s.", @@ -19,7 +20,7 @@ "loc.messages.LIB_EndpointDataNotExist": "Отсутствует параметр %s данных конечной точки: %s", "loc.messages.LIB_EndpointAuthNotExist": "Отсутствуют данные для проверки подлинности конечной точки: %s", "loc.messages.LIB_InvalidEndpointAuth": "Недопустимая проверка подлинности конечной точки: %s.", - "loc.messages.LIB_InvalidSecureFilesInput": "Недопустимые входные данные защищенного файла: %s", + "loc.messages.LIB_InvalidSecureFilesInput": "Недопустимые входные данные защитного файла: %s", "loc.messages.LIB_PathNotFound": "Не найден %s: %s.", "loc.messages.LIB_PathHasNullByte": "Путь не может содержать пустые байты.", "loc.messages.LIB_OperationFailed": "Сбой %s: %s.", diff --git a/node/Strings/resources.resjson/zh-CN/resources.resjson b/node/Strings/resources.resjson/zh-CN/resources.resjson index 6afdcc58d..6ae8cad0e 100644 --- a/node/Strings/resources.resjson/zh-CN/resources.resjson +++ b/node/Strings/resources.resjson/zh-CN/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "无法创建目录“%s”。存在冲突文件:“%s”", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "无法创建目录“%s”。根目录不存在:“%s”", "loc.messages.LIB_MkdirFailedInvalidShare": "无法创建目录“%s”。无法验证“%s”目录是否存在。如果目录是文件共享,请验证共享名称是否正确、共享是否已联机以及当前进程是否有权访问该共享。", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "返回代码: %d", "loc.messages.LIB_ResourceFileNotExist": "资源文件不存在: %s", "loc.messages.LIB_ResourceFileAlreadySet": "资源文件已被设置为 %s", diff --git a/node/Strings/resources.resjson/zh-TW/resources.resjson b/node/Strings/resources.resjson/zh-TW/resources.resjson index 378f5574c..bd12064ec 100644 --- a/node/Strings/resources.resjson/zh-TW/resources.resjson +++ b/node/Strings/resources.resjson/zh-TW/resources.resjson @@ -5,6 +5,7 @@ "loc.messages.LIB_MkdirFailedFileExists": "無法建立目錄 '%s'。存在衝突的檔案: '%s'", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "無法建立目錄 '%s'。根目錄不存在: '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "無法建立目錄 '%s'。無法驗證目錄是否存在: '%s'。如果目錄是檔案共用,請驗證共用名稱正確、共用在線上,而且目前的流程具有存取共用的權限。", + "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", "loc.messages.LIB_ReturnCode": "傳回程式碼: %d", "loc.messages.LIB_ResourceFileNotExist": "資源檔案不存在: %s", "loc.messages.LIB_ResourceFileAlreadySet": "資源檔案已設定至: %s", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/de-de/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/de-de/resources.resjson index ec97e9b64..248b674d6 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/de-de/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/de-de/resources.resjson @@ -1,4 +1,5 @@ { + "loc.messages.PSLIB_AgentVersion0Required": "Agentversion {0} oder höher ist erforderlich.", "loc.messages.PSLIB_ContainerPathNotFound0": "Der Containerpfad wurde nicht gefunden: \"{0}\".", "loc.messages.PSLIB_EndpointAuth0": "\"{0}\"-Dienstendpunkt-Anmeldeinformationen", "loc.messages.PSLIB_EndpointUrl0": "\"{0}\"-Dienstendpunkt-URL", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/es-es/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/es-es/resources.resjson index d09d96cc6..b79ac21aa 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/es-es/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/es-es/resources.resjson @@ -1,4 +1,5 @@ { + "loc.messages.PSLIB_AgentVersion0Required": "Se require la versión {0} o posterior del agente.", "loc.messages.PSLIB_ContainerPathNotFound0": "No se encuentra la ruta de acceso del contenedor: '{0}'", "loc.messages.PSLIB_EndpointAuth0": "Credenciales del punto de conexión de servicio '{0}'", "loc.messages.PSLIB_EndpointUrl0": "URL del punto de conexión de servicio '{0}'", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/fr-fr/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/fr-fr/resources.resjson index 6606ddda5..dc2da0521 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/fr-fr/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/fr-fr/resources.resjson @@ -1,4 +1,5 @@ { + "loc.messages.PSLIB_AgentVersion0Required": "L'agent version {0} (ou une version ultérieure) est obligatoire.", "loc.messages.PSLIB_ContainerPathNotFound0": "Le chemin du conteneur est introuvable : '{0}'", "loc.messages.PSLIB_EndpointAuth0": "Informations d'identification du point de terminaison de service '{0}'", "loc.messages.PSLIB_EndpointUrl0": "URL du point de terminaison de service '{0}'", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson index 0b54d99aa..77bea5360 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson @@ -1,4 +1,5 @@ { + "loc.messages.PSLIB_AgentVersion0Required": "È richiesta la versione dell'agente {0} o superiore.", "loc.messages.PSLIB_ContainerPathNotFound0": "Percorso del contenitore non trovato: '{0}'", "loc.messages.PSLIB_EndpointAuth0": "Credenziali dell'endpoint servizio '{0}'", "loc.messages.PSLIB_EndpointUrl0": "URL dell'endpoint servizio '{0}'", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/ja-jp/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/ja-jp/resources.resjson index b0e2bdfc3..9f2f9feae 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/ja-jp/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/ja-jp/resources.resjson @@ -1,4 +1,5 @@ { + "loc.messages.PSLIB_AgentVersion0Required": "バージョン {0} 以降のエージェントが必要です。", "loc.messages.PSLIB_ContainerPathNotFound0": "コンテナーのパスが見つかりません: '{0}'", "loc.messages.PSLIB_EndpointAuth0": "'{0}' サービス エンドポイントの資格情報", "loc.messages.PSLIB_EndpointUrl0": "'{0}' サービス エンドポイントの URL", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson index 60df35b60..17e0d28a8 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson @@ -1,4 +1,5 @@ { + "loc.messages.PSLIB_AgentVersion0Required": "에이전트 버전 {0} 이상이 필요합니다.", "loc.messages.PSLIB_ContainerPathNotFound0": "컨테이너 경로를 찾을 수 없음: '{0}'", "loc.messages.PSLIB_EndpointAuth0": "'{0}' 서비스 끝점 자격 증명", "loc.messages.PSLIB_EndpointUrl0": "'{0}' 서비스 끝점 URL", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson index 2d422087d..b9c5a2709 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson @@ -1,4 +1,5 @@ { + "loc.messages.PSLIB_AgentVersion0Required": "Требуется версия агента {0} или более поздняя.", "loc.messages.PSLIB_ContainerPathNotFound0": "Путь к контейнеру не найден: \"{0}\".", "loc.messages.PSLIB_EndpointAuth0": "Учетные данные конечной точки службы \"{0}\"", "loc.messages.PSLIB_EndpointUrl0": "URL-адрес конечной точки службы \"{0}\"", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson index 80adce28a..49d824b50 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson @@ -1,4 +1,5 @@ { + "loc.messages.PSLIB_AgentVersion0Required": "需要代理版本 {0} 或更高版本。", "loc.messages.PSLIB_ContainerPathNotFound0": "找不到容器路径:“{0}”", "loc.messages.PSLIB_EndpointAuth0": "“{0}”服务终结点凭据", "loc.messages.PSLIB_EndpointUrl0": "“{0}”服务终结点 URL", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson index d6a041cfa..7cbf22e17 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson @@ -1,4 +1,5 @@ { + "loc.messages.PSLIB_AgentVersion0Required": "需要代理程式版本 {0} 或更新的版本。", "loc.messages.PSLIB_ContainerPathNotFound0": "找不到容器路徑: '{0}'", "loc.messages.PSLIB_EndpointAuth0": "'{0}' 服務端點認證", "loc.messages.PSLIB_EndpointUrl0": "'{0}' 服務端點 URL", From 00f310feab2b7a678b1ea53a50d546ac16dc4758 Mon Sep 17 00:00:00 2001 From: Eric Sciple Date: Thu, 1 Mar 2018 11:00:37 -0500 Subject: [PATCH 030/259] add trigger into yaml --- .vsts-ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vsts-ci.yml b/.vsts-ci.yml index efb5fcc24..c66ea78f7 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -1,5 +1,8 @@ -phases: +trigger: +- master +- features/* +phases: ################################################# - phase: build_on_windows ################################################# From 72eb91022fa9c61185a71ce262a5af812f506abd Mon Sep 17 00:00:00 2001 From: Eric Sciple Date: Thu, 1 Mar 2018 11:09:40 -0500 Subject: [PATCH 031/259] change queues --- .vsts-ci.yml | 19 +- node/test/scripts/match-input-exe.cs | 59 +++++++ node/test/scripts/print-output-exe.cs | 39 +++++ node/test/toolrunnertests.ts | 211 +++++++++++++++-------- powershell/Tests/lib/Initialize-Test.ps1 | 6 + powershell/Tests/lib/Start-Engine.ps1 | 30 +++- 6 files changed, 275 insertions(+), 89 deletions(-) create mode 100644 node/test/scripts/match-input-exe.cs create mode 100644 node/test/scripts/print-output-exe.cs diff --git a/.vsts-ci.yml b/.vsts-ci.yml index c66ea78f7..215276b98 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -4,13 +4,10 @@ trigger: phases: ################################################# -- phase: build_on_windows +- phase: windows ################################################# - displayName: (vsts-task-lib) Windows - queue: - #name: Hosted - name: buildDevs - demands: agent.os -equals Windows_NT + condition: and(succeeded(), eq(variables.os, 'Windows_NT')) + queue: Hosted VS2017 steps: ################################################################################ @@ -62,9 +59,9 @@ phases: workingDirectory: powershell ################################################# -- phase: build_on_linux +- phase: linux ################################################# - displayName: (vsts-task-lib) linux + condition: and(succeeded(), eq(variables.os, 'Linux')) queue: Hosted Linux Preview steps: @@ -102,10 +99,10 @@ phases: workingDirectory: node ################################################# -- phase: build_on_osx +- phase: macOS ################################################# - displayName: (vsts-task-lib) osx - queue: Hosted Mac Internal + condition: and(succeeded(), eq(variables.os, 'Darwin')) + queue: Hosted macOS Preview steps: ################################################################################ diff --git a/node/test/scripts/match-input-exe.cs b/node/test/scripts/match-input-exe.cs new file mode 100644 index 000000000..30ccf8655 --- /dev/null +++ b/node/test/scripts/match-input-exe.cs @@ -0,0 +1,59 @@ +using System; +namespace MatchInput +{ + public static class Program + { + public static Int32 Main(String[] args) + { + // Validate at least two args + if (args.Length < 2) + { + throw new Exception("Expected arguments EXIT_CODE MATCH_VALUE [ERROR]"); + } + + // Validate exit code arg + Int32 code; + if (!Int32.TryParse(args[0] ?? String.Empty, out code)) + { + throw new Exception("Expected EXIT_CODE to be an integer"); + } + + // Validate match value arg + String match = args[1]; + if (String.IsNullOrEmpty(match)) + { + throw new Exception("Expected MATCH_VALUE to not be empty"); + } + + // Optional error message arg + String error = args.Length >= 3 ? args[2] : null; + + String line; + while (true) + { + // Read STDIN + line = Console.ReadLine(); + + // Null indicates end of stream + if (line == null) + { + break; + } + + // Print matches + if (line.IndexOf(match) >= 0) + { + Console.WriteLine(line); + } + } + + // Print optional error message + if (!String.IsNullOrEmpty(error)) + { + Console.Error.WriteLine(error); + } + + return code; + } + } +} diff --git a/node/test/scripts/print-output-exe.cs b/node/test/scripts/print-output-exe.cs new file mode 100644 index 000000000..59755352d --- /dev/null +++ b/node/test/scripts/print-output-exe.cs @@ -0,0 +1,39 @@ +using System; +namespace PrintOutput +{ + public static class Program + { + public static Int32 Main(String[] args) + { + // Validate at least two args + if (args.Length < 2) + { + throw new Exception("Expected arguments EXIT_CODE VALUE [...VALUE]"); + } + + // Validate exit code arg + Int32 code; + if (!Int32.TryParse(args[0] ?? String.Empty, out code)) + { + throw new Exception("Expected EXIT_CODE to be an integer"); + } + + // Validate value args + for (Int32 i = 1 ; i < args.Length ; i++) + { + if (String.IsNullOrEmpty(args[i])) + { + throw new Exception("Expected VALUE to not be empty"); + } + } + + // Write values + for (Int32 i = 1 ; i < args.Length ; i++) + { + Console.WriteLine(args[i]); + } + + return code; + } + } +} diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index f930fc113..561793351 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -33,7 +33,7 @@ describe('Toolrunner Tests', function () { it('ExecSync convenience with stdout', function (done) { - this.timeout(1000); + this.timeout(10000); var _testExecOptions = { cwd: __dirname, @@ -58,7 +58,7 @@ describe('Toolrunner Tests', function () { done(); }) it('ExecSync with stdout', function (done) { - this.timeout(1000); + this.timeout(10000); var _testExecOptions = { cwd: __dirname, @@ -90,7 +90,7 @@ describe('Toolrunner Tests', function () { done(); }) it('ExecSync fails with rc 1 and stderr', function (done) { - this.timeout(1000); + this.timeout(10000); var _testExecOptions = { cwd: __dirname, @@ -120,7 +120,7 @@ describe('Toolrunner Tests', function () { done(); }) it('Exec convenience with stdout', function (done) { - this.timeout(1000); + this.timeout(10000); var _testExecOptions = { cwd: __dirname, @@ -154,7 +154,7 @@ describe('Toolrunner Tests', function () { } }) it('ToolRunner writes debug', function (done) { - this.timeout(1000); + this.timeout(10000); var stdStream = testutil.createStringStream(); tl.setStdStream(stdStream); @@ -203,7 +203,7 @@ describe('Toolrunner Tests', function () { } }) it('Execs with stdout', function (done) { - this.timeout(1000); + this.timeout(10000); var _testExecOptions = { cwd: __dirname, @@ -255,7 +255,7 @@ describe('Toolrunner Tests', function () { } }) it('Fails on return code 1 with stderr', function (done) { - this.timeout(1000); + this.timeout(10000); var _testExecOptions = { cwd: __dirname, @@ -329,7 +329,7 @@ describe('Toolrunner Tests', function () { } }) it('Succeeds on stderr by default', function (done) { - this.timeout(2000); + this.timeout(10000); var scriptPath = path.join(__dirname, 'scripts', 'stderroutput.js'); var ls = tl.tool(tl.which('node', true)); @@ -354,7 +354,7 @@ describe('Toolrunner Tests', function () { }) }) it('Fails on stderr if specified', function (done) { - this.timeout(1000); + this.timeout(10000); var scriptPath = path.join(__dirname, 'scripts', 'stderroutput.js'); var node = tl.tool(tl.which('node', true)) @@ -395,8 +395,68 @@ describe('Toolrunner Tests', function () { done(err); }); }) + // function to compile a .NET program that prints lines. + // the helper program is used on Windows to validate piping output between tools. + let compileOutputExe = (): string => { + let directory = path.join(testutil.getTestTemp(), 'print-output-exe'); + tl.mkdirP(directory); + let exePath = path.join(directory, 'print-output.exe'); + + // short-circuit if already compiled + try { + fs.statSync(exePath); + return exePath; + } + catch (err) { + if (err.code != 'ENOENT') { + throw err; + } + } + + let sourceFile = path.join(__dirname, 'scripts', 'print-output-exe.cs'); + let cscPath = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe'; + fs.statSync(cscPath); + child_process.execFileSync( + cscPath, + [ + '/target:exe', + `/out:${exePath}`, + sourceFile + ]); + return exePath; + } + // function to compile a .NET program that matches input lines. + // the helper program is used on Windows to validate piping output between tools. + let compileMatchExe = (): string => { + let directory = path.join(testutil.getTestTemp(), 'match-input-exe'); + tl.mkdirP(directory); + let exePath = path.join(directory, 'match-input.exe'); + + // short-circuit if already compiled + try { + fs.statSync(exePath); + return exePath; + } + catch (err) { + if (err.code != 'ENOENT') { + throw err; + } + } + + let sourceFile = path.join(__dirname, 'scripts', 'match-input-exe.cs'); + let cscPath = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe'; + fs.statSync(cscPath); + child_process.execFileSync( + cscPath, + [ + '/target:exe', + `/out:${exePath}`, + sourceFile + ]); + return exePath; + } it('Exec pipe output to another tool, succeeds if both tools succeed', function(done) { - this.timeout(1000); + this.timeout(20000); var _testExecOptions = { cwd: __dirname, @@ -409,21 +469,25 @@ describe('Toolrunner Tests', function () { }; if (os.platform() === 'win32') { - var find = tl.tool(tl.which('FIND', true)) - .arg('System Idle Process'); - - var tasklist = tl.tool(tl.which('tasklist', true)); - tasklist.pipeExecOutputToTool(find); + var matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('line 2'); // match value + var outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe); var output = ''; - tasklist.on('stdout', (data) => { + outputExe.on('stdout', (data) => { output += data.toString(); }); - tasklist.exec(_testExecOptions) + outputExe.exec(_testExecOptions) .then(function (code) { assert.equal(code, 0, 'return code of exec should be 0'); - assert(output && output.length > 0 && output.indexOf('System Idle Process') >= 0, 'should have emitted stdout ' + output); + assert(output && output.length > 0 && output.indexOf('line 2') >= 0, 'should have emitted stdout ' + output); done(); }) .fail(function (err) { @@ -455,7 +519,7 @@ describe('Toolrunner Tests', function () { } }) it('Exec pipe output to another tool, fails if first tool fails', function(done) { - this.timeout(1000); + this.timeout(20000); var _testExecOptions = { cwd: __dirname, @@ -468,31 +532,33 @@ describe('Toolrunner Tests', function () { }; if (os.platform() === 'win32') { - var find = tl.tool(tl.which('FIND', true)); - find.arg('System Idle Process'); - - var tasklist = tl.tool(tl.which('tasklist', true)); - tasklist.arg('bad'); - tasklist.pipeExecOutputToTool(find); + var matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('line 2'); // match value + var outputExe = tl.tool(compileOutputExe()) + .arg('1') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe); var output = ''; - tasklist.on('stdout', (data) => { + outputExe.on('stdout', (data) => { output += data.toString(); }); var succeeded = false; - tasklist.exec(_testExecOptions) + outputExe.exec(_testExecOptions) .then(function () { succeeded = true; - assert.fail('tasklist bad | findstr cmd was a bad command and it did not fail'); + assert.fail('print-output.exe | findstr "line 2" was a bad command and it did not fail'); }) .fail(function (err) { if (succeeded) { done(err); } else { - //assert(output && output.length > 0 && output.indexOf('ERROR: Invalid argument/option') >= 0, 'error output from tasklist command does not match expected. actual: ' + output); - assert(err && err.message && err.message.indexOf('tasklist.exe') >=0, 'error from tasklist is not reported'); + assert(err && err.message && err.message.indexOf('print-output.exe') >=0, 'error from print-output.exe is not reported'); done(); } }) @@ -535,7 +601,7 @@ describe('Toolrunner Tests', function () { } }) it('Exec pipe output to another tool, fails if second tool fails', function(done) { - this.timeout(1000); + this.timeout(20000); var _testExecOptions = { cwd: __dirname, @@ -548,35 +614,40 @@ describe('Toolrunner Tests', function () { }; if (os.platform() === 'win32') { - var find = tl.tool(tl.which('FIND.exe', true)); - find.arg('bad'); - - var tasklist = tl.tool(tl.which('tasklist', true)); - tasklist.pipeExecOutputToTool(find); + var matchExe = tl.tool(compileMatchExe()) + .arg('1') // exit code + .arg('line 2') // match value + .arg('some error message'); // error + var outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe); var output = ''; - tasklist.on('stdout', (data) => { + outputExe.on('stdout', (data) => { output += data.toString(); }); var errOut = ''; - tasklist.on('stderr', (data) => { + outputExe.on('stderr', (data) => { errOut += data.toString(); }); var succeeded = false; - tasklist.exec(_testExecOptions) + outputExe.exec(_testExecOptions) .then(function (code) { succeeded = true; - assert.fail('tasklist bad | find "cmd" was a bad command and it did not fail'); + assert.fail('print-output.exe 0 "line 1" "line 2" "line 3" | match-input.exe 1 "line 2" "some error message" was a bad command and it did not fail'); }) .fail(function (err) { if (succeeded) { done(err); } else { - assert(errOut && errOut.length > 0 && errOut.indexOf('FIND: Parameter format not correct') >= 0, 'error output from FIND command is expected'); - assert(err && err.message && err.message.indexOf('FIND.exe') >=0, 'error from find does not match expeced. actual: ' + err.message); + assert(errOut && errOut.length > 0 && errOut.indexOf('some error message') >= 0, 'error output from match-input.exe is expected'); + assert(err && err.message && err.message.indexOf('match-input.exe') >=0, 'error from find does not match expeced. actual: ' + err.message); done(); } }) @@ -625,7 +696,7 @@ describe('Toolrunner Tests', function () { } }) it('handles single args', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.arg('one'); @@ -635,7 +706,7 @@ describe('Toolrunner Tests', function () { done(); }) it('handles arg chaining', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.arg('one').arg('two').argIf(true, 'three').line('four five'); @@ -645,7 +716,7 @@ describe('Toolrunner Tests', function () { done(); }) it('handles padded spaces', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.arg(' one '); @@ -655,7 +726,7 @@ describe('Toolrunner Tests', function () { done(); }) it('handles basic arg string with spaces', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.line('one two'); @@ -665,7 +736,7 @@ describe('Toolrunner Tests', function () { done(); }) it('handles arg string with extra spaces', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.line('one two'); @@ -675,7 +746,7 @@ describe('Toolrunner Tests', function () { done(); }) it('handles arg string with backslash', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.line('one two\\arg'); @@ -685,7 +756,7 @@ describe('Toolrunner Tests', function () { done(); }) it('handles equals and switches', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.line('foo=bar -x'); @@ -695,7 +766,7 @@ describe('Toolrunner Tests', function () { done(); }) it('handles double quotes', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.line('foo="bar baz" -x'); @@ -705,7 +776,7 @@ describe('Toolrunner Tests', function () { done(); }) it('handles quote in double quotes', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.line('foo="bar \\" baz" -x'); @@ -715,7 +786,7 @@ describe('Toolrunner Tests', function () { done(); }) it('handles literal path', function (done) { - this.timeout(1000); + this.timeout(10000); var node = tl.tool(tl.which('node', true)); node.arg('--path').arg('/bin/working folder1'); @@ -726,7 +797,7 @@ describe('Toolrunner Tests', function () { if (process.platform != 'win32') { it('exec prints [command] (OSX/Linux)', function (done) { - this.timeout(1000); + this.timeout(10000); let bash = tl.tool(tl.which('bash')) .arg('--norc') .arg('--noprofile') @@ -793,7 +864,7 @@ describe('Toolrunner Tests', function () { // -------------------------- it('exec .exe AND verbatim args (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // the echo built-in is a good tool for this test let exePath = process.env.ComSpec; @@ -827,7 +898,7 @@ describe('Toolrunner Tests', function () { }); it('exec .exe AND arg quoting (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // the echo built-in is a good tool for this test let exePath = process.env.ComSpec; @@ -870,7 +941,7 @@ describe('Toolrunner Tests', function () { }); it('exec .exe with space AND verbatim args (Windows)', function (done) { - this.timeout(1000); + this.timeout(20000); // this test validates the quoting that tool runner adds around the tool path // when using the windowsVerbatimArguments option. otherwise the target process @@ -904,7 +975,7 @@ describe('Toolrunner Tests', function () { }); it('exec .cmd with space AND verbatim args (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // this test validates the quoting that tool runner adds around the script path. // otherwise cmd.exe will not be able to resolve the path to the script. @@ -939,7 +1010,7 @@ describe('Toolrunner Tests', function () { }); it('exec .cmd with space AND arg with space (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // this test validates the command is wrapped in quotes (i.e. cmd.exe /S /C ""). // otherwise the leading quote (around the script with space path) would be stripped @@ -974,7 +1045,7 @@ describe('Toolrunner Tests', function () { }); it('exec .cmd AND arg quoting (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // this test validates .cmd quoting rules are applied, not the default libuv rules let cmdPath = path.join(__dirname, 'scripts', 'print args cmd with spaces.cmd'); @@ -1080,7 +1151,7 @@ describe('Toolrunner Tests', function () { // ------------------------------- it('exec sync .exe AND verbatim args (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // the echo built-in is a good tool for this test let exePath = process.env.ComSpec; @@ -1105,7 +1176,7 @@ describe('Toolrunner Tests', function () { }); it('exec sync .exe AND arg quoting (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // the echo built-in is a good tool for this test let exePath = process.env.ComSpec; @@ -1139,7 +1210,7 @@ describe('Toolrunner Tests', function () { }); it('exec sync .exe with space AND verbatim args (Windows)', function (done) { - this.timeout(1000); + this.timeout(20000); // this test validates the quoting that tool runner adds around the tool path // when using the windowsVerbatimArguments option. otherwise the target process @@ -1163,7 +1234,7 @@ describe('Toolrunner Tests', function () { }); it('exec sync .cmd with space AND verbatim args (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // this test validates the quoting that tool runner adds around the script path. // otherwise cmd.exe will not be able to resolve the path to the script. @@ -1188,7 +1259,7 @@ describe('Toolrunner Tests', function () { }); it('exec sync .cmd with space AND arg with space (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // this test validates the command is wrapped in quotes (i.e. cmd.exe /S /C ""). // otherwise the leading quote (around the script with space path) would be stripped @@ -1213,7 +1284,7 @@ describe('Toolrunner Tests', function () { }); it('exec sync .cmd AND arg quoting (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // this test validates .cmd quoting rules are applied, not the default libuv rules let cmdPath = path.join(__dirname, 'scripts', 'print args cmd with spaces.cmd'); @@ -1309,7 +1380,7 @@ describe('Toolrunner Tests', function () { // ------------------------------- it('exec pipe .cmd to .exe AND arg quoting (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); let cmdPath = path.join(__dirname, 'scripts', 'print args cmd with spaces.cmd'); let cmdRunner = tl.tool(cmdPath) @@ -1346,7 +1417,7 @@ describe('Toolrunner Tests', function () { }); it('exec pipe .cmd to .exe AND verbatim args (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); let cmdPath = path.join(__dirname, 'scripts', 'print args cmd with spaces.cmd'); let cmdRunner = tl.tool(cmdPath) @@ -1387,7 +1458,7 @@ describe('Toolrunner Tests', function () { // -------------------------------------- it('_windowsQuoteCmdArg quotes .exe args (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // create a .exe file let testPath = path.join(testutil.getTestTemp(), 'which-finds-file-name'); @@ -1477,7 +1548,7 @@ describe('Toolrunner Tests', function () { }); it('_windowsQuoteCmdArg quotes .cmd args (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // create a .cmd file let testPath = path.join(testutil.getTestTemp(), 'which-finds-file-name'); @@ -1536,7 +1607,7 @@ describe('Toolrunner Tests', function () { }); it('_windowsQuoteCmdArg quotes .bat args (Windows)', function (done) { - this.timeout(1000); + this.timeout(10000); // create a .bat file let testPath = path.join(testutil.getTestTemp(), 'which-finds-file-name'); diff --git a/powershell/Tests/lib/Initialize-Test.ps1 b/powershell/Tests/lib/Initialize-Test.ps1 index f383223ab..19ddb1f44 100644 --- a/powershell/Tests/lib/Initialize-Test.ps1 +++ b/powershell/Tests/lib/Initialize-Test.ps1 @@ -14,6 +14,12 @@ Import-Module 'Microsoft.PowerShell.Utility' -Verbose:$false Write-Verbose "Importing module: Microsoft.PowerShell.Security" Import-Module 'Microsoft.PowerShell.Security' -Verbose:$false +if ($env:AGENT_TEMPDIRECTORY) { + Write-Verbose "Overriding env:TMP and env:TEMP with env:AGENT_TEMPDIRECTORY" + $env:TEMP = $env:AGENT_TEMPDIRECTORY + $env:TMP = $env:AGENT_TEMPDIRECTORY +} + Write-Verbose "Importing module: TestHelpersModule" Import-Module $PSScriptRoot\TestHelpersModule -Verbose:$false diff --git a/powershell/Tests/lib/Start-Engine.ps1 b/powershell/Tests/lib/Start-Engine.ps1 index 51bf66980..b65bfe378 100644 --- a/powershell/Tests/lib/Start-Engine.ps1 +++ b/powershell/Tests/lib/Start-Engine.ps1 @@ -57,8 +57,19 @@ function Invoke-Test { # Record the original environment variables. $originalEnv = @{ } -foreach ($envVar in (Get-ChildItem -LiteralPath env:)) { - $originalEnv[$envVar.Name] = $envVar.Value +foreach ($key in ([Environment]::GetEnvironmentVariables()).Keys) { + if (!$originalEnv.ContainsKey($key)) { + $value = [Environment]::GetEnvironmentVariable($key) + $originalEnv[$key] = "$value" + } else { + # NPM on Windows is somehow able to create a duplicate environment variable cased differently. + # For example, if the environment variable NPM_CONFIG_CACHE is defined, npm test is somehow able + # to create a duplicate variable npm_config_cache. This causes powershell to error "An item with + # the same key has already been added" when attempting to: Get-ChildItem -LiteralPath env: + Write-Host "Squashing duplicate environment variable: $key" + [Environment]::SetEnvironmentVariable($key, $null) + [Environment]::SetEnvironmentVariable($key, $originalEnv[$key]) + } } while ($true) { @@ -71,23 +82,26 @@ while ($true) { # Cleanup the environment variables. $currentMatches = @{ } - foreach ($envVar in (Get-ChildItem -LiteralPath env:)) { + foreach ($key in ([Environment]::GetEnvironmentVariables().Keys)) { + $value = [Environment]::GetEnvironmentVariable($key) + # Remove the environment variable if it is new. - if (!$originalEnv.ContainsKey($envVar.Name)) { - Remove-Item -LiteralPath $envVar.PSPath - } elseif ($originalEnv[$envVar.Name] -ceq $envVar.Value) { + if (!$originalEnv.ContainsKey($key)) { + [Environment]::SetEnvironmentVariable($key, $null) + } elseif ($originalEnv[$key] -ceq $value) { # Otherwise record it if it matches. - $currentMatches[$envVar.Name] = $envVar.Value + $currentMatches[$key] = $true } } # Add or update the environment variables that are missing or changed. foreach ($key in $originalEnv.Keys) { if (!$currentMatches.ContainsKey($key)) { - Set-Content -LiteralPath "env:$key" -Value "$($originalEnv[$key])" + [Environment]::SetEnvironmentVariable($key, $originalEnv[$key]) } } # Write a special "end-of-test" message over STDOUT. Write-Host '_END_OF_TEST_ce10a77a_' + [Console]::Out.Flush() } From 1bbd289026a76fd3e9906be47fa3e79cdf704378 Mon Sep 17 00:00:00 2001 From: Eric Sciple Date: Fri, 2 Mar 2018 12:02:36 -0500 Subject: [PATCH 032/259] Refactor compile exe helper functions --- node/test/toolrunnertests.ts | 139 ++++++++++++----------------------- 1 file changed, 48 insertions(+), 91 deletions(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 561793351..756fce2b7 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -31,7 +31,6 @@ describe('Toolrunner Tests', function () { }); - it('ExecSync convenience with stdout', function (done) { this.timeout(10000); @@ -395,66 +394,6 @@ describe('Toolrunner Tests', function () { done(err); }); }) - // function to compile a .NET program that prints lines. - // the helper program is used on Windows to validate piping output between tools. - let compileOutputExe = (): string => { - let directory = path.join(testutil.getTestTemp(), 'print-output-exe'); - tl.mkdirP(directory); - let exePath = path.join(directory, 'print-output.exe'); - - // short-circuit if already compiled - try { - fs.statSync(exePath); - return exePath; - } - catch (err) { - if (err.code != 'ENOENT') { - throw err; - } - } - - let sourceFile = path.join(__dirname, 'scripts', 'print-output-exe.cs'); - let cscPath = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe'; - fs.statSync(cscPath); - child_process.execFileSync( - cscPath, - [ - '/target:exe', - `/out:${exePath}`, - sourceFile - ]); - return exePath; - } - // function to compile a .NET program that matches input lines. - // the helper program is used on Windows to validate piping output between tools. - let compileMatchExe = (): string => { - let directory = path.join(testutil.getTestTemp(), 'match-input-exe'); - tl.mkdirP(directory); - let exePath = path.join(directory, 'match-input.exe'); - - // short-circuit if already compiled - try { - fs.statSync(exePath); - return exePath; - } - catch (err) { - if (err.code != 'ENOENT') { - throw err; - } - } - - let sourceFile = path.join(__dirname, 'scripts', 'match-input-exe.cs'); - let cscPath = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe'; - fs.statSync(cscPath); - child_process.execFileSync( - cscPath, - [ - '/target:exe', - `/out:${exePath}`, - sourceFile - ]); - return exePath; - } it('Exec pipe output to another tool, succeeds if both tools succeed', function(done) { this.timeout(20000); @@ -828,36 +767,6 @@ describe('Toolrunner Tests', function () { }); } else { // process.platform == 'win32' - // function to compile a .NET program that prints the command line args. - // the helper program is used to validate that command line args are passed correctly. - let compileArgsExe = (fileNameOnly: string): string => { - let directory = path.join(testutil.getTestTemp(), 'print-args-exe'); - tl.mkdirP(directory); - let exePath = path.join(directory, fileNameOnly); - - // short-circuit if already compiled - try { - fs.statSync(exePath); - return exePath; - } - catch (err) { - if (err.code != 'ENOENT') { - throw err; - } - } - - let sourceFile = path.join(__dirname, 'scripts', 'print-args-exe.cs'); - let cscPath = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe'; - fs.statSync(cscPath); - child_process.execFileSync( - cscPath, - [ - '/target:exe', - `/out:${exePath}`, - sourceFile - ]); - return exePath; - } // -------------------------- // exec arg tests (Windows) @@ -1623,4 +1532,52 @@ describe('Toolrunner Tests', function () { done(); }); } + + // function to compile a .NET program on Windows. + let compileExe = (sourceFileName: string, targetFileName: string): string => { + let directory = path.join(testutil.getTestTemp(), sourceFileName); + tl.mkdirP(directory); + let exePath = path.join(directory, targetFileName); + + // short-circuit if already compiled + try { + fs.statSync(exePath); + return exePath; + } + catch (err) { + if (err.code != 'ENOENT') { + throw err; + } + } + + let sourceFile = path.join(__dirname, 'scripts', sourceFileName); + let cscPath = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe'; + fs.statSync(cscPath); + child_process.execFileSync( + cscPath, + [ + '/target:exe', + `/out:${exePath}`, + sourceFile + ]); + return exePath; + } + + // function to compile a .NET program that prints the command line args. + // the helper program is used to validate that command line args are passed correctly. + let compileArgsExe = (targetFileName: string): string => { + return compileExe('print-args-exe.cs', targetFileName); + } + + // function to compile a .NET program that matches input lines. + // the helper program is used on Windows to validate piping output between tools. + let compileMatchExe = (): string => { + return compileExe('match-input-exe.cs', 'match-input.exe'); + } + + // function to compile a .NET program that prints lines. + // the helper program is used on Windows to validate piping output between tools. + let compileOutputExe = (): string => { + return compileExe('print-output-exe.cs', 'print-output.exe'); + } }); From bda1c275a054b8f444682b9672c856d8dc738300 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Tue, 6 Mar 2018 12:59:46 -0500 Subject: [PATCH 033/259] Update dependencies to prevent warnings during npm install. (#325) * Update dependencies to prevent warnings during npm install. * Remove package-lock until we fully upgrade. --- node/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package.json b/node/package.json index f6e042401..4043623a1 100644 --- a/node/package.json +++ b/node/package.json @@ -25,7 +25,7 @@ }, "homepage": "https://github.com/Microsoft/vsts-task-lib", "dependencies": { - "minimatch": "^3.0.0", + "minimatch": "3.0.4", "mockery": "^1.7.0", "uuid": "^3.0.1", "q": "^1.1.2", @@ -33,7 +33,7 @@ "shelljs": "^0.3.0" }, "devDependencies": { - "mocha": "^2.2.5", + "mocha": "5.0.2", "sync-request": "3.0.1", "typescript": "1.8.7" } From 0811d3506cba93d892741458a02145cfa2dfed66 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Tue, 6 Mar 2018 13:35:24 -0500 Subject: [PATCH 034/259] Update dependency versions. (#327) --- node/package.json | 10 +++++----- node/test/dirtests.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/node/package.json b/node/package.json index 4043623a1..ba95efdd0 100644 --- a/node/package.json +++ b/node/package.json @@ -26,15 +26,15 @@ "homepage": "https://github.com/Microsoft/vsts-task-lib", "dependencies": { "minimatch": "3.0.4", - "mockery": "^1.7.0", - "uuid": "^3.0.1", - "q": "^1.1.2", - "semver": "^5.1.0", + "mockery": "2.1.0", + "uuid": "3.2.1", + "q": "1.5.1", + "semver": "5.5.0", "shelljs": "^0.3.0" }, "devDependencies": { "mocha": "5.0.2", - "sync-request": "3.0.1", + "sync-request": "6.0.0", "typescript": "1.8.7" } } diff --git a/node/test/dirtests.ts b/node/test/dirtests.ts index 40544c547..ff8e007fa 100644 --- a/node/test/dirtests.ts +++ b/node/test/dirtests.ts @@ -1107,7 +1107,7 @@ describe('Dir Operation Tests', function () { }); it('removes folder with locked file with rmRF', function (done) { - this.timeout(1000); + this.timeout(2000); var testPath = path.join(testutil.getTestTemp(), 'testFolder'); tl.mkdirP(testPath); From 216fed267eeebc4da86039ed7f346befbbec9076 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Tue, 13 Mar 2018 09:30:27 -0400 Subject: [PATCH 035/259] Update dependency versions. (#328) From 22e9d3341f2d099b69ef46690e1a4da37106f15e Mon Sep 17 00:00:00 2001 From: madhurig Date: Tue, 13 Mar 2018 20:22:21 -0400 Subject: [PATCH 036/259] Add option to pipe to file and another tool --- node/toolrunner.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 14c8f60b7..48629ce6f 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -7,6 +7,7 @@ import child = require('child_process'); import stream = require('stream'); import im = require('./internal'); import tcm = require('./taskcommand'); +import fs = require('fs'); /** * Interface for exec options @@ -73,6 +74,7 @@ export class ToolRunner extends events.EventEmitter { private toolPath: string; private args: string[]; private pipeOutputToTool: ToolRunner; + private pipeOutputToFile: string; private _debug(message) { this.emit('debug', message); @@ -528,10 +530,12 @@ export class ToolRunner extends events.EventEmitter { /** * Pipe output of exec() to another tool * @param tool + * @param file optional filename to additionally stream the output to. * @returns {ToolRunner} */ - public pipeExecOutputToTool(tool: ToolRunner) : ToolRunner { + public pipeExecOutputToTool(tool: ToolRunner, file?: string) : ToolRunner { this.pipeOutputToTool = tool; + this.pipeOutputToFile = file; return this; } @@ -584,9 +588,17 @@ export class ToolRunner extends events.EventEmitter { this.pipeOutputToTool._getSpawnArgs(options), this.pipeOutputToTool._getSpawnOptions(options)); + let fileStream: fs.WriteStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; + if (fileStream) { + fileStream.write(this._getCommandString(options) + os.EOL); + } + //pipe stdout of first tool to stdin of second tool cpFirst.stdout.on('data', (data: Buffer) => { try { + if (fileStream) { + fileStream.write(data.toString()); + } cp.stdin.write(data); } catch (err) { this._debug('Failed to pipe output of ' + toolPathFirst + ' to ' + toolPath); @@ -594,6 +606,9 @@ export class ToolRunner extends events.EventEmitter { } }); cpFirst.stderr.on('data', (data: Buffer) => { + if (fileStream) { + fileStream.write(data.toString()); + } successFirst = !options.failOnStdErr; if (!options.silent) { var s = options.failOnStdErr ? options.errStream : options.outStream; @@ -601,10 +616,20 @@ export class ToolRunner extends events.EventEmitter { } }); cpFirst.on('error', (err) => { + if (fileStream) { + fileStream.on('finish', () => { + fileStream.close(); + }); + } cp.stdin.end(); defer.reject(new Error(toolPathFirst + ' failed. ' + err.message)); }); cpFirst.on('close', (code, signal) => { + if (fileStream) { + fileStream.on('finish', () => { + fileStream.close(); + }); + } if (code != 0 && !options.ignoreReturnCode) { successFirst = false; returnCodeFirst = code; From 54cba4e0bda1634ecae7f27f18de87abf209694f Mon Sep 17 00:00:00 2001 From: Brian Cristante <33549821+brcrista@users.noreply.github.com> Date: Wed, 21 Mar 2018 15:26:07 -0400 Subject: [PATCH 037/259] Update TaskLibAnswers to have all supported commands (#332) * Add supported commands to TaskLibAnswers * Remove stray semicolon * Typescript -> TypeScript --- README.md | 4 ++-- node/README.md | 6 +++--- node/mock-answer.ts | 6 ++++-- node/mock-toolrunner.ts | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1b5028b6a..00f15452e 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ Reference examples of our in the box tasks [are here](https://github.com/Microso * __Consistent API:__ The TypeScript and PowerShell libs are largely consistent. They only differ where it makes sense (being true to the platform). * __Tracing for free:__ Tracing has been built-in to many of the commands. Use the SDK and get some debug tracing for free. -## Typescript Tasks +## TypeScript Tasks -Cross platform tasks are written in Typescript. It is the preferred way to write tasks once. +Cross platform tasks are written in TypeScript. It is the preferred way to write tasks once. [![NPM version][npm-lib-image]][npm-lib-url] ![VSTS](https://mseng.visualstudio.com/DefaultCollection/_apis/public/build/definitions/b924d696-3eae-4116-8443-9a18392d8544/2553/badge) diff --git a/node/README.md b/node/README.md index 36382cee4..5112fc774 100644 --- a/node/README.md +++ b/node/README.md @@ -7,15 +7,15 @@ Libraries for writing [Visual Studio Team Services](https://www.visualstudio.com Reference examples of our in the box tasks [are here](https://github.com/Microsoft/vsts-tasks) -## Typescript Tasks +## TypeScript Tasks -Cross platform tasks are written in Typescript. It is the preferred way to write tasks once. +Cross platform tasks are written in TypeScript. It is the preferred way to write tasks once. [![NPM version][npm-lib-image]][npm-lib-url] Step by Step: [Create Task](docs/stepbystep.md) -Documentation: [Typescript API](docs/vsts-task-lib.md), [task JSON schema](https://aka.ms/vsts-tasks.schema.json) +Documentation: [TypeScript API](docs/vsts-task-lib.md), [task JSON schema](https://aka.ms/vsts-tasks.schema.json) Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/minagent.md), [Proxy](docs/proxy.md), [Certificate](docs/cert.md) diff --git a/node/mock-answer.ts b/node/mock-answer.ts index 250a2e002..845fa78f6 100644 --- a/node/mock-answer.ts +++ b/node/mock-answer.ts @@ -8,13 +8,15 @@ export interface TaskLibAnswerExecResult { } export interface TaskLibAnswers { - which?: { [key: string]: string; }, - exec?: { [ key: string]: TaskLibAnswerExecResult }, checkPath?: { [key: string]: boolean }, + cwd?: { [key: string]: string }, + exec?: { [ key: string]: TaskLibAnswerExecResult }, exist?: { [key: string]: boolean }, find?: { [key: string]: string[] }, findMatch?: { [key: string]: string[] }, + ls?: { [key: string]: string }, rmRF?: { [key: string]: { success: boolean } }, + which?: { [key: string]: string; }, } export class MockAnswers { diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 45d7f8cf8..0c9463dc5 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -46,7 +46,7 @@ export interface IExecSyncResult { export function debug(message) { // do nothing, overridden -}; +} export class ToolRunner extends events.EventEmitter { constructor(toolPath) { From 4c75ff7c942d6ddc62a18360bc3089a2763f981b Mon Sep 17 00:00:00 2001 From: madhurig Date: Fri, 23 Mar 2018 12:02:43 -0400 Subject: [PATCH 038/259] PR feedback, add tests --- node/test/toolrunnertests.ts | 271 ++++++++++++++++++++++++++++++++++- node/toolrunner.ts | 9 +- 2 files changed, 267 insertions(+), 13 deletions(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 756fce2b7..93ec41a84 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -394,7 +394,7 @@ describe('Toolrunner Tests', function () { done(err); }); }) - it('Exec pipe output to another tool, succeeds if both tools succeed', function(done) { + it('Exec pipe output to another tool, succeeds if both tools succeed', function (done) { this.timeout(20000); var _testExecOptions = { @@ -457,7 +457,7 @@ describe('Toolrunner Tests', function () { }); } }) - it('Exec pipe output to another tool, fails if first tool fails', function(done) { + it('Exec pipe output to another tool, fails if first tool fails', function (done) { this.timeout(20000); var _testExecOptions = { @@ -497,7 +497,7 @@ describe('Toolrunner Tests', function () { done(err); } else { - assert(err && err.message && err.message.indexOf('print-output.exe') >=0, 'error from print-output.exe is not reported'); + assert(err && err.message && err.message.indexOf('print-output.exe') >= 0, 'error from print-output.exe is not reported'); done(); } }) @@ -530,7 +530,7 @@ describe('Toolrunner Tests', function () { } else { //assert(output && output.length > 0 && output.indexOf('ps: illegal option') >= 0, `error output "ps: illegal option" is expected. actual "${output}"`); - assert(err && err.message && err.message.indexOf('/bin/ps') >=0, 'error from ps is not reported'); + assert(err && err.message && err.message.indexOf('/bin/ps') >= 0, 'error from ps is not reported'); done(); } }) @@ -539,7 +539,7 @@ describe('Toolrunner Tests', function () { }) } }) - it('Exec pipe output to another tool, fails if second tool fails', function(done) { + it('Exec pipe output to another tool, fails if second tool fails', function (done) { this.timeout(20000); var _testExecOptions = { @@ -586,7 +586,7 @@ describe('Toolrunner Tests', function () { } else { assert(errOut && errOut.length > 0 && errOut.indexOf('some error message') >= 0, 'error output from match-input.exe is expected'); - assert(err && err.message && err.message.indexOf('match-input.exe') >=0, 'error from find does not match expeced. actual: ' + err.message); + assert(err && err.message && err.message.indexOf('match-input.exe') >= 0, 'error from find does not match expeced. actual: ' + err.message); done(); } }) @@ -634,6 +634,263 @@ describe('Toolrunner Tests', function () { }); } }) + it('Exec pipe output to file and another tool, succeeds if both tools succeed', function (done) { + this.timeout(20000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + const testFile = path.join(testutil.getTestTemp(), 'BothToolsSucceed.log'); + + if (os.platform() === 'win32') { + var matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('line 2'); // match value + var outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe, testFile); + + var output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + outputExe.exec(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('line 2') >= 0, 'should have emitted stdout ' + output); + assert(fs.existsSync(testFile), 'Log of first tool output is created when both tools succeed'); + assert(fs.readFileSync(testFile).indexOf('line 2') >= 0, 'Log file of first toool should have stdout from first tool') + done(); + }) + .fail(function (err) { + done(err); + }); + } + else { + var grep = tl.tool(tl.which('grep', true)); + grep.arg('node'); + + var ps = tl.tool(tl.which('ps', true)); + ps.arg('ax'); + ps.pipeExecOutputToTool(grep, testFile); + + var output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + ps.exec(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('node') >= 0, 'should have emitted stdout ' + output); + assert(fs.existsSync(testFile), 'Log of first tool output is created when both tools succeed'); + assert(fs.readFileSync(testFile).indexOf('PID') >= 0, 'Log of first tool should have stdout from first tool'); + done(); + }) + .fail(function (err) { + done(err); + }); + } + }) + it('Exec pipe output to another tool, fails if first tool fails', function (done) { + this.timeout(20000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + const testFile = path.join(testutil.getTestTemp(), 'FirstToolFails.log'); + + if (os.platform() === 'win32') { + var matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('line 2'); // match value + var outputExe = tl.tool(compileOutputExe()) + .arg('1') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe, testFile); + + var output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + var succeeded = false; + outputExe.exec(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('print-output.exe | findstr "line 2" was a bad command and it did not fail'); + }) + .fail(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err && err.message && err.message.indexOf('print-output.exe') >= 0, 'error from print-output.exe is not reported'); + assert(fs.existsSync(testFile), 'Log of first tool output is created when first tool fails'); + assert(fs.readFileSync(testFile).indexOf('line 3') >= 0, 'Error from first tool should be written to log file'); + done(); + } + }) + .fail(function (err) { + done(err); + }); + } + else { + var grep = tl.tool(tl.which('grep', true)); + grep.arg('ssh'); + + var ps = tl.tool(tl.which('ps', true)); + ps.arg('bad'); + ps.pipeExecOutputToTool(grep, testFile); + + var output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + var succeeded = false; + ps.exec(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('ps bad | grep ssh was a bad command and it did not fail'); + }) + .fail(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err && err.message && err.message.indexOf('/bin/ps') >= 0, 'error from ps is not reported'); + assert(fs.existsSync(testFile), 'Log of first tool output is created when first tool fails'); + assert(fs.readFileSync(testFile).indexOf('illegal option') >= 0, 'error from first tool should be written to log file'); + done(); + } + }) + .fail(function (err) { + done(err); + }) + } + }) + it('Exec pipe output to another tool, fails if second tool fails', function (done) { + this.timeout(20000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + const testFile = path.join(testutil.getTestTemp(), 'SecondToolFails.log'); + + if (os.platform() === 'win32') { + var matchExe = tl.tool(compileMatchExe()) + .arg('1') // exit code + .arg('line 2') // match value + .arg('some error message'); // error + var outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe, testFile); + + var output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + var errOut = ''; + outputExe.on('stderr', (data) => { + errOut += data.toString(); + }); + + var succeeded = false; + outputExe.exec(_testExecOptions) + .then(function (code) { + succeeded = true; + assert.fail('print-output.exe 0 "line 1" "line 2" "line 3" | match-input.exe 1 "line 2" "some error message" was a bad command and it did not fail'); + }) + .fail(function (err) { + if (succeeded) { + done(err); + } + else { + assert(errOut && errOut.length > 0 && errOut.indexOf('some error message') >= 0, 'error output from match-input.exe is expected'); + assert(err && err.message && err.message.indexOf('match-input.exe') >= 0, 'error from find does not match expeced. actual: ' + err.message); + assert(fs.existsSync(testFile), 'Log of first tool output is created when second tool fails'); + assert(fs.readFileSync(testFile).indexOf('some error message') < 0, 'error from second tool should not be in the log for first tool'); + done(); + } + }) + .fail(function (err) { + done(err); + }); + } + else { + var grep = tl.tool(tl.which('grep', true)); + grep.arg('--?'); + + var ps = tl.tool(tl.which('ps', true)); + ps.arg('ax'); + ps.pipeExecOutputToTool(grep, testFile); + + var output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + var errOut = ''; + ps.on('stderr', (data) => { + errOut += data.toString(); + }) + + var succeeded = false; + ps.exec(_testExecOptions) + .then(function (code) { + succeeded = true; + assert.fail('ps ax | grep --? was a bad command and it did not fail'); + }) + .fail(function (err) { + if (succeeded) { + done(err); + } + else { + assert(errOut && errOut.length > 0 && errOut.indexOf('grep: unrecognized option') >= 0, 'error output from ps command is expected'); + // grep is /bin/grep on Linux and /usr/bin/grep on OSX + assert(err && err.message && err.message.match(/\/[usr\/]?bin\/grep/), 'error from grep is not reported. actual: ' + err.message); + assert(fs.existsSync(testFile), 'Log of first tool output is created when second tool fails'); + assert(fs.readFileSync(testFile).indexOf('unrecognized option') < 0, 'error from second tool should not be in the first tool log file'); + done(); + } + }) + .fail(function (err) { + done(err); + }); + } + }) it('handles single args', function (done) { this.timeout(10000); @@ -653,7 +910,7 @@ describe('Toolrunner Tests', function () { assert.equal((node as any).args.length, 5, 'should have 5 args'); assert.equal((node as any).args.toString(), 'one,two,three,four,five', 'should be one,two,three,four,five'); done(); - }) + }) it('handles padded spaces', function (done) { this.timeout(10000); diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 48629ce6f..eae21d93a 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -589,15 +589,12 @@ export class ToolRunner extends events.EventEmitter { this.pipeOutputToTool._getSpawnOptions(options)); let fileStream: fs.WriteStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; - if (fileStream) { - fileStream.write(this._getCommandString(options) + os.EOL); - } - + //pipe stdout of first tool to stdin of second tool cpFirst.stdout.on('data', (data: Buffer) => { try { if (fileStream) { - fileStream.write(data.toString()); + fileStream.write(data); } cp.stdin.write(data); } catch (err) { @@ -607,7 +604,7 @@ export class ToolRunner extends events.EventEmitter { }); cpFirst.stderr.on('data', (data: Buffer) => { if (fileStream) { - fileStream.write(data.toString()); + fileStream.write(data); } successFirst = !options.failOnStdErr; if (!options.silent) { From 288a43f48f5caf5bcf8c5b786e2a73c0142a6dc6 Mon Sep 17 00:00:00 2001 From: madhurig Date: Fri, 23 Mar 2018 13:50:38 -0400 Subject: [PATCH 039/259] close filestream correctly --- node/test/toolrunnertests.ts | 18 ++++++++++++------ node/toolrunner.ts | 12 ++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 93ec41a84..dcedf507b 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -670,7 +670,8 @@ describe('Toolrunner Tests', function () { assert.equal(code, 0, 'return code of exec should be 0'); assert(output && output.length > 0 && output.indexOf('line 2') >= 0, 'should have emitted stdout ' + output); assert(fs.existsSync(testFile), 'Log of first tool output is created when both tools succeed'); - assert(fs.readFileSync(testFile).indexOf('line 2') >= 0, 'Log file of first toool should have stdout from first tool') + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('line 2') >= 0, 'Log file of first tool should have stdout from first tool: ' + fileContents); done(); }) .fail(function (err) { @@ -695,7 +696,8 @@ describe('Toolrunner Tests', function () { assert.equal(code, 0, 'return code of exec should be 0'); assert(output && output.length > 0 && output.indexOf('node') >= 0, 'should have emitted stdout ' + output); assert(fs.existsSync(testFile), 'Log of first tool output is created when both tools succeed'); - assert(fs.readFileSync(testFile).indexOf('PID') >= 0, 'Log of first tool should have stdout from first tool'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('PID') >= 0, 'Log of first tool should have stdout from first tool: ' + fileContents); done(); }) .fail(function (err) { @@ -747,7 +749,8 @@ describe('Toolrunner Tests', function () { else { assert(err && err.message && err.message.indexOf('print-output.exe') >= 0, 'error from print-output.exe is not reported'); assert(fs.existsSync(testFile), 'Log of first tool output is created when first tool fails'); - assert(fs.readFileSync(testFile).indexOf('line 3') >= 0, 'Error from first tool should be written to log file'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('line 3') >= 0, 'Error from first tool should be written to log file: ' + fileContents); done(); } }) @@ -781,7 +784,8 @@ describe('Toolrunner Tests', function () { else { assert(err && err.message && err.message.indexOf('/bin/ps') >= 0, 'error from ps is not reported'); assert(fs.existsSync(testFile), 'Log of first tool output is created when first tool fails'); - assert(fs.readFileSync(testFile).indexOf('illegal option') >= 0, 'error from first tool should be written to log file'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('illegal option') >= 0, 'error from first tool should be written to log file: ' + fileContents); done(); } }) @@ -841,7 +845,8 @@ describe('Toolrunner Tests', function () { assert(errOut && errOut.length > 0 && errOut.indexOf('some error message') >= 0, 'error output from match-input.exe is expected'); assert(err && err.message && err.message.indexOf('match-input.exe') >= 0, 'error from find does not match expeced. actual: ' + err.message); assert(fs.existsSync(testFile), 'Log of first tool output is created when second tool fails'); - assert(fs.readFileSync(testFile).indexOf('some error message') < 0, 'error from second tool should not be in the log for first tool'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('some error message') < 0, 'error from second tool should not be in the log for first tool: ' + fileContents); done(); } }) @@ -882,7 +887,8 @@ describe('Toolrunner Tests', function () { // grep is /bin/grep on Linux and /usr/bin/grep on OSX assert(err && err.message && err.message.match(/\/[usr\/]?bin\/grep/), 'error from grep is not reported. actual: ' + err.message); assert(fs.existsSync(testFile), 'Log of first tool output is created when second tool fails'); - assert(fs.readFileSync(testFile).indexOf('unrecognized option') < 0, 'error from second tool should not be in the first tool log file'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('unrecognized option') < 0, 'error from second tool should not be in the first tool log file: ' + fileContents); done(); } }) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index eae21d93a..06933adda 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -570,7 +570,7 @@ export class ToolRunner extends events.EventEmitter { let toolPathFirst: string; let successFirst = true; let returnCodeFirst: number; - + if(this.pipeOutputToTool) { toolPath = this.pipeOutputToTool.toolPath; toolPathFirst = this.toolPath; @@ -588,7 +588,7 @@ export class ToolRunner extends events.EventEmitter { this.pipeOutputToTool._getSpawnArgs(options), this.pipeOutputToTool._getSpawnOptions(options)); - let fileStream: fs.WriteStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; + let fileStream: fs.WriteStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; //pipe stdout of first tool to stdin of second tool cpFirst.stdout.on('data', (data: Buffer) => { @@ -614,18 +614,14 @@ export class ToolRunner extends events.EventEmitter { }); cpFirst.on('error', (err) => { if (fileStream) { - fileStream.on('finish', () => { - fileStream.close(); - }); + fileStream.end(); } cp.stdin.end(); defer.reject(new Error(toolPathFirst + ' failed. ' + err.message)); }); cpFirst.on('close', (code, signal) => { if (fileStream) { - fileStream.on('finish', () => { - fileStream.close(); - }); + fileStream.end(); } if (code != 0 && !options.ignoreReturnCode) { successFirst = false; From e4fd4e8d534d6056d065d9f1d9da1e754242a0c1 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Fri, 23 Mar 2018 13:57:20 -0400 Subject: [PATCH 040/259] Users/stfrance/oss (#331) * Revert updates to dependency versions. * Add package-lock.json. * Remove root package lock. * Try to get node_modules output. * Cleanup. * Fix make.ks. * Add third party notice generator. * Update docs. * Add third party notice. * Copy node_modules to _build. * Copy ThirdPartyNotices.txt to _build directory. * Prune dev dependencies in _build. * Remove node_modules copy, shouldn't need to be there. * Cleanup. * Clean. * Clean. --- .gitignore | 5 +- node/README.md | 6 + node/ThirdPartyNotice.txt | 1114 +++++++++++++++++++++++++++ node/generate-third-party-notice.js | 169 ++++ node/make.js | 2 + node/package-lock.json | 362 +++++++++ node/package.json | 12 +- node/test/dirtests.ts | 5 +- node/test/inputtests.ts | 2 +- 9 files changed, 1667 insertions(+), 10 deletions(-) create mode 100644 node/ThirdPartyNotice.txt create mode 100644 node/generate-third-party-notice.js create mode 100644 node/package-lock.json diff --git a/.gitignore b/.gitignore index 08bdb1e03..2319b712f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,7 @@ node/.taskKey # powershell compiled helper powershell/CompiledHelpers/bin -powershell/CompiledHelpers/obj \ No newline at end of file +powershell/CompiledHelpers/obj + +# generate third party notice script +!generate-third-party-notice.js \ No newline at end of file diff --git a/node/README.md b/node/README.md index 5112fc774..3019d4b3d 100644 --- a/node/README.md +++ b/node/README.md @@ -42,3 +42,9 @@ Set environment variable TASK_TEST_TRACE=1 to display test output. [npm-lib-image]: https://img.shields.io/npm/v/vsts-task-lib.svg?style=flat [npm-lib-url]: https://www.npmjs.com/package/vsts-task-lib + +## Third Party Notices +To generate/update third party notice file run: +```bash +$ node generate-third-party-notice.js +``` \ No newline at end of file diff --git a/node/ThirdPartyNotice.txt b/node/ThirdPartyNotice.txt new file mode 100644 index 000000000..6d9e723ec --- /dev/null +++ b/node/ThirdPartyNotice.txt @@ -0,0 +1,1114 @@ + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +This Visual Studio Team Services extension (vsts-task-lib) is based on or incorporates material from the projects listed below (Third Party IP). The original copyright notice and the license under which Microsoft received such Third Party IP, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft licenses the Third Party IP to you under the licensing terms for the Visual Studio Team Services extension. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. + +1. asap (git+https://github.com/kriskowal/asap.git) +2. balanced-match (git://github.com/juliangruber/balanced-match.git) +3. brace-expansion (git://github.com/juliangruber/brace-expansion.git) +4. browser-stdout (git+ssh://git@github.com/kumavis/browser-stdout.git) +5. caseless (git+https://github.com/mikeal/caseless.git) +6. concat-map (git://github.com/substack/node-concat-map.git) +7. concat-stream (git+ssh://git@github.com/maxogden/concat-stream.git) +8. core-util-is (git://github.com/isaacs/core-util-is.git) +9. fs.realpath (git+https://github.com/isaacs/fs.realpath.git) +10. has-flag (git+https://github.com/sindresorhus/has-flag.git) +11. he (git+https://github.com/mathiasbynens/he.git) +12. http-basic (git+https://github.com/ForbesLindesay/http-basic.git) +13. http-response-object (git+https://github.com/ForbesLindesay/http-response-object.git) +14. inflight (git+https://github.com/npm/inflight.git) +15. inherits (git://github.com/isaacs/inherits.git) +16. isarray (git://github.com/juliangruber/isarray.git) +17. minimatch (git://github.com/isaacs/minimatch.git) +18. minimist (git://github.com/substack/minimist.git) +19. mkdirp (git+https://github.com/substack/node-mkdirp.git) +20. mocha (git+https://github.com/mochajs/mocha.git) +21. mockery (git://github.com/mfncooper/mockery.git) +22. once (git://github.com/isaacs/once.git) +23. path-is-absolute (git+https://github.com/sindresorhus/path-is-absolute.git) +24. process-nextick-args (git+https://github.com/calvinmetcalf/process-nextick-args.git) +25. promise (git+https://github.com/then/promise.git) +26. q (git://github.com/kriskowal/q.git) +27. qs (git+https://github.com/ljharb/qs.git) +28. readable-stream (git://github.com/nodejs/readable-stream.git) +29. safe-buffer (git://github.com/feross/safe-buffer.git) +30. semver (git+https://github.com/npm/node-semver.git) +31. shelljs (git://github.com/arturadib/shelljs.git) +32. string_decoder (git://github.com/rvagg/string_decoder.git) +33. sync-request (git+https://github.com/ForbesLindesay/sync-request.git) +34. then-request (git+https://github.com/then/then-request.git) +35. typedarray (git://github.com/substack/typedarray.git) +36. typescript (git+https://github.com/Microsoft/TypeScript.git) +37. util-deprecate (git://github.com/TooTallNate/util-deprecate.git) +38. uuid (git+https://github.com/kelektiv/node-uuid.git) +39. wrappy (git+https://github.com/npm/wrappy.git) + + +%% asap NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright 2009–2014 Contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +========================================= +END OF asap NOTICES, INFORMATION, AND LICENSE + +%% balanced-match NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(MIT) + +Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF balanced-match NOTICES, INFORMATION, AND LICENSE + +%% brace-expansion NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +MIT License + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF brace-expansion NOTICES, INFORMATION, AND LICENSE + +%% browser-stdout NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright 2018 kumavis + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF browser-stdout NOTICES, INFORMATION, AND LICENSE + +%% caseless NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +1. Definitions. +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. +END OF TERMS AND CONDITIONS +========================================= +END OF caseless NOTICES, INFORMATION, AND LICENSE + +%% concat-map NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF concat-map NOTICES, INFORMATION, AND LICENSE + +%% concat-stream NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License + +Copyright (c) 2013 Max Ogden + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF concat-stream NOTICES, INFORMATION, AND LICENSE + +%% core-util-is NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +========================================= +END OF core-util-is NOTICES, INFORMATION, AND LICENSE + +%% fs.realpath NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---- + +This library bundles a version of the `fs.realpath` and `fs.realpathSync` +methods from Node.js v0.10 under the terms of the Node.js MIT license. + +Node's license follows, also included at the header of `old.js` which contains +the licensed code: + + Copyright Joyent, Inc. and other Node contributors. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +========================================= +END OF fs.realpath NOTICES, INFORMATION, AND LICENSE + +%% has-flag NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF has-flag NOTICES, INFORMATION, AND LICENSE + +%% he NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright Mathias Bynens + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF he NOTICES, INFORMATION, AND LICENSE + +%% http-basic NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2014 Forbes Lindesay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF http-basic NOTICES, INFORMATION, AND LICENSE + +%% http-response-object NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2014 Forbes Lindesay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF http-response-object NOTICES, INFORMATION, AND LICENSE + +%% inflight NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF inflight NOTICES, INFORMATION, AND LICENSE + +%% inherits NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF inherits NOTICES, INFORMATION, AND LICENSE + +%% isarray NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +No license text available. +========================================= +END OF isarray NOTICES, INFORMATION, AND LICENSE + +%% minimatch NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF minimatch NOTICES, INFORMATION, AND LICENSE + +%% minimist NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF minimist NOTICES, INFORMATION, AND LICENSE + +%% mkdirp NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF mkdirp NOTICES, INFORMATION, AND LICENSE + +%% mocha NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(The MIT License) + +Copyright (c) 2011-2018 JS Foundation and contributors, https://js.foundation + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF mocha NOTICES, INFORMATION, AND LICENSE + +%% mockery NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyrights for code authored by Yahoo! Inc. is licensed under the following + terms: + + MIT License + + Copyright (c) 2011 Yahoo! Inc. All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +========================================= +END OF mockery NOTICES, INFORMATION, AND LICENSE + +%% once NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF once NOTICES, INFORMATION, AND LICENSE + +%% path-is-absolute NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF path-is-absolute NOTICES, INFORMATION, AND LICENSE + +%% process-nextick-args NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +# Copyright (c) 2015 Calvin Metcalf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.** +========================================= +END OF process-nextick-args NOTICES, INFORMATION, AND LICENSE + +%% promise NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2014 Forbes Lindesay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF promise NOTICES, INFORMATION, AND LICENSE + +%% q NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright 2009–2017 Kristopher Michael Kowal. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +========================================= +END OF q NOTICES, INFORMATION, AND LICENSE + +%% qs NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2014 Nathan LaFreniere and other contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of any contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * * * + +The complete list of contributors can be found at: https://github.com/hapijs/qs/graphs/contributors +========================================= +END OF qs NOTICES, INFORMATION, AND LICENSE + +%% readable-stream NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Node.js is licensed for use as follows: + +""" +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +This license applies to parts of Node.js originating from the +https://github.com/joyent/node repository: + +""" +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" +========================================= +END OF readable-stream NOTICES, INFORMATION, AND LICENSE + +%% safe-buffer NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF safe-buffer NOTICES, INFORMATION, AND LICENSE + +%% semver NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF semver NOTICES, INFORMATION, AND LICENSE + +%% shelljs NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2012, Artur Adib +All rights reserved. + +You may use this project under the terms of the New BSD license as follows: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Artur Adib nor the + names of the contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +========================================= +END OF shelljs NOTICES, INFORMATION, AND LICENSE + +%% string_decoder NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Node.js is licensed for use as follows: + +""" +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +This license applies to parts of Node.js originating from the +https://github.com/joyent/node repository: + +""" +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" +========================================= +END OF string_decoder NOTICES, INFORMATION, AND LICENSE + +%% sync-request NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2014 Forbes Lindesay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF sync-request NOTICES, INFORMATION, AND LICENSE + +%% then-request NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Copyright (c) 2014 Forbes Lindesay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF then-request NOTICES, INFORMATION, AND LICENSE + +%% typedarray NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +/* + Copyright (c) 2010, Linden Research, Inc. + Copyright (c) 2012, Joshua Bell + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + $/LicenseInfo$ + */ + +// Original can be found at: +// https://bitbucket.org/lindenlab/llsd +// Modifications by Joshua Bell inexorabletash@gmail.com +// https://github.com/inexorabletash/polyfill + +// ES3/ES5 implementation of the Krhonos Typed Array Specification +// Ref: http://www.khronos.org/registry/typedarray/specs/latest/ +// Date: 2011-02-01 +// +// Variations: +// * Allows typed_array.get/set() as alias for subscripts (typed_array[]) +========================================= +END OF typedarray NOTICES, INFORMATION, AND LICENSE + +%% typescript NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS +========================================= +END OF typescript NOTICES, INFORMATION, AND LICENSE + +%% util-deprecate NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +(The MIT License) + +Copyright (c) 2014 Nathan Rajlich + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF util-deprecate NOTICES, INFORMATION, AND LICENSE + +%% uuid NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2010-2016 Robert Kieffer and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF uuid NOTICES, INFORMATION, AND LICENSE + +%% wrappy NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +========================================= +END OF wrappy NOTICES, INFORMATION, AND LICENSE + diff --git a/node/generate-third-party-notice.js b/node/generate-third-party-notice.js new file mode 100644 index 000000000..5d905984f --- /dev/null +++ b/node/generate-third-party-notice.js @@ -0,0 +1,169 @@ +/** + * Run from the root of the vsts-tasks repo. + * Usage: `node generate-third-party-notice.js ` + */ +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +const log = { + info(message) { + console.log(`[INFO] ${message}`); + }, + warning(message) { + console.log(`[WARNING] ${message}`); + }, + error(message) { + console.error(`[ERROR] ${message}`) + } +}; + +/** Log `label: ${value}` and pass the value through. */ +function trace(label, value) { + log.info(`${label}: ${value}`); + return value; +} + +/** + * Read `packagePath`'s package.json and deserialize it. + * @param packagePath Absolute path to the NPM package + * @returns Package manifest information parsed from the package's package.json + */ +function readPackageJson(packagePath) { + log.info(`Reading the package.json for ${packagePath} ...`); + const contents = fs.readFileSync(path.join(packagePath, 'package.json'), { encoding: 'utf-8' }); + return JSON.parse(contents); +} + +/** + * Get the name of the file containing the license for `packagePath`. + * @param packagePath Absolute path to the NPM package + * @returns Absolute path to the license file, or `null` if the license file can't be found + */ +function findLicense(packagePath) { + log.info(`Finding the license for '${packagePath}'`); + const children = fs.readdirSync(packagePath); + const licenseNames = [ + 'LICENSE', + 'LICENSE.md', + 'LICENSE.txt', + 'LICENSE-MIT.txt' + ].map(x => x.toLowerCase()); + const candidates = children.filter(x => licenseNames.includes(x.toLowerCase())); + if (!candidates || candidates.length === 0) { + log.warning(`Could not find a license for ${packagePath}`); + return null; + } else { + //console.log(JSON.stringify(candidates)); + if (candidates.length > 1) { + log.warning(`Found multiple license files for ${packagePath}: ${candidates.join(', ')}`); + } + return trace('Found license', path.join(packagePath, candidates[0])); + } +} + +/** + * Scan the contents of the 'node_modules' directory for license information. + * @param modulesRoot NPM package installation directory to scan + * @returns Iterable of objects: `name` x `url` x `licenseText` + */ +function* collectLicenseInfo(modulesRoot) { + const packagePaths = fs.readdirSync(modulesRoot).map(x => path.join(modulesRoot, x)); + for (let absolutePath of packagePaths) { + log.info(`Collecting license information from ${absolutePath} ...`); + const name = (() => { + const basename = path.basename(absolutePath); + if (path.dirname(absolutePath).endsWith('@types')) { + return `@types/${basename}`; + } else { + return basename; + } + })(); + + if (name === '.bin') { + continue; + } + + if (name === '@types') { + yield* collectLicenseInfo(absolutePath); + continue; + } + + const manifest = readPackageJson(absolutePath); + const license = findLicense(absolutePath); + + let licenseText; + if (license) { + licenseText = fs.readFileSync(license, { encoding: 'utf-8' }); + } else { + licenseText = 'No license text available.'; + } + + yield { + name: name, + url: manifest.repository.url, + licenseText: licenseText + }; + } +} + +/** Generate the third party notice line-by-line. */ +function* thirdPartyNotice(libName, licenseInfo) { + // Preamble + yield ''; + yield 'THIRD-PARTY SOFTWARE NOTICES AND INFORMATION'; + yield 'Do Not Translate or Localize'; + yield ''; + yield `This Visual Studio Team Services extension (${libName}) is based on or incorporates material from the projects listed below (Third Party IP). The original copyright notice and the license under which Microsoft received such Third Party IP, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft licenses the Third Party IP to you under the licensing terms for the Visual Studio Team Services extension. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise.`; + yield ''; + + // Enumerated modules + let num = 1; + for (let item of licenseInfo) { + if (item.url) { + yield `${num}.\t${item.name} (${item.url})`; + } else { + yield `${num}.\t${item.name}`; + } + num += 1; + } + + yield ''; + yield ''; + + // Module licenses + for (let item of licenseInfo) { + yield `%% ${item.name} NOTICES, INFORMATION, AND LICENSE BEGIN HERE`; + yield '========================================='; + yield item.licenseText.trim(); + yield '========================================='; + yield `END OF ${item.name} NOTICES, INFORMATION, AND LICENSE`; + yield ''; + } +} + +function writeLines(writeStream, lines) { + const writeLine = (line) => { + writeStream.write(line); + writeStream.write(os.EOL); + }; + + for (let line of lines) { + writeLine(line); + } +} + +function main(args) { + try { + const nodeModuleDir = path.join(__dirname, 'node_modules'); + const licenseInfo = Array.from(collectLicenseInfo(nodeModuleDir)); + + const writeStream = fs.createWriteStream(path.join(__dirname, 'ThirdPartyNotice.txt')); + writeLines(writeStream, thirdPartyNotice('vsts-task-lib', licenseInfo)); + writeStream.end(); + } catch (e) { + log.error(e.message); + } +} + +main(process.argv); \ No newline at end of file diff --git a/node/make.js b/node/make.js index fbfc97a5c..4e9bbf2c5 100644 --- a/node/make.js +++ b/node/make.js @@ -30,9 +30,11 @@ target.build = function() { run('tsc --outDir ' + buildPath); cp(rp('dependencies/typings.json'), buildPath); cp(rp('package.json'), buildPath); + cp(rp('package-lock.json'), buildPath); cp(rp('README.md'), buildPath); cp(rp('../LICENSE'), buildPath); cp(rp('lib.json'), buildPath); + cp(rp('ThirdPartyNotice.txt'), buildPath); cp('-Rf', rp('Strings'), buildPath); // just a bootstrap file to avoid /// in final js and .d.ts file rm(path.join(buildPath, 'index.*')); diff --git a/node/package-lock.json b/node/package-lock.json new file mode 100644 index 000000000..719c97237 --- /dev/null +++ b/node/package-lock.json @@ -0,0 +1,362 @@ +{ + "name": "vsts-task-lib", + "version": "2.3.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "typedarray": "0.0.6" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "http-basic": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-2.5.1.tgz", + "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", + "dev": true, + "requires": { + "caseless": "0.11.0", + "concat-stream": "1.6.1", + "http-response-object": "1.1.0" + } + }, + "http-response-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-1.1.0.tgz", + "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.2.tgz", + "integrity": "sha512-nmlYKMRpJZLxgzk0bRhcvlpjSisbi0x1JiRl7kctadOMPmecUie7WwCZmcyth+PzX5txKbpcMIvDZCAlx9ISxg==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + } + }, + "mockery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/mockery/-/mockery-1.7.0.tgz", + "integrity": "sha1-9O3g2HUMHJcnwnLqLGBiniyaHE8=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "requires": { + "asap": "2.0.6" + } + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + }, + "readable-stream": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", + "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "sync-request": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-3.0.1.tgz", + "integrity": "sha1-yqEjWq+Im6UBB2oYNMQ2gwqC+3M=", + "dev": true, + "requires": { + "concat-stream": "1.6.1", + "http-response-object": "1.1.0", + "then-request": "2.2.0" + } + }, + "then-request": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz", + "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", + "dev": true, + "requires": { + "caseless": "0.11.0", + "concat-stream": "1.6.1", + "http-basic": "2.5.1", + "http-response-object": "1.1.0", + "promise": "7.3.1", + "qs": "6.5.1" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.7.tgz", + "integrity": "sha1-NeODjeMckc/h2MIODleF04aTikk=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/node/package.json b/node/package.json index ba95efdd0..8db285608 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.3.0", + "version": "2.3.1", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", @@ -26,15 +26,15 @@ "homepage": "https://github.com/Microsoft/vsts-task-lib", "dependencies": { "minimatch": "3.0.4", - "mockery": "2.1.0", - "uuid": "3.2.1", - "q": "1.5.1", - "semver": "5.5.0", + "mockery": "^1.7.0", + "uuid": "^3.0.1", + "q": "^1.1.2", + "semver": "^5.1.0", "shelljs": "^0.3.0" }, "devDependencies": { "mocha": "5.0.2", - "sync-request": "6.0.0", + "sync-request": "3.0.1", "typescript": "1.8.7" } } diff --git a/node/test/dirtests.ts b/node/test/dirtests.ts index ff8e007fa..5ddcf3160 100644 --- a/node/test/dirtests.ts +++ b/node/test/dirtests.ts @@ -31,8 +31,9 @@ describe('Dir Operation Tests', function () { this.timeout(1000); console.log('node version: ' + process.version); - if (process.version != 'v5.10.1' && process.version != 'v6.10.3') { - assert.fail('expected node v5.10.1 or v6.10.3. actual: ' + process.version); + const supportedNodeVersions = ['v5.10.1', 'v6.10.3', 'v8.9.1']; + if (supportedNodeVersions.indexOf(process.version) === -1) { + assert.fail(`expected node node version to be one of ${supportedNodeVersions.map(o => o).join(', ')}. actual: ` + process.version); } done(); diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index 929f43439..cdaf0778f 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -877,7 +877,7 @@ describe('Input Tests', function () { // _loadData tests it('_loadData does not run twice', function (done) { - this.timeout(2000); + this.timeout(5000); // intialize an input (stored in vault) process.env['INPUT_SOMEINPUT'] = 'some input value'; From 953cf2e64d4cf726fb7dd615f54ac895cebd75ae Mon Sep 17 00:00:00 2001 From: madhurig Date: Fri, 23 Mar 2018 15:19:15 -0400 Subject: [PATCH 041/259] guard for timing issues where filestream of first tool is not closed --- node/toolrunner.ts | 96 +++++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 35 deletions(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 06933adda..42d7a1568 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -84,10 +84,10 @@ export class ToolRunner extends events.EventEmitter { var args = []; var inQuotes = false; - var escaped =false; + var escaped = false; var arg = ''; - var append = function(c) { + var append = function (c) { // we only escape double quotes. if (escaped && c !== '"') { arg += '\\'; @@ -97,7 +97,7 @@ export class ToolRunner extends events.EventEmitter { escaped = false; } - for (var i=0; i < argString.length; i++) { + for (var i = 0; i < argString.length; i++) { var c = argString.charAt(i); if (c === '"') { @@ -109,7 +109,7 @@ export class ToolRunner extends events.EventEmitter { } continue; } - + if (c === "\\" && inQuotes) { escaped = true; continue; @@ -180,9 +180,9 @@ export class ToolRunner extends events.EventEmitter { private _getSpawnFileName(): string { if (process.platform == 'win32') { - if (this._isCmdFile()) { - return process.env['COMSPEC'] || 'cmd.exe'; - } + if (this._isCmdFile()) { + return process.env['COMSPEC'] || 'cmd.exe'; + } } return this.toolPath; @@ -192,13 +192,13 @@ export class ToolRunner extends events.EventEmitter { if (process.platform == 'win32') { if (this._isCmdFile()) { let argline: string = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`; - for (let i = 0 ; i < this.args.length ; i++) { + for (let i = 0; i < this.args.length; i++) { argline += ' '; argline += options.windowsVerbatimArguments ? this.args[i] : this._windowsQuoteCmdArg(this.args[i]); } argline += '"'; - return [ argline ]; + return [argline]; } if (options.windowsVerbatimArguments) { @@ -275,7 +275,7 @@ export class ToolRunner extends events.EventEmitter { } // determine whether the arg needs to be quoted - const cmdSpecialChars = [ ' ', '\t', '&', '(', ')', '[', ']', '{', '}', '^', '=', ';', '!', '\'', '+', ',', '`', '~', '|', '<', '>', '"' ]; + const cmdSpecialChars = [' ', '\t', '&', '(', ')', '[', ']', '{', '}', '^', '=', ';', '!', '\'', '+', ',', '`', '~', '|', '<', '>', '"']; let needsQuotes = false; for (let char of arg) { if (cmdSpecialChars.some(x => x == char)) { @@ -338,7 +338,7 @@ export class ToolRunner extends events.EventEmitter { // % can be escaped within a .cmd file. let reverse: string = '"'; let quote_hit = true; - for (let i = arg.length ; i > 0 ; i--) { // walk the string in reverse + for (let i = arg.length; i > 0; i--) { // walk the string in reverse reverse += arg[i - 1]; if (quote_hit && arg[i - 1] == '\\') { reverse += '\\'; // double the slash @@ -419,7 +419,7 @@ export class ToolRunner extends events.EventEmitter { // but it appears the comment is wrong, it should be "hello world\\" let reverse: string = '"'; let quote_hit = true; - for (let i = arg.length ; i > 0 ; i--) { // walk the string in reverse + for (let i = arg.length; i > 0; i--) { // walk the string in reverse reverse += arg[i - 1]; if (quote_hit && arg[i - 1] == '\\') { reverse += '\\'; @@ -485,7 +485,7 @@ export class ToolRunner extends events.EventEmitter { this._debug(this.toolPath + ' arg: ' + JSON.stringify(val)); this.args = this.args.concat(val); } - else if (typeof(val) === 'string') { + else if (typeof (val) === 'string') { this._debug(this.toolPath + ' arg: ' + val); this.args = this.args.concat(val.trim()); } @@ -508,9 +508,9 @@ export class ToolRunner extends events.EventEmitter { this._debug(this.toolPath + ' arg: ' + val); this.args = this.args.concat(this._argStringToArray(val)); - return this; + return this; } - + /** * Add argument(s) if a condition is met * Wraps arg(). See arg for details @@ -533,7 +533,7 @@ export class ToolRunner extends events.EventEmitter { * @param file optional filename to additionally stream the output to. * @returns {ToolRunner} */ - public pipeExecOutputToTool(tool: ToolRunner, file?: string) : ToolRunner { + public pipeExecOutputToTool(tool: ToolRunner, file?: string): ToolRunner { this.pipeOutputToTool = tool; this.pipeOutputToFile = file; return this; @@ -570,8 +570,9 @@ export class ToolRunner extends events.EventEmitter { let toolPathFirst: string; let successFirst = true; let returnCodeFirst: number; - - if(this.pipeOutputToTool) { + let fileStream: fs.WriteStream; + + if (this.pipeOutputToTool) { toolPath = this.pipeOutputToTool.toolPath; toolPathFirst = this.toolPath; @@ -588,8 +589,13 @@ export class ToolRunner extends events.EventEmitter { this.pipeOutputToTool._getSpawnArgs(options), this.pipeOutputToTool._getSpawnOptions(options)); - let fileStream: fs.WriteStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; - + fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; + if (fileStream) { + fileStream.on('finish', () => { + fileStream = null; + }) + } + //pipe stdout of first tool to stdin of second tool cpFirst.stdout.on('data', (data: Buffer) => { try { @@ -635,12 +641,12 @@ export class ToolRunner extends events.EventEmitter { cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(options), this._getSpawnOptions(options)); } - var processLineBuffer = (data: Buffer, strBuffer: string, onLine:(line: string) => void): void => { + var processLineBuffer = (data: Buffer, strBuffer: string, onLine: (line: string) => void): void => { try { var s = strBuffer + data.toString(); var n = s.indexOf(os.EOL); - while(n > -1) { + while (n > -1) { var line = s.substring(0, n); onLine(line); @@ -649,7 +655,7 @@ export class ToolRunner extends events.EventEmitter { n = s.indexOf(os.EOL); } - strBuffer = s; + strBuffer = s; } catch (err) { // streaming lines to console is best effort. Don't fail a build. @@ -667,7 +673,7 @@ export class ToolRunner extends events.EventEmitter { } processLineBuffer(data, stdbuffer, (line: string) => { - this.emit('stdline', line); + this.emit('stdline', line); }); }); @@ -682,12 +688,18 @@ export class ToolRunner extends events.EventEmitter { } processLineBuffer(data, errbuffer, (line: string) => { - this.emit('errline', line); - }); + this.emit('errline', line); + }); }); cp.on('error', (err) => { - defer.reject(new Error(toolPath + ' failed. ' + err.message)); + if (!fileStream) { + defer.reject(new Error(toolPath + ' failed. ' + err.message)); + } else { + fileStream.on('finish', () => { + defer.reject(new Error(toolPath + ' failed. ' + err.message)); + }); + } }); cp.on('close', (code, signal) => { @@ -696,7 +708,7 @@ export class ToolRunner extends events.EventEmitter { if (stdbuffer.length > 0) { this.emit('stdline', stdbuffer); } - + if (errbuffer.length > 0) { this.emit('errline', errbuffer); } @@ -706,13 +718,27 @@ export class ToolRunner extends events.EventEmitter { } this._debug('success:' + success); - if(!successFirst) { //in the case output is piped to another tool, check exit code of both tools - defer.reject(new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst)); - } else if (!success) { - defer.reject(new Error(toolPath + ' failed with return code: ' + code)); - } - else { - defer.resolve(code); + + if (!fileStream) { + if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools + defer.reject(new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst)); + } else if (!success) { + defer.reject(new Error(toolPath + ' failed with return code: ' + code)); + } + else { + defer.resolve(code); + } + } else { + fileStream.on('finish', () => { + if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools + defer.reject(new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst)); + } else if (!success) { + defer.reject(new Error(toolPath + ' failed with return code: ' + code)); + } + else { + defer.resolve(code); + } + }); } }); From 05038e6158a7e7db366c025d316e7e31480722b1 Mon Sep 17 00:00:00 2001 From: madhurig Date: Fri, 23 Mar 2018 15:32:57 -0400 Subject: [PATCH 042/259] add error message for linux --- node/test/toolrunnertests.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index dcedf507b..c2ebaa3b7 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -785,7 +785,8 @@ describe('Toolrunner Tests', function () { assert(err && err.message && err.message.indexOf('/bin/ps') >= 0, 'error from ps is not reported'); assert(fs.existsSync(testFile), 'Log of first tool output is created when first tool fails'); const fileContents = fs.readFileSync(testFile); - assert(fileContents.indexOf('illegal option') >= 0, 'error from first tool should be written to log file: ' + fileContents); + assert(fileContents.indexOf('illegal option') >= 0 || fileContents.indexOf('unsupported option') >= 0, + 'error from first tool should be written to log file: ' + fileContents); done(); } }) From d99b570b1f209ee21a05970a1ad7a6af1c780220 Mon Sep 17 00:00:00 2001 From: madhurig Date: Tue, 27 Mar 2018 10:43:59 -0400 Subject: [PATCH 043/259] Refactor as per offline discussion with Eric --- node/toolrunner.ts | 135 ++++++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 51 deletions(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 42d7a1568..23d3e64dd 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -178,6 +178,29 @@ export class ToolRunner extends events.EventEmitter { return cmd; } + private _processLineBuffer(data: Buffer, strBuffer: string, onLine: (line: string) => void): void { + try { + var s = strBuffer + data.toString(); + var n = s.indexOf(os.EOL); + + while (n > -1) { + var line = s.substring(0, n); + onLine(line); + + // the rest of the string ... + s = s.substring(n + os.EOL.length); + n = s.indexOf(os.EOL); + } + + strBuffer = s; + } + catch (err) { + // streaming lines to console is best effort. Don't fail a build. + this._debug('error processing line'); + } + + } + private _getSpawnFileName(): string { if (process.platform == 'win32') { if (this._isCmdFile()) { @@ -571,6 +594,9 @@ export class ToolRunner extends events.EventEmitter { let successFirst = true; let returnCodeFirst: number; let fileStream: fs.WriteStream; + let waitingEvents: number = 0; // number of process or stream events we are waiting on to complete + let returnCode: number = 0; + let error; if (this.pipeOutputToTool) { toolPath = this.pipeOutputToTool.toolPath; @@ -580,10 +606,13 @@ export class ToolRunner extends events.EventEmitter { // https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options //start the child process for both tools + waitingEvents++; var cpFirst = child.spawn( this._getSpawnFileName(), this._getSpawnArgs(options), this._getSpawnOptions(options)); + + waitingEvents ++; cp = child.spawn( this.pipeOutputToTool._getSpawnFileName(), this.pipeOutputToTool._getSpawnArgs(options), @@ -591,8 +620,29 @@ export class ToolRunner extends events.EventEmitter { fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; if (fileStream) { + waitingEvents ++; fileStream.on('finish', () => { + waitingEvents--; //file write is complete fileStream = null; + if(waitingEvents == 0) { + if (error) { + defer.reject(error); + } else { + defer.resolve(returnCode); + } + } + }); + fileStream.on('error', (err) => { + waitingEvents--; //there were errors writing to the file, write is done + this._debug(`Failed to pipe output of ${toolPathFirst} to file ${this.pipeOutputToFile}. Error = ${err}`); + fileStream = null; + if(waitingEvents == 0) { + if (error) { + defer.reject(error); + } else { + defer.resolve(returnCode); + } + } }) } @@ -619,51 +669,42 @@ export class ToolRunner extends events.EventEmitter { } }); cpFirst.on('error', (err) => { + waitingEvents--; //first process is complete with errors if (fileStream) { fileStream.end(); } cp.stdin.end(); - defer.reject(new Error(toolPathFirst + ' failed. ' + err.message)); + error = new Error(toolPathFirst + ' failed. ' + err.message); + if(waitingEvents == 0) { + defer.reject(error); + } }); cpFirst.on('close', (code, signal) => { + waitingEvents--; //first process is complete if (fileStream) { fileStream.end(); } if (code != 0 && !options.ignoreReturnCode) { successFirst = false; returnCodeFirst = code; + returnCode = returnCodeFirst; } this._debug('success of first tool:' + successFirst); cp.stdin.end(); + if(waitingEvents == 0) { + if (error) { + defer.reject(error); + } else { + defer.resolve(returnCode); + } + } }); } else { + waitingEvents++; cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(options), this._getSpawnOptions(options)); } - var processLineBuffer = (data: Buffer, strBuffer: string, onLine: (line: string) => void): void => { - try { - var s = strBuffer + data.toString(); - var n = s.indexOf(os.EOL); - - while (n > -1) { - var line = s.substring(0, n); - onLine(line); - - // the rest of the string ... - s = s.substring(n + os.EOL.length); - n = s.indexOf(os.EOL); - } - - strBuffer = s; - } - catch (err) { - // streaming lines to console is best effort. Don't fail a build. - this._debug('error processing line'); - } - - } - var stdbuffer: string = ''; cp.stdout.on('data', (data: Buffer) => { this.emit('stdout', data); @@ -672,7 +713,7 @@ export class ToolRunner extends events.EventEmitter { options.outStream.write(data); } - processLineBuffer(data, stdbuffer, (line: string) => { + this._processLineBuffer(data, stdbuffer, (line: string) => { this.emit('stdline', line); }); }); @@ -687,23 +728,23 @@ export class ToolRunner extends events.EventEmitter { s.write(data); } - processLineBuffer(data, errbuffer, (line: string) => { + this._processLineBuffer(data, errbuffer, (line: string) => { this.emit('errline', line); }); }); cp.on('error', (err) => { - if (!fileStream) { - defer.reject(new Error(toolPath + ' failed. ' + err.message)); - } else { - fileStream.on('finish', () => { - defer.reject(new Error(toolPath + ' failed. ' + err.message)); - }); + waitingEvents--; //process is done with errors + error = new Error(toolPath + ' failed. ' + err.message); + if(waitingEvents == 0) { + defer.reject(error); } }); cp.on('close', (code, signal) => { + waitingEvents--; //process is complete this._debug('rc:' + code); + returnCode = code; if (stdbuffer.length > 0) { this.emit('stdline', stdbuffer); @@ -719,26 +760,18 @@ export class ToolRunner extends events.EventEmitter { this._debug('success:' + success); - if (!fileStream) { - if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools - defer.reject(new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst)); - } else if (!success) { - defer.reject(new Error(toolPath + ' failed with return code: ' + code)); - } - else { - defer.resolve(code); + if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools + error = new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst); + } else if (!success) { + error = new Error(toolPath + ' failed with return code: ' + code); + } + + if(waitingEvents == 0) { + if (error) { + defer.reject(error); + } else { + defer.resolve(returnCode); } - } else { - fileStream.on('finish', () => { - if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools - defer.reject(new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst)); - } else if (!success) { - defer.reject(new Error(toolPath + ' failed with return code: ' + code)); - } - else { - defer.resolve(code); - } - }); } }); From 057056b9e9b2ac18921a94d075cd8dba05873d81 Mon Sep 17 00:00:00 2001 From: madhurig Date: Tue, 27 Mar 2018 10:50:46 -0400 Subject: [PATCH 044/259] close stream after setting returncode --- node/toolrunner.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 23d3e64dd..e87fdf420 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -681,15 +681,15 @@ export class ToolRunner extends events.EventEmitter { }); cpFirst.on('close', (code, signal) => { waitingEvents--; //first process is complete - if (fileStream) { - fileStream.end(); - } if (code != 0 && !options.ignoreReturnCode) { successFirst = false; returnCodeFirst = code; returnCode = returnCodeFirst; } this._debug('success of first tool:' + successFirst); + if (fileStream) { + fileStream.end(); + } cp.stdin.end(); if(waitingEvents == 0) { if (error) { From 6c3c61d845b45024a4057bd88168ef6a87d617d0 Mon Sep 17 00:00:00 2001 From: Ting Date: Thu, 29 Mar 2018 15:02:40 -0400 Subject: [PATCH 045/259] add optional parameter to getHttpProxyConfig allow bypass filter. (#324) * add optional parameter to getHttpProxyConfig allow bypass filter. * fix --- node/task.ts | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/node/task.ts b/node/task.ts index 783402d35..a8a0ff965 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1563,19 +1563,33 @@ export interface ProxyConfiguration { * * @return ProxyConfiguration */ -export function getHttpProxyConfiguration(): ProxyConfiguration { +export function getHttpProxyConfiguration(requestUrl?: string): ProxyConfiguration { let proxyUrl: string = getVariable('Agent.ProxyUrl'); if (proxyUrl && proxyUrl.length > 0) { let proxyUsername: string = getVariable('Agent.ProxyUsername'); let proxyPassword: string = getVariable('Agent.ProxyPassword'); let proxyBypassHosts: string[] = JSON.parse(getVariable('Agent.ProxyBypassList') || '[]'); - return { - proxyUrl: proxyUrl, - proxyUsername: proxyUsername, - proxyPassword: proxyPassword, - proxyBypassHosts: proxyBypassHosts - }; + let bypass: boolean = false; + if (requestUrl) { + proxyBypassHosts.forEach(bypassHost => { + if (new RegExp(bypassHost, 'i').test(requestUrl)) { + bypass = true; + } + }); + } + + if (bypass) { + return null; + } + else { + return { + proxyUrl: proxyUrl, + proxyUsername: proxyUsername, + proxyPassword: proxyPassword, + proxyBypassHosts: proxyBypassHosts + }; + } } else { return null; From d3b2d15ed3f74c4b6cc538454b70f11651e661e0 Mon Sep 17 00:00:00 2001 From: Brian Cristante <33549821+brcrista@users.noreply.github.com> Date: Mon, 2 Apr 2018 15:14:07 -0400 Subject: [PATCH 046/259] Missed a couple mocked commands. Bump patch version (#334) * Missed a couple commands. Catch unmocked commands at compile time. Bump patch version * Need to bump again, had un-synced changes upstream * Strings should be single-quoted * Bump minor version * clarify TODO --- node/mock-answer.ts | 25 ++++++++++++++++++++----- node/package.json | 2 +- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/node/mock-answer.ts b/node/mock-answer.ts index 845fa78f6..eabf703a6 100644 --- a/node/mock-answer.ts +++ b/node/mock-answer.ts @@ -1,5 +1,5 @@ -import path = require('path'); -import fs = require('fs'); +import * as path from 'path'; +import * as fs from 'fs'; export interface TaskLibAnswerExecResult { code: number, @@ -15,10 +15,25 @@ export interface TaskLibAnswers { find?: { [key: string]: string[] }, findMatch?: { [key: string]: string[] }, ls?: { [key: string]: string }, + osType?: { [key: string]: string }, rmRF?: { [key: string]: { success: boolean } }, - which?: { [key: string]: string; }, + stats?: { [key: string]: any }, // Can't use `fs.Stats` as most existing uses don't mock all required properties + which?: { [key: string]: string }, } +// TODO TypeScript 2.1: replace with `keyof TaskLibAnswers` +export type MockedCommand = 'checkPath' + | 'cwd' + | 'exec' + | 'exist' + | 'find' + | 'findMatch' + | 'ls' + | 'osType' + | 'rmRF' + | 'stats' + | 'which'; + export class MockAnswers { private _answers: TaskLibAnswers; @@ -29,14 +44,14 @@ export class MockAnswers { this._answers = answers; } - public getResponse(cmd: string, key: string, debug: (message: string) => void): any { + public getResponse(cmd: MockedCommand, key: string, debug: (message: string) => void): any { debug(`looking up mock answers for ${JSON.stringify(cmd)}, key '${JSON.stringify(key)}'`); if (!this._answers) { throw new Error('Must initialize'); } if (!this._answers[cmd]) { - debug(`no mock responses registered for given cmd`); + debug(`no mock responses registered for ${JSON.stringify(cmd)}`); return null; } diff --git a/node/package.json b/node/package.json index 8db285608..3f3c7c697 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.3.1", + "version": "2.4.0", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", From f56d971e14f8f7f9dddcae8d14c9c1862ba9e4d1 Mon Sep 17 00:00:00 2001 From: ericsciple Date: Sun, 15 Apr 2018 11:26:42 -0400 Subject: [PATCH 047/259] Fix indentation for table of contents --- powershell/Docs/Commands.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/powershell/Docs/Commands.md b/powershell/Docs/Commands.md index e13919894..52babe2ee 100644 --- a/powershell/Docs/Commands.md +++ b/powershell/Docs/Commands.md @@ -1,22 +1,22 @@ # Commands (v0.9.0) ## Table of Contents - * [Find](#find) +* [Find](#find) * [Find-VstsMatch](#find-vstsmatch) * [New-VstsFindOptions](#new-vstsfindoptions) * [New-VstsMatchOptions](#new-vstsmatchoptions) * [Select-VstsMatch](#select-vstsmatch) - * [Input](#input) +* [Input](#input) * [Get-VstsEndpoint](#get-vstsendpoint) * [Get-VstsInput](#get-vstsinput) * [Get-VstsTaskVariable](#get-vststaskvariable) * [Get-VstsTaskVariableInfo](#get-vststaskvariableinfo) * [Set-VstsTaskVariable](#set-vststaskvariable) - * [Legacy Find](#legacyfind) +* [Legacy Find](#legacyfind) * [Find-VstsFiles](#find-vstsfiles) - * [Localization](#localization) +* [Localization](#localization) * [Get-VstsLocString](#get-vstslocstring) * [Import-VstsLocStrings](#import-vstslocstrings) - * [Logging Command](#loggingcommand) +* [Logging Command](#loggingcommand) * [Write-VstsAddAttachment](#write-vstsaddattachment) * [Write-VstsAddBuildTag](#write-vstsaddbuildtag) * [Write-VstsAssociateArtifact](#write-vstsassociateartifact) @@ -32,18 +32,18 @@ * [Write-VstsUpdateBuildNumber](#write-vstsupdatebuildnumber) * [Write-VstsUploadArtifact](#write-vstsuploadartifact) * [Write-VstsUploadBuildLog](#write-vstsuploadbuildlog) - * [Server OM](#serverom) +* [Server OM](#serverom) * [Get-VstsAssemblyReference](#get-vstsassemblyreference) * [Get-VstsTfsClientCredentials](#get-vststfsclientcredentials) * [Get-VstsTfsService](#get-vststfsservice) * [Get-VstsVssCredentials](#get-vstsvsscredentials) * [Get-VstsVssHttpClient](#get-vstsvsshttpclient) * [Get-VstsWebProxy](#get-vstswebproxy) - * [Tool](#tool) +* [Tool](#tool) * [Assert-VstsAgent](#assert-vstsagent) * [Assert-VstsPath](#assert-vstspath) * [Invoke-VstsTool](#invoke-vststool) - * [Trace](#trace) +* [Trace](#trace) * [Trace-VstsEnteringInvocation](#trace-vstsenteringinvocation) * [Trace-VstsLeavingInvocation](#trace-vstsleavinginvocation) * [Trace-VstsPath](#trace-vstspath) From 1a3ba0d673064b26d0f9f7688e357393be82882f Mon Sep 17 00:00:00 2001 From: Eric Sciple Date: Sun, 15 Apr 2018 11:45:36 -0400 Subject: [PATCH 048/259] update docs --- powershell/Docs/Commands.md | 26 ++++++++++++---- .../FullHelp/Get-VstsAssemblyReference.md | 6 ++-- .../FullHelp/Get-VstsClientCertificate.md | 28 +++++++++++++++++ .../Docs/FullHelp/Get-VstsTfsService.md | 2 +- .../Docs/FullHelp/Get-VstsVssHttpClient.md | 30 +++++++++++++++++-- powershell/Docs/Update-Docs.ps1 | 4 +-- 6 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 powershell/Docs/FullHelp/Get-VstsClientCertificate.md diff --git a/powershell/Docs/Commands.md b/powershell/Docs/Commands.md index 52babe2ee..d44ecc5d5 100644 --- a/powershell/Docs/Commands.md +++ b/powershell/Docs/Commands.md @@ -1,4 +1,4 @@ -# Commands (v0.9.0) +# Commands (v0.10.1) ## Table of Contents * [Find](#find) * [Find-VstsMatch](#find-vstsmatch) @@ -34,6 +34,7 @@ * [Write-VstsUploadBuildLog](#write-vstsuploadbuildlog) * [Server OM](#serverom) * [Get-VstsAssemblyReference](#get-vstsassemblyreference) + * [Get-VstsClientCertificate](#get-vstsclientcertificate) * [Get-VstsTfsClientCredentials](#get-vststfsclientcredentials) * [Get-VstsTfsService](#get-vststfsservice) * [Get-VstsVssCredentials](#get-vstsvsscredentials) @@ -477,9 +478,9 @@ SYNTAX Get-VstsAssemblyReference [-LiteralPath] [] DESCRIPTION - Not supported for use during task exection. This function is only intended to help developers resolve the - minimal set of DLLs that need to be bundled when consuming the VSTS REST SDK or TFS Extended Client SDK. - The interface and output may change between patch releases of the VSTS Task SDK. + Not supported for use during task execution. This function is only intended to help developers resolve + the minimal set of DLLs that need to be bundled when consuming the VSTS REST SDK or TFS Extended Client + SDK. The interface and output may change between patch releases of the VSTS Task SDK. Only a subset of the referenced assemblies may actually be required, depending on the functionality used by your task. It is best to bundle only the DLLs required for your scenario. @@ -491,6 +492,21 @@ DESCRIPTION See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. ``` +### Get-VstsClientCertificate +[table of contents](#toc) | [full](FullHelp/Get-VstsClientCertificate.md) +``` +NAME + Get-VstsClientCertificate + +SYNOPSIS + Gets a client certificate for current connected TFS instance + +SYNTAX + Get-VstsClientCertificate [] + +DESCRIPTION + Gets an instance of a X509Certificate2 that is the client certificate Build/Release agent used. +``` ### Get-VstsTfsClientCredentials [table of contents](#toc) | [full](FullHelp/Get-VstsTfsClientCredentials.md) ``` @@ -568,7 +584,7 @@ SYNOPSIS SYNTAX Get-VstsVssHttpClient [-TypeName] [[-OMDirectory] ] [[-Uri] ] [[-VssCredentials] - ] [[-WebProxy] ] [] + ] [[-WebProxy] ] [[-ClientCert] ] [-IgnoreSslError] [] DESCRIPTION Gets an instance of an VSS HTTP client. diff --git a/powershell/Docs/FullHelp/Get-VstsAssemblyReference.md b/powershell/Docs/FullHelp/Get-VstsAssemblyReference.md index bdb384d9c..f9acfc108 100644 --- a/powershell/Docs/FullHelp/Get-VstsAssemblyReference.md +++ b/powershell/Docs/FullHelp/Get-VstsAssemblyReference.md @@ -11,9 +11,9 @@ SYNTAX Get-VstsAssemblyReference [-LiteralPath] [] DESCRIPTION - Not supported for use during task exection. This function is only intended to help developers resolve the - minimal set of DLLs that need to be bundled when consuming the VSTS REST SDK or TFS Extended Client SDK. - The interface and output may change between patch releases of the VSTS Task SDK. + Not supported for use during task execution. This function is only intended to help developers resolve + the minimal set of DLLs that need to be bundled when consuming the VSTS REST SDK or TFS Extended Client + SDK. The interface and output may change between patch releases of the VSTS Task SDK. Only a subset of the referenced assemblies may actually be required, depending on the functionality used by your task. It is best to bundle only the DLLs required for your scenario. diff --git a/powershell/Docs/FullHelp/Get-VstsClientCertificate.md b/powershell/Docs/FullHelp/Get-VstsClientCertificate.md new file mode 100644 index 000000000..842a8b197 --- /dev/null +++ b/powershell/Docs/FullHelp/Get-VstsClientCertificate.md @@ -0,0 +1,28 @@ +# Get-VstsClientCertificate +[table of contents](../Commands.md#toc) | [brief](../Commands.md#get-vstsclientcertificate) +``` +NAME + Get-VstsClientCertificate + +SYNOPSIS + Gets a client certificate for current connected TFS instance + +SYNTAX + Get-VstsClientCertificate [] + +DESCRIPTION + Gets an instance of a X509Certificate2 that is the client certificate Build/Release agent used. + +PARAMETERS + + This cmdlet supports the common parameters: Verbose, Debug, + ErrorAction, ErrorVariable, WarningAction, WarningVariable, + OutBuffer, PipelineVariable, and OutVariable. For more information, see + about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). + + -------------------------- EXAMPLE 1 -------------------------- + + PS C:\>$x509cert = Get-ClientCertificate + + WebRequestHandler.ClientCertificates.Add(x509cert) +``` diff --git a/powershell/Docs/FullHelp/Get-VstsTfsService.md b/powershell/Docs/FullHelp/Get-VstsTfsService.md index 30110f216..eeba24efe 100644 --- a/powershell/Docs/FullHelp/Get-VstsTfsService.md +++ b/powershell/Docs/FullHelp/Get-VstsTfsService.md @@ -56,7 +56,7 @@ PARAMETERS Accept wildcard characters? false -TfsClientCredentials - Credentials to use when intializing the service. If not specified, the default uses the agent job + Credentials to use when initializing the service. If not specified, the default uses the agent job token to construct the credentials object. The identity associated with the token depends on the scope selected in the build/release definition (either the project collection build/release service identity, or the project build/release service identity). diff --git a/powershell/Docs/FullHelp/Get-VstsVssHttpClient.md b/powershell/Docs/FullHelp/Get-VstsVssHttpClient.md index 6c44b8e67..44ec2ddc0 100644 --- a/powershell/Docs/FullHelp/Get-VstsVssHttpClient.md +++ b/powershell/Docs/FullHelp/Get-VstsVssHttpClient.md @@ -9,7 +9,7 @@ SYNOPSIS SYNTAX Get-VstsVssHttpClient [-TypeName] [[-OMDirectory] ] [[-Uri] ] [[-VssCredentials] - ] [[-WebProxy] ] [] + ] [[-WebProxy] ] [[-ClientCert] ] [-IgnoreSslError] [] DESCRIPTION Gets an instance of an VSS HTTP client. @@ -44,15 +44,23 @@ PARAMETERS System.TeamFoundationCollectionUri. # .PARAMETER VssCredentials - # Credentials to use when intializing the HTTP client. If not specified, the default uses the agent + # Credentials to use when initializing the HTTP client. If not specified, the default uses the agent job token to construct the credentials object. The identity associated with the token depends on the scope selected in the build/release definition (either the project collection build/release service identity, or the project build/release service identity). # .PARAMETER WebProxy - # WebProxy to use when intializing the HTTP client. If not specified, the default uses the proxy + # WebProxy to use when initializing the HTTP client. If not specified, the default uses the proxy configuration agent current has. + # .PARAMETER ClientCert + # ClientCert to use when initializing the HTTP client. If not specified, the default uses the client + certificate agent current has. + + # .PARAMETER IgnoreSslError + # Skip SSL server certificate validation on all requests made by this HTTP client. If not specified, + the default is to validate SSL server certificate. + Required? false Position? 2 Default value @@ -83,6 +91,22 @@ PARAMETERS Accept pipeline input? false Accept wildcard characters? false + -ClientCert + + Required? false + Position? 6 + Default value (Get-ClientCertificate) + Accept pipeline input? false + Accept wildcard characters? false + + -IgnoreSslError [] + + Required? false + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + This cmdlet supports the common parameters: Verbose, Debug, ErrorAction, ErrorVariable, WarningAction, WarningVariable, diff --git a/powershell/Docs/Update-Docs.ps1 b/powershell/Docs/Update-Docs.ps1 index cbc63bcec..c8b38e924 100644 --- a/powershell/Docs/Update-Docs.ps1 +++ b/powershell/Docs/Update-Docs.ps1 @@ -25,6 +25,7 @@ function Get-HelpString { foreach ($line in $str.Trim().Replace("`r", "").Split("`n")) { $line = $line.TrimEnd() $line = $line.Replace("http://go.microsoft.com", "https://go.microsoft.com") + $line = $line.Replace("https:/go.microsoft.com", "https://go.microsoft.com") # Add the blank line. if (!$line) { # Prevent multiple blank lines. @@ -118,7 +119,6 @@ foreach ($functionName in $module.ExportedFunctions.Keys) { $functionToFullHelpMap[$functionName] = Get-HelpString -Name $functionName -Full } - # Build a mapping of help sections to functions. Write-Host "Resolving section information." $sectionToFunctionsMap = @{ } @@ -167,7 +167,7 @@ $null = $tocContent.AppendLine("## Table of Contents") foreach ($sectionName in ($sectionToFunctionsMap.Keys | Sort-Object)) { $functionNames = $sectionToFunctionsMap[$sectionName] | Sort-Object $sectionId = $sectionName.Replace(" ", "").ToLowerInvariant() - $null = $tocContent.AppendLine(" * [$sectionName](#$sectionId)") + $null = $tocContent.AppendLine("* [$sectionName](#$sectionId)") $null = $commandsContent.AppendLine("## $sectionName") foreach ($functionName in $functionNames) { $functionId = $functionName.Replace(" ", "").ToLowerInvariant() From 8a1a8a9dec5b6de37ab8835a852f6cf72272634e Mon Sep 17 00:00:00 2001 From: Eric Sciple Date: Tue, 17 Apr 2018 23:22:15 -0400 Subject: [PATCH 049/259] allow broken symlink --- node/docs/releases.md | 3 + node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 40 ++++++- node/test/dirtests.ts | 248 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 287 insertions(+), 8 deletions(-) diff --git a/node/docs/releases.md b/node/docs/releases.md index 6c6261139..8989c7ad6 100644 --- a/node/docs/releases.md +++ b/node/docs/releases.md @@ -1,5 +1,8 @@ # VSTS-TASK-LIB RELEASES +## 2.5.0 + * Updated `FindOptions` to expose `allowBrokenSymbolicLinks`. + ## 2.3.0 * Updated `setVariable` to fail when a secret contains multiple lines. * Added `setSecret` to register a secret with the log scrubber, without registering a variable. Multi-line secrets are not supported. diff --git a/node/package-lock.json b/node/package-lock.json index 719c97237..4e3305bdb 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.3.1", + "version": "2.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 3f3c7c697..1c0dce6b2 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.4.0", + "version": "2.5.0", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index a8a0ff965..8f7ea0ba7 100644 --- a/node/task.ts +++ b/node/task.ts @@ -718,6 +718,12 @@ export function mv(source: string, dest: string, options?: string, continueOnErr * Contains properties to control whether to follow symlinks */ export interface FindOptions { + + /** + * When true, broken symbolic link will not cause an error. + */ + allowBrokenSymbolicLinks: boolean, + /** * Equivalent to the -H command line option. Indicates whether to traverse descendants if * the specified path is a symbolic link directory. Does not cause nested symbolic link @@ -785,12 +791,36 @@ export function find(findPath: string, options?: FindOptions): string[] { // lstat returns info about a symlink itself let stats: fs.Stats; if (options.followSymbolicLinks) { - // use stat (following all symlinks) - stats = fs.statSync(item.path); + 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) { - // use stat (following symlinks for the specified path and this is the specified path) - stats = fs.statSync(item.path); + 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; + } + } } else { // use lstat (not following symlinks) @@ -851,12 +881,14 @@ class _FindItem { } function _debugFindOptions(options: FindOptions): void { + debug(`findOptions.allowBrokenSymbolicLinks: '${options.allowBrokenSymbolicLinks}'`); debug(`findOptions.followSpecifiedSymbolicLink: '${options.followSpecifiedSymbolicLink}'`); debug(`findOptions.followSymbolicLinks: '${options.followSymbolicLinks}'`); } function _getDefaultFindOptions(): FindOptions { return { + allowBrokenSymbolicLinks: false, followSpecifiedSymbolicLink: true, followSymbolicLinks: true }; diff --git a/node/test/dirtests.ts b/node/test/dirtests.ts index 5ddcf3160..508691860 100644 --- a/node/test/dirtests.ts +++ b/node/test/dirtests.ts @@ -744,6 +744,227 @@ describe('Dir Operation Tests', function () { done(); }); + it('allows broken symlink', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /brokenSym -> /noSuch + // /realDir + // /realDir/file + // /symDir -> /realDir + let root: string = path.join(testutil.getTestTemp(), 'find_no_follow_symlink_allows_broken_symlink'); + tl.mkdirP(root); + testutil.createSymlinkDir(path.join(root, 'noSuch'), path.join(root, 'brokenSym')); + tl.mkdirP(path.join(root, 'realDir')); + fs.writeFileSync(path.join(root, 'realDir', 'file'), 'test file content'); + testutil.createSymlinkDir(path.join(root, 'realDir'), path.join(root, 'symDir')); + + let itemPaths: string[] = tl.find(root, { }); + assert.equal(itemPaths.length, 5); + assert.equal(itemPaths[0], root); + assert.equal(itemPaths[1], path.join(root, 'brokenSym')); + assert.equal(itemPaths[2], path.join(root, 'realDir')); + assert.equal(itemPaths[3], path.join(root, 'realDir', 'file')); + assert.equal(itemPaths[4], path.join(root, 'symDir')); + + done(); + }); + + it('allows specified broken symlink', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /brokenSym -> /noSuch + let root: string = path.join(testutil.getTestTemp(), 'find_no_follow_symlink_allows_specified_broken_symlink'); + tl.mkdirP(root); + let brokenSymPath = path.join(root, 'brokenSym'); + testutil.createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath); + + let itemPaths: string[] = tl.find(brokenSymPath, { }); + assert.equal(itemPaths.length, 1); + assert.equal(itemPaths[0], brokenSymPath); + + done(); + }); + + it('allows nested broken symlink when -H', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /brokenSym -> /noSuch + // /realDir + // /realDir/file + // /symDir -> /realDir + let root: string = path.join(testutil.getTestTemp(), 'find_allows_nested_broken_symlink_when_-H'); + tl.mkdirP(root); + testutil.createSymlinkDir(path.join(root, 'noSuch'), path.join(root, 'brokenSym')); + tl.mkdirP(path.join(root, 'realDir')); + fs.writeFileSync(path.join(root, 'realDir', 'file'), 'test file content'); + testutil.createSymlinkDir(path.join(root, 'realDir'), path.join(root, 'symDir')); + + let options: tl.FindOptions = {} as tl.FindOptions; + options.followSpecifiedSymbolicLink = true; + let itemPaths: string[] = tl.find(root, options); + assert.equal(itemPaths.length, 5); + assert.equal(itemPaths[0], root); + assert.equal(itemPaths[1], path.join(root, 'brokenSym')); + assert.equal(itemPaths[2], path.join(root, 'realDir')); + assert.equal(itemPaths[3], path.join(root, 'realDir', 'file')); + assert.equal(itemPaths[4], path.join(root, 'symDir')); + + done(); + }); + + it('allows specified broken symlink with -H', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /brokenSym -> /noSuch + let root: string = path.join(testutil.getTestTemp(), 'find_allows_specified_broken_symlink_with_-H'); + tl.mkdirP(root); + let brokenSymPath = path.join(root, 'brokenSym'); + testutil.createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath); + + let options: tl.FindOptions = {} as tl.FindOptions; + options.allowBrokenSymbolicLinks = true; + options.followSpecifiedSymbolicLink = true; + let itemPaths: string[] = tl.find(brokenSymPath, options); + assert.equal(itemPaths.length, 1); + assert.equal(itemPaths[0], brokenSymPath); + + done(); + }); + + it('does not allow specified broken symlink when only -H', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /brokenSym -> /noSuch + let root: string = path.join(testutil.getTestTemp(), 'find_not_allow_specified_broken_sym_when_only_-H'); + tl.mkdirP(root); + let brokenSymPath = path.join(root, 'brokenSym'); + testutil.createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath); + fs.lstatSync(brokenSymPath); + + let options: tl.FindOptions = {} as tl.FindOptions; + options.followSpecifiedSymbolicLink = true; + try { + tl.find(brokenSymPath, options); + throw new Error('Expected tl.find to throw'); + } + catch (err) { + assert(err.message.match(/ENOENT.*brokenSym/), `Expected broken symlink error message, actual: '${err.message}'`); + } + + done(); + }); + + it('does not allow broken symlink when only -L', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /brokenSym -> /noSuch + let root: string = path.join(testutil.getTestTemp(), 'find_not_allow_broken_sym_when_only_-L'); + tl.mkdirP(root); + testutil.createSymlinkDir(path.join(root, 'noSuch'), path.join(root, 'brokenSym')); + + let options: tl.FindOptions = {} as tl.FindOptions; + options.followSymbolicLinks = true; + try { + tl.find(root, options); + throw new Error('Expected tl.find to throw'); + } + catch (err) { + assert(err.message.match(/ENOENT.*brokenSym/), `Expected broken symlink error message, actual: '${err.message}'`); + } + + done(); + }); + + it('does not allow specied broken symlink when only -L', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /brokenSym -> /noSuch + let root: string = path.join(testutil.getTestTemp(), 'find_not_allow_specified_broken_sym_when_only_-L'); + tl.mkdirP(root); + let brokenSymPath = path.join(root, 'brokenSym'); + testutil.createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath); + fs.lstatSync(brokenSymPath); + + let options: tl.FindOptions = {} as tl.FindOptions; + options.followSymbolicLinks = true; + try { + tl.find(brokenSymPath, options); + throw new Error('Expected tl.find to throw'); + } + catch (err) { + assert(err.message.match(/ENOENT.*brokenSym/), `Expected broken symlink error message, actual: '${err.message}'`); + } + + done(); + }); + + it('allow broken symlink with -L', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /brokenSym -> /noSuch + // /realDir + // /realDir/file + // /symDir -> /realDir + let root: string = path.join(testutil.getTestTemp(), 'find_allow_broken_sym_with_-L'); + tl.mkdirP(root); + testutil.createSymlinkDir(path.join(root, 'noSuch'), path.join(root, 'brokenSym')); + tl.mkdirP(path.join(root, 'realDir')); + fs.writeFileSync(path.join(root, 'realDir', 'file'), 'test file content'); + testutil.createSymlinkDir(path.join(root, 'realDir'), path.join(root, 'symDir')); + + let options: tl.FindOptions = {} as tl.FindOptions; + options.allowBrokenSymbolicLinks = true; + options.followSymbolicLinks = true; + let itemPaths: string[] = tl.find(root, options); + assert.equal(itemPaths.length, 6); + assert.equal(itemPaths[0], root); + assert.equal(itemPaths[1], path.join(root, 'brokenSym')); + assert.equal(itemPaths[2], path.join(root, 'realDir')); + assert.equal(itemPaths[3], path.join(root, 'realDir', 'file')); + assert.equal(itemPaths[4], path.join(root, 'symDir')); + assert.equal(itemPaths[5], path.join(root, 'symDir', 'file')); + + done(); + }); + + it('allow specified broken symlink with -L', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /brokenSym -> /noSuch + let root: string = path.join(testutil.getTestTemp(), 'find_allow_specified_broken_sym_with_-L'); + tl.mkdirP(root); + let brokenSymPath = path.join(root, 'brokenSym'); + testutil.createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath); + fs.lstatSync(brokenSymPath); + + let options: tl.FindOptions = {} as tl.FindOptions; + options.allowBrokenSymbolicLinks = true; + options.followSymbolicLinks = true; + let itemPaths: string[] = tl.find(brokenSymPath, options); + assert.equal(itemPaths.length, 1); + assert.equal(itemPaths[0], brokenSymPath); + + done(); + }); + it('detects cycle', (done: MochaDone) => { this.timeout(1000); @@ -825,7 +1046,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('applies default options', (done: MochaDone) => { + it('default options', (done: MochaDone) => { this.timeout(1000); // create the following layout: @@ -833,7 +1054,7 @@ describe('Dir Operation Tests', function () { // /real_folder // /real_folder/file_under_real_folder // /sym_folder -> real_folder - let root: string = path.join(testutil.getTestTemp(), 'find_applies_default_options'); + let root: string = path.join(testutil.getTestTemp(), 'find_default_options'); tl.mkdirP(path.join(root, 'real_folder')); fs.writeFileSync(path.join(root, 'real_folder', 'file_under_real_folder'), 'test file under real folder'); testutil.createSymlinkDir(path.join(root, 'real_folder'), path.join(root, 'sym_folder')); @@ -841,6 +1062,7 @@ describe('Dir Operation Tests', function () { () => fs.statSync(path.join(root, 'sym_folder', 'file_under_real_folder')), 'sym_folder should be created properly'); + // assert the expected files are returned let actual: string[] = tl.find(root); let expected: string[] = [ root, @@ -854,6 +1076,28 @@ describe('Dir Operation Tests', function () { done(); }); + it('default options do not allow broken symlinks', (done: MochaDone) => { + this.timeout(1000); + + // create the following layout: + // + // /broken_symlink -> no_such_file + let root: string = path.join(testutil.getTestTemp(), 'find_default_options_broken_symlink'); + tl.mkdirP(root); + testutil.createSymlinkDir(path.join(root, 'no_such_file'), path.join(root, 'broken_symlink')); + + // assert the broken symlink is a problem + try { + tl.find(root); + throw new Error('Expected tl.find to throw'); + } + catch (err) { + assert(err.message.match(/ENOENT.*broken_symlink/), `Expected broken symlink error message, actual: '${err.message}'`); + } + + done(); + }); + it('empty find path returns empty array', (done: MochaDone) => { this.timeout(1000); From 29043561bfbc434c0e8457809dfea68b98470ef9 Mon Sep 17 00:00:00 2001 From: Brad Willis <37845348+bradwillis@users.noreply.github.com> Date: Wed, 2 May 2018 14:29:26 -0400 Subject: [PATCH 050/259] Update schema to support showEnvironmentBlock (#350) * Update schema to support showEnvironmentBlock --- tasks.schema.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tasks.schema.json b/tasks.schema.json index 4f7c36486..41be6d257 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -29,6 +29,13 @@ "author": { "type": "string" }, + "preview": { + "type": "boolean" + }, + "showEnvironmentVariables": { + "type": "boolean" + "description": "Toggles showing the environment variable editor in the task editor UI. Allows passing environment variables to script based tasks." + }, "runsOn": { "type": "array", "items": { From bc5701bfe82f1308f61f5b521058ec227e51a61c Mon Sep 17 00:00:00 2001 From: Mario Majcica Date: Sat, 12 May 2018 21:59:58 +0200 Subject: [PATCH 051/259] Added quoutes around test tooling invocation --- powershell/make.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/powershell/make.js b/powershell/make.js index ebcd686c8..957a07a7e 100644 --- a/powershell/make.js +++ b/powershell/make.js @@ -66,10 +66,10 @@ target.test = function() { target.build(); util.mkdir('-p', testPath); - util.run(`tsc --outDir ${testPath} --module commonjs --rootDir Tests Tests/lib/psRunner.ts`); - util.run(`tsc --outDir ${testPath} --rootDir Tests Tests/L0/_suite.ts`); + util.run(`tsc --outDir "${testPath}" --module commonjs --rootDir Tests Tests/lib/psRunner.ts`); + util.run(`tsc --outDir "${testPath}" --rootDir Tests Tests/L0/_suite.ts`); util.cp('-r', path.join('Tests', '*'), testPath); - util.run('mocha ' + path.join(testPath, 'L0', '_suite.js')); + util.run('mocha "' + path.join(testPath, 'L0', '_suite.js') + '"'); } target.loc = function() { From caf32e0afa950e14687d5d9c71d1e22f8d4e16fc Mon Sep 17 00:00:00 2001 From: Mario Majcica Date: Sat, 12 May 2018 21:35:07 +0200 Subject: [PATCH 052/259] Fixed isses with $env:TMP in PS Tests --- .../Tests/L0/Assert-Path.DoesNotThrowWhenPathExists.ps1 | 2 +- powershell/Tests/L0/Assert-Path.ThrowsWhenNotFound.ps1 | 2 +- ...gacyPatternSupportsDirectoryNameSingleCharWildcard.ps1 | 2 +- ...d-Files.LegacyPatternSupportsDirectoryNameWildcard.ps1 | 2 +- .../L0/Find-Files.LegacyPatternSupportsExcludePattern.ps1 | 2 +- ...es.LegacyPatternSupportsFileNameSingleCharWildcard.ps1 | 2 +- .../Find-Files.LegacyPatternSupportsFileNameWildcard.ps1 | 2 +- .../Tests/L0/Find-Files.LegacyPatternSupportsGlobstar.ps1 | 2 +- ...Find-Files.LegacyPatternSupportsIncludeDirectories.ps1 | 2 +- ...-Files.LegacyPatternSupportsIncludeDirectoriesOnly.ps1 | 2 +- ...nd-Files.LegacyPatternSupportsInterSegmentWildcard.ps1 | 2 +- .../Tests/L0/Find-Files.LegacyPatternUnionsMatches.ps1 | 2 +- powershell/Tests/L0/Find-Match.AggregatesMatches.ps1 | 2 +- .../Tests/L0/Find-Match.AppliesDefaultFindOptions.ps1 | 2 +- .../Tests/L0/Find-Match.AppliesDefaultMatchOptions.ps1 | 2 +- powershell/Tests/L0/Find-Match.BracesNotEscaped.ps1 | 2 +- .../Tests/L0/Find-Match.CountsLeadingNegateMarkers.ps1 | 2 +- .../Tests/L0/Find-Match.DefaultRootFallsBackToCwd.ps1 | 2 +- ...efaultRootFallsBackToSystemDefaultWorkingDirectory.ps1 | 2 +- .../Tests/L0/Find-Match.DoesNotDuplicateMatches.ps1 | 2 +- .../Find-Match.EscapesDefaultRootWhenRootingPatterns.ps1 | 2 +- .../Find-Match.EvaluatesCommentsBeforeExpandingBraces.ps1 | 2 +- .../L0/Find-Match.EvaluatesCommentsBeforeNegation.ps1 | 2 +- .../Find-Match.EvaluatesNegationBeforeExpandingBraces.ps1 | 2 +- powershell/Tests/L0/Find-Match.SinglePattern.ps1 | 2 +- powershell/Tests/L0/Find-Match.SkipsEmptyPatterns.ps1 | 2 +- .../Tests/L0/Find-Match.SupportsCustomFindOptions.ps1 | 2 +- powershell/Tests/L0/Find-Match.SupportsFlipNegateTrue.ps1 | 2 +- .../L0/Find-Match.SupportsInterleavedExcludePatterns.ps1 | 2 +- .../L0/Find-Match.SupportsMatchBaseExcludePatterns.ps1 | 2 +- .../L0/Find-Match.SupportsMatchBaseIncludePatterns.ps1 | 2 +- ...ind-Match.SupportsMatchBaseIncludePatternsWithGlob.ps1 | 2 +- powershell/Tests/L0/Find-Match.SupportsNoBraceFalse.ps1 | 2 +- powershell/Tests/L0/Find-Match.SupportsNoCommentTrue.ps1 | 2 +- powershell/Tests/L0/Find-Match.SupportsNoNegateTrue.ps1 | 2 +- powershell/Tests/L0/Find-Match.TrimsPatterns.ps1 | 2 +- .../L0/Find-Match.TrimsWhitespaceAfterNegateMarkers.ps1 | 2 +- .../L0/Get-FindResult.DoesNotFollowSpecifiedSymlink.ps1 | 2 +- .../Tests/L0/Get-FindResult.DoesNotFollowSymlink.ps1 | 2 +- .../L0/Get-FindResult.DoesNotFollowSymlinkWhen-H.ps1 | 2 +- .../L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-H.ps1 | 2 +- .../L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-L.ps1 | 2 +- .../Tests/L0/Get-FindResult.FollowsSymlinkWhen-L.ps1 | 2 +- .../Tests/L0/Get-FindResult.ReturnsEmptyWhenNotExists.ps1 | 2 +- powershell/Tests/L0/Get-FindResult.ReturnsHiddenFiles.ps1 | 2 +- .../L0/Get-LocString.HandlesDotNetFormatException.ps1 | 2 +- .../L0/Get-LocString.OverlaysWithCultureSpecific.ps1 | 2 +- .../L0/Get-LocString.SupportsDotNetFormatStrings.ps1 | 2 +- .../Tests/L0/Invoke-Tool.SupportsOutputEncoding.ps1 | 2 +- .../Tests/L0/Invoke-Tool.SupportsWorkingDirectory.ps1 | 8 ++++---- 50 files changed, 53 insertions(+), 53 deletions(-) diff --git a/powershell/Tests/L0/Assert-Path.DoesNotThrowWhenPathExists.ps1 b/powershell/Tests/L0/Assert-Path.DoesNotThrowWhenPathExists.ps1 index 1b02fb314..804078738 100644 --- a/powershell/Tests/L0/Assert-Path.DoesNotThrowWhenPathExists.ps1 +++ b/powershell/Tests/L0/Assert-Path.DoesNotThrowWhenPathExists.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { $directory = (New-Item -Path $tempDirectory\SomeDir -ItemType Directory).FullName diff --git a/powershell/Tests/L0/Assert-Path.ThrowsWhenNotFound.ps1 b/powershell/Tests/L0/Assert-Path.ThrowsWhenNotFound.ps1 index c6985224b..a38da6874 100644 --- a/powershell/Tests/L0/Assert-Path.ThrowsWhenNotFound.ps1 +++ b/powershell/Tests/L0/Assert-Path.ThrowsWhenNotFound.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { $directory = New-Item -Path $tempDirectory\SomeDir -ItemType Directory diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsDirectoryNameSingleCharWildcard.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsDirectoryNameSingleCharWildcard.ps1 index 9dab57e86..a3f3caf11 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsDirectoryNameSingleCharWildcard.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsDirectoryNameSingleCharWildcard.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsDirectoryNameWildcard.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsDirectoryNameWildcard.ps1 index 27dc04731..c5817627b 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsDirectoryNameWildcard.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsDirectoryNameWildcard.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsExcludePattern.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsExcludePattern.ps1 index 1023e7fa5..5f9bf8cf2 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsExcludePattern.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsExcludePattern.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsFileNameSingleCharWildcard.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsFileNameSingleCharWildcard.ps1 index d58799e61..76096710d 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsFileNameSingleCharWildcard.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsFileNameSingleCharWildcard.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsFileNameWildcard.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsFileNameWildcard.ps1 index 9c3e4eef1..bc83655b3 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsFileNameWildcard.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsFileNameWildcard.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsGlobstar.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsGlobstar.ps1 index ede36ea1f..8b533876a 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsGlobstar.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsGlobstar.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsIncludeDirectories.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsIncludeDirectories.ps1 index ea7a0c7a0..d0da9c667 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsIncludeDirectories.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsIncludeDirectories.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsIncludeDirectoriesOnly.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsIncludeDirectoriesOnly.ps1 index d58fbcbe8..98abdea34 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsIncludeDirectoriesOnly.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsIncludeDirectoriesOnly.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsInterSegmentWildcard.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsInterSegmentWildcard.ps1 index 7d00cb3ae..6df41d905 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternSupportsInterSegmentWildcard.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternSupportsInterSegmentWildcard.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Files.LegacyPatternUnionsMatches.ps1 b/powershell/Tests/L0/Find-Files.LegacyPatternUnionsMatches.ps1 index 23398dc35..8ad3c1592 100644 --- a/powershell/Tests/L0/Find-Files.LegacyPatternUnionsMatches.ps1 +++ b/powershell/Tests/L0/Find-Files.LegacyPatternUnionsMatches.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.AggregatesMatches.ps1 b/powershell/Tests/L0/Find-Match.AggregatesMatches.ps1 index 24d3ae5ba..6815b9b92 100644 --- a/powershell/Tests/L0/Find-Match.AggregatesMatches.ps1 +++ b/powershell/Tests/L0/Find-Match.AggregatesMatches.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.AppliesDefaultFindOptions.ps1 b/powershell/Tests/L0/Find-Match.AppliesDefaultFindOptions.ps1 index b8fb4e991..f416ece16 100644 --- a/powershell/Tests/L0/Find-Match.AppliesDefaultFindOptions.ps1 +++ b/powershell/Tests/L0/Find-Match.AppliesDefaultFindOptions.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.AppliesDefaultMatchOptions.ps1 b/powershell/Tests/L0/Find-Match.AppliesDefaultMatchOptions.ps1 index aaadf5ea3..c2eac9d4b 100644 --- a/powershell/Tests/L0/Find-Match.AppliesDefaultMatchOptions.ps1 +++ b/powershell/Tests/L0/Find-Match.AppliesDefaultMatchOptions.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.BracesNotEscaped.ps1 b/powershell/Tests/L0/Find-Match.BracesNotEscaped.ps1 index d3c7f465e..5bd3ade13 100644 --- a/powershell/Tests/L0/Find-Match.BracesNotEscaped.ps1 +++ b/powershell/Tests/L0/Find-Match.BracesNotEscaped.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.CountsLeadingNegateMarkers.ps1 b/powershell/Tests/L0/Find-Match.CountsLeadingNegateMarkers.ps1 index d01d58d21..cb0921fc4 100644 --- a/powershell/Tests/L0/Find-Match.CountsLeadingNegateMarkers.ps1 +++ b/powershell/Tests/L0/Find-Match.CountsLeadingNegateMarkers.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.DefaultRootFallsBackToCwd.ps1 b/powershell/Tests/L0/Find-Match.DefaultRootFallsBackToCwd.ps1 index 1aa997b4e..90111ae92 100644 --- a/powershell/Tests/L0/Find-Match.DefaultRootFallsBackToCwd.ps1 +++ b/powershell/Tests/L0/Find-Match.DefaultRootFallsBackToCwd.ps1 @@ -6,7 +6,7 @@ param() Invoke-VstsTaskScript -ScriptBlock { $originalSystemDefaultWorkingDirectory = $env:SYSTEM_DEFAULTWORKINGDIRECTORY $originalCwd = Get-Location - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.DefaultRootFallsBackToSystemDefaultWorkingDirectory.ps1 b/powershell/Tests/L0/Find-Match.DefaultRootFallsBackToSystemDefaultWorkingDirectory.ps1 index 468e773db..eb459811f 100644 --- a/powershell/Tests/L0/Find-Match.DefaultRootFallsBackToSystemDefaultWorkingDirectory.ps1 +++ b/powershell/Tests/L0/Find-Match.DefaultRootFallsBackToSystemDefaultWorkingDirectory.ps1 @@ -5,7 +5,7 @@ param() . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { $originalSystemDefaultWorkingDirectory = $env:SYSTEM_DEFAULTWORKINGDIRECTORY - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.DoesNotDuplicateMatches.ps1 b/powershell/Tests/L0/Find-Match.DoesNotDuplicateMatches.ps1 index 10ad93984..f84fba7d6 100644 --- a/powershell/Tests/L0/Find-Match.DoesNotDuplicateMatches.ps1 +++ b/powershell/Tests/L0/Find-Match.DoesNotDuplicateMatches.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.EscapesDefaultRootWhenRootingPatterns.ps1 b/powershell/Tests/L0/Find-Match.EscapesDefaultRootWhenRootingPatterns.ps1 index 585a23958..b9a276c1a 100644 --- a/powershell/Tests/L0/Find-Match.EscapesDefaultRootWhenRootingPatterns.ps1 +++ b/powershell/Tests/L0/Find-Match.EscapesDefaultRootWhenRootingPatterns.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.EvaluatesCommentsBeforeExpandingBraces.ps1 b/powershell/Tests/L0/Find-Match.EvaluatesCommentsBeforeExpandingBraces.ps1 index 1490cf110..28f1c9737 100644 --- a/powershell/Tests/L0/Find-Match.EvaluatesCommentsBeforeExpandingBraces.ps1 +++ b/powershell/Tests/L0/Find-Match.EvaluatesCommentsBeforeExpandingBraces.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.EvaluatesCommentsBeforeNegation.ps1 b/powershell/Tests/L0/Find-Match.EvaluatesCommentsBeforeNegation.ps1 index cc543d192..963830029 100644 --- a/powershell/Tests/L0/Find-Match.EvaluatesCommentsBeforeNegation.ps1 +++ b/powershell/Tests/L0/Find-Match.EvaluatesCommentsBeforeNegation.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.EvaluatesNegationBeforeExpandingBraces.ps1 b/powershell/Tests/L0/Find-Match.EvaluatesNegationBeforeExpandingBraces.ps1 index c7ef6b815..de8a2b605 100644 --- a/powershell/Tests/L0/Find-Match.EvaluatesNegationBeforeExpandingBraces.ps1 +++ b/powershell/Tests/L0/Find-Match.EvaluatesNegationBeforeExpandingBraces.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SinglePattern.ps1 b/powershell/Tests/L0/Find-Match.SinglePattern.ps1 index 31f23727d..af22e61c6 100644 --- a/powershell/Tests/L0/Find-Match.SinglePattern.ps1 +++ b/powershell/Tests/L0/Find-Match.SinglePattern.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SkipsEmptyPatterns.ps1 b/powershell/Tests/L0/Find-Match.SkipsEmptyPatterns.ps1 index df6553164..aa670f4cd 100644 --- a/powershell/Tests/L0/Find-Match.SkipsEmptyPatterns.ps1 +++ b/powershell/Tests/L0/Find-Match.SkipsEmptyPatterns.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SupportsCustomFindOptions.ps1 b/powershell/Tests/L0/Find-Match.SupportsCustomFindOptions.ps1 index 417f1e0e1..bf530537f 100644 --- a/powershell/Tests/L0/Find-Match.SupportsCustomFindOptions.ps1 +++ b/powershell/Tests/L0/Find-Match.SupportsCustomFindOptions.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SupportsFlipNegateTrue.ps1 b/powershell/Tests/L0/Find-Match.SupportsFlipNegateTrue.ps1 index 4982884e1..b840e70cf 100644 --- a/powershell/Tests/L0/Find-Match.SupportsFlipNegateTrue.ps1 +++ b/powershell/Tests/L0/Find-Match.SupportsFlipNegateTrue.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SupportsInterleavedExcludePatterns.ps1 b/powershell/Tests/L0/Find-Match.SupportsInterleavedExcludePatterns.ps1 index 7114207c0..b41d862ee 100644 --- a/powershell/Tests/L0/Find-Match.SupportsInterleavedExcludePatterns.ps1 +++ b/powershell/Tests/L0/Find-Match.SupportsInterleavedExcludePatterns.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SupportsMatchBaseExcludePatterns.ps1 b/powershell/Tests/L0/Find-Match.SupportsMatchBaseExcludePatterns.ps1 index 9e354aaa3..36b2bcec9 100644 --- a/powershell/Tests/L0/Find-Match.SupportsMatchBaseExcludePatterns.ps1 +++ b/powershell/Tests/L0/Find-Match.SupportsMatchBaseExcludePatterns.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SupportsMatchBaseIncludePatterns.ps1 b/powershell/Tests/L0/Find-Match.SupportsMatchBaseIncludePatterns.ps1 index 3927299fa..fe6c50fe6 100644 --- a/powershell/Tests/L0/Find-Match.SupportsMatchBaseIncludePatterns.ps1 +++ b/powershell/Tests/L0/Find-Match.SupportsMatchBaseIncludePatterns.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SupportsMatchBaseIncludePatternsWithGlob.ps1 b/powershell/Tests/L0/Find-Match.SupportsMatchBaseIncludePatternsWithGlob.ps1 index ccc044e4b..2a6446b6d 100644 --- a/powershell/Tests/L0/Find-Match.SupportsMatchBaseIncludePatternsWithGlob.ps1 +++ b/powershell/Tests/L0/Find-Match.SupportsMatchBaseIncludePatternsWithGlob.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SupportsNoBraceFalse.ps1 b/powershell/Tests/L0/Find-Match.SupportsNoBraceFalse.ps1 index 4982884e1..b840e70cf 100644 --- a/powershell/Tests/L0/Find-Match.SupportsNoBraceFalse.ps1 +++ b/powershell/Tests/L0/Find-Match.SupportsNoBraceFalse.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SupportsNoCommentTrue.ps1 b/powershell/Tests/L0/Find-Match.SupportsNoCommentTrue.ps1 index 6d5caa2e6..ad2c1b3a5 100644 --- a/powershell/Tests/L0/Find-Match.SupportsNoCommentTrue.ps1 +++ b/powershell/Tests/L0/Find-Match.SupportsNoCommentTrue.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.SupportsNoNegateTrue.ps1 b/powershell/Tests/L0/Find-Match.SupportsNoNegateTrue.ps1 index dfd746288..fc04a5fc6 100644 --- a/powershell/Tests/L0/Find-Match.SupportsNoNegateTrue.ps1 +++ b/powershell/Tests/L0/Find-Match.SupportsNoNegateTrue.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.TrimsPatterns.ps1 b/powershell/Tests/L0/Find-Match.TrimsPatterns.ps1 index 2f514ded7..816c4ab95 100644 --- a/powershell/Tests/L0/Find-Match.TrimsPatterns.ps1 +++ b/powershell/Tests/L0/Find-Match.TrimsPatterns.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Find-Match.TrimsWhitespaceAfterNegateMarkers.ps1 b/powershell/Tests/L0/Find-Match.TrimsWhitespaceAfterNegateMarkers.ps1 index ad42003dc..623eca703 100644 --- a/powershell/Tests/L0/Find-Match.TrimsWhitespaceAfterNegateMarkers.ps1 +++ b/powershell/Tests/L0/Find-Match.TrimsWhitespaceAfterNegateMarkers.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Get-FindResult.DoesNotFollowSpecifiedSymlink.ps1 b/powershell/Tests/L0/Get-FindResult.DoesNotFollowSpecifiedSymlink.ps1 index 7aba63703..00b40514a 100644 --- a/powershell/Tests/L0/Get-FindResult.DoesNotFollowSpecifiedSymlink.ps1 +++ b/powershell/Tests/L0/Get-FindResult.DoesNotFollowSpecifiedSymlink.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Get-FindResult.DoesNotFollowSymlink.ps1 b/powershell/Tests/L0/Get-FindResult.DoesNotFollowSymlink.ps1 index 68f5d8f3f..86bccec1e 100644 --- a/powershell/Tests/L0/Get-FindResult.DoesNotFollowSymlink.ps1 +++ b/powershell/Tests/L0/Get-FindResult.DoesNotFollowSymlink.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Get-FindResult.DoesNotFollowSymlinkWhen-H.ps1 b/powershell/Tests/L0/Get-FindResult.DoesNotFollowSymlinkWhen-H.ps1 index 8445d8489..c7ebe2672 100644 --- a/powershell/Tests/L0/Get-FindResult.DoesNotFollowSymlinkWhen-H.ps1 +++ b/powershell/Tests/L0/Get-FindResult.DoesNotFollowSymlinkWhen-H.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-H.ps1 b/powershell/Tests/L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-H.ps1 index 5312d951a..efdd79c1f 100644 --- a/powershell/Tests/L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-H.ps1 +++ b/powershell/Tests/L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-H.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-L.ps1 b/powershell/Tests/L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-L.ps1 index e5624acf9..8ea902aa8 100644 --- a/powershell/Tests/L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-L.ps1 +++ b/powershell/Tests/L0/Get-FindResult.FollowsSpecifiedSymlinkWhen-L.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Get-FindResult.FollowsSymlinkWhen-L.ps1 b/powershell/Tests/L0/Get-FindResult.FollowsSymlinkWhen-L.ps1 index 4091dcc11..238a04934 100644 --- a/powershell/Tests/L0/Get-FindResult.FollowsSymlinkWhen-L.ps1 +++ b/powershell/Tests/L0/Get-FindResult.FollowsSymlinkWhen-L.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Get-FindResult.ReturnsEmptyWhenNotExists.ps1 b/powershell/Tests/L0/Get-FindResult.ReturnsEmptyWhenNotExists.ps1 index 844430834..7e211cf6c 100644 --- a/powershell/Tests/L0/Get-FindResult.ReturnsEmptyWhenNotExists.ps1 +++ b/powershell/Tests/L0/Get-FindResult.ReturnsEmptyWhenNotExists.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Get-FindResult.ReturnsHiddenFiles.ps1 b/powershell/Tests/L0/Get-FindResult.ReturnsHiddenFiles.ps1 index aa605a85c..a7423deb8 100644 --- a/powershell/Tests/L0/Get-FindResult.ReturnsHiddenFiles.ps1 +++ b/powershell/Tests/L0/Get-FindResult.ReturnsHiddenFiles.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { diff --git a/powershell/Tests/L0/Get-LocString.HandlesDotNetFormatException.ps1 b/powershell/Tests/L0/Get-LocString.HandlesDotNetFormatException.ps1 index 5d8f8c2a0..380432ac3 100644 --- a/powershell/Tests/L0/Get-LocString.HandlesDotNetFormatException.ps1 +++ b/powershell/Tests/L0/Get-LocString.HandlesDotNetFormatException.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { New-Item -Path $tempDirectory\my.json -ItemType File -Value @' diff --git a/powershell/Tests/L0/Get-LocString.OverlaysWithCultureSpecific.ps1 b/powershell/Tests/L0/Get-LocString.OverlaysWithCultureSpecific.ps1 index 39a61ee30..0d3d10324 100644 --- a/powershell/Tests/L0/Get-LocString.OverlaysWithCultureSpecific.ps1 +++ b/powershell/Tests/L0/Get-LocString.OverlaysWithCultureSpecific.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { New-Item -Path $tempDirectory\my.json -ItemType File -Value @' diff --git a/powershell/Tests/L0/Get-LocString.SupportsDotNetFormatStrings.ps1 b/powershell/Tests/L0/Get-LocString.SupportsDotNetFormatStrings.ps1 index 2975c9637..079203559 100644 --- a/powershell/Tests/L0/Get-LocString.SupportsDotNetFormatStrings.ps1 +++ b/powershell/Tests/L0/Get-LocString.SupportsDotNetFormatStrings.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { New-Item -Path $tempDirectory\my.json -ItemType File -Value @' diff --git a/powershell/Tests/L0/Invoke-Tool.SupportsOutputEncoding.ps1 b/powershell/Tests/L0/Invoke-Tool.SupportsOutputEncoding.ps1 index 5508e9df3..54cfb6c5e 100644 --- a/powershell/Tests/L0/Invoke-Tool.SupportsOutputEncoding.ps1 +++ b/powershell/Tests/L0/Invoke-Tool.SupportsOutputEncoding.ps1 @@ -4,7 +4,7 @@ param() # Arrange. . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { set-Content -LiteralPath $tempDirectory\Program.cs -Value @" diff --git a/powershell/Tests/L0/Invoke-Tool.SupportsWorkingDirectory.ps1 b/powershell/Tests/L0/Invoke-Tool.SupportsWorkingDirectory.ps1 index c3c9de0eb..f42f3e7e8 100644 --- a/powershell/Tests/L0/Invoke-Tool.SupportsWorkingDirectory.ps1 +++ b/powershell/Tests/L0/Invoke-Tool.SupportsWorkingDirectory.ps1 @@ -5,13 +5,13 @@ param() . $PSScriptRoot\..\lib\Initialize-Test.ps1 Invoke-VstsTaskScript -ScriptBlock { $originalLocation = $PWD - $tempDirectory = [System.IO.Path]::Combine($env:TMP, [System.IO.Path]::GetRandomFileName()) + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) New-Item -Path $tempDirectory -ItemType Directory | ForEach-Object { $_.FullName } try { Set-Location $env:TMP $variableSets = @( - @{ Expected = $env:TMP ; Splat = @{ } } - @{ Expected = $env:TMP ; Splat = @{ WorkingDirectory = $env:TMP } } + @{ Expected = [System.IO.Path]::GetTempPath().TrimEnd('\') ; Splat = @{ } } + @{ Expected = [System.IO.Path]::GetTempPath().TrimEnd('\') ; Splat = @{ WorkingDirectory = [System.IO.Path]::GetTempPath() } } @{ Expected = $tempDirectory ; Splat = @{ WorkingDirectory = $tempDirectory } } ) foreach ($variableSet in $variableSets) { @@ -22,7 +22,7 @@ Invoke-VstsTaskScript -ScriptBlock { # Assert. Assert-AreEqual $variableSet.Expected $actual - Assert-AreEqual $env:TMP (Get-Location).Path + Assert-AreEqual ([System.IO.Path]::GetTempPath().TrimEnd('\')) (Get-Location).Path } } finally { Set-Location $originalLocation From 790560a1599bc7571c1486d605269f3944214fe3 Mon Sep 17 00:00:00 2001 From: Mario Majcica Date: Sat, 12 May 2018 20:55:03 +0200 Subject: [PATCH 053/259] Added SecureFile functions in PS SDK --- powershell/Tests/L0/Get-SecureFileName.ps1 | 17 +++++ powershell/Tests/L0/Get-SecureFileTicket.ps1 | 15 +++++ powershell/VstsTaskSdk/InputFunctions.ps1 | 68 ++++++++++++++++++++ powershell/VstsTaskSdk/VstsTaskSdk.psm1 | 2 + 4 files changed, 102 insertions(+) create mode 100644 powershell/Tests/L0/Get-SecureFileName.ps1 create mode 100644 powershell/Tests/L0/Get-SecureFileTicket.ps1 diff --git a/powershell/Tests/L0/Get-SecureFileName.ps1 b/powershell/Tests/L0/Get-SecureFileName.ps1 new file mode 100644 index 000000000..3689588b6 --- /dev/null +++ b/powershell/Tests/L0/Get-SecureFileName.ps1 @@ -0,0 +1,17 @@ +[CmdletBinding()] +param() + +# Arrange. +. $PSScriptRoot\..\lib\Initialize-Test.ps1 +$env:SECUREFILE_NAME_10 = 'securefile10.p12' + +Invoke-VstsTaskScript -ScriptBlock { + # Act. + $actual = Get-VstsSecureFileName -Id '10' + + # Assert. + Assert-IsNotNullOrEmpty $actual + Assert-AreEqual 'securefile10.p12' $actual + + Assert-IsNullOrEmpty $env:SECUREFILE_NAME_10 +} \ No newline at end of file diff --git a/powershell/Tests/L0/Get-SecureFileTicket.ps1 b/powershell/Tests/L0/Get-SecureFileTicket.ps1 new file mode 100644 index 000000000..4100f13c7 --- /dev/null +++ b/powershell/Tests/L0/Get-SecureFileTicket.ps1 @@ -0,0 +1,15 @@ +[CmdletBinding()] +param() + +# Arrange. +. $PSScriptRoot\..\lib\Initialize-Test.ps1 +$env:SECUREFILE_TICKET_10 = 'rsaticket10' +Invoke-VstsTaskScript -ScriptBlock { + # Act. + $actual = Get-VstsSecureFileTicket -Id '10' + + # Assert. + Assert-IsNotNullOrEmpty $actual + Assert-AreEqual 'rsaticket10' $actual + Assert-IsNullOrEmpty $env:SECUREFILE_TICKET_10 +} \ No newline at end of file diff --git a/powershell/VstsTaskSdk/InputFunctions.ps1 b/powershell/VstsTaskSdk/InputFunctions.ps1 index 21c4ade1a..d7eb7c282 100644 --- a/powershell/VstsTaskSdk/InputFunctions.ps1 +++ b/powershell/VstsTaskSdk/InputFunctions.ps1 @@ -64,6 +64,74 @@ function Get-Endpoint { } } +<# +.SYNOPSIS +Gets a secure file ticket. + +.DESCRIPTION +Gets the secure file ticket that can be used to download the secure file contents. + +.PARAMETER Id +Secure file id. + +.PARAMETER Require +Writes an error to the error pipeline if the ticket is not found. +#> +function Get-SecureFileTicket { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Id, + [switch]$Require) + + $originalErrorActionPreference = $ErrorActionPreference + try { + $ErrorActionPreference = 'Stop' + + $description = Get-LocString -Key PSLIB_Input0 -ArgumentList $Id + $key = "SECUREFILE_TICKET_$Id" + + Get-VaultValue -Description $description -Key $key -Require:$Require + } catch { + $ErrorActionPreference = $originalErrorActionPreference + Write-Error $_ + } +} + +<# +.SYNOPSIS +Gets a secure file name. + +.DESCRIPTION +Gets the name for a secure file. + +.PARAMETER Id +Secure file id. + +.PARAMETER Require +Writes an error to the error pipeline if the ticket is not found. +#> +function Get-SecureFileName { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Id, + [switch]$Require) + + $originalErrorActionPreference = $ErrorActionPreference + try { + $ErrorActionPreference = 'Stop' + + $description = Get-LocString -Key PSLIB_Input0 -ArgumentList $Id + $key = "SECUREFILE_NAME_$Id" + + Get-VaultValue -Description $description -Key $key -Require:$Require + } catch { + $ErrorActionPreference = $originalErrorActionPreference + Write-Error $_ + } +} + <# .SYNOPSIS Gets an input. diff --git a/powershell/VstsTaskSdk/VstsTaskSdk.psm1 b/powershell/VstsTaskSdk/VstsTaskSdk.psm1 index 833a621cc..7f9905638 100644 --- a/powershell/VstsTaskSdk/VstsTaskSdk.psm1 +++ b/powershell/VstsTaskSdk/VstsTaskSdk.psm1 @@ -38,6 +38,8 @@ Export-ModuleMember -Function @( 'Select-Match' # Input functions. 'Get-Endpoint' + 'Get-SecureFileTicket' + 'Get-SecureFileName' 'Get-Input' 'Get-TaskVariable' 'Get-TaskVariableInfo' From b16a15151dba3a98777bfcdd6f86e57d0ec73fa7 Mon Sep 17 00:00:00 2001 From: madhurig Date: Tue, 17 Apr 2018 12:06:58 -0400 Subject: [PATCH 054/259] Adding secureFile input type in task.json scheme --- tasks.schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks.schema.json b/tasks.schema.json index 41be6d257..627d46b48 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -148,6 +148,7 @@ "multiLine", "pickList", "radio", + "secureFile", "string" ] }, From 29a04de25363ce9a3dbcd2c4cb5a0aeb25f3dc6a Mon Sep 17 00:00:00 2001 From: Mario Majcica Date: Fri, 18 May 2018 18:41:27 +0200 Subject: [PATCH 055/259] Added missing logging commands (#361) * Added missing logging commands * Renamed Write-SetEndpointField function to Write-SetEndpoint --- .../VstsTaskSdk/LoggingCommandFunctions.ps1 | 95 +++++++++++++++++++ powershell/VstsTaskSdk/VstsTaskSdk.psm1 | 5 + 2 files changed, 100 insertions(+) diff --git a/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 b/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 index a1c8b1eb3..a2eeb9d68 100644 --- a/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 +++ b/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 @@ -36,6 +36,50 @@ function Write-AddAttachment { .SYNOPSIS See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md +.PARAMETER AsOutput +Indicates whether to write the logging command directly to the host or to the output pipeline. +#> +function Write-UploadSummary { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Path, + [switch]$AsOutput) + + Write-LoggingCommand -Area 'task' -Event 'uploadsummary' -Data $Path -AsOutput:$AsOutput +} + +<# +.SYNOPSIS +See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +.PARAMETER AsOutput +Indicates whether to write the logging command directly to the host or to the output pipeline. +#> +function Write-SetEndpoint { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Id, + [Parameter(Mandatory = $true)] + [string]$Field, + [Parameter(Mandatory = $true)] + [string]$Key, + [Parameter(Mandatory = $true)] + [string]$Value, + [switch]$AsOutput) + + Write-LoggingCommand -Area 'task' -Event 'setendpoint' -Data $Value -Properties @{ + 'id' = $Id + 'field' = $Field + 'key' = $Key + } -AsOutput:$AsOutput +} + +<# +.SYNOPSIS +See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + .PARAMETER AsOutput Indicates whether to write the logging command directly to the host or to the output pipeline. #> @@ -287,6 +331,40 @@ function Write-TaskWarning { .SYNOPSIS See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md +.PARAMETER AsOutput +Indicates whether to write the logging command directly to the host or to the output pipeline. +#> +function Write-UploadFile { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Path, + [switch]$AsOutput) + + Write-LoggingCommand -Area 'task' -Event 'uploadfile' -Data $Path -AsOutput:$AsOutput +} + +<# +.SYNOPSIS +See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +.PARAMETER AsOutput +Indicates whether to write the logging command directly to the host or to the output pipeline. +#> +function Write-PrependPath { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Path, + [switch]$AsOutput) + + Write-LoggingCommand -Area 'task' -Event 'prependpath' -Data $Path -AsOutput:$AsOutput +} + +<# +.SYNOPSIS +See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + .PARAMETER AsOutput Indicates whether to write the logging command directly to the host or to the output pipeline. #> @@ -341,6 +419,23 @@ function Write-UploadBuildLog { Write-LoggingCommand -Area 'build' -Event 'uploadlog' -Data $Path -AsOutput:$AsOutput } +<# +.SYNOPSIS +See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +.PARAMETER AsOutput +Indicates whether to write the logging command directly to the host or to the output pipeline. +#> +function Write-UpdateReleaseName { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Name, + [switch]$AsOutput) + + Write-LoggingCommand -Area 'release' -Event 'updatereleasename' -Data $Name -AsOutput:$AsOutput +} + ######################################## # Private functions. ######################################## diff --git a/powershell/VstsTaskSdk/VstsTaskSdk.psm1 b/powershell/VstsTaskSdk/VstsTaskSdk.psm1 index 7f9905638..43b9561a4 100644 --- a/powershell/VstsTaskSdk/VstsTaskSdk.psm1 +++ b/powershell/VstsTaskSdk/VstsTaskSdk.psm1 @@ -54,6 +54,8 @@ Export-ModuleMember -Function @( 'Write-AddBuildTag' 'Write-AssociateArtifact' 'Write-LogDetail' + 'Write-PrependPath' + 'Write-SetEndpoint' 'Write-SetProgress' 'Write-SetResult' 'Write-SetSecret' @@ -63,8 +65,11 @@ Export-ModuleMember -Function @( 'Write-TaskVerbose' 'Write-TaskWarning' 'Write-UpdateBuildNumber' + 'Write-UpdateReleaseName' 'Write-UploadArtifact' 'Write-UploadBuildLog' + 'Write-UploadFile' + 'Write-UploadSummary' # Out functions. 'Out-Default' # Server OM functions. From a96eb8b1cf63b4bc45cb0174ef8b3c3af655d6d5 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Fri, 18 May 2018 16:43:53 -0400 Subject: [PATCH 056/259] Update typescript version --- node/package-lock.json | 14 +++++++------- node/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 4e3305bdb..d2b2c8c7b 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -165,7 +165,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "requires": { "brace-expansion": "1.1.11" } @@ -252,7 +252,7 @@ "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=", "dev": true }, "readable-stream": { @@ -273,7 +273,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", "dev": true }, "semver": { @@ -289,7 +289,7 @@ "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", "dev": true, "requires": { "safe-buffer": "5.1.1" @@ -336,9 +336,9 @@ "dev": true }, "typescript": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.7.tgz", - "integrity": "sha1-NeODjeMckc/h2MIODleF04aTikk=", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.4.tgz", + "integrity": "sha1-PTgyGCgjHkNPKHUUlZw3qCtin0I=", "dev": true }, "util-deprecate": { diff --git a/node/package.json b/node/package.json index 1c0dce6b2..2530ace4b 100644 --- a/node/package.json +++ b/node/package.json @@ -35,6 +35,6 @@ "devDependencies": { "mocha": "5.0.2", "sync-request": "3.0.1", - "typescript": "1.8.7" + "typescript": "^2.3.4" } } From 856c48e92acd528a85fc962ec5093a499d6ef8c4 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Fri, 18 May 2018 17:21:58 -0400 Subject: [PATCH 057/259] Fix low-hanging fruit --- node/internal.ts | 14 ++++++++------ node/mock-answer.ts | 15 ++------------- node/mock-run.ts | 2 +- node/mock-task.ts | 39 ++++++++++++++++++++++++++++++++------- node/mock-test.ts | 20 ++++++++++++-------- node/mock-toolrunner.ts | 18 +++++++++--------- node/task.ts | 22 +++++++++++----------- node/toolrunner.ts | 28 ++++++++++++++-------------- node/tsconfig.json | 3 ++- node/vault.ts | 6 +++--- 10 files changed, 94 insertions(+), 73 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index 532c81318..7b7d8069c 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -53,11 +53,11 @@ export function _writeLine(str: string): void { _outStream.write(str + os.EOL); } -export function _setStdStream(stdStream): void { +export function _setStdStream(stdStream: NodeJS.WritableStream): void { _outStream = stdStream; } -export function _setErrStream(errStream): void { +export function _setErrStream(errStream: NodeJS.WritableStream): void { _errStream = errStream; } @@ -71,7 +71,7 @@ let _libResourceFileLoaded: boolean = false; let _resourceCulture: string = 'en-US'; function _loadResJson(resjsonFile: string): any { - var resJson: {} = null; + var resJson: {} | null = null; if (_exist(resjsonFile)) { var resjsonContent = fs.readFileSync(resjsonFile, 'utf8').toString(); // remove BOM @@ -107,7 +107,7 @@ function _loadLocStrings(resourceFile: string, culture: string): { [key: string] var localizedResourceFile = path.join(path.dirname(resourceFile), 'Strings', 'resources.resjson'); var upperCulture = culture.toUpperCase(); - var cultures = []; + var cultures: string[] = []; try { cultures = fs.readdirSync(localizedResourceFile); } catch (ex) { } for (var i = 0; i < cultures.length; i++) { @@ -274,7 +274,7 @@ export interface _KnownVariableInfo { // Cmd Helpers //----------------------------------------------------- -export function _command(command: string, properties, message: string) { +export function _command(command: string, properties: any, message: string) { var taskCmd = new tcm.TaskCommand(command, properties, message); _writeLine(taskCmd.toString()); } @@ -304,7 +304,9 @@ export function _debug(message: string): void { export function _exist(path: string): boolean { var exist = false; try { - exist = path && fs.statSync(path) != null; + if (path && fs.statSync(path) != null) { + exist = true; + } } catch (err) { if (err && err.code === 'ENOENT') { exist = false; diff --git a/node/mock-answer.ts b/node/mock-answer.ts index eabf703a6..0760d94ee 100644 --- a/node/mock-answer.ts +++ b/node/mock-answer.ts @@ -21,21 +21,10 @@ export interface TaskLibAnswers { which?: { [key: string]: string }, } -// TODO TypeScript 2.1: replace with `keyof TaskLibAnswers` -export type MockedCommand = 'checkPath' - | 'cwd' - | 'exec' - | 'exist' - | 'find' - | 'findMatch' - | 'ls' - | 'osType' - | 'rmRF' - | 'stats' - | 'which'; +export type MockedCommand = keyof TaskLibAnswers; export class MockAnswers { - private _answers: TaskLibAnswers; + private _answers: TaskLibAnswers | undefined; public initialize(answers: TaskLibAnswers) { if (!answers) { diff --git a/node/mock-run.ts b/node/mock-run.ts index 8cb3245fb..90d432536 100644 --- a/node/mock-run.ts +++ b/node/mock-run.ts @@ -7,7 +7,7 @@ export class TaskMockRunner { } _taskPath: string; - _answers: ma.TaskLibAnswers; + _answers: ma.TaskLibAnswers | undefined; _exports: {[key: string]: any} = { }; _moduleCount: number = 0; diff --git a/node/mock-task.ts b/node/mock-task.ts index 731d2cb88..991ceb89a 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -69,7 +69,7 @@ module.exports.getBoolInput = task.getBoolInput; module.exports.getDelimitedInput = task.getDelimitedInput; module.exports.filePathSupplied = task.filePathSupplied; -function getPathInput(name, required, check) { +function getPathInput(name: string, required?: boolean, check?: boolean): string { var inval = module.exports.getInput(name, required); if (inval) { if (check) { @@ -131,7 +131,34 @@ export class FsStats implements fs.Stats { ctime: Date; birthtime: Date; - setAnswers(mockResponses) { + constructor() { + this.m_isFile = false; + this.m_isDirectory = false; + this.m_isBlockDevice = false; + this.m_isCharacterDevice = false; + this.m_isSymbolicLink = false; + this.m_isFIFO = false; + this.m_isSocket = false; + + this.dev = 0; + this.ino = 0; + this.mode = 0; + this.nlink = 0; + this.uid = 0; + this.gid = 0; + this.rdev = 0; + this.size = 0; + this.blksize = 0; + this.blocks = 0; + this.atime = new Date(); + this.mtime = new Date(); + this.ctime = new Date(); + this.m_isSocket = false; + + this.birthtime = new Date(); + } + + setAnswers(mockResponses: any): void { this.m_isFile = mockResponses['isFile'] || false; this.m_isDirectory = mockResponses['isDirectory'] || false; this.m_isBlockDevice = mockResponses['isBlockDevice'] || false; @@ -362,13 +389,11 @@ export function findMatch(defaultRoot: string, patterns: string[] | string) : st // Test Publisher //----------------------------------------------------- export class TestPublisher { - constructor(testRunner) { + constructor(public testRunner: string) { this.testRunner = testRunner; } - public testRunner: string; - - public publish(resultFiles, mergeResults, platform, config, runTitle, publishRunAttachments) { + public publish(resultFiles?: string, mergeResults?: string, platform?: string, config?: string, runTitle?: string, publishRunAttachments?: string) { var properties = <{ [key: string]: string }>{}; properties['type'] = this.testRunner; @@ -407,7 +432,7 @@ export class TestPublisher { export class CodeCoveragePublisher { constructor() { } - public publish(codeCoverageTool, summaryFileLocation, reportDirectory, additionalCodeCoverageFiles) { + public publish(codeCoverageTool?: string, summaryFileLocation?: string, reportDirectory?: string, additionalCodeCoverageFiles?: string) { var properties = <{ [key: string]: string }>{}; diff --git a/node/mock-test.ts b/node/mock-test.ts index 4dce93786..9eda7934f 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -11,14 +11,17 @@ const COMMAND_LENGTH = COMMAND_TAG.length; export class MockTestRunner { constructor(testPath: string) { this._testPath = testPath; + this.invokedToolCount = 0; + this.errorIssues = []; + this.warningIssues = []; } private _testPath: string; - public stdout: string; - public stderr: string; + public stdout: string | undefined; + public stderr: string | undefined; public cmdlines: any; public invokedToolCount: number; - public succeeded: boolean; + public succeeded: boolean | undefined; public errorIssues: string[]; public warningIssues: string[]; @@ -31,19 +34,20 @@ export class MockTestRunner { } public createdErrorIssue(message: string): boolean { + // return this.errorIssues ? this.errorIssues.indexOf(message.trim()) >= 0 : false; return this.errorIssues.indexOf(message.trim()) >= 0; } public createdWarningIssue(message: string): boolean { return this.warningIssues.indexOf(message.trim()) >= 0; - } + } public stdOutContained(message: string): boolean { - return this.stdout && this.stdout.indexOf(message) > 0; + return !!(this.stdout && this.stdout.indexOf(message) > 0); } public stdErrContained(message: string): boolean { - return this.stderr && this.stderr.indexOf(message) > 0; + return !!(this.stderr && this.stderr.indexOf(message) > 0); } public run(): void { @@ -59,7 +63,7 @@ export class MockTestRunner { let nodePath = shelljs.which('node'); if (!nodePath) { console.error('Could not find node in path'); - return; + return; } let spawn = cp.spawnSync(nodePath, [this._testPath]); @@ -80,7 +84,7 @@ export class MockTestRunner { let traceFile: string = this._testPath + '.log'; lines.forEach((line: string) => { let ci = line.indexOf('##vso['); - let cmd: cmdm.TaskCommand; + let cmd: cmdm.TaskCommand | undefined; let cmi = line.indexOf(COMMAND_TAG); if (ci >= 0) { cmd = cmdm.commandFromString(line.substring(ci)); diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 0c9463dc5..c89fc4168 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -11,7 +11,7 @@ export function setAnswers(answers: ma.TaskLibAnswers) { mock.initialize(answers); } -var run = function(cmd, callback) { +var run = function(cmd: any, callback: any) { console.log('running: ' + cmd); var output = ''; try { @@ -44,12 +44,12 @@ export interface IExecSyncResult { error: Error; } -export function debug(message) { +export function debug(message: any) { // do nothing, overridden } export class ToolRunner extends events.EventEmitter { - constructor(toolPath) { + constructor(toolPath: string) { debug('toolRunner toolPath: ' + toolPath); super(); @@ -60,9 +60,9 @@ export class ToolRunner extends events.EventEmitter { private toolPath: string; private args: string[]; - private pipeOutputToTool: ToolRunner; + private pipeOutputToTool: ToolRunner | undefined; - private _debug(message) { + private _debug(message: any) { debug(message); this.emit('debug', message); } @@ -74,7 +74,7 @@ export class ToolRunner extends events.EventEmitter { var escaped =false; var arg = ''; - var append = function(c) { + var append = function(c: string) { // we only escape double quotes. if (escaped && c !== '"') { arg += '\\'; @@ -122,7 +122,7 @@ export class ToolRunner extends events.EventEmitter { public arg(val: any): ToolRunner { if (!val) { - return; + return this; } if (val instanceof Array) { @@ -147,7 +147,7 @@ export class ToolRunner extends events.EventEmitter { public line(val: string): ToolRunner { if (!val) { - return; + return this; } this._debug(this.toolPath + ' arg: ' + val); @@ -175,7 +175,7 @@ export class ToolRunner extends events.EventEmitter { // Exec - use for long running tools where you need to stream live output as it runs // returns a promise with return code. // - public exec(options: IExecOptions): Q.Promise { + public exec(options?: IExecOptions): Q.Promise { var defer = Q.defer(); this._debug('exec tool: ' + this.toolPath); diff --git a/node/task.ts b/node/task.ts index 8f7ea0ba7..7181ec314 100644 --- a/node/task.ts +++ b/node/task.ts @@ -55,7 +55,7 @@ export function setResult(result: TaskResult, message: string): void { // // Catching all exceptions // -process.on('uncaughtException', (err) => { +process.on('uncaughtException', (err: Error) => { setResult(TaskResult.Failed, loc('LIB_UnhandledEx', err.message)); }); @@ -517,7 +517,9 @@ export interface FsOptions { flag?: string; } -export function writeFile(file: string, data: string | Buffer, options?: string | FsOptions) { +export function writeFile(file: string, data: string | Buffer, options: string): void; +export function writeFile(file: string, data: string | Buffer, options?: FsOptions): void; +export function writeFile(file: string, data: string | Buffer, options: any): void { fs.writeFileSync(file, data, options); } @@ -636,7 +638,7 @@ export function mkdirP(p: string): void { // create each directory while (stack.length) { - let dir = stack.pop(); + let dir = stack.pop()!; // non-null because `stack.length` was truthy debug(`mkdir '${dir}'`); try { fs.mkdirSync(dir); @@ -782,7 +784,7 @@ export function find(findPath: string, options?: FindOptions): string[] { while (stack.length) { // pop the next item and push to the result array - let item: _FindItem = stack.pop(); + 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 @@ -1164,7 +1166,7 @@ export function exec(tool: string, args: any, options?: trm.IExecOptions): Q.Pro */ export function execSync(tool: string, args: string | string[], options?: trm.IExecSyncOptions): trm.IExecSyncResult { let tr: trm.ToolRunner = this.tool(tool); - tr.on('debug', (data) => { + tr.on('debug', (data: string) => { debug(data); }); @@ -1645,7 +1647,7 @@ export interface CertConfiguration { * * @return CertConfiguration */ -export function getHttpCertConfiguration(): CertConfiguration { +export function getHttpCertConfiguration(): CertConfiguration | null { let ca: string = getVariable('Agent.CAInfo'); let clientCert: string = getVariable('Agent.ClientCert'); @@ -1675,13 +1677,11 @@ export function getHttpCertConfiguration(): CertConfiguration { // Test Publisher //----------------------------------------------------- export class TestPublisher { - constructor(testRunner) { - this.testRunner = testRunner; + constructor(public testRunner: string) { } - public testRunner: string; - public publish(resultFiles, mergeResults, platform, config, runTitle, publishRunAttachments) { + public publish(resultFiles?: string, mergeResults?: string, platform?: string, config?: string, runTitle?: string, publishRunAttachments?: string) { var properties = <{ [key: string]: string }>{}; properties['type'] = this.testRunner; @@ -1720,7 +1720,7 @@ export class TestPublisher { export class CodeCoveragePublisher { constructor() { } - public publish(codeCoverageTool, summaryFileLocation, reportDirectory, additionalCodeCoverageFiles) { + public publish(codeCoverageTool?: string, summaryFileLocation?: string, reportDirectory?: string, additionalCodeCoverageFiles?: string) { var properties = <{ [key: string]: string }>{}; diff --git a/node/toolrunner.ts b/node/toolrunner.ts index e87fdf420..49efe3fe9 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -59,7 +59,7 @@ export interface IExecSyncResult { } export class ToolRunner extends events.EventEmitter { - constructor(toolPath) { + constructor(toolPath: string) { super(); if (!toolPath) { @@ -73,10 +73,10 @@ export class ToolRunner extends events.EventEmitter { private toolPath: string; private args: string[]; - private pipeOutputToTool: ToolRunner; - private pipeOutputToFile: string; + private pipeOutputToTool: ToolRunner | undefined; + private pipeOutputToFile: string | undefined; - private _debug(message) { + private _debug(message: string) { this.emit('debug', message); } @@ -87,7 +87,7 @@ export class ToolRunner extends events.EventEmitter { var escaped = false; var arg = ''; - var append = function (c) { + var append = function (c: string) { // we only escape double quotes. if (escaped && c !== '"') { arg += '\\'; @@ -501,7 +501,7 @@ export class ToolRunner extends events.EventEmitter { */ public arg(val: string | string[]): ToolRunner { if (!val) { - return; + return this; } if (val instanceof Array) { @@ -526,7 +526,7 @@ export class ToolRunner extends events.EventEmitter { */ public line(val: string): ToolRunner { if (!val) { - return; + return this; } this._debug(this.toolPath + ' arg: ' + val); @@ -588,15 +588,15 @@ export class ToolRunner extends events.EventEmitter { } // TODO: filter process.env - let cp; + let cp: any; let toolPath: string = this.toolPath; let toolPathFirst: string; let successFirst = true; let returnCodeFirst: number; - let fileStream: fs.WriteStream; + let fileStream: fs.WriteStream | null; let waitingEvents: number = 0; // number of process or stream events we are waiting on to complete let returnCode: number = 0; - let error; + let error: any; if (this.pipeOutputToTool) { toolPath = this.pipeOutputToTool.toolPath; @@ -632,7 +632,7 @@ export class ToolRunner extends events.EventEmitter { } } }); - fileStream.on('error', (err) => { + fileStream.on('error', (err: any) => { waitingEvents--; //there were errors writing to the file, write is done this._debug(`Failed to pipe output of ${toolPathFirst} to file ${this.pipeOutputToFile}. Error = ${err}`); fileStream = null; @@ -668,7 +668,7 @@ export class ToolRunner extends events.EventEmitter { s.write(data); } }); - cpFirst.on('error', (err) => { + cpFirst.on('error', (err: Error) => { waitingEvents--; //first process is complete with errors if (fileStream) { fileStream.end(); @@ -815,8 +815,8 @@ export class ToolRunner extends events.EventEmitter { } var res: IExecSyncResult = { code: r.status, error: r.error }; - res.stdout = (r.stdout) ? r.stdout.toString() : null; - res.stderr = (r.stderr) ? r.stderr.toString() : null; + res.stdout = (r.stdout) ? r.stdout.toString() : ''; + res.stderr = (r.stderr) ? r.stderr.toString() : ''; return res; } } \ No newline at end of file diff --git a/node/tsconfig.json b/node/tsconfig.json index 00816c570..4631cc0c6 100644 --- a/node/tsconfig.json +++ b/node/tsconfig.json @@ -3,7 +3,8 @@ "target": "ES5", "module": "commonjs", "declaration": true, - "moduleResolution": "node" + "moduleResolution": "node", + "strictNullChecks": true }, "files": [ "index.ts", diff --git a/node/vault.ts b/node/vault.ts index 554ed3c79..c0d59d727 100644 --- a/node/vault.ts +++ b/node/vault.ts @@ -50,7 +50,7 @@ export class Vault { return true; } - public retrieveSecret(name: string): string { + public retrieveSecret(name: string): string | null { var secret = null; name = (name || '').toLowerCase() @@ -72,6 +72,6 @@ export class Vault { } private genKey(): void { - fs.writeFileSync(this._keyFile, uuidV4(), 'utf8'); - } + fs.writeFileSync(this._keyFile, uuidV4(), { encoding: 'utf8' }); + } } \ No newline at end of file From d3379b62bc22834c188e82b089a361e728f5e578 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Fri, 18 May 2018 17:51:09 -0400 Subject: [PATCH 058/259] Fix more low-hanging fruit --- node/internal.ts | 34 +++++++++++++++++----------------- node/mock-answer.ts | 10 ++++++---- node/mock-toolrunner.ts | 2 +- node/task.ts | 40 +++++++++++++++++++++------------------- node/toolrunner.ts | 2 +- node/vault.ts | 4 ++-- 6 files changed, 48 insertions(+), 44 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index 7b7d8069c..fc2c28b8c 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -102,7 +102,7 @@ function _loadLocStrings(resourceFile: string, culture: string): { [key: string] if (_exist(resourceFile)) { var resourceJson = require(resourceFile); if (resourceJson && resourceJson.hasOwnProperty('messages')) { - var locResourceJson = null; + var locResourceJson: any; // load up resource resjson for different culture var localizedResourceFile = path.join(path.dirname(resourceFile), 'Strings', 'resources.resjson'); @@ -224,11 +224,11 @@ export function _loc(key: string, ...param: any[]): string { * @param name name of the variable to get * @returns string */ -export function _getVariable(name: string): string { - let varval: string; +export function _getVariable(name: string): string | undefined { + let varval: string | undefined; // get the metadata - let info: _KnownVariableInfo; + let info: _KnownVariableInfo | undefined; let key: string = _getVariableKey(name); if (_knownVariableMap.hasOwnProperty(key)) { info = _knownVariableMap[key]; @@ -240,7 +240,7 @@ export function _getVariable(name: string): string { } else { // get the public value - varval = process.env[key]; + varval = process.env[key] || null; // fallback for pre 2.104.1 agent if (!varval && name.toUpperCase() == 'AGENT.JOBSTATUS') { @@ -922,16 +922,16 @@ export function _normalizeSeparators(p: string): string { // Expose proxy information to vsts-node-api //----------------------------------------------------- export function _exposeProxySettings(): void { - let proxyUrl: string = _getVariable('Agent.ProxyUrl'); + let proxyUrl = _getVariable('Agent.ProxyUrl'); if (proxyUrl && proxyUrl.length > 0) { - let proxyUsername: string = _getVariable('Agent.ProxyUsername'); - let proxyPassword: string = _getVariable('Agent.ProxyPassword'); - let proxyBypassHostsJson: string = _getVariable('Agent.ProxyBypassList'); + let proxyUsername = _getVariable('Agent.ProxyUsername'); + let proxyPassword = _getVariable('Agent.ProxyPassword'); + let proxyBypassHostsJson = _getVariable('Agent.ProxyBypassList'); global['_vsts_task_lib_proxy_url'] = proxyUrl; global['_vsts_task_lib_proxy_username'] = proxyUsername; global['_vsts_task_lib_proxy_bypass'] = proxyBypassHostsJson; - global['_vsts_task_lib_proxy_password'] = _exposeTaskLibSecret('proxy', proxyPassword); + global['_vsts_task_lib_proxy_password'] = _exposeTaskLibSecret('proxy', proxyPassword || ''); _debug('expose agent proxy configuration.') global['_vsts_task_lib_proxy'] = true; @@ -942,21 +942,21 @@ export function _exposeProxySettings(): void { // Expose certificate information to vsts-node-api //----------------------------------------------------- export function _exposeCertSettings(): void { - let ca: string = _getVariable('Agent.CAInfo'); + let ca = _getVariable('Agent.CAInfo'); if (ca) { global['_vsts_task_lib_cert_ca'] = ca; } - let clientCert: string = _getVariable('Agent.ClientCert'); + let clientCert = _getVariable('Agent.ClientCert'); if (clientCert) { - let clientCertKey: string = _getVariable('Agent.ClientCertKey'); - let clientCertArchive: string = _getVariable('Agent.ClientCertArchive'); - let clientCertPassword: string = _getVariable('Agent.ClientCertPassword'); + let clientCertKey = _getVariable('Agent.ClientCertKey'); + let clientCertArchive = _getVariable('Agent.ClientCertArchive'); + let clientCertPassword = _getVariable('Agent.ClientCertPassword'); global['_vsts_task_lib_cert_clientcert'] = clientCert; global['_vsts_task_lib_cert_key'] = clientCertKey; global['_vsts_task_lib_cert_archive'] = clientCertArchive; - global['_vsts_task_lib_cert_passphrase'] = _exposeTaskLibSecret('cert', clientCertPassword); + global['_vsts_task_lib_cert_passphrase'] = _exposeTaskLibSecret('cert', clientCertPassword || ''); } if (ca || clientCert) { @@ -973,7 +973,7 @@ export function _exposeCertSettings(): void { // We store the encryption key on disk and hold the encrypted content and key file in memory // return base64encoded:base64encoded // downstream vsts-node-api will retrieve the secret later -function _exposeTaskLibSecret(keyFile: string, secret: string): string { +function _exposeTaskLibSecret(keyFile: string, secret: string): string | undefined { if (secret) { let encryptKey = crypto.randomBytes(256); let cipher = crypto.createCipher("aes-256-ctr", encryptKey); diff --git a/node/mock-answer.ts b/node/mock-answer.ts index 0760d94ee..0bebacc00 100644 --- a/node/mock-answer.ts +++ b/node/mock-answer.ts @@ -44,17 +44,19 @@ export class MockAnswers { return null; } - if (this._answers[cmd][key]) { + const cmd_answer = this._answers[cmd]!; + + if (cmd_answer[key]) { debug('found mock response'); - return this._answers[cmd][key]; + return cmd_answer[key]; } if (key && process.env['MOCK_NORMALIZE_SLASHES'] === 'true') { // try normalizing the slashes var key2 = key.replace(/\\/g, "/"); - if (this._answers[cmd][key2]) { + if (cmd_answer[key2]) { debug('found mock response for normalized key'); - return this._answers[cmd][key2]; + return cmd_answer[key2]; } } diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index c89fc4168..808203b40 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -68,7 +68,7 @@ export class ToolRunner extends events.EventEmitter { } private _argStringToArray(argString: string): string[] { - var args = []; + var args: string[] = []; var inQuotes = false; var escaped =false; diff --git a/node/task.ts b/node/task.ts index 7181ec314..ae1771b8b 100644 --- a/node/task.ts +++ b/node/task.ts @@ -174,7 +174,7 @@ export interface VariableInfo { * @param required whether input is required. optional, defaults to false * @returns string */ -export function getInput(name: string, required?: boolean): string { +export function getInput(name: string, required?: boolean): string | undefined { var inval = im._vault.retrieveSecret('INPUT_' + name.replace(' ', '_').toUpperCase()); if (inval) { inval = inval.trim(); @@ -258,7 +258,7 @@ export function filePathSupplied(name: string): boolean { * @param check whether path is checked. optional, defaults to false * @returns string */ -export function getPathInput(name: string, required?: boolean, check?: boolean): string { +export function getPathInput(name: string, required?: boolean, check?: boolean): string | undefined { var inval = getInput(name, required); if (inval) { if (check) { @@ -320,7 +320,7 @@ export function getEndpointDataParameter(id: string, key: string, optional: bool * @param optional whether the endpoint authorization scheme is optional * @returns {string} value of the endpoint authorization scheme */ -export function getEndpointAuthorizationScheme(id: string, optional: boolean): string { +export function getEndpointAuthorizationScheme(id: string, optional: boolean): string | undefined { var authScheme = im._vault.retrieveSecret('ENDPOINT_AUTH_SCHEME_' + id); if (!optional && !authScheme) { @@ -340,7 +340,7 @@ export function getEndpointAuthorizationScheme(id: string, optional: boolean): s * @param optional optional whether the endpoint authorization scheme is optional * @returns {string} value of the endpoint authorization parameter value */ -export function getEndpointAuthorizationParameter(id: string, key: string, optional: boolean): string { +export function getEndpointAuthorizationParameter(id: string, key: string, optional: boolean): string | undefined { var authParam = im._vault.retrieveSecret('ENDPOINT_AUTH_PARAMETER_' + id + '_' + key.toUpperCase()); if (!optional && !authParam) { @@ -372,7 +372,7 @@ export interface EndpointAuthorization { * @param optional whether the url is optional * @returns string */ -export function getEndpointAuthorization(id: string, optional: boolean): EndpointAuthorization { +export function getEndpointAuthorization(id: string, optional: boolean): EndpointAuthorization | undefined { var aval = im._vault.retrieveSecret('ENDPOINT_AUTH_' + id); if (!optional && !aval) { @@ -382,9 +382,11 @@ export function getEndpointAuthorization(id: string, optional: boolean): Endpoin console.log(id + ' exists ' + (aval !== null)); debug(id + ' exists ' + (aval !== null)); - var auth: EndpointAuthorization; + var auth: EndpointAuthorization | undefined; try { - auth = JSON.parse(aval); + if (aval) { + auth = JSON.parse(aval); + } } catch (err) { throw new Error(loc('LIB_InvalidEndpointAuth', aval)); @@ -416,7 +418,7 @@ export function getSecureFileName(id: string): string { * @param id name of the secure file * @returns {string} secure file ticket */ -export function getSecureFileTicket(id: string): string { +export function getSecureFileTicket(id: string): string | undefined { var ticket = im._vault.retrieveSecret('SECUREFILE_TICKET_' + id); debug('secure file ticket for id ' + id + ' = ' + ticket); @@ -433,7 +435,7 @@ export function getSecureFileTicket(id: string): string { * @param name name of the variable to get * @returns string */ -export function getTaskVariable(name: string): string { +export function getTaskVariable(name: string): string | undefined { assertAgent('2.115.0'); var inval = im._vault.retrieveSecret('VSTS_TASKVARIABLE_' + name.replace(' ', '_').toUpperCase()); if (inval) { @@ -1597,12 +1599,12 @@ export interface ProxyConfiguration { * * @return ProxyConfiguration */ -export function getHttpProxyConfiguration(requestUrl?: string): ProxyConfiguration { - let proxyUrl: string = getVariable('Agent.ProxyUrl'); +export function getHttpProxyConfiguration(requestUrl?: string): ProxyConfiguration | null { + let proxyUrl = getVariable('Agent.ProxyUrl'); if (proxyUrl && proxyUrl.length > 0) { - let proxyUsername: string = getVariable('Agent.ProxyUsername'); - let proxyPassword: string = getVariable('Agent.ProxyPassword'); - let proxyBypassHosts: string[] = JSON.parse(getVariable('Agent.ProxyBypassList') || '[]'); + let proxyUsername = getVariable('Agent.ProxyUsername'); + let proxyPassword = getVariable('Agent.ProxyPassword'); + let proxyBypassHosts = JSON.parse(getVariable('Agent.ProxyBypassList') || '[]'); let bypass: boolean = false; if (requestUrl) { @@ -1648,8 +1650,8 @@ export interface CertConfiguration { * @return CertConfiguration */ export function getHttpCertConfiguration(): CertConfiguration | null { - let ca: string = getVariable('Agent.CAInfo'); - let clientCert: string = getVariable('Agent.ClientCert'); + let ca = getVariable('Agent.CAInfo'); + let clientCert = getVariable('Agent.ClientCert'); if (ca || clientCert) { let certConfig: CertConfiguration = {}; @@ -1657,9 +1659,9 @@ export function getHttpCertConfiguration(): CertConfiguration | null { certConfig.certFile = clientCert; if (clientCert) { - let clientCertKey: string = getVariable('Agent.ClientCertKey'); - let clientCertArchive: string = getVariable('Agent.ClientCertArchive'); - let clientCertPassword: string = getVariable('Agent.ClientCertPassword'); + let clientCertKey = getVariable('Agent.ClientCertKey'); + let clientCertArchive = getVariable('Agent.ClientCertArchive'); + let clientCertPassword = getVariable('Agent.ClientCertPassword'); certConfig.keyFile = clientCertKey; certConfig.certArchiveFile = clientCertArchive; diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 49efe3fe9..103784677 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -81,7 +81,7 @@ export class ToolRunner extends events.EventEmitter { } private _argStringToArray(argString: string): string[] { - var args = []; + var args: string[] = []; var inQuotes = false; var escaped = false; diff --git a/node/vault.ts b/node/vault.ts index c0d59d727..782b8672f 100644 --- a/node/vault.ts +++ b/node/vault.ts @@ -50,8 +50,8 @@ export class Vault { return true; } - public retrieveSecret(name: string): string | null { - var secret = null; + public retrieveSecret(name: string): string | undefined { + var secret: string | undefined; name = (name || '').toLowerCase() if (this._store.hasOwnProperty(name)) { From eacd7f7438c6bcbb0c537c420651d67764cd816b Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Fri, 18 May 2018 17:52:51 -0400 Subject: [PATCH 059/259] Temporarily add .gitignore --- node/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 node/.gitignore diff --git a/node/.gitignore b/node/.gitignore new file mode 100644 index 000000000..dbe9c82b3 --- /dev/null +++ b/node/.gitignore @@ -0,0 +1 @@ +.vscode/ \ No newline at end of file From b61ba63490169c937080c6d94742d670c5dc16c8 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Fri, 18 May 2018 17:56:29 -0400 Subject: [PATCH 060/259] Fix nullable IExecOptions --- node/mock-toolrunner.ts | 2 +- node/toolrunner.ts | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 808203b40..3597a4ade 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -264,7 +264,7 @@ export class ToolRunner extends events.EventEmitter { // ExecSync - use for short running simple commands. Simple and convenient (synchronous) // but also has limits. For example, no live output and limited to max buffer // - public execSync(options: IExecSyncOptions): IExecSyncResult { + public execSync(options?: IExecSyncOptions): IExecSyncResult { var defer = Q.defer(); this._debug('exec tool: ' + this.toolPath); diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 103784677..c821db9ee 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -460,7 +460,7 @@ export class ToolRunner extends events.EventEmitter { return reverse.split('').reverse().join(''); } - private _cloneExecOptions(options: IExecOptions): IExecOptions { + private _cloneExecOptions(options?: IExecOptions): IExecOptions { options = options || {}; let result: IExecOptions = { cwd: options.cwd || process.cwd(), @@ -581,10 +581,10 @@ export class ToolRunner extends events.EventEmitter { }); let success = true; - options = this._cloneExecOptions(options); + const clonedOptions = this._cloneExecOptions(options); - if (!options.silent) { - options.outStream.write(this._getCommandString(options) + os.EOL); + if (!clonedOptions.silent) { + clonedOptions.outStream.write(this._getCommandString(clonedOptions) + os.EOL); } // TODO: filter process.env @@ -609,14 +609,14 @@ export class ToolRunner extends events.EventEmitter { waitingEvents++; var cpFirst = child.spawn( this._getSpawnFileName(), - this._getSpawnArgs(options), - this._getSpawnOptions(options)); + this._getSpawnArgs(clonedOptions), + this._getSpawnOptions(clonedOptions)); waitingEvents ++; cp = child.spawn( this.pipeOutputToTool._getSpawnFileName(), - this.pipeOutputToTool._getSpawnArgs(options), - this.pipeOutputToTool._getSpawnOptions(options)); + this.pipeOutputToTool._getSpawnArgs(clonedOptions), + this.pipeOutputToTool._getSpawnOptions(clonedOptions)); fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; if (fileStream) { @@ -662,9 +662,9 @@ export class ToolRunner extends events.EventEmitter { if (fileStream) { fileStream.write(data); } - successFirst = !options.failOnStdErr; - if (!options.silent) { - var s = options.failOnStdErr ? options.errStream : options.outStream; + successFirst = !clonedOptions.failOnStdErr; + if (!clonedOptions.silent) { + var s = clonedOptions.failOnStdErr ? clonedOptions.errStream : clonedOptions.outStream; s.write(data); } }); @@ -681,7 +681,7 @@ export class ToolRunner extends events.EventEmitter { }); cpFirst.on('close', (code, signal) => { waitingEvents--; //first process is complete - if (code != 0 && !options.ignoreReturnCode) { + if (code != 0 && !clonedOptions.ignoreReturnCode) { successFirst = false; returnCodeFirst = code; returnCode = returnCodeFirst; @@ -702,15 +702,15 @@ export class ToolRunner extends events.EventEmitter { } else { waitingEvents++; - cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(options), this._getSpawnOptions(options)); + cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(clonedOptions), this._getSpawnOptions(clonedOptions)); } var stdbuffer: string = ''; cp.stdout.on('data', (data: Buffer) => { this.emit('stdout', data); - if (!options.silent) { - options.outStream.write(data); + if (!clonedOptions.silent) { + clonedOptions.outStream.write(data); } this._processLineBuffer(data, stdbuffer, (line: string) => { @@ -722,9 +722,9 @@ export class ToolRunner extends events.EventEmitter { cp.stderr.on('data', (data: Buffer) => { this.emit('stderr', data); - success = !options.failOnStdErr; - if (!options.silent) { - var s = options.failOnStdErr ? options.errStream : options.outStream; + success = !clonedOptions.failOnStdErr; + if (!clonedOptions.silent) { + var s = clonedOptions.failOnStdErr ? clonedOptions.errStream : clonedOptions.outStream; s.write(data); } @@ -754,7 +754,7 @@ export class ToolRunner extends events.EventEmitter { this.emit('errline', errbuffer); } - if (code != 0 && !options.ignoreReturnCode) { + if (code != 0 && !clonedOptions.ignoreReturnCode) { success = false; } From a210ba38960e084bdcd6cc61206dde0b392c917b Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Fri, 18 May 2018 17:56:50 -0400 Subject: [PATCH 061/259] Delete .gitignore --- node/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 node/.gitignore diff --git a/node/.gitignore b/node/.gitignore deleted file mode 100644 index dbe9c82b3..000000000 --- a/node/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.vscode/ \ No newline at end of file From 8c8f46c581bb37555792dc9060d93a03772bc8a4 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Fri, 18 May 2018 17:59:11 -0400 Subject: [PATCH 062/259] Bump major version number --- node/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/package.json b/node/package.json index 2530ace4b..f2d3ef909 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.5.0", + "version": "3.0.0", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 83eb4747cb2d6dcf9907926678f1aa45cd6bf578 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Fri, 18 May 2018 18:07:29 -0400 Subject: [PATCH 063/259] Simplify a few things --- node/internal.ts | 9 +++------ node/mock-task.ts | 1 - node/mock-toolrunner.ts | 6 +++--- node/toolrunner.ts | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index fc2c28b8c..72fb849da 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -71,7 +71,7 @@ let _libResourceFileLoaded: boolean = false; let _resourceCulture: string = 'en-US'; function _loadResJson(resjsonFile: string): any { - var resJson: {} | null = null; + var resJson: {} | undefined; if (_exist(resjsonFile)) { var resjsonContent = fs.readFileSync(resjsonFile, 'utf8').toString(); // remove BOM @@ -84,7 +84,6 @@ function _loadResJson(resjsonFile: string): any { } catch (err) { _debug('unable to parse resjson with err: ' + err.message); - resJson = null; } } else { @@ -240,7 +239,7 @@ export function _getVariable(name: string): string | undefined { } else { // get the public value - varval = process.env[key] || null; + varval = process.env[key]; // fallback for pre 2.104.1 agent if (!varval && name.toUpperCase() == 'AGENT.JOBSTATUS') { @@ -304,9 +303,7 @@ export function _debug(message: string): void { export function _exist(path: string): boolean { var exist = false; try { - if (path && fs.statSync(path) != null) { - exist = true; - } + exist = !!(path && fs.statSync(path) != null); } catch (err) { if (err && err.code === 'ENOENT') { exist = false; diff --git a/node/mock-task.ts b/node/mock-task.ts index 991ceb89a..1397f2e11 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -390,7 +390,6 @@ export function findMatch(defaultRoot: string, patterns: string[] | string) : st //----------------------------------------------------- export class TestPublisher { constructor(public testRunner: string) { - this.testRunner = testRunner; } public publish(resultFiles?: string, mergeResults?: string, platform?: string, config?: string, runTitle?: string, publishRunAttachments?: string) { diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 3597a4ade..feaa2596a 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -11,7 +11,7 @@ export function setAnswers(answers: ma.TaskLibAnswers) { mock.initialize(answers); } -var run = function(cmd: any, callback: any) { +var run = function(cmd, callback) { console.log('running: ' + cmd); var output = ''; try { @@ -44,7 +44,7 @@ export interface IExecSyncResult { error: Error; } -export function debug(message: any) { +export function debug(message) { // do nothing, overridden } @@ -62,7 +62,7 @@ export class ToolRunner extends events.EventEmitter { private args: string[]; private pipeOutputToTool: ToolRunner | undefined; - private _debug(message: any) { + private _debug(message) { debug(message); this.emit('debug', message); } diff --git a/node/toolrunner.ts b/node/toolrunner.ts index c821db9ee..a91182f83 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -632,7 +632,7 @@ export class ToolRunner extends events.EventEmitter { } } }); - fileStream.on('error', (err: any) => { + fileStream.on('error', (err) => { waitingEvents--; //there were errors writing to the file, write is done this._debug(`Failed to pipe output of ${toolPathFirst} to file ${this.pipeOutputToFile}. Error = ${err}`); fileStream = null; From 7dca1fc53e9886ac719a98e811b28339de108de0 Mon Sep 17 00:00:00 2001 From: ericsciple Date: Mon, 21 May 2018 02:11:07 -0400 Subject: [PATCH 064/259] v0.11.0 (#363) --- powershell/Docs/Commands.md | 100 +++++++++++++++++- .../Docs/FullHelp/Get-VstsSecureFileName.md | 40 +++++++ .../Docs/FullHelp/Get-VstsSecureFileTicket.md | 40 +++++++ .../Docs/FullHelp/Write-VstsPrependPath.md | 36 +++++++ .../Docs/FullHelp/Write-VstsSetEndpoint.md | 61 +++++++++++ .../FullHelp/Write-VstsUpdateReleaseName.md | 36 +++++++ .../Docs/FullHelp/Write-VstsUploadFile.md | 36 +++++++ .../Docs/FullHelp/Write-VstsUploadSummary.md | 36 +++++++ powershell/Docs/ReleaseNotes.md | 54 +++++----- powershell/package.json | 2 +- 10 files changed, 414 insertions(+), 27 deletions(-) create mode 100644 powershell/Docs/FullHelp/Get-VstsSecureFileName.md create mode 100644 powershell/Docs/FullHelp/Get-VstsSecureFileTicket.md create mode 100644 powershell/Docs/FullHelp/Write-VstsPrependPath.md create mode 100644 powershell/Docs/FullHelp/Write-VstsSetEndpoint.md create mode 100644 powershell/Docs/FullHelp/Write-VstsUpdateReleaseName.md create mode 100644 powershell/Docs/FullHelp/Write-VstsUploadFile.md create mode 100644 powershell/Docs/FullHelp/Write-VstsUploadSummary.md diff --git a/powershell/Docs/Commands.md b/powershell/Docs/Commands.md index d44ecc5d5..cabe2d0d9 100644 --- a/powershell/Docs/Commands.md +++ b/powershell/Docs/Commands.md @@ -1,4 +1,4 @@ -# Commands (v0.10.1) +# Commands (v0.11.0) ## Table of Contents * [Find](#find) * [Find-VstsMatch](#find-vstsmatch) @@ -8,6 +8,8 @@ * [Input](#input) * [Get-VstsEndpoint](#get-vstsendpoint) * [Get-VstsInput](#get-vstsinput) + * [Get-VstsSecureFileName](#get-vstssecurefilename) + * [Get-VstsSecureFileTicket](#get-vstssecurefileticket) * [Get-VstsTaskVariable](#get-vststaskvariable) * [Get-VstsTaskVariableInfo](#get-vststaskvariableinfo) * [Set-VstsTaskVariable](#set-vststaskvariable) @@ -21,6 +23,8 @@ * [Write-VstsAddBuildTag](#write-vstsaddbuildtag) * [Write-VstsAssociateArtifact](#write-vstsassociateartifact) * [Write-VstsLogDetail](#write-vstslogdetail) + * [Write-VstsPrependPath](#write-vstsprependpath) + * [Write-VstsSetEndpoint](#write-vstssetendpoint) * [Write-VstsSetProgress](#write-vstssetprogress) * [Write-VstsSetResult](#write-vstssetresult) * [Write-VstsSetSecret](#write-vstssetsecret) @@ -30,8 +34,11 @@ * [Write-VstsTaskVerbose](#write-vststaskverbose) * [Write-VstsTaskWarning](#write-vststaskwarning) * [Write-VstsUpdateBuildNumber](#write-vstsupdatebuildnumber) + * [Write-VstsUpdateReleaseName](#write-vstsupdatereleasename) * [Write-VstsUploadArtifact](#write-vstsuploadartifact) * [Write-VstsUploadBuildLog](#write-vstsuploadbuildlog) + * [Write-VstsUploadFile](#write-vstsuploadfile) + * [Write-VstsUploadSummary](#write-vstsuploadsummary) * [Server OM](#serverom) * [Get-VstsAssemblyReference](#get-vstsassemblyreference) * [Get-VstsClientCertificate](#get-vstsclientcertificate) @@ -153,6 +160,36 @@ SYNTAX DESCRIPTION Gets the value for the specified input name. ``` +### Get-VstsSecureFileName +[table of contents](#toc) | [full](FullHelp/Get-VstsSecureFileName.md) +``` +NAME + Get-VstsSecureFileName + +SYNOPSIS + Gets a secure file name. + +SYNTAX + Get-VstsSecureFileName [-Id] [-Require] [] + +DESCRIPTION + Gets the name for a secure file. +``` +### Get-VstsSecureFileTicket +[table of contents](#toc) | [full](FullHelp/Get-VstsSecureFileTicket.md) +``` +NAME + Get-VstsSecureFileTicket + +SYNOPSIS + Gets a secure file ticket. + +SYNTAX + Get-VstsSecureFileTicket [-Id] [-Require] [] + +DESCRIPTION + Gets the secure file ticket that can be used to download the secure file contents. +``` ### Get-VstsTaskVariable [table of contents](#toc) | [full](FullHelp/Get-VstsTaskVariable.md) ``` @@ -327,6 +364,31 @@ SYNTAX ] [[-StartTime] ] [[-FinishTime] ] [[-Progress] ] [[-State] ] [[-Result] ] [[-Message] ] [-AsOutput] [] ``` +### Write-VstsPrependPath +[table of contents](#toc) | [full](FullHelp/Write-VstsPrependPath.md) +``` +NAME + Write-VstsPrependPath + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsPrependPath [-Path] [-AsOutput] [] +``` +### Write-VstsSetEndpoint +[table of contents](#toc) | [full](FullHelp/Write-VstsSetEndpoint.md) +``` +NAME + Write-VstsSetEndpoint + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsSetEndpoint [-Id] [-Field] [-Key] [-Value] [-AsOutput] + [] +``` ### Write-VstsSetProgress [table of contents](#toc) | [full](FullHelp/Write-VstsSetProgress.md) ``` @@ -439,6 +501,18 @@ SYNOPSIS SYNTAX Write-VstsUpdateBuildNumber [-Value] [-AsOutput] [] ``` +### Write-VstsUpdateReleaseName +[table of contents](#toc) | [full](FullHelp/Write-VstsUpdateReleaseName.md) +``` +NAME + Write-VstsUpdateReleaseName + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsUpdateReleaseName [-Name] [-AsOutput] [] +``` ### Write-VstsUploadArtifact [table of contents](#toc) | [full](FullHelp/Write-VstsUploadArtifact.md) ``` @@ -464,6 +538,30 @@ SYNOPSIS SYNTAX Write-VstsUploadBuildLog [-Path] [-AsOutput] [] ``` +### Write-VstsUploadFile +[table of contents](#toc) | [full](FullHelp/Write-VstsUploadFile.md) +``` +NAME + Write-VstsUploadFile + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsUploadFile [-Path] [-AsOutput] [] +``` +### Write-VstsUploadSummary +[table of contents](#toc) | [full](FullHelp/Write-VstsUploadSummary.md) +``` +NAME + Write-VstsUploadSummary + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsUploadSummary [-Path] [-AsOutput] [] +``` ## Server OM ### Get-VstsAssemblyReference [table of contents](#toc) | [full](FullHelp/Get-VstsAssemblyReference.md) diff --git a/powershell/Docs/FullHelp/Get-VstsSecureFileName.md b/powershell/Docs/FullHelp/Get-VstsSecureFileName.md new file mode 100644 index 000000000..ccc89dde1 --- /dev/null +++ b/powershell/Docs/FullHelp/Get-VstsSecureFileName.md @@ -0,0 +1,40 @@ +# Get-VstsSecureFileName +[table of contents](../Commands.md#toc) | [brief](../Commands.md#get-vstssecurefilename) +``` +NAME + Get-VstsSecureFileName + +SYNOPSIS + Gets a secure file name. + +SYNTAX + Get-VstsSecureFileName [-Id] [-Require] [] + +DESCRIPTION + Gets the name for a secure file. + +PARAMETERS + -Id + Secure file id. + + Required? true + Position? 1 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -Require [] + Writes an error to the error pipeline if the ticket is not found. + + Required? false + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + + + This cmdlet supports the common parameters: Verbose, Debug, + ErrorAction, ErrorVariable, WarningAction, WarningVariable, + OutBuffer, PipelineVariable, and OutVariable. For more information, see + about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). +``` diff --git a/powershell/Docs/FullHelp/Get-VstsSecureFileTicket.md b/powershell/Docs/FullHelp/Get-VstsSecureFileTicket.md new file mode 100644 index 000000000..ce974e3aa --- /dev/null +++ b/powershell/Docs/FullHelp/Get-VstsSecureFileTicket.md @@ -0,0 +1,40 @@ +# Get-VstsSecureFileTicket +[table of contents](../Commands.md#toc) | [brief](../Commands.md#get-vstssecurefileticket) +``` +NAME + Get-VstsSecureFileTicket + +SYNOPSIS + Gets a secure file ticket. + +SYNTAX + Get-VstsSecureFileTicket [-Id] [-Require] [] + +DESCRIPTION + Gets the secure file ticket that can be used to download the secure file contents. + +PARAMETERS + -Id + Secure file id. + + Required? true + Position? 1 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -Require [] + Writes an error to the error pipeline if the ticket is not found. + + Required? false + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + + + This cmdlet supports the common parameters: Verbose, Debug, + ErrorAction, ErrorVariable, WarningAction, WarningVariable, + OutBuffer, PipelineVariable, and OutVariable. For more information, see + about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). +``` diff --git a/powershell/Docs/FullHelp/Write-VstsPrependPath.md b/powershell/Docs/FullHelp/Write-VstsPrependPath.md new file mode 100644 index 000000000..258b17f1d --- /dev/null +++ b/powershell/Docs/FullHelp/Write-VstsPrependPath.md @@ -0,0 +1,36 @@ +# Write-VstsPrependPath +[table of contents](../Commands.md#toc) | [brief](../Commands.md#write-vstsprependpath) +``` +NAME + Write-VstsPrependPath + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsPrependPath [-Path] [-AsOutput] [] + +PARAMETERS + -Path + + Required? true + Position? 1 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -AsOutput [] + Indicates whether to write the logging command directly to the host or to the output pipeline. + + Required? false + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + + + This cmdlet supports the common parameters: Verbose, Debug, + ErrorAction, ErrorVariable, WarningAction, WarningVariable, + OutBuffer, PipelineVariable, and OutVariable. For more information, see + about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). +``` diff --git a/powershell/Docs/FullHelp/Write-VstsSetEndpoint.md b/powershell/Docs/FullHelp/Write-VstsSetEndpoint.md new file mode 100644 index 000000000..9ecf85d9f --- /dev/null +++ b/powershell/Docs/FullHelp/Write-VstsSetEndpoint.md @@ -0,0 +1,61 @@ +# Write-VstsSetEndpoint +[table of contents](../Commands.md#toc) | [brief](../Commands.md#write-vstssetendpoint) +``` +NAME + Write-VstsSetEndpoint + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsSetEndpoint [-Id] [-Field] [-Key] [-Value] [-AsOutput] + [] + +PARAMETERS + -Id + + Required? true + Position? 1 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -Field + + Required? true + Position? 2 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -Key + + Required? true + Position? 3 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -Value + + Required? true + Position? 4 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -AsOutput [] + Indicates whether to write the logging command directly to the host or to the output pipeline. + + Required? false + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + + + This cmdlet supports the common parameters: Verbose, Debug, + ErrorAction, ErrorVariable, WarningAction, WarningVariable, + OutBuffer, PipelineVariable, and OutVariable. For more information, see + about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). +``` diff --git a/powershell/Docs/FullHelp/Write-VstsUpdateReleaseName.md b/powershell/Docs/FullHelp/Write-VstsUpdateReleaseName.md new file mode 100644 index 000000000..1f9916541 --- /dev/null +++ b/powershell/Docs/FullHelp/Write-VstsUpdateReleaseName.md @@ -0,0 +1,36 @@ +# Write-VstsUpdateReleaseName +[table of contents](../Commands.md#toc) | [brief](../Commands.md#write-vstsupdatereleasename) +``` +NAME + Write-VstsUpdateReleaseName + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsUpdateReleaseName [-Name] [-AsOutput] [] + +PARAMETERS + -Name + + Required? true + Position? 1 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -AsOutput [] + Indicates whether to write the logging command directly to the host or to the output pipeline. + + Required? false + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + + + This cmdlet supports the common parameters: Verbose, Debug, + ErrorAction, ErrorVariable, WarningAction, WarningVariable, + OutBuffer, PipelineVariable, and OutVariable. For more information, see + about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). +``` diff --git a/powershell/Docs/FullHelp/Write-VstsUploadFile.md b/powershell/Docs/FullHelp/Write-VstsUploadFile.md new file mode 100644 index 000000000..db086ace1 --- /dev/null +++ b/powershell/Docs/FullHelp/Write-VstsUploadFile.md @@ -0,0 +1,36 @@ +# Write-VstsUploadFile +[table of contents](../Commands.md#toc) | [brief](../Commands.md#write-vstsuploadfile) +``` +NAME + Write-VstsUploadFile + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsUploadFile [-Path] [-AsOutput] [] + +PARAMETERS + -Path + + Required? true + Position? 1 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -AsOutput [] + Indicates whether to write the logging command directly to the host or to the output pipeline. + + Required? false + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + + + This cmdlet supports the common parameters: Verbose, Debug, + ErrorAction, ErrorVariable, WarningAction, WarningVariable, + OutBuffer, PipelineVariable, and OutVariable. For more information, see + about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). +``` diff --git a/powershell/Docs/FullHelp/Write-VstsUploadSummary.md b/powershell/Docs/FullHelp/Write-VstsUploadSummary.md new file mode 100644 index 000000000..62ee2e660 --- /dev/null +++ b/powershell/Docs/FullHelp/Write-VstsUploadSummary.md @@ -0,0 +1,36 @@ +# Write-VstsUploadSummary +[table of contents](../Commands.md#toc) | [brief](../Commands.md#write-vstsuploadsummary) +``` +NAME + Write-VstsUploadSummary + +SYNOPSIS + See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +SYNTAX + Write-VstsUploadSummary [-Path] [-AsOutput] [] + +PARAMETERS + -Path + + Required? true + Position? 1 + Default value + Accept pipeline input? false + Accept wildcard characters? false + + -AsOutput [] + Indicates whether to write the logging command directly to the host or to the output pipeline. + + Required? false + Position? named + Default value False + Accept pipeline input? false + Accept wildcard characters? false + + + This cmdlet supports the common parameters: Verbose, Debug, + ErrorAction, ErrorVariable, WarningAction, WarningVariable, + OutBuffer, PipelineVariable, and OutVariable. For more information, see + about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). +``` diff --git a/powershell/Docs/ReleaseNotes.md b/powershell/Docs/ReleaseNotes.md index 072110f07..3b8820fbd 100644 --- a/powershell/Docs/ReleaseNotes.md +++ b/powershell/Docs/ReleaseNotes.md @@ -1,54 +1,58 @@ # Release Notes +## 0.11.0 +* Added input functions `Get-SecureFileTicket` and `Get-SecureFileName`. +* Added missing agent commands. + ## 0.10.0 - * Updated `Get-VstsVssHttpClient`. Added `-WebProxy` parameter. `Get-VstsVssHttpClient` will follow agent proxy setting by default. - * Added `Get-VstsWebProxy` to retrieve agent proxy settings. +* Updated `Get-VstsVssHttpClient`. Added `-WebProxy` parameter. `Get-VstsVssHttpClient` will follow agent proxy setting by default. +* Added `Get-VstsWebProxy` to retrieve agent proxy settings. ## 0.9.0 - * Breaking change for `Select-VstsMatch` due to positional parameter changes and partial parameter name impact. - * Updated `Select-VstsMatch`. Added `-PatternRoot` parameter. - * Added `Assert-VstsAgent`. +* Breaking change for `Select-VstsMatch` due to positional parameter changes and partial parameter name impact. +* Updated `Select-VstsMatch`. Added `-PatternRoot` parameter. +* Added `Assert-VstsAgent`. ## 0.8.2 - * Fixed issue with env block size limit. +* Fixed issue with env block size limit. ## 0.8.0 - * Added `Find-VstsMatch` and `Select-VstsMatch` for finding files with advanced pattern matching. +* Added `Find-VstsMatch` and `Select-VstsMatch` for finding files with advanced pattern matching. ## 0.7.1 - * Updated `Find-VstsFiles` to fix error when traversing files with IntegrityStream, Virtual, or NoScrubData attribute. +* Updated `Find-VstsFiles` to fix error when traversing files with IntegrityStream, Virtual, or NoScrubData attribute. ## 0.7.0 - * Breaking changes for `Get-VstsTfsClientCredentials` and `Get-VstsVssCredentials`. See [Using the VSTS REST SDK and TFS Extended Client SDK](UsingOM.md). - * Added `Get-VstsTfsService` and `Get-VstsVssHttpClient`. - * Added `Get-VstsTaskVariableInfo` to get all task variables, secret and non-secret. +* Breaking changes for `Get-VstsTfsClientCredentials` and `Get-VstsVssCredentials`. See [Using the VSTS REST SDK and TFS Extended Client SDK](UsingOM.md). +* Added `Get-VstsTfsService` and `Get-VstsVssHttpClient`. +* Added `Get-VstsTaskVariableInfo` to get all task variables, secret and non-secret. ## 0.6.4 - * Updated `Get-VstsTfsClientCredentials` to fix authentication bugs. +* Updated `Get-VstsTfsClientCredentials` to fix authentication bugs. ## 0.6.3 - * Updated `Find-VstsFiles` to fix `-IncludeDirectories` functionality. - * Updated initialization (`Invoke-VstsTaskScript`) to remove secret variables from the environment drive. The variables are stored within the module as `PSCredential` objects. `Get-VstsTaskVariable` has been updated to retrieve the internally stored secret variables and return the plain values. Otherwise `Get-VstsTaskVariable` falls back to checking for the variable as a non-secret variable on the environment drive. +* Updated `Find-VstsFiles` to fix `-IncludeDirectories` functionality. +* Updated initialization (`Invoke-VstsTaskScript`) to remove secret variables from the environment drive. The variables are stored within the module as `PSCredential` objects. `Get-VstsTaskVariable` has been updated to retrieve the internally stored secret variables and return the plain values. Otherwise `Get-VstsTaskVariable` falls back to checking for the variable as a non-secret variable on the environment drive. ## 0.6.2 - * Updated initialization (`Invoke-VstsTaskScript`) to run within the global session state. Modules imported by the task script will now be imported into the global session state. +* Updated initialization (`Invoke-VstsTaskScript`) to run within the global session state. Modules imported by the task script will now be imported into the global session state. ## 0.6.1 - * Updated initialization (`Invoke-VstsTaskScript`) to globally set `ErrorActionPreference` to `Stop`. - * Updated initialization (`Invoke-VstsTaskScript`) to remove input and endpoint variables from the environment drive. The variables are stored within the module as `PSCredential` objects. `Get-VstsInput` and `Get-VstsEndpoint` have been updated to retrieve the internally stored variables and return the plain values. - * Updated `Invoke-VstsTool`. The command line being invoked is now written to the host stream. - * Added `Write-VstsSetSecret`. +* Updated initialization (`Invoke-VstsTaskScript`) to globally set `ErrorActionPreference` to `Stop`. +* Updated initialization (`Invoke-VstsTaskScript`) to remove input and endpoint variables from the environment drive. The variables are stored within the module as `PSCredential` objects. `Get-VstsInput` and `Get-VstsEndpoint` have been updated to retrieve the internally stored variables and return the plain values. +* Updated `Invoke-VstsTool`. The command line being invoked is now written to the host stream. +* Added `Write-VstsSetSecret`. ## 0.6.0 - * Updated `Get-VstsEndpoint`. Added a `Data` property to the endpoint object. - * Updated `Write-VstsSetVariable`. Added a `Secret` switch. - * Added `Write-VstsAddBuildTag`. +* Updated `Get-VstsEndpoint`. Added a `Data` property to the endpoint object. +* Updated `Write-VstsSetVariable`. Added a `Secret` switch. +* Added `Write-VstsAddBuildTag`. ## 0.5.4 - * Loc string updates for TFS 2015 Update 2. +* Loc string updates for TFS 2015 Update 2. ## 0.5.1 - * Updated `Write-VstsAssociateArtifact`. Added a mandatory `Type` parameter. Added an optional `Properties` parameter so that additional properties can be stored on an artifact. +* Updated `Write-VstsAssociateArtifact`. Added a mandatory `Type` parameter. Added an optional `Properties` parameter so that additional properties can be stored on an artifact. ## 0.5.0 - * Initial release. +* Initial release. diff --git a/powershell/package.json b/powershell/package.json index e64eb4321..64aee00af 100644 --- a/powershell/package.json +++ b/powershell/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-sdk", - "version": "0.10.1", + "version": "0.11.0", "description": "VSTS Task SDK", "scripts": { "build": "node make.js build", From 0af0d33e75b834bad53b5157170fa80d8f36c9d3 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Mon, 21 May 2018 08:47:05 -0400 Subject: [PATCH 065/259] Simplify some of the type annotations --- node/internal.ts | 6 +++--- node/mock-test.ts | 28 +++++++++++----------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index 72fb849da..77a657e07 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -53,11 +53,11 @@ export function _writeLine(str: string): void { _outStream.write(str + os.EOL); } -export function _setStdStream(stdStream: NodeJS.WritableStream): void { +export function _setStdStream(stdStream): void { _outStream = stdStream; } -export function _setErrStream(errStream: NodeJS.WritableStream): void { +export function _setErrStream(errStream): void { _errStream = errStream; } @@ -71,7 +71,7 @@ let _libResourceFileLoaded: boolean = false; let _resourceCulture: string = 'en-US'; function _loadResJson(resjsonFile: string): any { - var resJson: {} | undefined; + var resJson: any; if (_exist(resjsonFile)) { var resjsonContent = fs.readFileSync(resjsonFile, 'utf8').toString(); // remove BOM diff --git a/node/mock-test.ts b/node/mock-test.ts index 9eda7934f..09f4e85be 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -9,32 +9,26 @@ const COMMAND_TAG = '[command]'; const COMMAND_LENGTH = COMMAND_TAG.length; export class MockTestRunner { - constructor(testPath: string) { - this._testPath = testPath; - this.invokedToolCount = 0; - this.errorIssues = []; - this.warningIssues = []; + constructor(private _testPath: string) { } - private _testPath: string; - public stdout: string | undefined; - public stderr: string | undefined; - public cmdlines: any; - public invokedToolCount: number; - public succeeded: boolean | undefined; - public errorIssues: string[]; - public warningIssues: string[]; + public stdout = ''; + public stderr = '' + public cmdlines = {}; + public invokedToolCount = 0; + public succeeded = false; + public errorIssues: string[] = []; + public warningIssues: string[] = []; get failed(): boolean { return !this.succeeded; } - + public ran(cmdline: string): boolean { return this.cmdlines.hasOwnProperty(cmdline.trim()); } public createdErrorIssue(message: string): boolean { - // return this.errorIssues ? this.errorIssues.indexOf(message.trim()) >= 0 : false; return this.errorIssues.indexOf(message.trim()) >= 0; } @@ -43,11 +37,11 @@ export class MockTestRunner { } public stdOutContained(message: string): boolean { - return !!(this.stdout && this.stdout.indexOf(message) > 0); + return this.stdout.indexOf(message.trim()) > 0; } public stdErrContained(message: string): boolean { - return !!(this.stderr && this.stderr.indexOf(message) > 0); + return this.stderr.indexOf(message.trim()) > 0; } public run(): void { From 1b1d33de5d5f614e53ea1738252e23db286b2661 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 19 Jun 2018 15:00:00 -0400 Subject: [PATCH 066/259] norc on bash -c for test failing on certain environments --- node/test/toolrunnertests.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index c2ebaa3b7..e502c1cf2 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -109,12 +109,13 @@ describe('Toolrunner Tests', function () { } else { tool = tl.tool(tl.which('bash', true)); + tool.arg('--norc'); tool.arg('-c'); tool.arg('echo hello from stderr 1>&2 ; exit 123'); } var ret = tool.execSync(_testExecOptions); - assert.equal(ret.code, 123, 'return code of tool should be 1'); + assert.equal(ret.code, 123, 'return code of tool should be 123'); assert.equal(ret.stderr.toString().trim(), 'hello from stderr'); done(); }) From 89c94b4e46be711d0344e2e3f926a12920f45f07 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Fri, 22 Jun 2018 10:26:28 -0400 Subject: [PATCH 067/259] Add back some type annotations --- node/internal.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index 77a657e07..12e641a46 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -919,11 +919,11 @@ export function _normalizeSeparators(p: string): string { // Expose proxy information to vsts-node-api //----------------------------------------------------- export function _exposeProxySettings(): void { - let proxyUrl = _getVariable('Agent.ProxyUrl'); + let proxyUrl: string | undefined = _getVariable('Agent.ProxyUrl'); if (proxyUrl && proxyUrl.length > 0) { - let proxyUsername = _getVariable('Agent.ProxyUsername'); - let proxyPassword = _getVariable('Agent.ProxyPassword'); - let proxyBypassHostsJson = _getVariable('Agent.ProxyBypassList'); + let proxyUsername: string | undefined = _getVariable('Agent.ProxyUsername'); + let proxyPassword: string | undefined = _getVariable('Agent.ProxyPassword'); + let proxyBypassHostsJson: string | undefined = _getVariable('Agent.ProxyBypassList'); global['_vsts_task_lib_proxy_url'] = proxyUrl; global['_vsts_task_lib_proxy_username'] = proxyUsername; @@ -939,16 +939,16 @@ export function _exposeProxySettings(): void { // Expose certificate information to vsts-node-api //----------------------------------------------------- export function _exposeCertSettings(): void { - let ca = _getVariable('Agent.CAInfo'); + let ca: string | undefined = _getVariable('Agent.CAInfo'); if (ca) { global['_vsts_task_lib_cert_ca'] = ca; } let clientCert = _getVariable('Agent.ClientCert'); if (clientCert) { - let clientCertKey = _getVariable('Agent.ClientCertKey'); - let clientCertArchive = _getVariable('Agent.ClientCertArchive'); - let clientCertPassword = _getVariable('Agent.ClientCertPassword'); + let clientCertKey: string | undefined = _getVariable('Agent.ClientCertKey'); + let clientCertArchive: string | undefined = _getVariable('Agent.ClientCertArchive'); + let clientCertPassword: string | undefined = _getVariable('Agent.ClientCertPassword'); global['_vsts_task_lib_cert_clientcert'] = clientCert; global['_vsts_task_lib_cert_key'] = clientCertKey; From 9cedd04cc64c6b899b8b7826c54ec67c4682aa28 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Tue, 26 Jun 2018 15:07:39 -0400 Subject: [PATCH 068/259] Update timeout. (#375) --- node/test/toolrunnertests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index e502c1cf2..321f367e9 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -396,7 +396,7 @@ describe('Toolrunner Tests', function () { }); }) it('Exec pipe output to another tool, succeeds if both tools succeed', function (done) { - this.timeout(20000); + this.timeout(25000); var _testExecOptions = { cwd: __dirname, From 6b9c4f4a758a92c3d328e40cf22d9482229dc0d2 Mon Sep 17 00:00:00 2001 From: Sandor <2660262+sandorfr@users.noreply.github.com> Date: Sat, 30 Jun 2018 22:39:04 +1000 Subject: [PATCH 069/259] Add missing comma (#377) There was a missing comma making the document unusable. --- tasks.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks.schema.json b/tasks.schema.json index 627d46b48..4b798d7ea 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -33,7 +33,7 @@ "type": "boolean" }, "showEnvironmentVariables": { - "type": "boolean" + "type": "boolean", "description": "Toggles showing the environment variable editor in the task editor UI. Allows passing environment variables to script based tasks." }, "runsOn": { From 3a7905b99d698a535d9f5a477efc124894b8d2ae Mon Sep 17 00:00:00 2001 From: Navin Date: Thu, 5 Jul 2018 17:41:07 +0530 Subject: [PATCH 070/259] Adding a new API to get the test run system from the publisher tasks. (#373) --- node/package.json | 2 +- node/task.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/node/package.json b/node/package.json index 1c0dce6b2..17d1f00f8 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.5.0", + "version": "2.6.0", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index 8f7ea0ba7..9983ad3fb 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1681,8 +1681,10 @@ export class TestPublisher { public testRunner: string; - public publish(resultFiles, mergeResults, platform, config, runTitle, publishRunAttachments) { + public publish(resultFiles, mergeResults, platform, config, runTitle, publishRunAttachments): void; + public publish(resultsFiles, mergeResults, platform, config, runTitle, publishRunAttachments, testRunSystem): void; + public publish(resultFiles, mergeResults, platform, config, runTitle, publishRunAttachments, testRunSystem = "VSTSTask") { var properties = <{ [key: string]: string }>{}; properties['type'] = this.testRunner; @@ -1710,6 +1712,8 @@ export class TestPublisher { properties['resultFiles'] = resultFiles; } + properties['testRunSystem'] = testRunSystem; + command('results.publish', properties, ''); } } From 2e457270d2f50dd74ac4299381343ee811eb11da Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Thu, 5 Jul 2018 16:43:03 -0400 Subject: [PATCH 071/259] Increase timeout. (#378) --- node/test/toolrunnertests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 321f367e9..45abfdfc8 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -396,7 +396,7 @@ describe('Toolrunner Tests', function () { }); }) it('Exec pipe output to another tool, succeeds if both tools succeed', function (done) { - this.timeout(25000); + this.timeout(30000); var _testExecOptions = { cwd: __dirname, From 1195328c3ab17a17a75360015e230193177c4981 Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Thu, 5 Jul 2018 16:48:59 -0400 Subject: [PATCH 072/259] Fix timeout. (#379) --- node/test/dirtests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/test/dirtests.ts b/node/test/dirtests.ts index 508691860..aaab2322e 100644 --- a/node/test/dirtests.ts +++ b/node/test/dirtests.ts @@ -540,7 +540,7 @@ describe('Dir Operation Tests', function () { // find tests it('returns hidden files with find', (done: MochaDone) => { - this.timeout(1000); + this.timeout(3000); // create the following layout: // find_hidden_files From d9e58069310f2ce902ad43ba4df9490b06859c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20D=C3=A9moulins?= Date: Thu, 12 Jul 2018 18:04:54 +0200 Subject: [PATCH 073/259] Find-Match in powershell does not support searching a pattern in a path that does not exist (#365) --- node/test/findmatchtests.ts | 16 ++++++++++++++ .../L0/Find-Match.SupportsPathNotFound.ps1 | 22 +++++++++++++++++++ powershell/VstsTaskSdk/FindFunctions.ps1 | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 powershell/Tests/L0/Find-Match.SupportsPathNotFound.ps1 diff --git a/node/test/findmatchtests.ts b/node/test/findmatchtests.ts index 13a3b5b63..1b0a39f8b 100644 --- a/node/test/findmatchtests.ts +++ b/node/test/findmatchtests.ts @@ -75,6 +75,22 @@ describe('Find and Match Tests', function () { done(); }); + it('supports path not found', (done: MochaDone) => { + this.timeout(1000); + + let root: string = path.join(testutil.getTestTemp(), 'find-and-match_supports-path-not-found'); + tl.mkdirP(root); + let patterns: string[] = [ + path.join(root, 'NotFound', '*.proj'), + ]; + + let actual: string[] = tl.findMatch('', patterns); + let expected: string[] = []; + assert.deepEqual(actual, expected); + + done(); + }); + it('does not duplicate matches', (done: MochaDone) => { this.timeout(1000); diff --git a/powershell/Tests/L0/Find-Match.SupportsPathNotFound.ps1 b/powershell/Tests/L0/Find-Match.SupportsPathNotFound.ps1 new file mode 100644 index 000000000..bd629a730 --- /dev/null +++ b/powershell/Tests/L0/Find-Match.SupportsPathNotFound.ps1 @@ -0,0 +1,22 @@ +[CmdletBinding()] +param() + +# Arrange. +. $PSScriptRoot\..\lib\Initialize-Test.ps1 +Invoke-VstsTaskScript -ScriptBlock { + $tempDirectory = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) + New-Item -Path $tempDirectory -ItemType Directory | + ForEach-Object { $_.FullName } + try { + $patterns = "$tempDirectory\NotFound\*.proj" + + # Act. + $actual = Find-VstsMatch -Pattern $patterns + + # Assert. + $expected = $null + Assert-AreEqual $expected $actual + } finally { + Remove-Item $tempDirectory -Recurse + } +} \ No newline at end of file diff --git a/powershell/VstsTaskSdk/FindFunctions.ps1 b/powershell/VstsTaskSdk/FindFunctions.ps1 index 8687f1bf4..c0278ea01 100644 --- a/powershell/VstsTaskSdk/FindFunctions.ps1 +++ b/powershell/VstsTaskSdk/FindFunctions.ps1 @@ -155,7 +155,7 @@ function Find-Match { $findResults += $findPath } } else { - $findResults = Get-FindResult -Path $findPath -Options $FindOptions + $findResults = @( Get-FindResult -Path $findPath -Options $FindOptions ) } Write-Verbose "Found $($findResults.Count) paths." From f6ac00662f9156fe826da9495d2cddf77caaa6a8 Mon Sep 17 00:00:00 2001 From: Chris Sidi Date: Tue, 14 Aug 2018 13:48:55 -0400 Subject: [PATCH 074/259] Update docs (#390) --- node/docs/docs.json | 16 ++- node/docs/vsts-task-lib.json | 204 +++++++++++++++++++++++++++++------ node/docs/vsts-task-lib.md | 46 ++++++-- node/package-lock.json | 2 +- 4 files changed, 220 insertions(+), 48 deletions(-) diff --git a/node/docs/docs.json b/node/docs/docs.json index a3f1da4db..aac80228a 100644 --- a/node/docs/docs.json +++ b/node/docs/docs.json @@ -41,9 +41,9 @@ "task.execSync", "task.setResult" ] - }, - "Service Endpoints": { - "Summary": "Retrieve service endpoints and authorization details", + }, + "Service Connections": { + "Summary": "Retrieve service connections (previously called \"service endpoints\") and authorization details", "Document": [ "task.getEndpointUrl", "task.getEndpointDataParameter", @@ -52,7 +52,13 @@ "task.EndpointAuthorization", "task.getEndpointAuthorization" ] - }, + }, + "Secrets": { + "Summary": "Functions for managing pipeline secrets", + "Document": [ + "task.setSecret" + ] + }, "Secure Files": { "Summary": "Retrieve secure files details required to download the file", "Document": [ @@ -103,6 +109,6 @@ "Document": [ "task.getHttpProxyConfiguration" ] - } + } } } \ No newline at end of file diff --git a/node/docs/vsts-task-lib.json b/node/docs/vsts-task-lib.json index 91ef1f065..0bbfe2859 100644 --- a/node/docs/vsts-task-lib.json +++ b/node/docs/vsts-task-lib.json @@ -157,6 +157,39 @@ } ] }, + "_processLineBuffer": { + "name": "_processLineBuffer", + "members": {}, + "documentation": "", + "kind": "method", + "signatures": [ + { + "parameters": [ + { + "name": "data", + "type": "any", + "optional": false, + "documentation": "" + }, + { + "name": "strBuffer", + "type": "string", + "optional": false, + "documentation": "" + }, + { + "name": "onLine", + "type": "(line: string) => void", + "optional": false, + "documentation": "" + } + ], + "members": {}, + "return": "void", + "documentation": "" + } + ] + }, "_getSpawnFileName": { "name": "_getSpawnFileName", "members": {}, @@ -393,6 +426,12 @@ "type": "ToolRunner", "optional": false, "documentation": "" + }, + { + "name": "file", + "type": "string", + "optional": true, + "documentation": "optional filename to additionally stream the output to." } ], "members": {}, @@ -665,7 +704,7 @@ "name": "secret", "type": "boolean", "optional": true, - "documentation": "whether variable is secret. optional, defaults to false" + "documentation": "whether variable is secret. Multi-line secrets are not allowed. Optional, defaults to false" } ], "members": {}, @@ -674,6 +713,27 @@ } ] }, + "setSecret": { + "name": "setSecret", + "members": {}, + "documentation": "Registers a value with the logger, so the value will be masked from the logs. Multi-line secrets are not allowed.", + "kind": "function", + "signatures": [ + { + "parameters": [ + { + "name": "val", + "type": "string", + "optional": false, + "documentation": "value to register" + } + ], + "members": {}, + "return": "void", + "documentation": "Registers a value with the logger, so the value will be masked from the logs. Multi-line secrets are not allowed." + } + ] + }, "VariableInfo": { "name": "VariableInfo", "members": { @@ -1611,6 +1671,11 @@ "FindOptions": { "name": "FindOptions", "members": { + "allowBrokenSymbolicLinks": { + "documentation": "When true, broken symbolic link will not cause an error.", + "name": "allowBrokenSymbolicLinks", + "return": "boolean" + }, "followSpecifiedSymbolicLink": { "documentation": "Equivalent to the -H command line option. Indicates whether to traverse descendants if\nthe specified path is a symbolic link directory. Does not cause nested symbolic link\ndirectories to be traversed.", "name": "followSpecifiedSymbolicLink", @@ -2136,13 +2201,66 @@ "kind": "function", "signatures": [ { - "parameters": [], + "parameters": [ + { + "name": "requestUrl", + "type": "string", + "optional": true, + "documentation": "" + } + ], "members": {}, "return": "ProxyConfiguration", "documentation": "Gets http proxy configuration used by Build/Release agent\n\n@return ProxyConfiguration" } ] }, + "CertConfiguration": { + "name": "CertConfiguration", + "members": { + "caFile": { + "documentation": "", + "name": "caFile", + "return": "string" + }, + "certFile": { + "documentation": "", + "name": "certFile", + "return": "string" + }, + "keyFile": { + "documentation": "", + "name": "keyFile", + "return": "string" + }, + "certArchiveFile": { + "documentation": "", + "name": "certArchiveFile", + "return": "string" + }, + "passphrase": { + "documentation": "", + "name": "passphrase", + "return": "string" + } + }, + "documentation": "", + "kind": "interface" + }, + "getHttpCertConfiguration": { + "name": "getHttpCertConfiguration", + "members": {}, + "documentation": "Gets http certificate configuration used by Build/Release agent\n\n@return CertConfiguration", + "kind": "function", + "signatures": [ + { + "parameters": [], + "members": {}, + "return": "CertConfiguration", + "documentation": "Gets http certificate configuration used by Build/Release agent\n\n@return CertConfiguration" + } + ] + }, "TestPublisher": { "name": "TestPublisher", "members": { @@ -2194,6 +2312,55 @@ "members": {}, "return": "void", "documentation": "" + }, + { + "parameters": [ + { + "name": "resultsFiles", + "type": "any", + "optional": false, + "documentation": "" + }, + { + "name": "mergeResults", + "type": "any", + "optional": false, + "documentation": "" + }, + { + "name": "platform", + "type": "any", + "optional": false, + "documentation": "" + }, + { + "name": "config", + "type": "any", + "optional": false, + "documentation": "" + }, + { + "name": "runTitle", + "type": "any", + "optional": false, + "documentation": "" + }, + { + "name": "publishRunAttachments", + "type": "any", + "optional": false, + "documentation": "" + }, + { + "name": "testRunSystem", + "type": "any", + "optional": false, + "documentation": "" + } + ], + "members": {}, + "return": "void", + "documentation": "" } ] } @@ -2318,39 +2485,6 @@ "documentation": "" } ] - }, - "publishTelemetry": { - "name": "publishTelemetry", - "members": {}, - "documentation": "Publishes the telemetry data\n\n@returns void", - "kind": "function", - "signatures": [ - { - "parameters": [ - { - "name": "area", - "type": "string", - "optional": false, - "documentation": "Area path where the telemetry will be published" - }, - { - "name": "feature", - "type": "string", - "optional": false, - "documentation": "Feature group where the telemetry will be published" - }, - { - "name": "properties", - "type": "{ [key: string]: any; }", - "optional": false, - "documentation": "telemetry data which will be published" - } - ], - "members": {}, - "return": "void", - "documentation": "Publishes the telemetry data\n\n@returns void" - } - ] } } } diff --git a/node/docs/vsts-task-lib.md b/node/docs/vsts-task-lib.md index df8812fe2..bb0670ad4 100644 --- a/node/docs/vsts-task-lib.md +++ b/node/docs/vsts-task-lib.md @@ -49,7 +49,7 @@ import tl = require('vsts-task-lib/task') execSync
setResult
-### Service Endpoints (v) +### Service Connections (v) getEndpointUrl
getEndpointDataParameter
@@ -58,6 +58,10 @@ import tl = require('vsts-task-lib/task') EndpointAuthorization
getEndpointAuthorization
+### Secrets (v) + +setSecret
+ ### Secure Files (v) getSecureFileName
@@ -260,7 +264,7 @@ Param | Type | Description --- | --- | --- name | string | name of the variable to set val | string | value to set -secret | boolean | whether variable is secret. optional, defaults to false +secret | boolean | whether variable is secret. Multi\-line secrets are not allowed. Optional, defaults to false
@@ -441,12 +445,13 @@ options | IExecSyncOptions | optional exec options. See IExecSyncOptions Pipe output of exec() to another tool @returns {ToolRunner} ```javascript -pipeExecOutputToTool(tool:ToolRunner):ToolRunner +pipeExecOutputToTool(tool:ToolRunner, file?:string):ToolRunner ``` Param | Type | Description --- | --- | --- tool | ToolRunner | +file | string | optional filename to additionally stream the output to.
@@ -521,13 +526,13 @@ message | string | A message which will be logged as an error issue if the resul
-
+
-## Service Endpoints +## Service Connections --- -Retrieve service endpoints and authorization details +Retrieve service connections (previously called "service endpoints") and authorization details
@@ -624,6 +629,28 @@ id | string | name of the service endpoint optional | boolean | whether the url is optional +
+
+ +## Secrets + +--- + +Functions for managing pipeline secrets +
+
+ +### task.setSecret (^) +Registers a value with the logger, so the value will be masked from the logs. Multi-line secrets are not allowed. +```javascript +setSecret(val:string):void +``` + +Param | Type | Description +--- | --- | --- +val | string | value to register + +
@@ -792,6 +819,7 @@ Contains properties to control whether to follow symlinks Property | Type | Description --- | --- | --- +allowBrokenSymbolicLinks | boolean | When true, broken symbolic link will not cause an error. followSpecifiedSymbolicLink | boolean | Equivalent to the \-H command line option. Indicates whether to traverse descendants if the specified path is a symbolic link directory. Does not cause nested symbolic link directories to be traversed. followSymbolicLinks | boolean | Equivalent to the \-L command line option. Indicates whether to traverse descendants of symbolic link directories. @@ -1072,6 +1100,10 @@ Gets http proxy configuration used by Build/Release agent @return ProxyConfiguration ```javascript -getHttpProxyConfiguration():ProxyConfiguration +getHttpProxyConfiguration(requestUrl?:string):ProxyConfiguration ``` +Param | Type | Description +--- | --- | --- +requestUrl | string | + diff --git a/node/package-lock.json b/node/package-lock.json index 4e3305bdb..a2ae8e176 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.5.0", + "version": "2.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { From 491cf85372a8367a0a0b1b71b99337168a52f790 Mon Sep 17 00:00:00 2001 From: Keith Robertson Date: Wed, 12 Sep 2018 21:34:24 -0700 Subject: [PATCH 075/259] Add mocks for proxy helpers --- node/mock-task.ts | 18 ++++++++++++++++-- node/package-lock.json | 2 +- node/package.json | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/node/mock-task.ts b/node/mock-task.ts index 731d2cb88..c5b30c41d 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -89,7 +89,7 @@ module.exports.getEndpointAuthorizationScheme = task.getEndpointAuthorizationSch module.exports.getEndpointAuthorizationParameter = task.getEndpointAuthorizationParameter; module.exports.getEndpointAuthorization = task.getEndpointAuthorization; -// TODO: should go away when task lib +// TODO: should go away when task lib export interface EndpointAuthorization { parameters: { [key: string]: string; @@ -427,7 +427,7 @@ export class CodeCoveragePublisher { properties['additionalcodecoveragefiles'] = additionalCodeCoverageFiles; } - module.exports.command('codecoverage.publish', properties, ""); + module.exports.command('codecoverage.publish', properties, ""); } } @@ -456,3 +456,17 @@ export class CodeCoverageEnabler { exports.TaskCommand = tcm.TaskCommand; exports.commandFromString = tcm.commandFromString; exports.ToolRunner = trm.ToolRunner; + +//----------------------------------------------------- +// Http Proxy Helper +//----------------------------------------------------- +export function getHttpProxyConfiguration(requestUrl?: string): task.ProxyConfiguration { + return null; +} + +//----------------------------------------------------- +// Http Certificate Helper +//----------------------------------------------------- +export function getHttpCertConfiguration(): task.CertConfiguration { + return null +} \ No newline at end of file diff --git a/node/package-lock.json b/node/package-lock.json index a2ae8e176..8a362afed 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.6.0", + "version": "2.6.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 17d1f00f8..4fd9d92e4 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.6.0", + "version": "2.6.1", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 800dadcce45aee0b5bd64f293292fe3177c06362 Mon Sep 17 00:00:00 2001 From: ericsciple Date: Mon, 1 Oct 2018 14:12:06 -0400 Subject: [PATCH 076/259] handle exit event; added overload for setResult to expose a 'done' boolean (#397) --- .../resources.resjson/en-US/resources.resjson | 4 + node/lib.json | 4 + node/make.js | 2 +- node/package-lock.json | 72 +-- node/task.ts | 14 +- node/test/scripts/wait-for-file.js | 35 ++ node/test/toolrunnertests.ts | 239 ++++++++- node/toolrunner.ts | 465 ++++++++++++------ 8 files changed, 638 insertions(+), 197 deletions(-) create mode 100644 node/test/scripts/wait-for-file.js diff --git a/node/Strings/resources.resjson/en-US/resources.resjson b/node/Strings/resources.resjson/en-US/resources.resjson index 8fa6a5bc1..586217d7a 100644 --- a/node/Strings/resources.resjson/en-US/resources.resjson +++ b/node/Strings/resources.resjson/en-US/resources.resjson @@ -6,10 +6,14 @@ "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Unable to create directory '%s'. Root directory does not exist: '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "Unable to create directory '%s'. Unable to verify the directory exists: '%s'. If directory is a file share, please verify the share name is correct, the share is online, and the current process has permission to access the share.", "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", + "loc.messages.LIB_ProcessError": "There was an error when attempting to execute the process '%s'. This may indicate the process failed to start. Error: %s", + "loc.messages.LIB_ProcessExitCode": "The process '%s' failed with exit code %s", + "loc.messages.LIB_ProcessStderr": "The process '%s' failed because one or more lines were written to the STDERR stream", "loc.messages.LIB_ReturnCode": "Return code: %d", "loc.messages.LIB_ResourceFileNotExist": "Resource file doesn\\'t exist: %s", "loc.messages.LIB_ResourceFileAlreadySet": "Resource file has already set to: %s", "loc.messages.LIB_ResourceFileNotSet": "Resource file haven\\'t set, can\\'t find loc string for key: %s", + "loc.messages.LIB_StdioNotClosed": "The STDIO streams did not close within %s seconds of the exit event from process '%s'. This may indicate a child process inherited the STDIO streams and has not yet exited.", "loc.messages.LIB_WhichNotFound_Linux": "Unable to locate executable file: '%s'. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.", "loc.messages.LIB_WhichNotFound_Win": "Unable to locate executable file: '%s'. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.", "loc.messages.LIB_LocStringNotFound": "Can\\'t find loc string for key: %s", diff --git a/node/lib.json b/node/lib.json index caed8df73..e1954de2d 100644 --- a/node/lib.json +++ b/node/lib.json @@ -7,10 +7,14 @@ "LIB_MkdirFailedInvalidDriveRoot": "Unable to create directory '%s'. Root directory does not exist: '%s'", "LIB_MkdirFailedInvalidShare": "Unable to create directory '%s'. Unable to verify the directory exists: '%s'. If directory is a file share, please verify the share name is correct, the share is online, and the current process has permission to access the share.", "LIB_MultilineSecret": "Secrets cannot contain multiple lines", + "LIB_ProcessError": "There was an error when attempting to execute the process '%s'. This may indicate the process failed to start. Error: %s", + "LIB_ProcessExitCode": "The process '%s' failed with exit code %s", + "LIB_ProcessStderr": "The process '%s' failed because one or more lines were written to the STDERR stream", "LIB_ReturnCode": "Return code: %d", "LIB_ResourceFileNotExist": "Resource file doesn\\'t exist: %s", "LIB_ResourceFileAlreadySet": "Resource file has already set to: %s", "LIB_ResourceFileNotSet": "Resource file haven\\'t set, can\\'t find loc string for key: %s", + "LIB_StdioNotClosed": "The STDIO streams did not close within %s seconds of the exit event from process '%s'. This may indicate a child process inherited the STDIO streams and has not yet exited.", "LIB_WhichNotFound_Linux": "Unable to locate executable file: '%s'. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.", "LIB_WhichNotFound_Win": "Unable to locate executable file: '%s'. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.", "LIB_LocStringNotFound": "Can\\'t find loc string for key: %s", diff --git a/node/make.js b/node/make.js index 4e9bbf2c5..2071dbc54 100644 --- a/node/make.js +++ b/node/make.js @@ -47,7 +47,7 @@ target.test = function() { run('tsc -p ./test'); cp('-Rf', rp('test/scripts'), testPath); process.env['TASKLIB_INPROC_UNITS'] = '1'; // export task-lib internals for internal unit testing - run('mocha ' + testPath + ' --recursive'); + run('mocha ' + testPath); } target.loc = function() { diff --git a/node/package-lock.json b/node/package-lock.json index 8a362afed..091495072 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -20,7 +20,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -53,9 +53,9 @@ "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.5", - "typedarray": "0.0.6" + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, "core-util-is": { @@ -97,12 +97,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "growl": { @@ -129,9 +129,9 @@ "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", "dev": true, "requires": { - "caseless": "0.11.0", - "concat-stream": "1.6.1", - "http-response-object": "1.1.0" + "caseless": "~0.11.0", + "concat-stream": "^1.4.6", + "http-response-object": "^1.0.0" } }, "http-response-object": { @@ -146,8 +146,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -167,7 +167,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -220,7 +220,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "path-is-absolute": { @@ -241,7 +241,7 @@ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dev": true, "requires": { - "asap": "2.0.6" + "asap": "~2.0.3" } }, "q": { @@ -261,13 +261,13 @@ "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "safe-buffer": { @@ -292,7 +292,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "supports-color": { @@ -301,7 +301,7 @@ "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } }, "sync-request": { @@ -310,9 +310,9 @@ "integrity": "sha1-yqEjWq+Im6UBB2oYNMQ2gwqC+3M=", "dev": true, "requires": { - "concat-stream": "1.6.1", - "http-response-object": "1.1.0", - "then-request": "2.2.0" + "concat-stream": "^1.4.7", + "http-response-object": "^1.0.1", + "then-request": "^2.0.1" } }, "then-request": { @@ -321,12 +321,12 @@ "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", "dev": true, "requires": { - "caseless": "0.11.0", - "concat-stream": "1.6.1", - "http-basic": "2.5.1", - "http-response-object": "1.1.0", - "promise": "7.3.1", - "qs": "6.5.1" + "caseless": "~0.11.0", + "concat-stream": "^1.4.7", + "http-basic": "^2.5.1", + "http-response-object": "^1.1.0", + "promise": "^7.1.1", + "qs": "^6.1.0" } }, "typedarray": { diff --git a/node/task.ts b/node/task.ts index 9983ad3fb..7a11c1502 100644 --- a/node/task.ts +++ b/node/task.ts @@ -35,9 +35,12 @@ export const setErrStream = im._setErrStream; * * @param result TaskResult enum of Succeeded, SucceededWithIssues or Failed. * @param message A message which will be logged as an error issue if the result is Failed. + * @param done Optional. Instructs the agent the task is done. This is helpful when child processes + * may still be running and prevent node from fully exiting. This argument is supported + * from agent version 2.142.0 or higher (otherwise will no-op). * @returns void */ -export function setResult(result: TaskResult, message: string): void { +export function setResult(result: TaskResult, message: string, done?: boolean): void { debug('task result: ' + TaskResult[result]); // add an error issue @@ -48,8 +51,13 @@ export function setResult(result: TaskResult, message: string): void { warning(message); } - // set the task result - command('task.complete', { 'result': TaskResult[result] }, message); + // task.complete + var properties = { 'result': TaskResult[result] }; + if (done) { + properties['done'] = 'true'; + } + + command('task.complete', properties, message); } // diff --git a/node/test/scripts/wait-for-file.js b/node/test/scripts/wait-for-file.js new file mode 100644 index 000000000..70b727d81 --- /dev/null +++ b/node/test/scripts/wait-for-file.js @@ -0,0 +1,35 @@ +var fs = require('fs'); + +// get the command line args that use the format someArg=someValue +var args = {}; +process.argv.forEach(function (arg) { + var match = arg.match(/^(.+)=(.*)$/); + if (match) { + args[match[1]] = match[2]; + } +}); + +var state = { + file: args.file +}; + +if (!state.file) { + throw new Error('file is not specified'); +} + +state.checkFile = function (s) { + try { + fs.statSync(s.file); + } + catch (err) { + if (err.code == 'ENOENT') { + return; + } + + throw err; + } + + setTimeout(s.checkFile, 100, s); +}; + +state.checkFile(state); diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 45abfdfc8..1f18a7500 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -217,8 +217,9 @@ describe('Toolrunner Tests', function () { var output = ''; if (os.platform() === 'win32') { - var cmd = tl.tool(tl.which('cmd', true)); - cmd.arg('/c echo \'vsts-task-lib\''); + var cmd = tl.tool(tl.which('cmd', true)) + .arg('/c') + .arg('echo \'vsts-task-lib\''); cmd.on('stdout', (data) => { output = data.toString(); @@ -287,7 +288,7 @@ describe('Toolrunner Tests', function () { done(err); } else { - assert(err.message.indexOf('return code: 1') >= 0, `expected error message to indicate "return code: 1". actual error message: "${err}"`); + assert(err.message.indexOf('failed with exit code 1') >= 0, `expected error message to indicate "failed with exit code 1". actual error message: "${err}"`); assert(output && output.length > 0, 'should have emitted stderr'); done(); } @@ -318,7 +319,7 @@ describe('Toolrunner Tests', function () { done(err); } else { - assert(err.message.indexOf('return code: 123') >= 0, `expected error message to indicate "return code: 123". actual error message: "${err}"`); + assert(err.message.indexOf('failed with exit code 123') >= 0, `expected error message to indicate "failed with exit code 123". actual error message: "${err}"`); assert(output && output.length > 0, 'should have emitted stderr'); done(); } @@ -386,7 +387,7 @@ describe('Toolrunner Tests', function () { done(err); } else { - assert(err.message.indexOf('return code: 0') >= 0, `expected error message to indicate "return code: 0". actual error message: "${err}"`); + assert(err.message.indexOf('one or more lines were written to the STDERR stream') >= 0, `expected error message to indicate "one or more lines were written to the STDERR stream". actual error message: "${err}"`); assert(output && output.length > 0, 'should have emitted stderr'); done(); } @@ -395,6 +396,219 @@ describe('Toolrunner Tests', function () { done(err); }); }) + it('Fails when process fails to launch', function (done) { + this.timeout(10000); + + var tool = tl.tool(tl.which('node', true)); + var _testExecOptions = { + cwd: path.join(testutil.getTestTemp(), 'nosuchdir'), + env: {}, + silent: false, + failOnStdErr: true, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + } + + var output = ''; + tool.on('stderr', (data) => { + output = data.toString(); + }); + + var succeeded = false; + tool.exec(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('should not have succeeded'); + }) + .fail(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err.message.indexOf('This may indicate the process failed to start') >= 0, `expected error message to indicate "This may indicate the process failed to start". actual error message: "${err}"`); + done(); + } + }) + .fail(function (err) { + done(err); + }); + }) + it('Handles child process holding streams open', function (done) { + this.timeout(10000); + + let semaphorePath = path.join(testutil.getTestTemp(), 'child-process-semaphore.txt'); + fs.writeFileSync(semaphorePath, ''); + + let nodePath = tl.which('node', true); + let scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js'); + let shell: trm.ToolRunner; + if (os.platform() == 'win32') { + shell = tl.tool(tl.which('cmd.exe', true)) + .arg('/D') // Disable execution of AutoRun commands from registry. + .arg('/E:ON') // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. + .arg('/V:OFF') // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. + .arg('/S') // Will cause first and last quote after /C to be stripped. + .arg('/C') + .arg(`"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}""`); + } + else { + shell = tl.tool(tl.which('bash', true)) + .arg('-c') + .arg(`node '${scriptPath}' 'file=${semaphorePath}' &`); + } + + let toolRunnerDebug = []; + shell.on('debug', function (data) { + toolRunnerDebug.push(data); + }); + + process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = 500; // 0.5 seconds + + let options = { + cwd: __dirname, + env: process.env, + silent: false, + failOnStdErr: true, + ignoreReturnCode: false, + outStream: process.stdout, + errStream: process.stdout, + windowsVerbatimArguments: true + }; + + shell.exec(options) + .then(function () { + assert(toolRunnerDebug.filter((x) => x.indexOf('STDIO streams did not close') >= 0).length == 1, 'Did not find expected debug message'); + done(); + }) + .fail(function (err) { + done(err); + }) + .finally(function () { + fs.unlinkSync(semaphorePath); + delete process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY']; + }); + }) + it('Handles child process holding streams open and non-zero exit code', function (done) { + this.timeout(10000); + + let semaphorePath = path.join(testutil.getTestTemp(), 'child-process-semaphore.txt'); + fs.writeFileSync(semaphorePath, ''); + + let nodePath = tl.which('node', true); + let scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js'); + let shell: trm.ToolRunner; + if (os.platform() == 'win32') { + shell = tl.tool(tl.which('cmd.exe', true)) + .arg('/D') // Disable execution of AutoRun commands from registry. + .arg('/E:ON') // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. + .arg('/V:OFF') // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. + .arg('/S') // Will cause first and last quote after /C to be stripped. + .arg('/C') + .arg(`"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}"" & exit /b 123`); + } + else { + shell = tl.tool(tl.which('bash', true)) + .arg('-c') + .arg(`node '${scriptPath}' 'file=${semaphorePath}' & exit 123`); + } + + let toolRunnerDebug = []; + shell.on('debug', function (data) { + toolRunnerDebug.push(data); + }); + + process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = 500; // 0.5 seconds + + let options = { + cwd: __dirname, + env: process.env, + silent: false, + failOnStdErr: true, + ignoreReturnCode: false, + outStream: process.stdout, + errStream: process.stdout, + windowsVerbatimArguments: true + }; + + shell.exec(options) + .then(function () { + done(new Error('should not have been successful')); + done(); + }) + .fail(function (err) { + assert(toolRunnerDebug.filter((x) => x.indexOf('STDIO streams did not close') >= 0).length == 1, 'Did not find expected debug message'); + assert(err.message.indexOf('failed with exit code 123') >= 0); + done(); + }) + .fail(function (err) { + done(err); + }) + .finally(function () { + fs.unlinkSync(semaphorePath); + delete process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY']; + }); + }) + it('Handles child process holding streams open and stderr', function (done) { + this.timeout(10000); + + let semaphorePath = path.join(testutil.getTestTemp(), 'child-process-semaphore.txt'); + fs.writeFileSync(semaphorePath, ''); + + let nodePath = tl.which('node', true); + let scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js'); + let shell: trm.ToolRunner; + if (os.platform() == 'win32') { + shell = tl.tool(tl.which('cmd.exe', true)) + .arg('/D') // Disable execution of AutoRun commands from registry. + .arg('/E:ON') // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. + .arg('/V:OFF') // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. + .arg('/S') // Will cause first and last quote after /C to be stripped. + .arg('/C') + .arg(`"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}"" & echo hi 1>&2`); + } + else { + shell = tl.tool(tl.which('bash', true)) + .arg('-c') + .arg(`node '${scriptPath}' 'file=${semaphorePath}' & echo hi 1>&2`); + } + + let toolRunnerDebug = []; + shell.on('debug', function (data) { + toolRunnerDebug.push(data); + }); + + process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = 500; // 0.5 seconds + + let options = { + cwd: __dirname, + env: process.env, + silent: false, + failOnStdErr: true, + ignoreReturnCode: false, + outStream: process.stdout, + errStream: process.stdout, + windowsVerbatimArguments: true + }; + + shell.exec(options) + .then(function () { + done(new Error('should not have been successful')); + done(); + }) + .fail(function (err) { + assert(toolRunnerDebug.filter((x) => x.indexOf('STDIO streams did not close') >= 0).length == 1, 'Did not find expected debug message'); + assert(err.message.indexOf('failed because one or more lines were written to the STDERR stream') >= 0); + done(); + }) + .fail(function (err) { + done(err); + }) + .finally(function () { + fs.unlinkSync(semaphorePath); + delete process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY']; + }); + }) it('Exec pipe output to another tool, succeeds if both tools succeed', function (done) { this.timeout(30000); @@ -599,25 +813,26 @@ describe('Toolrunner Tests', function () { var grep = tl.tool(tl.which('grep', true)); grep.arg('--?'); - var ps = tl.tool(tl.which('ps', true)); - ps.arg('ax'); - ps.pipeExecOutputToTool(grep); + var node = tl.tool(tl.which('node', true)) + .arg('-e') + .arg('console.log("line1"); setTimeout(function () { console.log("line2"); }, 200);'); // allow long enough to hook up stdout to stdin + node.pipeExecOutputToTool(grep); var output = ''; - ps.on('stdout', (data) => { + node.on('stdout', (data) => { output += data.toString(); }); var errOut = ''; - ps.on('stderr', (data) => { + node.on('stderr', (data) => { errOut += data.toString(); }) var succeeded = false; - ps.exec(_testExecOptions) + node.exec(_testExecOptions) .then(function (code) { succeeded = true; - assert.fail('ps ax | grep --? was a bad command and it did not fail'); + assert.fail('node [...] | grep --? was a bad command and it did not fail'); }) .fail(function (err) { if (succeeded) { diff --git a/node/toolrunner.ts b/node/toolrunner.ts index e87fdf420..a752a5503 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -6,7 +6,6 @@ import events = require('events'); import child = require('child_process'); import stream = require('stream'); import im = require('./internal'); -import tcm = require('./taskcommand'); import fs = require('fs'); /** @@ -491,6 +490,206 @@ export class ToolRunner extends events.EventEmitter { return result; } + private execWithPiping(options?: IExecOptions): Q.Promise { + var defer = Q.defer(); + + this._debug('exec tool: ' + this.toolPath); + this._debug('arguments:'); + this.args.forEach((arg) => { + this._debug(' ' + arg); + }); + + let success = true; + options = this._cloneExecOptions(options); + + if (!options.silent) { + options.outStream.write(this._getCommandString(options) + os.EOL); + } + + let cp; + let toolPath: string = this.toolPath; + let toolPathFirst: string; + let successFirst = true; + let returnCodeFirst: number; + let fileStream: fs.WriteStream; + let waitingEvents: number = 0; // number of process or stream events we are waiting on to complete + let returnCode: number = 0; + let error; + + toolPath = this.pipeOutputToTool.toolPath; + toolPathFirst = this.toolPath; + + // Following node documentation example from this link on how to pipe output of one process to another + // https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options + + //start the child process for both tools + waitingEvents++; + var cpFirst = child.spawn( + this._getSpawnFileName(), + this._getSpawnArgs(options), + this._getSpawnOptions(options)); + + waitingEvents ++; + cp = child.spawn( + this.pipeOutputToTool._getSpawnFileName(), + this.pipeOutputToTool._getSpawnArgs(options), + this.pipeOutputToTool._getSpawnOptions(options)); + + fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; + if (fileStream) { + waitingEvents ++; + fileStream.on('finish', () => { + waitingEvents--; //file write is complete + fileStream = null; + if(waitingEvents == 0) { + if (error) { + defer.reject(error); + } else { + defer.resolve(returnCode); + } + } + }); + fileStream.on('error', (err) => { + waitingEvents--; //there were errors writing to the file, write is done + this._debug(`Failed to pipe output of ${toolPathFirst} to file ${this.pipeOutputToFile}. Error = ${err}`); + fileStream = null; + if(waitingEvents == 0) { + if (error) { + defer.reject(error); + } else { + defer.resolve(returnCode); + } + } + }) + } + + //pipe stdout of first tool to stdin of second tool + cpFirst.stdout.on('data', (data: Buffer) => { + try { + if (fileStream) { + fileStream.write(data); + } + cp.stdin.write(data); + } catch (err) { + this._debug('Failed to pipe output of ' + toolPathFirst + ' to ' + toolPath); + this._debug(toolPath + ' might have exited due to errors prematurely. Verify the arguments passed are valid.'); + } + }); + cpFirst.stderr.on('data', (data: Buffer) => { + if (fileStream) { + fileStream.write(data); + } + successFirst = !options.failOnStdErr; + if (!options.silent) { + var s = options.failOnStdErr ? options.errStream : options.outStream; + s.write(data); + } + }); + cpFirst.on('error', (err) => { + waitingEvents--; //first process is complete with errors + if (fileStream) { + fileStream.end(); + } + cp.stdin.end(); + error = new Error(toolPathFirst + ' failed. ' + err.message); + if(waitingEvents == 0) { + defer.reject(error); + } + }); + cpFirst.on('close', (code, signal) => { + waitingEvents--; //first process is complete + if (code != 0 && !options.ignoreReturnCode) { + successFirst = false; + returnCodeFirst = code; + returnCode = returnCodeFirst; + } + this._debug('success of first tool:' + successFirst); + if (fileStream) { + fileStream.end(); + } + cp.stdin.end(); + if(waitingEvents == 0) { + if (error) { + defer.reject(error); + } else { + defer.resolve(returnCode); + } + } + }); + + var stdbuffer: string = ''; + cp.stdout.on('data', (data: Buffer) => { + this.emit('stdout', data); + + if (!options.silent) { + options.outStream.write(data); + } + + this._processLineBuffer(data, stdbuffer, (line: string) => { + this.emit('stdline', line); + }); + }); + + var errbuffer: string = ''; + cp.stderr.on('data', (data: Buffer) => { + this.emit('stderr', data); + + success = !options.failOnStdErr; + if (!options.silent) { + var s = options.failOnStdErr ? options.errStream : options.outStream; + s.write(data); + } + + this._processLineBuffer(data, errbuffer, (line: string) => { + this.emit('errline', line); + }); + }); + + cp.on('error', (err) => { + waitingEvents--; //process is done with errors + error = new Error(toolPath + ' failed. ' + err.message); + if(waitingEvents == 0) { + defer.reject(error); + } + }); + + cp.on('close', (code, signal) => { + waitingEvents--; //process is complete + this._debug('rc:' + code); + returnCode = code; + + if (stdbuffer.length > 0) { + this.emit('stdline', stdbuffer); + } + + if (errbuffer.length > 0) { + this.emit('errline', errbuffer); + } + + if (code != 0 && !options.ignoreReturnCode) { + success = false; + } + + this._debug('success:' + success); + + if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools + error = new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst); + } else if (!success) { + error = new Error(toolPath + ' failed with return code: ' + code); + } + + if(waitingEvents == 0) { + if (error) { + defer.reject(error); + } else { + defer.resolve(returnCode); + } + } + }); + + return >defer.promise; + } + /** * Add argument * Append an argument or an array of arguments @@ -572,6 +771,10 @@ export class ToolRunner extends events.EventEmitter { * @returns number */ public exec(options?: IExecOptions): Q.Promise { + if (this.pipeOutputToTool) { + return this.execWithPiping(options); + } + var defer = Q.defer(); this._debug('exec tool: ' + this.toolPath); @@ -580,130 +783,17 @@ export class ToolRunner extends events.EventEmitter { this._debug(' ' + arg); }); - let success = true; options = this._cloneExecOptions(options); - if (!options.silent) { options.outStream.write(this._getCommandString(options) + os.EOL); } - // TODO: filter process.env - let cp; - let toolPath: string = this.toolPath; - let toolPathFirst: string; - let successFirst = true; - let returnCodeFirst: number; - let fileStream: fs.WriteStream; - let waitingEvents: number = 0; // number of process or stream events we are waiting on to complete - let returnCode: number = 0; - let error; - - if (this.pipeOutputToTool) { - toolPath = this.pipeOutputToTool.toolPath; - toolPathFirst = this.toolPath; - - // Following node documentation example from this link on how to pipe output of one process to another - // https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options - - //start the child process for both tools - waitingEvents++; - var cpFirst = child.spawn( - this._getSpawnFileName(), - this._getSpawnArgs(options), - this._getSpawnOptions(options)); - - waitingEvents ++; - cp = child.spawn( - this.pipeOutputToTool._getSpawnFileName(), - this.pipeOutputToTool._getSpawnArgs(options), - this.pipeOutputToTool._getSpawnOptions(options)); - - fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; - if (fileStream) { - waitingEvents ++; - fileStream.on('finish', () => { - waitingEvents--; //file write is complete - fileStream = null; - if(waitingEvents == 0) { - if (error) { - defer.reject(error); - } else { - defer.resolve(returnCode); - } - } - }); - fileStream.on('error', (err) => { - waitingEvents--; //there were errors writing to the file, write is done - this._debug(`Failed to pipe output of ${toolPathFirst} to file ${this.pipeOutputToFile}. Error = ${err}`); - fileStream = null; - if(waitingEvents == 0) { - if (error) { - defer.reject(error); - } else { - defer.resolve(returnCode); - } - } - }) - } - - //pipe stdout of first tool to stdin of second tool - cpFirst.stdout.on('data', (data: Buffer) => { - try { - if (fileStream) { - fileStream.write(data); - } - cp.stdin.write(data); - } catch (err) { - this._debug('Failed to pipe output of ' + toolPathFirst + ' to ' + toolPath); - this._debug(toolPath + ' might have exited due to errors prematurely. Verify the arguments passed are valid.'); - } - }); - cpFirst.stderr.on('data', (data: Buffer) => { - if (fileStream) { - fileStream.write(data); - } - successFirst = !options.failOnStdErr; - if (!options.silent) { - var s = options.failOnStdErr ? options.errStream : options.outStream; - s.write(data); - } - }); - cpFirst.on('error', (err) => { - waitingEvents--; //first process is complete with errors - if (fileStream) { - fileStream.end(); - } - cp.stdin.end(); - error = new Error(toolPathFirst + ' failed. ' + err.message); - if(waitingEvents == 0) { - defer.reject(error); - } - }); - cpFirst.on('close', (code, signal) => { - waitingEvents--; //first process is complete - if (code != 0 && !options.ignoreReturnCode) { - successFirst = false; - returnCodeFirst = code; - returnCode = returnCodeFirst; - } - this._debug('success of first tool:' + successFirst); - if (fileStream) { - fileStream.end(); - } - cp.stdin.end(); - if(waitingEvents == 0) { - if (error) { - defer.reject(error); - } else { - defer.resolve(returnCode); - } - } - }); + let state = new ExecState(options, this.toolPath); + state.on('debug', (message) => { + this._debug(message); + }); - } else { - waitingEvents++; - cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(options), this._getSpawnOptions(options)); - } + let cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(options), this._getSpawnOptions(options)); var stdbuffer: string = ''; cp.stdout.on('data', (data: Buffer) => { @@ -720,9 +810,9 @@ export class ToolRunner extends events.EventEmitter { var errbuffer: string = ''; cp.stderr.on('data', (data: Buffer) => { + state.processStderr = true; this.emit('stderr', data); - success = !options.failOnStdErr; if (!options.silent) { var s = options.failOnStdErr ? options.errStream : options.outStream; s.write(data); @@ -734,18 +824,28 @@ export class ToolRunner extends events.EventEmitter { }); cp.on('error', (err) => { - waitingEvents--; //process is done with errors - error = new Error(toolPath + ' failed. ' + err.message); - if(waitingEvents == 0) { - defer.reject(error); - } + state.processError = err.message; + state.processExited = true; + state.processClosed = true; + state.CheckComplete(); + }); + + cp.on('exit', (code, signal) => { + state.processExitCode = code; + state.processExited = true; + this._debug(`Exit code ${code} received from tool '${this.toolPath}'`); + state.CheckComplete() }); cp.on('close', (code, signal) => { - waitingEvents--; //process is complete - this._debug('rc:' + code); - returnCode = code; + state.processExitCode = code; + state.processExited = true; + state.processClosed = true; + this._debug(`STDIO streams have closed for tool '${this.toolPath}'`) + state.CheckComplete(); + }); + state.on('done', (error, exitCode) => { if (stdbuffer.length > 0) { this.emit('stdline', stdbuffer); } @@ -754,24 +854,13 @@ export class ToolRunner extends events.EventEmitter { this.emit('errline', errbuffer); } - if (code != 0 && !options.ignoreReturnCode) { - success = false; - } + cp.removeAllListeners(); - this._debug('success:' + success); - - if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools - error = new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst); - } else if (!success) { - error = new Error(toolPath + ' failed with return code: ' + code); + if (error) { + defer.reject(error); } - - if(waitingEvents == 0) { - if (error) { - defer.reject(error); - } else { - defer.resolve(returnCode); - } + else { + defer.resolve(exitCode); } }); @@ -819,4 +908,90 @@ export class ToolRunner extends events.EventEmitter { res.stderr = (r.stderr) ? r.stderr.toString() : null; return res; } -} \ No newline at end of file +} + +class ExecState extends events.EventEmitter { + constructor( + options: IExecOptions, + toolPath: string) { + + super(); + + if (!toolPath) { + throw new Error('toolPath must not be empty'); + } + + this.options = options; + this.toolPath = toolPath; + let delay = process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY']; + if (delay) { + this.delay = parseInt(delay); + } + } + + processClosed: boolean; // tracks whether the process has exited and stdio is closed + processError: string; + processExitCode: number; + processExited: boolean; // tracks whether the process has exited + processStderr: boolean; // tracks whether stderr was written to + private delay = 10000; // 10 seconds + private done: boolean; + private options: IExecOptions; + private timeout; + private toolPath: string; + + public CheckComplete(): void { + if (this.done) { + return; + } + + if (this.processClosed) { + this._setResult(); + } + else if (this.processExited) { + this.timeout = setTimeout(ExecState.HandleTimeout, this.delay, this); + } + } + + private _debug(message): void { + this.emit('debug', message); + } + + private _setResult(): void { + // determine whether there is an error + let error: Error; + if (this.processExited) { + if (this.processError) { + error = new Error(im._loc('LIB_ProcessError', this.toolPath, this.processError)); + } + else if (this.processExitCode != 0 && !this.options.ignoreReturnCode) { + error = new Error(im._loc('LIB_ProcessExitCode', this.toolPath, this.processExitCode)); + } + else if (this.processStderr && this.options.failOnStdErr) { + error = new Error(im._loc('LIB_ProcessStderr', this.toolPath)); + } + } + + // clear the timeout + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } + + this.done = true; + this.emit('done', error, this.processExitCode); + } + + private static HandleTimeout(state: ExecState) { + if (state.done) { + return; + } + + if (!state.processClosed && state.processExited) { + console.log(im._loc('LIB_StdioNotClosed', state.delay / 1000, state.toolPath)); + state._debug(im._loc('LIB_StdioNotClosed', state.delay / 1000, state.toolPath)); + } + + state._setResult(); + } +} From 0b96551bd24bc693cdf57552d01742f52ce972a5 Mon Sep 17 00:00:00 2001 From: ericsciple Date: Mon, 1 Oct 2018 14:27:43 -0400 Subject: [PATCH 077/259] v2.7.0 (#407) --- node/docs/releases.md | 4 ++++ node/package-lock.json | 2 +- node/package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/node/docs/releases.md b/node/docs/releases.md index 8989c7ad6..ce2f344e9 100644 --- a/node/docs/releases.md +++ b/node/docs/releases.md @@ -1,5 +1,9 @@ # VSTS-TASK-LIB RELEASES +## 2.7.0 + * Updated `setResult` to expose optional done parameter + * Updated `ToolRunner` to distinguish between events for process exit and STDIO streams closed + ## 2.5.0 * Updated `FindOptions` to expose `allowBrokenSymbolicLinks`. diff --git a/node/package-lock.json b/node/package-lock.json index 091495072..d960362d0 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.6.1", + "version": "2.7.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 4fd9d92e4..322ccef4a 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-lib", - "version": "2.6.1", + "version": "2.7.0", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 0c873415d5ae647f0f4eca76dd3984abe4b5c9be Mon Sep 17 00:00:00 2001 From: damccorm Date: Wed, 10 Oct 2018 09:59:27 -0400 Subject: [PATCH 078/259] Take care of some simple doucmentation issues (#411) * Fixed issue 388 * Fixed issue 205 * Get rid of extra stuff, wasn't helping * Get rid of extra stuff, wasn't helping * Add note about exporting * capitalize Mac --- node/docs/stepbystep.md | 10 ++++++++-- powershell/Docs/README.md | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/node/docs/stepbystep.md b/node/docs/stepbystep.md index 1d99b58f0..42dc71764 100644 --- a/node/docs/stepbystep.md +++ b/node/docs/stepbystep.md @@ -5,8 +5,6 @@ This step by step will show how to manually create, debug and test a cross platf Tasks can be created using tfx as a convenience, but it's good to understand the parts of a task. This tutorial walks through each manual step of creating a task. -Note: The tutorial was done on a mac. Attempted to make generic for all platforms. Create issues in the repo. - ## Video https://youtu.be/O_34c7p4GlM?t=1173 @@ -25,6 +23,14 @@ This tutorial uses [VS Code](https://code.visualstudio.com) for great intellisen Files used for this walk through are [located here in this gist](https://gist.github.com/bryanmacfarlane/154f14dd8cb11a71ef04b0c836e5be6e) +## Unix vs Windows + +This tutorial was done on a Mac. We attempted to make it generic for all platforms, but the syntax for setting environment variables is different. + +If using Windows, replace any instances of ```export =``` with ```$env:=``` + +If there are additional discrepancies or unexpected behavior, please create issues in the repo. + ## Create Task Scaffolding Create a directory and package.json. Accept defaults of npm init for sample. diff --git a/powershell/Docs/README.md b/powershell/Docs/README.md index 77aadf55f..e01b5facb 100644 --- a/powershell/Docs/README.md +++ b/powershell/Docs/README.md @@ -5,7 +5,7 @@ The VSTS Task SDK for PowerShell is designed to work with the agent's new PowerS ## Reference Examples -The [MSBuild Task](https://github.com/Microsoft/vsts-tasks/blob/master/Tasks/MSBuild/MSBuild.ps1) and [VSBuild Task](https://github.com/Microsoft/vsts-tasks/blob/master/Tasks/VSBuild/VSBuild.ps1) are good examples. +The [MSBuild Task](https://github.com/Microsoft/vsts-tasks/blob/master/Tasks/MSBuildV1/MSBuild.ps1) and [VSBuild Task](https://github.com/Microsoft/vsts-tasks/blob/master/Tasks/VSBuildV1/VSBuild.ps1) are good examples. ## Documentation From de5f350a063ac2e62da225ea469f224524825277 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Wed, 10 Oct 2018 15:29:35 -0500 Subject: [PATCH 079/259] Update TestingAndDebugging.md (#412) $env:ENDPOINT_DATA_EP1 = '{ "Key1": "Value1", "Key2", "Value2" }' was incorrect syntax. Replaced "Key2", with "Key2": --- powershell/Docs/TestingAndDebugging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powershell/Docs/TestingAndDebugging.md b/powershell/Docs/TestingAndDebugging.md index b3fed5fe6..01d2489df 100644 --- a/powershell/Docs/TestingAndDebugging.md +++ b/powershell/Docs/TestingAndDebugging.md @@ -24,7 +24,7 @@ $env:INPUT_MYINPUT = [...] $env:INPUT_MYENDPOINT = 'EP1' $env:ENDPOINT_URL_EP1 = 'https://[...]' $env:ENDPOINT_AUTH_EP1 = '{ "Parameters": { "UserName": "Some user", "Password": "Some password" }, "Scheme": "Some scheme" }' -$env:ENDPOINT_DATA_EP1 = '{ "Key1": "Value1", "Key2", "Value2" }' +$env:ENDPOINT_DATA_EP1 = '{ "Key1": "Value1", "Key2": "Value2" }' ``` For the convenience of interactive testing, the module will prompt for undefined task variables and inputs. For example, `Get-VstsTaskInput -Name SomeVariable` will prompt for the value if the task variable is not defined. If a value is entered, then it will be stored so that subsequent calls will return the same value. Task variables are stored as environment variables. Inputs and endpoints are stored internally within the VstsTaskSdk module and can be cleared by removing and re-importing the module. @@ -49,4 +49,4 @@ To view the verbose output when testing interactively: ```PowerShell Import-Module .\ps_modules\VstsTaskSdk Invoke-VstsTaskScript -ScriptBlock { . .\MyTask.ps1 } -Verbose -``` \ No newline at end of file +``` From 2b014286874f16c528907b08b839df8dc81a33e1 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sat, 13 Oct 2018 09:53:11 -0400 Subject: [PATCH 080/259] rebrand --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 00f15452e..9e4cf57ed 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # VSTS DevOps Task SDK -Libraries for writing [Visual Studio Team Services](https://www.visualstudio.com/en-us/products/visual-studio-team-services-vs.aspx) build and deployment tasks +Libraries for writing [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) tasks Reference examples of our in the box tasks [are here](https://github.com/Microsoft/vsts-tasks) From 36d132f07e4a4e030b2d30683eeeadf9efade184 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sat, 13 Oct 2018 09:54:02 -0400 Subject: [PATCH 081/259] update heading --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e4cf57ed..fec27f3f3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# VSTS DevOps Task SDK +# Azure Pipelines Task SDK Libraries for writing [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) tasks From 66384f0de07eeebcf9f318b8a62c8367320c2d26 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sun, 14 Oct 2018 19:03:07 -0400 Subject: [PATCH 082/259] more rebranding --- .vsts-ci.yml => azure-pipelines.yml | 0 node/package-lock.json | 74 ++++++++++++++--------------- node/package.json | 6 +-- 3 files changed, 40 insertions(+), 40 deletions(-) rename .vsts-ci.yml => azure-pipelines.yml (100%) diff --git a/.vsts-ci.yml b/azure-pipelines.yml similarity index 100% rename from .vsts-ci.yml rename to azure-pipelines.yml diff --git a/node/package-lock.json b/node/package-lock.json index d960362d0..e45648de1 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,5 +1,5 @@ { - "name": "vsts-task-lib", + "name": "azure-pipelines-task-lib", "version": "2.7.0", "lockfileVersion": 1, "requires": true, @@ -20,7 +20,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -53,9 +53,9 @@ "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==", "dev": true, "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "inherits": "2.0.3", + "readable-stream": "2.3.5", + "typedarray": "0.0.6" } }, "core-util-is": { @@ -97,12 +97,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "growl": { @@ -129,9 +129,9 @@ "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", "dev": true, "requires": { - "caseless": "~0.11.0", - "concat-stream": "^1.4.6", - "http-response-object": "^1.0.0" + "caseless": "0.11.0", + "concat-stream": "1.6.1", + "http-response-object": "1.1.0" } }, "http-response-object": { @@ -146,8 +146,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -167,7 +167,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -220,7 +220,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "path-is-absolute": { @@ -241,7 +241,7 @@ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dev": true, "requires": { - "asap": "~2.0.3" + "asap": "2.0.6" } }, "q": { @@ -261,13 +261,13 @@ "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "safe-buffer": { @@ -292,7 +292,7 @@ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "supports-color": { @@ -301,7 +301,7 @@ "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "2.0.0" } }, "sync-request": { @@ -310,9 +310,9 @@ "integrity": "sha1-yqEjWq+Im6UBB2oYNMQ2gwqC+3M=", "dev": true, "requires": { - "concat-stream": "^1.4.7", - "http-response-object": "^1.0.1", - "then-request": "^2.0.1" + "concat-stream": "1.6.1", + "http-response-object": "1.1.0", + "then-request": "2.2.0" } }, "then-request": { @@ -321,12 +321,12 @@ "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", "dev": true, "requires": { - "caseless": "~0.11.0", - "concat-stream": "^1.4.7", - "http-basic": "^2.5.1", - "http-response-object": "^1.1.0", - "promise": "^7.1.1", - "qs": "^6.1.0" + "caseless": "0.11.0", + "concat-stream": "1.6.1", + "http-basic": "2.5.1", + "http-response-object": "1.1.0", + "promise": "7.3.1", + "qs": "6.5.1" } }, "typedarray": { diff --git a/node/package.json b/node/package.json index 322ccef4a..0a26786fc 100644 --- a/node/package.json +++ b/node/package.json @@ -1,5 +1,5 @@ { - "name": "vsts-task-lib", + "name": "azure-pipelines-task-lib", "version": "2.7.0", "description": "VSTS Task SDK", "main": "./task.js", @@ -21,9 +21,9 @@ "author": "Microsoft", "license": "MIT", "bugs": { - "url": "https://github.com/Microsoft/vsts-task-lib/issues" + "url": "https://github.com/Microsoft/azure-pipelines-task-lib/issues" }, - "homepage": "https://github.com/Microsoft/vsts-task-lib", + "homepage": "https://github.com/Microsoft/azure-pipelines-task-lib", "dependencies": { "minimatch": "3.0.4", "mockery": "^1.7.0", From f57b4ffdcc4c0cba1ae88fa7fee2669c06cbe060 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sun, 14 Oct 2018 19:50:26 -0400 Subject: [PATCH 083/259] update yaml --- azure-pipelines.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 215276b98..e81c4200c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,12 +2,13 @@ trigger: - master - features/* -phases: +jobs: ################################################# -- phase: windows +- job: windows ################################################# - condition: and(succeeded(), eq(variables.os, 'Windows_NT')) - queue: Hosted VS2017 + displayName: windows + pool: + vmImage:vs2017-win2016 steps: ################################################################################ @@ -61,8 +62,9 @@ phases: ################################################# - phase: linux ################################################# - condition: and(succeeded(), eq(variables.os, 'Linux')) - queue: Hosted Linux Preview + displayName: Linux + pool: + vmImage: ubuntu-16.04 steps: ################################################################################ @@ -101,8 +103,9 @@ phases: ################################################# - phase: macOS ################################################# - condition: and(succeeded(), eq(variables.os, 'Darwin')) - queue: Hosted macOS Preview + displayName: macOS + pool: + vmImage: macOS-10.13 steps: ################################################################################ From 3ba557560d0cf93957aadcc6d94bf48a1bcd5d0a Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sun, 14 Oct 2018 19:51:47 -0400 Subject: [PATCH 084/259] job typo --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e81c4200c..fa69f6c99 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -60,7 +60,7 @@ jobs: workingDirectory: powershell ################################################# -- phase: linux +- job: linux ################################################# displayName: Linux pool: @@ -101,7 +101,7 @@ jobs: workingDirectory: node ################################################# -- phase: macOS +- job: macOS ################################################# displayName: macOS pool: From c319ce1518bc3611022ae351cd5ddc79ff78752c Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sun, 14 Oct 2018 19:56:55 -0400 Subject: [PATCH 085/259] space typo --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fa69f6c99..96bcc2864 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -8,7 +8,7 @@ jobs: ################################################# displayName: windows pool: - vmImage:vs2017-win2016 + vmImage: vs2017-win2016 steps: ################################################################################ From 99ba16bc00631600f6fb1bb24a57a38c3f7c838b Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sun, 14 Oct 2018 21:30:56 -0400 Subject: [PATCH 086/259] update links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fec27f3f3..e842dcd94 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ Reference examples of our in the box tasks [are here](https://github.com/Microso ## Status | | Build & Test | |---|:-----:| -|![Win](res/win_med.png) **Windows**|![Build & Test](https://mseng.visualstudio.com/DefaultCollection/_apis/public/build/definitions/b924d696-3eae-4116-8443-9a18392d8544/2553/badge?branch=master)| -|![Apple](res/apple_med.png) **OSX**|![Build & Test](https://mseng.visualstudio.com/_apis/public/build/definitions/b924d696-3eae-4116-8443-9a18392d8544/5471/badge?branch=master)| -|![Ubuntu14](res/ubuntu_med.png) **Ubuntu 14.04**|![Build & Test](https://mseng.visualstudio.com/_apis/public/build/definitions/b924d696-3eae-4116-8443-9a18392d8544/4123/badge?branch=master)| +|![Win](res/win_med.png) **Windows**|![Build & Test](https://mseng.visualstudio.com/pipelinetools/_apis/build/status/azure-pipelines-task-lib?branchName=master&jobname=windows)| +|![Apple](res/apple_med.png) **OSX**|![Build & Test](https://mseng.visualstudio.com/pipelinetools/_apis/build/status/azure-pipelines-task-lib?branchName=master&jobname=macOS)| +|![Ubuntu14](res/ubuntu_med.png) **Ubuntu 14.04**|![Build & Test](https://mseng.visualstudio.com/pipelinetools/_apis/build/status/azure-pipelines-task-lib?branchName=master&jobname=linux)| ## Highlights From 066c8c2babbbad685333637ea90490722d41176c Mon Sep 17 00:00:00 2001 From: Stephen Franceschelli Date: Mon, 15 Oct 2018 14:52:28 -0400 Subject: [PATCH 087/259] Fix badges. (#456) * Fix badges. * Fix Linux. --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e842dcd94..0747242b2 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,17 @@ Libraries for writing [Azure Pipelines](https://azure.microsoft.com/en-us/servic Reference examples of our in the box tasks [are here](https://github.com/Microsoft/vsts-tasks) ## Status + | | Build & Test | |---|:-----:| -|![Win](res/win_med.png) **Windows**|![Build & Test](https://mseng.visualstudio.com/pipelinetools/_apis/build/status/azure-pipelines-task-lib?branchName=master&jobname=windows)| -|![Apple](res/apple_med.png) **OSX**|![Build & Test](https://mseng.visualstudio.com/pipelinetools/_apis/build/status/azure-pipelines-task-lib?branchName=master&jobname=macOS)| -|![Ubuntu14](res/ubuntu_med.png) **Ubuntu 14.04**|![Build & Test](https://mseng.visualstudio.com/pipelinetools/_apis/build/status/azure-pipelines-task-lib?branchName=master&jobname=linux)| +|![Win-x64](res/win_med.png) **Windows**|[![Build & Test][win-build-badge]][build]| +|![macOS](res/apple_med.png) **macOS**|[![Build & Test][macOS-build-badge]][build]| +|![Linux-x64](res/ubuntu_med.png) **Linux**|[![Build & Test][linux-build-badge]][build]| + +[win-build-badge]: https://dev.azure.com/mseng/PipelineTools/_apis/build/status/azure-pipelines-task-lib-ci?branchName=master&jobname=windows +[macOS-build-badge]: https://dev.azure.com/mseng/PipelineTools/_apis/build/status/azure-pipelines-task-lib-ci?branchName=master&jobname=macOS +[linux-build-badge]: https://dev.azure.com/mseng/PipelineTools/_apis/build/status/azure-pipelines-task-lib-ci?branchName=master&jobname=linux +[build]: https://dev.azure.com/mseng/PipelineTools/_build/latest?definitionId=7623 ## Highlights From b9602e028255065aed92c64fa5c07f235542b1bc Mon Sep 17 00:00:00 2001 From: damccorm Date: Tue, 16 Oct 2018 09:23:32 -0400 Subject: [PATCH 088/259] Update links and string in documents from rebranding (#457) * Update references to vsts-task-lib to azure-pipelines-task-lib * Rename files in node docs * Keep capitalization consistent in headings * Bump patch version * Don't need patch bump for powershell Only docs were changed. --- README.md | 4 +- azure-pipelines.yml | 36 +++++++++--------- node/README.md | 6 +-- node/ThirdPartyNotice.txt | 2 +- node/dependencies/typings.json | 2 +- ...lib.json => azure-pipelines-task-lib.json} | 0 ...ask-lib.md => azure-pipelines-task-lib.md} | 18 ++++----- node/docs/cert.md | 6 +-- node/docs/findingfiles.md | 12 +++--- node/docs/gendocs.ts | 14 +++---- node/docs/proxy.md | 6 +-- node/docs/releases.md | 2 +- node/docs/samples/loc.src | 2 +- node/docs/samples/toolrunner.src | 6 +-- node/docs/stepbystep.md | 18 ++++----- node/generate-third-party-notice.js | 2 +- node/internal.ts | 4 +- node/mock-run.ts | 10 ++--- node/package-lock.json | 2 +- node/package.json | 4 +- node/test/inputtests.ts | 2 +- node/test/testindex.d.ts | 2 +- node/test/toolrunnertests.ts | 10 ++--- node/typings.json | 2 +- powershell/Docs/Commands.md | 10 ++--- .../FullHelp/Get-VstsAssemblyReference.md | 2 +- .../FullHelp/Get-VstsTfsClientCredentials.md | 4 +- .../Docs/FullHelp/Get-VstsTfsService.md | 4 +- .../Docs/FullHelp/Get-VstsVssCredentials.md | 4 +- .../Docs/FullHelp/Get-VstsVssHttpClient.md | 4 +- powershell/Docs/FullHelp/Get-VstsWebProxy.md | 2 +- powershell/VstsTaskSdk/ServerOMFunctions.ps1 | 20 +++++----- powershell/VstsTaskSdk/VstsTaskSdk.psd1 | Bin 1678 -> 1722 bytes powershell/package.json | 6 +-- 34 files changed, 114 insertions(+), 114 deletions(-) rename node/docs/{vsts-task-lib.json => azure-pipelines-task-lib.json} (100%) rename node/docs/{vsts-task-lib.md => azure-pipelines-task-lib.md} (97%) diff --git a/README.md b/README.md index 0747242b2..f7d8485ef 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ A task which automates Powershell technologies can be written with our Powershel Documentation: [PowerShell API](powershell/Docs/README.md) -[npm-lib-image]: https://img.shields.io/npm/v/vsts-task-lib.svg?style=flat -[npm-lib-url]: https://www.npmjs.com/package/vsts-task-lib +[npm-lib-image]: https://img.shields.io/npm/v/azure-pipelines-task-lib.svg?style=flat +[npm-lib-url]: https://www.npmjs.com/package/azure-pipelines-task-lib [npm-sdk-image]: https://img.shields.io/npm/v/vsts-task-sdk.svg?style=flat [npm-sdk-url]: https://www.npmjs.com/package/vsts-task-sdk diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 96bcc2864..748eacfe1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -12,12 +12,12 @@ jobs: steps: ################################################################################ - # vsts-task-lib + # azure-pipelines-task-lib ################################################################################ # npm install - task: Npm@1 - displayName: (vsts-task-lib) npm install + displayName: (azure-pipelines-task-lib) npm install inputs: command: install workingDir: node @@ -25,22 +25,22 @@ jobs: # use node 5 - powershell: | & "$(build.sourcesDirectory)/res/UseNode5.ps1" - displayName: (vsts-task-lib) use node 5.10.1 + displayName: (azure-pipelines-task-lib) use node 5.10.1 # build/test - script: node make.js test workingDirectory: node - displayName: (vsts-task-lib) node make.js test + displayName: (azure-pipelines-task-lib) node make.js test # use node 6 - task: NodeTool@0 - displayName: (vsts-task-lib) use node 6.10.3 + displayName: (azure-pipelines-task-lib) use node 6.10.3 inputs: versionSpec: "6.10.3" # build/test - script: node make.js test - displayName: (vsts-task-lib) node make.js test + displayName: (azure-pipelines-task-lib) node make.js test workingDirectory: node ################################################################################ @@ -68,36 +68,36 @@ jobs: steps: ################################################################################ - # vsts-task-lib + # azure-pipelines-task-lib ################################################################################ # npm install - task: Npm@1 - displayName: (vsts-task-lib) npm install + displayName: (azure-pipelines-task-lib) npm install inputs: command: install workingDir: node # use node 5 - task: NodeTool@0 - displayName: (vsts-task-lib) use node 5.10.1 + displayName: (azure-pipelines-task-lib) use node 5.10.1 inputs: versionSpec: "5.10.1" # build/test - script: node make.js test workingDirectory: node - displayName: (vsts-task-lib) node make.js test + displayName: (azure-pipelines-task-lib) node make.js test # use node 6 - task: NodeTool@0 - displayName: (vsts-task-lib) use node 6.10.3 + displayName: (azure-pipelines-task-lib) use node 6.10.3 inputs: versionSpec: "6.10.3" # build/test - script: node make.js test - displayName: (vsts-task-lib) node make.js test + displayName: (azure-pipelines-task-lib) node make.js test workingDirectory: node ################################################# @@ -109,34 +109,34 @@ jobs: steps: ################################################################################ - # vsts-task-lib + # azure-pipelines-task-lib ################################################################################ # npm install - task: Npm@1 - displayName: (vsts-task-lib) npm install + displayName: (azure-pipelines-task-lib) npm install inputs: command: install workingDir: node # use node 5 - task: NodeTool@0 - displayName: (vsts-task-lib) use node 5.10.1 + displayName: (azure-pipelines-task-lib) use node 5.10.1 inputs: versionSpec: "5.10.1" # build/test - script: node make.js test workingDirectory: node - displayName: (vsts-task-lib) node make.js test + displayName: (azure-pipelines-task-lib) node make.js test # use node 6 - task: NodeTool@0 - displayName: (vsts-task-lib) use node 6.10.3 + displayName: (azure-pipelines-task-lib) use node 6.10.3 inputs: versionSpec: "6.10.3" # build/test - script: node make.js test - displayName: (vsts-task-lib) node make.js test + displayName: (azure-pipelines-task-lib) node make.js test workingDirectory: node diff --git a/node/README.md b/node/README.md index 3019d4b3d..6e3bda913 100644 --- a/node/README.md +++ b/node/README.md @@ -15,7 +15,7 @@ Cross platform tasks are written in TypeScript. It is the preferred way to writ Step by Step: [Create Task](docs/stepbystep.md) -Documentation: [TypeScript API](docs/vsts-task-lib.md), [task JSON schema](https://aka.ms/vsts-tasks.schema.json) +Documentation: [TypeScript API](docs/azure-pipelines-task-lib.md), [task JSON schema](https://aka.ms/vsts-tasks.schema.json) Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/minagent.md), [Proxy](docs/proxy.md), [Certificate](docs/cert.md) @@ -40,8 +40,8 @@ $ npm test Set environment variable TASK_TEST_TRACE=1 to display test output. -[npm-lib-image]: https://img.shields.io/npm/v/vsts-task-lib.svg?style=flat -[npm-lib-url]: https://www.npmjs.com/package/vsts-task-lib +[npm-lib-image]: https://img.shields.io/npm/v/azure-pipelines-task-lib.svg?style=flat +[npm-lib-url]: https://www.npmjs.com/package/azure-pipelines-task-lib ## Third Party Notices To generate/update third party notice file run: diff --git a/node/ThirdPartyNotice.txt b/node/ThirdPartyNotice.txt index 6d9e723ec..ddde2acda 100644 --- a/node/ThirdPartyNotice.txt +++ b/node/ThirdPartyNotice.txt @@ -2,7 +2,7 @@ THIRD-PARTY SOFTWARE NOTICES AND INFORMATION Do Not Translate or Localize -This Visual Studio Team Services extension (vsts-task-lib) is based on or incorporates material from the projects listed below (Third Party IP). The original copyright notice and the license under which Microsoft received such Third Party IP, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft licenses the Third Party IP to you under the licensing terms for the Visual Studio Team Services extension. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. +This Azure Pipelines extension (azure-pipelines-task-lib) is based on or incorporates material from the projects listed below (Third Party IP). The original copyright notice and the license under which Microsoft received such Third Party IP, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft licenses the Third Party IP to you under the licensing terms for the Visual Studio Team Services extension. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. 1. asap (git+https://github.com/kriskowal/asap.git) 2. balanced-match (git://github.com/juliangruber/balanced-match.git) diff --git a/node/dependencies/typings.json b/node/dependencies/typings.json index 4d3fecabe..9afa921b7 100644 --- a/node/dependencies/typings.json +++ b/node/dependencies/typings.json @@ -1,5 +1,5 @@ { - "name": "vsts-task-lib", + "name": "azure-pipelines-task-lib", "main": "task.d.ts", "globalDependencies": { "node": "registry:dt/node#6.0.0+20160709114037", diff --git a/node/docs/vsts-task-lib.json b/node/docs/azure-pipelines-task-lib.json similarity index 100% rename from node/docs/vsts-task-lib.json rename to node/docs/azure-pipelines-task-lib.json diff --git a/node/docs/vsts-task-lib.md b/node/docs/azure-pipelines-task-lib.md similarity index 97% rename from node/docs/vsts-task-lib.md rename to node/docs/azure-pipelines-task-lib.md index bb0670ad4..85a0f4d55 100644 --- a/node/docs/vsts-task-lib.md +++ b/node/docs/azure-pipelines-task-lib.md @@ -1,17 +1,17 @@ -# VSTS-TASK-LIB TYPESCRIPT API +# AZURE-PIPELINES-TASK-LIB TYPESCRIPT API ## Dependencies A [cross platform agent](https://github.com/Microsoft/vso-agent) OR a TFS 2015 Update 2 Windows agent (or higher) is required to run a Node task end-to-end. However, an agent is not required for interactively testing the task. ## Importing -For now, the built vsts-task-lib (in _build) should be packaged with your task in a node_modules folder +For now, the built azure-pipelines-task-lib (in _build) should be packaged with your task in a node_modules folder -The build generates a vsts-task-lib.d.ts file for use when compiling tasks +The build generates a azure-pipelines-task-lib.d.ts file for use when compiling tasks In the example below, it is in a folder named definitions above the tasks lib ``` -/// -import tl = require('vsts-task-lib/task') +/// +import tl = require('azure-pipelines-task-lib/task') ``` ## [Release notes](releases.md) @@ -311,9 +311,9 @@ secret | boolean | whether variable is secret. optional, defaults to false Tasks typically execute a series of tools (cli) and set the result of the task based on the outcome of those ```javascript -/// -import tl = require('vsts-task-lib/task'); -import tr = require('vsts-task-lib/toolrunner'); +/// +import tl = require('azure-pipelines-task-lib/task'); +import tr = require('azure-pipelines-task-lib/toolrunner'); try { var toolPath = tl.which('atool'); @@ -1035,7 +1035,7 @@ includeDirectories | boolean | whether to include directories in the result Localization is optional but is supported using these functions at runtime ```javascript -/// +/// tl.setResourcePath(path.join( __dirname, 'task.json')); diff --git a/node/docs/cert.md b/node/docs/cert.md index f270ae012..7354d7941 100644 --- a/node/docs/cert.md +++ b/node/docs/cert.md @@ -1,4 +1,4 @@ -### Get certificate configuration by using [VSTS-Task-Lib](https://github.com/Microsoft/vsts-task-lib) method (Min Agent Version 2.122.0) +### Get certificate configuration by using [AZURE-PIPELINES-TASK-LIB](https://github.com/Microsoft/azure-pipelines-task-lib) method (Min Agent Version 2.122.0) #### Node.js Lib @@ -21,7 +21,7 @@ export interface CertConfiguration { In the following example, we will retrieve certificate configuration information and use VSTS-Node-Api to make a Rest Api call back to VSTS/TFS service, the Rest call will use the certificates you configured in agent. ```typescript // MyCertExampleTask.ts -import tl = require('vsts-task-lib/task'); +import tl = require('azure-pipelines-task-lib/task'); import api = require('vso-node-api'); import VsoBaseInterfaces = require('vso-node-api/interfaces/common/VsoBaseInterfaces'); @@ -40,7 +40,7 @@ async function run() { // Options for VSTS-Node-Api, // this is not required if you want to send http request to the same TFS // instance the agent currently connect to. - // VSTS-Node-Api will pick up certificate setting from VSTS-Task-Lib automatically + // VSTS-Node-Api will pick up certificate setting from azure-pipelines-task-lib automatically let option: VsoBaseInterfaces.IRequestOptions = { cert: { caFile: "C:\\ca.pem", diff --git a/node/docs/findingfiles.md b/node/docs/findingfiles.md index ab5eccf8c..f5748904c 100644 --- a/node/docs/findingfiles.md +++ b/node/docs/findingfiles.md @@ -16,19 +16,19 @@ When designing a task glob input experience, one of the following UI layout patt ## Task Lib Functions -The above UI experiences translate to one of the following consumption patterns of the [task lib API](vsts-task-lib.md): +The above UI experiences translate to one of the following consumption patterns of the [task lib API](azure-pipelines-task-lib.md): 1. `filePath` input only - - Call [findMatch](vsts-task-lib.md#taskfindMatch) and pass the filePath input as the pattern. + - Call [findMatch](azure-pipelines-task-lib.md#taskfindMatch) and pass the filePath input as the pattern. 2. `filePath` input to specify a root directory, followed by a `multiLine` input for match patterns. - - Call [find](vsts-task-lib.md#taskfind) to recursively find all paths under the specified root directory. - - Then call [match](vsts-task-lib.md#taskmatch) to filter the results using the multiLine input as the patterns. + - Call [find](azure-pipelines-task-lib.md#taskfind) to recursively find all paths under the specified root directory. + - Then call [match](azure-pipelines-task-lib.md#taskmatch) to filter the results using the multiLine input as the patterns. 3. `filePath` input to specify a *default* root directory to root any unrooted patterns, followed by a `multiLine` input for match patterns. - - Call [findMatch](vsts-task-lib.md#taskfindMatch) and pass the filePath input as the defaultRoot and the multiLine input as the patterns. + - Call [findMatch](azure-pipelines-task-lib.md#taskfindMatch) and pass the filePath input as the defaultRoot and the multiLine input as the patterns. -Note, use [getDelimitedInput](vsts-task-lib.md#taskgetDelimitedInput) to split a multiLine input using the delimiter `'\n'`. +Note, use [getDelimitedInput](azure-pipelines-task-lib.md#taskgetDelimitedInput) to split a multiLine input using the delimiter `'\n'`. ## Recommended FindOptions and MatchOptions diff --git a/node/docs/gendocs.ts b/node/docs/gendocs.ts index 5192b7b18..86cf09a4f 100644 --- a/node/docs/gendocs.ts +++ b/node/docs/gendocs.ts @@ -20,8 +20,8 @@ let options: ts.CompilerOptions = { target: srcOptions['target'] } -const jsonDocName: string = "vsts-task-lib.json"; -const mdDocName: string = "vsts-task-lib.md"; +const jsonDocName: string = "azure-pipelines-task-lib.json"; +const mdDocName: string = "azure-pipelines-task-lib.md"; header('Generating ' + jsonDocName); let doc: ts2json.DocEntry = ts2json.generate(docs.files, options); @@ -155,20 +155,20 @@ var writeInterface = function(name: string, item: ts2json.DocEntry) { writeLine(); } -writeLine('# VSTS-TASK-LIB TYPESCRIPT API'); +writeLine('# AZURE-DEVOPS-TASK-LIB TYPESCRIPT API'); writeLine(); writeLine('## Dependencies'); writeLine('A [cross platform agent](https://github.com/Microsoft/vso-agent) OR a TFS 2015 Update 2 Windows agent (or higher) is required to run a Node task end-to-end. However, an agent is not required for interactively testing the task.'); writeLine(); writeLine('## Importing'); -writeLine('For now, the built vsts-task-lib (in _build) should be packaged with your task in a node_modules folder'); +writeLine('For now, the built azure-pipelines-task-lib (in _build) should be packaged with your task in a node_modules folder'); writeLine(); -writeLine('The build generates a vsts-task-lib.d.ts file for use when compiling tasks'); +writeLine('The build generates a azure-pipelines-task-lib.d.ts file for use when compiling tasks'); writeLine('In the example below, it is in a folder named definitions above the tasks lib'); writeLine(); writeLine('```'); -writeLine('/// '); -writeLine("import tl = require('vsts-task-lib/task')"); +writeLine('/// '); +writeLine("import tl = require('azure-pipelines-task-lib/task')"); writeLine('```'); writeLine(); writeLine('## [Release notes](releases.md)'); diff --git a/node/docs/proxy.md b/node/docs/proxy.md index d5413dd1b..11c6a2495 100644 --- a/node/docs/proxy.md +++ b/node/docs/proxy.md @@ -1,4 +1,4 @@ -### Get proxy configuration by using [VSTS-Task-Lib](https://github.com/Microsoft/vsts-task-lib) method +### Get proxy configuration by using [AZURE-DEVOPS-TASK-LIB](https://github.com/Microsoft/azure-pipelines-task-lib) method #### Node.js Lib @@ -20,7 +20,7 @@ export interface ProxyConfiguration { In the following example, we will retrieve proxy configuration information and use VSTS-Node-Api to make a Rest Api call back to VSTS/TFS service, the Rest call will go through the web proxy you configured in `.proxy` file. ```typescript // MyProxyExampleTask.ts -import tl = require('vsts-task-lib/task'); +import tl = require('azure-pipelines-task-lib/task'); import api = require('vso-node-api'); import VsoBaseInterfaces = require('vso-node-api/interfaces/common/VsoBaseInterfaces'); @@ -39,7 +39,7 @@ async function run() { // Options for VSTS-Node-Api, // this is not required if you want to send http request to the same VSTS/TFS // instance the agent currently connect to. - // VSTS-Node-Api will pick up proxy setting from VSTS-Task-Lib automatically + // VSTS-Node-Api will pick up proxy setting from azure-pipelines-task-lib automatically let option: VsoBaseInterfaces.IRequestOptions = { proxy: { proxyUrl: proxy.proxyUrl, diff --git a/node/docs/releases.md b/node/docs/releases.md index ce2f344e9..ff63c1444 100644 --- a/node/docs/releases.md +++ b/node/docs/releases.md @@ -1,4 +1,4 @@ -# VSTS-TASK-LIB RELEASES +# AZURE-PIPELINES-TASK-LIB RELEASES ## 2.7.0 * Updated `setResult` to expose optional done parameter diff --git a/node/docs/samples/loc.src b/node/docs/samples/loc.src index c284c05b6..1d88b20ce 100644 --- a/node/docs/samples/loc.src +++ b/node/docs/samples/loc.src @@ -1,4 +1,4 @@ -/// +/// tl.setResourcePath(path.join( __dirname, 'task.json')); diff --git a/node/docs/samples/toolrunner.src b/node/docs/samples/toolrunner.src index ddc0935e5..ed2a1009a 100644 --- a/node/docs/samples/toolrunner.src +++ b/node/docs/samples/toolrunner.src @@ -1,6 +1,6 @@ -/// -import tl = require('vsts-task-lib/task'); -import tr = require('vsts-task-lib/toolrunner'); +/// +import tl = require('azure-pipelines-task-lib/task'); +import tr = require('azure-pipelines-task-lib/toolrunner'); try { var toolPath = tl.which('atool'); diff --git a/node/docs/stepbystep.md b/node/docs/stepbystep.md index 42dc71764..06620332f 100644 --- a/node/docs/stepbystep.md +++ b/node/docs/stepbystep.md @@ -39,18 +39,18 @@ $ mkdir sampletask && cd sampletask $ npm init ``` -### Add vsts-task-lib +### Add azure-pipelines-task-lib -Add vsts-task-lib to your task. Remember your task must carry the lib. Ensure it's at least 0.9.5 which now carries typings. +Add azure-pipelines-task-lib to your task. Remember your task must carry the lib. Ensure it's at least 0.9.5 which now carries typings. The package.json should have dependency with ^. Ex: ^0.9.5. This means you are locked to 0.9.x and will pick up patches on npm install. The npm module carries the .d.ts typecript definition files so compile and intellisense support will just work. ``` -$ npm install vsts-task-lib --save +$ npm install azure-pipelines-task-lib --save ... -└─┬ vsts-task-lib@2.0.5 +└─┬ azure-pipelines-task-lib@2.0.5 ... ``` @@ -95,8 +95,8 @@ Instellisense should just work in [VS Code](https://code.visualstudio.com) The code is straight forward. As a reference: ```javascript -import tl = require('vsts-task-lib/task'); -import trm = require('vsts-task-lib/toolrunner'); +import tl = require('azure-pipelines-task-lib/task'); +import trm = require('azure-pipelines-task-lib/toolrunner'); import mod = require('./taskmod'); async function run() { @@ -145,7 +145,7 @@ Key Points: - Never process.exit your task. You can sometimes lose output and often the last bit of output is critical If we did our job well, the code should be pretty self explanatory. -But, see the [API Reference](vsts-task-lib.md) for specifics. +But, see the [API Reference](azure-pipelines-task-lib.md) for specifics. ## Compile @@ -228,7 +228,7 @@ Node offers an interactive console and since the task lib is in your node_module ```bash $ node -> var tl = require('vsts-task-lib/task'); +> var tl = require('azure-pipelines-task-lib/task'); ##vso[task.debug]agent.workFolder=undefined ##vso[task.debug]loading inputs and endpoints ##vso[task.debug]loaded 0 @@ -248,7 +248,7 @@ Coming soon ## Unit testing your task scripts -This requires vsts-task-lib 0.9.15 or greater. +This requires azure-pipelines-task-lib 0.9.15 or greater. ### Goals: diff --git a/node/generate-third-party-notice.js b/node/generate-third-party-notice.js index 5d905984f..a076e4d64 100644 --- a/node/generate-third-party-notice.js +++ b/node/generate-third-party-notice.js @@ -159,7 +159,7 @@ function main(args) { const licenseInfo = Array.from(collectLicenseInfo(nodeModuleDir)); const writeStream = fs.createWriteStream(path.join(__dirname, 'ThirdPartyNotice.txt')); - writeLines(writeStream, thirdPartyNotice('vsts-task-lib', licenseInfo)); + writeLines(writeStream, thirdPartyNotice('azure-pipelines-task-lib', licenseInfo)); writeStream.end(); } catch (e) { log.error(e.message); diff --git a/node/internal.ts b/node/internal.ts index 532c81318..98def6adb 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -180,11 +180,11 @@ export function _setResourcePath(path: string): void { */ export function _loc(key: string, ...param: any[]): string { if (!_libResourceFileLoaded) { - // merge loc strings from vsts-task-lib. + // merge loc strings from azure-pipelines-task-lib. var libResourceFile = path.join(__dirname, 'lib.json'); var libLocStrs = _loadLocStrings(libResourceFile, _resourceCulture); for (var libKey in libLocStrs) { - //cache vsts-task-lib loc string + //cache azure-pipelines-task-lib loc string _locStringCache[libKey] = libLocStrs[libKey]; } diff --git a/node/mock-run.ts b/node/mock-run.ts index 8cb3245fb..24002fb21 100644 --- a/node/mock-run.ts +++ b/node/mock-run.ts @@ -16,7 +16,7 @@ export class TaskMockRunner { } /** - * Register answers for the mock "vsts-task-lib/task" instance. + * Register answers for the mock "azure-pipelines-task-lib/task" instance. * * @param answers Answers to be returned when the task lib functions are called. */ @@ -38,7 +38,7 @@ export class TaskMockRunner { } /** - * Registers an override for a specific function on the mock "vsts-task-lib/task" instance. + * Registers an override for a specific function on the mock "azure-pipelines-task-lib/task" instance. * This can be used in conjunction with setAnswers(), for cases where additional runtime * control is needed for a specific function. * @@ -53,7 +53,7 @@ export class TaskMockRunner { /** * Runs a task script. * - * @param noMockTask Indicates whether to mock "vsts-task-lib/task". Default is to mock. + * @param noMockTask Indicates whether to mock "azure-pipelines-task-lib/task". Default is to mock. * @returns void */ public run(noMockTask?: boolean): void { @@ -70,7 +70,7 @@ export class TaskMockRunner { } // register mock task lib else { - var tlm = require('vsts-task-lib/mock-task'); + var tlm = require('azure-pipelines-task-lib/mock-task'); if (this._answers) { tlm.setAnswers(this._answers); } @@ -80,7 +80,7 @@ export class TaskMockRunner { tlm[key] = this._exports[key]; }); - mockery.registerMock('vsts-task-lib/task', tlm); + mockery.registerMock('azure-pipelines-task-lib/task', tlm); } // run it diff --git a/node/package-lock.json b/node/package-lock.json index e45648de1..1ff9dfb73 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.0", + "version": "2.7.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 0a26786fc..bf58ec2ef 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.0", + "version": "2.7.1", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", @@ -10,7 +10,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/Microsoft/vsts-task-lib" + "url": "https://github.com/Microsoft/azure-pipelines-task-lib" }, "keywords": [ "vsts", diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index cdaf0778f..fa8697319 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -884,7 +884,7 @@ describe('Input Tests', function () { im._loadData(); assert.equal(tl.getInput('SomeInput'), 'some input value'); - // copy vsts-task-lib to a different dir and load it from + // copy azure-pipelines-task-lib to a different dir and load it from // there so it will be forced to load again let testDir = path.join(testutil.getTestTemp(), '_loadData-not-called-twice'); tl.mkdirP(testDir); diff --git a/node/test/testindex.d.ts b/node/test/testindex.d.ts index 03f5d49c2..9a175de33 100644 --- a/node/test/testindex.d.ts +++ b/node/test/testindex.d.ts @@ -1,4 +1,4 @@ // this is copied to build output so tests compiling use the just built task lib .d.ts /// -/// \ No newline at end of file +/// \ No newline at end of file diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 1f18a7500..602baa71e 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -45,7 +45,7 @@ describe('Toolrunner Tests', function () { }; if (os.platform() === 'win32') { - var ret = tl.execSync('cmd', '/c echo \'vsts-task-lib\'', _testExecOptions); + var ret = tl.execSync('cmd', '/c echo \'azure-pipelines-task-lib\'', _testExecOptions); assert.equal(ret.code, 0, 'return code of cmd should be 0'); } else { @@ -71,7 +71,7 @@ describe('Toolrunner Tests', function () { if (os.platform() === 'win32') { var cmd = tl.tool(tl.which('cmd', true)); - cmd.arg('/c echo \'vsts-task-lib\''); + cmd.arg('/c echo \'azure-pipelines-task-lib\''); var ret = cmd.execSync(_testExecOptions); assert.equal(ret.code, 0, 'return code of cmd should be 0'); @@ -133,7 +133,7 @@ describe('Toolrunner Tests', function () { }; if (os.platform() === 'win32') { - tl.exec('cmd', '/c echo \'vsts-task-lib\'', _testExecOptions) + tl.exec('cmd', '/c echo \'azure-pipelines-task-lib\'', _testExecOptions) .then(function (code) { assert.equal(code, 0, 'return code of cmd should be 0'); done(); @@ -172,7 +172,7 @@ describe('Toolrunner Tests', function () { if (os.platform() === 'win32') { var cmdPath = tl.which('cmd', true); var cmd = tl.tool(cmdPath); - cmd.arg('/c echo \'vsts-task-lib\''); + cmd.arg('/c echo \'azure-pipelines-task-lib\''); cmd.exec(_testExecOptions) .then(function (code) { @@ -219,7 +219,7 @@ describe('Toolrunner Tests', function () { if (os.platform() === 'win32') { var cmd = tl.tool(tl.which('cmd', true)) .arg('/c') - .arg('echo \'vsts-task-lib\''); + .arg('echo \'azure-pipelines-task-lib\''); cmd.on('stdout', (data) => { output = data.toString(); diff --git a/node/typings.json b/node/typings.json index c8ee026f6..2d16d4634 100644 --- a/node/typings.json +++ b/node/typings.json @@ -1,5 +1,5 @@ { - "name": "vsts-task-lib", + "name": "azure-pipelines-task-lib", "version": false, "dependencies": {}, "globalDependencies": { diff --git a/powershell/Docs/Commands.md b/powershell/Docs/Commands.md index cabe2d0d9..dee6bf7ee 100644 --- a/powershell/Docs/Commands.md +++ b/powershell/Docs/Commands.md @@ -587,7 +587,7 @@ DESCRIPTION dependencies, and so on until all nested dependencies have been traversed. Dependencies are searched for in the directory of the specified assembly. NET Framework assemblies are omitted. - See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage + See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. ``` ### Get-VstsClientCertificate @@ -625,7 +625,7 @@ DESCRIPTION Refer to Get-VstsTfsService for a more simple to get a TFS service object. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. ``` ### Get-VstsTfsService @@ -645,7 +645,7 @@ DESCRIPTION Gets an instance of an ITfsTeamProjectCollectionObject. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. ``` ### Get-VstsVssCredentials @@ -668,7 +668,7 @@ DESCRIPTION Refer to Get-VstsVssHttpClient for a more simple to get a VSS HTTP client. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. ``` ### Get-VstsVssHttpClient @@ -688,7 +688,7 @@ DESCRIPTION Gets an instance of an VSS HTTP client. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. ``` ### Get-VstsWebProxy diff --git a/powershell/Docs/FullHelp/Get-VstsAssemblyReference.md b/powershell/Docs/FullHelp/Get-VstsAssemblyReference.md index f9acfc108..ff7622e9a 100644 --- a/powershell/Docs/FullHelp/Get-VstsAssemblyReference.md +++ b/powershell/Docs/FullHelp/Get-VstsAssemblyReference.md @@ -22,7 +22,7 @@ DESCRIPTION dependencies, and so on until all nested dependencies have been traversed. Dependencies are searched for in the directory of the specified assembly. NET Framework assemblies are omitted. - See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage + See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. PARAMETERS diff --git a/powershell/Docs/FullHelp/Get-VstsTfsClientCredentials.md b/powershell/Docs/FullHelp/Get-VstsTfsClientCredentials.md index 373d76e55..cf6d9d044 100644 --- a/powershell/Docs/FullHelp/Get-VstsTfsClientCredentials.md +++ b/powershell/Docs/FullHelp/Get-VstsTfsClientCredentials.md @@ -18,7 +18,7 @@ DESCRIPTION Refer to Get-VstsTfsService for a more simple to get a TFS service object. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. PARAMETERS @@ -30,7 +30,7 @@ PARAMETERS If not specified, defaults to the directory of the entry script for the task. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. Required? false diff --git a/powershell/Docs/FullHelp/Get-VstsTfsService.md b/powershell/Docs/FullHelp/Get-VstsTfsService.md index eeba24efe..2adea683d 100644 --- a/powershell/Docs/FullHelp/Get-VstsTfsService.md +++ b/powershell/Docs/FullHelp/Get-VstsTfsService.md @@ -15,7 +15,7 @@ DESCRIPTION Gets an instance of an ITfsTeamProjectCollectionObject. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. PARAMETERS @@ -36,7 +36,7 @@ PARAMETERS If not specified, defaults to the directory of the entry script for the task. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. Required? false diff --git a/powershell/Docs/FullHelp/Get-VstsVssCredentials.md b/powershell/Docs/FullHelp/Get-VstsVssCredentials.md index d18876e49..72a19c6b9 100644 --- a/powershell/Docs/FullHelp/Get-VstsVssCredentials.md +++ b/powershell/Docs/FullHelp/Get-VstsVssCredentials.md @@ -18,7 +18,7 @@ DESCRIPTION Refer to Get-VstsVssHttpClient for a more simple to get a VSS HTTP client. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. PARAMETERS @@ -30,7 +30,7 @@ PARAMETERS If not specified, defaults to the directory of the entry script for the task. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. Required? false diff --git a/powershell/Docs/FullHelp/Get-VstsVssHttpClient.md b/powershell/Docs/FullHelp/Get-VstsVssHttpClient.md index 44ec2ddc0..681f7870c 100644 --- a/powershell/Docs/FullHelp/Get-VstsVssHttpClient.md +++ b/powershell/Docs/FullHelp/Get-VstsVssHttpClient.md @@ -15,7 +15,7 @@ DESCRIPTION Gets an instance of an VSS HTTP client. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. PARAMETERS @@ -36,7 +36,7 @@ PARAMETERS If not specified, defaults to the directory of the entry script for the task. *** DO NOT USE Agent.ServerOMDirectory *** See - https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage + https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. # .PARAMETER Uri diff --git a/powershell/Docs/FullHelp/Get-VstsWebProxy.md b/powershell/Docs/FullHelp/Get-VstsWebProxy.md index 80e4ca630..b6c33ccae 100644 --- a/powershell/Docs/FullHelp/Get-VstsWebProxy.md +++ b/powershell/Docs/FullHelp/Get-VstsWebProxy.md @@ -26,5 +26,5 @@ PARAMETERS PS C:\>$webProxy = Get-VstsWebProxy - $webProxy.GetProxy(New-Object System.Uri("https://github.com/Microsoft/vsts-task-lib")) + $webProxy.GetProxy(New-Object System.Uri("https://github.com/Microsoft/azure-pipelines-task-lib")) ``` diff --git a/powershell/VstsTaskSdk/ServerOMFunctions.ps1 b/powershell/VstsTaskSdk/ServerOMFunctions.ps1 index 9d43b2088..6aa2908b1 100644 --- a/powershell/VstsTaskSdk/ServerOMFunctions.ps1 +++ b/powershell/VstsTaskSdk/ServerOMFunctions.ps1 @@ -9,7 +9,7 @@ Only a subset of the referenced assemblies may actually be required, depending o Walks an assembly's references to determine all of it's dependencies. Also walks the references of the dependencies, and so on until all nested dependencies have been traversed. Dependencies are searched for in the directory of the specified assembly. NET Framework assemblies are omitted. -See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. +See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. .PARAMETER LiteralPath Assembly to walk. @@ -106,14 +106,14 @@ The agent job token is used to construct the credentials object. The identity as Refer to Get-VstsTfsService for a more simple to get a TFS service object. -*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. +*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. .PARAMETER OMDirectory Directory where the extended client object model DLLs are located. If the DLLs for the credential types are not already loaded, an attempt will be made to automatically load the required DLLs from the object model directory. If not specified, defaults to the directory of the entry script for the task. -*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. +*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. .EXAMPLE # @@ -162,7 +162,7 @@ Gets a TFS extended client service. .DESCRIPTION Gets an instance of an ITfsTeamProjectCollectionObject. -*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. +*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. .PARAMETER TypeName Namespace-qualified type name of the service to get. @@ -172,7 +172,7 @@ Directory where the extended client object model DLLs are located. If the DLLs f If not specified, defaults to the directory of the entry script for the task. -*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. +*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the TFS extended client SDK from a task. .PARAMETER Uri URI to use when initializing the service. If not specified, defaults to System.TeamFoundationCollectionUri. @@ -239,14 +239,14 @@ The agent job token is used to construct the credentials object. The identity as Refer to Get-VstsVssHttpClient for a more simple to get a VSS HTTP client. -*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. +*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. .PARAMETER OMDirectory Directory where the REST client object model DLLs are located. If the DLLs for the credential types are not already loaded, an attempt will be made to automatically load the required DLLs from the object model directory. If not specified, defaults to the directory of the entry script for the task. -*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. +*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. .EXAMPLE # @@ -306,7 +306,7 @@ Gets a VSS HTTP client. .DESCRIPTION Gets an instance of an VSS HTTP client. -*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. +*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. .PARAMETER TypeName Namespace-qualified type name of the HTTP client to get. @@ -316,7 +316,7 @@ Directory where the REST client object model DLLs are located. If the DLLs for t If not specified, defaults to the directory of the entry script for the task. -*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/vsts-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. +*** DO NOT USE Agent.ServerOMDirectory *** See https://github.com/Microsoft/azure-pipelines-task-lib/tree/master/powershell/Docs/UsingOM.md for reliable usage when working with the VSTS REST SDK from a task. # .PARAMETER Uri # URI to use when initializing the HTTP client. If not specified, defaults to System.TeamFoundationCollectionUri. @@ -475,7 +475,7 @@ VstsTaskSdk.VstsWebProxy implement System.Net.IWebProxy interface. .EXAMPLE $webProxy = Get-VstsWebProxy -$webProxy.GetProxy(New-Object System.Uri("https://github.com/Microsoft/vsts-task-lib")) +$webProxy.GetProxy(New-Object System.Uri("https://github.com/Microsoft/azure-pipelines-task-lib")) #> function Get-WebProxy { [CmdletBinding()] diff --git a/powershell/VstsTaskSdk/VstsTaskSdk.psd1 b/powershell/VstsTaskSdk/VstsTaskSdk.psd1 index 7229eb9a2d05a96f415797b02a50390cd5205698..3c606fdf7cb9e6f1a738067d5d50f12ff231dcba 100644 GIT binary patch delta 76 zcmeC<-Nn0M2a9wfLlr|QLlHwNgDyh>LnaWXGUNd1JciWGds$K$@hO Date: Wed, 17 Oct 2018 11:52:34 -0700 Subject: [PATCH 089/259] Change reference to VSTS to Azure DevOps (#406) I shared these instructions with someone who is *very* new to ~VSTS~ Azure DevOps and was confused by the title of this doc. The naming/branding change will undoubtedly take a while to get through, but hopefully this little bit helps. --- powershell/Docs/UsingOM.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/Docs/UsingOM.md b/powershell/Docs/UsingOM.md index 321b8e149..23d4ff5cf 100644 --- a/powershell/Docs/UsingOM.md +++ b/powershell/Docs/UsingOM.md @@ -1,4 +1,4 @@ -# Using the VSTS .NET SDKs +# Using the Azure DevOps .NET SDKs ## Bundle the SDK with your task Bundle the subset of the SDK required by your task. From 9cb1dc3ee70537fd5c67e1a4a247b1a19740a2b4 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Wed, 17 Oct 2018 14:59:36 -0400 Subject: [PATCH 090/259] Fixed broken links for reference examples (#393) ShellScript Task link updated to V2 and XCode Task link updated to V5 --- node/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/README.md b/node/README.md index 6e3bda913..68d344419 100644 --- a/node/README.md +++ b/node/README.md @@ -21,7 +21,7 @@ Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/mi ## Reference Examples -The [ShellScript Task](https://github.com/Microsoft/vsts-tasks/tree/master/Tasks/ShellScript) and the [XCode Task](https://github.com/Microsoft/vsts-tasks/tree/master/Tasks/Xcode) are good examples. +The [ShellScript Task](https://github.com/Microsoft/vsts-tasks/tree/master/Tasks/ShellScriptV2) and the [XCode Task](https://github.com/Microsoft/vsts-tasks/tree/master/Tasks/XcodeV5) are good examples. ## Contributing @@ -47,4 +47,4 @@ Set environment variable TASK_TEST_TRACE=1 to display test output. To generate/update third party notice file run: ```bash $ node generate-third-party-notice.js -``` \ No newline at end of file +``` From d716f5755ce5dc74d5bc96037199cc2a56d63a61 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sat, 10 Nov 2018 12:25:14 -0500 Subject: [PATCH 091/259] branding misses --- node/README.md | 10 ++++------ node/package.json | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/node/README.md b/node/README.md index 68d344419..f8ec9e1fa 100644 --- a/node/README.md +++ b/node/README.md @@ -1,11 +1,9 @@ -# VSTS DevOps Task SDK +# Azure Pi[elines Task SDK -Libraries for writing [Visual Studio Team Services](https://www.visualstudio.com/en-us/products/visual-studio-team-services-vs.aspx) build and deployment tasks +Libraries for writing [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines) tasks -![VSTS](https://mseng.visualstudio.com/DefaultCollection/_apis/public/build/definitions/b924d696-3eae-4116-8443-9a18392d8544/2553/badge) - -Reference examples of our in the box tasks [are here](https://github.com/Microsoft/vsts-tasks) +Reference examples of our in the box tasks [are here](https://github.com/Microsoft/azure-pipelines-tasks) ## TypeScript Tasks @@ -21,7 +19,7 @@ Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/mi ## Reference Examples -The [ShellScript Task](https://github.com/Microsoft/vsts-tasks/tree/master/Tasks/ShellScriptV2) and the [XCode Task](https://github.com/Microsoft/vsts-tasks/tree/master/Tasks/XcodeV5) are good examples. +The [ShellScript Task](https://github.com/Microsoft/azure-pipelines-tasks/tree/master/Tasks/ShellScriptV2) and the [XCode Task](https://github.com/Microsoft/azure-pipelines-tasks/tree/master/Tasks/XcodeV5) are good examples. ## Contributing diff --git a/node/package.json b/node/package.json index bf58ec2ef..e02f25c7e 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.1", + "version": "2.7.3", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", From d98199620f9b5232579455bc7c670b1326edb4a3 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sat, 10 Nov 2018 12:26:47 -0500 Subject: [PATCH 092/259] typo --- node/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/README.md b/node/README.md index f8ec9e1fa..426792ad7 100644 --- a/node/README.md +++ b/node/README.md @@ -1,5 +1,5 @@ -# Azure Pi[elines Task SDK +# Azure Pipelines Task SDK Libraries for writing [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines) tasks From 350e635901a8a657523458213a0426a8d136bcdc Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sat, 10 Nov 2018 12:28:06 -0500 Subject: [PATCH 093/259] bump --- node/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/package.json b/node/package.json index e02f25c7e..f11343860 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.3", + "version": "2.7.4", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 9fdd82cd724c61aa1615e732a20d3a5ffce67397 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Fri, 16 Nov 2018 23:36:36 -0500 Subject: [PATCH 094/259] unc arg issue (#474) * initial thought * CR feedback --- node/test/toolrunnertests.ts | 9 +++++++++ node/toolrunner.ts | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 602baa71e..f32b5bb8e 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -1174,6 +1174,15 @@ describe('Toolrunner Tests', function () { assert.equal((node as any).args.toString(), 'one,two\\arg,three', 'should be one,two,three'); done(); }) + it('handles multiple escaped backslashes', function (done) { + this.timeout(10000); + + var node = tl.tool(tl.which('node', true)); + node.line('one "\\\\two\\arg"'); + assert.equal((node as any).args.length, 2, 'should have 2 args'); + assert.equal((node as any).args.toString(), 'one,\\\\two\\arg', 'should be one,\\\\two\\arg'); + done(); + }) it('handles equals and switches', function (done) { this.timeout(10000); diff --git a/node/toolrunner.ts b/node/toolrunner.ts index a752a5503..2dd964992 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -109,6 +109,11 @@ export class ToolRunner extends events.EventEmitter { continue; } + if (c === "\\" && escaped) { + append(c); + continue; + } + if (c === "\\" && inQuotes) { escaped = true; continue; From 7015476286cf260178a089f1a1950c994f2b3fe2 Mon Sep 17 00:00:00 2001 From: Mario Majcica Date: Tue, 27 Nov 2018 18:50:49 +0100 Subject: [PATCH 095/259] Added missing logging commands to Node lib (#477) * Added missing logging commands to Node lib * Refactored based on review. * Unused import added by some of the dev tools * Fixed uploadArtifact optional name * Made message param mandatory on logIssue * Fixed optional params --- node/task.ts | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 254 insertions(+), 2 deletions(-) diff --git a/node/task.ts b/node/task.ts index 7a11c1502..4d52efca6 100644 --- a/node/task.ts +++ b/node/task.ts @@ -14,7 +14,35 @@ import semver = require('semver'); export enum TaskResult { Succeeded = 0, SucceededWithIssues = 1, - Failed = 2 + Failed = 2, + Cancelled = 3, + Skipped = 4 +} + +export enum TaskState { + Unknown = 0, + Initialized = 1, + InProgress = 2, + Completed = 3 +} + +export enum IssueType { + Error = "error", + Warning = "warning" +} + +export enum ArtifactType { + Container = "container", + FilePath = "filepath", + VersionControl = "versioncontrol", + GitRef = "gitref", + TfvcLabel = "tfvclabel" +} + +export enum FieldType { + AuthParameter = "authParameter", + DataParameter = "dataParameter", + Url = "url" } //----------------------------------------------------- @@ -33,7 +61,7 @@ export const setErrStream = im._setErrStream; * If not set, task will be Succeeded. * If multiple calls are made to setResult the most pessimistic call wins (Failed) regardless of the order of calls. * - * @param result TaskResult enum of Succeeded, SucceededWithIssues or Failed. + * @param result TaskResult enum of Succeeded, SucceededWithIssues, Failed, Cancelled or Skipped. * @param message A message which will be logged as an error issue if the result is Failed. * @param done Optional. Instructs the agent the task is done. This is helpful when child processes * may still be running and prevent node from fully exiting. This argument is supported @@ -1775,6 +1803,230 @@ export class CodeCoverageEnabler { } } +//----------------------------------------------------- +// Task Logging Commands +//----------------------------------------------------- + +/** + * Upload user interested file as additional log information + * to the current timeline record. + * + * The file shall be available for download along with task logs. + * + * @param path Path to the file that should be uploaded. + * @returns void + */ +export function uploadFile(path: string) { + command("task.uploadfile", null, path); +} + +/** + * Instruction for the agent to update the PATH environment variable. + * The specified directory is prepended to the PATH. + * The updated environment variable will be reflected in subsequent tasks. + * + * @param path Local directory path. + * @returns void + */ +export function prependPath(path: string) { + assertAgent("2.115.0"); + command("task.prependpath", null, path); +} + +/** + * Upload and attach summary markdown to current timeline record. + * This summary shall be added to the build/release summary and + * not available for download with logs. + * + * @param path Local directory path. + * @returns void + */ +export function uploadSummary(path: string) { + command("task.uploadsummary", null, path); +} + +/** + * Upload and attach attachment to current timeline record. + * These files are not available for download with logs. + * These can only be referred to by extensions using the type or name values. + * + * @param type Attachment type. + * @param name Attachment name. + * @param path Attachment path. + * @returns void + */ +export function addAttachment(type: string, name: string, path: string) { + command("task.addattachment", { "type": type, "name": name }, path); +} + +/** + * Set an endpoint field with given value. + * Value updated will be retained in the endpoint for + * the subsequent tasks that execute within the same job. + * + * @param id Endpoint id. + * @param field FieldType enum of AuthParameter, DataParameter or Url. + * @param key Key. + * @param value Value for key or url. + * @returns void + */ +export function setEndpoint(id: string, field: FieldType, key: string, value: string) { + command("task.setendpoint", { "id": id, "field": field, "key": key }, value); +} + +/** + * Set progress and current operation for current task. + * + * @param percent Percentage of completion. + * @param currentOperation Current pperation. + * @returns void + */ +export function setProgress(percent: number, currentOperation: string) { + command("task.setprogress", { "value": `${percent}` }, currentOperation); +} + +/** + * Indicates whether to write the logging command directly to the host or to the output pipeline. + * + * @param id Timeline record Guid. + * @param parentId Parent timeline record Guid. + * @param recordType Record type. + * @param recordName Record name. + * @param order Order of timeline record. + * @param startTime Start time. + * @param finishTime End time. + * @param progress Percentage of completion. + * @param state TaskState enum of Unknown, Initialized, InProgress or Completed. + * @param result TaskResult enum of Succeeded, SucceededWithIssues, Failed, Cancelled or Skipped. + * @param message current operation + * @returns void + */ +export function logDetail(id: string, message: string, parentId?: string, recordType?: string, + recordName?: string, order?: number, startTime?: string, finishTime?: string, + progress?: number, state?: TaskState, result?: TaskResult) { + const properties = { + "id": id, + "parentid": parentId, + "type": recordType, + "name": recordName, + "order": order ? order.toString() : undefined, + "starttime": startTime, + "finishtime": finishTime, + "progress": progress ? progress.toString() : undefined, + "state": state ? TaskState[state] : undefined, + "result": result ? TaskResult[result] : undefined + }; + + command("task.logdetail", properties, message); +} + +/** + * Log error or warning issue to timeline record of current task. + * + * @param type IssueType enum of Error or Warning. + * @param sourcePath Source file location. + * @param lineNumber Line number. + * @param columnNumber Column number. + * @param code Error or warning code. + * @param message Error or warning message. + * @returns void + */ +export function logIssue(type: IssueType, message: string, sourcePath?: string, lineNumber?: number, + columnNumber?: number, errorCode?: string, ) { + const properties = { + "type": type, + "code": errorCode, + "sourcepath": sourcePath, + "linenumber": lineNumber ? lineNumber.toString() : undefined, + "columnnumber": columnNumber ? columnNumber.toString() : undefined, + }; + + command("task.logissue", properties, message); +} + +//----------------------------------------------------- +// Artifact Logging Commands +//----------------------------------------------------- + +/** + * Upload user interested file as additional log information + * to the current timeline record. + * + * The file shall be available for download along with task logs. + * + * @param containerFolder Folder that the file will upload to, folder will be created if needed. + * @param path Path to the file that should be uploaded. + * @param name Artifact name. + * @returns void + */ +export function uploadArtifact(containerFolder: string, path: string, name?: string) { + command("artifact.upload", { "containerfolder": containerFolder, "artifactname": name }, path); +} + +/** + * Create an artifact link, artifact location is required to be + * a file container path, VC path or UNC share path. + * + * The file shall be available for download along with task logs. + * + * @param name Artifact name. + * @param path Path to the file that should be associated. + * @param artifactType ArtifactType enum of Container, FilePath, VersionControl, GitRef or TfvcLabel. + * @returns void + */ +export function associateArtifact(name: string, path: string, artifactType: ArtifactType) { + command("artifact.associate", { "type": artifactType, "artifactname": name }, path); +} + +//----------------------------------------------------- +// Build Logging Commands +//----------------------------------------------------- + +/** + * Upload user interested log to build’s container “logs\tool” folder. + * + * @param path Path to the file that should be uploaded. + * @returns void + */ +export function uploadBuildLog(path: string) { + command("build.uploadlog", null, path); +} + +/** + * Update build number for current build. + * + * @param value Value to be assigned as the build number. + * @returns void + */ +export function updateBuildNumber(value: string) { + command("build.updatebuildnumber", null, value); +} + +/** + * Add a tag for current build. + * + * @param value Tag value. + * @returns void + */ +export function addBuildTag(value: string) { + command("build.addbuildtag", null, value); +} + +//----------------------------------------------------- +// Release Logging Commands +//----------------------------------------------------- + +/** + * Update release name for current release. + * + * @param value Value to be assigned as the release name. + * @returns void + */ +export function updateReleaseName(name: string) { + assertAgent("2.132"); + command("release.updatereleasename", null, name); +} + //----------------------------------------------------- // Tools //----------------------------------------------------- From e0a9634ca6aa7c0095dc5fbf3608ea2539d430fa Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 27 Nov 2018 13:15:32 -0500 Subject: [PATCH 096/259] Fix issues with enum (#478) * Fix errors with enum * Force to lowercase --- node/task.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/node/task.ts b/node/task.ts index 4d52efca6..19fd63bef 100644 --- a/node/task.ts +++ b/node/task.ts @@ -27,22 +27,22 @@ export enum TaskState { } export enum IssueType { - Error = "error", - Warning = "warning" + Error, + Warning } export enum ArtifactType { - Container = "container", - FilePath = "filepath", - VersionControl = "versioncontrol", - GitRef = "gitref", - TfvcLabel = "tfvclabel" + Container, + FilePath, + VersionControl, + GitRef, + TfvcLabel } export enum FieldType { - AuthParameter = "authParameter", - DataParameter = "dataParameter", - Url = "url" + AuthParameter, + DataParameter, + Url } //----------------------------------------------------- @@ -1871,7 +1871,7 @@ export function addAttachment(type: string, name: string, path: string) { * @returns void */ export function setEndpoint(id: string, field: FieldType, key: string, value: string) { - command("task.setendpoint", { "id": id, "field": field, "key": key }, value); + command("task.setendpoint", { "id": id, "field": FieldType[field].toLowerCase(), "key": key }, value); } /** @@ -1932,9 +1932,9 @@ export function logDetail(id: string, message: string, parentId?: string, record * @returns void */ export function logIssue(type: IssueType, message: string, sourcePath?: string, lineNumber?: number, - columnNumber?: number, errorCode?: string, ) { + columnNumber?: number, errorCode?: string) { const properties = { - "type": type, + "type": IssueType[type].toLowerCase(), "code": errorCode, "sourcepath": sourcePath, "linenumber": lineNumber ? lineNumber.toString() : undefined, @@ -1975,7 +1975,7 @@ export function uploadArtifact(containerFolder: string, path: string, name?: str * @returns void */ export function associateArtifact(name: string, path: string, artifactType: ArtifactType) { - command("artifact.associate", { "type": artifactType, "artifactname": name }, path); + command("artifact.associate", { "type": ArtifactType[artifactType].toLowerCase(), "artifactname": name }, path); } //----------------------------------------------------- From 683b3b412195afd37c1d53a2d3fbf4262f6b356f Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 27 Nov 2018 13:16:14 -0500 Subject: [PATCH 097/259] Update documentation to point to correct source (#470) --- node/README.md | 2 +- node/docs/stepbystep.md | 471 +--------------------------------------- 2 files changed, 2 insertions(+), 471 deletions(-) diff --git a/node/README.md b/node/README.md index 426792ad7..19906934b 100644 --- a/node/README.md +++ b/node/README.md @@ -11,7 +11,7 @@ Cross platform tasks are written in TypeScript. It is the preferred way to writ [![NPM version][npm-lib-image]][npm-lib-url] -Step by Step: [Create Task](docs/stepbystep.md) +Step by Step: [Create Task](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=vsts) Documentation: [TypeScript API](docs/azure-pipelines-task-lib.md), [task JSON schema](https://aka.ms/vsts-tasks.schema.json) diff --git a/node/docs/stepbystep.md b/node/docs/stepbystep.md index 06620332f..9bc217ff8 100644 --- a/node/docs/stepbystep.md +++ b/node/docs/stepbystep.md @@ -1,472 +1,3 @@ # Step by Step: Node Task with Typescript API -This step by step will show how to manually create, debug and test a cross platform task with no service, server or agent! - -Tasks can be created using tfx as a convenience, but it's good to understand the parts of a task. -This tutorial walks through each manual step of creating a task. - -## Video - -https://youtu.be/O_34c7p4GlM?t=1173 - -## Tools - -[Typescript Compiler 2.2.0 or greater](https://www.npmjs.com/package/typescript) - -[Node 4.4.7 (LTS) or greater](https://nodejs.org/en/) - -[Npm 3.0 or greater recommended](https://www.npmjs.com/package/npm3) (comes with node >=5. creates flat dependencies in tasks) - -This tutorial uses [VS Code](https://code.visualstudio.com) for great intellisense and debugging support - -## Sample Files - -Files used for this walk through are [located here in this gist](https://gist.github.com/bryanmacfarlane/154f14dd8cb11a71ef04b0c836e5be6e) - -## Unix vs Windows - -This tutorial was done on a Mac. We attempted to make it generic for all platforms, but the syntax for setting environment variables is different. - -If using Windows, replace any instances of ```export =``` with ```$env:=``` - -If there are additional discrepancies or unexpected behavior, please create issues in the repo. - -## Create Task Scaffolding - -Create a directory and package.json. Accept defaults of npm init for sample. -```bash -$ mkdir sampletask && cd sampletask -$ npm init -``` - -### Add azure-pipelines-task-lib - -Add azure-pipelines-task-lib to your task. Remember your task must carry the lib. Ensure it's at least 0.9.5 which now carries typings. - -The package.json should have dependency with ^. Ex: ^0.9.5. This means you are locked to 0.9.x and will pick up patches on npm install. - -The npm module carries the .d.ts typecript definition files so compile and intellisense support will just work. - -``` -$ npm install azure-pipelines-task-lib --save -... -└─┬ azure-pipelines-task-lib@2.0.5 -... -``` - -### Add Typings for Externals - -Ensure typings are installed for external dependencies - -```bash -$ npm install @types/node --save-dev -$ npm install @types/q --save-dev -``` - -Create a .gitignore and add node_modules to it. Your build process should do `npm install` and `typings install`. No need to checkin dependencies. - -```bash -$ cat .gitignore -node_modules -``` - -### Create tsconfig.json Compiler Options - -```bash -$ tsc --init -``` - -Change `tsconfig.json` file to ES6 to match the sample gist. ES6 is for async await support. - -## Task Implementation - -Now that the scaffolding is out of the way, let's create the task! - -Create a `task.json` file using `sample_task.json` as a starting point. The full task JSON schema is [here](https://aka.ms/vsts-tasks.schema.json). - -Replace the `{{placeholders}}`. The most important being a [unique guid](http://www.guidgen.com/). -Note: copy from web view since file needs property names in quotes (browser might strip in raw view) - -Create a `index.ts` file using index.ts from the gist as a starting point. -Create a `taskmod.ts` file using taskmod.ts from the gist as a starting point. - -Instellisense should just work in [VS Code](https://code.visualstudio.com) - -The code is straight forward. As a reference: - -```javascript -import tl = require('azure-pipelines-task-lib/task'); -import trm = require('azure-pipelines-task-lib/toolrunner'); -import mod = require('./taskmod'); - -async function run() { - try { - console.log(process.env["INPUT_SAMPLESTRING"]); - let tool: trm.ToolRunner; - if (process.platform == 'win32') { - let cmdPath = tl.which('cmd'); - tool = tl.tool(cmdPath).arg('/c').arg('echo ' + tl.getInput('samplestring', true)); - } - else { - let echoPath = tl.which('echo'); - tool = tl.tool(echoPath).arg(tl.getInput('samplestring', true)); - } - - let rc1: number = await tool.exec(); - - // call some module which does external work - if (rc1 == 0) { - mod.sayHello(); - } - - console.log('Task done! ' + rc1); - } - catch (err) { - tl.setResult(tl.TaskResult.Failed, err.message); - } -} - -run(); -``` - -taskmod.ts: - -```javascript -export function sayHello() { - console.log('Hello World!'); -} -``` - -Key Points: - - - Async code is linear. Note the two executions written one after each other in linear fashion. - - Must be wrapped in async function. - - Greatly simplifies error handling. - - Never process.exit your task. You can sometimes lose output and often the last bit of output is critical - -If we did our job well, the code should be pretty self explanatory. -But, see the [API Reference](azure-pipelines-task-lib.md) for specifics. - -## Compile - -Just type `tsc` from the root of the task. That should have compiled an index.js - -## Run the Task - -The task can be run by simply running `node index.js`. Note that is exactly what our agent will do. - -```bash -$ node index.js -##vso[task.debug]agent.workFolder=undefined -##vso[task.debug]loading inputs and endpoints -##vso[task.debug]loaded 0 -##vso[task.debug]task result: Failed -##vso[task.issue type=error;]Input required: samplestring -##vso[task.complete result=Failed;]Input required: samplestring -``` - -The task failed! That's exactly what would happen if the task run and inputs were not supplied. - -The agent runs the task and reads key information from command output output over stdout and stderr. This allows for consistency in node, powershell or even ad-hoc scripts. The lib is mostly a thin wrapper generating those commands. - -Let's supply one of the inputs and try again. - -```bash -$ export INPUT_SAMPLESTRING="Hello World" -$ node index.js -##vso[task.debug]agent.workFolder=undefined -##vso[task.debug]loading inputs and endpoints -##vso[task.debug]loading INPUT_SAMPLESTRING -##vso[task.debug]loaded 1 -##vso[task.debug]samplestring=Hello World -##vso[task.debug]echo arg: Hello World -##vso[task.debug]exec tool: echo -##vso[task.debug]Arguments: -##vso[task.debug] Hello World -[command]echo Hello World -Hello World -##vso[task.debug]rc:0 -##vso[task.debug]success:true -##vso[task.debug]samplebool=null -Task done! 0,-1 -$ -``` - -> TIP: be careful with chars like ! in env vars. [Example here](http://superuser.com/questions/133780/in-bash-how-do-i-escape-an-exclamation-mark) - -Now let's set the sample bool. This should fail since if sample bool is true, it should need the other input. See the code. - -```bash -$ export INPUT_SAMPLEBOOL=true -$ node index.js -##vso[task.debug]agent.workFolder=undefined -##vso[task.debug]loading inputs and endpoints -##vso[task.debug]loading INPUT_SAMPLEBOOL -##vso[task.debug]loading INPUT_SAMPLESTRING -##vso[task.debug]loaded 2 -##vso[task.debug]samplestring=Hello World -##vso[task.debug]echo arg: Hello World -##vso[task.debug]exec tool: echo -##vso[task.debug]Arguments: -##vso[task.debug] Hello World -[command]echo Hello World -Hello World -##vso[task.debug]rc:0 -##vso[task.debug]success:true -##vso[task.debug]samplebool=true -##vso[task.debug]task result: Failed -##vso[task.issue type=error;]Input required: samplepathinput -##vso[task.complete result=Failed;]Input required: samplepathinput -$ -``` - -So, as you can see, this offers powerful testing automation options to test all arcs with no agent or server. - -## Interactive Use from Command Line (Advanced) - -Node offers an interactive console and since the task lib is in your node_modules folder, you can interactively poke around. - -```bash -$ node -> var tl = require('azure-pipelines-task-lib/task'); -##vso[task.debug]agent.workFolder=undefined -##vso[task.debug]loading inputs and endpoints -##vso[task.debug]loaded 0 -undefined -> tl.which('echo'); -##vso[task.debug]echo=/bin/echo -'/bin/echo' -> tl.tool('echo').arg('Hello World!').args -##vso[task.debug]echo arg: Hello World! -[ 'Hello World!' ] -> .exit - -``` -## Debugging - -Coming soon - -## Unit testing your task scripts - -This requires azure-pipelines-task-lib 0.9.15 or greater. - - -### Goals: - -- Unit test the script, not the external tools it's calling. -- Run subsecond (often < 200 ms) -- Validate all arcs of the script, including failure paths. -- Run your task script unaltered the exact way the agent does (envvar as contract, run node against your script) -- Assert all aspects of the execution (succeeded, issues, std/errout etc...) by intercepting command output - -### Install test tools - -We will use mocha as the test driver in this examples. Others exist. -```bash -npm install mocha --save-dev -g -npm install @types/mocha --save-dev -``` - -### Create test suite - -Creates tests folder and _suite.ts. [Example here](https://gist.github.com/bryanmacfarlane/154f14dd8cb11a71ef04b0c836e5be6e#file-_suite-ts) - -### Success test - -The success test will validate that the task will succeed if the tool returns 0. It also confirms that no error or warning issues are added to the build summary. Finally it validates that the task module is called if the tool succeeds. - -The success test [from _suite.ts](https://gist.github.com/bryanmacfarlane/154f14dd8cb11a71ef04b0c836e5be6e#file-_suite-ts) looks like: - -```javascript - it('should succeed with simple inputs', (done: MochaDone) => { - this.timeout(1000); - - let tp = path.join(__dirname, 'success.js'); - let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); - - tr.run(); - assert(tr.succeeded, 'should have succeeded'); - assert.equal(tr.invokedToolCount, 1); - assert.equal(tr.warningIssues.length, 0, "should have no warnings"); - assert.equal(tr.errorIssues.length, 0, "should have no errors"); - assert(tr.stdout.indexOf('atool output here') >= 0, "tool stdout"); - assert(tr.stdout.indexOf('Hello Mock!') >= 0, "task module is called"); - - done(); - }); -``` - -key code from [success.ts test file](https://gist.github.com/bryanmacfarlane/154f14dd8cb11a71ef04b0c836e5be6e#file-success-ts) - -```javascript - -let taskPath = path.join(__dirname, '..', 'index.js'); -let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath); - -tmr.setInput('samplestring', "Hello, from task!"); -tmr.setInput('samplebool', 'true'); - -// provide answers for task mock -let a: ma.TaskLibAnswers = { - "which": { - "echo": "/mocked/tools/echo", - "cmd": "/mocked/tools/cmd" - }, - "exec": { - "/mocked/tools/echo Hello, from task!": { - "code": 0, - "stdout": "atool output here", - "stderr": "atool with this stderr output" - }, - "/mocked/tools/cmd /c echo Hello, from task!": { - "code": 0, - "stdout": "atool output here", - "stderr": "atool with this stderr output" - } - } -}; -tmr.setAnswers(a); - -// mock a specific module function called in task -tmr.registerMock('./taskmod', { - sayHello: function() { - console.log('Hello Mock!'); - } -}); - -tmr.run(); - -``` - -### Fail test - -This test validates that the task will fail if the tool returns 1. It also validates that an error issue will be added to the build summary. Finally, it validated that the task module is not called if the tool fails. - -The fail test [from _suite.ts](https://gist.github.com/bryanmacfarlane/154f14dd8cb11a71ef04b0c836e5be6e#file-_suite-ts) looks like: - -```javascript - it('it should fail if tool returns 1', (done: MochaDone) => { - this.timeout(1000); - - let tp = path.join(__dirname, 'failrc.js'); - let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); - - tr.run(); - assert(!tr.succeeded, 'should have failed'); - assert.equal(tr.invokedToolCount, 1); - assert.equal(tr.warningIssues, 0, "should have no warnings"); - assert.equal(tr.errorIssues.length, 1, "should have 1 error issue"); - if (process.platform == 'win32') { - assert.equal(tr.errorIssues[0], '/mocked/tools/cmd failed with return code: 1', 'error issue output'); - } - else { - assert.equal(tr.errorIssues[0], '/mocked/tools/echo failed with return code: 1', 'error issue output'); - } - assert(tr.stdout.indexOf('atool output here') >= 0, "tool stdout"); - assert.equal(tr.stdout.indexOf('Hello Mock!'), -1, "task module should have never been called"); - - done(); - }); -``` - -The [failrc.ts test file is here](https://gist.github.com/bryanmacfarlane/154f14dd8cb11a71ef04b0c836e5be6e#file-failrc-ts) - -### Fail on required inputs - -We also typically write tests to confirm the task fails with proper actionable guidance if required inputs are not supplied. Give it a try. - -### Running the tests - -```bash -mocha tests/_suite.js -``` - -```bash -$ mocha tests/_suite.js - - - Sample task tests - ✓ should succeed with simple inputs (127ms) - ✓ it should fail if tool returns 1 (121ms) - - - 2 passing (257ms) -``` - -If you want to run tests with verbose task output (what you would see in the build console) set envvar TASK_TEST_TRACE=1 - -```bash -export TASK_TEST_TRACE=1 -``` - -## Add Task to an Extension - -### Create your publisher - -All extensions are identified as being provided by a publisher. If you aren't already a member of an existing publisher, you'll create one. Sign in to the [Visual Studio Marketplace Publishing Portal](http://aka.ms/vsmarketplace-manage). If you're not prompted to create a publisher, scroll down to the bottom of the page and select *Publish Extensions* underneath **Related Sites**. - -### Create an extension manifest file - -You will need to create the extension manifest file in the directory above `sampletask`. - -Create a file `vss-extension.json`: -```json -{ - "manifestVersion": 1, - "id": "sample-task", - "name": "Sample Build Tools", - "version": "0.0.1", - "publisher": "samplepublisher", - "targets": [ - { - "id": "Microsoft.VisualStudio.Services" - } - ], - "description": "Sample tools for building. Includes one build task.", - "categories": [ - "Build and release" - ], - "//uncomment 'icons' below to include a custom icon": "", - "//icons": { - "default": "images/extension-icon.png" - }, - "files": [ - { - "//Relative path of the task directory": "", - "path": "sampletask" - } - ], - "contributions": [ - { - "//Identifier of the contribution. Must be unique within the extension. Does not need to match the name of the build task, but typically the build task name is included in the ID of the contribution.": "", - "id": "sample-build-task", - "type": "ms.vss-distributed-task.task", - "targets": [ - "ms.vss-distributed-task.tasks" - ], - "properties": { - "//Name of the task. This must match the folder name of the corresponding self-contained build task definition.": "", - "name": "sampletask" - } - } - ] -} -``` - -### Publish, Install, Publish - -Publish the extension to the Marketplace and grant your account the ability to see it. Sharing the extension with your account allows you to install and test it. - -You will need a personal access token scoped to `All accessible` accounts. - -``` -tfx extension publish --manifest-globs your-manifest.json --share-with youraccountname -``` - -Install the extension into your account. - -``` -tfx extension install --vsix .\samplepublisher.sample-task-0.0.1.vsix --accounts youraccountname -``` - -Future publishes will automatically update the extension installed into your account. Add `--rev-version` when publishing updates to the extension, and also rev the task version (in the `task.json`). +This documentation has been moved and can now be found [here](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=vsts)! \ No newline at end of file From 4abe4569c28e9b789ce3cfe594d7b256e1e7c4eb Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 27 Nov 2018 13:18:15 -0500 Subject: [PATCH 098/259] Bump --- node/package-lock.json | 2 +- node/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 1ff9dfb73..a9ccb83b9 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.1", + "version": "2.7.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index f11343860..d49bfe4e4 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.4", + "version": "2.7.5", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 033aba56ac965f9d5d5222cd374e5f90ec8f96cd Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 27 Nov 2018 13:48:46 -0500 Subject: [PATCH 099/259] bump --- node/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/package.json b/node/package.json index d49bfe4e4..5f8847ef4 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.5", + "version": "2.7.7", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 2f0dde7e1786bcca01f0e8a0d6bdaa760b89ea8f Mon Sep 17 00:00:00 2001 From: Jesse Houwing Date: Mon, 3 Dec 2018 20:44:44 +0100 Subject: [PATCH 100/259] Updating schema (#479) * Updating schema * Shortening description for `deprecated` --- tasks.schema.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tasks.schema.json b/tasks.schema.json index 4b798d7ea..ede1b83f2 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -32,6 +32,10 @@ "preview": { "type": "boolean" }, + "deprecated": { + "type": "boolean", + "description": "Task is deprecated only when the latest version is marked as deprecated. Deprecated tasks appear at the end of searches under a section that is collapsed by default." + }, "showEnvironmentVariables": { "type": "boolean", "description": "Toggles showing the environment variable editor in the task editor UI. Allows passing environment variables to script based tasks." @@ -49,13 +53,19 @@ }, "category": { "type": "string", - "description": "Where the task appears in VSTS", + "description": "Where the task appears in VSTS. Use the 'Azure *' categories for Azure DevOps and Azure DevOps Server 2019. Use the other categories for Team Foundation Server 2018 and below.", "enum": [ "Build", "Utility", "Test", "Package", - "Deploy" + "Deploy", + + "Azure Repos", + "Azure Boards", + "Azure Pipelines", + "Azure Test Plans", + "Azure Artifacts" ] }, "groups": { From 7e4c00179ce0f8e305143d491082928d77f956ea Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Wed, 12 Dec 2018 09:27:07 -0500 Subject: [PATCH 101/259] Mock stdline and errline (#462) * Mock stdline and errline * Responsd to feedback * Fix issue with displaying extra line * Don't ignore last line * Bump patch version --- node/mock-toolrunner.ts | 14 ++++++ node/package-lock.json | 2 +- node/package.json | 2 +- node/test/mocktests.ts | 98 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 113 insertions(+), 3 deletions(-) diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 0c9463dc5..0bb57326a 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -228,6 +228,13 @@ export class ToolRunner extends events.EventEmitter { if (!ops.silent) { ops.outStream.write(res.stdout + os.EOL); } + const stdLineArray = res.stdout.split(os.EOL); + for (const line of stdLineArray.slice(0, -1)) { + this.emit('stdline', line); + } + if(stdLineArray.length > 0 && stdLineArray[stdLineArray.length - 1].length > 0) { + this.emit('stdline', stdLineArray[stdLineArray.length - 1]); + } } if (res.stderr) { @@ -238,6 +245,13 @@ export class ToolRunner extends events.EventEmitter { var s = ops.failOnStdErr ? ops.errStream : ops.outStream; s.write(res.stderr + os.EOL); } + const stdErrArray = res.stderr.split(os.EOL); + for (const line of stdErrArray.slice(0, -1)) { + this.emit('errline', line); + } + if (stdErrArray.length > 0 && stdErrArray[stdErrArray.length - 1].length > 0) { + this.emit('errline', stdErrArray[stdErrArray.length - 1]); + } } diff --git a/node/package-lock.json b/node/package-lock.json index a9ccb83b9..57fa2f276 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.5", + "version": "2.7.8", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 5f8847ef4..7a1988d8c 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.7", + "version": "2.7.8", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/test/mocktests.ts b/node/test/mocktests.ts index b813da12f..cc140a756 100644 --- a/node/test/mocktests.ts +++ b/node/test/mocktests.ts @@ -11,6 +11,7 @@ import * as mtr from '../_build/mock-toolrunner'; import * as ma from '../_build/mock-answer'; import * as tl from '../_build/task'; +import os = require('os'); import testutil = require('./testutil'); describe('Mock Tests', function () { @@ -183,5 +184,100 @@ describe('Mock Tests', function () { assert(tool, "tool should not be null"); assert(rc == 0, "rc is 0"); - }) + }) + + it('Mock toolRunner returns correct output', async () => { + const expectedStdout = "atool output here" + os.EOL + "abc"; + const expectedStderr = "atool with this stderr output" + os.EOL + "def"; + var a: ma.TaskLibAnswers = { + "exec": { + "/usr/local/bin/atool --arg foo": { + "code": 0, + "stdout": expectedStdout, + "stderr": expectedStderr + } + } + }; + + mt.setAnswers(a); + + let tool: mtr.ToolRunner = mt.tool('/usr/local/bin/atool'); + tool.arg('--arg'); + tool.arg('foo'); + + let firstStdline = true; + let firstErrline = true; + let numStdLineCalls = 0; + let numStdErrCalls = 0; + tool.on('stdout', (out) => { + assert.equal(expectedStdout, out); + }); + tool.on('stderr', (out) => { + assert.equal(expectedStderr, out); + }); + tool.on('stdline', (out) => { + numStdLineCalls += 1; + if (firstStdline) { + assert.equal("atool output here", out); + firstStdline = false; + } + else { + assert.equal("abc", out); + } + }); + tool.on('errline', (out) => { + numStdErrCalls += 1; + if (firstErrline) { + assert.equal("atool with this stderr output", out); + firstErrline = false; + } + else { + assert.equal("def", out); + } + }); + await tool.exec({}); + + assert.equal(numStdLineCalls, 2); + assert.equal(numStdErrCalls, 2); + }) + + it('Mock toolRunner returns correct output when ending on EOL', async () => { + const expectedStdout = os.EOL; + const expectedStderr = os.EOL; + var a: ma.TaskLibAnswers = { + "exec": { + "/usr/local/bin/atool --arg foo": { + "code": 0, + "stdout": expectedStdout, + "stderr": expectedStderr + } + } + }; + + mt.setAnswers(a); + + let tool: mtr.ToolRunner = mt.tool('/usr/local/bin/atool'); + tool.arg('--arg'); + tool.arg('foo'); + let numStdLineCalls = 0; + let numStdErrCalls = 0; + tool.on('stdout', (out) => { + assert.equal(expectedStdout, out); + }); + tool.on('stderr', (out) => { + assert.equal(expectedStderr, out); + }); + tool.on('stdline', (out) => { + numStdLineCalls += 1; + assert.equal("", out); + }); + tool.on('errline', (out) => { + numStdErrCalls += 1; + assert.equal("", out); + }); + await tool.exec({}); + + assert.equal(numStdLineCalls, 1); + assert.equal(numStdErrCalls, 1); + }) }); From 578d70c9722e3078bdc7d3b66ab36add9ea229da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Sch=C3=BCtze?= Date: Wed, 19 Dec 2018 17:40:08 +0100 Subject: [PATCH 102/259] rename VSTS to Azure DevOps (#484) * rename VSTS to Azure DevOps maybe the id of the schema should be renamed as well if possible. * updated id of the schema to reflect the new name --- tasks.schema.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasks.schema.json b/tasks.schema.json index ede1b83f2..bdefce31d 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-04/schema", - "id": "https://aka.ms/vsts-tasks.schema.json", - "title": "VSTS Tasks schema", + "id": "https://raw.githubusercontent.com/Microsoft/azure-pipelines-task-lib/master/tasks.schema.json", + "title": "Azure DevOps Tasks schema", "type": "object", "additionalProperties": false, "properties": { @@ -53,7 +53,7 @@ }, "category": { "type": "string", - "description": "Where the task appears in VSTS. Use the 'Azure *' categories for Azure DevOps and Azure DevOps Server 2019. Use the other categories for Team Foundation Server 2018 and below.", + "description": "Where the task appears in Azure DevOps. Use the 'Azure *' categories for Azure DevOps and Azure DevOps Server 2019. Use the other categories for Team Foundation Server 2018 and below.", "enum": [ "Build", "Utility", From 045cdebde0cc14897ddcacd8de64a51c5898a975 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 2 Jan 2019 21:29:01 +0300 Subject: [PATCH 103/259] Example correction (#486) --- node/docs/azure-pipelines-task-lib.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/docs/azure-pipelines-task-lib.md b/node/docs/azure-pipelines-task-lib.md index 85a0f4d55..153825eb9 100644 --- a/node/docs/azure-pipelines-task-lib.md +++ b/node/docs/azure-pipelines-task-lib.md @@ -318,7 +318,7 @@ import tr = require('azure-pipelines-task-lib/toolrunner'); try { var toolPath = tl.which('atool'); var atool:tr.ToolRunner = tl.tool(toolPath).arg('--afile').line('arguments'); - var code: number = await tr.exec(); + var code: number = await atool.exec(); console.log('rc=' + code); } catch (err) { From 1a7e4ddca5277f87301b4880fed1971022c16b7e Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 5 Feb 2019 16:01:31 -0500 Subject: [PATCH 104/259] Fix executable check in _tryGetExecutablePath (#421) * Update internal.ts * Missed that there were 2 instances of this * Update package.json * Add comments for clarity * Add tests, check process permissions * Update package.json * Respond to feedback --- node/internal.ts | 19 +++++++------ node/package.json | 2 +- node/test/dirtests.ts | 66 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 11 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index 98def6adb..8e6032c21 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -442,10 +442,7 @@ function _tryGetExecutablePath(filePath: string, extensions: string[]): string { } } else { - // on Mac/Linux, test the execute bit - // R W X R W X R W X - // 256 128 64 32 16 8 4 2 1 - if ((stats.mode & 1) == 1) { + if (isUnixExecutable(stats)) { return filePath; } } @@ -484,10 +481,7 @@ function _tryGetExecutablePath(filePath: string, extensions: string[]): string { return filePath; } else { - // on Mac/Linux, test the execute bit - // R W X R W X R W X - // 256 128 64 32 16 8 4 2 1 - if ((stats.mode & 1) == 1) { + if (isUnixExecutable(stats)) { return filePath; } } @@ -503,6 +497,13 @@ function _tryGetExecutablePath(filePath: string, extensions: string[]): string { return ''; } +// on Mac/Linux, test the execute bit +// R W X R W X R W X +// 256 128 64 32 16 8 4 2 1 +function isUnixExecutable(stats: fs.Stats) { + return (stats.mode & 1) > 0 || ((stats.mode & 8) > 0 && stats.gid === process.getgid()) || ((stats.mode & 64) > 0 && stats.uid === process.getuid()); +} + export function _legacyFindFiles_convertPatternToRegExp(pattern: string): RegExp { 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 @@ -983,4 +984,4 @@ function _exposeTaskLibSecret(keyFile: string, secret: string): string { return new Buffer(storageFile).toString('base64') + ':' + new Buffer(encryptedContent).toString('base64'); } -} \ No newline at end of file +} diff --git a/node/package.json b/node/package.json index 7a1988d8c..887ec8b87 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.8", + "version": "2.7.9", "description": "VSTS Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/test/dirtests.ts b/node/test/dirtests.ts index aaab2322e..e92c078d2 100644 --- a/node/test/dirtests.ts +++ b/node/test/dirtests.ts @@ -96,6 +96,7 @@ describe('Dir Operation Tests', function () { done(); }); + it('which() not found', function (done) { this.timeout(1000); @@ -184,6 +185,28 @@ describe('Dir Operation Tests', function () { done(); }); + + // which permissions tests + it('which() finds executable with owner permissions', function (done) { + this.timeout(1000); + findsExecutableWithScopedPermissions('u=rwx,g=r,o=r'); + done(); + }); + + // which permissions tests + it('which() finds executable with group permissions', function (done) { + this.timeout(1000); + findsExecutableWithScopedPermissions('u=rw,g=rx,o=r'); + done(); + }); + + // which permissions tests + it('which() finds executable with everyone permissions', function (done) { + this.timeout(1000); + findsExecutableWithScopedPermissions('u=rw,g=r,o=rx'); + done(); + }); + it('which() ignores directory match', function (done) { this.timeout(1000); @@ -1837,4 +1860,45 @@ describe('Dir Operation Tests', function () { done(); }); -}); \ No newline at end of file +}); + +function findsExecutableWithScopedPermissions(chmodOptions) { + // create a executable file + let testPath = path.join(testutil.getTestTemp(), 'which-finds-file-name'); + tl.mkdirP(testPath); + let fileName = 'Which-Test-File'; + if (process.platform == 'win32') { + return; + } + + let filePath = path.join(testPath, fileName); + fs.writeFileSync(filePath, ''); + testutil.chmod(filePath, chmodOptions); + + let originalPath = process.env['PATH']; + try { + // update the PATH + process.env['PATH'] = process.env['PATH'] + path.delimiter + testPath; + + // exact file name + assert.equal(tl.which(fileName), filePath); + assert.equal(tl.which(fileName, false), filePath); + assert.equal(tl.which(fileName, true), filePath); + + if (process.platform == 'darwin') { + // not case sensitive on Mac + assert.equal(tl.which(fileName.toUpperCase()), path.join(testPath, fileName.toUpperCase())); + assert.equal(tl.which(fileName.toUpperCase(), false), path.join(testPath, fileName.toUpperCase())); + assert.equal(tl.which(fileName.toUpperCase(), true), path.join(testPath, fileName.toUpperCase())); + } + else { + // case sensitive on Linux + assert.equal(tl.which(fileName.toUpperCase()) || '', ''); + } + } + finally { + process.env['PATH'] = originalPath; + } + + return; +} \ No newline at end of file From fa4a786d9fc9f1a962c085a362065bd1eda182fa Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Wed, 6 Feb 2019 11:08:53 -0500 Subject: [PATCH 105/259] Get tests running again (#494) Code page changes seem to have broken csc.exe. I'm investigating further, but this should at least get them up and running again. --- azure-pipelines.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 748eacfe1..c3ce11366 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -28,7 +28,9 @@ jobs: displayName: (azure-pipelines-task-lib) use node 5.10.1 # build/test - - script: node make.js test + - script: | + chcp 437 + node make.js test workingDirectory: node displayName: (azure-pipelines-task-lib) node make.js test @@ -39,7 +41,9 @@ jobs: versionSpec: "6.10.3" # build/test - - script: node make.js test + - script: | + chcp 437 + node make.js test displayName: (azure-pipelines-task-lib) node make.js test workingDirectory: node From 0a110b84706c452ac3bd9dd49da751eea1ca9b03 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 12 Feb 2019 09:20:46 -0500 Subject: [PATCH 106/259] Added setVariableName method (#419) * Added setVariableName method * lint --- node/mock-run.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/node/mock-run.ts b/node/mock-run.ts index 24002fb21..91f51567a 100644 --- a/node/mock-run.ts +++ b/node/mock-run.ts @@ -15,6 +15,15 @@ export class TaskMockRunner { process.env['INPUT_' + name.replace(' ', '_').toUpperCase()] = val; } + public setVariableName(name: string, val: string, isSecret?: boolean) { + if (isSecret) { + process.env['SECRET_' + name.replace(' ', '_').toUpperCase()] = val; + } + else { + process.env['VSTS_TASKVARIABLE_' + name.replace(' ', '_').toUpperCase()] = val; + } + } + /** * Register answers for the mock "azure-pipelines-task-lib/task" instance. * @@ -86,4 +95,4 @@ export class TaskMockRunner { // run it require(this._taskPath); } -} \ No newline at end of file +} From 58e620105a4090383e4811383ff917f8ef6bf36e Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 12 Feb 2019 09:21:36 -0500 Subject: [PATCH 107/259] Upgrade to TypeScript 3 (#459) * Upgrade to typescript 3 * Forgot to add package-lock * Update package.json * Respond to feedback --- node/package-lock.json | 8 ++++---- node/package.json | 6 +++--- node/task.ts | 7 ++++++- node/vault.ts | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 57fa2f276..81c892bbc 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.8", + "version": "2.8.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -336,9 +336,9 @@ "dev": true }, "typescript": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.7.tgz", - "integrity": "sha1-NeODjeMckc/h2MIODleF04aTikk=", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.3.tgz", + "integrity": "sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA==", "dev": true }, "util-deprecate": { diff --git a/node/package.json b/node/package.json index 887ec8b87..3de8fe5b2 100644 --- a/node/package.json +++ b/node/package.json @@ -1,7 +1,7 @@ { "name": "azure-pipelines-task-lib", - "version": "2.7.9", - "description": "VSTS Task SDK", + "version": "2.8.0", + "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", "scripts": { @@ -35,6 +35,6 @@ "devDependencies": { "mocha": "5.0.2", "sync-request": "3.0.1", - "typescript": "1.8.7" + "typescript": "^3.0.0" } } diff --git a/node/task.ts b/node/task.ts index 19fd63bef..8caf34c99 100644 --- a/node/task.ts +++ b/node/task.ts @@ -554,7 +554,12 @@ export interface FsOptions { } export function writeFile(file: string, data: string | Buffer, options?: string | FsOptions) { - fs.writeFileSync(file, data, options); + if(typeof(options) === 'string'){ + fs.writeFileSync(file, data, {encoding: options}); + } + else { + fs.writeFileSync(file, data, options); + } } /** diff --git a/node/vault.ts b/node/vault.ts index 554ed3c79..db518b44d 100644 --- a/node/vault.ts +++ b/node/vault.ts @@ -72,6 +72,6 @@ export class Vault { } private genKey(): void { - fs.writeFileSync(this._keyFile, uuidV4(), 'utf8'); + fs.writeFileSync(this._keyFile, uuidV4(), {encoding: 'utf8'}); } } \ No newline at end of file From 4a77fd68aacc43ff2933d3a366ea815b72f08342 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 12 Feb 2019 09:34:39 -0500 Subject: [PATCH 108/259] Updated dependencies from component governance (#466) * Updated dependencies from component governance * Don't need this added in * Try restoring adm-zip dependency --- node/package-lock.json | 83 +++++++++++++++++++++-------------------- node/package.json | 2 +- powershell/make.js | 2 +- powershell/package.json | 2 +- 4 files changed, 46 insertions(+), 43 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 81c892bbc..4bf358c79 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -36,12 +36,6 @@ "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", "dev": true }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -73,12 +67,6 @@ "ms": "2.0.0" } }, - "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", - "dev": true - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -105,18 +93,6 @@ "path-is-absolute": "1.0.1" } }, - "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -186,21 +162,57 @@ } }, "mocha": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.2.tgz", - "integrity": "sha512-nmlYKMRpJZLxgzk0bRhcvlpjSisbi0x1JiRl7kctadOMPmecUie7WwCZmcyth+PzX5txKbpcMIvDZCAlx9ISxg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", "dev": true, "requires": { "browser-stdout": "1.3.1", - "commander": "2.11.0", + "commander": "2.15.1", "debug": "3.1.0", - "diff": "3.3.1", + "diff": "3.5.0", "escape-string-regexp": "1.0.5", "glob": "7.1.2", - "growl": "1.10.3", + "growl": "1.10.5", "he": "1.1.1", + "minimatch": "3.0.4", "mkdirp": "0.5.1", - "supports-color": "4.4.0" + "supports-color": "5.4.0" + }, + "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } } }, "mockery": { @@ -295,15 +307,6 @@ "safe-buffer": "5.1.1" } }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - }, "sync-request": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-3.0.1.tgz", diff --git a/node/package.json b/node/package.json index 3de8fe5b2..aa015a3ee 100644 --- a/node/package.json +++ b/node/package.json @@ -33,7 +33,7 @@ "shelljs": "^0.3.0" }, "devDependencies": { - "mocha": "5.0.2", + "mocha": "5.2.0", "sync-request": "3.0.1", "typescript": "^3.0.0" } diff --git a/powershell/make.js b/powershell/make.js index 957a07a7e..e5d0b1cde 100644 --- a/powershell/make.js +++ b/powershell/make.js @@ -62,7 +62,7 @@ target.build = function() { target.test = function() { util.ensureTool('tsc', '--version', 'Version 1.8.7'); - util.ensureTool('mocha', '--version', '2.3.3'); + util.ensureTool('mocha', '--version', '5.2.0'); target.build(); util.mkdir('-p', testPath); diff --git a/powershell/package.json b/powershell/package.json index f752b728e..f4c0381fe 100644 --- a/powershell/package.json +++ b/powershell/package.json @@ -24,7 +24,7 @@ "homepage": "https://github.com/Microsoft/azure-pipelines-task-lib#readme", "devDependencies": { "adm-zip": "0.4.7", - "mocha": "2.3.3", + "mocha": "5.2.0", "q": "1.4.1", "shelljs": "^0.3.0", "sync-request": "3.0.1", From 3f2176ce2d9801d66f1be504c684643d3a77cc19 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 12 Feb 2019 14:36:42 -0500 Subject: [PATCH 109/259] Update release notes for 2.8.0 (#495) * Update release notes for 2.8.0 * Missed changes that happened earlier on. --- node/docs/releases.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/node/docs/releases.md b/node/docs/releases.md index ff63c1444..641db42b9 100644 --- a/node/docs/releases.md +++ b/node/docs/releases.md @@ -1,5 +1,13 @@ # AZURE-PIPELINES-TASK-LIB RELEASES +## 2.8.0 + * Updated to TypeScript 3.0 + * Fixed `which` so that it finds executables that can be executed by the current process, not just those that are executable by everyone. + * Added `setVariable` method to `mock-run` for mocking variables during test. + * Update `MockToolRunner` to emit stdout + * Added support for UNC args in `ToolRunner` + * Added additional logging commands to the Node library to obtain parity with PowerShell. + ## 2.7.0 * Updated `setResult` to expose optional done parameter * Updated `ToolRunner` to distinguish between events for process exit and STDIO streams closed From 2b465aef641398361a779d43173cdaf399e6dc7f Mon Sep 17 00:00:00 2001 From: Juan Date: Thu, 14 Feb 2019 13:52:30 +0000 Subject: [PATCH 110/259] Fix an small typo in the documentation (#497) * Fixed typo in silent parameter * Fix typo Fixed an small typo --- node/docs/azure-pipelines-task-lib.json | 4 ++-- node/toolrunner.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/docs/azure-pipelines-task-lib.json b/node/docs/azure-pipelines-task-lib.json index 0bbfe2859..4b0c6fde2 100644 --- a/node/docs/azure-pipelines-task-lib.json +++ b/node/docs/azure-pipelines-task-lib.json @@ -35,7 +35,7 @@ "return": "{ [key: string]: string; }" }, "silent": { - "documentation": "optional. defaults to fales ", + "documentation": "optional. defaults to false ", "name": "silent", "return": "boolean" }, @@ -2489,4 +2489,4 @@ } } } -} \ No newline at end of file +} diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 2dd964992..f48fef272 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -29,7 +29,7 @@ export interface IExecSyncOptions { /** optional envvar dictionary. defaults to current process's env */ env: { [key: string]: string }; - /** optional. defaults to fales */ + /** optional. defaults to false */ silent: boolean; outStream: stream.Writable; From 3ca1b0ef0110e96d31db73a922ad17e922e4a488 Mon Sep 17 00:00:00 2001 From: Sandor <2660262+sandorfr@users.noreply.github.com> Date: Fri, 15 Feb 2019 01:36:09 +1100 Subject: [PATCH 111/259] Add missing "type" on connectService string pattern (#384) It is required for some tools (such as json2ts) to work properly. Other similar pattern have the type specified so I assume this has just been forgotten. --- tasks.schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks.schema.json b/tasks.schema.json index bdefce31d..230c367e0 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -163,6 +163,7 @@ ] }, { + "type": "string", "pattern": "^connectedService\\:.+$" } ] From f46409f4e7cffba95200334341dee90ce0fd6f74 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 14 Feb 2019 10:34:20 -0500 Subject: [PATCH 112/259] Updating documentation (#461) * Updating documentation * Fixing broken link * Cutting out irrelevant parts of contributing * Added issue template * Update numbering * Just got rid of issue logging section of contributing, kinda redundant with issue template * Just got rid of issue logging section of contributing, kinda redundant with issue template * Just got rid of issue logging section of contributing, kinda redundant with issue template --- CONTRIBUTING.md | 33 +++++++++++++++++++++++++++++++++ ISSUE_TEMPLATE.md | 23 +++++++++++++++++++++++ node/README.md | 16 +++++++++++++++- 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md create mode 100644 ISSUE_TEMPLATE.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..ed8e74deb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# Instructions for Contributing Code + +## Contributing bug fixes + +We are currently accepting contributions in the form of bug fixes. A bug must have an issue tracking it in the issue tracker. Your pull request should include a link to the bug that you are fixing. If you've submitted a PR for a bug, please post a comment in the bug to avoid duplication of effort. + +## Contributing features + +Features (things that add new or improved functionality) may be accepted, but will need to first be approved in the form of a suggestion issue. + +Design changes will not be accepted at this time. If you have a design change proposal, please log a suggestion issue. + +## Legal + +You will need to complete a Contributor License Agreement (CLA). Briefly, this agreement testifies that you are granting us permission to use the submitted change according to the terms of the project's license, and that the work being submitted is under appropriate copyright. + +Please submit a Contributor License Agreement (CLA) before submitting a pull request. You may visit https://cla.microsoft.com to sign digitally. + +## Housekeeping + +Your pull request should: + +* Include a description of what your change intends to do +* Be a child commit of a reasonably recent commit in the **master** branch + * Requests need not be a single commit, but should be a linear sequence of commits (i.e. no merge commits in your PR) +* It is desirable, but not necessary, for the tests to pass at each commit +* Have clear commit messages + * e.g. "Refactor feature", "Fix issue", "Add tests for issue" +* Include adequate tests + * At least one test should fail in the absence of your non-test code changes. If your PR does not match this criteria, please specify why + * Tests should include reasonable permutations of the target fix/change + * Include baseline changes with your change + * All changed code must have 100% code coverage \ No newline at end of file diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..764b797f3 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,23 @@ +Please check our current Issues to see if someone already reported this https://github.com/Microsoft/azure-pipelines-task-lib/issues + +### Environment +azure-pipelines-task-lib version: + +### Issue Description + + +### Expected behaviour + + +### Actual behaviour + + +### Steps to reproduce +1. +2. +3. +4. +5. + +### Logs + \ No newline at end of file diff --git a/node/README.md b/node/README.md index 19906934b..e3ea39b2f 100644 --- a/node/README.md +++ b/node/README.md @@ -23,7 +23,13 @@ The [ShellScript Task](https://github.com/Microsoft/azure-pipelines-tasks/tree/m ## Contributing -### Node +We are accepting contributions and we try to stay on top of issues. + +[Contribution Guide](../CONTRIBUTING.md). + +[Logging Issues](https://github.com/Microsoft/azure-pipelines-task-lib/issues) + +## Building the library Once: ```bash @@ -41,6 +47,14 @@ Set environment variable TASK_TEST_TRACE=1 to display test output. [npm-lib-image]: https://img.shields.io/npm/v/azure-pipelines-task-lib.svg?style=flat [npm-lib-url]: https://www.npmjs.com/package/azure-pipelines-task-lib +## Powershell + +We also maintain a PowerShell library for Windows task development. + +Library: [Powershell Library](../powershell) + +Usage: [Consuming the SDK](../powershell/Docs/Consuming.md) + ## Third Party Notices To generate/update third party notice file run: ```bash From 5143ee8450089146a0be3df4c54ba58df5098ba2 Mon Sep 17 00:00:00 2001 From: Brian Cristante <33549821+brcrista@users.noreply.github.com> Date: Fri, 15 Feb 2019 15:18:01 -0500 Subject: [PATCH 113/259] Add `getPlatform` function (#374) * Add `getPlatform` function * Clean up --- .../resources.resjson/en-US/resources.resjson | 3 ++- node/lib.json | 3 ++- node/task.ts | 24 +++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/node/Strings/resources.resjson/en-US/resources.resjson b/node/Strings/resources.resjson/en-US/resources.resjson index 586217d7a..cd5a30851 100644 --- a/node/Strings/resources.resjson/en-US/resources.resjson +++ b/node/Strings/resources.resjson/en-US/resources.resjson @@ -29,5 +29,6 @@ "loc.messages.LIB_PathHasNullByte": "Path cannot contain null bytes", "loc.messages.LIB_OperationFailed": "Failed %s: %s", "loc.messages.LIB_UseFirstGlobMatch": "Multiple workspace matches. using first.", - "loc.messages.LIB_MergeTestResultNotSupported": "Merging test results from multiple files to one test run is not supported on this version of build agent for OSX/Linux, each test result file will be published as a separate test run in VSO/TFS." + "loc.messages.LIB_MergeTestResultNotSupported": "Merging test results from multiple files to one test run is not supported on this version of build agent for OSX/Linux, each test result file will be published as a separate test run in VSO/TFS.", + "loc.messages.LIB_PlatformNotSupported": "Platform not supported: %s" } \ No newline at end of file diff --git a/node/lib.json b/node/lib.json index e1954de2d..1f02f249c 100644 --- a/node/lib.json +++ b/node/lib.json @@ -30,6 +30,7 @@ "LIB_PathHasNullByte": "Path cannot contain null bytes", "LIB_OperationFailed": "Failed %s: %s", "LIB_UseFirstGlobMatch": "Multiple workspace matches. using first.", - "LIB_MergeTestResultNotSupported": "Merging test results from multiple files to one test run is not supported on this version of build agent for OSX/Linux, each test result file will be published as a separate test run in VSO/TFS." + "LIB_MergeTestResultNotSupported": "Merging test results from multiple files to one test run is not supported on this version of build agent for OSX/Linux, each test result file will be published as a separate test run in VSO/TFS.", + "LIB_PlatformNotSupported": "Platform not supported: %s" } } diff --git a/node/task.ts b/node/task.ts index 8caf34c99..3c0292999 100644 --- a/node/task.ts +++ b/node/task.ts @@ -4,11 +4,9 @@ import fs = require('fs'); import path = require('path'); import os = require('os'); import minimatch = require('minimatch'); -import util = require('util'); import im = require('./internal'); import tcm = require('./taskcommand'); import trm = require('./toolrunner'); -import vm = require('./vault'); import semver = require('semver'); export enum TaskResult { @@ -45,6 +43,13 @@ export enum FieldType { Url } +/** Platforms supported by our build agent */ +export enum Platform { + Windows, + MacOS, + Linux +} + //----------------------------------------------------- // General Helpers //----------------------------------------------------- @@ -563,6 +568,7 @@ export function writeFile(file: string, data: string | Buffer, options?: string } /** + * @deprecated Use `getPlatform` * Useful for determining the host operating system. * see [os.type](https://nodejs.org/api/os.html#os_os_type) * @@ -572,6 +578,20 @@ export function osType(): string { return os.type(); } +/** + * Determine the operating system the build agent is running on. + * @returns {Platform} + * @throws {Error} Platform is not supported by our agent + */ +export function getPlatform(): Platform { + switch (process.platform) { + case 'win32': return Platform.Windows; + case 'darwin': return Platform.MacOS; + case 'linux': return Platform.Linux; + default: throw Error(loc('LIB_PlatformNotSupported', process.platform)); + } +} + /** * Returns the process's current working directory. * see [process.cwd](https://nodejs.org/api/process.html#process_process_cwd) From 0574f6cba72cf8b385a015a683bb3c1016657b8f Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Fri, 15 Feb 2019 15:51:52 -0500 Subject: [PATCH 114/259] Update with `getPlatform` change --- node/docs/releases.md | 1 + 1 file changed, 1 insertion(+) diff --git a/node/docs/releases.md b/node/docs/releases.md index 641db42b9..72668105b 100644 --- a/node/docs/releases.md +++ b/node/docs/releases.md @@ -7,6 +7,7 @@ * Update `MockToolRunner` to emit stdout * Added support for UNC args in `ToolRunner` * Added additional logging commands to the Node library to obtain parity with PowerShell. + * Added `getPlatform` convenience function ## 2.7.0 * Updated `setResult` to expose optional done parameter From 98a98c79239141bbf6ad6931bd4723580ca12a0a Mon Sep 17 00:00:00 2001 From: Julio Barba Date: Mon, 4 Mar 2019 10:45:31 -0500 Subject: [PATCH 115/259] Include a note on ARM and x86 agent support (#402) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f7d8485ef..f9820aa1b 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,10 @@ A task which automates Powershell technologies can be written with our Powershel Documentation: [PowerShell API](powershell/Docs/README.md) +## Notes on authoring Tasks + +Starting from [version v2.141.0](https://github.com/Microsoft/azure-pipelines-agent/releases/tag/v2.141.0), the agent can now run on three OS architectures: x86, x64, and 32-bit ARM. When authoring a new task, you can check agent variable: `Agent.OSArchitecture` (possible values: X86, X64, ARM) to restrict running said task to a particular set of OS architectures. + [npm-lib-image]: https://img.shields.io/npm/v/azure-pipelines-task-lib.svg?style=flat [npm-lib-url]: https://www.npmjs.com/package/azure-pipelines-task-lib From aa02e7f8d90eca120e72f2007715bbb2b8a5309e Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Fri, 8 Mar 2019 14:53:26 -0500 Subject: [PATCH 116/259] Updated vault to encode secrets using cipheriv (#505) * Updated vault to encode secrets using cipheriv * Changed too much * Don't hash key * Update releases.md * Add back hashing --- node/docs/releases.md | 9 +++++---- node/vault.ts | 32 ++++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/node/docs/releases.md b/node/docs/releases.md index 72668105b..230012fa7 100644 --- a/node/docs/releases.md +++ b/node/docs/releases.md @@ -1,13 +1,14 @@ # AZURE-PIPELINES-TASK-LIB RELEASES ## 2.8.0 - * Updated to TypeScript 3.0 + * Updated to TypeScript 3.0. * Fixed `which` so that it finds executables that can be executed by the current process, not just those that are executable by everyone. * Added `setVariable` method to `mock-run` for mocking variables during test. - * Update `MockToolRunner` to emit stdout - * Added support for UNC args in `ToolRunner` + * Update `MockToolRunner` to emit stdout. + * Added support for UNC args in `ToolRunner`. * Added additional logging commands to the Node library to obtain parity with PowerShell. - * Added `getPlatform` convenience function + * Added `getPlatform` convenience function. + * Updated `vault` to use safer cipheriv encoding (stops npm from warning about encryption). ## 2.7.0 * Updated `setResult` to expose optional done parameter diff --git a/node/vault.ts b/node/vault.ts index db518b44d..6f220263a 100644 --- a/node/vault.ts +++ b/node/vault.ts @@ -6,6 +6,8 @@ import crypto = require('crypto'); var uuidV4 = require('uuid/v4'); var algorithm = "aes-256-ctr"; +var encryptEncoding: 'hex' = 'hex'; +var unencryptedEncoding: 'utf8' = 'utf8'; // // Store sensitive data in proc. @@ -43,10 +45,13 @@ export class Vault { } var key = this.getKey(); - var cipher = crypto.createCipher(algorithm, key); - var crypted = cipher.update(data,'utf8','hex') - crypted += cipher.final('hex'); - this._store[name] = crypted; + var iv = crypto.randomBytes(16); + + var cipher = crypto.createCipheriv(algorithm, key, iv); + var crypted = cipher.update(data, unencryptedEncoding, encryptEncoding) + var cryptedFinal = cipher.final(encryptEncoding); + + this._store[name] = iv.toString(encryptEncoding) + crypted + cryptedFinal; return true; } @@ -57,10 +62,15 @@ export class Vault { if (this._store.hasOwnProperty(name)) { var key = this.getKey(); var data = this._store[name]; - var decipher = crypto.createDecipher(algorithm, key) - var dec = decipher.update(data,'hex','utf8') - dec += decipher.final('utf8'); - secret = dec; + var ivDataBuffer = Buffer.from(data, encryptEncoding); + var iv = ivDataBuffer.slice(0, 16); + var encryptedText = ivDataBuffer.slice(16); + + var decipher = crypto.createDecipheriv(algorithm, key, iv); + var dec = decipher.update(encryptedText,encryptEncoding,unencryptedEncoding); + var decFinal = decipher.final(unencryptedEncoding); + + secret = dec + decFinal; } return secret; @@ -68,10 +78,12 @@ export class Vault { private getKey() { - return fs.readFileSync(this._keyFile).toString('utf8'); + var key = fs.readFileSync(this._keyFile).toString('utf8'); + // Key needs to be hashed to correct length to match algorithm (aes-256-ctr) + return crypto.createHash('sha256').update(key).digest(); } private genKey(): void { fs.writeFileSync(this._keyFile, uuidV4(), {encoding: 'utf8'}); } -} \ No newline at end of file +} From df7bf53868688e03ff393af06a859a42bf47ec7d Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Sat, 9 Mar 2019 08:54:04 -0500 Subject: [PATCH 117/259] React to changes in toolrunner --- node/toolrunner.ts | 114 ++++++++++++++++++++++----------------------- node/vault.ts | 2 +- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index b075f476b..500859c27 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -17,7 +17,7 @@ export interface IExecOptions extends IExecSyncOptions { /** optional. defaults to failing on non zero. ignore will not fail leaving it up to the caller */ ignoreReturnCode: boolean; -}; +} /** * Interface for execSync options @@ -38,7 +38,7 @@ export interface IExecSyncOptions { /** optional. foo.whether to skip quoting/escaping arguments if needed. defaults to false. */ windowsVerbatimArguments: boolean; -}; +} /** * Interface for exec results returned from synchronous exec functions @@ -479,7 +479,8 @@ export class ToolRunner extends events.EventEmitter { return result; } - private _getSpawnOptions(options: IExecOptions): child.SpawnOptions { + private _getSpawnOptions(options?: IExecOptions): child.SpawnOptions { + options = options || {}; let result = {}; result.cwd = options.cwd; result.env = options.env; @@ -504,24 +505,29 @@ export class ToolRunner extends events.EventEmitter { this._debug(' ' + arg); }); + // This is a private method, and must be called only when `this.pipeOutputToTool` is set + if (!this.pipeOutputToTool) { + throw new Error('You must call pipeExecOutputToTool before calling execWithPiping'); + } + const pipeOutputToTool = this.pipeOutputToTool; + let success = true; - options = this._cloneExecOptions(options); + const optionsNonNull = this._cloneExecOptions(options); - if (!options.silent) { - options.outStream.write(this._getCommandString(options) + os.EOL); + if (!optionsNonNull.silent) { + optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL); } - let cp; - let toolPath: string = this.toolPath; + let cp: child.ChildProcess; + let toolPath: string = pipeOutputToTool.toolPath; let toolPathFirst: string; let successFirst = true; let returnCodeFirst: number; - let fileStream: fs.WriteStream; + let fileStream: fs.WriteStream | null; let waitingEvents: number = 0; // number of process or stream events we are waiting on to complete let returnCode: number = 0; - let error; + let error: any; - toolPath = this.pipeOutputToTool.toolPath; toolPathFirst = this.toolPath; // Following node documentation example from this link on how to pipe output of one process to another @@ -531,18 +537,18 @@ export class ToolRunner extends events.EventEmitter { waitingEvents++; var cpFirst = child.spawn( this._getSpawnFileName(), - this._getSpawnArgs(options), - this._getSpawnOptions(options)); - + this._getSpawnArgs(optionsNonNull), + this._getSpawnOptions(optionsNonNull)); + waitingEvents ++; cp = child.spawn( - this.pipeOutputToTool._getSpawnFileName(), - this.pipeOutputToTool._getSpawnArgs(options), - this.pipeOutputToTool._getSpawnOptions(options)); + pipeOutputToTool._getSpawnFileName(), + pipeOutputToTool._getSpawnArgs(optionsNonNull), + pipeOutputToTool._getSpawnOptions(optionsNonNull)); fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; if (fileStream) { - waitingEvents ++; + waitingEvents++; fileStream.on('finish', () => { waitingEvents--; //file write is complete fileStream = null; @@ -554,7 +560,7 @@ export class ToolRunner extends events.EventEmitter { } } }); - fileStream.on('error', (err) => { + fileStream.on('error', (err: Error) => { waitingEvents--; //there were errors writing to the file, write is done this._debug(`Failed to pipe output of ${toolPathFirst} to file ${this.pipeOutputToFile}. Error = ${err}`); fileStream = null; @@ -565,7 +571,7 @@ export class ToolRunner extends events.EventEmitter { defer.resolve(returnCode); } } - }) + }); } //pipe stdout of first tool to stdin of second tool @@ -584,13 +590,13 @@ export class ToolRunner extends events.EventEmitter { if (fileStream) { fileStream.write(data); } - successFirst = !options.failOnStdErr; - if (!options.silent) { - var s = options.failOnStdErr ? options.errStream : options.outStream; + successFirst = !optionsNonNull.failOnStdErr; + if (!optionsNonNull.silent) { + var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream; s.write(data); } }); - cpFirst.on('error', (err) => { + cpFirst.on('error', (err: Error) => { waitingEvents--; //first process is complete with errors if (fileStream) { fileStream.end(); @@ -601,9 +607,9 @@ export class ToolRunner extends events.EventEmitter { defer.reject(error); } }); - cpFirst.on('close', (code, signal) => { + cpFirst.on('close', (code: number, signal: any) => { waitingEvents--; //first process is complete - if (code != 0 && !options.ignoreReturnCode) { + if (code != 0 && !optionsNonNull.ignoreReturnCode) { successFirst = false; returnCodeFirst = code; returnCode = returnCodeFirst; @@ -626,8 +632,8 @@ export class ToolRunner extends events.EventEmitter { cp.stdout.on('data', (data: Buffer) => { this.emit('stdout', data); - if (!options.silent) { - options.outStream.write(data); + if (!optionsNonNull.silent) { + optionsNonNull.outStream.write(data); } this._processLineBuffer(data, stdbuffer, (line: string) => { @@ -639,9 +645,9 @@ export class ToolRunner extends events.EventEmitter { cp.stderr.on('data', (data: Buffer) => { this.emit('stderr', data); - success = !options.failOnStdErr; - if (!options.silent) { - var s = options.failOnStdErr ? options.errStream : options.outStream; + success = !optionsNonNull.failOnStdErr; + if (!optionsNonNull.silent) { + var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream; s.write(data); } @@ -650,7 +656,7 @@ export class ToolRunner extends events.EventEmitter { }); }); - cp.on('error', (err) => { + cp.on('error', (err: Error) => { waitingEvents--; //process is done with errors error = new Error(toolPath + ' failed. ' + err.message); if(waitingEvents == 0) { @@ -658,7 +664,7 @@ export class ToolRunner extends events.EventEmitter { } }); - cp.on('close', (code, signal) => { + cp.on('close', (code: number, signal: any) => { waitingEvents--; //process is complete this._debug('rc:' + code); returnCode = code; @@ -671,12 +677,12 @@ export class ToolRunner extends events.EventEmitter { this.emit('errline', errbuffer); } - if (code != 0 && !options.ignoreReturnCode) { + if (code != 0 && !optionsNonNull.ignoreReturnCode) { success = false; } this._debug('success:' + success); - + if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools error = new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst); } else if (!success) { @@ -788,26 +794,22 @@ export class ToolRunner extends events.EventEmitter { this._debug(' ' + arg); }); - options = this._cloneExecOptions(options); - if (!options.silent) { - options.outStream.write(this._getCommandString(options) + os.EOL); + const optionsNonNull = this._cloneExecOptions(options); + if (!optionsNonNull.silent) { + optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL); } - let state = new ExecState(options, this.toolPath); - state.on('debug', (message) => { + let state = new ExecState(optionsNonNull, this.toolPath); + state.on('debug', (message: string) => { this._debug(message); }); - let cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(options), this._getSpawnOptions(options)); + let cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(options)); var stdbuffer: string = ''; cp.stdout.on('data', (data: Buffer) => { this.emit('stdout', data); - if (!clonedOptions.silent) { - clonedOptions.outStream.write(data); - } - this._processLineBuffer(data, stdbuffer, (line: string) => { this.emit('stdline', line); }); @@ -818,8 +820,8 @@ export class ToolRunner extends events.EventEmitter { state.processStderr = true; this.emit('stderr', data); - if (!options.silent) { - var s = options.failOnStdErr ? options.errStream : options.outStream; + if (!optionsNonNull.silent) { + var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream; s.write(data); } @@ -828,21 +830,21 @@ export class ToolRunner extends events.EventEmitter { }); }); - cp.on('error', (err) => { + cp.on('error', (err: Error) => { state.processError = err.message; state.processExited = true; state.processClosed = true; state.CheckComplete(); }); - cp.on('exit', (code, signal) => { + cp.on('exit', (code: number, signal: any) => { state.processExitCode = code; state.processExited = true; this._debug(`Exit code ${code} received from tool '${this.toolPath}'`); state.CheckComplete() }); - cp.on('close', (code, signal) => { + cp.on('close', (code: number, signal: any) => { state.processExitCode = code; state.processExited = true; state.processClosed = true; @@ -850,7 +852,7 @@ export class ToolRunner extends events.EventEmitter { state.CheckComplete(); }); - state.on('done', (error, exitCode) => { + state.on('done', (error: Error, exitCode: number) => { if (stdbuffer.length > 0) { this.emit('stdline', stdbuffer); } @@ -869,7 +871,7 @@ export class ToolRunner extends events.EventEmitter { } }); - return >defer.promise; + return defer.promise; } /** @@ -883,8 +885,6 @@ export class ToolRunner extends events.EventEmitter { * @returns IExecSyncResult */ public execSync(options?: IExecSyncOptions): IExecSyncResult { - var defer = Q.defer(); - this._debug('exec tool: ' + this.toolPath); this._debug('arguments:'); this.args.forEach((arg) => { @@ -942,7 +942,7 @@ class ExecState extends events.EventEmitter { private delay = 10000; // 10 seconds private done: boolean; private options: IExecOptions; - private timeout; + private timeout: NodeJS.Timer | null = null; private toolPath: string; public CheckComplete(): void { @@ -958,13 +958,13 @@ class ExecState extends events.EventEmitter { } } - private _debug(message): void { + private _debug(message: any): void { this.emit('debug', message); } private _setResult(): void { // determine whether there is an error - let error: Error; + let error: Error | undefined; if (this.processExited) { if (this.processError) { error = new Error(im._loc('LIB_ProcessError', this.toolPath, this.processError)); diff --git a/node/vault.ts b/node/vault.ts index c556efdf1..f0a39534a 100644 --- a/node/vault.ts +++ b/node/vault.ts @@ -85,5 +85,5 @@ export class Vault { private genKey(): void { fs.writeFileSync(this._keyFile, uuidV4(), {encoding: 'utf8'}); - } + } } From c4cdcf24cdf24a52b4087b025f5750dba2b7bff6 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Sat, 9 Mar 2019 09:12:08 -0500 Subject: [PATCH 118/259] fix build errors in mock-task --- node/mock-task.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/node/mock-task.ts b/node/mock-task.ts index c2e44a583..8fe51d428 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -3,7 +3,6 @@ import Q = require('q'); import path = require('path'); import fs = require('fs'); import os = require('os'); -import util = require('util'); import task = require('./task'); import tcm = require('./taskcommand'); import trm = require('./mock-toolrunner'); @@ -484,13 +483,13 @@ exports.ToolRunner = trm.ToolRunner; //----------------------------------------------------- // Http Proxy Helper //----------------------------------------------------- -export function getHttpProxyConfiguration(requestUrl?: string): task.ProxyConfiguration { +export function getHttpProxyConfiguration(requestUrl?: string): task.ProxyConfiguration | null { return null; } //----------------------------------------------------- // Http Certificate Helper //----------------------------------------------------- -export function getHttpCertConfiguration(): task.CertConfiguration { +export function getHttpCertConfiguration(): task.ProxyConfiguration | null { return null } \ No newline at end of file From 58ee961c011c0b6030233e4a90bb4a0095d52ff5 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Sat, 9 Mar 2019 09:15:43 -0500 Subject: [PATCH 119/259] simplify mock-task.FsStats --- node/mock-task.ts | 115 ++++++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 71 deletions(-) diff --git a/node/mock-task.ts b/node/mock-task.ts index 8fe51d428..7fc7e313c 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -107,79 +107,52 @@ module.exports.getSecureFileTicket = task.getSecureFileTicket; //----------------------------------------------------- export class FsStats implements fs.Stats { - private m_isFile: boolean; - private m_isDirectory: boolean; - private m_isBlockDevice: boolean; - private m_isCharacterDevice: boolean; - private m_isSymbolicLink: boolean; - private m_isFIFO: boolean; - private m_isSocket: boolean; - - dev: number; - ino: number; - mode: number; - nlink: number; - uid: number; - gid: number; - rdev: number; - size: number; - blksize: number; - blocks: number; - atime: Date; - mtime: Date; - ctime: Date; - birthtime: Date; - - constructor() { - this.m_isFile = false; - this.m_isDirectory = false; - this.m_isBlockDevice = false; - this.m_isCharacterDevice = false; - this.m_isSymbolicLink = false; - this.m_isFIFO = false; - this.m_isSocket = false; - - this.dev = 0; - this.ino = 0; - this.mode = 0; - this.nlink = 0; - this.uid = 0; - this.gid = 0; - this.rdev = 0; - this.size = 0; - this.blksize = 0; - this.blocks = 0; - this.atime = new Date(); - this.mtime = new Date(); - this.ctime = new Date(); - this.m_isSocket = false; - - this.birthtime = new Date(); - } + private m_isFile: boolean = false; + private m_isDirectory: boolean = false; + private m_isBlockDevice: boolean = false; + private m_isCharacterDevice: boolean = false; + private m_isSymbolicLink: boolean = false; + private m_isFIFO: boolean = false; + private m_isSocket: boolean = false; + + dev: number = 0; + ino: number = 0; + mode: number = 0; + nlink: number = 0; + uid: number = 0; + gid: number = 0; + rdev: number = 0; + size: number = 0; + blksize: number = 0; + blocks: number = 0; + atime: Date = new Date(); + mtime: Date = new Date(); + ctime: Date = new Date(); + birthtime: Date = new Date(); setAnswers(mockResponses: any): void { - this.m_isFile = mockResponses['isFile'] || false; - this.m_isDirectory = mockResponses['isDirectory'] || false; - this.m_isBlockDevice = mockResponses['isBlockDevice'] || false; - this.m_isCharacterDevice = mockResponses['isCharacterDevice'] || false; - this.m_isSymbolicLink = mockResponses['isSymbolicLink'] || false; - this.m_isFIFO = mockResponses['isFIFO'] || false; - this.m_isSocket = mockResponses['isSocket'] || false; - - this.dev = mockResponses['dev']; - this.ino = mockResponses['ino']; - this.mode = mockResponses['mode']; - this.nlink = mockResponses['nlink']; - this.uid = mockResponses['uid']; - this.gid = mockResponses['gid']; - this.rdev = mockResponses['rdev']; - this.size = mockResponses['size']; - this.blksize = mockResponses['blksize']; - this.blocks = mockResponses['blocks']; - this.atime = mockResponses['atime']; - this.mtime = mockResponses['mtime']; - this.ctime = mockResponses['ctime']; - this.m_isSocket = mockResponses['isSocket']; + this.m_isFile = mockResponses['isFile'] || this.m_isFile; + this.m_isDirectory = mockResponses['isDirectory'] || this.m_isDirectory; + this.m_isBlockDevice = mockResponses['isBlockDevice'] || this.m_isBlockDevice; + this.m_isCharacterDevice = mockResponses['isCharacterDevice'] || this.m_isCharacterDevice; + this.m_isSymbolicLink = mockResponses['isSymbolicLink'] || this.m_isSymbolicLink; + this.m_isFIFO = mockResponses['isFIFO'] || this.m_isFIFO; + this.m_isSocket = mockResponses['isSocket'] || this.m_isSocket; + + this.dev = mockResponses['dev'] || this.dev; + this.ino = mockResponses['ino'] || this.ino; + this.mode = mockResponses['mode'] || this.mode; + this.nlink = mockResponses['nlink'] || this.nlink; + this.uid = mockResponses['uid'] || this.uid; + this.gid = mockResponses['gid'] || this.gid; + this.rdev = mockResponses['rdev'] || this.rdev; + this.size = mockResponses['size'] || this.size; + this.blksize = mockResponses['blksize'] || this.blksize; + this.blocks = mockResponses['blocks'] || this.blocks; + this.atime = mockResponses['atime'] || this.atime; + this.mtime = mockResponses['mtime'] || this.mtime; + this.ctime = mockResponses['ctime'] || this.ctime; + this.m_isSocket = mockResponses['isSocket'] || this.m_isSocket; } isFile(): boolean { From 29137f12878fdd4428c8cf054c78a7218f3e270d Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Sun, 10 Mar 2019 09:52:38 -0400 Subject: [PATCH 120/259] Require non-null pipeOutputToTool --- node/task.ts | 2 +- node/toolrunner.ts | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/node/task.ts b/node/task.ts index 7c4da8484..f4619ad33 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1199,7 +1199,7 @@ export function rmRF(path: string): void { */ export function exec(tool: string, args: any, options?: trm.IExecOptions): Q.Promise { let tr: trm.ToolRunner = this.tool(tool); - tr.on('debug', (data) => { + tr.on('debug', (data: string) => { debug(data); }); diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 500859c27..69dfba8f3 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -496,7 +496,7 @@ export class ToolRunner extends events.EventEmitter { return result; } - private execWithPiping(options?: IExecOptions): Q.Promise { + private execWithPiping(pipeOutputToTool: ToolRunner, options?: IExecOptions): Q.Promise { var defer = Q.defer(); this._debug('exec tool: ' + this.toolPath); @@ -505,12 +505,6 @@ export class ToolRunner extends events.EventEmitter { this._debug(' ' + arg); }); - // This is a private method, and must be called only when `this.pipeOutputToTool` is set - if (!this.pipeOutputToTool) { - throw new Error('You must call pipeExecOutputToTool before calling execWithPiping'); - } - const pipeOutputToTool = this.pipeOutputToTool; - let success = true; const optionsNonNull = this._cloneExecOptions(options); @@ -783,7 +777,7 @@ export class ToolRunner extends events.EventEmitter { */ public exec(options?: IExecOptions): Q.Promise { if (this.pipeOutputToTool) { - return this.execWithPiping(options); + return this.execWithPiping(this.pipeOutputToTool, options); } var defer = Q.defer(); From 64605eb5a2ae65c0c2e68c3d93e9ee62c12f61d1 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Sun, 10 Mar 2019 09:58:18 -0400 Subject: [PATCH 121/259] Annotate optional properties on IExecOptions --- node/mock-toolrunner.ts | 14 +++++++------- node/toolrunner.ts | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 7dd16100e..3d45a939e 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -11,7 +11,7 @@ export function setAnswers(answers: ma.TaskLibAnswers) { mock.initialize(answers); } -var run = function(cmd, callback) { +var run = function(cmd, callback) {F console.log('running: ' + cmd); var output = ''; try { @@ -24,17 +24,17 @@ var run = function(cmd, callback) { } export interface IExecOptions extends IExecSyncOptions { - failOnStdErr: boolean; - ignoreReturnCode: boolean; + failOnStdErr?: boolean; + ignoreReturnCode?: boolean; }; export interface IExecSyncOptions { - cwd: string; - env: { [key: string]: string }; - silent: boolean; + cwd?: string; + env?: { [key: string]: string }; + silent?: boolean; outStream: NodeJS.WritableStream; errStream: NodeJS.WritableStream; - windowsVerbatimArguments: boolean; + windowsVerbatimArguments?: boolean; }; export interface IExecSyncResult { diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 69dfba8f3..7d46bb2e6 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -13,10 +13,10 @@ import fs = require('fs'); */ export interface IExecOptions extends IExecSyncOptions { /** optional. whether to fail if output to stderr. defaults to false */ - failOnStdErr: boolean; + failOnStdErr?: boolean; /** optional. defaults to failing on non zero. ignore will not fail leaving it up to the caller */ - ignoreReturnCode: boolean; + ignoreReturnCode?: boolean; } /** @@ -24,20 +24,20 @@ export interface IExecOptions extends IExecSyncOptions { */ export interface IExecSyncOptions { /** optional working directory. defaults to current */ - cwd: string; + cwd?: string; /** optional envvar dictionary. defaults to current process's env */ - env: { [key: string]: string }; + env?: { [key: string]: string }; /** optional. defaults to false */ - silent: boolean; + silent?: boolean; outStream: stream.Writable; errStream: stream.Writable; /** optional. foo.whether to skip quoting/escaping arguments if needed. defaults to false. */ - windowsVerbatimArguments: boolean; + windowsVerbatimArguments?: boolean; } /** From aa41fd51f02a9f26411608588593001de7adb99d Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Sun, 10 Mar 2019 09:58:36 -0400 Subject: [PATCH 122/259] Remove unused code in mock-toolrunner --- node/mock-toolrunner.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 3d45a939e..7d459b089 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -1,7 +1,6 @@ import Q = require('q'); import os = require('os'); -import path = require('path'); import events = require('events'); import ma = require('./mock-answer'); @@ -11,18 +10,6 @@ export function setAnswers(answers: ma.TaskLibAnswers) { mock.initialize(answers); } -var run = function(cmd, callback) {F - console.log('running: ' + cmd); - var output = ''; - try { - - } - catch (err) { - console.log(err.message); - } - -} - export interface IExecOptions extends IExecSyncOptions { failOnStdErr?: boolean; ignoreReturnCode?: boolean; From f8be449a8c6160a85d00b054435178ce99f8c242 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Mon, 11 Mar 2019 13:31:20 -0400 Subject: [PATCH 123/259] PR feedback --- node/mock-task.ts | 2 +- node/mock-test.ts | 4 ++-- node/toolrunner.ts | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/node/mock-task.ts b/node/mock-task.ts index 7fc7e313c..7f8d576de 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -463,6 +463,6 @@ export function getHttpProxyConfiguration(requestUrl?: string): task.ProxyConfig //----------------------------------------------------- // Http Certificate Helper //----------------------------------------------------- -export function getHttpCertConfiguration(): task.ProxyConfiguration | null { +export function getHttpCertConfiguration(): task.CertConfiguration | null { return null } \ No newline at end of file diff --git a/node/mock-test.ts b/node/mock-test.ts index 09f4e85be..e33fdd424 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -37,11 +37,11 @@ export class MockTestRunner { } public stdOutContained(message: string): boolean { - return this.stdout.indexOf(message.trim()) > 0; + return this.stdout.indexOf(message) > 0; } public stdErrContained(message: string): boolean { - return this.stderr.indexOf(message.trim()) > 0; + return this.stderr.indexOf(message) > 0; } public run(): void { diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 7d46bb2e6..bf34ebeff 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -804,6 +804,10 @@ export class ToolRunner extends events.EventEmitter { cp.stdout.on('data', (data: Buffer) => { this.emit('stdout', data); + if (!optionsNonNull.silent) { + optionsNonNull.outStream.write(data); + } + this._processLineBuffer(data, stdbuffer, (line: string) => { this.emit('stdline', line); }); From 002fff704346591aafe6f8decd5ab4cc9521c094 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Mon, 11 Mar 2019 16:03:57 -0400 Subject: [PATCH 124/259] Bump to 3.0.0-preview --- node/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/package.json b/node/package.json index aa015a3ee..1ee74b791 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.8.0", + "version": "3.0.0-preview", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 73738571847f04faa6f71549c4ced66a91058705 Mon Sep 17 00:00:00 2001 From: Brian Cristante Date: Tue, 12 Mar 2019 08:54:51 -0400 Subject: [PATCH 125/259] Add enums to mock-task --- node/mock-task.ts | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/node/mock-task.ts b/node/mock-task.ts index c5b30c41d..434f2fdbc 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -2,8 +2,6 @@ import Q = require('q'); import path = require('path'); import fs = require('fs'); -import os = require('os'); -import util = require('util'); import task = require('./task'); import tcm = require('./taskcommand'); import trm = require('./mock-toolrunner'); @@ -16,29 +14,23 @@ export function setAnswers(answers: ma.TaskLibAnswers) { trm.setAnswers(answers); } -module.exports.TaskResult = task.TaskResult; - //----------------------------------------------------- -// General Helpers +// Enums //----------------------------------------------------- -let _outStream = process.stdout; -let _errStream = process.stderr; - -function _writeError(str: string): void { - _errStream.write(str + os.EOL); -} - -function _writeLine(str: string): void { - _outStream.write(str + os.EOL); -} -module.exports.setStdStream = task.setStdStream; -module.exports.setErrStream = task.setErrStream; +module.exports.TaskResult = task.TaskResult; +module.exports.TaskState = task.TaskState; +module.exports.IssueType = task.IssueType; +module.exports.ArtifactType = task.ArtifactType; +module.exports.FieldType = task.FieldType; +module.exports.Platform = task.Platform; //----------------------------------------------------- // Results and Exiting //----------------------------------------------------- +module.exports.setStdStream = task.setStdStream; +module.exports.setErrStream = task.setErrStream; module.exports.setResult = task.setResult; //----------------------------------------------------- From f41749c756fb8865c546b65ccfde910699b55ed1 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Wed, 13 Mar 2019 15:55:07 -0400 Subject: [PATCH 126/259] Detect correct node handler (#503) * Allow testing with handlers other than node 10 * Fix imports * Update based on discussion * Fix up tests * Get rid of bad logging * Add doc explaining change * Add types * nit: unused import * Respond to feedback * Respond to feedback * Bump major version to 3.0.0-preview * Update package-lock.json * Use parameters instead of env variables * Optional parameter * Feedback * Use chdir * Remove shelljs dep * add back shelljs dep * Add marker file * Clean files before downloading * Update mock-test.ts * React to strict null checking * Clean directory before installing node --- node/docs/nodeVersioning.md | 20 +++ node/make.js | 1 + node/mock-test.ts | 207 +++++++++++++++++++++-- node/package-lock.json | 74 ++++---- node/test/fakeTasks/node10task/task.json | 9 + node/test/fakeTasks/node6task/task.json | 9 + node/test/mocktests.ts | 23 +++ 7 files changed, 295 insertions(+), 48 deletions(-) create mode 100644 node/docs/nodeVersioning.md create mode 100644 node/test/fakeTasks/node10task/task.json create mode 100644 node/test/fakeTasks/node6task/task.json diff --git a/node/docs/nodeVersioning.md b/node/docs/nodeVersioning.md new file mode 100644 index 000000000..11eb39676 --- /dev/null +++ b/node/docs/nodeVersioning.md @@ -0,0 +1,20 @@ +# Node versioning + +## Agent Node Handler + +The agent currently has 2 different node handlers that it can use to execute node tasks: Node 6 and Node 10. +The handler used depends on the `execution` property specified in the tasks `task.json`. +If the `execution` property is specified to be `Node`, the task will run on the Node 6 handler, if it is specified to be `Node10` it will run on the Node 10 handler. + +## Mock-test Node Handler + +[Unit testing](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops#step-2-unit-testing-your-task-scripts) of tasks can be done using the task-lib's built in mock-task functionality. +To ensure tests are run in the same environment as the agent, this library looks for a `task.json` file in the same directory as the supplied task entry point. +If no `task.json` is found it searches all ancestor directories as well. +If the `task.json` is still not found, the library defaults to node 10, otherwise it uses the appropriate handler based on the `execution` property. +If this version of node is not found on the path, the library downloads the appropriate version. + +### Behavior overrides + +To specify a specific version of node to use, set the `nodeVersion` optional parameter in the `run` function of the `MockTestRunner` to the integer major version (e.g. `mtr.run(5)`). +To specify the location of a `task.json` file, set the `taskJsonPath` optional parameter in the `MockTestRunner` constructor to the path of the file (e.g. `let mtr = new mt.MockTaskRunner('', ''`). \ No newline at end of file diff --git a/node/make.js b/node/make.js index 2071dbc54..6e914da41 100644 --- a/node/make.js +++ b/node/make.js @@ -46,6 +46,7 @@ target.test = function() { buildutils.getExternals(); run('tsc -p ./test'); cp('-Rf', rp('test/scripts'), testPath); + cp('-Rf', rp('test/fakeTasks'), testPath); process.env['TASKLIB_INPROC_UNITS'] = '1'; // export task-lib internals for internal unit testing run('mocha ' + testPath); } diff --git a/node/mock-test.ts b/node/mock-test.ts index e33fdd424..308128279 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -1,19 +1,28 @@ import cp = require('child_process'); import fs = require('fs'); -import path = require('path'); +import ncp = require('child_process'); import os = require('os'); +import path = require('path'); import cmdm = require('./taskcommand'); import shelljs = require('shelljs'); +import syncRequest = require('sync-request'); const COMMAND_TAG = '[command]'; const COMMAND_LENGTH = COMMAND_TAG.length; +const downloadDirectory = path.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE, 'azure-pipelines-task-lib', '_download'); export class MockTestRunner { - constructor(private _testPath: string) { + constructor(testPath: string, taskJsonPath?: string) { + this._taskJsonPath = taskJsonPath || ''; + this._testPath = testPath; + this.nodePath = this.getNodePath(); } + private _testPath = ''; + private _taskJsonPath = ''; + public nodePath = ''; public stdout = ''; - public stderr = '' + public stderr = ''; public cmdlines = {}; public invokedToolCount = 0; public succeeded = false; @@ -44,7 +53,7 @@ export class MockTestRunner { return this.stderr.indexOf(message) > 0; } - public run(): void { + public run(nodeVersion?: number): void { this.cmdlines = {}; this.invokedToolCount = 0; this.succeeded = true; @@ -52,14 +61,10 @@ export class MockTestRunner { this.errorIssues = []; this.warningIssues = []; - // we use node in the path. - // if you want to test with a specific node, ensure it's in the path - let nodePath = shelljs.which('node'); - if (!nodePath) { - console.error('Could not find node in path'); - return; + let nodePath = this.nodePath; + if (nodeVersion) { + nodePath = this.getNodePath(nodeVersion); } - let spawn = cp.spawnSync(nodePath, [this._testPath]); if (spawn.error) { console.error('Running test failed'); @@ -123,4 +128,184 @@ export class MockTestRunner { console.log('TRACE FILE: ' + traceFile); } } + + // Returns a path to node.exe with the correct version for this task (based on if its node10 or node) + private getNodePath(nodeVersion?: number): string { + const version: number = nodeVersion || this.getNodeVersion(); + + let downloadVersion: string; + switch (version) { + case 5: + downloadVersion = 'v5.10.1'; + break; + case 6: + downloadVersion = 'v6.10.3'; + break; + case 10: + downloadVersion = 'v10.15.1'; + break; + default: + throw new Error('Invalid node version, must be 5, 6, or 10 (received ' + version + ')'); + } + + // Install node in home directory if it isn't already there. + const downloadDestination: string = path.join(downloadDirectory, 'node' + version); + const pathToExe: string = this.getPathToNodeExe(downloadVersion, downloadDestination); + if (pathToExe) { + return pathToExe; + } + else { + return this.downloadNode(downloadVersion, downloadDestination); + } + } + + // Determines the correct version of node to use based on the contents of the task's task.json. Defaults to Node 10. + private getNodeVersion(): number { + const taskJsonPath: string = this.getTaskJsonPath(); + if (!taskJsonPath) { + console.warn('Unable to find task.json, defaulting to use Node 10'); + return 10; + } + const taskJsonContents = fs.readFileSync(taskJsonPath, { encoding: 'utf-8' }); + const taskJson: object = JSON.parse(taskJsonContents); + + let nodeVersionFound = false; + const execution: object = taskJson['execution']; + const keys = Object.keys(execution); + for (let i = 0; i < keys.length; i++) { + if (keys[i].toLowerCase() == 'node10') { + // Prefer node 10 and return immediately. + return 10; + } + else if (keys[i].toLowerCase() == 'node') { + nodeVersionFound = true; + } + } + + if (!nodeVersionFound) { + console.warn('Unable to determine execution type from task.json, defaulting to use Node 10'); + return 10; + } + + return 6; + } + + // Returns the path to the task.json for the task being tested. Returns null if unable to find it. + // Searches by moving up the directory structure from the initial starting point and checking at each level. + private getTaskJsonPath(): string { + if (this._taskJsonPath) { + return this._taskJsonPath; + } + let curPath: string = this._testPath; + let newPath: string = path.join(this._testPath, '..'); + while (curPath != newPath) { + curPath = newPath; + let taskJsonPath: string = path.join(curPath, 'task.json'); + if (fs.existsSync(taskJsonPath)) { + return taskJsonPath; + } + newPath = path.join(curPath, '..'); + } + return ''; + } + + // Downloads the specified node version to the download destination. Returns a path to node.exe + private downloadNode(nodeVersion: string, downloadDestination: string): string { + shelljs.rm('-rf', downloadDestination); + const nodeUrl: string = 'https://nodejs.org/dist'; + let downloadPath = ''; + switch (this.getPlatform()) { + case 'darwin': + this.downloadTarGz(nodeUrl + '/' + nodeVersion + '/node-' + nodeVersion + '-darwin-x64.tar.gz', downloadDestination); + downloadPath = path.join(downloadDestination, 'node-' + nodeVersion + '-darwin-x64', 'bin', 'node'); + break; + case 'linux': + this.downloadTarGz(nodeUrl + '/' + nodeVersion + '/node-' + nodeVersion + '-linux-x64.tar.gz', downloadDestination); + downloadPath = path.join(downloadDestination, 'node-' + nodeVersion + '-linux-x64', 'bin', 'node'); + break; + case 'win32': + this.downloadFile(nodeUrl + '/' + nodeVersion + '/win-x64/node.exe', downloadDestination, 'node.exe'); + this.downloadFile(nodeUrl + '/' + nodeVersion + '/win-x64/node.lib', downloadDestination, 'node.lib'); + downloadPath = path.join(downloadDestination, 'node.exe') + } + + // Write marker to indicate download completed. + const marker = downloadDestination + '.completed'; + fs.writeFileSync(marker, ''); + + return downloadPath; + } + + // Downloads file to the downloadDestination, making any necessary folders along the way. + private downloadFile(url: string, downloadDestination: string, fileName: string): void { + const filePath: string = path.join(downloadDestination, fileName); + if (!url) { + throw new Error('Parameter "url" must be set.'); + } + if (!downloadDestination) { + throw new Error('Parameter "downloadDestination" must be set.'); + } + console.log('Downloading file:', url); + shelljs.mkdir('-p', downloadDestination); + const result: any = syncRequest('GET', url); + fs.writeFileSync(filePath, result.getBody()); + } + + // Downloads tarGz to the download destination, making any necessary folders along the way. + private downloadTarGz(url: string, downloadDestination: string): void { + if (!url) { + throw new Error('Parameter "url" must be set.'); + } + if (!downloadDestination) { + throw new Error('Parameter "downloadDestination" must be set.'); + } + const tarGzName: string = 'node.tar.gz'; + this.downloadFile(url, downloadDestination, tarGzName); + + // Extract file + const originalCwd: string = process.cwd(); + process.chdir(downloadDestination); + try { + ncp.execSync(`tar -xzf "${path.join(downloadDestination, tarGzName)}"`); + } + catch { + throw new Error('Failed to unzip node tar.gz from ' + url); + } + finally { + process.chdir(originalCwd); + } + } + + // Checks if node is installed at downloadDestination. If it is, returns a path to node.exe, otherwise returns null. + private getPathToNodeExe(nodeVersion: string, downloadDestination: string): string { + let exePath = ''; + switch (this.getPlatform()) { + case 'darwin': + exePath = path.join(downloadDestination, 'node-' + nodeVersion + '-darwin-x64', 'bin', 'node'); + break; + case 'linux': + exePath = path.join(downloadDestination, 'node-' + nodeVersion + '-linux-x64', 'bin', 'node'); + break; + case 'win32': + exePath = path.join(downloadDestination, 'node.exe'); + } + + // Only use path if marker is found indicating download completed successfully (and not partially) + const marker = downloadDestination + '.completed'; + + if (fs.existsSync(exePath) && fs.existsSync(marker)) { + return exePath; + } + else { + return ''; + } + } + + private getPlatform(): string { + let platform: string = os.platform(); + if (platform != 'darwin' && platform != 'linux' && platform != 'win32') { + throw new Error('Unexpected platform: ' + platform); + } + return platform; + } } diff --git a/node/package-lock.json b/node/package-lock.json index d718580af..ef1521d61 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.8.0", + "version": "3.0.0-preview", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -20,7 +20,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -47,9 +47,9 @@ "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.5", - "typedarray": "0.0.6" + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, "core-util-is": { @@ -85,12 +85,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "he": { @@ -105,9 +105,9 @@ "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", "dev": true, "requires": { - "caseless": "0.11.0", - "concat-stream": "1.6.1", - "http-response-object": "1.1.0" + "caseless": "~0.11.0", + "concat-stream": "^1.4.6", + "http-response-object": "^1.0.0" } }, "http-response-object": { @@ -122,8 +122,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -143,7 +143,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -210,7 +210,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -232,7 +232,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "path-is-absolute": { @@ -253,7 +253,7 @@ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dev": true, "requires": { - "asap": "2.0.6" + "asap": "~2.0.3" } }, "q": { @@ -273,13 +273,13 @@ "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "safe-buffer": { @@ -304,7 +304,7 @@ "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "sync-request": { @@ -313,9 +313,9 @@ "integrity": "sha1-yqEjWq+Im6UBB2oYNMQ2gwqC+3M=", "dev": true, "requires": { - "concat-stream": "1.6.1", - "http-response-object": "1.1.0", - "then-request": "2.2.0" + "concat-stream": "^1.4.7", + "http-response-object": "^1.0.1", + "then-request": "^2.0.1" } }, "then-request": { @@ -324,12 +324,12 @@ "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", "dev": true, "requires": { - "caseless": "0.11.0", - "concat-stream": "1.6.1", - "http-basic": "2.5.1", - "http-response-object": "1.1.0", - "promise": "7.3.1", - "qs": "6.5.1" + "caseless": "~0.11.0", + "concat-stream": "^1.4.7", + "http-basic": "^2.5.1", + "http-response-object": "^1.1.0", + "promise": "^7.1.1", + "qs": "^6.1.0" } }, "typedarray": { diff --git a/node/test/fakeTasks/node10task/task.json b/node/test/fakeTasks/node10task/task.json new file mode 100644 index 000000000..9345cf49f --- /dev/null +++ b/node/test/fakeTasks/node10task/task.json @@ -0,0 +1,9 @@ +{ + "id": "id", + "name": "Node10Task", + "execution": { + "Node10": { + "target": "usedotnet.js" + } + } +} diff --git a/node/test/fakeTasks/node6task/task.json b/node/test/fakeTasks/node6task/task.json new file mode 100644 index 000000000..2cb7e45e2 --- /dev/null +++ b/node/test/fakeTasks/node6task/task.json @@ -0,0 +1,9 @@ +{ + "id": "id", + "name": "Node6Task", + "execution": { + "Node": { + "target": "usedotnet.js" + } + } +} diff --git a/node/test/mocktests.ts b/node/test/mocktests.ts index cc140a756..74e4052db 100644 --- a/node/test/mocktests.ts +++ b/node/test/mocktests.ts @@ -11,7 +11,10 @@ import * as mtr from '../_build/mock-toolrunner'; import * as ma from '../_build/mock-answer'; import * as tl from '../_build/task'; +import ncp = require('child_process'); import os = require('os'); +import path = require('path'); +import semver = require('semver'); import testutil = require('./testutil'); describe('Mock Tests', function () { @@ -280,4 +283,24 @@ describe('Mock Tests', function () { assert.equal(numStdLineCalls, 1); assert.equal(numStdErrCalls, 1); }) + + it('MockTest handles node 6 tasks correctly', function (done) { + this.timeout(10000); + 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'); + const version = ncp.execSync(nodePath + ' -v').toString().trim(); + assert(semver.satisfies(version, '6.x'), 'Downloaded node version should be Node 6 instead of ' + version); + done(); + }) + + it('MockTest handles node 10 tasks correctly', function (done) { + this.timeout(10000); + 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'); + const version = ncp.execSync(nodePath + ' -v').toString().trim(); + assert(semver.satisfies(version, '10.x'), 'Downloaded node version should be Node 10 instead of ' + version); + done(); + }) }); From a1bbaef0bcfaa7475ab80ab34c9797e955829c7b Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Fri, 15 Mar 2019 09:19:42 -0400 Subject: [PATCH 127/259] Run ci on changes to releases branch --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c3ce11366..478cc0144 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,6 +1,7 @@ trigger: - master - features/* +- releases/* jobs: ################################################# From cad60509d6abb7fc5ae1501e11f01a601c996a81 Mon Sep 17 00:00:00 2001 From: Chris Sidi Date: Sat, 23 Mar 2019 10:39:17 -0400 Subject: [PATCH 128/259] Add setSecret to mock-task. (#512) --- node/mock-task.ts | 1 + node/package-lock.json | 2 +- node/package.json | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/node/mock-task.ts b/node/mock-task.ts index e4a993b2b..337156f7c 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -54,6 +54,7 @@ export function loc(key: string, ...args: any[]): string { //----------------------------------------------------- module.exports.getVariable = task.getVariable; module.exports.setVariable = task.setVariable; +module.exports.setSecret = task.setSecret; module.exports.getTaskVariable = task.getTaskVariable; module.exports.setTaskVariable = task.setTaskVariable; module.exports.getInput = task.getInput; diff --git a/node/package-lock.json b/node/package-lock.json index ef1521d61..adc04c628 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.0.0-preview", + "version": "3.0.1-preview", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 1ee74b791..48e940765 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.0.0-preview", + "version": "3.0.1-preview", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", @@ -27,10 +27,10 @@ "dependencies": { "minimatch": "3.0.4", "mockery": "^1.7.0", - "uuid": "^3.0.1", "q": "^1.1.2", "semver": "^5.1.0", - "shelljs": "^0.3.0" + "shelljs": "^0.3.0", + "uuid": "^3.0.1" }, "devDependencies": { "mocha": "5.2.0", From 11c9439d4af17e6475d9fe058e6b2e03914d17e6 Mon Sep 17 00:00:00 2001 From: Chris Sidi Date: Mon, 25 Mar 2019 10:12:07 -0400 Subject: [PATCH 129/259] Mock every exported function in task-lib. (#514) * Mock every exported function in task-lib. * mock `legacyFindFiles` as well. --- node/mock-answer.ts | 3 +++ node/mock-task.ts | 40 ++++++++++++++++++++++++++++++++++++++++ node/package-lock.json | 2 +- node/package.json | 2 +- node/test/mocktests.ts | 27 ++++++++++++++++++++++----- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/node/mock-answer.ts b/node/mock-answer.ts index 0bebacc00..338e62c2c 100644 --- a/node/mock-answer.ts +++ b/node/mock-answer.ts @@ -1,5 +1,6 @@ import * as path from 'path'; import * as fs from 'fs'; +import * as task from './task'; export interface TaskLibAnswerExecResult { code: number, @@ -14,6 +15,8 @@ export interface TaskLibAnswers { exist?: { [key: string]: boolean }, find?: { [key: string]: string[] }, findMatch?: { [key: string]: string[] }, + getPlatform?: { [key: string]: task.Platform }, + legacyFindFiles?: { [key: string]: string[] }, ls?: { [key: string]: string }, osType?: { [key: string]: string }, rmRF?: { [key: string]: { success: boolean } }, diff --git a/node/mock-task.ts b/node/mock-task.ts index 337156f7c..1f062e9d6 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -52,7 +52,9 @@ export function loc(key: string, ...args: any[]): string { //----------------------------------------------------- // Input Helpers //----------------------------------------------------- +module.exports.assertAgent = task.assertAgent; module.exports.getVariable = task.getVariable; +module.exports.getVariables = task.getVariables; module.exports.setVariable = task.setVariable; module.exports.setSecret = task.setSecret; module.exports.getTaskVariable = task.getTaskVariable; @@ -202,6 +204,10 @@ export function osType(): string { return mock.getResponse('osType', 'osType', module.exports.debug); } +export function getPlatform(): task.Platform { + return mock.getResponse('getPlatform', 'getPlatform', module.exports.debug); +} + export function cwd(): string { return mock.getResponse('cwd', 'cwd', module.exports.debug); } @@ -351,6 +357,10 @@ export function findMatch(defaultRoot: string, patterns: string[] | string) : st return mock.getResponse('findMatch', responseKey, module.exports.debug); } +export function legacyFindFiles(rootDirectory: string, pattern: string, includeFiles?: boolean, includeDirectories?: boolean) : string[] { + return mock.getResponse('legacyFindFiles', pattern, module.exports.debug); +} + //----------------------------------------------------- // Test Publisher //----------------------------------------------------- @@ -440,6 +450,36 @@ export class CodeCoverageEnabler { } } +//----------------------------------------------------- +// Task Logging Commands +//----------------------------------------------------- +exports.uploadFile = task.uploadFile; +exports.prependPath = task.prependPath; +exports.uploadSummary = task.uploadSummary; +exports.addAttachment = task.addAttachment; +exports.setEndpoint = task.setEndpoint; +exports.setProgress = task.setProgress; +exports.logDetail = task.logDetail; +exports.logIssue = task.logIssue; + +//----------------------------------------------------- +// Artifact Logging Commands +//----------------------------------------------------- +exports.uploadArtifact = task.uploadArtifact; +exports.associateArtifact = task.associateArtifact; + +//----------------------------------------------------- +// Build Logging Commands +//----------------------------------------------------- +exports.uploadBuildLog = task.uploadBuildLog; +exports.updateBuildNumber = task.updateBuildNumber; +exports.addBuildTag = task.addBuildTag; + +//----------------------------------------------------- +// Release Logging Commands +//----------------------------------------------------- +exports.updateReleaseName = task.updateReleaseName; + //----------------------------------------------------- // Tools //----------------------------------------------------- diff --git a/node/package-lock.json b/node/package-lock.json index adc04c628..2765c4b36 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.0.1-preview", + "version": "3.0.2-preview", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 48e940765..808373572 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.0.1-preview", + "version": "3.0.2-preview", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/test/mocktests.ts b/node/test/mocktests.ts index 74e4052db..19a783227 100644 --- a/node/test/mocktests.ts +++ b/node/test/mocktests.ts @@ -33,6 +33,23 @@ describe('Mock Tests', function () { }); + // Verify mock-task exports all the exported functions exported by task. If a task-lib function isn't mocked, + // it's difficult to use in a task with unit tests. + it('mock-task exports every function in task', (done) => { + for (let memberName of Object.keys(tl)) { + const member = tl[memberName]; + + if (typeof member === 'function') { + const mockMember = mt[memberName]; + if (!mockMember || typeof mockMember !== typeof member) { + assert.fail(`mock-task missing function exported by task: "${memberName}"`); + } + } + } + + done(); + }); + it('Mocks which and returns path on exists', (done) => { var a: ma.TaskLibAnswers = { "which": { @@ -184,11 +201,11 @@ describe('Mock Tests', function () { tool.arg('--arg'); tool.arg('foo'); let rc: number = await tool.exec({}); - + assert(tool, "tool should not be null"); assert(rc == 0, "rc is 0"); }) - + it('Mock toolRunner returns correct output', async () => { const expectedStdout = "atool output here" + os.EOL + "abc"; const expectedStderr = "atool with this stderr output" + os.EOL + "def"; @@ -239,11 +256,11 @@ describe('Mock Tests', function () { } }); await tool.exec({}); - + assert.equal(numStdLineCalls, 2); assert.equal(numStdErrCalls, 2); }) - + it('Mock toolRunner returns correct output when ending on EOL', async () => { const expectedStdout = os.EOL; const expectedStderr = os.EOL; @@ -279,7 +296,7 @@ describe('Mock Tests', function () { assert.equal("", out); }); await tool.exec({}); - + assert.equal(numStdLineCalls, 1); assert.equal(numStdErrCalls, 1); }) From 3fb907124e461cb7dc757200266d57f6ac743c8e Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Mon, 8 Apr 2019 09:27:17 -0400 Subject: [PATCH 130/259] Fix infinite loop (#483) * Fix infinite loop * If loc file not found, don't try to loc * Spacing --- node/internal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/internal.ts b/node/internal.ts index 606747d4e..1ebe291c3 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -131,7 +131,7 @@ function _loadLocStrings(resourceFile: string, culture: string): { [key: string] } } else { - _warning(_loc('LIB_ResourceFileNotExist', resourceFile)); + _warning('LIB_ResourceFile does not exist'); } return locStrings; From 059405c3ce46f6675630ef9224d3b3bfa65a868d Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Mon, 8 Apr 2019 16:45:58 -0400 Subject: [PATCH 131/259] Clean environment after running mocks --- node/mock-test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/node/mock-test.ts b/node/mock-test.ts index 308128279..89fd824fc 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -66,6 +66,12 @@ export class MockTestRunner { nodePath = this.getNodePath(nodeVersion); } let spawn = cp.spawnSync(nodePath, [this._testPath]); + // Clean environment + Object.keys(process.env) + .filter(key => (key.substr(0, 'INPUT_'.length) === 'INPUT_' || + key.substr(0, 'SECRET_'.length) === 'SECRET_' || + key.substr(0, 'VSTS_TASKVARIABLE_'.length) === 'VSTS_TASKVARIABLE_')) + .forEach(key => delete process.env[key]); if (spawn.error) { console.error('Running test failed'); console.error(spawn.error.message); From 385f26750d6a533758bfab872c82f5af4255e360 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Mon, 8 Apr 2019 16:47:07 -0400 Subject: [PATCH 132/259] Revert "Clean environment after running mocks" Accidentally committed to master instead of branch --- node/mock-test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/node/mock-test.ts b/node/mock-test.ts index 89fd824fc..308128279 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -66,12 +66,6 @@ export class MockTestRunner { nodePath = this.getNodePath(nodeVersion); } let spawn = cp.spawnSync(nodePath, [this._testPath]); - // Clean environment - Object.keys(process.env) - .filter(key => (key.substr(0, 'INPUT_'.length) === 'INPUT_' || - key.substr(0, 'SECRET_'.length) === 'SECRET_' || - key.substr(0, 'VSTS_TASKVARIABLE_'.length) === 'VSTS_TASKVARIABLE_')) - .forEach(key => delete process.env[key]); if (spawn.error) { console.error('Running test failed'); console.error(spawn.error.message); From e5a622a92dd95b77b30daf5ce8ae8095301d2a2c Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 9 Apr 2019 09:06:06 -0400 Subject: [PATCH 133/259] Clean environment after running mocks (#526) --- node/mock-test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/node/mock-test.ts b/node/mock-test.ts index 308128279..806dac026 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -66,6 +66,14 @@ export class MockTestRunner { nodePath = this.getNodePath(nodeVersion); } let spawn = cp.spawnSync(nodePath, [this._testPath]); + + // Clean environment + Object.keys(process.env) + .filter(key => (key.substr(0, 'INPUT_'.length) === 'INPUT_' || + key.substr(0, 'SECRET_'.length) === 'SECRET_' || + key.substr(0, 'VSTS_TASKVARIABLE_'.length) === 'VSTS_TASKVARIABLE_')) + .forEach(key => delete process.env[key]); + if (spawn.error) { console.error('Running test failed'); console.error(spawn.error.message); From ca5952dbbc7607ecdfccf18b741323d730d62199 Mon Sep 17 00:00:00 2001 From: Jesse Houwing Date: Thu, 11 Apr 2019 16:14:47 +0200 Subject: [PATCH 134/259] Adds Node10 and PowerShell execution handlers. (#528) --- tasks.schema.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tasks.schema.json b/tasks.schema.json index 230c367e0..3bfbb9cc5 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -264,11 +264,17 @@ "additionalProperties": false, "description": "Execution options for this task", "properties": { + "Node10": { + "$ref": "#/definitions/executionObject" + }, "Node": { "$ref": "#/definitions/executionObject" }, "PowerShell3": { "$ref": "#/definitions/executionObject" + }, + "PowerShell": { + "$ref": "#/definitions/executionObject" } } }, From 478c98392966361a803513b3d5175406571e4fea Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Wed, 1 May 2019 16:05:19 -0400 Subject: [PATCH 135/259] Enable large numbers of children to be pushed in find (#509) * Get rid of ... operator and reverse * Update package.json * Update package-lock.json --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 2765c4b36..cfe838fdb 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.0.2-preview", + "version": "3.1.0-preview", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 808373572..f4869b236 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.0.2-preview", + "version": "3.1.0-preview", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index f4619ad33..f08722982 100644 --- a/node/task.ts +++ b/node/task.ts @@ -918,7 +918,9 @@ export function find(findPath: string, options?: FindOptions): string[] { let childItems: _FindItem[] = fs.readdirSync(item.path) .map((childName: string) => new _FindItem(path.join(item.path, childName), childLevel)); - stack.push(...childItems.reverse()); + for (var i = childItems.length - 1; i >= 0; i--) { + stack.push(childItems[i]); + } } else { debug(` ${item.path} (file)`); From 198b22ef9ba5b13eeb95fd68efc74ec101720e4e Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Wed, 8 May 2019 12:09:19 -0400 Subject: [PATCH 136/259] Add Node environment doc (#533) --- node/docs/nodeEnvironment.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 node/docs/nodeEnvironment.md diff --git a/node/docs/nodeEnvironment.md b/node/docs/nodeEnvironment.md new file mode 100644 index 000000000..0bbd140da --- /dev/null +++ b/node/docs/nodeEnvironment.md @@ -0,0 +1,19 @@ +# Node Handler Versioning + +As of agent version 2.144.0, Azure Pipelines supports running tasks in a Node 10 environment in addition to the previously supported Node 6 environment. + +To leverage this capability, simply add `Node10` as an execution target: + +``` +"execution": { + "Node10": { + "target": "path/to/entry" + } + }, +``` + +Existing `Node` execution targets will still resolve to a Node 6 environment for now to maintain back-compat. + +### Testing your task + +If you use the task-lib for testing, it will automatically use the appropriate version of Node to test your tasks, downloading it if necessary. From 2f33a98ba5ba53488286e0f4c5120e014193c91d Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Fri, 24 May 2019 09:37:57 -0400 Subject: [PATCH 137/259] Drop back down to 2.9.0 - not major versioning --- node/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/package.json b/node/package.json index f4869b236..79d191a32 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.0-preview", + "version": "2.9.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 082990798aa65203ddbfe9de9d8c078f7a05be84 Mon Sep 17 00:00:00 2001 From: Eriawan Kusumawardhono Date: Mon, 24 Jun 2019 19:16:56 +0700 Subject: [PATCH 138/259] Update link to vsts-task to use the newer name of azure-pipelines-tasks (#545) Update link to vsts-task to use the newer repo name of vsts-tasks, the azure-pipelines-tasks --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f9820aa1b..901b40950 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Libraries for writing [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) tasks -Reference examples of our in the box tasks [are here](https://github.com/Microsoft/vsts-tasks) +Reference examples of our in the box tasks [are here](https://github.com/microsoft/azure-pipelines-tasks) ## Status From ae939a5f51e1c2caa5aaf3f24c67394fa7d3ec85 Mon Sep 17 00:00:00 2001 From: "Zach Renner (MSFT)" Date: Wed, 10 Jul 2019 06:22:53 -0700 Subject: [PATCH 139/259] Remove endpoint authorization non-debug logging (SYSTEMVSSCONNECTION exists true). Bump to 2.9.1. (#553) --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index cfe838fdb..643113927 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.0-preview", + "version": "2.9.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 79d191a32..8b1418c8c 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.0", + "version": "2.9.1", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index f08722982..2849f177e 100644 --- a/node/task.ts +++ b/node/task.ts @@ -420,7 +420,6 @@ export function getEndpointAuthorization(id: string, optional: boolean): Endpoin setResult(TaskResult.Failed, loc('LIB_EndpointAuthNotExist', id)); } - console.log(id + ' exists ' + (aval !== null)); debug(id + ' exists ' + (aval !== null)); var auth: EndpointAuthorization | undefined; From dd18c6a10c444885c117ea29bb1ca2f15e5637f5 Mon Sep 17 00:00:00 2001 From: Ian Kemp Date: Mon, 19 Aug 2019 13:11:36 +0100 Subject: [PATCH 140/259] correct issue (#564) --- node/task.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/task.ts b/node/task.ts index 2849f177e..024f2388b 100644 --- a/node/task.ts +++ b/node/task.ts @@ -232,10 +232,11 @@ export function getInput(name: string, required?: boolean): string | undefined { /** * Gets the value of an input and converts to a bool. Convenience. * If required is true and the value is not set, it will throw. + * If required is false and the value is not set, returns false. * * @param name name of the bool input to get * @param required whether input is required. optional, defaults to false - * @returns string + * @returns boolean */ export function getBoolInput(name: string, required?: boolean): boolean { return (getInput(name, required) || '').toUpperCase() == "TRUE"; From d461757689fcb070d16f8ff7dcc6a35a1898cb20 Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Wed, 28 Aug 2019 14:23:30 -0400 Subject: [PATCH 141/259] Fixed an issue were output from command without newline was dropped Reported as an issue in the agent: https://github.com/microsoft/azure-pipelines-agent/issues/2242 --- node/package-lock.json | 2 +- node/package.json | 2 +- node/toolrunner.ts | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 643113927..79ee05efc 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.1", + "version": "2.9.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 8b1418c8c..125153d34 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.1", + "version": "2.9.2", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/toolrunner.ts b/node/toolrunner.ts index bf34ebeff..c79afac63 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -800,6 +800,15 @@ export class ToolRunner extends events.EventEmitter { let cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(options)); + // it is possible for the child process to end its last line without a new line. + // because stdout is buffered, this causes the last line to not get sent to the parent + // stream. Adding this event forces a flush before the child streams are closed. + cp.stdout.on('finish', () => { + if (!optionsNonNull.silent) { + optionsNonNull.outStream.write(os.EOL); + } + }); + var stdbuffer: string = ''; cp.stdout.on('data', (data: Buffer) => { this.emit('stdout', data); @@ -813,6 +822,7 @@ export class ToolRunner extends events.EventEmitter { }); }); + var errbuffer: string = ''; cp.stderr.on('data', (data: Buffer) => { state.processStderr = true; From d9fafa90bf7eb983ee0425a4f22b1b415eabbee3 Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Wed, 28 Aug 2019 14:52:00 -0400 Subject: [PATCH 142/259] In some instances (with pipes) adding the \n breaks things. Making this a controllable option for now. --- node/toolrunner.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index c79afac63..b79c4f871 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -17,6 +17,9 @@ export interface IExecOptions extends IExecSyncOptions { /** optional. defaults to failing on non zero. ignore will not fail leaving it up to the caller */ ignoreReturnCode?: boolean; + + /** optional. force flush of stdout. defaults to false */ + forceFlushStdOut?: boolean; } /** @@ -803,11 +806,13 @@ export class ToolRunner extends events.EventEmitter { // it is possible for the child process to end its last line without a new line. // because stdout is buffered, this causes the last line to not get sent to the parent // stream. Adding this event forces a flush before the child streams are closed. - cp.stdout.on('finish', () => { - if (!optionsNonNull.silent) { - optionsNonNull.outStream.write(os.EOL); - } - }); + if (optionsNonNull.forceFlushStdOut) { + cp.stdout.on('finish', () => { + if (!optionsNonNull.silent) { + optionsNonNull.outStream.write(os.EOL); + } + }); + } var stdbuffer: string = ''; cp.stdout.on('data', (data: Buffer) => { From e5484f9bdf08231749b33c9714225ca48a5d4251 Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Wed, 28 Aug 2019 15:09:45 -0400 Subject: [PATCH 143/259] Back out force flag since failing test is flaky --- node/toolrunner.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index b79c4f871..c79afac63 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -17,9 +17,6 @@ export interface IExecOptions extends IExecSyncOptions { /** optional. defaults to failing on non zero. ignore will not fail leaving it up to the caller */ ignoreReturnCode?: boolean; - - /** optional. force flush of stdout. defaults to false */ - forceFlushStdOut?: boolean; } /** @@ -806,13 +803,11 @@ export class ToolRunner extends events.EventEmitter { // it is possible for the child process to end its last line without a new line. // because stdout is buffered, this causes the last line to not get sent to the parent // stream. Adding this event forces a flush before the child streams are closed. - if (optionsNonNull.forceFlushStdOut) { - cp.stdout.on('finish', () => { - if (!optionsNonNull.silent) { - optionsNonNull.outStream.write(os.EOL); - } - }); - } + cp.stdout.on('finish', () => { + if (!optionsNonNull.silent) { + optionsNonNull.outStream.write(os.EOL); + } + }); var stdbuffer: string = ''; cp.stdout.on('data', (data: Buffer) => { From 363167bb8c063e1a06ac2abf19b2195bd05bf9ea Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Wed, 28 Aug 2019 15:45:34 -0400 Subject: [PATCH 144/259] Renamed one of tests with same name to identify which one is failing --- node/test/toolrunnertests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index f32b5bb8e..25c696ce5 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -1011,7 +1011,7 @@ describe('Toolrunner Tests', function () { }) } }) - it('Exec pipe output to another tool, fails if second tool fails', function (done) { + it('Exec pipe output to another tool, fails if second tool fails (with file syncs)', function (done) { this.timeout(20000); var _testExecOptions = { From c70415945c8282ee86305e12c135b93987d353d1 Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Wed, 28 Aug 2019 15:57:16 -0400 Subject: [PATCH 145/259] Properly renamed duplicate tests --- node/test/toolrunnertests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 25c696ce5..64105ab0e 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -921,7 +921,7 @@ describe('Toolrunner Tests', function () { }); } }) - it('Exec pipe output to another tool, fails if first tool fails', function (done) { + it('Exec pipe output to file and another tool, fails if first tool fails', function (done) { this.timeout(20000); var _testExecOptions = { @@ -1011,7 +1011,7 @@ describe('Toolrunner Tests', function () { }) } }) - it('Exec pipe output to another tool, fails if second tool fails (with file syncs)', function (done) { + it('Exec pipe output to file and another tool, fails if second tool fails', function (done) { this.timeout(20000); var _testExecOptions = { From 40b976302a792bcb76454c81ee943074a0413014 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 5 Sep 2019 10:15:38 -0400 Subject: [PATCH 146/259] Escape % signs (#543) * Escape % signs * Fix functionality and add testing --- node/taskcommand.ts | 12 ++++++++---- node/test/commandtests.ts | 8 ++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/node/taskcommand.ts b/node/taskcommand.ts index 1634bac0b..ceed1bbe5 100644 --- a/node/taskcommand.ts +++ b/node/taskcommand.ts @@ -91,17 +91,20 @@ export function commandFromString(commandLine) { } function escapedata(s) : string { - return s.replace(/\r/g, '%0D') + return s.replace(/%/g, '%25') + .replace(/\r/g, '%0D') .replace(/\n/g, '%0A'); } function unescapedata(s) : string { return s.replace(/%0D/g, '\r') - .replace(/%0A/g, '\n'); + .replace(/%0A/g, '\n') + .replace(/%25/g, '%'); } function escape(s) : string { - return s.replace(/\r/g, '%0D') + return s.replace(/%/g, '%25') + .replace(/\r/g, '%0D') .replace(/\n/g, '%0A') .replace(/]/g, '%5D') .replace(/;/g, '%3B'); @@ -111,5 +114,6 @@ function unescape(s) : string { return s.replace(/%0D/g, '\r') .replace(/%0A/g, '\n') .replace(/%5D/g, ']') - .replace(/%3B/g, ';'); + .replace(/%3B/g, ';') + .replace(/%25/g, '%'); } diff --git a/node/test/commandtests.ts b/node/test/commandtests.ts index 4a7ea9012..34daa5a91 100644 --- a/node/test/commandtests.ts +++ b/node/test/commandtests.ts @@ -68,10 +68,10 @@ describe('Command Tests', function () { it ('toString escapes properties', function (done) { this.timeout(1000); - var tc = new tcm.TaskCommand('some.cmd', { foo: ';=\r=\n' }, 'dog'); + var tc = new tcm.TaskCommand('some.cmd', { foo: ';=\r=\n%3B' }, 'dog'); assert(tc, 'TaskCommand constructor works'); var cmdStr = tc.toString(); - assert.equal(cmdStr, '##vso[some.cmd foo=%3B=%0D=%0A;]dog'); + assert.equal(cmdStr, '##vso[some.cmd foo=%3B=%0D=%0A%253B;]dog'); done(); }) @@ -142,11 +142,11 @@ describe('Command Tests', function () { }) it ('parses and unescapes properties', function (done) { - var cmdStr = '##vso[basic.command foo=%3B=%0D=%0A;]dog'; + var cmdStr = '##vso[basic.command foo=%3B=%0D=%0A%253B;]dog'; var tc = tcm.commandFromString(cmdStr); assert.equal(tc.command, 'basic.command', 'cmd should be basic.command'); - assert.equal(tc.properties['foo'], ';=\r=\n', 'property should be unescaped') + assert.equal(tc.properties['foo'], ';=\r=\n%3B', 'property should be unescaped') assert.equal(tc.message, 'dog'); done(); }) From 68aab72d5ed143acb02123571ecf637951a76ecb Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 5 Sep 2019 10:21:02 -0400 Subject: [PATCH 147/259] Fix rmrf and drop version to 2.9.0 (#531) * Fix rmrf and drop version to 2.9.0 * Handle case where it already doesn't exist * Remove extra brace * Use old linux behavior * Consistent logging * Don't mix implementations * Add explanatory comment * Better debugging --- node/task.ts | 92 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/node/task.ts b/node/task.ts index 024f2388b..79ac56b85 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1,5 +1,6 @@ import Q = require('q'); import shell = require('shelljs'); +import childProcess = require('child_process'); import fs = require('fs'); import path = require('path'); import os = require('os'); @@ -1150,42 +1151,77 @@ function _legacyFindFiles_getMatchingItems( * @param path path to remove * @returns void */ -export function rmRF(path: string): void { - debug('rm -rf ' + path); +export function rmRF(inputPath: string): void { + debug('rm -rf ' + inputPath); - // get the lstats in order to workaround a bug in shelljs@0.3.0 where symlinks - // with missing targets are not handled correctly by "rm('-rf', path)" - let lstats: fs.Stats; - try { - lstats = fs.lstatSync(path); - } - catch (err) { - // if you try to delete a file that doesn't exist, desired result is achieved - // other errors are valid - if (err.code == 'ENOENT') { - return; + if (getPlatform() == Platform.Windows) { + // Node doesn't provide a delete operation, only an unlink function. This means that if the file is being used by another + // program (e.g. antivirus), it won't be deleted. To address this, we shell out the work to rd/del. + try { + if (fs.statSync(inputPath).isDirectory()) { + debug('removing directory ' + inputPath); + childProcess.execSync(`rd /s /q "${inputPath}"`); + } + else { + debug('removing file ' + inputPath); + childProcess.execSync(`del /f /a "${inputPath}"`); + } + } + catch (err) { + // if you try to delete a file that doesn't exist, desired result is achieved + // other errors are valid + if (err.code != 'ENOENT') { + throw new Error(loc('LIB_OperationFailed', 'rmRF', err.message)); + } } - throw new Error(loc('LIB_OperationFailed', 'rmRF', err.message)); + // Shelling out fails to remove a symlink folder with missing source, this unlink catches that + try { + fs.unlinkSync(inputPath); + } + catch (err) { + // if you try to delete a file that doesn't exist, desired result is achieved + // other errors are valid + if (err.code != 'ENOENT') { + throw new Error(loc('LIB_OperationFailed', 'rmRF', err.message)); + } + } } + else { + // get the lstats in order to workaround a bug in shelljs@0.3.0 where symlinks + // with missing targets are not handled correctly by "rm('-rf', path)" + let lstats: fs.Stats; + try { + lstats = fs.lstatSync(inputPath); + } + catch (err) { + // if you try to delete a file that doesn't exist, desired result is achieved + // other errors are valid + if (err.code == 'ENOENT') { + return; + } - if (lstats.isDirectory()) { - debug('removing directory'); - shell.rm('-rf', path); - let errMsg: string = shell.error(); - if (errMsg) { - throw new Error(loc('LIB_OperationFailed', 'rmRF', errMsg)); + throw new Error(loc('LIB_OperationFailed', 'rmRF', err.message)); } - return; - } + if (lstats.isDirectory()) { + debug('removing directory'); + shell.rm('-rf', inputPath); + let errMsg: string = shell.error(); + if (errMsg) { + throw new Error(loc('LIB_OperationFailed', 'rmRF', errMsg)); + } - debug('removing file'); - try { - fs.unlinkSync(path); - } - catch (err) { - throw new Error(loc('LIB_OperationFailed', 'rmRF', err.message)); + return; + } + + debug('removing file'); + try { + fs.unlinkSync(inputPath); + } + catch (err) { + throw new Error(loc('LIB_OperationFailed', 'rmRF', err.message)); + } } } From 554c7e64203dbd29da9403ec3a153bab74ea2386 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 5 Sep 2019 10:23:46 -0400 Subject: [PATCH 148/259] Dont perform which twice when check is true (#529) --- node/internal.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/node/internal.ts b/node/internal.ts index 1ebe291c3..f43e7561e 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -345,7 +345,10 @@ export function _which(tool: string, check?: boolean): string { // recursive when check=true if (check) { let result: string = _which(tool, false); - if (!result) { + if (result) { + return result; + } + else { if (process.platform == 'win32') { throw new Error(_loc('LIB_WhichNotFound_Win', tool)); } From 5bb2ea0ab7506af3ba5b138fe59e3d46e57b3c79 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 5 Sep 2019 11:01:50 -0400 Subject: [PATCH 149/259] export write-logging command for powershell (#417) --- .../VstsTaskSdk/LoggingCommandFunctions.ps1 | 63 ++++++++++--------- powershell/VstsTaskSdk/VstsTaskSdk.psm1 | 1 + 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 b/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 index a2eeb9d68..4e8eb7323 100644 --- a/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 +++ b/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 @@ -436,6 +436,41 @@ function Write-UpdateReleaseName { Write-LoggingCommand -Area 'release' -Event 'updatereleasename' -Data $Name -AsOutput:$AsOutput } +<# +.SYNOPSIS +See https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md + +.PARAMETER AsOutput +Indicates whether to write the logging command directly to the host or to the output pipeline. +#> +function Write-LoggingCommand { + [CmdletBinding(DefaultParameterSetName = 'Parameters')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Parameters')] + [string]$Area, + [Parameter(Mandatory = $true, ParameterSetName = 'Parameters')] + [string]$Event, + [Parameter(ParameterSetName = 'Parameters')] + [string]$Data, + [Parameter(ParameterSetName = 'Parameters')] + [hashtable]$Properties, + [Parameter(Mandatory = $true, ParameterSetName = 'Object')] + $Command, + [switch]$AsOutput) + + if ($PSCmdlet.ParameterSetName -eq 'Object') { + Write-LoggingCommand -Area $Command.Area -Event $Command.Event -Data $Command.Data -Properties $Command.Properties -AsOutput:$AsOutput + return + } + + $command = Format-LoggingCommand -Area $Area -Event $Event -Data $Data -Properties $Properties + if ($AsOutput) { + $command + } else { + Write-Host $command + } +} + ######################################## # Private functions. ######################################## @@ -498,34 +533,6 @@ function Format-LoggingCommand { $sb.Append(']').Append($Data).ToString() } -function Write-LoggingCommand { - [CmdletBinding(DefaultParameterSetName = 'Parameters')] - param( - [Parameter(Mandatory = $true, ParameterSetName = 'Parameters')] - [string]$Area, - [Parameter(Mandatory = $true, ParameterSetName = 'Parameters')] - [string]$Event, - [Parameter(ParameterSetName = 'Parameters')] - [string]$Data, - [Parameter(ParameterSetName = 'Parameters')] - [hashtable]$Properties, - [Parameter(Mandatory = $true, ParameterSetName = 'Object')] - $Command, - [switch]$AsOutput) - - if ($PSCmdlet.ParameterSetName -eq 'Object') { - Write-LoggingCommand -Area $Command.Area -Event $Command.Event -Data $Command.Data -Properties $Command.Properties -AsOutput:$AsOutput - return - } - - $command = Format-LoggingCommand -Area $Area -Event $Event -Data $Data -Properties $Properties - if ($AsOutput) { - $command - } else { - Write-Host $command - } -} - function Write-LogIssue { [CmdletBinding()] param( diff --git a/powershell/VstsTaskSdk/VstsTaskSdk.psm1 b/powershell/VstsTaskSdk/VstsTaskSdk.psm1 index 43b9561a4..bba3e2bff 100644 --- a/powershell/VstsTaskSdk/VstsTaskSdk.psm1 +++ b/powershell/VstsTaskSdk/VstsTaskSdk.psm1 @@ -54,6 +54,7 @@ Export-ModuleMember -Function @( 'Write-AddBuildTag' 'Write-AssociateArtifact' 'Write-LogDetail' + 'Write-LoggingCommand' 'Write-PrependPath' 'Write-SetEndpoint' 'Write-SetProgress' From 89ac932ca4c2fc1a754aced1e83abea66e94972c Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Fri, 6 Sep 2019 15:30:37 -0400 Subject: [PATCH 150/259] Allow empty string in toolRunner (#569) --- node/test/toolrunnertests.ts | 10 ++++++++++ node/toolrunner.ts | 24 +++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 64105ab0e..559b9be20 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -1213,6 +1213,16 @@ describe('Toolrunner Tests', function () { assert.equal((node as any).args.toString(), 'foo=bar " baz,-x,-y', 'should be foo=bar " baz,-x,-y'); done(); }) + it('handles empty string', function (done) { + this.timeout(10000); + + var node = tl.tool(tl.which('node', true)); + node.line('"" -x'); + node.arg('-y'); + assert.equal((node as any).args.length, 3, 'should have 3 args'); + assert.equal((node as any).args.toString(), ',-x,-y', 'should be ,-x,-y'); + done(); + }) it('handles literal path', function (done) { this.timeout(10000); diff --git a/node/toolrunner.ts b/node/toolrunner.ts index c79afac63..cdf447b4c 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -84,6 +84,7 @@ export class ToolRunner extends events.EventEmitter { var inQuotes = false; var escaped = false; + var lastCharWasSpace = true; var arg = ''; var append = function (c: string) { @@ -99,6 +100,18 @@ export class ToolRunner extends events.EventEmitter { for (var i = 0; i < argString.length; i++) { var c = argString.charAt(i); + if (c === ' ' && !inQuotes) { + if (!lastCharWasSpace) { + args.push(arg); + arg = ''; + } + lastCharWasSpace = true; + continue; + } + else { + lastCharWasSpace = false; + } + if (c === '"') { if (!escaped) { inQuotes = !inQuotes; @@ -119,18 +132,11 @@ export class ToolRunner extends events.EventEmitter { continue; } - if (c === ' ' && !inQuotes) { - if (arg.length > 0) { - args.push(arg); - arg = ''; - } - continue; - } - append(c); + lastCharWasSpace = false; } - if (arg.length > 0) { + if (!lastCharWasSpace) { args.push(arg.trim()); } From 3183fa8c256b504b875022c0d20b5355b89459f9 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Mon, 9 Sep 2019 16:38:24 -0400 Subject: [PATCH 151/259] Update task.ts (#570) --- node/task.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/task.ts b/node/task.ts index 79ac56b85..5dd4a108c 100644 --- a/node/task.ts +++ b/node/task.ts @@ -217,7 +217,7 @@ export interface VariableInfo { * @returns string */ export function getInput(name: string, required?: boolean): string | undefined { - var inval = im._vault.retrieveSecret('INPUT_' + name.replace(' ', '_').toUpperCase()); + var inval = im._vault.retrieveSecret('INPUT_' + im._getVariableKey(name)); if (inval) { inval = inval.trim(); } @@ -479,7 +479,7 @@ export function getSecureFileTicket(id: string): string | undefined { */ export function getTaskVariable(name: string): string | undefined { assertAgent('2.115.0'); - var inval = im._vault.retrieveSecret('VSTS_TASKVARIABLE_' + name.replace(' ', '_').toUpperCase()); + var inval = im._vault.retrieveSecret('VSTS_TASKVARIABLE_' + im._getVariableKey(name)); if (inval) { inval = inval.trim(); } From bd40eb8771ef94b9960af7aedb1efbcd2f9547f3 Mon Sep 17 00:00:00 2001 From: Matt Cooper Date: Fri, 20 Sep 2019 14:21:53 -0400 Subject: [PATCH 152/259] Change some tags (#572) --- node/package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/node/package.json b/node/package.json index 125153d34..644a6ac1e 100644 --- a/node/package.json +++ b/node/package.json @@ -13,10 +13,12 @@ "url": "https://github.com/Microsoft/azure-pipelines-task-lib" }, "keywords": [ - "vsts", - "xplat", + "azure-pipelines", "agent", - "build" + "build", + "release", + "ci-cd", + "task" ], "author": "Microsoft", "license": "MIT", From 33207e0632f6dd3f450a001c3b5f7840cd4023a2 Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Mon, 23 Sep 2019 13:46:05 -0400 Subject: [PATCH 153/259] Update version since 2.9.2 had a publishing issue --- node/package-lock.json | 2 +- node/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 79ee05efc..0d2ffa2b8 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.2", + "version": "2.9.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 644a6ac1e..579dd541c 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.2", + "version": "2.9.3", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From d685603695c71828779dcd53057d63a68a21ace2 Mon Sep 17 00:00:00 2001 From: teh13th Date: Tue, 1 Oct 2019 20:05:39 +0300 Subject: [PATCH 154/259] add missed properties to task.json schema (#560) * add OutputVariables property * add missed visibility property * add missed postjobexecution object * fix --- tasks.schema.json | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tasks.schema.json b/tasks.schema.json index 3bfbb9cc5..0f3d578e1 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -51,6 +51,16 @@ ] } }, + "visibility": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "Build", + "Release" + ] + } + }, "category": { "type": "string", "description": "Where the task appears in Azure DevOps. Use the 'Azure *' categories for Azure DevOps and Azure DevOps Server 2019. Use the other categories for Team Foundation Server 2018 and below.", @@ -278,9 +288,50 @@ } } }, + "postjobexecution": { + "type": "object", + "additionalProperties": false, + "description": "Execution options for this task (on Post-Job stage)", + "properties": { + "Node10": { + "$ref": "#/definitions/executionObject" + }, + "Node": { + "$ref": "#/definitions/executionObject" + }, + "PowerShell3": { + "$ref": "#/definitions/executionObject" + }, + "PowerShell": { + "$ref": "#/definitions/executionObject" + } + } + }, "messages": { "type": "object" }, + "OutputVariables": { + "type": "array", + "description": "Describes output variables of task.", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The variable name", + "pattern": "^[A-Za-z][A-Za-z0-9]*$" + }, + "description": { + "type": "string", + "description": "Detailed description of the variable" + } + } + } + }, "$schema": { "type": "string" } From d3e1350e64e7bd207b481931240970c10a1438bb Mon Sep 17 00:00:00 2001 From: Matt Cooper Date: Thu, 24 Oct 2019 08:58:10 -0400 Subject: [PATCH 155/259] note about security issues --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 901b40950..f916a057a 100644 --- a/README.md +++ b/README.md @@ -49,3 +49,7 @@ Starting from [version v2.141.0](https://github.com/Microsoft/azure-pipelines-ag [npm-lib-url]: https://www.npmjs.com/package/azure-pipelines-task-lib [npm-sdk-image]: https://img.shields.io/npm/v/vsts-task-sdk.svg?style=flat [npm-sdk-url]: https://www.npmjs.com/package/vsts-task-sdk + +## Security issues + +Do you think there might be a security issue? Have you been phished or identified a security vulnerability? Please don't report it here - let us know by sending an email to secure@microsoft.com. From f64f0bf4930a99a6de35c3ca77ab08f9f61754a9 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 12 Nov 2019 11:25:18 -0500 Subject: [PATCH 156/259] Update tasks.schema.json --- tasks.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks.schema.json b/tasks.schema.json index 0f3d578e1..f8971fecc 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -17,7 +17,7 @@ }, "friendlyName": { "type": "string", - "description": "Descriptive name (spaces allowed)" + "description": "Descriptive name (spaces allowed). Must be <= 40 chars" }, "description": { "type": "string", From f2c40a3884f54bce5ec4531174ad5b1a721d0727 Mon Sep 17 00:00:00 2001 From: Keegan Campbell Date: Thu, 14 Nov 2019 15:07:07 -0800 Subject: [PATCH 157/259] Add missing assertion. (#588) --- node/test/findmatchtests.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/node/test/findmatchtests.ts b/node/test/findmatchtests.ts index 1b0a39f8b..4a7c09ead 100644 --- a/node/test/findmatchtests.ts +++ b/node/test/findmatchtests.ts @@ -44,6 +44,7 @@ describe('Find and Match Tests', function () { path.join(root, 'hello.txt'), path.join(root, 'world.txt'), ]; + assert.deepEqual(actual, expected); done(); }); From 0fdbbc1ccab7d3f5272c3d1ed147c3e30c392c1a Mon Sep 17 00:00:00 2001 From: Adam Coulter Date: Tue, 3 Dec 2019 01:16:37 +1100 Subject: [PATCH 158/259] Update tasks.schema.json for missing attributes (#591) Added: - Groups: visibleRule - DataSourceBindings: endpointUrl - DataSourceBindings: resultSelector --- tasks.schema.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tasks.schema.json b/tasks.schema.json index f8971fecc..7f400dba5 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -97,6 +97,10 @@ }, "isExpanded": { "type": "boolean" + }, + "visibleRule": { + "type": "string", + "description": "Allow's you to define a rule which dictates when the group will be visible to a user, for example \"variableName = value\"" } } } @@ -241,6 +245,12 @@ }, "resultTemplate": { "type": "string" + }, + "endpointUrl": { + "type": "string" + }, + "resultSelector": { + "type": "string" } } } From 6ebfd5e331ed74eab2d695496256f395f13227a2 Mon Sep 17 00:00:00 2001 From: Nikita Nallamothu Date: Tue, 3 Dec 2019 19:43:29 +0530 Subject: [PATCH 159/259] added helpUrl to task.schema.json (#592) --- tasks.schema.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tasks.schema.json b/tasks.schema.json index 7f400dba5..17c6542d9 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -23,6 +23,9 @@ "type": "string", "description": "Detailed description of what your task does" }, + "helpUrl": { + "type": "string" + }, "helpMarkDown": { "type": "string" }, From 439989394b820d14e0fd7c4ff84b1f80f31f0f4b Mon Sep 17 00:00:00 2001 From: Jesse Houwing Date: Thu, 12 Dec 2019 20:32:06 +0100 Subject: [PATCH 160/259] Resolves Newtonsoft.Json loading against 2019 version of the TFS ClientOM (#594) Fixes https://github.com/microsoft/azure-pipelines-task-lib/issues/580 --- powershell/VstsTaskSdk/ServerOMFunctions.ps1 | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/powershell/VstsTaskSdk/ServerOMFunctions.ps1 b/powershell/VstsTaskSdk/ServerOMFunctions.ps1 index 6aa2908b1..6fd19ea13 100644 --- a/powershell/VstsTaskSdk/ServerOMFunctions.ps1 +++ b/powershell/VstsTaskSdk/ServerOMFunctions.ps1 @@ -139,6 +139,31 @@ function Get-TfsClientCredentials { # Get the endpoint. $endpoint = Get-Endpoint -Name SystemVssConnection -Require + # Test if the Newtonsoft.Json DLL exists in the OM directory. + $newtonsoftDll = [System.IO.Path]::Combine($OMDirectory, "Newtonsoft.Json.dll") + Write-Verbose "Testing file path: '$newtonsoftDll'" + if (!(Test-Path -LiteralPath $newtonsoftDll -PathType Leaf)) { + Write-Verbose 'Not found. Rethrowing exception.' + throw + } + + # Add a binding redirect and try again. Parts of the Dev15 preview SDK have a + # dependency on the 6.0.0.0 Newtonsoft.Json DLL, while other parts reference + # the 8.0.0.0 Newtonsoft.Json DLL. + Write-Verbose "Adding assembly resolver." + $onAssemblyResolve = [System.ResolveEventHandler] { + param($sender, $e) + + if ($e.Name -like 'Newtonsoft.Json, *') { + Write-Verbose "Resolving '$($e.Name)' to '$newtonsoftDll'." + + return [System.Reflection.Assembly]::LoadFrom($newtonsoftDll) + } + + return $null + } + [System.AppDomain]::CurrentDomain.add_AssemblyResolve($onAssemblyResolve) + # Validate the type can be found. $null = Get-OMType -TypeName 'Microsoft.TeamFoundation.Client.TfsClientCredentials' -OMKind 'ExtendedClient' -OMDirectory $OMDirectory -Require From 9e19afd44cdb65069cb8751d907d94d77635ae5c Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Mon, 30 Dec 2019 09:43:54 -0500 Subject: [PATCH 161/259] Added sync-request as dependency (#597) Fixes #595 --- node/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package.json b/node/package.json index 579dd541c..0b5e5b194 100644 --- a/node/package.json +++ b/node/package.json @@ -32,11 +32,11 @@ "q": "^1.1.2", "semver": "^5.1.0", "shelljs": "^0.3.0", - "uuid": "^3.0.1" + "uuid": "^3.0.1", + "sync-request": "3.0.1" }, "devDependencies": { "mocha": "5.2.0", - "sync-request": "3.0.1", "typescript": "^3.0.0" } } From aae2d84d73f701c514ed10ba7390263ff9398794 Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Mon, 30 Dec 2019 09:45:59 -0500 Subject: [PATCH 162/259] Fixed issues with generating docs. Issue #587 (#598) --- node/buildutils.js | 2 +- node/docs/TypeScriptSourceToJson.ts | 36 ++++++++++++++--------------- node/docs/run.sh | 2 +- node/test/dirtests.ts | 4 ++-- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/node/buildutils.js b/node/buildutils.js index fe6b90b2d..534774ce8 100644 --- a/node/buildutils.js +++ b/node/buildutils.js @@ -34,7 +34,7 @@ exports.getExternals = function () { // download the same version of node used by the agent // and add node to the PATH var nodeUrl = 'https://nodejs.org/dist'; - var nodeVersion = 'v5.10.1'; + var nodeVersion = 'v6.17.1'; switch (platform) { case 'darwin': var nodeArchivePath = downloadArchive(nodeUrl + '/' + nodeVersion + '/node-' + nodeVersion + '-darwin-x64.tar.gz'); diff --git a/node/docs/TypeScriptSourceToJson.ts b/node/docs/TypeScriptSourceToJson.ts index f0b1e6c73..a6e7c8ee6 100644 --- a/node/docs/TypeScriptSourceToJson.ts +++ b/node/docs/TypeScriptSourceToJson.ts @@ -26,11 +26,10 @@ export function generate(filePaths: string[], options: ts.CompilerOptions): DocE program = ts.createProgram(filePaths, options); checker = program.getTypeChecker(); - let files: ts.SourceFile[] = program.getSourceFiles(); for (const sourceFile of program.getSourceFiles()) { // only document files we specified. dependency files may be in program if (filePaths.indexOf(sourceFile.fileName) >= 0) { - let name = path.basename(sourceFile.fileName, '.ts'); + let name = path.basename(sourceFile.fileName, '.ts'); console.log('Processing:', name); let fd: DocEntry = { @@ -38,14 +37,14 @@ export function generate(filePaths: string[], options: ts.CompilerOptions): DocE kind: 'file', members: {} as { string: [DocEntry]} }; - + doc.members[name] = fd; push(fd); ts.forEachChild(sourceFile, visit); } } - + return doc; } @@ -80,7 +79,7 @@ function visit(node: ts.Node): void { else if (node.kind == ts.SyntaxKind.InterfaceDeclaration) { if (!isNodeExported(node)) { return; - } + } let id: ts.InterfaceDeclaration = node; let symbol = checker.getSymbolAtLocation(id.name); if (symbol) { @@ -100,7 +99,7 @@ function visit(node: ts.Node): void { let memberDeclarations: ts.Declaration[] = s.getDeclarations(); if (memberDeclarations.length > 0) { let memberDoc: DocEntry = {}; - memberDoc.documentation = ts.displayPartsToString(s.getDocumentationComment()); + memberDoc.documentation = ts.displayPartsToString(s.getDocumentationComment(checker)); memberDoc.name = memberName; memberDoc.return = checker.typeToString(checker.getTypeAtLocation(memberDeclarations[0])) doc.members[memberName] = memberDoc; @@ -109,7 +108,7 @@ function visit(node: ts.Node): void { current.members[doc.name] = doc; push(doc); - } + } } if (node.kind == ts.SyntaxKind.EndOfFileToken) { inClass = false; @@ -118,7 +117,7 @@ function visit(node: ts.Node): void { else if (node.kind == ts.SyntaxKind.MethodDeclaration) { let m: ts.MethodDeclaration = node; let symbol = checker.getSymbolAtLocation(m.name); - + if (symbol) { let doc: DocEntry = getDocEntryFromSymbol(symbol); doc.kind = 'method'; @@ -156,7 +155,7 @@ function visit(node: ts.Node): void { } } // handle re-export from internal.ts - else if (node.kind === ts.SyntaxKind.VariableStatement && node.flags === ts.NodeFlags.Export) { + else if (node.kind === ts.SyntaxKind.VariableStatement && node.flags === ts.NodeFlags.ExportContext) { let statement = node; let list: ts.VariableDeclarationList = statement.declarationList; for (let declaration of list.declarations) { @@ -171,13 +170,12 @@ function visit(node: ts.Node): void { } } - ts.forEachChild(node, visit); + ts.forEachChild(node, visit); } function getDocEntryFromSignature(signature: ts.Signature): DocEntry { let paramEntries: DocEntry[] = []; - let params: ts.Symbol[] = signature.parameters; - params.forEach((ps: ts.Symbol) => { + signature.parameters.forEach((ps: ts.Symbol) => { let de = {} as DocEntry; de.name = ps.getName(); @@ -185,7 +183,7 @@ function getDocEntryFromSignature(signature: ts.Signature): DocEntry { let paramType: ts.Type = checker.getTypeAtLocation(decls[0]); de.type = checker.typeToString(paramType); de.optional = checker.isOptionalParameter(ps.declarations[0] as ts.ParameterDeclaration); - de.documentation = ts.displayPartsToString(ps.getDocumentationComment()); + de.documentation = ts.displayPartsToString(ps.getDocumentationComment(checker)); paramEntries.push(de); }); @@ -193,7 +191,7 @@ function getDocEntryFromSignature(signature: ts.Signature): DocEntry { parameters: paramEntries, members: {} as { string: [DocEntry]}, return: checker.typeToString(signature.getReturnType()), - documentation: ts.displayPartsToString(signature.getDocumentationComment()) + documentation: ts.displayPartsToString(signature.getDocumentationComment(checker)) }; return e; @@ -203,19 +201,19 @@ function getDocEntryFromSymbol(symbol: ts.Symbol): DocEntry { return { name: symbol.getName(), members: {} as { string: [DocEntry]}, - documentation: ts.displayPartsToString(symbol.getDocumentationComment()), - + documentation: ts.displayPartsToString(symbol.getDocumentationComment(checker)), + //type: checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration)) }; } /** True if this is visible outside this file, false otherwise */ function isNodeExported(node: ts.Node): boolean { - return (node.flags & ts.NodeFlags.Export) !== 0 || (node.parent && node.parent.kind === ts.SyntaxKind.SourceFile); -} + return (node.flags & ts.NodeFlags.ExportContext) !== 0 || (node.parent && node.parent.kind === ts.SyntaxKind.SourceFile); +} // -// convenience stack +// convenience stack // let push = function(entry: DocEntry) { diff --git a/node/docs/run.sh b/node/docs/run.sh index 7676aeae6..4eca166db 100755 --- a/node/docs/run.sh +++ b/node/docs/run.sh @@ -6,4 +6,4 @@ function failed() } ../node_modules/.bin/tsc || failed 'Compilation failed.' -../_download/archive/https___nodejs.org_dist_v5.10.1_node-v5.10.1-darwin-x64.tar.gz/node-v5.10.1-darwin-x64/bin/node gendocs.js \ No newline at end of file +../_download/archive/*/*/bin/node gendocs.js \ No newline at end of file diff --git a/node/test/dirtests.ts b/node/test/dirtests.ts index e92c078d2..41dc22744 100644 --- a/node/test/dirtests.ts +++ b/node/test/dirtests.ts @@ -31,7 +31,7 @@ describe('Dir Operation Tests', function () { this.timeout(1000); console.log('node version: ' + process.version); - const supportedNodeVersions = ['v5.10.1', 'v6.10.3', 'v8.9.1']; + const supportedNodeVersions = ['v5.10.1', 'v6.10.3', 'v6.17.1', 'v8.9.1', 'v10.17.0', 'v10.18.0']; if (supportedNodeVersions.indexOf(process.version) === -1) { assert.fail(`expected node node version to be one of ${supportedNodeVersions.map(o => o).join(', ')}. actual: ` + process.version); } @@ -292,7 +292,7 @@ describe('Dir Operation Tests', function () { done(); }); - + it('which() requires rooted path to be a file', function (done) { this.timeout(1000); From 3a9ccf2b4e3ff4abcd381f1adf13dbf9acd68c98 Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Thu, 9 Jan 2020 10:27:58 -0500 Subject: [PATCH 163/259] Updated to generate correct env var keys in mock task runner (#600) --- node/mock-run.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/node/mock-run.ts b/node/mock-run.ts index 2d558c465..54c05c37d 100644 --- a/node/mock-run.ts +++ b/node/mock-run.ts @@ -1,5 +1,7 @@ import ma = require('./mock-answer'); import mockery = require('mockery'); +import im = require('./internal'); + export class TaskMockRunner { constructor(taskPath: string) { @@ -12,15 +14,17 @@ export class TaskMockRunner { _moduleCount: number = 0; public setInput(name: string, val: string) { - process.env['INPUT_' + name.replace(' ', '_').toUpperCase()] = val; + let key: string = im._getVariableKey(name); + process.env['INPUT_' + key] = val; } public setVariableName(name: string, val: string, isSecret?: boolean) { + let key: string = im._getVariableKey(name); if (isSecret) { - process.env['SECRET_' + name.replace(' ', '_').toUpperCase()] = val; + process.env['SECRET_' + key] = val; } else { - process.env['VSTS_TASKVARIABLE_' + name.replace(' ', '_').toUpperCase()] = val; + process.env['VSTS_TASKVARIABLE_' + key] = val; } } From f81bb6ff80534904486d046b875432dcd45b525a Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Fri, 10 Jan 2020 09:42:14 -0500 Subject: [PATCH 164/259] Slight doc clarity nit (#602) --- powershell/Docs/Consuming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/Docs/Consuming.md b/powershell/Docs/Consuming.md index 3e053fc31..7173c33b1 100644 --- a/powershell/Docs/Consuming.md +++ b/powershell/Docs/Consuming.md @@ -30,7 +30,7 @@ Use the `PowerShell3` execution handler and set the target to the entry PS1 scri ``` ## Package the SDK with the task -The SDK should be packaged with the task in a `ps_modules` folder. The `ps_modules` folder should be in the root of the task folder. +The SDK should be packaged with the task in a `ps_modules` folder. The `ps_modules` folder should be at the same level as the entry PS1 script. Example layout: Consider the following layout where `MyTask` is the root folder for the task. ``` From 63d954e0202c20011fbc0fbe2ab08980a6b626bf Mon Sep 17 00:00:00 2001 From: Lucas Araujo Date: Tue, 21 Jan 2020 16:14:05 +0100 Subject: [PATCH 165/259] fixed casing for editableoptions (#607) (#608) --- tasks.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks.schema.json b/tasks.schema.json index 17c6542d9..7d9659288 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -212,7 +212,7 @@ "properties": { "type": "object", "properties": { - "editableOptions": { + "EditableOptions": { "type": "string", "enum": [ "True", From 49a7bf3f8c6352ad415ae98b2e9cb49f7c794027 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Mon, 27 Jan 2020 11:08:23 -0700 Subject: [PATCH 166/259] Annotate outStream and errStream as optional (#613) In fact the handling of these properties already allows for them to be undefined/null. But the annotations didn't indicate this. Since the entire options object was optional, having a couple properties on it *not* optional makes it hard to start supplying *any* options since the user must figure out what values to supply for these two properties to get equivalent behavior, in order to set some *other* option on the object. --- node/toolrunner.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index cdf447b4c..c382a9b35 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -32,9 +32,11 @@ export interface IExecSyncOptions { /** optional. defaults to false */ silent?: boolean; - outStream: stream.Writable; + /** Optional. Default is process.stdout. */ + outStream?: stream.Writable; - errStream: stream.Writable; + /** Optional. Default is process.stderr. */ + errStream?: stream.Writable; /** optional. foo.whether to skip quoting/escaping arguments if needed. defaults to false. */ windowsVerbatimArguments?: boolean; @@ -515,7 +517,7 @@ export class ToolRunner extends events.EventEmitter { const optionsNonNull = this._cloneExecOptions(options); if (!optionsNonNull.silent) { - optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL); + optionsNonNull.outStream!.write(this._getCommandString(optionsNonNull) + os.EOL); } let cp: child.ChildProcess; @@ -592,7 +594,7 @@ export class ToolRunner extends events.EventEmitter { } successFirst = !optionsNonNull.failOnStdErr; if (!optionsNonNull.silent) { - var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream; + var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream! : optionsNonNull.outStream!; s.write(data); } }); @@ -633,7 +635,7 @@ export class ToolRunner extends events.EventEmitter { this.emit('stdout', data); if (!optionsNonNull.silent) { - optionsNonNull.outStream.write(data); + optionsNonNull.outStream!.write(data); } this._processLineBuffer(data, stdbuffer, (line: string) => { @@ -647,7 +649,7 @@ export class ToolRunner extends events.EventEmitter { success = !optionsNonNull.failOnStdErr; if (!optionsNonNull.silent) { - var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream; + var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream! : optionsNonNull.outStream!; s.write(data); } @@ -796,7 +798,7 @@ export class ToolRunner extends events.EventEmitter { const optionsNonNull = this._cloneExecOptions(options); if (!optionsNonNull.silent) { - optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL); + optionsNonNull.outStream!.write(this._getCommandString(optionsNonNull) + os.EOL); } let state = new ExecState(optionsNonNull, this.toolPath); @@ -811,7 +813,7 @@ export class ToolRunner extends events.EventEmitter { // stream. Adding this event forces a flush before the child streams are closed. cp.stdout.on('finish', () => { if (!optionsNonNull.silent) { - optionsNonNull.outStream.write(os.EOL); + optionsNonNull.outStream!.write(os.EOL); } }); @@ -820,7 +822,7 @@ export class ToolRunner extends events.EventEmitter { this.emit('stdout', data); if (!optionsNonNull.silent) { - optionsNonNull.outStream.write(data); + optionsNonNull.outStream!.write(data); } this._processLineBuffer(data, stdbuffer, (line: string) => { @@ -835,7 +837,7 @@ export class ToolRunner extends events.EventEmitter { this.emit('stderr', data); if (!optionsNonNull.silent) { - var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream; + var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream! : optionsNonNull.outStream!; s.write(data); } @@ -909,17 +911,17 @@ export class ToolRunner extends events.EventEmitter { options = this._cloneExecOptions(options as IExecOptions); if (!options.silent) { - options.outStream.write(this._getCommandString(options as IExecOptions) + os.EOL); + options.outStream!.write(this._getCommandString(options as IExecOptions) + os.EOL); } var r = child.spawnSync(this._getSpawnFileName(), this._getSpawnArgs(options as IExecOptions), this._getSpawnSyncOptions(options)); if (!options.silent && r.stdout && r.stdout.length > 0) { - options.outStream.write(r.stdout); + options.outStream!.write(r.stdout); } if (!options.silent && r.stderr && r.stderr.length > 0) { - options.errStream.write(r.stderr); + options.errStream!.write(r.stderr); } var res: IExecSyncResult = { code: r.status, error: r.error }; From 7f62b8ac93211af925ace759770f2b3b22a1d3f3 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Mon, 27 Jan 2020 12:28:57 -0700 Subject: [PATCH 167/259] Allow undefined env var values (#615) While it doesn't make any sense, the typings for `process.env` is: ```ts interface ProcessEnv { [key: string]: string | undefined; } ``` So assigning `process.env` to `IExecSyncOptions.env` in typescript with all typings turned on produces compiler errors (at least when strict null checking is applied). Without this simple typings adjustment, the following workaround in user code is required: ```ts function GetFilteredEnv() { const result = {}; const block = process.env; for (const key in block) { if (block.hasOwnProperty(key)) { const value = block[key]; if (value !== undefined) { result[key] = value; } } } return result; } let options: IExecSyncOptions = { env: GetFilteredEnv() }; ``` But with this change it's as simple as: ```ts let options: IExecSyncOptions = { env: process.env }; ``` Which ironically is already what is done *within* this repo, but isn't a problem because the node typings aren't imported so `process.env` is typed as `any`. --- node/toolrunner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index c382a9b35..80f419f50 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -27,7 +27,7 @@ export interface IExecSyncOptions { cwd?: string; /** optional envvar dictionary. defaults to current process's env */ - env?: { [key: string]: string }; + env?: { [key: string]: string | undefined }; /** optional. defaults to false */ silent?: boolean; From 2e3cdea38f49c7a1a225ebde1b2e353a0abacaf1 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Tue, 28 Jan 2020 08:06:39 -0700 Subject: [PATCH 168/259] Fix doc comment for exported `rmRF` function (#616) --- node/task.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/node/task.ts b/node/task.ts index 5dd4a108c..43bd828e8 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1146,10 +1146,9 @@ function _legacyFindFiles_getMatchingItems( /** * Remove a path recursively with force - * Returns whether it succeeds * - * @param path path to remove - * @returns void + * @param inputPath path to remove + * @throws when the file or directory exists but could not be deleted. */ export function rmRF(inputPath: string): void { debug('rm -rf ' + inputPath); From d35a5b74fd82991b590c8f7e50d2783b7767835b Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Tue, 28 Jan 2020 13:53:18 -0500 Subject: [PATCH 169/259] Update to honor silent option for all stdout writes in exec. Fixes #605 (#610) --- node/mock-toolrunner.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 7d459b089..15b5b79de 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -40,7 +40,7 @@ export class ToolRunner extends events.EventEmitter { debug('toolRunner toolPath: ' + toolPath); super(); - + this.toolPath = toolPath; this.args = []; } @@ -83,7 +83,7 @@ export class ToolRunner extends events.EventEmitter { } continue; } - + if (c === "\\" && inQuotes) { escaped = true; continue; @@ -123,7 +123,7 @@ export class ToolRunner extends events.EventEmitter { return this; } - + public argIf(condition: any, val: any): ToolRunner { if (condition) { this.arg(val); @@ -139,7 +139,7 @@ export class ToolRunner extends events.EventEmitter { this._debug(this.toolPath + ' arg: ' + val); this.args = this.args.concat(this._argStringToArray(val)); - return this; + return this; } public pipeExecOutputToTool(tool: ToolRunner) : ToolRunner { @@ -156,7 +156,7 @@ export class ToolRunner extends events.EventEmitter { } return cmdString; - } + } // // Exec - use for long running tools where you need to stream live output as it runs @@ -244,13 +244,17 @@ export class ToolRunner extends events.EventEmitter { var code = res.code; - ops.outStream.write('rc:' + res.code + os.EOL); + if (!ops.silent) { + ops.outStream.write('rc:' + res.code + os.EOL); + } if (code != 0 && !ops.ignoreReturnCode) { success = false; } - ops.outStream.write('success:' + success + os.EOL); + if (!ops.silent) { + ops.outStream.write('success:' + success + os.EOL); + } if (success) { defer.resolve(code); } From 34e17f6ca02c256bcb4e121880f6d4b7da65eed2 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Tue, 28 Jan 2020 11:59:33 -0700 Subject: [PATCH 170/259] Remove erroneous "foo" from docs (#612) --- node/toolrunner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 80f419f50..90c28f9b9 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -38,7 +38,7 @@ export interface IExecSyncOptions { /** Optional. Default is process.stderr. */ errStream?: stream.Writable; - /** optional. foo.whether to skip quoting/escaping arguments if needed. defaults to false. */ + /** optional. Whether to skip quoting/escaping arguments if needed. defaults to false. */ windowsVerbatimArguments?: boolean; } From 14c54c256b33c90464b2cf5e450ac0ed7bec2e92 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 5 Mar 2020 14:41:24 -0500 Subject: [PATCH 171/259] Update to new mac pool --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 478cc0144..1975371e6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -110,7 +110,7 @@ jobs: ################################################# displayName: macOS pool: - vmImage: macOS-10.13 + vmImage: macOS-10.15 steps: ################################################################################ From 0a6b208c2e94339d2ee9d33d971d965ca25d61de Mon Sep 17 00:00:00 2001 From: Tommy Petty Date: Fri, 6 Mar 2020 14:04:59 -0500 Subject: [PATCH 172/259] bump version (#619) Co-authored-by: Tommy Petty --- node/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/package.json b/node/package.json index 0b5e5b194..a0fe9ab13 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.3", + "version": "2.9.4", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 62c92b5236c900500752862a13378ae3deac84e5 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 2 Apr 2020 11:35:12 -0400 Subject: [PATCH 173/259] Add stale workflow (#626) --- .github/workflows/stale.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..5f72a783c --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,21 @@ + +name: Mark stale issues and pull requests + +on: + schedule: + - cron: "0 * * * *" + +jobs: + stale: + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue has had no activity in 90 days. Please comment if it is not actually stale' + stale-issue-label: 'stale' + days-before-stale: 90 + days-before-close: 7 + exempt-pr-label: 'no-stale' From f2094a2580311b010ad93ee6dbdca2e1309edefe Mon Sep 17 00:00:00 2001 From: YasserHayali <45432968+YasserHayali@users.noreply.github.com> Date: Tue, 21 Apr 2020 18:37:39 +0200 Subject: [PATCH 174/259] Adding prejobexecution (#629) Adding execution options on the pre-job stage. --- tasks.schema.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tasks.schema.json b/tasks.schema.json index 7d9659288..2f865822a 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -282,6 +282,25 @@ } } }, + "prejobexecution": { + "type": "object", + "additionalProperties": false, + "description": "Execution options for this task (on Pre-Job stage)", + "properties": { + "Node10": { + "$ref": "#/definitions/executionObject" + }, + "Node": { + "$ref": "#/definitions/executionObject" + }, + "PowerShell3": { + "$ref": "#/definitions/executionObject" + }, + "PowerShell": { + "$ref": "#/definitions/executionObject" + } + } + }, "execution": { "type": "object", "additionalProperties": false, From 44869ec39351bc68eed29b8c4fc9879e32482d82 Mon Sep 17 00:00:00 2001 From: Torben Clasen Date: Tue, 5 May 2020 17:50:09 +0200 Subject: [PATCH 175/259] Update Consuming.md (#631) --- powershell/Docs/Consuming.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/powershell/Docs/Consuming.md b/powershell/Docs/Consuming.md index 7173c33b1..7fbe2d20e 100644 --- a/powershell/Docs/Consuming.md +++ b/powershell/Docs/Consuming.md @@ -17,6 +17,15 @@ or install a specific version: Save-Module -Name VstsTaskSdk -Path .\ -RequiredVersion 0.7.0 ``` +Using the Save-Module Cmdlet creates a Folder structure like this +``` +VstsTaskSdk +└─── + [...] + VstsTaskSdk.psd1 +``` +you need to manually adjust it to resemble the structure shown in the [Package the SDK with the task](#package-the-sdk-with-the-task) section. That means you need to move the content of the version Folder one directory up. + ## task.json modifications Use the `PowerShell3` execution handler and set the target to the entry PS1 script. The entry PS1 script should be located in the root of the task folder. ```JSON From 5f230a274df010d769ce51f6ca37e364e8dc5cec Mon Sep 17 00:00:00 2001 From: found-it Date: Wed, 6 May 2020 17:12:16 -0600 Subject: [PATCH 176/259] Fixed documentation for tr.exec() (#633) The previous code example had `tr.exec()` on line 8. However, it should be `atool.exec()`. --- node/docs/samples/toolrunner.src | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/docs/samples/toolrunner.src b/node/docs/samples/toolrunner.src index ed2a1009a..b9a325b31 100644 --- a/node/docs/samples/toolrunner.src +++ b/node/docs/samples/toolrunner.src @@ -5,9 +5,9 @@ import tr = require('azure-pipelines-task-lib/toolrunner'); try { var toolPath = tl.which('atool'); var atool:tr.ToolRunner = tl.tool(toolPath).arg('--afile').line('arguments'); - var code: number = await tr.exec(); + var code: number = await atool.exec(); console.log('rc=' + code); } catch (err) { tl.setResult(tl.TaskResult.Failed, err.message); -} \ No newline at end of file +} From b1c2d7f6d242c21675ea0edc09694b1573b688fe Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Mon, 11 May 2020 12:34:15 -0400 Subject: [PATCH 177/259] Update stale.yml --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 5f72a783c..33a5792f3 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/stale@v1 + - uses: actions/stale@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue has had no activity in 90 days. Please comment if it is not actually stale' From 1519dfc6dcd7bd7e2efa8d36a41012914a77cae9 Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Tue, 12 May 2020 18:56:04 +0300 Subject: [PATCH 178/259] Added shell property to IExecOptions (#637) * Added shell property to IExecOptions Added shell property to IExecOptions to allow this property to be specified in pipelines tasks code. This option allows to handle variables in command the way it's needed. * Incremented versions --- node/package-lock.json | 2 +- node/package.json | 2 +- node/toolrunner.ts | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 0d2ffa2b8..bcda4e8d2 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.3", + "version": "2.9.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index a0fe9ab13..1af0583e8 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.4", + "version": "2.9.5", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 90c28f9b9..79e94fb21 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -40,6 +40,9 @@ export interface IExecSyncOptions { /** optional. Whether to skip quoting/escaping arguments if needed. defaults to false. */ windowsVerbatimArguments?: boolean; + + /** optional. Run command inside of the shell. Defaults to false. */ + shell?: boolean; } /** @@ -480,7 +483,8 @@ export class ToolRunner extends events.EventEmitter { silent: options.silent || false, failOnStdErr: options.failOnStdErr || false, ignoreReturnCode: options.ignoreReturnCode || false, - windowsVerbatimArguments: options.windowsVerbatimArguments || false + windowsVerbatimArguments: options.windowsVerbatimArguments || false, + shell: options.shell || false }; result.outStream = options.outStream || process.stdout; result.errStream = options.errStream || process.stderr; @@ -492,6 +496,7 @@ export class ToolRunner extends events.EventEmitter { let result = {}; result.cwd = options.cwd; result.env = options.env; + result.shell = options.shell; result['windowsVerbatimArguments'] = options.windowsVerbatimArguments || this._isCmdFile(); return result; } @@ -500,6 +505,7 @@ export class ToolRunner extends events.EventEmitter { let result = {}; result.cwd = options.cwd; result.env = options.env; + result.shell = options.shell; result['windowsVerbatimArguments'] = options.windowsVerbatimArguments || this._isCmdFile(); return result; } From c532de60cd68ba382b17d574c669333beabe0b9f Mon Sep 17 00:00:00 2001 From: MorganRodgers Date: Tue, 19 May 2020 12:32:37 -0500 Subject: [PATCH 179/259] Permit getDelimitedInput to split on either a string or a Regexp (#638) * Permit getDelimitedInput to split on either a string or a Regexp * Add test for splitting on regexp * Add missing argument * Remove extra arg Co-authored-by: Morgan Rodgers --- node/task.ts | 2 +- node/test/inputtests.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/node/task.ts b/node/task.ts index 43bd828e8..c9bfbee70 100644 --- a/node/task.ts +++ b/node/task.ts @@ -256,7 +256,7 @@ export function getBoolInput(name: string, required?: boolean): boolean { * @param required whether input is required. optional, defaults to false * @returns string[] */ -export function getDelimitedInput(name: string, delim: string, required?: boolean): string[] { +export function getDelimitedInput(name: string, delim: string | RegExp, required?: boolean): string[] { let inputVal = getInput(name, required); if (!inputVal) { return []; diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index fa8697319..50fe1ec18 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -620,6 +620,18 @@ describe('Input Tests', function () { done(); }) + it('gets delimited input with a Regexp', function (done) { + this.timeout(1000); + + var inputValue = 'a,b\nc'; + process.env['INPUT_DELIM'] = inputValue; + im._loadData(); + + var outVal = tl.getDelimitedInput('delim', /[,\n]/, /*required*/false); + assert.equal(outVal.length, 3, 'should return array with 3 elements'); + + done(); + }) // getPathInput tests it('gets path input value', function (done) { From 7b32ca7fb2d2f6fe966dd2b413b6f38a1a6eeebf Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Thu, 18 Jun 2020 18:31:45 +0300 Subject: [PATCH 180/259] [ToolRunner] Enhanced 'shell' execution option (#643) * Wrapped tool path with quotes for running inside shell * Refactored toolrunner.ts * Increased version * Updated package-lock.json * Moved RegExp to separate variable * Moved toolPath wrapping to separate method * Revert "Updated package-lock.json" This reverts commit ad943d1e44ddbfd9bd7b80038f058cec12473dc0. * Fixed package-lock.json * Added exec and execSync tests * Improved arguments handling for inside shell execution * Added tests and moved them to separate section. * Fixed Linux tests * Fixed quotes handling test * Fixed tests to support macOS * Minor code cleanup. * Made cmdSpecialChars constant readonly * Minor refactoring * Refactored arguments unwrapping * Removed redundant import. * Changes in accordance with review points * Refactored in accordance with review points * Fixed JSDocs --- node/package-lock.json | 2 +- node/package.json | 2 +- node/test/toolrunnertests.ts | 173 +++++++++++++++++++++++++++++++++++ node/toolrunner.ts | 147 +++++++++++++++++++++++------ 4 files changed, 292 insertions(+), 32 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index bcda4e8d2..06383f47f 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.5", + "version": "2.9.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 1af0583e8..6621c9d1f 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.5", + "version": "2.9.6", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 559b9be20..39daa1e92 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -2062,6 +2062,179 @@ describe('Toolrunner Tests', function () { return exePath; } + describe('Executing inside shell', function () { + + let tempPath: string = testutil.getTestTemp(); + let _testExecOptions: trm.IExecOptions; + + before (function () { + _testExecOptions = { + cwd: __dirname, + env: { + WIN_TEST: 'test value', + TESTPATH: tempPath, + TEST_NODE: 'node', + TEST: 'test value' + }, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + shell: true, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + }) + + it('Exec sync inside shell', function (done) { + this.timeout(10000); + + if (os.platform() === 'win32') { + let exePath = compileArgsExe('print args with spaces.exe'); + let exeRunner = tl.tool(exePath); + exeRunner.line('%WIN_TEST%') + var ret = exeRunner.execSync(_testExecOptions); + assert.equal(ret.code, 0, 'return code of cmd should be 0'); + assert.equal(ret.stdout.trim(), 'args[0]: \'test value\'', 'Command should return \"args[0]: \'test value\'\"'); + } + else { + var ret = tl.execSync('stat', '$TESTPATH', _testExecOptions); + assert.equal(ret.code, 0, 'return code of stat should be 0'); + assert(ret.stdout.includes(tempPath), `Result should include \'${tempPath}\'`); + } + + assert(ret.stdout && ret.stdout.length > 0, 'should have emitted stdout'); + done(); + }); + it('Exec inside shell', function (done) { + this.timeout(10000); + + let output: string = ''; + if (os.platform() === 'win32') { + let exePath = compileArgsExe('print args with spaces.exe'); + let exeRunner = tl.tool(exePath); + exeRunner.line('%WIN_TEST%'); + exeRunner.on('stdout', (data) => { + output = data.toString(); + }); + exeRunner.exec(_testExecOptions).then(function (code) { + assert.equal(code, 0, 'return code of cmd should be 0'); + assert.equal(output.trim(), 'args[0]: \'test value\'', 'Command should return \"args[0]: \'test value\'\"'); + done(); + }) + .fail(function (err) { + done(err); + }); + } + else { + let statRunner = tl.tool('stat'); + statRunner.line('$TESTPATH'); + statRunner.on('stdout', (data) => { + output = data.toString(); + }); + statRunner.exec(_testExecOptions).then(function (code) { + assert.equal(code, 0, 'return code of stat should be 0'); + assert(output.includes(tempPath), `Result should include \'${tempPath}\'`); + done(); + }) + .fail(function (err) { + done(err); + }); + } + }); + it('Exec pipe output to another tool inside shell, succeeds if both tools succeed', function (done) { + this.timeout(30000); + + if (os.platform() === 'win32') { + const matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('test value'); // match value + const outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('"%WIN_TEST%"') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe); + + let output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + outputExe.exec(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('test value') >= 0, 'should have emitted stdout ' + output); + done(); + }) + .fail(function (err) { + done(err); + }); + } + else { + const grep = tl.tool(tl.which('grep', true)); + grep.arg('$TEST_NODE'); + + const ps = tl.tool(tl.which('ps', true)); + ps.arg('ax'); + ps.pipeExecOutputToTool(grep); + + let output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + ps.exec(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('node') >= 0, 'should have emitted stdout ' + output); + done(); + }) + .fail(function (err) { + done(err); + }); + } + }); + it('Should handle arguments with quotes properly', function (done) { + this.timeout(10000); + + let output: string = ''; + if (os.platform() === 'win32') { + let exePath = compileArgsExe('print args with spaces.exe'); + let exeRunner = tl.tool(exePath); + exeRunner.line('-TEST1="space test" "-TEST2=%WIN_TEST%" \'-TEST3=value\''); + exeRunner.on('stdout', (data) => { + output = data.toString(); + }); + exeRunner.exec(_testExecOptions).then(function (code) { + assert.equal(code, 0, 'return code of cmd should be 0'); + assert.equal(output.trim(), 'args[0]: \'-TEST1=space test\'\r\n' + + 'args[1]: \'-TEST2=test value\'\r\n' + + 'args[2]: \'\'-TEST3=value\'\''); + done(); + }) + .fail(function (err) { + done(err); + }); + } + else { + let statRunner = tl.tool('echo'); + statRunner.line('-TEST1="$TEST;test" "-TEST2=/one/two/three" \'-TEST3=out:$TEST\''); + statRunner.on('stdout', (data) => { + output = data.toString(); + }); + statRunner.exec(_testExecOptions).then(function (code) { + assert.equal(code, 0, 'return code of stat should be 0'); + assert.equal(output, '-TEST1=test value;test -TEST2=/one/two/three -TEST3=out:$TEST\n'); + done(); + }) + .fail(function (err) { + done(err); + }); + } + }); + }) + // function to compile a .NET program that prints the command line args. // the helper program is used to validate that command line args are passed correctly. let compileArgsExe = (targetFileName: string): string => { diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 79e94fb21..77b11cd7e 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -75,6 +75,7 @@ export class ToolRunner extends events.EventEmitter { this._debug('toolRunner toolPath: ' + toolPath); } + private readonly cmdSpecialChars: string[] = [' ', '\t', '&', '(', ')', '[', ']', '{', '}', '^', '=', ';', '!', '\'', '+', ',', '`', '~', '|', '<', '>', '"']; private toolPath: string; private args: string[]; private pipeOutputToTool: ToolRunner | undefined; @@ -152,38 +153,36 @@ export class ToolRunner extends events.EventEmitter { let toolPath: string = this._getSpawnFileName(); let args: string[] = this._getSpawnArgs(options); let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool + let commandParts: string[] = []; if (process.platform == 'win32') { // Windows + cmd file if (this._isCmdFile()) { - cmd += toolPath; - args.forEach((a: string): void => { - cmd += ` ${a}`; - }); + commandParts.push(toolPath); + commandParts = commandParts.concat(args); } // Windows + verbatim else if (options.windowsVerbatimArguments) { - cmd += `"${toolPath}"`; - args.forEach((a: string): void => { - cmd += ` ${a}`; - }); + commandParts.push(`"${toolPath}"`); + commandParts = commandParts.concat(args); + } + else if (options.shell) { + commandParts.push(this._windowsQuoteCmdArg(toolPath)); + commandParts = commandParts.concat(args); } // Windows (regular) else { - cmd += this._windowsQuoteCmdArg(toolPath); - args.forEach((a: string): void => { - cmd += ` ${this._windowsQuoteCmdArg(a)}`; - }); + commandParts.push(this._windowsQuoteCmdArg(toolPath)); + commandParts = commandParts.concat(args.map(arg =>this._windowsQuoteCmdArg(arg))); } } else { // OSX/Linux - this can likely be improved with some form of quoting. // creating processes on Unix is fundamentally different than Windows. // on Unix, execvp() takes an arg array. - cmd += toolPath; - args.forEach((a: string): void => { - cmd += ` ${a}`; - }); + commandParts.push(toolPath); + commandParts = commandParts.concat(args); } + cmd += commandParts.join(' '); // append second tool if (this.pipeOutputToTool) { @@ -216,13 +215,50 @@ export class ToolRunner extends events.EventEmitter { } - private _getSpawnFileName(): string { + /** + * Wraps an arg string with specified char if it's not already wrapped + * @returns {string} Arg wrapped with specified char + * @param {string} arg Input argument string + * @param {string} wrapChar A char input string should be wrapped with + */ + private _wrapArg(arg: string, wrapChar: string): string { + if (!this._isWrapped(arg, wrapChar)) { + return `${wrapChar}${arg}${wrapChar}`; + } + return arg; + } + + /** + * Unwraps an arg string wrapped with specified char + * @param arg Arg wrapped with specified char + * @param wrapChar A char to be removed + */ + private _unwrapArg(arg: string, wrapChar: string): string { + if (this._isWrapped(arg, wrapChar)) { + const pattern = new RegExp(`(^\\\\?${wrapChar})|(\\\\?${wrapChar}$)`, 'g'); + return arg.trim().replace(pattern, ''); + } + return arg; + } + + /** + * Determine if arg string is wrapped with specified char + * @param arg Input arg string + */ + private _isWrapped(arg: string, wrapChar: string): boolean { + const pattern: RegExp = new RegExp(`^\\\\?${wrapChar}.+\\\\?${wrapChar}$`); + return pattern.test(arg.trim()) + } + + private _getSpawnFileName(options?: IExecOptions): string { if (process.platform == 'win32') { if (this._isCmdFile()) { return process.env['COMSPEC'] || 'cmd.exe'; } } - + if (options && options.shell) { + return this._wrapArg(this.toolPath, '"'); + } return this.toolPath; } @@ -283,17 +319,75 @@ export class ToolRunner extends events.EventEmitter { return Array.prototype.unshift.call(args, `"${arguments[0]}"`); // quote the file name }; return args; + } else if (options.shell) { + let args: string[] = []; + for (let arg of this.args) { + if (this._needQuotesForCmd(arg, '%')) { + args.push(this._wrapArg(arg, '"')); + } else { + args.push(arg); + } + } + return args; } + } else if (options.shell) { + return this.args.map(arg => { + if (this._isWrapped(arg, "'")) { + return arg; + } + // remove wrapping double quotes to avoid escaping + arg = this._unwrapArg(arg, '"'); + arg = this._escapeChar(arg, '"'); + return this._wrapArg(arg, '"'); + }); } return this.args; } + /** + * Escape specified character. + * @param arg String to escape char in + * @param charToEscape Char should be escaped + */ + private _escapeChar(arg: string, charToEscape: string): string { + const escChar: string = "\\"; + let output: string = ''; + let charIsEscaped: boolean = false; + for (const char of arg) { + if (char === charToEscape && !charIsEscaped) { + output += escChar + char; + } else { + output += char; + } + charIsEscaped = char === escChar && !charIsEscaped; + } + return output; + } + private _isCmdFile(): boolean { let upperToolPath: string = this.toolPath.toUpperCase(); return im._endsWith(upperToolPath, '.CMD') || im._endsWith(upperToolPath, '.BAT'); } + /** + * Determine whether the cmd arg needs to be quoted. Returns true if arg contains any of special chars array. + * @param arg The cmd command arg. + * @param additionalChars Additional chars which should be also checked. + */ + private _needQuotesForCmd(arg: string, additionalChars?: string[] | string): boolean { + let specialChars: string[] = this.cmdSpecialChars; + if (additionalChars) { + specialChars = this.cmdSpecialChars.concat(additionalChars); + } + for (let char of arg) { + if (specialChars.some(x => x === char)) { + return true; + } + } + return false; + } + private _windowsQuoteCmdArg(arg: string): string { // for .exe, apply the normal quoting rules that libuv applies if (!this._isCmdFile()) { @@ -313,14 +407,7 @@ export class ToolRunner extends events.EventEmitter { } // determine whether the arg needs to be quoted - const cmdSpecialChars = [' ', '\t', '&', '(', ')', '[', ']', '{', '}', '^', '=', ';', '!', '\'', '+', ',', '`', '~', '|', '<', '>', '"']; - let needsQuotes = false; - for (let char of arg) { - if (cmdSpecialChars.some(x => x == char)) { - needsQuotes = true; - break; - } - } + const needsQuotes: boolean = this._needQuotesForCmd(arg); // short-circuit if quotes not needed if (!needsQuotes) { @@ -544,13 +631,13 @@ export class ToolRunner extends events.EventEmitter { //start the child process for both tools waitingEvents++; var cpFirst = child.spawn( - this._getSpawnFileName(), + this._getSpawnFileName(optionsNonNull), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(optionsNonNull)); waitingEvents ++; cp = child.spawn( - pipeOutputToTool._getSpawnFileName(), + pipeOutputToTool._getSpawnFileName(optionsNonNull), pipeOutputToTool._getSpawnArgs(optionsNonNull), pipeOutputToTool._getSpawnOptions(optionsNonNull)); @@ -812,7 +899,7 @@ export class ToolRunner extends events.EventEmitter { this._debug(message); }); - let cp = child.spawn(this._getSpawnFileName(), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(options)); + let cp = child.spawn(this._getSpawnFileName(options), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(options)); // it is possible for the child process to end its last line without a new line. // because stdout is buffered, this causes the last line to not get sent to the parent @@ -920,7 +1007,7 @@ export class ToolRunner extends events.EventEmitter { options.outStream!.write(this._getCommandString(options as IExecOptions) + os.EOL); } - var r = child.spawnSync(this._getSpawnFileName(), this._getSpawnArgs(options as IExecOptions), this._getSpawnSyncOptions(options)); + var r = child.spawnSync(this._getSpawnFileName(options), this._getSpawnArgs(options as IExecOptions), this._getSpawnSyncOptions(options)); if (!options.silent && r.stdout && r.stdout.length > 0) { options.outStream!.write(r.stdout); From ea7cfd92c86568620a761ab092d0bcc4816ed074 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 23 Jul 2020 13:16:29 -0400 Subject: [PATCH 181/259] Dont trim whitespace from inputs (#652) --- node/package-lock.json | 42 ++++++++++++----------------------------- node/package.json | 2 +- node/task.ts | 5 +---- node/test/inputtests.ts | 12 ------------ 4 files changed, 14 insertions(+), 47 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 06383f47f..9c1cadc22 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,14 +1,13 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.6", + "version": "2.10.0", "lockfileVersion": 1, "requires": true, "dependencies": { "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "balanced-match": { "version": "1.0.0", @@ -33,8 +32,7 @@ "caseless": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" }, "concat-map": { "version": "0.0.1", @@ -45,7 +43,6 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.1.tgz", "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==", - "dev": true, "requires": { "inherits": "^2.0.3", "readable-stream": "^2.2.2", @@ -55,8 +52,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "debug": { "version": "3.1.0", @@ -103,7 +99,6 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-2.5.1.tgz", "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", - "dev": true, "requires": { "caseless": "~0.11.0", "concat-stream": "^1.4.6", @@ -113,8 +108,7 @@ "http-response-object": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-1.1.0.tgz", - "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM=", - "dev": true + "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM=" }, "inflight": { "version": "1.0.6", @@ -129,14 +123,12 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "minimatch": { "version": "3.0.4", @@ -244,14 +236,12 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, "requires": { "asap": "~2.0.3" } @@ -264,14 +254,12 @@ "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=", - "dev": true + "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=" }, "readable-stream": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -285,8 +273,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", - "dev": true + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" }, "semver": { "version": "5.5.0", @@ -302,7 +289,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -311,7 +297,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-3.0.1.tgz", "integrity": "sha1-yqEjWq+Im6UBB2oYNMQ2gwqC+3M=", - "dev": true, "requires": { "concat-stream": "^1.4.7", "http-response-object": "^1.0.1", @@ -322,7 +307,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz", "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", - "dev": true, "requires": { "caseless": "~0.11.0", "concat-stream": "^1.4.7", @@ -335,8 +319,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { "version": "3.1.3", @@ -347,8 +330,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { "version": "3.2.1", diff --git a/node/package.json b/node/package.json index 6621c9d1f..c21f16218 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.9.6", + "version": "2.10.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index c9bfbee70..442a6d2ce 100644 --- a/node/task.ts +++ b/node/task.ts @@ -209,7 +209,7 @@ export interface VariableInfo { } /** - * Gets the value of an input. The value is also trimmed. + * Gets the value of an input. * If required is true and the value is not set, it will throw. * * @param name name of the input to get @@ -218,9 +218,6 @@ export interface VariableInfo { */ export function getInput(name: string, required?: boolean): string | undefined { var inval = im._vault.retrieveSecret('INPUT_' + im._getVariableKey(name)); - if (inval) { - inval = inval.trim(); - } if (required && !inval) { throw new Error(loc('LIB_InputRequired', name)); diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index 50fe1ec18..a45b5d3c0 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -63,18 +63,6 @@ describe('Input Tests', function () { done(); }) - it('gets input value with whitespace', function (done) { - this.timeout(1000); - - process.env['INPUT_UNITTESTINPUT'] = ' test value '; - im._loadData(); - - var inval = tl.getInput('UnitTestInput', true); - assert.equal(inval, 'test value'); - - done(); - }) - // getVariable tests it('gets a variable', function (done) { this.timeout(1000); From 0dbd3518cc611d150314e63a9c22ba3a0fc0c5f1 Mon Sep 17 00:00:00 2001 From: alex-peck <53013351+alex-peck@users.noreply.github.com> Date: Fri, 11 Sep 2020 09:40:42 -0400 Subject: [PATCH 182/259] Fix stdout/stderr types (#660) * Fix stdout/stderr types * version bump --- node/package-lock.json | 2 +- node/package.json | 2 +- node/toolrunner.ts | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 9c1cadc22..cbc1ef172 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.10.0", + "version": "2.10.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index c21f16218..23cf40021 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.10.0", + "version": "2.10.1", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 77b11cd7e..f216f6d17 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -33,10 +33,10 @@ export interface IExecSyncOptions { silent?: boolean; /** Optional. Default is process.stdout. */ - outStream?: stream.Writable; + outStream?: NodeJS.WritableStream; /** Optional. Default is process.stderr. */ - errStream?: stream.Writable; + errStream?: NodeJS.WritableStream; /** optional. Whether to skip quoting/escaping arguments if needed. defaults to false. */ windowsVerbatimArguments?: boolean; @@ -224,7 +224,7 @@ export class ToolRunner extends events.EventEmitter { private _wrapArg(arg: string, wrapChar: string): string { if (!this._isWrapped(arg, wrapChar)) { return `${wrapChar}${arg}${wrapChar}`; - } + } return arg; } @@ -573,8 +573,8 @@ export class ToolRunner extends events.EventEmitter { windowsVerbatimArguments: options.windowsVerbatimArguments || false, shell: options.shell || false }; - result.outStream = options.outStream || process.stdout; - result.errStream = options.errStream || process.stderr; + result.outStream = options.outStream || process.stdout; + result.errStream = options.errStream || process.stderr; return result; } @@ -798,9 +798,9 @@ export class ToolRunner extends events.EventEmitter { /** * Add argument - * Append an argument or an array of arguments + * Append an argument or an array of arguments * returns ToolRunner for chaining - * + * * @param val string cmdline or array of strings * @returns ToolRunner */ @@ -825,7 +825,7 @@ export class ToolRunner extends events.EventEmitter { * Parses an argument line into one or more arguments * e.g. .line('"arg one" two -z') is equivalent to .arg(['arg one', 'two', '-z']) * returns ToolRunner for chaining - * + * * @param val string argument line * @returns ToolRunner */ @@ -871,7 +871,7 @@ export class ToolRunner extends events.EventEmitter { * Exec a tool. * Output will be streamed to the live console. * Returns promise with return code - * + * * @param tool path to tool to exec * @param options optional exec options. See IExecOptions * @returns number @@ -984,11 +984,11 @@ export class ToolRunner extends events.EventEmitter { } /** - * Exec a tool synchronously. + * Exec a tool synchronously. * Output will be *not* be streamed to the live console. It will be returned after execution is complete. - * Appropriate for short running tools + * Appropriate for short running tools * Returns IExecSyncResult with output and return code - * + * * @param tool path to tool to exec * @param options optional exec options. See IExecSyncOptions * @returns IExecSyncResult From 5f17f1c0e06bb7f347627f2d6fb40b8bf9411ef0 Mon Sep 17 00:00:00 2001 From: alex-peck <53013351+alex-peck@users.noreply.github.com> Date: Thu, 17 Sep 2020 10:24:25 -0400 Subject: [PATCH 183/259] Support node 14 tests (#661) * Support node 14 tests * Forgot to save files... * words --- node/docs/nodeEnvironment.md | 8 +++---- node/docs/nodeVersioning.md | 6 +++--- node/mock-test.ts | 27 ++++++++++++++---------- node/package-lock.json | 2 +- node/package.json | 2 +- node/test/fakeTasks/node14task/task.json | 9 ++++++++ node/test/mocktests.ts | 10 +++++++++ 7 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 node/test/fakeTasks/node14task/task.json diff --git a/node/docs/nodeEnvironment.md b/node/docs/nodeEnvironment.md index 0bbd140da..7c5978d83 100644 --- a/node/docs/nodeEnvironment.md +++ b/node/docs/nodeEnvironment.md @@ -1,18 +1,18 @@ # Node Handler Versioning -As of agent version 2.144.0, Azure Pipelines supports running tasks in a Node 10 environment in addition to the previously supported Node 6 environment. +As of agent version 2.175.0, Azure Pipelines supports running tasks in a Node 14 environment in addition to the previously supported Node 6 and Node 10 environments. -To leverage this capability, simply add `Node10` as an execution target: +To leverage this capability, simply add `Node14` as an execution target: ``` "execution": { - "Node10": { + "Node14": { "target": "path/to/entry" } }, ``` -Existing `Node` execution targets will still resolve to a Node 6 environment for now to maintain back-compat. +Existing `Node` and `Node10` execution targets will still resolve to a Node 6 and Node 10 environment respectively for now to maintain back-compat. ### Testing your task diff --git a/node/docs/nodeVersioning.md b/node/docs/nodeVersioning.md index 11eb39676..1c1f65f72 100644 --- a/node/docs/nodeVersioning.md +++ b/node/docs/nodeVersioning.md @@ -2,16 +2,16 @@ ## Agent Node Handler -The agent currently has 2 different node handlers that it can use to execute node tasks: Node 6 and Node 10. +The agent currently has 3 different node handlers that it can use to execute node tasks: Node 6, Node 10, and Node 14. The handler used depends on the `execution` property specified in the tasks `task.json`. -If the `execution` property is specified to be `Node`, the task will run on the Node 6 handler, if it is specified to be `Node10` it will run on the Node 10 handler. +If the `execution` property is specified to be `Node`, the task will run on the Node 6 handler, if it is specified to be `Node10` it will run on the Node 10 handler, and if it is specified to be `Node14` it will run on the Node 14 handler. ## Mock-test Node Handler [Unit testing](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops#step-2-unit-testing-your-task-scripts) of tasks can be done using the task-lib's built in mock-task functionality. To ensure tests are run in the same environment as the agent, this library looks for a `task.json` file in the same directory as the supplied task entry point. If no `task.json` is found it searches all ancestor directories as well. -If the `task.json` is still not found, the library defaults to node 10, otherwise it uses the appropriate handler based on the `execution` property. +If the `task.json` is still not found, the library defaults to Node 14, otherwise it uses the appropriate handler based on the `execution` property. If this version of node is not found on the path, the library downloads the appropriate version. ### Behavior overrides diff --git a/node/mock-test.ts b/node/mock-test.ts index 806dac026..69ae17b4c 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -147,13 +147,16 @@ export class MockTestRunner { downloadVersion = 'v5.10.1'; break; case 6: - downloadVersion = 'v6.10.3'; + downloadVersion = 'v6.17.1'; break; case 10: - downloadVersion = 'v10.15.1'; + downloadVersion = 'v10.21.0'; + break; + case 14: + downloadVersion = 'v14.11.0'; break; default: - throw new Error('Invalid node version, must be 5, 6, or 10 (received ' + version + ')'); + throw new Error('Invalid node version, must be 5, 6, 10, or 14 (received ' + version + ')'); } // Install node in home directory if it isn't already there. @@ -167,11 +170,11 @@ export class MockTestRunner { } } - // Determines the correct version of node to use based on the contents of the task's task.json. Defaults to Node 10. + // Determines the correct version of node to use based on the contents of the task's task.json. Defaults to Node 14. private getNodeVersion(): number { const taskJsonPath: string = this.getTaskJsonPath(); if (!taskJsonPath) { - console.warn('Unable to find task.json, defaulting to use Node 10'); + console.warn('Unable to find task.json, defaulting to use Node 14'); return 10; } const taskJsonContents = fs.readFileSync(taskJsonPath, { encoding: 'utf-8' }); @@ -181,18 +184,20 @@ export class MockTestRunner { const execution: object = taskJson['execution']; const keys = Object.keys(execution); for (let i = 0; i < keys.length; i++) { - if (keys[i].toLowerCase() == 'node10') { + if (keys[i].toLowerCase() == 'node14') { + // Prefer node 14 and return immediately. + return 14; + } else if (keys[i].toLowerCase() == 'node10') { // Prefer node 10 and return immediately. return 10; - } - else if (keys[i].toLowerCase() == 'node') { + } else if (keys[i].toLowerCase() == 'node') { nodeVersionFound = true; } } if (!nodeVersionFound) { - console.warn('Unable to determine execution type from task.json, defaulting to use Node 10'); - return 10; + console.warn('Unable to determine execution type from task.json, defaulting to use Node 14'); + return 14; } return 6; @@ -269,7 +274,7 @@ export class MockTestRunner { } const tarGzName: string = 'node.tar.gz'; this.downloadFile(url, downloadDestination, tarGzName); - + // Extract file const originalCwd: string = process.cwd(); process.chdir(downloadDestination); diff --git a/node/package-lock.json b/node/package-lock.json index cbc1ef172..15a067b32 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.10.1", + "version": "2.11.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 23cf40021..634102d4f 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.10.1", + "version": "2.11.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/test/fakeTasks/node14task/task.json b/node/test/fakeTasks/node14task/task.json new file mode 100644 index 000000000..a4825b6ec --- /dev/null +++ b/node/test/fakeTasks/node14task/task.json @@ -0,0 +1,9 @@ +{ + "id": "id", + "name": "Node14Task", + "execution": { + "Node14": { + "target": "usedotnet.js" + } + } +} diff --git a/node/test/mocktests.ts b/node/test/mocktests.ts index 19a783227..d4f3d28f9 100644 --- a/node/test/mocktests.ts +++ b/node/test/mocktests.ts @@ -320,4 +320,14 @@ describe('Mock Tests', function () { assert(semver.satisfies(version, '10.x'), 'Downloaded node version should be Node 10 instead of ' + version); done(); }) + + it('MockTest handles node 14 tasks correctly', function (done) { + this.timeout(10000); + 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'); + const version = ncp.execSync(nodePath + ' -v').toString().trim(); + assert(semver.satisfies(version, '14.x'), 'Downloaded node version should be Node 14 instead of ' + version); + done(); + }) }); From 9d739728e547fe2105160c2ef11fb07c3466f582 Mon Sep 17 00:00:00 2001 From: Nikita Ezzhev Date: Thu, 17 Sep 2020 20:31:53 +0300 Subject: [PATCH 184/259] Add support for killing child processes with SIGINT signal (#663) * Added child process closing method * Added missed void statement * Bump version --- node/package-lock.json | 2 +- node/package.json | 2 +- node/toolrunner.ts | 13 ++++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 15a067b32..b911cb5d1 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.0", + "version": "2.11.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 634102d4f..00861b074 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.0", + "version": "2.11.1", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/toolrunner.ts b/node/toolrunner.ts index f216f6d17..9e516def9 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -80,6 +80,7 @@ export class ToolRunner extends events.EventEmitter { private args: string[]; private pipeOutputToTool: ToolRunner | undefined; private pipeOutputToFile: string | undefined; + private childProcess: child.ChildProcess | undefined; private _debug(message: string) { this.emit('debug', message); @@ -900,7 +901,7 @@ export class ToolRunner extends events.EventEmitter { }); let cp = child.spawn(this._getSpawnFileName(options), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(options)); - + this.childProcess = cp; // it is possible for the child process to end its last line without a new line. // because stdout is buffered, this causes the last line to not get sent to the parent // stream. Adding this event forces a flush before the child streams are closed. @@ -1022,6 +1023,16 @@ export class ToolRunner extends events.EventEmitter { res.stderr = (r.stderr) ? r.stderr.toString() : ''; return res; } + + /** + * Used to close child process by sending SIGNINT signal. + * It allows executed script to have some additional logic on SIGINT, before exiting. + */ + public killChildProcess(): void { + if (this.childProcess) { + this.childProcess.kill(); + } + } } class ExecState extends events.EventEmitter { From 0d9223f07484efb6168c67d3632bfa6526c401c7 Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Mon, 5 Oct 2020 16:51:54 +0300 Subject: [PATCH 185/259] Added parameter to setResourcePath to add posibility to disable warnings thrown (#668) * fix build * Revert "fix build" This reverts commit be3dc1de3ed761a02e63a27ee4f4f2c582a03217. * Fully support Node 14 and TS 4 (#665) * Full Node 14 support * fix * fix build * don't need that anymore * fix mv test * Remove old reference directives * comment * docs and preview version * help improve flaky tests * package-lock * Add support for killing child processes with SIGINT signal (#663) * Added child process closing method * Added missed void statement * Bump version * Updated setResourcePath method * Updated package version * Revert "Merge branch 'releases/3.x' into users/egor-bryzgalov/set-resource-path-update" This reverts commit 1a628db09e5bc1a89eb6b706478fb8f8a38b940d, reversing changes made to ad533f0366d595ce960075ce9ef910073bdd0b1c. Co-authored-by: Alex Peck Co-authored-by: alex-peck <53013351+alex-peck@users.noreply.github.com> Co-authored-by: Nikita Ezzhev --- node/internal.ts | 9 +++++++-- node/package-lock.json | 2 +- node/package.json | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index f43e7561e..eed6bd4e1 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -142,9 +142,10 @@ function _loadLocStrings(resourceFile: string, culture: string): { [key: string] * Call once at the beginning of the script before any calls to loc. * * @param path Full path to the json. + * @param ignoreWarnings Won't throw warnings if path already set. * @returns void */ -export function _setResourcePath(path: string): void { +export function _setResourcePath(path: string, ignoreWarnings: boolean = false): void { if (process.env['TASKLIB_INPROC_UNITS']) { _resourceFiles = {}; _libResourceFileLoaded = false; @@ -166,7 +167,11 @@ export function _setResourcePath(path: string): void { } else { - _warning(_loc('LIB_ResourceFileAlreadySet', path)); + if (ignoreWarnings) { + _debug(_loc('LIB_ResourceFileAlreadySet', path)); + } else { + _warning(_loc('LIB_ResourceFileAlreadySet', path)); + } } } diff --git a/node/package-lock.json b/node/package-lock.json index b911cb5d1..1fb8a6275 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.1", + "version": "2.11.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 00861b074..dca18ce12 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.1", + "version": "2.11.2", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From cf18a238d0ec500d8e8e86b2ff74f901cc92aec2 Mon Sep 17 00:00:00 2001 From: DaniilShmelev <72494759+DaniilShmelev@users.noreply.github.com> Date: Tue, 13 Oct 2020 16:22:52 +0300 Subject: [PATCH 186/259] [MockTestRunner] Fixed node version determination (#672) * fix MockTestRunner.getNodeVersion() * bump version --- node/mock-test.ts | 6 +++++- node/package-lock.json | 2 +- node/package.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/node/mock-test.ts b/node/mock-test.ts index 69ae17b4c..4ad3c9e6d 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -181,7 +181,11 @@ export class MockTestRunner { const taskJson: object = JSON.parse(taskJsonContents); let nodeVersionFound = false; - const execution: object = taskJson['execution']; + const execution: object = ( + taskJson['execution'] + || taskJson['prejobexecution'] + || taskJson['postjobexecution'] + ); const keys = Object.keys(execution); for (let i = 0; i < keys.length; i++) { if (keys[i].toLowerCase() == 'node14') { diff --git a/node/package-lock.json b/node/package-lock.json index 1fb8a6275..1b47dca64 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.2", + "version": "2.11.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index dca18ce12..6dfc0a1b3 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.2", + "version": "2.11.3", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 77b9e4731876c5661f4f7755c78f607bc23d9451 Mon Sep 17 00:00:00 2001 From: Maxim Zaytsev Date: Mon, 26 Oct 2020 14:50:12 +0300 Subject: [PATCH 187/259] Change type of the resultFiles parameter of publish method in TestPublisher class (#676) * fix task.ts and bump version --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 1b47dca64..4288140d7 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.3", + "version": "2.11.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 6dfc0a1b3..3065ff3a9 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.3", + "version": "2.11.4", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index 442a6d2ce..869bffe2a 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1775,12 +1775,12 @@ export class TestPublisher { constructor(public testRunner: string) { } - public publish(resultFiles?: string, mergeResults?: string, platform?: string, config?: string, runTitle?: string, publishRunAttachments?: string, testRunSystem?: string) { + public publish(resultFiles?: string[], mergeResults?: string, platform?: string, config?: string, runTitle?: string, publishRunAttachments?: string, testRunSystem?: string) { // Could have used an initializer, but wanted to avoid reordering parameters when converting to strict null checks // (A parameter cannot both be optional and have an initializer) testRunSystem = testRunSystem || "VSTSTask"; - var properties = <{ [key: string]: string }>{}; + var properties = <{ [key: string]: string | string[] }>{}; properties['type'] = this.testRunner; if (mergeResults) { From 8c406fa042a4bd1aa7ddb0ddaebb593eba9c37c6 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 12 Nov 2020 10:07:34 -0500 Subject: [PATCH 188/259] Automatically publish package on CI (#686) * Automatically publish package on CI * Fix * Fix --- azure-pipelines.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1975371e6..63eeddff0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -105,6 +105,17 @@ jobs: displayName: (azure-pipelines-task-lib) node make.js test workingDirectory: node + # For CI runs on master, automatically publish packages + - bash: | + echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc + cd _build + npm publish || true # Ignore publish failures, usually will happen because package already exists + displayName: (azure-pipelines-task-lib) npm publish + workingDirectory: node + condition: and(succeeded(), in(variables['build.reason'], 'IndividualCI', 'BatchedCI', 'Manual'), in(variables['build.sourcebranchname'], 'master', 'releases/3.x')) + env: + NPM_TOKEN: $(npmPublishToken) + ################################################# - job: macOS ################################################# From 90e9cde0e509cba77185a80ef3af2fc898fb026c Mon Sep 17 00:00:00 2001 From: teh13th Date: Tue, 8 Dec 2020 17:34:47 +0300 Subject: [PATCH 189/259] add missed properties to tasks.schema.json (#641) * add missed properties to tasks.schema.json * Update tasks.schema.json * Update tasks.schema.json * Update tasks.schema.json --- tasks.schema.json | 117 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 5 deletions(-) diff --git a/tasks.schema.json b/tasks.schema.json index 2f865822a..7591ceb85 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -50,7 +50,9 @@ "enum": [ "Agent", "MachineGroup", - "Server" + "Server", + "ServerGate", + "DeploymentGroup" ] } }, @@ -103,7 +105,7 @@ }, "visibleRule": { "type": "string", - "description": "Allow's you to define a rule which dictates when the group will be visible to a user, for example \"variableName = value\"" + "description": "Allow's you to define a rule which dictates when the group will be visible to a user, for example \"variableName1 != \\\"\\\" && variableName2 = value || variableName3 NotEndsWith value\"" } } } @@ -144,6 +146,9 @@ "type": "string", "description": "This is how the task will be displayed within the build step list - you can use variable values by using $(variablename)" }, + "releaseNotes": { + "type": "string" + }, "inputs": { "type": "array", "items": { @@ -160,6 +165,12 @@ "description": "The variable name to use to store the user-supplied value", "pattern": "^[A-Za-z][A-Za-z0-9]*$" }, + "aliases": { + "type": "array", + "items": { + "type": "string" + } + }, "label": { "type": "string", "description": "The text displayed to the user for the input label" @@ -176,7 +187,10 @@ "pickList", "radio", "secureFile", - "string" + "string", + "int", + "identities", + "querycontrol" ] }, { @@ -207,7 +221,7 @@ }, "visibleRule": { "type": "string", - "description": "Allow's you to define a rule which dictates when the input will be visible to a user, for example \"variableName = value\"" + "description": "Allow's you to define a rule which dictates when the input will be visible to a user, for example \"variableName1 != \\\"\\\" && variableName2 = value || variableName3 NotEndsWith value\"" }, "properties": { "type": "object", @@ -218,6 +232,65 @@ "True", "False" ] + }, + "MultiSelect": { + "type": "string", + "enum": [ + "True", + "False" + ] + }, + "MultiSelectFlatList": { + "type": "string", + "enum": [ + "True", + "False" + ] + }, + "DisableManageLink": { + "type": "string", + "enum": [ + "True", + "False" + ] + }, + "IsSearchable": { + "type": "string", + "enum": [ + "True", + "False" + ] + }, + "PopulateDefaultValue": { + "type": "string", + "enum": [ + "True", + "False" + ] + }, + "isVariableOrNonNegativeNumber": { + "type": "string", + "enum": [ + "true", + "false" + ] + }, + "resizable": { + "type": "boolean" + }, + "rows": { + "type": "string", + "pattern": "^\\d+$" + }, + "maxLength": { + "type": "string", + "pattern": "^\\d+$" + }, + "editorExtension": { + "type": "string" + }, + "EndpointFilterRule": { + "type": "string" } } }, @@ -254,6 +327,31 @@ }, "resultSelector": { "type": "string" + }, + "RequestVerb": { + "type": "string", + "enum": [ + "GET", + "POST", + "DELETE", + "OPTIONS", + "HEAD", + "PUT", + "TRACE", + "PATCH" + ] + }, + "requestContent": { + "type": "string" + }, + "callbackContextTemplate": { + "type": "string" + }, + "callbackRequiredTemplate": { + "type": "string" + }, + "initialContextTemplate": { + "type": "string" } } } @@ -342,7 +440,7 @@ "messages": { "type": "object" }, - "OutputVariables": { + "outputVariables": { "type": "array", "description": "Describes output variables of task.", "items": { @@ -372,6 +470,9 @@ "executionObject": { "type": "object", "additionalProperties": true, + "required": [ + "target" + ], "properties": { "target": { "type": "string", @@ -384,6 +485,12 @@ "windows" ] } + }, + "argumentFormat": { + "type": "string" + }, + "workingDirectory": { + "type": "string" } } } From f7e45887cddc8bc425fad91c7dfa17ce76a1758c Mon Sep 17 00:00:00 2001 From: John Reilly Date: Thu, 10 Dec 2020 18:19:18 +0000 Subject: [PATCH 190/259] Extend setVariable to allow 'output' variables to be created (#691) * apply @danmccorm changes * trigger build --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 13 +++++++------ node/test/commandtests.ts | 10 ++++++++++ node/test/inputtests.ts | 9 +++++++++ 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 4288140d7..6a17f30ad 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.4", + "version": "2.12.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 3065ff3a9..cc9819b44 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.11.4", + "version": "2.12.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index 869bffe2a..ec0e74036 100644 --- a/node/task.ts +++ b/node/task.ts @@ -154,12 +154,13 @@ export function getVariables(): VariableInfo[] { /** * Sets a variable which will be available to subsequent tasks as well. * - * @param name name of the variable to set - * @param val value to set - * @param secret whether variable is secret. Multi-line secrets are not allowed. Optional, defaults to false + * @param name name of the variable to set + * @param val value to set + * @param secret whether variable is secret. Multi-line secrets are not allowed. Optional, defaults to false + * @param isOutput whether variable is an output variable. Optional, defaults to false * @returns void */ -export function setVariable(name: string, val: string, secret: boolean = false): void { +export function setVariable(name: string, val: string, secret: boolean = false, isOutput: boolean = false): void { // once a secret always a secret let key: string = im._getVariableKey(name); if (im._knownVariableMap.hasOwnProperty(key)) { @@ -183,8 +184,8 @@ export function setVariable(name: string, val: string, secret: boolean = false): // store the metadata im._knownVariableMap[key] = { name: name, secret: secret }; - // write the command - command('task.setvariable', { 'variable': name || '', 'issecret': (secret || false).toString() }, varValue); + // write the setvariable command + command('task.setvariable', { 'variable': name || '', isOutput: (isOutput || false).toString(), 'issecret': (secret || false).toString() }, varValue); } /** diff --git a/node/test/commandtests.ts b/node/test/commandtests.ts index 34daa5a91..fb042f860 100644 --- a/node/test/commandtests.ts +++ b/node/test/commandtests.ts @@ -75,6 +75,16 @@ describe('Command Tests', function () { done(); }) + it ('toString writes isOutput', function (done) { + this.timeout(1000); + + var tc = new tcm.TaskCommand('task.setvariable', { variable: 'bar', isOutput: 'true' }, 'dog'); + assert(tc, 'TaskCommand constructor works'); + var cmdStr = tc.toString(); + assert.equal(cmdStr, '##vso[task.setvariable variable=bar;isOutput=true;]dog'); + done(); + }) + it('handles null properties', function (done) { this.timeout(1000); diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index a45b5d3c0..73697fd90 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -140,6 +140,15 @@ describe('Input Tests', function () { done(); }) + it('sets and gets an output variable', function (done) { + this.timeout(1000); + + tl.setVariable('UnitTestVariable', 'test var value', false, true); + let varVal: string = tl.getVariable('UnitTestVariable'); + assert.equal(varVal, 'test var value'); + + done(); + }) it('sets and gets a secret variable', function (done) { this.timeout(1000); From 674214de1b6ad50ea3a0d51ac778d74bf5ab302c Mon Sep 17 00:00:00 2001 From: Anatoly Bolshakov Date: Tue, 29 Dec 2020 12:55:45 +0300 Subject: [PATCH 191/259] Reverted changes for node 14 (#693) --- node/docs/nodeEnvironment.md | 10 +++++----- node/docs/nodeVersioning.md | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/node/docs/nodeEnvironment.md b/node/docs/nodeEnvironment.md index 7c5978d83..93f2867fc 100644 --- a/node/docs/nodeEnvironment.md +++ b/node/docs/nodeEnvironment.md @@ -1,19 +1,19 @@ # Node Handler Versioning -As of agent version 2.175.0, Azure Pipelines supports running tasks in a Node 14 environment in addition to the previously supported Node 6 and Node 10 environments. +As of agent version 2.144.0, Azure Pipelines supports running tasks in a Node 10 environment in addition to the previously supported Node 6 environment. -To leverage this capability, simply add `Node14` as an execution target: +To leverage this capability, simply add `Node10` as an execution target: ``` "execution": { - "Node14": { + "Node10": { "target": "path/to/entry" } }, ``` -Existing `Node` and `Node10` execution targets will still resolve to a Node 6 and Node 10 environment respectively for now to maintain back-compat. +Existing `Node` execution targets will still resolve to a Node 6 environment for now to maintain back-compat. ### Testing your task -If you use the task-lib for testing, it will automatically use the appropriate version of Node to test your tasks, downloading it if necessary. +If you use the task-lib for testing, it will automatically use the appropriate version of Node to test your tasks, downloading it if necessary. \ No newline at end of file diff --git a/node/docs/nodeVersioning.md b/node/docs/nodeVersioning.md index 1c1f65f72..8ead6a56b 100644 --- a/node/docs/nodeVersioning.md +++ b/node/docs/nodeVersioning.md @@ -1,17 +1,17 @@ -# Node versioning +# Node Handler versioning ## Agent Node Handler -The agent currently has 3 different node handlers that it can use to execute node tasks: Node 6, Node 10, and Node 14. +The agent currently has 2 different node handlers that it can use to execute node tasks: Node 6 and Node 10. The handler used depends on the `execution` property specified in the tasks `task.json`. -If the `execution` property is specified to be `Node`, the task will run on the Node 6 handler, if it is specified to be `Node10` it will run on the Node 10 handler, and if it is specified to be `Node14` it will run on the Node 14 handler. +If the `execution` property is specified to be `Node`, the task will run on the Node 6 handler, if it is specified to be `Node10` it will run on the Node 10 handler. ## Mock-test Node Handler [Unit testing](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops#step-2-unit-testing-your-task-scripts) of tasks can be done using the task-lib's built in mock-task functionality. To ensure tests are run in the same environment as the agent, this library looks for a `task.json` file in the same directory as the supplied task entry point. If no `task.json` is found it searches all ancestor directories as well. -If the `task.json` is still not found, the library defaults to Node 14, otherwise it uses the appropriate handler based on the `execution` property. +If the `task.json` is still not found, the library defaults to node 10, otherwise it uses the appropriate handler based on the `execution` property. If this version of node is not found on the path, the library downloads the appropriate version. ### Behavior overrides From a5967c30c000c3b1d8f7f7e6edba221672c58470 Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Tue, 12 Jan 2021 12:21:26 +0300 Subject: [PATCH 192/259] Updated publish methods to keep it compatible (#696) --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 6a17f30ad..848851735 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.12.0", + "version": "2.12.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index cc9819b44..55a7b8ebe 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.12.0", + "version": "2.12.1", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index ec0e74036..3a7a111ef 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1776,12 +1776,12 @@ export class TestPublisher { constructor(public testRunner: string) { } - public publish(resultFiles?: string[], mergeResults?: string, platform?: string, config?: string, runTitle?: string, publishRunAttachments?: string, testRunSystem?: string) { + public publish(resultFiles?: string | string[], mergeResults?: string, platform?: string, config?: string, runTitle?: string, publishRunAttachments?: string, testRunSystem?: string) { // Could have used an initializer, but wanted to avoid reordering parameters when converting to strict null checks // (A parameter cannot both be optional and have an initializer) testRunSystem = testRunSystem || "VSTSTask"; - var properties = <{ [key: string]: string | string[] }>{}; + var properties = <{ [key: string]: string }>{}; properties['type'] = this.testRunner; if (mergeResults) { @@ -1805,7 +1805,7 @@ export class TestPublisher { } if (resultFiles) { - properties['resultFiles'] = resultFiles; + properties['resultFiles'] = Array.isArray(resultFiles) ? resultFiles.join() : resultFiles; } properties['testRunSystem'] = testRunSystem; @@ -1820,7 +1820,7 @@ export class TestPublisher { export class CodeCoveragePublisher { constructor() { } - public publish(codeCoverageTool?: string, summaryFileLocation?: string, reportDirectory?: string, additionalCodeCoverageFiles?: string) { + public publish(codeCoverageTool?: string, summaryFileLocation?: string, reportDirectory?: string, additionalCodeCoverageFiles?: string | string[]) { var properties = <{ [key: string]: string }>{}; @@ -1837,7 +1837,7 @@ export class CodeCoveragePublisher { } if (additionalCodeCoverageFiles) { - properties['additionalcodecoveragefiles'] = additionalCodeCoverageFiles; + properties['additionalcodecoveragefiles'] = Array.isArray(additionalCodeCoverageFiles) ? additionalCodeCoverageFiles.join() : additionalCodeCoverageFiles; } command('codecoverage.publish', properties, ""); From 0089003f65fd47ff842e54675ebc6fb742ffda5a Mon Sep 17 00:00:00 2001 From: Mark Roghelia Date: Mon, 8 Feb 2021 17:27:58 -0500 Subject: [PATCH 193/259] Update schema for task restrictions (#705) --- tasks.schema.json | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tasks.schema.json b/tasks.schema.json index 7591ceb85..414d4157b 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -462,6 +462,40 @@ } } }, + "restrictions": { + "type": "object", + "additionalProperties": false, + "description": "Restrictions on tasks", + "properties": { + "commands": { + "type": "object", + "additionalProperties": false, + "description": "Restrictions on available task commands", + "properties": { + "mode": { + "type": "string", + "enum": [ + "any", + "restricted" + ] + } + } + }, + "settableVariables": { + "type": "object", + "additionalProperties": false, + "description": "Restrictions on which variables can be set via commands", + "properties": { + "allowed": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, "$schema": { "type": "string" } From d431a263c37ccd70b5e28478844bc79a50222546 Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Fri, 12 Feb 2021 17:29:24 +0300 Subject: [PATCH 194/259] Added retry logic to the cp() method (#707) Co-authored-by: sdobrodeev --- .../resources.resjson/en-US/resources.resjson | 3 +- node/docs/azure-pipelines-task-lib.md | 1 + node/lib.json | 3 +- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 37 ++++++++++++++----- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/node/Strings/resources.resjson/en-US/resources.resjson b/node/Strings/resources.resjson/en-US/resources.resjson index cd5a30851..5abc0b4e7 100644 --- a/node/Strings/resources.resjson/en-US/resources.resjson +++ b/node/Strings/resources.resjson/en-US/resources.resjson @@ -30,5 +30,6 @@ "loc.messages.LIB_OperationFailed": "Failed %s: %s", "loc.messages.LIB_UseFirstGlobMatch": "Multiple workspace matches. using first.", "loc.messages.LIB_MergeTestResultNotSupported": "Merging test results from multiple files to one test run is not supported on this version of build agent for OSX/Linux, each test result file will be published as a separate test run in VSO/TFS.", - "loc.messages.LIB_PlatformNotSupported": "Platform not supported: %s" + "loc.messages.LIB_PlatformNotSupported": "Platform not supported: %s", + "loc.messages.LIB_CopyFileFailed": "Error while copying the file. Attempts left: %s" } \ No newline at end of file diff --git a/node/docs/azure-pipelines-task-lib.md b/node/docs/azure-pipelines-task-lib.md index 153825eb9..6d9b85727 100644 --- a/node/docs/azure-pipelines-task-lib.md +++ b/node/docs/azure-pipelines-task-lib.md @@ -777,6 +777,7 @@ source | string | source path dest | string | destination path options | string | string \-r, \-f or \-rf for recursive and force continueOnError | boolean | optional. whether to continue on error +retryCount | number | optional. Retry count to copy the file. It might help to resolve intermittent issues e.g. with UNC target paths on a remote host.
diff --git a/node/lib.json b/node/lib.json index 1f02f249c..333fa1477 100644 --- a/node/lib.json +++ b/node/lib.json @@ -31,6 +31,7 @@ "LIB_OperationFailed": "Failed %s: %s", "LIB_UseFirstGlobMatch": "Multiple workspace matches. using first.", "LIB_MergeTestResultNotSupported": "Merging test results from multiple files to one test run is not supported on this version of build agent for OSX/Linux, each test result file will be published as a separate test run in VSO/TFS.", - "LIB_PlatformNotSupported": "Platform not supported: %s" + "LIB_PlatformNotSupported": "Platform not supported: %s", + "LIB_CopyFileFailed": "Error while copying the file. Attempts left: %s" } } diff --git a/node/package-lock.json b/node/package-lock.json index 848851735..425b9583f 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.12.1", + "version": "2.12.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 55a7b8ebe..8dd134e33 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.12.1", + "version": "2.12.2", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index 3a7a111ef..c0d662655 100644 --- a/node/task.ts +++ b/node/task.ts @@ -560,8 +560,8 @@ export interface FsOptions { } export function writeFile(file: string, data: string | Buffer, options?: string | FsOptions) { - if(typeof(options) === 'string'){ - fs.writeFileSync(file, data, {encoding: options}); + if (typeof (options) === 'string') { + fs.writeFileSync(file, data, { encoding: options }); } else { fs.writeFileSync(file, data, options); @@ -744,16 +744,33 @@ export function ls(options: string, paths: string[]): string[] { * @param dest destination path * @param options string -r, -f or -rf for recursive and force * @param continueOnError optional. whether to continue on error + * @param retryCount optional. Retry count to copy the file. It might help to resolve intermittent issues e.g. with UNC target paths on a remote host. */ -export function cp(source: string, dest: string, options?: string, continueOnError?: boolean): void { - if (options) { - shell.cp(options, source, dest); - } - else { - shell.cp(source, dest); +export function cp(source: string, dest: string, options?: string, continueOnError?: boolean, retryCount: number = 0): void { + while (retryCount >= 0) { + if (options) { + shell.cp(options, source, dest); + } + else { + shell.cp(source, dest); + } + try { + _checkShell('cp', false); + break; + } catch (e) { + if (retryCount <= 0) { + if (continueOnError) { + warning(e); + break; + } else { + throw e; + } + } else { + console.log(loc('LIB_CopyFileFailed', retryCount)); + retryCount--; + } + } } - - _checkShell('cp', continueOnError); } /** From 578d7074e317355d638bea693b562bce8744731a Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Wed, 17 Feb 2021 10:48:59 +0300 Subject: [PATCH 195/259] Preparing for release of the 3.1.0 version (#713) --- azure-pipelines.yml | 39 +- node/README.md | 16 +- node/buildutils.js | 2 +- node/dependencies/typings.json | 8 - node/docs/nodeEnvironment.md | 8 +- node/docs/nodeVersioning.md | 2 +- node/index.ts | 2 - node/internal.ts | 20 +- node/make.js | 3 +- node/mock-answer.ts | 2 - node/mock-run.ts | 1 - node/mock-task.ts | 4 + node/mock-test.ts | 8 +- node/mock-toolrunner.ts | 4 +- node/package-lock.json | 321 ++- node/package.json | 17 +- node/task.ts | 130 +- node/test/cctests.ts | 3 - node/test/commandtests.ts | 3 - node/test/dirtests.ts | 12 +- node/test/disktests.ts | 3 - node/test/filtertests.ts | 3 - node/test/findmatchtests.ts | 3 - node/test/inputtests.ts | 3 - node/test/internalhelpertests.ts | 3 - node/test/legacyfindfilestests.ts | 3 - node/test/loctests.ts | 5 +- node/test/matchtests.ts | 3 - node/test/mocktests.ts | 9 +- node/test/resulttests.ts | 3 - node/test/testindex.d.ts | 4 - node/test/testutil.ts | 7 +- node/test/toolrunnertests.ts | 37 +- node/test/tsconfig.json | 14 +- node/test/vaulttests.ts | 3 - node/toolrunner.ts | 20 +- node/typings.json | 15 - node/typings/globals/glob/index.d.ts | 106 - node/typings/globals/glob/typings.json | 8 - node/typings/globals/minimatch/index.d.ts | 61 - node/typings/globals/minimatch/typings.json | 8 - node/typings/globals/mocha/index.d.ts | 234 -- node/typings/globals/mocha/typings.json | 8 - node/typings/globals/mockery/index.d.ts | 30 - node/typings/globals/mockery/typings.json | 8 - node/typings/globals/node/index.d.ts | 2578 ------------------- node/typings/globals/node/typings.json | 8 - node/typings/globals/q/index.d.ts | 357 --- node/typings/globals/q/typings.json | 8 - node/typings/globals/semver/index.d.ts | 175 -- node/typings/globals/semver/typings.json | 8 - node/typings/globals/shelljs/index.d.ts | 531 ---- node/typings/globals/shelljs/typings.json | 8 - node/typings/index.d.ts | 8 - res/UseNode5.ps1 | 18 - 55 files changed, 430 insertions(+), 4475 deletions(-) delete mode 100644 node/dependencies/typings.json delete mode 100644 node/test/testindex.d.ts delete mode 100644 node/typings.json delete mode 100644 node/typings/globals/glob/index.d.ts delete mode 100644 node/typings/globals/glob/typings.json delete mode 100644 node/typings/globals/minimatch/index.d.ts delete mode 100644 node/typings/globals/minimatch/typings.json delete mode 100644 node/typings/globals/mocha/index.d.ts delete mode 100644 node/typings/globals/mocha/typings.json delete mode 100644 node/typings/globals/mockery/index.d.ts delete mode 100644 node/typings/globals/mockery/typings.json delete mode 100644 node/typings/globals/node/index.d.ts delete mode 100644 node/typings/globals/node/typings.json delete mode 100644 node/typings/globals/q/index.d.ts delete mode 100644 node/typings/globals/q/typings.json delete mode 100644 node/typings/globals/semver/index.d.ts delete mode 100644 node/typings/globals/semver/typings.json delete mode 100644 node/typings/globals/shelljs/index.d.ts delete mode 100644 node/typings/globals/shelljs/typings.json delete mode 100644 node/typings/index.d.ts delete mode 100644 res/UseNode5.ps1 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 63eeddff0..f3ce262fb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,10 +23,11 @@ jobs: command: install workingDir: node - # use node 5 - - powershell: | - & "$(build.sourcesDirectory)/res/UseNode5.ps1" - displayName: (azure-pipelines-task-lib) use node 5.10.1 + # use node 10 + - task: NodeTool@0 + displayName: (azure-pipelines-task-lib) use node 10.23.0 + inputs: + versionSpec: "10.23.0" # build/test - script: | @@ -35,12 +36,6 @@ jobs: workingDirectory: node displayName: (azure-pipelines-task-lib) node make.js test - # use node 6 - - task: NodeTool@0 - displayName: (azure-pipelines-task-lib) use node 6.10.3 - inputs: - versionSpec: "6.10.3" - # build/test - script: | chcp 437 @@ -83,23 +78,17 @@ jobs: command: install workingDir: node - # use node 5 + # use node 10 - task: NodeTool@0 - displayName: (azure-pipelines-task-lib) use node 5.10.1 + displayName: (azure-pipelines-task-lib) use node 10.23.0 inputs: - versionSpec: "5.10.1" + versionSpec: "10.23.0" # build/test - script: node make.js test workingDirectory: node displayName: (azure-pipelines-task-lib) node make.js test - # use node 6 - - task: NodeTool@0 - displayName: (azure-pipelines-task-lib) use node 6.10.3 - inputs: - versionSpec: "6.10.3" - # build/test - script: node make.js test displayName: (azure-pipelines-task-lib) node make.js test @@ -135,23 +124,17 @@ jobs: command: install workingDir: node - # use node 5 + # use node 10 - task: NodeTool@0 - displayName: (azure-pipelines-task-lib) use node 5.10.1 + displayName: (azure-pipelines-task-lib) use node 10.23.0 inputs: - versionSpec: "5.10.1" + versionSpec: "10.23.0" # build/test - script: node make.js test workingDirectory: node displayName: (azure-pipelines-task-lib) node make.js test - # use node 6 - - task: NodeTool@0 - displayName: (azure-pipelines-task-lib) use node 6.10.3 - inputs: - versionSpec: "6.10.3" - # build/test - script: node make.js test displayName: (azure-pipelines-task-lib) node make.js test diff --git a/node/README.md b/node/README.md index e3ea39b2f..6f5e66409 100644 --- a/node/README.md +++ b/node/README.md @@ -11,19 +11,31 @@ Cross platform tasks are written in TypeScript. It is the preferred way to writ [![NPM version][npm-lib-image]][npm-lib-url] -Step by Step: [Create Task](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=vsts) +Step by Step: [Create Task](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=vsts) Documentation: [TypeScript API](docs/azure-pipelines-task-lib.md), [task JSON schema](https://aka.ms/vsts-tasks.schema.json) Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/minagent.md), [Proxy](docs/proxy.md), [Certificate](docs/cert.md) +## Node 10 Upgrade Notice + +Azure DevOps is currently working to establish Node 10 as the new preferred runtime for tasks, upgrading from Node 6. +Relevant work is happening in the `master`` branch and the major version should be used with Node 10 is 3. +Previous major version is stored in the `releases/2.x` + +### Upgrading to Node 10 + +Upgrading your tasks from Node 6 should be relatively painless, however there are some things to note: +* Typescript has been upgraded to TS 4. Older versions of TS may or may not work with Node 14 or the 3.x branch. We recommend upgrading to TS 4 when upgrading to task-lib 3.x. +* Node has made some changes to `fs` between Node 6 and Node 10. It is worth reviewing and testing your tasks thoroughly before publishing updates to Node 10. + ## Reference Examples The [ShellScript Task](https://github.com/Microsoft/azure-pipelines-tasks/tree/master/Tasks/ShellScriptV2) and the [XCode Task](https://github.com/Microsoft/azure-pipelines-tasks/tree/master/Tasks/XcodeV5) are good examples. ## Contributing -We are accepting contributions and we try to stay on top of issues. +We are accepting contributions and we try to stay on top of issues. [Contribution Guide](../CONTRIBUTING.md). diff --git a/node/buildutils.js b/node/buildutils.js index 534774ce8..ac8ef612f 100644 --- a/node/buildutils.js +++ b/node/buildutils.js @@ -34,7 +34,7 @@ exports.getExternals = function () { // download the same version of node used by the agent // and add node to the PATH var nodeUrl = 'https://nodejs.org/dist'; - var nodeVersion = 'v6.17.1'; + var nodeVersion = 'v10.23.0'; switch (platform) { case 'darwin': var nodeArchivePath = downloadArchive(nodeUrl + '/' + nodeVersion + '/node-' + nodeVersion + '-darwin-x64.tar.gz'); diff --git a/node/dependencies/typings.json b/node/dependencies/typings.json deleted file mode 100644 index 9afa921b7..000000000 --- a/node/dependencies/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "azure-pipelines-task-lib", - "main": "task.d.ts", - "globalDependencies": { - "node": "registry:dt/node#6.0.0+20160709114037", - "q": "registry:dt/q#0.0.0+20160613154756" - } -} diff --git a/node/docs/nodeEnvironment.md b/node/docs/nodeEnvironment.md index 93f2867fc..3e0e2a4dc 100644 --- a/node/docs/nodeEnvironment.md +++ b/node/docs/nodeEnvironment.md @@ -6,10 +6,10 @@ To leverage this capability, simply add `Node10` as an execution target: ``` "execution": { - "Node10": { - "target": "path/to/entry" - } - }, + "Node10": { + "target": "path/to/entry" + } +}, ``` Existing `Node` execution targets will still resolve to a Node 6 environment for now to maintain back-compat. diff --git a/node/docs/nodeVersioning.md b/node/docs/nodeVersioning.md index 8ead6a56b..9c69015ca 100644 --- a/node/docs/nodeVersioning.md +++ b/node/docs/nodeVersioning.md @@ -11,7 +11,7 @@ If the `execution` property is specified to be `Node`, the task will run on the [Unit testing](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops#step-2-unit-testing-your-task-scripts) of tasks can be done using the task-lib's built in mock-task functionality. To ensure tests are run in the same environment as the agent, this library looks for a `task.json` file in the same directory as the supplied task entry point. If no `task.json` is found it searches all ancestor directories as well. -If the `task.json` is still not found, the library defaults to node 10, otherwise it uses the appropriate handler based on the `execution` property. +If the `task.json` is still not found, the library defaults to Node 10, otherwise it uses the appropriate handler based on the `execution` property. If this version of node is not found on the path, the library downloads the appropriate version. ### Behavior overrides diff --git a/node/index.ts b/node/index.ts index d3d76a1ea..095d090b4 100644 --- a/node/index.ts +++ b/node/index.ts @@ -1,3 +1 @@ -/// - import trm = require('./task'); diff --git a/node/internal.ts b/node/internal.ts index eed6bd4e1..70192ed06 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -27,7 +27,7 @@ export var _vault: vm.Vault; // async await needs generators in node 4.x+ if (semver.lt(process.versions.node, '4.2.0')) { - this.warning('Tasks require a new agent. Upgrade your agent or node to 4.2.0 or later'); + _warning('Tasks require a new agent. Upgrade your agent or node to 4.2.0 or later'); } //----------------------------------------------------- @@ -140,7 +140,6 @@ function _loadLocStrings(resourceFile: string, culture: string): { [key: string] /** * Sets the location of the resources json. This is typically the task.json file. * Call once at the beginning of the script before any calls to loc. - * * @param path Full path to the json. * @param ignoreWarnings Won't throw warnings if path already set. * @returns void @@ -177,7 +176,7 @@ export function _setResourcePath(path: string, ignoreWarnings: boolean = false): /** * Gets the localized string from the json resource file. Optionally formats with additional params. - * + * * @param key key of the resources string in the resource file * @param param additional params for formatting the string * @returns string @@ -224,7 +223,7 @@ export function _loc(key: string, ...param: any[]): string { /** * Gets a variable value that is defined on the build/release definition or set at runtime. - * + * * @param name name of the variable to get * @returns string */ @@ -301,9 +300,9 @@ export function _debug(message: string): void { /** * Returns whether a path exists. - * + * * @param path path to check - * @returns boolean + * @returns boolean */ export function _exist(path: string): boolean { var exist = false; @@ -322,7 +321,7 @@ export function _exist(path: string): boolean { /** * Checks whether a path exists. * If the path does not exist, it will throw. - * + * * @param p path to check * @param name name only used in error message to identify the path * @returns void @@ -337,7 +336,7 @@ export function _checkPath(p: string, name: string): void { /** * Returns path of a tool had the tool actually been invoked. Resolves via paths. * If you check and the tool does not exist, it will throw. - * + * * @param tool name of the tool * @param check whether to check if tool exists * @returns string @@ -723,10 +722,11 @@ export function _loadData(): void { } // store the secret - if (process.env[envvar]) { + var value = process.env[envvar]; + if (value) { ++loaded; _debug('loading ' + envvar); - _vault.storeSecret(envvar, process.env[envvar]); + _vault.storeSecret(envvar, value); delete process.env[envvar]; } } diff --git a/node/make.js b/node/make.js index 6e914da41..7782f4240 100644 --- a/node/make.js +++ b/node/make.js @@ -1,4 +1,3 @@ - require('shelljs/make'); var fs = require('fs'); var path = require('path'); @@ -28,7 +27,6 @@ target.build = function() { target.loc(); run('tsc --outDir ' + buildPath); - cp(rp('dependencies/typings.json'), buildPath); cp(rp('package.json'), buildPath); cp(rp('package-lock.json'), buildPath); cp(rp('README.md'), buildPath); @@ -48,6 +46,7 @@ target.test = function() { cp('-Rf', rp('test/scripts'), testPath); cp('-Rf', rp('test/fakeTasks'), testPath); process.env['TASKLIB_INPROC_UNITS'] = '1'; // export task-lib internals for internal unit testing + set('+e'); // Don't throw an exception when tests fail run('mocha ' + testPath); } diff --git a/node/mock-answer.ts b/node/mock-answer.ts index 338e62c2c..6b67e850f 100644 --- a/node/mock-answer.ts +++ b/node/mock-answer.ts @@ -1,5 +1,3 @@ -import * as path from 'path'; -import * as fs from 'fs'; import * as task from './task'; export interface TaskLibAnswerExecResult { diff --git a/node/mock-run.ts b/node/mock-run.ts index 54c05c37d..9989e40e5 100644 --- a/node/mock-run.ts +++ b/node/mock-run.ts @@ -2,7 +2,6 @@ import ma = require('./mock-answer'); import mockery = require('mockery'); import im = require('./internal'); - export class TaskMockRunner { constructor(taskPath: string) { this._taskPath = taskPath; diff --git a/node/mock-task.ts b/node/mock-task.ts index 1f062e9d6..3a30bf464 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -125,6 +125,10 @@ export class FsStats implements fs.Stats { mtime: Date = new Date(); ctime: Date = new Date(); birthtime: Date = new Date(); + atimeMs: number; + mtimeMs: number; + ctimeMs: number; + birthtimeMs: number; setAnswers(mockResponses: any): void { this.m_isFile = mockResponses['isFile'] || this.m_isFile; diff --git a/node/mock-test.ts b/node/mock-test.ts index 4ad3c9e6d..5b56c3e7c 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -5,11 +5,11 @@ import os = require('os'); import path = require('path'); import cmdm = require('./taskcommand'); import shelljs = require('shelljs'); -import syncRequest = require('sync-request'); +import syncRequest from 'sync-request'; const COMMAND_TAG = '[command]'; const COMMAND_LENGTH = COMMAND_TAG.length; -const downloadDirectory = path.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE, 'azure-pipelines-task-lib', '_download'); +const downloadDirectory = path.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE as string, 'azure-pipelines-task-lib', '_download'); export class MockTestRunner { constructor(testPath: string, taskJsonPath?: string) { @@ -200,8 +200,8 @@ export class MockTestRunner { } if (!nodeVersionFound) { - console.warn('Unable to determine execution type from task.json, defaulting to use Node 14'); - return 14; + console.warn('Unable to determine execution type from task.json, defaulting to use Node 10'); + return 10; } return 6; diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 15b5b79de..4c6c9d237 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -17,7 +17,7 @@ export interface IExecOptions extends IExecSyncOptions { export interface IExecSyncOptions { cwd?: string; - env?: { [key: string]: string }; + env?: { [key: string]: string | undefined }; silent?: boolean; outStream: NodeJS.WritableStream; errStream: NodeJS.WritableStream; @@ -152,7 +152,7 @@ export class ToolRunner extends events.EventEmitter { this._debug('tempPath=' + process.env['MOCK_TEMP_PATH']); if (process.env['MOCK_IGNORE_TEMP_PATH'] === 'true') { // Using split/join to replace the temp path - cmdString = cmdString.split(process.env['MOCK_TEMP_PATH']).join(''); + cmdString = cmdString.split(process.env['MOCK_TEMP_PATH'] || "").join(''); } return cmdString; diff --git a/node/package-lock.json b/node/package-lock.json index 425b9583f..5b425a4d4 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,14 +1,95 @@ { "name": "azure-pipelines-task-lib", - "version": "2.12.2", + "version": "3.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-OU2+C7X+5Gs42JZzXoto7yOQ0A0=", + "requires": { + "@types/node": "*" + } + }, + "@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha1-yayFsqX9GENbjIXZ7LUObWyJP/g=", + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, + "@types/mockery": { + "version": "1.4.29", + "resolved": "https://registry.npmjs.org/@types/mockery/-/mockery-1.4.29.tgz", + "integrity": "sha1-m6It838H43gP/4Ux0aOOYz+UV6U=", + "dev": true + }, + "@types/node": { + "version": "10.17.44", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.44.tgz", + "integrity": "sha512-vHPAyBX1ffLcy4fQHmDyIUMUb42gHZjPHU66nhvbMzAWJqHnySGZ6STwN3rwrnSd1FHB0DI/RWgGELgKSYRDmw==" + }, + "@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", + "dev": true + }, + "@types/qs": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==" + }, + "@types/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==", + "dev": true + }, + "@types/shelljs": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.8.tgz", + "integrity": "sha512-lD3LWdg6j8r0VRBFahJVaxoW0SIcswxKaFUrmKl33RJVeeoNYQAz4uqCJ5Z6v4oIBOsC5GozX+I5SorIKiTcQA==", + "dev": true, + "requires": { + "@types/glob": "*", + "@types/node": "*" + } + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -29,10 +110,23 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } }, "concat-map": { "version": "0.0.1", @@ -40,10 +134,11 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.1.tgz", - "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "requires": { + "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" @@ -63,23 +158,41 @@ "ms": "2.0.0" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=" }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -96,25 +209,35 @@ "dev": true }, "http-basic": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-2.5.1.tgz", - "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", "requires": { - "caseless": "~0.11.0", - "concat-stream": "^1.4.6", - "http-response-object": "^1.0.0" + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" } }, "http-response-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-1.1.0.tgz", - "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM=" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "requires": { + "@types/node": "^10.0.3" + }, + "dependencies": { + "@types/node": { + "version": "10.17.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.35.tgz", + "integrity": "sha512-gXx7jAWpMddu0f7a+L+txMplp3FnHl53OhQIF9puXKq3hDGY/GjH+MF04oWnV/adPSCrbtHumDCFwzq2VhltWA==" + } + } }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -125,11 +248,29 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -222,28 +363,36 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } }, + "parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha1-juqz5U+laSD+Fro493+iGqzC104=" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", "requires": { - "asap": "~2.0.3" + "asap": "~2.0.6" } }, "q": { @@ -252,28 +401,44 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=" + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" }, "readable-stream": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", - "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", + "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "requires": { + "path-parse": "^1.0.6" + } + }, "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "semver": { "version": "5.5.0", @@ -281,39 +446,64 @@ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" }, "shelljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", - "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } }, "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" } }, "sync-request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-3.0.1.tgz", - "integrity": "sha1-yqEjWq+Im6UBB2oYNMQ2gwqC+3M=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "requires": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + } + }, + "sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", "requires": { - "concat-stream": "^1.4.7", - "http-response-object": "^1.0.1", - "then-request": "^2.0.1" + "get-port": "^3.1.0" } }, "then-request": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz", - "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", "requires": { - "caseless": "~0.11.0", - "concat-stream": "^1.4.7", - "http-basic": "^2.5.1", - "http-response-object": "^1.1.0", - "promise": "^7.1.1", - "qs": "^6.1.0" + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.64", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.64.tgz", + "integrity": "sha512-/EwBIb+imu8Qi/A3NF9sJ9iuKo7yV+pryqjmeRqaU0C4wBAOhas5mdvoYeJ5PCKrh6thRSJHdoasFqh3BQGILA==" + } } }, "typedarray": { @@ -322,9 +512,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.3.tgz", - "integrity": "sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", "dev": true }, "util-deprecate": { @@ -340,8 +530,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } } diff --git a/node/package.json b/node/package.json index 8dd134e33..74b0842c5 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "2.12.2", + "version": "3.1.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", @@ -29,14 +29,21 @@ "dependencies": { "minimatch": "3.0.4", "mockery": "^1.7.0", - "q": "^1.1.2", + "q": "^1.5.1", "semver": "^5.1.0", - "shelljs": "^0.3.0", + "shelljs": "^0.8.4", "uuid": "^3.0.1", - "sync-request": "3.0.1" + "sync-request": "6.1.0" }, "devDependencies": { + "@types/minimatch": "3.0.3", + "@types/mocha": "^5.0.0", + "@types/mockery": "^1.4.29", + "@types/node": "^10.17.0", + "@types/q": "^1.5.4", + "@types/semver": "^7.3.4", + "@types/shelljs": "^0.8.8", "mocha": "5.2.0", - "typescript": "^3.0.0" + "typescript": "^4.0.0" } } diff --git a/node/task.ts b/node/task.ts index c0d662655..9726efa8b 100644 --- a/node/task.ts +++ b/node/task.ts @@ -66,7 +66,7 @@ export const setErrStream = im._setErrStream; * Execution will continue. * If not set, task will be Succeeded. * If multiple calls are made to setResult the most pessimistic call wins (Failed) regardless of the order of calls. - * + * * @param result TaskResult enum of Succeeded, SucceededWithIssues, Failed, Cancelled or Skipped. * @param message A message which will be logged as an error issue if the result is Failed. * @param done Optional. Instructs the agent the task is done. This is helpful when child processes @@ -212,7 +212,7 @@ export interface VariableInfo { /** * Gets the value of an input. * If required is true and the value is not set, it will throw. - * + * * @param name name of the input to get * @param required whether input is required. optional, defaults to false * @returns string @@ -232,7 +232,7 @@ export function getInput(name: string, required?: boolean): string | undefined { * Gets the value of an input and converts to a bool. Convenience. * If required is true and the value is not set, it will throw. * If required is false and the value is not set, returns false. - * + * * @param name name of the bool input to get * @param required whether input is required. optional, defaults to false * @returns boolean @@ -248,7 +248,7 @@ export function getBoolInput(name: string, required?: boolean): boolean { * IMPORTANT: Do not use this function for splitting additional args! Instead use argString(), which * follows normal argument splitting rules and handles values encapsulated by quotes. * If required is true and the value is not set, it will throw. - * + * * @param name name of the input to get * @param delim delimiter to split on * @param required whether input is required. optional, defaults to false @@ -274,7 +274,7 @@ export function getDelimitedInput(name: string, delim: string | RegExp, required * Checks whether a path inputs value was supplied by the user * File paths are relative with a picker, so an empty path is the root of the repo. * Useful if you need to condition work (like append an arg) if a value was supplied - * + * * @param name name of the path input to check * @returns boolean */ @@ -293,10 +293,10 @@ export function filePathSupplied(name: string): boolean { * It will be quoted for you if it isn't already and contains spaces * If required is true and the value is not set, it will throw. * If check is true and the path does not exist, it will throw. - * + * * @param name name of the input to get * @param required whether input is required. optional, defaults to false - * @param check whether path is checked. optional, defaults to false + * @param check whether path is checked. optional, defaults to false * @returns string */ export function getPathInput(name: string, required?: boolean, check?: boolean): string | undefined { @@ -317,12 +317,12 @@ export function getPathInput(name: string, required?: boolean, check?: boolean): /** * Gets the url for a service endpoint * If the url was not set and is not optional, it will throw. - * + * * @param id name of the service endpoint * @param optional whether the url is optional * @returns string */ -export function getEndpointUrl(id: string, optional: boolean): string { +export function getEndpointUrl(id: string, optional: boolean): string | undefined { var urlval = process.env['ENDPOINT_URL_' + id]; if (!optional && !urlval) { @@ -342,7 +342,7 @@ export function getEndpointUrl(id: string, optional: boolean): string { * @param optional whether the endpoint data is optional * @returns {string} value of the endpoint data parameter */ -export function getEndpointDataParameter(id: string, key: string, optional: boolean): string { +export function getEndpointDataParameter(id: string, key: string, optional: boolean): string | undefined { var dataParamVal = process.env['ENDPOINT_DATA_' + id + '_' + key.toUpperCase()]; if (!optional && !dataParamVal) { @@ -408,7 +408,7 @@ export interface EndpointAuthorization { /** * Gets the authorization details for a service endpoint * If the authorization was not set and is not optional, it will throw. - * + * * @param id name of the service endpoint * @param optional whether the url is optional * @returns string @@ -441,18 +441,18 @@ export function getEndpointAuthorization(id: string, optional: boolean): Endpoin /** * Gets the name for a secure file - * + * * @param id secure file id * @returns string */ -export function getSecureFileName(id: string): string { +export function getSecureFileName(id: string): string | undefined { var name = process.env['SECUREFILE_NAME_' + id]; debug('secure file name for id ' + id + ' = ' + name); return name; } -/** +/** * Gets the secure file ticket that can be used to download the secure file contents * * @param id name of the secure file @@ -471,7 +471,7 @@ export function getSecureFileTicket(id: string): string | undefined { /** * Gets a variable value that is set by previous step from the same wrapper task. * Requires a 2.115.0 agent or higher. - * + * * @param name name of the variable to get * @returns string */ @@ -489,7 +489,7 @@ export function getTaskVariable(name: string): string | undefined { /** * Sets a task variable which will only be available to subsequent steps belong to the same wrapper task. * Requires a 2.115.0 agent or higher. - * + * * @param name name of the variable to set * @param val value to set * @param secret whether variable is secret. optional, defaults to false @@ -540,12 +540,12 @@ export interface FsStats extends fs.Stats { } /** - * Get's stat on a path. + * Get's stat on a path. * Useful for checking whether a file or directory. Also getting created, modified and accessed time. * see [fs.stat](https://nodejs.org/api/fs.html#fs_class_fs_stats) - * + * * @param path path to check - * @returns fsStat + * @returns fsStat */ export function stats(path: string): FsStats { return fs.statSync(path); @@ -553,15 +553,9 @@ export function stats(path: string): FsStats { export const exist = im._exist; -export interface FsOptions { - encoding?: string; - mode?: number; - flag?: string; -} - -export function writeFile(file: string, data: string | Buffer, options?: string | FsOptions) { - if (typeof (options) === 'string') { - fs.writeFileSync(file, data, { encoding: options }); +export function writeFile(file: string, data: string | Buffer, options?: BufferEncoding | fs.WriteFileOptions) { + if (typeof(options) === 'string'){ + fs.writeFileSync(file, data, {encoding: options as BufferEncoding}); } else { fs.writeFileSync(file, data, options); @@ -572,7 +566,7 @@ export function writeFile(file: string, data: string | Buffer, options?: string * @deprecated Use `getPlatform` * Useful for determining the host operating system. * see [os.type](https://nodejs.org/api/os.html#os_os_type) - * + * * @return the name of the operating system */ export function osType(): string { @@ -596,7 +590,7 @@ export function getPlatform(): Platform { /** * Returns the process's current working directory. * see [process.cwd](https://nodejs.org/api/process.html#process_process_cwd) - * + * * @return the path to the current working directory of the process */ export function cwd(): string { @@ -607,9 +601,9 @@ export const checkPath = im._checkPath; /** * Change working directory. - * + * * @param path new working directory path - * @returns void + * @returns void */ export function cd(path: string): void { if (path) { @@ -620,7 +614,7 @@ export function cd(path: string): void { /** * Change working directory and push it on the stack - * + * * @param path new working directory path * @returns void */ @@ -631,7 +625,7 @@ export function pushd(path: string): void { /** * Change working directory back to previously pushed directory - * + * * @returns void */ export function popd(): void { @@ -642,7 +636,7 @@ export function popd(): void { /** * Make a directory. Creates the full path with folders in between * Will throw if it fails - * + * * @param p path to create * @returns void */ @@ -739,10 +733,10 @@ export function ls(options: string, paths: string[]): string[] { /** * Copies a file or folder. - * + * * @param source source path * @param dest destination path - * @param options string -r, -f or -rf for recursive and force + * @param options string -r, -f or -rf for recursive and force * @param continueOnError optional. whether to continue on error * @param retryCount optional. Retry count to copy the file. It might help to resolve intermittent issues e.g. with UNC target paths on a remote host. */ @@ -775,10 +769,10 @@ export function cp(source: string, dest: string, options?: string, continueOnErr /** * Moves a path. - * + * * @param source source path * @param dest destination path - * @param options string -f or -n for force and no clobber + * @param options string -f or -n for force and no clobber * @param continueOnError optional. whether to continue on error */ export function mv(source: string, dest: string, options?: string, continueOnError?: boolean): void { @@ -1161,7 +1155,7 @@ function _legacyFindFiles_getMatchingItems( /** * Remove a path recursively with force - * + * * @param inputPath path to remove * @throws when the file or directory exists but could not be deleted. */ @@ -1243,7 +1237,7 @@ export function rmRF(inputPath: string): void { * Exec a tool. Convenience wrapper over ToolRunner to exec with args in one call. * Output will be streamed to the live console. * Returns promise with return code - * + * * @param tool path to tool to exec * @param args an arg string or array of args * @param options optional exec options. See IExecOptions @@ -1269,9 +1263,9 @@ export function exec(tool: string, args: any, options?: trm.IExecOptions): Q.Pro /** * Exec a tool synchronously. Convenience wrapper over ToolRunner to execSync with args in one call. * Output will be *not* be streamed to the live console. It will be returned after execution is complete. - * Appropriate for short running tools + * Appropriate for short running tools * Returns IExecResult with output and return code - * + * * @param tool path to tool to exec * @param args an arg string or array of args * @param options optional exec options. See IExecSyncOptions @@ -1297,7 +1291,7 @@ export function execSync(tool: string, args: string | string[], options?: trm.IE /** * Convenience factory to create a ToolRunner. - * + * * @param tool path to tool to exec * @returns ToolRunner */ @@ -1885,11 +1879,11 @@ export class CodeCoverageEnabler { //----------------------------------------------------- /** - * Upload user interested file as additional log information + * Upload user interested file as additional log information * to the current timeline record. - * + * * The file shall be available for download along with task logs. - * + * * @param path Path to the file that should be uploaded. * @returns void */ @@ -1901,7 +1895,7 @@ export function uploadFile(path: string) { * Instruction for the agent to update the PATH environment variable. * The specified directory is prepended to the PATH. * The updated environment variable will be reflected in subsequent tasks. - * + * * @param path Local directory path. * @returns void */ @@ -1912,9 +1906,9 @@ export function prependPath(path: string) { /** * Upload and attach summary markdown to current timeline record. - * This summary shall be added to the build/release summary and + * This summary shall be added to the build/release summary and * not available for download with logs. - * + * * @param path Local directory path. * @returns void */ @@ -1925,8 +1919,8 @@ export function uploadSummary(path: string) { /** * Upload and attach attachment to current timeline record. * These files are not available for download with logs. - * These can only be referred to by extensions using the type or name values. - * + * These can only be referred to by extensions using the type or name values. + * * @param type Attachment type. * @param name Attachment name. * @param path Attachment path. @@ -1938,9 +1932,9 @@ export function addAttachment(type: string, name: string, path: string) { /** * Set an endpoint field with given value. - * Value updated will be retained in the endpoint for + * Value updated will be retained in the endpoint for * the subsequent tasks that execute within the same job. - * + * * @param id Endpoint id. * @param field FieldType enum of AuthParameter, DataParameter or Url. * @param key Key. @@ -1953,9 +1947,9 @@ export function setEndpoint(id: string, field: FieldType, key: string, value: st /** * Set progress and current operation for current task. - * + * * @param percent Percentage of completion. - * @param currentOperation Current pperation. + * @param currentOperation Current pperation. * @returns void */ export function setProgress(percent: number, currentOperation: string) { @@ -2026,11 +2020,11 @@ export function logIssue(type: IssueType, message: string, sourcePath?: string, //----------------------------------------------------- /** - * Upload user interested file as additional log information + * Upload user interested file as additional log information * to the current timeline record. - * + * * The file shall be available for download along with task logs. - * + * * @param containerFolder Folder that the file will upload to, folder will be created if needed. * @param path Path to the file that should be uploaded. * @param name Artifact name. @@ -2041,11 +2035,11 @@ export function uploadArtifact(containerFolder: string, path: string, name?: str } /** - * Create an artifact link, artifact location is required to be - * a file container path, VC path or UNC share path. - * + * Create an artifact link, artifact location is required to be + * a file container path, VC path or UNC share path. + * * The file shall be available for download along with task logs. - * + * * @param name Artifact name. * @param path Path to the file that should be associated. * @param artifactType ArtifactType enum of Container, FilePath, VersionControl, GitRef or TfvcLabel. @@ -2061,7 +2055,7 @@ export function associateArtifact(name: string, path: string, artifactType: Arti /** * Upload user interested log to build’s container “logs\tool” folder. - * + * * @param path Path to the file that should be uploaded. * @returns void */ @@ -2071,7 +2065,7 @@ export function uploadBuildLog(path: string) { /** * Update build number for current build. - * + * * @param value Value to be assigned as the build number. * @returns void */ @@ -2081,7 +2075,7 @@ export function updateBuildNumber(value: string) { /** * Add a tag for current build. - * + * * @param value Tag value. * @returns void */ @@ -2095,7 +2089,7 @@ export function addBuildTag(value: string) { /** * Update release name for current release. - * + * * @param value Value to be assigned as the release name. * @returns void */ @@ -2117,7 +2111,7 @@ exports.ToolRunner = trm.ToolRunner; // async await needs generators in node 4.x+ if (semver.lt(process.versions.node, '4.2.0')) { - this.warning('Tasks require a new agent. Upgrade your agent or node to 4.2.0 or later'); + warning('Tasks require a new agent. Upgrade your agent or node to 4.2.0 or later'); } //------------------------------------------------------------------- diff --git a/node/test/cctests.ts b/node/test/cctests.ts index 8519c50a1..f2f6e94c4 100644 --- a/node/test/cctests.ts +++ b/node/test/cctests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import * as tl from '../_build/task'; import testutil = require('./testutil'); diff --git a/node/test/commandtests.ts b/node/test/commandtests.ts index fb042f860..bc49bda25 100644 --- a/node/test/commandtests.ts +++ b/node/test/commandtests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import * as tcm from '../_build/taskcommand'; diff --git a/node/test/dirtests.ts b/node/test/dirtests.ts index 41dc22744..9321eb539 100644 --- a/node/test/dirtests.ts +++ b/node/test/dirtests.ts @@ -1,5 +1,5 @@ -/// -/// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. import assert = require('assert'); import path = require('path'); @@ -31,7 +31,7 @@ describe('Dir Operation Tests', function () { this.timeout(1000); console.log('node version: ' + process.version); - const supportedNodeVersions = ['v5.10.1', 'v6.10.3', 'v6.17.1', 'v8.9.1', 'v10.17.0', 'v10.18.0']; + const supportedNodeVersions = ['v5.10.1', 'v6.10.3', 'v6.17.1', 'v8.9.1', 'v10.17.0', 'v10.18.0', 'v10.23.0', 'v14.11.0']; if (supportedNodeVersions.indexOf(process.version) === -1) { assert.fail(`expected node node version to be one of ${supportedNodeVersions.map(o => o).join(', ')}. actual: ` + process.version); } @@ -41,7 +41,7 @@ describe('Dir Operation Tests', function () { // which tests it('which() finds file name', function (done) { - this.timeout(1000); + this.timeout(3000); // create a executable file let testPath = path.join(testutil.getTestTemp(), 'which-finds-file-name'); @@ -1801,7 +1801,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('move to existing destination should fail unless forced', function (done) { + it('move to existing destination should fail if no-clobber is enabled', function (done) { this.timeout(1000); var sourceFile = 'sourceFile'; @@ -1826,7 +1826,7 @@ describe('Dir Operation Tests', function () { var worked: boolean = false; try { - tl.mv(sourceFile, destFile); + tl.mv(sourceFile, destFile, "-n"); worked = true; } catch (err) { diff --git a/node/test/disktests.ts b/node/test/disktests.ts index 209c6dda2..e55cf8cde 100644 --- a/node/test/disktests.ts +++ b/node/test/disktests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import path = require('path'); import fs = require('fs'); diff --git a/node/test/filtertests.ts b/node/test/filtertests.ts index 46beaec5f..04ea46474 100644 --- a/node/test/filtertests.ts +++ b/node/test/filtertests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import * as tl from '../_build/task'; import testutil = require('./testutil'); diff --git a/node/test/findmatchtests.ts b/node/test/findmatchtests.ts index 4a7c09ead..a0bcfc185 100644 --- a/node/test/findmatchtests.ts +++ b/node/test/findmatchtests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import * as tl from '../_build/task'; import * as im from '../_build/internal' diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index 73697fd90..656e636f3 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import path = require('path'); import os = require('os'); diff --git a/node/test/internalhelpertests.ts b/node/test/internalhelpertests.ts index 6d0d8397c..fbcdfc2c5 100644 --- a/node/test/internalhelpertests.ts +++ b/node/test/internalhelpertests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import * as assert from 'assert'; import * as fs from 'fs'; import * as path from 'path'; diff --git a/node/test/legacyfindfilestests.ts b/node/test/legacyfindfilestests.ts index 9c86ae56c..89c7fd05b 100644 --- a/node/test/legacyfindfilestests.ts +++ b/node/test/legacyfindfilestests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import * as assert from 'assert'; import * as fs from 'fs'; import * as path from 'path'; diff --git a/node/test/loctests.ts b/node/test/loctests.ts index ba564a7e7..432c94577 100644 --- a/node/test/loctests.ts +++ b/node/test/loctests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import path = require('path'); import fs = require('fs'); @@ -119,7 +116,7 @@ describe('Loc Tests', function () { done(); }) - + it('fallback to current string if culture resources.resjson not found', function (done) { this.timeout(1000); diff --git a/node/test/matchtests.ts b/node/test/matchtests.ts index 7a08cb945..3aff887c9 100644 --- a/node/test/matchtests.ts +++ b/node/test/matchtests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import * as tl from '../_build/task'; import testutil = require('./testutil'); diff --git a/node/test/mocktests.ts b/node/test/mocktests.ts index d4f3d28f9..70a9d7638 100644 --- a/node/test/mocktests.ts +++ b/node/test/mocktests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import * as mt from '../_build/mock-task'; import * as mtm from '../_build/mock-test'; @@ -302,7 +299,7 @@ describe('Mock Tests', function () { }) it('MockTest handles node 6 tasks correctly', function (done) { - this.timeout(10000); + this.timeout(15000); 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'); @@ -312,7 +309,7 @@ describe('Mock Tests', function () { }) it('MockTest handles node 10 tasks correctly', function (done) { - this.timeout(10000); + this.timeout(15000); 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'); @@ -322,7 +319,7 @@ describe('Mock Tests', function () { }) it('MockTest handles node 14 tasks correctly', function (done) { - this.timeout(10000); + this.timeout(15000); 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'); diff --git a/node/test/resulttests.ts b/node/test/resulttests.ts index c48dfa4cf..ea2bd0238 100644 --- a/node/test/resulttests.ts +++ b/node/test/resulttests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import * as tl from '../_build/task'; import testutil = require('./testutil'); diff --git a/node/test/testindex.d.ts b/node/test/testindex.d.ts deleted file mode 100644 index 9a175de33..000000000 --- a/node/test/testindex.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -// this is copied to build output so tests compiling use the just built task lib .d.ts - -/// -/// \ No newline at end of file diff --git a/node/test/testutil.ts b/node/test/testutil.ts index 8ca4beb59..e55dbcd5c 100644 --- a/node/test/testutil.ts +++ b/node/test/testutil.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + import path = require('path'); import util = require('util'); import stream = require('stream'); @@ -35,7 +38,7 @@ var NullStream = function () { util.inherits(NullStream, stream.Writable); export function getNullStream() { - return new NullStream(); + return new NullStream(); } export class StringStream extends stream.Writable { @@ -57,7 +60,7 @@ export class StringStream extends stream.Writable { } export function createStringStream() { - return new StringStream(); + return new StringStream(); } export function buildOutput(lines: string[]): string { diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 39daa1e92..ac4aff1be 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import child_process = require('child_process'); import fs = require('fs'); @@ -463,7 +460,7 @@ describe('Toolrunner Tests', function () { toolRunnerDebug.push(data); }); - process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = 500; // 0.5 seconds + process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = "500"; // 0.5 seconds let options = { cwd: __dirname, @@ -518,7 +515,7 @@ describe('Toolrunner Tests', function () { toolRunnerDebug.push(data); }); - process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = 500; // 0.5 seconds + process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = "500"; // 0.5 seconds let options = { cwd: __dirname, @@ -578,7 +575,7 @@ describe('Toolrunner Tests', function () { toolRunnerDebug.push(data); }); - process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = 500; // 0.5 seconds + process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = "500"; // 0.5 seconds let options = { cwd: __dirname, @@ -1001,7 +998,7 @@ describe('Toolrunner Tests', function () { assert(err && err.message && err.message.indexOf('/bin/ps') >= 0, 'error from ps is not reported'); assert(fs.existsSync(testFile), 'Log of first tool output is created when first tool fails'); const fileContents = fs.readFileSync(testFile); - assert(fileContents.indexOf('illegal option') >= 0 || fileContents.indexOf('unsupported option') >= 0, + assert(fileContents.indexOf('illegal option') >= 0 || fileContents.indexOf('unsupported option') >= 0, 'error from first tool should be written to log file: ' + fileContents); done(); } @@ -1182,7 +1179,7 @@ describe('Toolrunner Tests', function () { assert.equal((node as any).args.length, 2, 'should have 2 args'); assert.equal((node as any).args.toString(), 'one,\\\\two\\arg', 'should be one,\\\\two\\arg'); done(); - }) + }) it('handles equals and switches', function (done) { this.timeout(10000); @@ -2063,14 +2060,14 @@ describe('Toolrunner Tests', function () { } describe('Executing inside shell', function () { - + let tempPath: string = testutil.getTestTemp(); let _testExecOptions: trm.IExecOptions; before (function () { _testExecOptions = { cwd: __dirname, - env: { + env: { WIN_TEST: 'test value', TESTPATH: tempPath, TEST_NODE: 'node', @@ -2083,12 +2080,12 @@ describe('Toolrunner Tests', function () { outStream: testutil.getNullStream(), errStream: testutil.getNullStream() }; - + }) it('Exec sync inside shell', function (done) { this.timeout(10000); - + if (os.platform() === 'win32') { let exePath = compileArgsExe('print args with spaces.exe'); let exeRunner = tl.tool(exePath); @@ -2102,13 +2099,13 @@ describe('Toolrunner Tests', function () { assert.equal(ret.code, 0, 'return code of stat should be 0'); assert(ret.stdout.includes(tempPath), `Result should include \'${tempPath}\'`); } - + assert(ret.stdout && ret.stdout.length > 0, 'should have emitted stdout'); done(); }); it('Exec inside shell', function (done) { this.timeout(10000); - + let output: string = ''; if (os.platform() === 'win32') { let exePath = compileArgsExe('print args with spaces.exe'); @@ -2155,12 +2152,12 @@ describe('Toolrunner Tests', function () { .arg('"%WIN_TEST%"') .arg('line 3'); outputExe.pipeExecOutputToTool(matchExe); - + let output = ''; outputExe.on('stdout', (data) => { output += data.toString(); }); - + outputExe.exec(_testExecOptions) .then(function (code) { assert.equal(code, 0, 'return code of exec should be 0'); @@ -2174,16 +2171,16 @@ describe('Toolrunner Tests', function () { else { const grep = tl.tool(tl.which('grep', true)); grep.arg('$TEST_NODE'); - + const ps = tl.tool(tl.which('ps', true)); ps.arg('ax'); ps.pipeExecOutputToTool(grep); - + let output = ''; ps.on('stdout', (data) => { output += data.toString(); }); - + ps.exec(_testExecOptions) .then(function (code) { assert.equal(code, 0, 'return code of exec should be 0'); @@ -2197,7 +2194,7 @@ describe('Toolrunner Tests', function () { }); it('Should handle arguments with quotes properly', function (done) { this.timeout(10000); - + let output: string = ''; if (os.platform() === 'win32') { let exePath = compileArgsExe('print args with spaces.exe'); diff --git a/node/test/tsconfig.json b/node/test/tsconfig.json index 5bbd878fc..27dcbee19 100644 --- a/node/test/tsconfig.json +++ b/node/test/tsconfig.json @@ -4,7 +4,7 @@ "module": "commonjs", "declaration": false, "outDir": "../_test", - "moduleResolution": "node" + "moduleResolution": "node" }, "files": [ "dirtests.ts", @@ -16,11 +16,11 @@ "legacyfindfilestests.ts", "vaulttests.ts", "toolrunnertests.ts", - "cctests", - "loctests", - "matchtests", - "filtertests", - "findmatchtests", - "mocktests" + "cctests.ts", + "loctests.ts", + "matchtests.ts", + "filtertests.ts", + "findmatchtests.ts", + "mocktests.ts" ] } \ No newline at end of file diff --git a/node/test/vaulttests.ts b/node/test/vaulttests.ts index 8991f54c8..4d7dcc2d6 100644 --- a/node/test/vaulttests.ts +++ b/node/test/vaulttests.ts @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -/// -/// - import assert = require('assert'); import * as vm from '../_build/vault'; import * as trm from '../_build/toolrunner'; diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 9e516def9..8f2e569ad 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -671,18 +671,18 @@ export class ToolRunner extends events.EventEmitter { } //pipe stdout of first tool to stdin of second tool - cpFirst.stdout.on('data', (data: Buffer) => { + cpFirst.stdout?.on('data', (data: Buffer) => { try { if (fileStream) { fileStream.write(data); } - cp.stdin.write(data); + cp.stdin?.write(data); } catch (err) { this._debug('Failed to pipe output of ' + toolPathFirst + ' to ' + toolPath); this._debug(toolPath + ' might have exited due to errors prematurely. Verify the arguments passed are valid.'); } }); - cpFirst.stderr.on('data', (data: Buffer) => { + cpFirst.stderr?.on('data', (data: Buffer) => { if (fileStream) { fileStream.write(data); } @@ -697,7 +697,7 @@ export class ToolRunner extends events.EventEmitter { if (fileStream) { fileStream.end(); } - cp.stdin.end(); + cp.stdin?.end(); error = new Error(toolPathFirst + ' failed. ' + err.message); if(waitingEvents == 0) { defer.reject(error); @@ -714,7 +714,7 @@ export class ToolRunner extends events.EventEmitter { if (fileStream) { fileStream.end(); } - cp.stdin.end(); + cp.stdin?.end(); if(waitingEvents == 0) { if (error) { defer.reject(error); @@ -725,7 +725,7 @@ export class ToolRunner extends events.EventEmitter { }); var stdbuffer: string = ''; - cp.stdout.on('data', (data: Buffer) => { + cp.stdout?.on('data', (data: Buffer) => { this.emit('stdout', data); if (!optionsNonNull.silent) { @@ -738,7 +738,7 @@ export class ToolRunner extends events.EventEmitter { }); var errbuffer: string = ''; - cp.stderr.on('data', (data: Buffer) => { + cp.stderr?.on('data', (data: Buffer) => { this.emit('stderr', data); success = !optionsNonNull.failOnStdErr; @@ -905,14 +905,14 @@ export class ToolRunner extends events.EventEmitter { // it is possible for the child process to end its last line without a new line. // because stdout is buffered, this causes the last line to not get sent to the parent // stream. Adding this event forces a flush before the child streams are closed. - cp.stdout.on('finish', () => { + cp.stdout?.on('finish', () => { if (!optionsNonNull.silent) { optionsNonNull.outStream!.write(os.EOL); } }); var stdbuffer: string = ''; - cp.stdout.on('data', (data: Buffer) => { + cp.stdout?.on('data', (data: Buffer) => { this.emit('stdout', data); if (!optionsNonNull.silent) { @@ -926,7 +926,7 @@ export class ToolRunner extends events.EventEmitter { var errbuffer: string = ''; - cp.stderr.on('data', (data: Buffer) => { + cp.stderr?.on('data', (data: Buffer) => { state.processStderr = true; this.emit('stderr', data); diff --git a/node/typings.json b/node/typings.json deleted file mode 100644 index 2d16d4634..000000000 --- a/node/typings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "azure-pipelines-task-lib", - "version": false, - "dependencies": {}, - "globalDependencies": { - "glob": "registry:dt/glob#5.0.10+20160317120654", - "minimatch": "registry:dt/minimatch#2.0.8+20160317120654", - "mocha": "registry:dt/mocha#2.2.5+20160619032855", - "mockery": "registry:dt/mockery#1.4.0+20160316155526", - "node": "registry:dt/node#6.0.0+20160709114037", - "q": "registry:dt/q#0.0.0+20160613154756", - "semver": "registry:dt/semver#4.3.4+20160608054219", - "shelljs": "registry:dt/shelljs#0.0.0+20160526131156" - } -} diff --git a/node/typings/globals/glob/index.d.ts b/node/typings/globals/glob/index.d.ts deleted file mode 100644 index 4eab63107..000000000 --- a/node/typings/globals/glob/index.d.ts +++ /dev/null @@ -1,106 +0,0 @@ -// Generated by typings -// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/glob/glob.d.ts -declare module "glob" { - - import events = require("events"); - import fs = require('fs'); - import minimatch = require("minimatch"); - - function G(pattern: string, cb: (err: Error, matches: string[]) => void): void; - function G(pattern: string, options: G.IOptions, cb: (err: Error, matches: string[]) => void): void; - - namespace G { - function sync(pattern: string, options?: IOptions): string[]; - - function hasMagic(pattern: string, options?: IOptions): boolean; - - var Glob: IGlobStatic; - var GlobSync: IGlobSyncStatic; - - interface IOptions extends minimatch.IOptions { - cwd?: string; - root?: string; - dot?: boolean; - nomount?: boolean; - mark?: boolean; - nosort?: boolean; - stat?: boolean; - silent?: boolean; - strict?: boolean; - cache?: { [path: string]: any /* boolean | string | string[] */ }; - statCache?: { [path: string]: fs.Stats }; - symlinks?: any; - sync?: boolean; - nounique?: boolean; - nonull?: boolean; - debug?: boolean; - nobrace?: boolean; - noglobstar?: boolean; - noext?: boolean; - nocase?: boolean; - matchBase?: any; - nodir?: boolean; - ignore?: any; /* string | string[] */ - follow?: boolean; - realpath?: boolean; - nonegate?: boolean; - nocomment?: boolean; - - /** Deprecated. */ - globDebug?: boolean; - } - - interface IGlobStatic extends events.EventEmitter { - new (pattern: string, cb?: (err: Error, matches: string[]) => void): IGlob; - new (pattern: string, options: IOptions, cb?: (err: Error, matches: string[]) => void): IGlob; - prototype: IGlob; - } - - interface IGlobSyncStatic { - new (pattern: string, options?: IOptions): IGlobBase - prototype: IGlobBase; - } - - interface IGlobBase { - minimatch: minimatch.IMinimatch; - options: IOptions; - aborted: boolean; - cache: { [path: string]: any /* boolean | string | string[] */ }; - statCache: { [path: string]: fs.Stats }; - symlinks: { [path: string]: boolean }; - realpathCache: { [path: string]: string }; - found: string[]; - } - - interface IGlob extends IGlobBase, events.EventEmitter { - pause(): void; - resume(): void; - abort(): void; - - /** Deprecated. */ - EOF: any; - /** Deprecated. */ - paused: boolean; - /** Deprecated. */ - maxDepth: number; - /** Deprecated. */ - maxLength: number; - /** Deprecated. */ - changedCwd: boolean; - /** Deprecated. */ - cwd: string; - /** Deprecated. */ - root: string; - /** Deprecated. */ - error: any; - /** Deprecated. */ - matches: string[]; - /** Deprecated. */ - log(...args: any[]): void; - /** Deprecated. */ - emitMatch(m: any): void; - } - } - - export = G; -} \ No newline at end of file diff --git a/node/typings/globals/glob/typings.json b/node/typings/globals/glob/typings.json deleted file mode 100644 index 505e919af..000000000 --- a/node/typings/globals/glob/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resolution": "main", - "tree": { - "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/glob/glob.d.ts", - "raw": "registry:dt/glob#5.0.10+20160317120654", - "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/glob/glob.d.ts" - } -} diff --git a/node/typings/globals/minimatch/index.d.ts b/node/typings/globals/minimatch/index.d.ts deleted file mode 100644 index 03e423179..000000000 --- a/node/typings/globals/minimatch/index.d.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Generated by typings -// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/minimatch/minimatch.d.ts -declare module "minimatch" { - - function M(target: string, pattern: string, options?: M.IOptions): boolean; - - namespace M { - function match(list: string[], pattern: string, options?: IOptions): string[]; - function filter(pattern: string, options?: IOptions): (element: string, indexed: number, array: string[]) => boolean; - function makeRe(pattern: string, options?: IOptions): RegExp; - - var Minimatch: IMinimatchStatic; - - interface IOptions { - debug?: boolean; - nobrace?: boolean; - noglobstar?: boolean; - dot?: boolean; - noext?: boolean; - nocase?: boolean; - nonull?: boolean; - matchBase?: boolean; - nocomment?: boolean; - nonegate?: boolean; - flipNegate?: boolean; - } - - interface IMinimatchStatic { - new (pattern: string, options?: IOptions): IMinimatch; - prototype: IMinimatch; - } - - interface IMinimatch { - pattern: string; - options: IOptions; - /** 2-dimensional array of regexp or string expressions. */ - set: any[][]; // (RegExp | string)[][] - regexp: RegExp; - negate: boolean; - comment: boolean; - empty: boolean; - - makeRe(): RegExp; // regexp or boolean - match(fname: string): boolean; - matchOne(files: string[], pattern: string[], partial: boolean): boolean; - - /** Deprecated. For internal use. */ - debug(): void; - /** Deprecated. For internal use. */ - make(): void; - /** Deprecated. For internal use. */ - parseNegate(): void; - /** Deprecated. For internal use. */ - braceExpand(pattern: string, options: IOptions): void; - /** Deprecated. For internal use. */ - parse(pattern: string, isSub?: boolean): void; - } - } - - export = M; -} \ No newline at end of file diff --git a/node/typings/globals/minimatch/typings.json b/node/typings/globals/minimatch/typings.json deleted file mode 100644 index b06a75ad7..000000000 --- a/node/typings/globals/minimatch/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resolution": "main", - "tree": { - "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/minimatch/minimatch.d.ts", - "raw": "registry:dt/minimatch#2.0.8+20160317120654", - "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/minimatch/minimatch.d.ts" - } -} diff --git a/node/typings/globals/mocha/index.d.ts b/node/typings/globals/mocha/index.d.ts deleted file mode 100644 index b284ea8b7..000000000 --- a/node/typings/globals/mocha/index.d.ts +++ /dev/null @@ -1,234 +0,0 @@ -// Generated by typings -// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/b1daff0be8fa53f645365303d8e0145d055370e9/mocha/mocha.d.ts -interface MochaSetupOptions { - //milliseconds to wait before considering a test slow - slow?: number; - - // timeout in milliseconds - timeout?: number; - - // ui name "bdd", "tdd", "exports" etc - ui?: string; - - //array of accepted globals - globals?: any[]; - - // reporter instance (function or string), defaults to `mocha.reporters.Spec` - reporter?: any; - - // bail on the first test failure - bail?: boolean; - - // ignore global leaks - ignoreLeaks?: boolean; - - // grep string or regexp to filter tests with - grep?: any; -} - -interface MochaDone { - (error?: Error): void; -} - -declare var mocha: Mocha; -declare var describe: Mocha.IContextDefinition; -declare var xdescribe: Mocha.IContextDefinition; -// alias for `describe` -declare var context: Mocha.IContextDefinition; -// alias for `describe` -declare var suite: Mocha.IContextDefinition; -declare var it: Mocha.ITestDefinition; -declare var xit: Mocha.ITestDefinition; -// alias for `it` -declare var test: Mocha.ITestDefinition; -declare var specify: Mocha.ITestDefinition; - -declare function before(action: () => void): void; - -declare function before(action: (done: MochaDone) => void): void; - -declare function before(description: string, action: () => void): void; - -declare function before(description: string, action: (done: MochaDone) => void): void; - -declare function setup(action: () => void): void; - -declare function setup(action: (done: MochaDone) => void): void; - -declare function after(action: () => void): void; - -declare function after(action: (done: MochaDone) => void): void; - -declare function after(description: string, action: () => void): void; - -declare function after(description: string, action: (done: MochaDone) => void): void; - -declare function teardown(action: () => void): void; - -declare function teardown(action: (done: MochaDone) => void): void; - -declare function beforeEach(action: () => void): void; - -declare function beforeEach(action: (done: MochaDone) => void): void; - -declare function beforeEach(description: string, action: () => void): void; - -declare function beforeEach(description: string, action: (done: MochaDone) => void): void; - -declare function suiteSetup(action: () => void): void; - -declare function suiteSetup(action: (done: MochaDone) => void): void; - -declare function afterEach(action: () => void): void; - -declare function afterEach(action: (done: MochaDone) => void): void; - -declare function afterEach(description: string, action: () => void): void; - -declare function afterEach(description: string, action: (done: MochaDone) => void): void; - -declare function suiteTeardown(action: () => void): void; - -declare function suiteTeardown(action: (done: MochaDone) => void): void; - -declare class Mocha { - constructor(options?: { - grep?: RegExp; - ui?: string; - reporter?: string; - timeout?: number; - bail?: boolean; - }); - - /** Setup mocha with the given options. */ - setup(options: MochaSetupOptions): Mocha; - bail(value?: boolean): Mocha; - addFile(file: string): Mocha; - /** Sets reporter by name, defaults to "spec". */ - reporter(name: string): Mocha; - /** Sets reporter constructor, defaults to mocha.reporters.Spec. */ - reporter(reporter: (runner: Mocha.IRunner, options: any) => any): Mocha; - ui(value: string): Mocha; - grep(value: string): Mocha; - grep(value: RegExp): Mocha; - invert(): Mocha; - ignoreLeaks(value: boolean): Mocha; - checkLeaks(): Mocha; - /** - * Function to allow assertion libraries to throw errors directly into mocha. - * This is useful when running tests in a browser because window.onerror will - * only receive the 'message' attribute of the Error. - */ - throwError(error: Error): void; - /** Enables growl support. */ - growl(): Mocha; - globals(value: string): Mocha; - globals(values: string[]): Mocha; - useColors(value: boolean): Mocha; - useInlineDiffs(value: boolean): Mocha; - timeout(value: number): Mocha; - slow(value: number): Mocha; - enableTimeouts(value: boolean): Mocha; - asyncOnly(value: boolean): Mocha; - noHighlighting(value: boolean): Mocha; - /** Runs tests and invokes `onComplete()` when finished. */ - run(onComplete?: (failures: number) => void): Mocha.IRunner; -} - -// merge the Mocha class declaration with a module -declare namespace Mocha { - /** Partial interface for Mocha's `Runnable` class. */ - interface IRunnable { - title: string; - fn: Function; - async: boolean; - sync: boolean; - timedOut: boolean; - } - - /** Partial interface for Mocha's `Suite` class. */ - interface ISuite { - parent: ISuite; - title: string; - - fullTitle(): string; - } - - /** Partial interface for Mocha's `Test` class. */ - interface ITest extends IRunnable { - parent: ISuite; - pending: boolean; - - fullTitle(): string; - } - - /** Partial interface for Mocha's `Runner` class. */ - interface IRunner {} - - interface IContextDefinition { - (description: string, spec: () => void): ISuite; - only(description: string, spec: () => void): ISuite; - skip(description: string, spec: () => void): void; - timeout(ms: number): void; - } - - interface ITestDefinition { - (expectation: string, assertion?: () => void): ITest; - (expectation: string, assertion?: (done: MochaDone) => void): ITest; - only(expectation: string, assertion?: () => void): ITest; - only(expectation: string, assertion?: (done: MochaDone) => void): ITest; - skip(expectation: string, assertion?: () => void): void; - skip(expectation: string, assertion?: (done: MochaDone) => void): void; - timeout(ms: number): void; - } - - export module reporters { - export class Base { - stats: { - suites: number; - tests: number; - passes: number; - pending: number; - failures: number; - }; - - constructor(runner: IRunner); - } - - export class Doc extends Base {} - export class Dot extends Base {} - export class HTML extends Base {} - export class HTMLCov extends Base {} - export class JSON extends Base {} - export class JSONCov extends Base {} - export class JSONStream extends Base {} - export class Landing extends Base {} - export class List extends Base {} - export class Markdown extends Base {} - export class Min extends Base {} - export class Nyan extends Base {} - export class Progress extends Base { - /** - * @param options.open String used to indicate the start of the progress bar. - * @param options.complete String used to indicate a complete test on the progress bar. - * @param options.incomplete String used to indicate an incomplete test on the progress bar. - * @param options.close String used to indicate the end of the progress bar. - */ - constructor(runner: IRunner, options?: { - open?: string; - complete?: string; - incomplete?: string; - close?: string; - }); - } - export class Spec extends Base {} - export class TAP extends Base {} - export class XUnit extends Base { - constructor(runner: IRunner, options?: any); - } - } -} - -declare module "mocha" { - export = Mocha; -} \ No newline at end of file diff --git a/node/typings/globals/mocha/typings.json b/node/typings/globals/mocha/typings.json deleted file mode 100644 index 57933df04..000000000 --- a/node/typings/globals/mocha/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resolution": "main", - "tree": { - "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/b1daff0be8fa53f645365303d8e0145d055370e9/mocha/mocha.d.ts", - "raw": "registry:dt/mocha#2.2.5+20160619032855", - "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/b1daff0be8fa53f645365303d8e0145d055370e9/mocha/mocha.d.ts" - } -} diff --git a/node/typings/globals/mockery/index.d.ts b/node/typings/globals/mockery/index.d.ts deleted file mode 100644 index 70c4ba798..000000000 --- a/node/typings/globals/mockery/index.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Generated by typings -// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/56295f5058cac7ae458540423c50ac2dcf9fc711/mockery/mockery.d.ts -declare module "mockery" { - - interface MockeryEnableArgs { - useCleanCache?: boolean; - warnOnReplace?: boolean; - warnOnUnregistered?: boolean; - } - - export function enable(args?: MockeryEnableArgs): void; - export function disable(): void; - - export function registerMock(name: string, mock: any): void; - export function deregisterMock(name: string): void; - - export function registerSubstitute(name: string, substitute: string): void; - export function deregisterSubstitute(name: string): void; - - export function registerAllowable(name: string, unhook?: boolean): void; - export function deregisterAllowable(name: string): void; - - export function registerAllowables(names: string[]): void; - export function deregisterAllowables(names: string[]): void; - - export function deregisterAll(): void; - export function resetCache(): void; - export function warnOnUnregistered(value: boolean): void; - export function warnOnReplace(value: boolean): void; -} \ No newline at end of file diff --git a/node/typings/globals/mockery/typings.json b/node/typings/globals/mockery/typings.json deleted file mode 100644 index 334d96df4..000000000 --- a/node/typings/globals/mockery/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resolution": "main", - "tree": { - "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/56295f5058cac7ae458540423c50ac2dcf9fc711/mockery/mockery.d.ts", - "raw": "registry:dt/mockery#1.4.0+20160316155526", - "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/56295f5058cac7ae458540423c50ac2dcf9fc711/mockery/mockery.d.ts" - } -} diff --git a/node/typings/globals/node/index.d.ts b/node/typings/globals/node/index.d.ts deleted file mode 100644 index bba54ec45..000000000 --- a/node/typings/globals/node/index.d.ts +++ /dev/null @@ -1,2578 +0,0 @@ -// Generated by typings -// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/77b1b1709315b03b9b1b67c589d599bebeeef2ee/node/node.d.ts -interface Error { - stack?: string; -} - -interface ErrorConstructor { - captureStackTrace(targetObject: Object, constructorOpt?: Function): void; - stackTraceLimit: number; -} - -// compat for TypeScript 1.8 -// if you use with --target es3 or --target es5 and use below definitions, -// use the lib.es6.d.ts that is bundled with TypeScript 1.8. -interface MapConstructor {} -interface WeakMapConstructor {} -interface SetConstructor {} -interface WeakSetConstructor {} - -/************************************************ -* * -* GLOBAL * -* * -************************************************/ -declare var process: NodeJS.Process; -declare var global: NodeJS.Global; - -declare var __filename: string; -declare var __dirname: string; - -declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; -declare function clearTimeout(timeoutId: NodeJS.Timer): void; -declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; -declare function clearInterval(intervalId: NodeJS.Timer): void; -declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any; -declare function clearImmediate(immediateId: any): void; - -interface NodeRequireFunction { - (id: string): any; -} - -interface NodeRequire extends NodeRequireFunction { - resolve(id:string): string; - cache: any; - extensions: any; - main: any; -} - -declare var require: NodeRequire; - -interface NodeModule { - exports: any; - require: NodeRequireFunction; - id: string; - filename: string; - loaded: boolean; - parent: any; - children: any[]; -} - -declare var module: NodeModule; - -// Same as module.exports -declare var exports: any; -declare var SlowBuffer: { - new (str: string, encoding?: string): Buffer; - new (size: number): Buffer; - new (size: Uint8Array): Buffer; - new (array: any[]): Buffer; - prototype: Buffer; - isBuffer(obj: any): boolean; - byteLength(string: string, encoding?: string): number; - concat(list: Buffer[], totalLength?: number): Buffer; -}; - - -// Buffer class -type BufferEncoding = "ascii" | "utf8" | "utf16le" | "ucs2" | "binary" | "hex"; -interface Buffer extends NodeBuffer {} - -/** - * Raw data is stored in instances of the Buffer class. - * A Buffer is similar to an array of integers but corresponds to a raw memory allocation outside the V8 heap. A Buffer cannot be resized. - * Valid string encodings: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' - */ -declare var Buffer: { - /** - * Allocates a new buffer containing the given {str}. - * - * @param str String to store in buffer. - * @param encoding encoding to use, optional. Default is 'utf8' - */ - new (str: string, encoding?: string): Buffer; - /** - * Allocates a new buffer of {size} octets. - * - * @param size count of octets to allocate. - */ - new (size: number): Buffer; - /** - * Allocates a new buffer containing the given {array} of octets. - * - * @param array The octets to store. - */ - new (array: Uint8Array): Buffer; - /** - * Produces a Buffer backed by the same allocated memory as - * the given {ArrayBuffer}. - * - * - * @param arrayBuffer The ArrayBuffer with which to share memory. - */ - new (arrayBuffer: ArrayBuffer): Buffer; - /** - * Allocates a new buffer containing the given {array} of octets. - * - * @param array The octets to store. - */ - new (array: any[]): Buffer; - /** - * Copies the passed {buffer} data onto a new {Buffer} instance. - * - * @param buffer The buffer to copy. - */ - new (buffer: Buffer): Buffer; - prototype: Buffer; - /** - * Allocates a new Buffer using an {array} of octets. - * - * @param array - */ - from(array: any[]): Buffer; - /** - * When passed a reference to the .buffer property of a TypedArray instance, - * the newly created Buffer will share the same allocated memory as the TypedArray. - * The optional {byteOffset} and {length} arguments specify a memory range - * within the {arrayBuffer} that will be shared by the Buffer. - * - * @param arrayBuffer The .buffer property of a TypedArray or a new ArrayBuffer() - * @param byteOffset - * @param length - */ - from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?:number): Buffer; - /** - * Copies the passed {buffer} data onto a new Buffer instance. - * - * @param buffer - */ - from(buffer: Buffer): Buffer; - /** - * Creates a new Buffer containing the given JavaScript string {str}. - * If provided, the {encoding} parameter identifies the character encoding. - * If not provided, {encoding} defaults to 'utf8'. - * - * @param str - */ - from(str: string, encoding?: string): Buffer; - /** - * Returns true if {obj} is a Buffer - * - * @param obj object to test. - */ - isBuffer(obj: any): obj is Buffer; - /** - * Returns true if {encoding} is a valid encoding argument. - * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' - * - * @param encoding string to test. - */ - isEncoding(encoding: string): boolean; - /** - * Gives the actual byte length of a string. encoding defaults to 'utf8'. - * This is not the same as String.prototype.length since that returns the number of characters in a string. - * - * @param string string to test. - * @param encoding encoding used to evaluate (defaults to 'utf8') - */ - byteLength(string: string, encoding?: string): number; - /** - * Returns a buffer which is the result of concatenating all the buffers in the list together. - * - * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer. - * If the list has exactly one item, then the first item of the list is returned. - * If the list has more than one item, then a new Buffer is created. - * - * @param list An array of Buffer objects to concatenate - * @param totalLength Total length of the buffers when concatenated. - * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly. - */ - concat(list: Buffer[], totalLength?: number): Buffer; - /** - * The same as buf1.compare(buf2). - */ - compare(buf1: Buffer, buf2: Buffer): number; - /** - * Allocates a new buffer of {size} octets. - * - * @param size count of octets to allocate. - * @param fill if specified, buffer will be initialized by calling buf.fill(fill). - * If parameter is omitted, buffer will be filled with zeros. - * @param encoding encoding used for call to buf.fill while initalizing - */ - alloc(size: number, fill?: string|Buffer|number, encoding?: string): Buffer; - /** - * Allocates a new buffer of {size} octets, leaving memory not initialized, so the contents - * of the newly created Buffer are unknown and may contain sensitive data. - * - * @param size count of octets to allocate - */ - allocUnsafe(size: number): Buffer; - /** - * Allocates a new non-pooled buffer of {size} octets, leaving memory not initialized, so the contents - * of the newly created Buffer are unknown and may contain sensitive data. - * - * @param size count of octets to allocate - */ - allocUnsafeSlow(size: number): Buffer; -}; - -/************************************************ -* * -* GLOBAL INTERFACES * -* * -************************************************/ -declare namespace NodeJS { - export interface ErrnoException extends Error { - errno?: number; - code?: string; - path?: string; - syscall?: string; - stack?: string; - } - - export interface EventEmitter { - addListener(event: string, listener: Function): this; - on(event: string, listener: Function): this; - once(event: string, listener: Function): this; - removeListener(event: string, listener: Function): this; - removeAllListeners(event?: string): this; - setMaxListeners(n: number): this; - getMaxListeners(): number; - listeners(event: string): Function[]; - emit(event: string, ...args: any[]): boolean; - listenerCount(type: string): number; - } - - export interface ReadableStream extends EventEmitter { - readable: boolean; - read(size?: number): string|Buffer; - setEncoding(encoding: string): void; - pause(): void; - resume(): void; - pipe(destination: T, options?: { end?: boolean; }): T; - unpipe(destination?: T): void; - unshift(chunk: string): void; - unshift(chunk: Buffer): void; - wrap(oldStream: ReadableStream): ReadableStream; - } - - export interface WritableStream extends EventEmitter { - writable: boolean; - write(buffer: Buffer|string, cb?: Function): boolean; - write(str: string, encoding?: string, cb?: Function): boolean; - end(): void; - end(buffer: Buffer, cb?: Function): void; - end(str: string, cb?: Function): void; - end(str: string, encoding?: string, cb?: Function): void; - } - - export interface ReadWriteStream extends ReadableStream, WritableStream {} - - export interface Events extends EventEmitter { } - - export interface Domain extends Events { - run(fn: Function): void; - add(emitter: Events): void; - remove(emitter: Events): void; - bind(cb: (err: Error, data: any) => any): any; - intercept(cb: (data: any) => any): any; - dispose(): void; - - addListener(event: string, listener: Function): this; - on(event: string, listener: Function): this; - once(event: string, listener: Function): this; - removeListener(event: string, listener: Function): this; - removeAllListeners(event?: string): this; - } - - export interface MemoryUsage { - rss: number; - heapTotal: number; - heapUsed: number; - } - - export interface Process extends EventEmitter { - stdout: WritableStream; - stderr: WritableStream; - stdin: ReadableStream; - argv: string[]; - execArgv: string[]; - execPath: string; - abort(): void; - chdir(directory: string): void; - cwd(): string; - env: any; - exit(code?: number): void; - getgid(): number; - setgid(id: number): void; - setgid(id: string): void; - getuid(): number; - setuid(id: number): void; - setuid(id: string): void; - version: string; - versions: { - http_parser: string; - node: string; - v8: string; - ares: string; - uv: string; - zlib: string; - modules: string; - openssl: string; - }; - config: { - target_defaults: { - cflags: any[]; - default_configuration: string; - defines: string[]; - include_dirs: string[]; - libraries: string[]; - }; - variables: { - clang: number; - host_arch: string; - node_install_npm: boolean; - node_install_waf: boolean; - node_prefix: string; - node_shared_openssl: boolean; - node_shared_v8: boolean; - node_shared_zlib: boolean; - node_use_dtrace: boolean; - node_use_etw: boolean; - node_use_openssl: boolean; - target_arch: string; - v8_no_strict_aliasing: number; - v8_use_snapshot: boolean; - visibility: string; - }; - }; - kill(pid:number, signal?: string|number): void; - pid: number; - title: string; - arch: string; - platform: string; - memoryUsage(): MemoryUsage; - nextTick(callback: Function): void; - umask(mask?: number): number; - uptime(): number; - hrtime(time?:number[]): number[]; - domain: Domain; - - // Worker - send?(message: any, sendHandle?: any): void; - disconnect(): void; - connected: boolean; - } - - export interface Global { - Array: typeof Array; - ArrayBuffer: typeof ArrayBuffer; - Boolean: typeof Boolean; - Buffer: typeof Buffer; - DataView: typeof DataView; - Date: typeof Date; - Error: typeof Error; - EvalError: typeof EvalError; - Float32Array: typeof Float32Array; - Float64Array: typeof Float64Array; - Function: typeof Function; - GLOBAL: Global; - Infinity: typeof Infinity; - Int16Array: typeof Int16Array; - Int32Array: typeof Int32Array; - Int8Array: typeof Int8Array; - Intl: typeof Intl; - JSON: typeof JSON; - Map: MapConstructor; - Math: typeof Math; - NaN: typeof NaN; - Number: typeof Number; - Object: typeof Object; - Promise: Function; - RangeError: typeof RangeError; - ReferenceError: typeof ReferenceError; - RegExp: typeof RegExp; - Set: SetConstructor; - String: typeof String; - Symbol: Function; - SyntaxError: typeof SyntaxError; - TypeError: typeof TypeError; - URIError: typeof URIError; - Uint16Array: typeof Uint16Array; - Uint32Array: typeof Uint32Array; - Uint8Array: typeof Uint8Array; - Uint8ClampedArray: Function; - WeakMap: WeakMapConstructor; - WeakSet: WeakSetConstructor; - clearImmediate: (immediateId: any) => void; - clearInterval: (intervalId: NodeJS.Timer) => void; - clearTimeout: (timeoutId: NodeJS.Timer) => void; - console: typeof console; - decodeURI: typeof decodeURI; - decodeURIComponent: typeof decodeURIComponent; - encodeURI: typeof encodeURI; - encodeURIComponent: typeof encodeURIComponent; - escape: (str: string) => string; - eval: typeof eval; - global: Global; - isFinite: typeof isFinite; - isNaN: typeof isNaN; - parseFloat: typeof parseFloat; - parseInt: typeof parseInt; - process: Process; - root: Global; - setImmediate: (callback: (...args: any[]) => void, ...args: any[]) => any; - setInterval: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; - setTimeout: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; - undefined: typeof undefined; - unescape: (str: string) => string; - gc: () => void; - v8debug?: any; - } - - export interface Timer { - ref() : void; - unref() : void; - } -} - -/** - * @deprecated - */ -interface NodeBuffer extends Uint8Array { - write(string: string, offset?: number, length?: number, encoding?: string): number; - toString(encoding?: string, start?: number, end?: number): string; - toJSON(): any; - equals(otherBuffer: Buffer): boolean; - compare(otherBuffer: Buffer): number; - copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; - slice(start?: number, end?: number): Buffer; - writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; - writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; - writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; - writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; - readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number; - readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number; - readIntLE(offset: number, byteLength: number, noAssert?: boolean): number; - readIntBE(offset: number, byteLength: number, noAssert?: boolean): number; - readUInt8(offset: number, noAssert?: boolean): number; - readUInt16LE(offset: number, noAssert?: boolean): number; - readUInt16BE(offset: number, noAssert?: boolean): number; - readUInt32LE(offset: number, noAssert?: boolean): number; - readUInt32BE(offset: number, noAssert?: boolean): number; - readInt8(offset: number, noAssert?: boolean): number; - readInt16LE(offset: number, noAssert?: boolean): number; - readInt16BE(offset: number, noAssert?: boolean): number; - readInt32LE(offset: number, noAssert?: boolean): number; - readInt32BE(offset: number, noAssert?: boolean): number; - readFloatLE(offset: number, noAssert?: boolean): number; - readFloatBE(offset: number, noAssert?: boolean): number; - readDoubleLE(offset: number, noAssert?: boolean): number; - readDoubleBE(offset: number, noAssert?: boolean): number; - writeUInt8(value: number, offset: number, noAssert?: boolean): number; - writeUInt16LE(value: number, offset: number, noAssert?: boolean): number; - writeUInt16BE(value: number, offset: number, noAssert?: boolean): number; - writeUInt32LE(value: number, offset: number, noAssert?: boolean): number; - writeUInt32BE(value: number, offset: number, noAssert?: boolean): number; - writeInt8(value: number, offset: number, noAssert?: boolean): number; - writeInt16LE(value: number, offset: number, noAssert?: boolean): number; - writeInt16BE(value: number, offset: number, noAssert?: boolean): number; - writeInt32LE(value: number, offset: number, noAssert?: boolean): number; - writeInt32BE(value: number, offset: number, noAssert?: boolean): number; - writeFloatLE(value: number, offset: number, noAssert?: boolean): number; - writeFloatBE(value: number, offset: number, noAssert?: boolean): number; - writeDoubleLE(value: number, offset: number, noAssert?: boolean): number; - writeDoubleBE(value: number, offset: number, noAssert?: boolean): number; - fill(value: any, offset?: number, end?: number): this; - // TODO: encoding param - indexOf(value: string | number | Buffer, byteOffset?: number): number; - // TODO: entries - // TODO: includes - // TODO: keys - // TODO: values -} - -/************************************************ -* * -* MODULES * -* * -************************************************/ -declare module "buffer" { - export var INSPECT_MAX_BYTES: number; - var BuffType: typeof Buffer; - var SlowBuffType: typeof SlowBuffer; - export { BuffType as Buffer, SlowBuffType as SlowBuffer }; -} - -declare module "querystring" { - export interface StringifyOptions { - encodeURIComponent?: Function; - } - - export interface ParseOptions { - maxKeys?: number; - decodeURIComponent?: Function; - } - - export function stringify(obj: T, sep?: string, eq?: string, options?: StringifyOptions): string; - export function parse(str: string, sep?: string, eq?: string, options?: ParseOptions): any; - export function parse(str: string, sep?: string, eq?: string, options?: ParseOptions): T; - export function escape(str: string): string; - export function unescape(str: string): string; -} - -declare module "events" { - export class EventEmitter implements NodeJS.EventEmitter { - static EventEmitter: EventEmitter; - static listenerCount(emitter: EventEmitter, event: string): number; // deprecated - static defaultMaxListeners: number; - - addListener(event: string, listener: Function): this; - on(event: string, listener: Function): this; - once(event: string, listener: Function): this; - prependListener(event: string, listener: Function): this; - prependOnceListener(event: string, listener: Function): this; - removeListener(event: string, listener: Function): this; - removeAllListeners(event?: string): this; - setMaxListeners(n: number): this; - getMaxListeners(): number; - listeners(event: string): Function[]; - emit(event: string, ...args: any[]): boolean; - eventNames(): string[]; - listenerCount(type: string): number; - } -} - -declare module "http" { - import * as events from "events"; - import * as net from "net"; - import * as stream from "stream"; - - export interface RequestOptions { - protocol?: string; - host?: string; - hostname?: string; - family?: number; - port?: number; - localAddress?: string; - socketPath?: string; - method?: string; - path?: string; - headers?: { [key: string]: any }; - auth?: string; - agent?: Agent|boolean; - } - - export interface Server extends events.EventEmitter, net.Server { - setTimeout(msecs: number, callback: Function): void; - maxHeadersCount: number; - timeout: number; - } - /** - * @deprecated Use IncomingMessage - */ - export interface ServerRequest extends IncomingMessage { - connection: net.Socket; - } - export interface ServerResponse extends events.EventEmitter, stream.Writable { - // Extended base methods - write(buffer: Buffer): boolean; - write(buffer: Buffer, cb?: Function): boolean; - write(str: string, cb?: Function): boolean; - write(str: string, encoding?: string, cb?: Function): boolean; - write(str: string, encoding?: string, fd?: string): boolean; - - writeContinue(): void; - writeHead(statusCode: number, reasonPhrase?: string, headers?: any): void; - writeHead(statusCode: number, headers?: any): void; - statusCode: number; - statusMessage: string; - headersSent: boolean; - setHeader(name: string, value: string | string[]): void; - sendDate: boolean; - getHeader(name: string): string; - removeHeader(name: string): void; - write(chunk: any, encoding?: string): any; - addTrailers(headers: any): void; - - // Extended base methods - end(): void; - end(buffer: Buffer, cb?: Function): void; - end(str: string, cb?: Function): void; - end(str: string, encoding?: string, cb?: Function): void; - end(data?: any, encoding?: string): void; - } - export interface ClientRequest extends events.EventEmitter, stream.Writable { - // Extended base methods - write(buffer: Buffer): boolean; - write(buffer: Buffer, cb?: Function): boolean; - write(str: string, cb?: Function): boolean; - write(str: string, encoding?: string, cb?: Function): boolean; - write(str: string, encoding?: string, fd?: string): boolean; - - write(chunk: any, encoding?: string): void; - abort(): void; - setTimeout(timeout: number, callback?: Function): void; - setNoDelay(noDelay?: boolean): void; - setSocketKeepAlive(enable?: boolean, initialDelay?: number): void; - - setHeader(name: string, value: string | string[]): void; - getHeader(name: string): string; - removeHeader(name: string): void; - addTrailers(headers: any): void; - - // Extended base methods - end(): void; - end(buffer: Buffer, cb?: Function): void; - end(str: string, cb?: Function): void; - end(str: string, encoding?: string, cb?: Function): void; - end(data?: any, encoding?: string): void; - } - export interface IncomingMessage extends events.EventEmitter, stream.Readable { - httpVersion: string; - headers: any; - rawHeaders: string[]; - trailers: any; - rawTrailers: any; - setTimeout(msecs: number, callback: Function): NodeJS.Timer; - /** - * Only valid for request obtained from http.Server. - */ - method?: string; - /** - * Only valid for request obtained from http.Server. - */ - url?: string; - /** - * Only valid for response obtained from http.ClientRequest. - */ - statusCode?: number; - /** - * Only valid for response obtained from http.ClientRequest. - */ - statusMessage?: string; - socket: net.Socket; - } - /** - * @deprecated Use IncomingMessage - */ - export interface ClientResponse extends IncomingMessage { } - - export interface AgentOptions { - /** - * Keep sockets around in a pool to be used by other requests in the future. Default = false - */ - keepAlive?: boolean; - /** - * When using HTTP KeepAlive, how often to send TCP KeepAlive packets over sockets being kept alive. Default = 1000. - * Only relevant if keepAlive is set to true. - */ - keepAliveMsecs?: number; - /** - * Maximum number of sockets to allow per host. Default for Node 0.10 is 5, default for Node 0.12 is Infinity - */ - maxSockets?: number; - /** - * Maximum number of sockets to leave open in a free state. Only relevant if keepAlive is set to true. Default = 256. - */ - maxFreeSockets?: number; - } - - export class Agent { - maxSockets: number; - sockets: any; - requests: any; - - constructor(opts?: AgentOptions); - - /** - * Destroy any sockets that are currently in use by the agent. - * It is usually not necessary to do this. However, if you are using an agent with KeepAlive enabled, - * then it is best to explicitly shut down the agent when you know that it will no longer be used. Otherwise, - * sockets may hang open for quite a long time before the server terminates them. - */ - destroy(): void; - } - - export var METHODS: string[]; - - export var STATUS_CODES: { - [errorCode: number]: string; - [errorCode: string]: string; - }; - export function createServer(requestListener?: (request: IncomingMessage, response: ServerResponse) =>void ): Server; - export function createClient(port?: number, host?: string): any; - export function request(options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest; - export function get(options: any, callback?: (res: IncomingMessage) => void): ClientRequest; - export var globalAgent: Agent; -} - -declare module "cluster" { - import * as child from "child_process"; - import * as events from "events"; - - export interface ClusterSettings { - exec?: string; - args?: string[]; - silent?: boolean; - } - - export interface Address { - address: string; - port: number; - addressType: string; - } - - export class Worker extends events.EventEmitter { - id: string; - process: child.ChildProcess; - suicide: boolean; - send(message: any, sendHandle?: any): void; - kill(signal?: string): void; - destroy(signal?: string): void; - disconnect(): void; - isConnected(): boolean; - isDead(): boolean; - } - - export var settings: ClusterSettings; - export var isMaster: boolean; - export var isWorker: boolean; - export function setupMaster(settings?: ClusterSettings): void; - export function fork(env?: any): Worker; - export function disconnect(callback?: Function): void; - export var worker: Worker; - export var workers: { - [index: string]: Worker - }; - - // Event emitter - export function addListener(event: string, listener: Function): void; - export function on(event: "disconnect", listener: (worker: Worker) => void): void; - export function on(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): void; - export function on(event: "fork", listener: (worker: Worker) => void): void; - export function on(event: "listening", listener: (worker: Worker, address: any) => void): void; - export function on(event: "message", listener: (worker: Worker, message: any) => void): void; - export function on(event: "online", listener: (worker: Worker) => void): void; - export function on(event: "setup", listener: (settings: any) => void): void; - export function on(event: string, listener: Function): any; - export function once(event: string, listener: Function): void; - export function removeListener(event: string, listener: Function): void; - export function removeAllListeners(event?: string): void; - export function setMaxListeners(n: number): void; - export function listeners(event: string): Function[]; - export function emit(event: string, ...args: any[]): boolean; -} - -declare module "zlib" { - import * as stream from "stream"; - export interface ZlibOptions { chunkSize?: number; windowBits?: number; level?: number; memLevel?: number; strategy?: number; dictionary?: any; } - - export interface Gzip extends stream.Transform { } - export interface Gunzip extends stream.Transform { } - export interface Deflate extends stream.Transform { } - export interface Inflate extends stream.Transform { } - export interface DeflateRaw extends stream.Transform { } - export interface InflateRaw extends stream.Transform { } - export interface Unzip extends stream.Transform { } - - export function createGzip(options?: ZlibOptions): Gzip; - export function createGunzip(options?: ZlibOptions): Gunzip; - export function createDeflate(options?: ZlibOptions): Deflate; - export function createInflate(options?: ZlibOptions): Inflate; - export function createDeflateRaw(options?: ZlibOptions): DeflateRaw; - export function createInflateRaw(options?: ZlibOptions): InflateRaw; - export function createUnzip(options?: ZlibOptions): Unzip; - - export function deflate(buf: Buffer, callback: (error: Error, result: any) =>void ): void; - export function deflateSync(buf: Buffer, options?: ZlibOptions): any; - export function deflateRaw(buf: Buffer, callback: (error: Error, result: any) =>void ): void; - export function deflateRawSync(buf: Buffer, options?: ZlibOptions): any; - export function gzip(buf: Buffer, callback: (error: Error, result: any) =>void ): void; - export function gzipSync(buf: Buffer, options?: ZlibOptions): any; - export function gunzip(buf: Buffer, callback: (error: Error, result: any) =>void ): void; - export function gunzipSync(buf: Buffer, options?: ZlibOptions): any; - export function inflate(buf: Buffer, callback: (error: Error, result: any) =>void ): void; - export function inflateSync(buf: Buffer, options?: ZlibOptions): any; - export function inflateRaw(buf: Buffer, callback: (error: Error, result: any) =>void ): void; - export function inflateRawSync(buf: Buffer, options?: ZlibOptions): any; - export function unzip(buf: Buffer, callback: (error: Error, result: any) =>void ): void; - export function unzipSync(buf: Buffer, options?: ZlibOptions): any; - - // Constants - export var Z_NO_FLUSH: number; - export var Z_PARTIAL_FLUSH: number; - export var Z_SYNC_FLUSH: number; - export var Z_FULL_FLUSH: number; - export var Z_FINISH: number; - export var Z_BLOCK: number; - export var Z_TREES: number; - export var Z_OK: number; - export var Z_STREAM_END: number; - export var Z_NEED_DICT: number; - export var Z_ERRNO: number; - export var Z_STREAM_ERROR: number; - export var Z_DATA_ERROR: number; - export var Z_MEM_ERROR: number; - export var Z_BUF_ERROR: number; - export var Z_VERSION_ERROR: number; - export var Z_NO_COMPRESSION: number; - export var Z_BEST_SPEED: number; - export var Z_BEST_COMPRESSION: number; - export var Z_DEFAULT_COMPRESSION: number; - export var Z_FILTERED: number; - export var Z_HUFFMAN_ONLY: number; - export var Z_RLE: number; - export var Z_FIXED: number; - export var Z_DEFAULT_STRATEGY: number; - export var Z_BINARY: number; - export var Z_TEXT: number; - export var Z_ASCII: number; - export var Z_UNKNOWN: number; - export var Z_DEFLATED: number; - export var Z_NULL: number; -} - -declare module "os" { - export interface CpuInfo { - model: string; - speed: number; - times: { - user: number; - nice: number; - sys: number; - idle: number; - irq: number; - }; - } - - export interface NetworkInterfaceInfo { - address: string; - netmask: string; - family: string; - mac: string; - internal: boolean; - } - - export function tmpdir(): string; - export function homedir(): string; - export function endianness(): "BE" | "LE"; - export function hostname(): string; - export function type(): string; - export function platform(): string; - export function arch(): string; - export function release(): string; - export function uptime(): number; - export function loadavg(): number[]; - export function totalmem(): number; - export function freemem(): number; - export function cpus(): CpuInfo[]; - export function networkInterfaces(): {[index: string]: NetworkInterfaceInfo[]}; - export var EOL: string; -} - -declare module "https" { - import * as tls from "tls"; - import * as events from "events"; - import * as http from "http"; - - export interface ServerOptions { - pfx?: any; - key?: any; - passphrase?: string; - cert?: any; - ca?: any; - crl?: any; - ciphers?: string; - honorCipherOrder?: boolean; - requestCert?: boolean; - rejectUnauthorized?: boolean; - NPNProtocols?: any; - SNICallback?: (servername: string) => any; - } - - export interface RequestOptions extends http.RequestOptions { - pfx?: any; - key?: any; - passphrase?: string; - cert?: any; - ca?: any; - ciphers?: string; - rejectUnauthorized?: boolean; - secureProtocol?: string; - } - - export interface Agent extends http.Agent { } - - export interface AgentOptions extends http.AgentOptions { - maxCachedSessions?: number; - } - - export var Agent: { - new (options?: AgentOptions): Agent; - }; - export interface Server extends tls.Server { } - export function createServer(options: ServerOptions, requestListener?: Function): Server; - export function request(options: RequestOptions, callback?: (res: http.IncomingMessage) =>void ): http.ClientRequest; - export function get(options: RequestOptions, callback?: (res: http.IncomingMessage) =>void ): http.ClientRequest; - export var globalAgent: Agent; -} - -declare module "punycode" { - export function decode(string: string): string; - export function encode(string: string): string; - export function toUnicode(domain: string): string; - export function toASCII(domain: string): string; - export var ucs2: ucs2; - interface ucs2 { - decode(string: string): number[]; - encode(codePoints: number[]): string; - } - export var version: any; -} - -declare module "repl" { - import * as stream from "stream"; - import * as events from "events"; - - export interface ReplOptions { - prompt?: string; - input?: NodeJS.ReadableStream; - output?: NodeJS.WritableStream; - terminal?: boolean; - eval?: Function; - useColors?: boolean; - useGlobal?: boolean; - ignoreUndefined?: boolean; - writer?: Function; - } - export function start(options: ReplOptions): events.EventEmitter; -} - -declare module "readline" { - import * as events from "events"; - import * as stream from "stream"; - - export interface Key { - sequence?: string; - name?: string; - ctrl?: boolean; - meta?: boolean; - shift?: boolean; - } - - export interface ReadLine extends events.EventEmitter { - setPrompt(prompt: string): void; - prompt(preserveCursor?: boolean): void; - question(query: string, callback: (answer: string) => void): void; - pause(): ReadLine; - resume(): ReadLine; - close(): void; - write(data: string|Buffer, key?: Key): void; - } - - export interface Completer { - (line: string): CompleterResult; - (line: string, callback: (err: any, result: CompleterResult) => void): any; - } - - export interface CompleterResult { - completions: string[]; - line: string; - } - - export interface ReadLineOptions { - input: NodeJS.ReadableStream; - output?: NodeJS.WritableStream; - completer?: Completer; - terminal?: boolean; - historySize?: number; - } - - export function createInterface(input: NodeJS.ReadableStream, output?: NodeJS.WritableStream, completer?: Completer, terminal?: boolean): ReadLine; - export function createInterface(options: ReadLineOptions): ReadLine; - - export function cursorTo(stream: NodeJS.WritableStream, x: number, y: number): void; - export function moveCursor(stream: NodeJS.WritableStream, dx: number|string, dy: number|string): void; - export function clearLine(stream: NodeJS.WritableStream, dir: number): void; - export function clearScreenDown(stream: NodeJS.WritableStream): void; -} - -declare module "vm" { - export interface Context { } - export interface ScriptOptions { - filename?: string; - lineOffset?: number; - columnOffset?: number; - displayErrors?: boolean; - timeout?: number; - cachedData?: Buffer; - produceCachedData?: boolean; - } - export interface RunningScriptOptions { - filename?: string; - lineOffset?: number; - columnOffset?: number; - displayErrors?: boolean; - timeout?: number; - } - export class Script { - constructor(code: string, options?: ScriptOptions); - runInContext(contextifiedSandbox: Context, options?: RunningScriptOptions): any; - runInNewContext(sandbox?: Context, options?: RunningScriptOptions): any; - runInThisContext(options?: RunningScriptOptions): any; - } - export function createContext(sandbox?: Context): Context; - export function isContext(sandbox: Context): boolean; - export function runInContext(code: string, contextifiedSandbox: Context, options?: RunningScriptOptions): any; - export function runInDebugContext(code: string): any; - export function runInNewContext(code: string, sandbox?: Context, options?: RunningScriptOptions): any; - export function runInThisContext(code: string, options?: RunningScriptOptions): any; -} - -declare module "child_process" { - import * as events from "events"; - import * as stream from "stream"; - - export interface ChildProcess extends events.EventEmitter { - stdin: stream.Writable; - stdout: stream.Readable; - stderr: stream.Readable; - stdio: [stream.Writable, stream.Readable, stream.Readable]; - pid: number; - kill(signal?: string): void; - send(message: any, sendHandle?: any): void; - connected: boolean; - disconnect(): void; - unref(): void; - } - - export interface SpawnOptions { - cwd?: string; - env?: any; - stdio?: any; - detached?: boolean; - uid?: number; - gid?: number; - shell?: boolean | string; - } - export function spawn(command: string, args?: string[], options?: SpawnOptions): ChildProcess; - - export interface ExecOptions { - cwd?: string; - env?: any; - shell?: string; - timeout?: number; - maxBuffer?: number; - killSignal?: string; - uid?: number; - gid?: number; - } - export interface ExecOptionsWithStringEncoding extends ExecOptions { - encoding: BufferEncoding; - } - export interface ExecOptionsWithBufferEncoding extends ExecOptions { - encoding: string; // specify `null`. - } - export function exec(command: string, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; - export function exec(command: string, options: ExecOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; - // usage. child_process.exec("tsc", {encoding: null as string}, (err, stdout, stderr) => {}); - export function exec(command: string, options: ExecOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; - export function exec(command: string, options: ExecOptions, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; - - export interface ExecFileOptions { - cwd?: string; - env?: any; - timeout?: number; - maxBuffer?: number; - killSignal?: string; - uid?: number; - gid?: number; - } - export interface ExecFileOptionsWithStringEncoding extends ExecFileOptions { - encoding: BufferEncoding; - } - export interface ExecFileOptionsWithBufferEncoding extends ExecFileOptions { - encoding: string; // specify `null`. - } - export function execFile(file: string, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; - export function execFile(file: string, options?: ExecFileOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; - // usage. child_process.execFile("file.sh", {encoding: null as string}, (err, stdout, stderr) => {}); - export function execFile(file: string, options?: ExecFileOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; - export function execFile(file: string, options?: ExecFileOptions, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; - export function execFile(file: string, args?: string[], callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; - export function execFile(file: string, args?: string[], options?: ExecFileOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; - // usage. child_process.execFile("file.sh", ["foo"], {encoding: null as string}, (err, stdout, stderr) => {}); - export function execFile(file: string, args?: string[], options?: ExecFileOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; - export function execFile(file: string, args?: string[], options?: ExecFileOptions, callback?: (error: Error, stdout: string, stderr: string) =>void ): ChildProcess; - - export interface ForkOptions { - cwd?: string; - env?: any; - execPath?: string; - execArgv?: string[]; - silent?: boolean; - uid?: number; - gid?: number; - } - export function fork(modulePath: string, args?: string[], options?: ForkOptions): ChildProcess; - - export interface SpawnSyncOptions { - cwd?: string; - input?: string | Buffer; - stdio?: any; - env?: any; - uid?: number; - gid?: number; - timeout?: number; - killSignal?: string; - maxBuffer?: number; - encoding?: string; - shell?: boolean | string; - } - export interface SpawnSyncOptionsWithStringEncoding extends SpawnSyncOptions { - encoding: BufferEncoding; - } - export interface SpawnSyncOptionsWithBufferEncoding extends SpawnSyncOptions { - encoding: string; // specify `null`. - } - export interface SpawnSyncReturns { - pid: number; - output: string[]; - stdout: T; - stderr: T; - status: number; - signal: string; - error: Error; - } - export function spawnSync(command: string): SpawnSyncReturns; - export function spawnSync(command: string, options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns; - export function spawnSync(command: string, options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns; - export function spawnSync(command: string, options?: SpawnSyncOptions): SpawnSyncReturns; - export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns; - export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns; - export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptions): SpawnSyncReturns; - - export interface ExecSyncOptions { - cwd?: string; - input?: string | Buffer; - stdio?: any; - env?: any; - shell?: string; - uid?: number; - gid?: number; - timeout?: number; - killSignal?: string; - maxBuffer?: number; - encoding?: string; - } - export interface ExecSyncOptionsWithStringEncoding extends ExecSyncOptions { - encoding: BufferEncoding; - } - export interface ExecSyncOptionsWithBufferEncoding extends ExecSyncOptions { - encoding: string; // specify `null`. - } - export function execSync(command: string): Buffer; - export function execSync(command: string, options?: ExecSyncOptionsWithStringEncoding): string; - export function execSync(command: string, options?: ExecSyncOptionsWithBufferEncoding): Buffer; - export function execSync(command: string, options?: ExecSyncOptions): Buffer; - - export interface ExecFileSyncOptions { - cwd?: string; - input?: string | Buffer; - stdio?: any; - env?: any; - uid?: number; - gid?: number; - timeout?: number; - killSignal?: string; - maxBuffer?: number; - encoding?: string; - } - export interface ExecFileSyncOptionsWithStringEncoding extends ExecFileSyncOptions { - encoding: BufferEncoding; - } - export interface ExecFileSyncOptionsWithBufferEncoding extends ExecFileSyncOptions { - encoding: string; // specify `null`. - } - export function execFileSync(command: string): Buffer; - export function execFileSync(command: string, options?: ExecFileSyncOptionsWithStringEncoding): string; - export function execFileSync(command: string, options?: ExecFileSyncOptionsWithBufferEncoding): Buffer; - export function execFileSync(command: string, options?: ExecFileSyncOptions): Buffer; - export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithStringEncoding): string; - export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithBufferEncoding): Buffer; - export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptions): Buffer; -} - -declare module "url" { - export interface Url { - href?: string; - protocol?: string; - auth?: string; - hostname?: string; - port?: string; - host?: string; - pathname?: string; - search?: string; - query?: string | any; - slashes?: boolean; - hash?: string; - path?: string; - } - - export function parse(urlStr: string, parseQueryString?: boolean , slashesDenoteHost?: boolean ): Url; - export function format(url: Url): string; - export function resolve(from: string, to: string): string; -} - -declare module "dns" { - export function lookup(domain: string, family: number, callback: (err: Error, address: string, family: number) =>void ): string; - export function lookup(domain: string, callback: (err: Error, address: string, family: number) =>void ): string; - export function resolve(domain: string, rrtype: string, callback: (err: Error, addresses: string[]) =>void ): string[]; - export function resolve(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; - export function resolve4(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; - export function resolve6(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; - export function resolveMx(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; - export function resolveTxt(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; - export function resolveSrv(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; - export function resolveNs(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; - export function resolveCname(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; - export function reverse(ip: string, callback: (err: Error, domains: string[]) =>void ): string[]; -} - -declare module "net" { - import * as stream from "stream"; - - export interface Socket extends stream.Duplex { - // Extended base methods - write(buffer: Buffer): boolean; - write(buffer: Buffer, cb?: Function): boolean; - write(str: string, cb?: Function): boolean; - write(str: string, encoding?: string, cb?: Function): boolean; - write(str: string, encoding?: string, fd?: string): boolean; - - connect(port: number, host?: string, connectionListener?: Function): void; - connect(path: string, connectionListener?: Function): void; - bufferSize: number; - setEncoding(encoding?: string): void; - write(data: any, encoding?: string, callback?: Function): void; - destroy(): void; - pause(): void; - resume(): void; - setTimeout(timeout: number, callback?: Function): void; - setNoDelay(noDelay?: boolean): void; - setKeepAlive(enable?: boolean, initialDelay?: number): void; - address(): { port: number; family: string; address: string; }; - unref(): void; - ref(): void; - - remoteAddress: string; - remoteFamily: string; - remotePort: number; - localAddress: string; - localPort: number; - bytesRead: number; - bytesWritten: number; - - // Extended base methods - end(): void; - end(buffer: Buffer, cb?: Function): void; - end(str: string, cb?: Function): void; - end(str: string, encoding?: string, cb?: Function): void; - end(data?: any, encoding?: string): void; - } - - export var Socket: { - new (options?: { fd?: string; type?: string; allowHalfOpen?: boolean; }): Socket; - }; - - export interface ListenOptions { - port?: number; - host?: string; - backlog?: number; - path?: string; - exclusive?: boolean; - } - - export interface Server extends Socket { - listen(port: number, hostname?: string, backlog?: number, listeningListener?: Function): Server; - listen(port: number, hostname?: string, listeningListener?: Function): Server; - listen(port: number, backlog?: number, listeningListener?: Function): Server; - listen(port: number, listeningListener?: Function): Server; - listen(path: string, backlog?: number, listeningListener?: Function): Server; - listen(path: string, listeningListener?: Function): Server; - listen(handle: any, backlog?: number, listeningListener?: Function): Server; - listen(handle: any, listeningListener?: Function): Server; - listen(options: ListenOptions, listeningListener?: Function): Server; - close(callback?: Function): Server; - address(): { port: number; family: string; address: string; }; - getConnections(cb: (error: Error, count: number) => void): void; - ref(): Server; - unref(): Server; - maxConnections: number; - connections: number; - } - export function createServer(connectionListener?: (socket: Socket) =>void ): Server; - export function createServer(options?: { allowHalfOpen?: boolean; }, connectionListener?: (socket: Socket) =>void ): Server; - export function connect(options: { port: number, host?: string, localAddress? : string, localPort? : string, family? : number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; - export function connect(port: number, host?: string, connectionListener?: Function): Socket; - export function connect(path: string, connectionListener?: Function): Socket; - export function createConnection(options: { port: number, host?: string, localAddress? : string, localPort? : string, family? : number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; - export function createConnection(port: number, host?: string, connectionListener?: Function): Socket; - export function createConnection(path: string, connectionListener?: Function): Socket; - export function isIP(input: string): number; - export function isIPv4(input: string): boolean; - export function isIPv6(input: string): boolean; -} - -declare module "dgram" { - import * as events from "events"; - - interface RemoteInfo { - address: string; - port: number; - size: number; - } - - interface AddressInfo { - address: string; - family: string; - port: number; - } - - export function createSocket(type: string, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket; - - interface Socket extends events.EventEmitter { - send(buf: Buffer, offset: number, length: number, port: number, address: string, callback?: (error: Error, bytes: number) => void): void; - bind(port: number, address?: string, callback?: () => void): void; - close(): void; - address(): AddressInfo; - setBroadcast(flag: boolean): void; - setMulticastTTL(ttl: number): void; - setMulticastLoopback(flag: boolean): void; - addMembership(multicastAddress: string, multicastInterface?: string): void; - dropMembership(multicastAddress: string, multicastInterface?: string): void; - } -} - -declare module "fs" { - import * as stream from "stream"; - import * as events from "events"; - - interface Stats { - isFile(): boolean; - isDirectory(): boolean; - isBlockDevice(): boolean; - isCharacterDevice(): boolean; - isSymbolicLink(): boolean; - isFIFO(): boolean; - isSocket(): boolean; - dev: number; - ino: number; - mode: number; - nlink: number; - uid: number; - gid: number; - rdev: number; - size: number; - blksize: number; - blocks: number; - atime: Date; - mtime: Date; - ctime: Date; - birthtime: Date; - } - - interface FSWatcher extends events.EventEmitter { - close(): void; - } - - export interface ReadStream extends stream.Readable { - close(): void; - destroy(): void; - } - export interface WriteStream extends stream.Writable { - close(): void; - bytesWritten: number; - } - - /** - * Asynchronous rename. - * @param oldPath - * @param newPath - * @param callback No arguments other than a possible exception are given to the completion callback. - */ - export function rename(oldPath: string, newPath: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - /** - * Synchronous rename - * @param oldPath - * @param newPath - */ - export function renameSync(oldPath: string, newPath: string): void; - export function truncate(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function truncate(path: string | Buffer, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function truncateSync(path: string | Buffer, len?: number): void; - export function ftruncate(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function ftruncate(fd: number, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function ftruncateSync(fd: number, len?: number): void; - export function chown(path: string | Buffer, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function chownSync(path: string | Buffer, uid: number, gid: number): void; - export function fchown(fd: number, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function fchownSync(fd: number, uid: number, gid: number): void; - export function lchown(path: string | Buffer, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function lchownSync(path: string | Buffer, uid: number, gid: number): void; - export function chmod(path: string | Buffer, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function chmod(path: string | Buffer, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function chmodSync(path: string | Buffer, mode: number): void; - export function chmodSync(path: string | Buffer, mode: string): void; - export function fchmod(fd: number, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function fchmod(fd: number, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function fchmodSync(fd: number, mode: number): void; - export function fchmodSync(fd: number, mode: string): void; - export function lchmod(path: string | Buffer, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function lchmod(path: string | Buffer, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function lchmodSync(path: string | Buffer, mode: number): void; - export function lchmodSync(path: string | Buffer, mode: string): void; - export function stat(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; - export function lstat(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; - export function fstat(fd: number, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; - export function statSync(path: string | Buffer): Stats; - export function lstatSync(path: string | Buffer): Stats; - export function fstatSync(fd: number): Stats; - export function link(srcpath: string | Buffer, dstpath: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function linkSync(srcpath: string | Buffer, dstpath: string | Buffer): void; - export function symlink(srcpath: string | Buffer, dstpath: string | Buffer, type?: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function symlinkSync(srcpath: string | Buffer, dstpath: string | Buffer, type?: string): void; - export function readlink(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, linkString: string) => any): void; - export function readlinkSync(path: string | Buffer): string; - export function realpath(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void; - export function realpath(path: string | Buffer, cache: {[path: string]: string}, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void; - export function realpathSync(path: string | Buffer, cache?: { [path: string]: string }): string; - /* - * Asynchronous unlink - deletes the file specified in {path} - * - * @param path - * @param callback No arguments other than a possible exception are given to the completion callback. - */ - export function unlink(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; - /* - * Synchronous unlink - deletes the file specified in {path} - * - * @param path - */ - export function unlinkSync(path: string | Buffer): void; - /* - * Asynchronous rmdir - removes the directory specified in {path} - * - * @param path - * @param callback No arguments other than a possible exception are given to the completion callback. - */ - export function rmdir(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; - /* - * Synchronous rmdir - removes the directory specified in {path} - * - * @param path - */ - export function rmdirSync(path: string | Buffer): void; - /* - * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param callback No arguments other than a possible exception are given to the completion callback. - */ - export function mkdir(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; - /* - * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param mode - * @param callback No arguments other than a possible exception are given to the completion callback. - */ - export function mkdir(path: string | Buffer, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - /* - * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param mode - * @param callback No arguments other than a possible exception are given to the completion callback. - */ - export function mkdir(path: string | Buffer, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - /* - * Synchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param mode - * @param callback No arguments other than a possible exception are given to the completion callback. - */ - export function mkdirSync(path: string | Buffer, mode?: number): void; - /* - * Synchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param mode - * @param callback No arguments other than a possible exception are given to the completion callback. - */ - export function mkdirSync(path: string | Buffer, mode?: string): void; - /* - * Asynchronous mkdtemp - Creates a unique temporary directory. Generates six random characters to be appended behind a required prefix to create a unique temporary directory. - * - * @param prefix - * @param callback The created folder path is passed as a string to the callback's second parameter. - */ - export function mkdtemp(prefix: string, callback?: (err: NodeJS.ErrnoException, folder: string) => void): void; - /* - * Synchronous mkdtemp - Creates a unique temporary directory. Generates six random characters to be appended behind a required prefix to create a unique temporary directory. - * - * @param prefix - * @returns Returns the created folder path. - */ - export function mkdtempSync(prefix: string): string; - export function readdir(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, files: string[]) => void): void; - export function readdirSync(path: string | Buffer): string[]; - export function close(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function closeSync(fd: number): void; - export function open(path: string | Buffer, flags: string | number, callback: (err: NodeJS.ErrnoException, fd: number) => void): void; - export function open(path: string | Buffer, flags: string | number, mode: number, callback: (err: NodeJS.ErrnoException, fd: number) => void): void; - export function openSync(path: string | Buffer, flags: string | number, mode?: number): number; - export function utimes(path: string | Buffer, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function utimes(path: string | Buffer, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function utimesSync(path: string | Buffer, atime: number, mtime: number): void; - export function utimesSync(path: string | Buffer, atime: Date, mtime: Date): void; - export function futimes(fd: number, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function futimes(fd: number, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function futimesSync(fd: number, atime: number, mtime: number): void; - export function futimesSync(fd: number, atime: Date, mtime: Date): void; - export function fsync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function fsyncSync(fd: number): void; - export function write(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void; - export function write(fd: number, buffer: Buffer, offset: number, length: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void; - export function write(fd: number, data: any, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; - export function write(fd: number, data: any, offset: number, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; - export function write(fd: number, data: any, offset: number, encoding: string, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; - export function writeSync(fd: number, buffer: Buffer, offset: number, length: number, position?: number): number; - export function writeSync(fd: number, data: any, position?: number, enconding?: string): number; - export function read(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, bytesRead: number, buffer: Buffer) => void): void; - export function readSync(fd: number, buffer: Buffer, offset: number, length: number, position: number): number; - /* - * Asynchronous readFile - Asynchronously reads the entire contents of a file. - * - * @param fileName - * @param encoding - * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. - */ - export function readFile(filename: string, encoding: string, callback: (err: NodeJS.ErrnoException, data: string) => void): void; - /* - * Asynchronous readFile - Asynchronously reads the entire contents of a file. - * - * @param fileName - * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer. - * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. - */ - export function readFile(filename: string, options: { encoding: string; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string) => void): void; - /* - * Asynchronous readFile - Asynchronously reads the entire contents of a file. - * - * @param fileName - * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer. - * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. - */ - export function readFile(filename: string, options: { flag?: string; }, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; - /* - * Asynchronous readFile - Asynchronously reads the entire contents of a file. - * - * @param fileName - * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. - */ - export function readFile(filename: string, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; - /* - * Synchronous readFile - Synchronously reads the entire contents of a file. - * - * @param fileName - * @param encoding - */ - export function readFileSync(filename: string, encoding: string): string; - /* - * Synchronous readFile - Synchronously reads the entire contents of a file. - * - * @param fileName - * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer. - */ - export function readFileSync(filename: string, options: { encoding: string; flag?: string; }): string; - /* - * Synchronous readFile - Synchronously reads the entire contents of a file. - * - * @param fileName - * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer. - */ - export function readFileSync(filename: string, options?: { flag?: string; }): Buffer; - export function writeFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; - export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; - export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; - export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; - export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; - export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; - export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; - export function appendFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; - export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; - export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; - export function watchFile(filename: string, listener: (curr: Stats, prev: Stats) => void): void; - export function watchFile(filename: string, options: { persistent?: boolean; interval?: number; }, listener: (curr: Stats, prev: Stats) => void): void; - export function unwatchFile(filename: string, listener?: (curr: Stats, prev: Stats) => void): void; - export function watch(filename: string, listener?: (event: string, filename: string) => any): FSWatcher; - export function watch(filename: string, encoding: string, listener?: (event: string, filename: string) => any): FSWatcher; - export function watch(filename: string, options: { persistent?: boolean; recursive?: boolean; encoding?: string }, listener?: (event: string, filename: string) => any): FSWatcher; - export function exists(path: string | Buffer, callback?: (exists: boolean) => void): void; - export function existsSync(path: string | Buffer): boolean; - /** Constant for fs.access(). File is visible to the calling process. */ - export var F_OK: number; - /** Constant for fs.access(). File can be read by the calling process. */ - export var R_OK: number; - /** Constant for fs.access(). File can be written by the calling process. */ - export var W_OK: number; - /** Constant for fs.access(). File can be executed by the calling process. */ - export var X_OK: number; - /** Tests a user's permissions for the file specified by path. */ - export function access(path: string | Buffer, callback: (err: NodeJS.ErrnoException) => void): void; - export function access(path: string | Buffer, mode: number, callback: (err: NodeJS.ErrnoException) => void): void; - /** Synchronous version of fs.access. This throws if any accessibility checks fail, and does nothing otherwise. */ - export function accessSync(path: string | Buffer, mode ?: number): void; - export function createReadStream(path: string | Buffer, options?: { - flags?: string; - encoding?: string; - fd?: number; - mode?: number; - autoClose?: boolean; - start?: number; - end?: number; - }): ReadStream; - export function createWriteStream(path: string | Buffer, options?: { - flags?: string; - encoding?: string; - fd?: number; - mode?: number; - }): WriteStream; -} - -declare module "path" { - - /** - * A parsed path object generated by path.parse() or consumed by path.format(). - */ - export interface ParsedPath { - /** - * The root of the path such as '/' or 'c:\' - */ - root: string; - /** - * The full directory path such as '/home/user/dir' or 'c:\path\dir' - */ - dir: string; - /** - * The file name including extension (if any) such as 'index.html' - */ - base: string; - /** - * The file extension (if any) such as '.html' - */ - ext: string; - /** - * The file name without extension (if any) such as 'index' - */ - name: string; - } - - /** - * Normalize a string path, reducing '..' and '.' parts. - * When multiple slashes are found, they're replaced by a single one; when the path contains a trailing slash, it is preserved. On Windows backslashes are used. - * - * @param p string path to normalize. - */ - export function normalize(p: string): string; - /** - * Join all arguments together and normalize the resulting path. - * Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown. - * - * @param paths string paths to join. - */ - export function join(...paths: any[]): string; - /** - * Join all arguments together and normalize the resulting path. - * Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown. - * - * @param paths string paths to join. - */ - export function join(...paths: string[]): string; - /** - * The right-most parameter is considered {to}. Other parameters are considered an array of {from}. - * - * Starting from leftmost {from} paramter, resolves {to} to an absolute path. - * - * If {to} isn't already absolute, {from} arguments are prepended in right to left order, until an absolute path is found. If after using all {from} paths still no absolute path is found, the current working directory is used as well. The resulting path is normalized, and trailing slashes are removed unless the path gets resolved to the root directory. - * - * @param pathSegments string paths to join. Non-string arguments are ignored. - */ - export function resolve(...pathSegments: any[]): string; - /** - * Determines whether {path} is an absolute path. An absolute path will always resolve to the same location, regardless of the working directory. - * - * @param path path to test. - */ - export function isAbsolute(path: string): boolean; - /** - * Solve the relative path from {from} to {to}. - * At times we have two absolute paths, and we need to derive the relative path from one to the other. This is actually the reverse transform of path.resolve. - * - * @param from - * @param to - */ - export function relative(from: string, to: string): string; - /** - * Return the directory name of a path. Similar to the Unix dirname command. - * - * @param p the path to evaluate. - */ - export function dirname(p: string): string; - /** - * Return the last portion of a path. Similar to the Unix basename command. - * Often used to extract the file name from a fully qualified path. - * - * @param p the path to evaluate. - * @param ext optionally, an extension to remove from the result. - */ - export function basename(p: string, ext?: string): string; - /** - * Return the extension of the path, from the last '.' to end of string in the last portion of the path. - * If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string - * - * @param p the path to evaluate. - */ - export function extname(p: string): string; - /** - * The platform-specific file separator. '\\' or '/'. - */ - export var sep: string; - /** - * The platform-specific file delimiter. ';' or ':'. - */ - export var delimiter: string; - /** - * Returns an object from a path string - the opposite of format(). - * - * @param pathString path to evaluate. - */ - export function parse(pathString: string): ParsedPath; - /** - * Returns a path string from an object - the opposite of parse(). - * - * @param pathString path to evaluate. - */ - export function format(pathObject: ParsedPath): string; - - export module posix { - export function normalize(p: string): string; - export function join(...paths: any[]): string; - export function resolve(...pathSegments: any[]): string; - export function isAbsolute(p: string): boolean; - export function relative(from: string, to: string): string; - export function dirname(p: string): string; - export function basename(p: string, ext?: string): string; - export function extname(p: string): string; - export var sep: string; - export var delimiter: string; - export function parse(p: string): ParsedPath; - export function format(pP: ParsedPath): string; - } - - export module win32 { - export function normalize(p: string): string; - export function join(...paths: any[]): string; - export function resolve(...pathSegments: any[]): string; - export function isAbsolute(p: string): boolean; - export function relative(from: string, to: string): string; - export function dirname(p: string): string; - export function basename(p: string, ext?: string): string; - export function extname(p: string): string; - export var sep: string; - export var delimiter: string; - export function parse(p: string): ParsedPath; - export function format(pP: ParsedPath): string; - } -} - -declare module "string_decoder" { - export interface NodeStringDecoder { - write(buffer: Buffer): string; - detectIncompleteChar(buffer: Buffer): number; - } - export var StringDecoder: { - new (encoding: string): NodeStringDecoder; - }; -} - -declare module "tls" { - import * as crypto from "crypto"; - import * as net from "net"; - import * as stream from "stream"; - - var CLIENT_RENEG_LIMIT: number; - var CLIENT_RENEG_WINDOW: number; - - export interface Certificate { - /** - * Country code. - */ - C: string; - /** - * Street. - */ - ST: string; - /** - * Locality. - */ - L: string; - /** - * Organization. - */ - O: string; - /** - * Organizational unit. - */ - OU: string; - /** - * Common name. - */ - CN: string; - } - - export interface CipherNameAndProtocol { - /** - * The cipher name. - */ - name: string; - /** - * SSL/TLS protocol version. - */ - version: string; - } - - export class TLSSocket extends stream.Duplex { - /** - * Returns the bound address, the address family name and port of the underlying socket as reported by - * the operating system. - * @returns {any} - An object with three properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' }. - */ - address(): { port: number; family: string; address: string }; - /** - * A boolean that is true if the peer certificate was signed by one of the specified CAs, otherwise false. - */ - authorized: boolean; - /** - * The reason why the peer's certificate has not been verified. - * This property becomes available only when tlsSocket.authorized === false. - */ - authorizationError: Error; - /** - * Static boolean value, always true. - * May be used to distinguish TLS sockets from regular ones. - */ - encrypted: boolean; - /** - * Returns an object representing the cipher name and the SSL/TLS protocol version of the current connection. - * @returns {CipherNameAndProtocol} - Returns an object representing the cipher name - * and the SSL/TLS protocol version of the current connection. - */ - getCipher(): CipherNameAndProtocol; - /** - * Returns an object representing the peer's certificate. - * The returned object has some properties corresponding to the field of the certificate. - * If detailed argument is true the full chain with issuer property will be returned, - * if false only the top certificate without issuer property. - * If the peer does not provide a certificate, it returns null or an empty object. - * @param {boolean} detailed - If true; the full chain with issuer property will be returned. - * @returns {any} - An object representing the peer's certificate. - */ - getPeerCertificate(detailed?: boolean): { - subject: Certificate; - issuerInfo: Certificate; - issuer: Certificate; - raw: any; - valid_from: string; - valid_to: string; - fingerprint: string; - serialNumber: string; - }; - /** - * Could be used to speed up handshake establishment when reconnecting to the server. - * @returns {any} - ASN.1 encoded TLS session or undefined if none was negotiated. - */ - getSession(): any; - /** - * NOTE: Works only with client TLS sockets. - * Useful only for debugging, for session reuse provide session option to tls.connect(). - * @returns {any} - TLS session ticket or undefined if none was negotiated. - */ - getTLSTicket(): any; - /** - * The string representation of the local IP address. - */ - localAddress: string; - /** - * The numeric representation of the local port. - */ - localPort: string; - /** - * The string representation of the remote IP address. - * For example, '74.125.127.100' or '2001:4860:a005::68'. - */ - remoteAddress: string; - /** - * The string representation of the remote IP family. 'IPv4' or 'IPv6'. - */ - remoteFamily: string; - /** - * The numeric representation of the remote port. For example, 443. - */ - remotePort: number; - /** - * Initiate TLS renegotiation process. - * - * NOTE: Can be used to request peer's certificate after the secure connection has been established. - * ANOTHER NOTE: When running as the server, socket will be destroyed with an error after handshakeTimeout timeout. - * @param {TlsOptions} options - The options may contain the following fields: rejectUnauthorized, - * requestCert (See tls.createServer() for details). - * @param {Function} callback - callback(err) will be executed with null as err, once the renegotiation - * is successfully completed. - */ - renegotiate(options: TlsOptions, callback: (err: Error) => any): any; - /** - * Set maximum TLS fragment size (default and maximum value is: 16384, minimum is: 512). - * Smaller fragment size decreases buffering latency on the client: large fragments are buffered by - * the TLS layer until the entire fragment is received and its integrity is verified; - * large fragments can span multiple roundtrips, and their processing can be delayed due to packet - * loss or reordering. However, smaller fragments add extra TLS framing bytes and CPU overhead, - * which may decrease overall server throughput. - * @param {number} size - TLS fragment size (default and maximum value is: 16384, minimum is: 512). - * @returns {boolean} - Returns true on success, false otherwise. - */ - setMaxSendFragment(size: number): boolean; - } - - export interface TlsOptions { - host?: string; - port?: number; - pfx?: any; //string or buffer - key?: any; //string or buffer - passphrase?: string; - cert?: any; - ca?: any; //string or buffer - crl?: any; //string or string array - ciphers?: string; - honorCipherOrder?: any; - requestCert?: boolean; - rejectUnauthorized?: boolean; - NPNProtocols?: any; //array or Buffer; - SNICallback?: (servername: string) => any; - } - - export interface ConnectionOptions { - host?: string; - port?: number; - socket?: net.Socket; - pfx?: string | Buffer - key?: string | Buffer - passphrase?: string; - cert?: string | Buffer - ca?: (string | Buffer)[]; - rejectUnauthorized?: boolean; - NPNProtocols?: (string | Buffer)[]; - servername?: string; - } - - export interface Server extends net.Server { - close(): Server; - address(): { port: number; family: string; address: string; }; - addContext(hostName: string, credentials: { - key: string; - cert: string; - ca: string; - }): void; - maxConnections: number; - connections: number; - } - - export interface ClearTextStream extends stream.Duplex { - authorized: boolean; - authorizationError: Error; - getPeerCertificate(): any; - getCipher: { - name: string; - version: string; - }; - address: { - port: number; - family: string; - address: string; - }; - remoteAddress: string; - remotePort: number; - } - - export interface SecurePair { - encrypted: any; - cleartext: any; - } - - export interface SecureContextOptions { - pfx?: string | Buffer; - key?: string | Buffer; - passphrase?: string; - cert?: string | Buffer; - ca?: string | Buffer; - crl?: string | string[] - ciphers?: string; - honorCipherOrder?: boolean; - } - - export interface SecureContext { - context: any; - } - - export function createServer(options: TlsOptions, secureConnectionListener?: (cleartextStream: ClearTextStream) =>void ): Server; - export function connect(options: TlsOptions, secureConnectionListener?: () =>void ): ClearTextStream; - export function connect(port: number, host?: string, options?: ConnectionOptions, secureConnectListener?: () =>void ): ClearTextStream; - export function connect(port: number, options?: ConnectionOptions, secureConnectListener?: () =>void ): ClearTextStream; - export function createSecurePair(credentials?: crypto.Credentials, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean): SecurePair; - export function createSecureContext(details: SecureContextOptions): SecureContext; -} - -declare module "crypto" { - export interface CredentialDetails { - pfx: string; - key: string; - passphrase: string; - cert: string; - ca: string | string[]; - crl: string | string[]; - ciphers: string; - } - export interface Credentials { context?: any; } - export function createCredentials(details: CredentialDetails): Credentials; - export function createHash(algorithm: string): Hash; - export function createHmac(algorithm: string, key: string): Hmac; - export function createHmac(algorithm: string, key: Buffer): Hmac; - export interface Hash { - update(data: any, input_encoding?: string): Hash; - digest(encoding: 'buffer'): Buffer; - digest(encoding: string): any; - digest(): Buffer; - } - export interface Hmac extends NodeJS.ReadWriteStream { - update(data: any, input_encoding?: string): Hmac; - digest(encoding: 'buffer'): Buffer; - digest(encoding: string): any; - digest(): Buffer; - } - export function createCipher(algorithm: string, password: any): Cipher; - export function createCipheriv(algorithm: string, key: any, iv: any): Cipher; - export interface Cipher extends NodeJS.ReadWriteStream { - update(data: Buffer): Buffer; - update(data: string, input_encoding: "utf8"|"ascii"|"binary"): Buffer; - update(data: Buffer, input_encoding: any, output_encoding: "binary"|"base64"|"hex"): string; - update(data: string, input_encoding: "utf8"|"ascii"|"binary", output_encoding: "binary"|"base64"|"hex"): string; - final(): Buffer; - final(output_encoding: string): string; - setAutoPadding(auto_padding: boolean): void; - getAuthTag(): Buffer; - } - export function createDecipher(algorithm: string, password: any): Decipher; - export function createDecipheriv(algorithm: string, key: any, iv: any): Decipher; - export interface Decipher extends NodeJS.ReadWriteStream { - update(data: Buffer): Buffer; - update(data: string, input_encoding: "binary"|"base64"|"hex"): Buffer; - update(data: Buffer, input_encoding: any, output_encoding: "utf8"|"ascii"|"binary"): string; - update(data: string, input_encoding: "binary"|"base64"|"hex", output_encoding: "utf8"|"ascii"|"binary"): string; - final(): Buffer; - final(output_encoding: string): string; - setAutoPadding(auto_padding: boolean): void; - setAuthTag(tag: Buffer): void; - } - export function createSign(algorithm: string): Signer; - export interface Signer extends NodeJS.WritableStream { - update(data: any): void; - sign(private_key: string, output_format: string): string; - } - export function createVerify(algorith: string): Verify; - export interface Verify extends NodeJS.WritableStream { - update(data: any): void; - verify(object: string, signature: string, signature_format?: string): boolean; - } - export function createDiffieHellman(prime_length: number): DiffieHellman; - export function createDiffieHellman(prime: number, encoding?: string): DiffieHellman; - export interface DiffieHellman { - generateKeys(encoding?: string): string; - computeSecret(other_public_key: string, input_encoding?: string, output_encoding?: string): string; - getPrime(encoding?: string): string; - getGenerator(encoding: string): string; - getPublicKey(encoding?: string): string; - getPrivateKey(encoding?: string): string; - setPublicKey(public_key: string, encoding?: string): void; - setPrivateKey(public_key: string, encoding?: string): void; - } - export function getDiffieHellman(group_name: string): DiffieHellman; - export function pbkdf2(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number, callback: (err: Error, derivedKey: Buffer) => any): void; - export function pbkdf2(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number, digest: string, callback: (err: Error, derivedKey: Buffer) => any): void; - export function pbkdf2Sync(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number) : Buffer; - export function pbkdf2Sync(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number, digest: string) : Buffer; - export function randomBytes(size: number): Buffer; - export function randomBytes(size: number, callback: (err: Error, buf: Buffer) =>void ): void; - export function pseudoRandomBytes(size: number): Buffer; - export function pseudoRandomBytes(size: number, callback: (err: Error, buf: Buffer) =>void ): void; - export interface RsaPublicKey { - key: string; - padding?: any; - } - export interface RsaPrivateKey { - key: string; - passphrase?: string, - padding?: any; - } - export function publicEncrypt(public_key: string|RsaPublicKey, buffer: Buffer): Buffer - export function privateDecrypt(private_key: string|RsaPrivateKey, buffer: Buffer): Buffer -} - -declare module "stream" { - import * as events from "events"; - - export class Stream extends events.EventEmitter { - pipe(destination: T, options?: { end?: boolean; }): T; - } - - export interface ReadableOptions { - highWaterMark?: number; - encoding?: string; - objectMode?: boolean; - } - - export class Readable extends events.EventEmitter implements NodeJS.ReadableStream { - readable: boolean; - constructor(opts?: ReadableOptions); - _read(size: number): void; - read(size?: number): any; - setEncoding(encoding: string): void; - pause(): void; - resume(): void; - pipe(destination: T, options?: { end?: boolean; }): T; - unpipe(destination?: T): void; - unshift(chunk: any): void; - wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; - push(chunk: any, encoding?: string): boolean; - } - - export interface WritableOptions { - highWaterMark?: number; - decodeStrings?: boolean; - objectMode?: boolean; - } - - export class Writable extends events.EventEmitter implements NodeJS.WritableStream { - writable: boolean; - constructor(opts?: WritableOptions); - _write(chunk: any, encoding: string, callback: Function): void; - write(chunk: any, cb?: Function): boolean; - write(chunk: any, encoding?: string, cb?: Function): boolean; - end(): void; - end(chunk: any, cb?: Function): void; - end(chunk: any, encoding?: string, cb?: Function): void; - } - - export interface DuplexOptions extends ReadableOptions, WritableOptions { - allowHalfOpen?: boolean; - } - - // Note: Duplex extends both Readable and Writable. - export class Duplex extends Readable implements NodeJS.ReadWriteStream { - writable: boolean; - constructor(opts?: DuplexOptions); - _write(chunk: any, encoding: string, callback: Function): void; - write(chunk: any, cb?: Function): boolean; - write(chunk: any, encoding?: string, cb?: Function): boolean; - end(): void; - end(chunk: any, cb?: Function): void; - end(chunk: any, encoding?: string, cb?: Function): void; - } - - export interface TransformOptions extends ReadableOptions, WritableOptions {} - - // Note: Transform lacks the _read and _write methods of Readable/Writable. - export class Transform extends events.EventEmitter implements NodeJS.ReadWriteStream { - readable: boolean; - writable: boolean; - constructor(opts?: TransformOptions); - _transform(chunk: any, encoding: string, callback: Function): void; - _flush(callback: Function): void; - read(size?: number): any; - setEncoding(encoding: string): void; - pause(): void; - resume(): void; - pipe(destination: T, options?: { end?: boolean; }): T; - unpipe(destination?: T): void; - unshift(chunk: any): void; - wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; - push(chunk: any, encoding?: string): boolean; - write(chunk: any, cb?: Function): boolean; - write(chunk: any, encoding?: string, cb?: Function): boolean; - end(): void; - end(chunk: any, cb?: Function): void; - end(chunk: any, encoding?: string, cb?: Function): void; - } - - export class PassThrough extends Transform {} -} - -declare module "util" { - export interface InspectOptions { - showHidden?: boolean; - depth?: number; - colors?: boolean; - customInspect?: boolean; - } - - export function format(format: any, ...param: any[]): string; - export function debug(string: string): void; - export function error(...param: any[]): void; - export function puts(...param: any[]): void; - export function print(...param: any[]): void; - export function log(string: string): void; - export function inspect(object: any, showHidden?: boolean, depth?: number, color?: boolean): string; - export function inspect(object: any, options: InspectOptions): string; - export function isArray(object: any): boolean; - export function isRegExp(object: any): boolean; - export function isDate(object: any): boolean; - export function isError(object: any): boolean; - export function inherits(constructor: any, superConstructor: any): void; - export function debuglog(key:string): (msg:string,...param: any[])=>void; -} - -declare module "assert" { - function internal (value: any, message?: string): void; - namespace internal { - export class AssertionError implements Error { - name: string; - message: string; - actual: any; - expected: any; - operator: string; - generatedMessage: boolean; - - constructor(options?: {message?: string; actual?: any; expected?: any; - operator?: string; stackStartFunction?: Function}); - } - - export function fail(actual?: any, expected?: any, message?: string, operator?: string): void; - export function ok(value: any, message?: string): void; - export function equal(actual: any, expected: any, message?: string): void; - export function notEqual(actual: any, expected: any, message?: string): void; - export function deepEqual(actual: any, expected: any, message?: string): void; - export function notDeepEqual(acutal: any, expected: any, message?: string): void; - export function strictEqual(actual: any, expected: any, message?: string): void; - export function notStrictEqual(actual: any, expected: any, message?: string): void; - export function deepStrictEqual(actual: any, expected: any, message?: string): void; - export function notDeepStrictEqual(actual: any, expected: any, message?: string): void; - export var throws: { - (block: Function, message?: string): void; - (block: Function, error: Function, message?: string): void; - (block: Function, error: RegExp, message?: string): void; - (block: Function, error: (err: any) => boolean, message?: string): void; - }; - - export var doesNotThrow: { - (block: Function, message?: string): void; - (block: Function, error: Function, message?: string): void; - (block: Function, error: RegExp, message?: string): void; - (block: Function, error: (err: any) => boolean, message?: string): void; - }; - - export function ifError(value: any): void; - } - - export = internal; -} - -declare module "tty" { - import * as net from "net"; - - export function isatty(fd: number): boolean; - export interface ReadStream extends net.Socket { - isRaw: boolean; - setRawMode(mode: boolean): void; - isTTY: boolean; - } - export interface WriteStream extends net.Socket { - columns: number; - rows: number; - isTTY: boolean; - } -} - -declare module "domain" { - import * as events from "events"; - - export class Domain extends events.EventEmitter implements NodeJS.Domain { - run(fn: Function): void; - add(emitter: events.EventEmitter): void; - remove(emitter: events.EventEmitter): void; - bind(cb: (err: Error, data: any) => any): any; - intercept(cb: (data: any) => any): any; - dispose(): void; - } - - export function create(): Domain; -} - -declare module "constants" { - export var E2BIG: number; - export var EACCES: number; - export var EADDRINUSE: number; - export var EADDRNOTAVAIL: number; - export var EAFNOSUPPORT: number; - export var EAGAIN: number; - export var EALREADY: number; - export var EBADF: number; - export var EBADMSG: number; - export var EBUSY: number; - export var ECANCELED: number; - export var ECHILD: number; - export var ECONNABORTED: number; - export var ECONNREFUSED: number; - export var ECONNRESET: number; - export var EDEADLK: number; - export var EDESTADDRREQ: number; - export var EDOM: number; - export var EEXIST: number; - export var EFAULT: number; - export var EFBIG: number; - export var EHOSTUNREACH: number; - export var EIDRM: number; - export var EILSEQ: number; - export var EINPROGRESS: number; - export var EINTR: number; - export var EINVAL: number; - export var EIO: number; - export var EISCONN: number; - export var EISDIR: number; - export var ELOOP: number; - export var EMFILE: number; - export var EMLINK: number; - export var EMSGSIZE: number; - export var ENAMETOOLONG: number; - export var ENETDOWN: number; - export var ENETRESET: number; - export var ENETUNREACH: number; - export var ENFILE: number; - export var ENOBUFS: number; - export var ENODATA: number; - export var ENODEV: number; - export var ENOENT: number; - export var ENOEXEC: number; - export var ENOLCK: number; - export var ENOLINK: number; - export var ENOMEM: number; - export var ENOMSG: number; - export var ENOPROTOOPT: number; - export var ENOSPC: number; - export var ENOSR: number; - export var ENOSTR: number; - export var ENOSYS: number; - export var ENOTCONN: number; - export var ENOTDIR: number; - export var ENOTEMPTY: number; - export var ENOTSOCK: number; - export var ENOTSUP: number; - export var ENOTTY: number; - export var ENXIO: number; - export var EOPNOTSUPP: number; - export var EOVERFLOW: number; - export var EPERM: number; - export var EPIPE: number; - export var EPROTO: number; - export var EPROTONOSUPPORT: number; - export var EPROTOTYPE: number; - export var ERANGE: number; - export var EROFS: number; - export var ESPIPE: number; - export var ESRCH: number; - export var ETIME: number; - export var ETIMEDOUT: number; - export var ETXTBSY: number; - export var EWOULDBLOCK: number; - export var EXDEV: number; - export var WSAEINTR: number; - export var WSAEBADF: number; - export var WSAEACCES: number; - export var WSAEFAULT: number; - export var WSAEINVAL: number; - export var WSAEMFILE: number; - export var WSAEWOULDBLOCK: number; - export var WSAEINPROGRESS: number; - export var WSAEALREADY: number; - export var WSAENOTSOCK: number; - export var WSAEDESTADDRREQ: number; - export var WSAEMSGSIZE: number; - export var WSAEPROTOTYPE: number; - export var WSAENOPROTOOPT: number; - export var WSAEPROTONOSUPPORT: number; - export var WSAESOCKTNOSUPPORT: number; - export var WSAEOPNOTSUPP: number; - export var WSAEPFNOSUPPORT: number; - export var WSAEAFNOSUPPORT: number; - export var WSAEADDRINUSE: number; - export var WSAEADDRNOTAVAIL: number; - export var WSAENETDOWN: number; - export var WSAENETUNREACH: number; - export var WSAENETRESET: number; - export var WSAECONNABORTED: number; - export var WSAECONNRESET: number; - export var WSAENOBUFS: number; - export var WSAEISCONN: number; - export var WSAENOTCONN: number; - export var WSAESHUTDOWN: number; - export var WSAETOOMANYREFS: number; - export var WSAETIMEDOUT: number; - export var WSAECONNREFUSED: number; - export var WSAELOOP: number; - export var WSAENAMETOOLONG: number; - export var WSAEHOSTDOWN: number; - export var WSAEHOSTUNREACH: number; - export var WSAENOTEMPTY: number; - export var WSAEPROCLIM: number; - export var WSAEUSERS: number; - export var WSAEDQUOT: number; - export var WSAESTALE: number; - export var WSAEREMOTE: number; - export var WSASYSNOTREADY: number; - export var WSAVERNOTSUPPORTED: number; - export var WSANOTINITIALISED: number; - export var WSAEDISCON: number; - export var WSAENOMORE: number; - export var WSAECANCELLED: number; - export var WSAEINVALIDPROCTABLE: number; - export var WSAEINVALIDPROVIDER: number; - export var WSAEPROVIDERFAILEDINIT: number; - export var WSASYSCALLFAILURE: number; - export var WSASERVICE_NOT_FOUND: number; - export var WSATYPE_NOT_FOUND: number; - export var WSA_E_NO_MORE: number; - export var WSA_E_CANCELLED: number; - export var WSAEREFUSED: number; - export var SIGHUP: number; - export var SIGINT: number; - export var SIGILL: number; - export var SIGABRT: number; - export var SIGFPE: number; - export var SIGKILL: number; - export var SIGSEGV: number; - export var SIGTERM: number; - export var SIGBREAK: number; - export var SIGWINCH: number; - export var SSL_OP_ALL: number; - export var SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: number; - export var SSL_OP_CIPHER_SERVER_PREFERENCE: number; - export var SSL_OP_CISCO_ANYCONNECT: number; - export var SSL_OP_COOKIE_EXCHANGE: number; - export var SSL_OP_CRYPTOPRO_TLSEXT_BUG: number; - export var SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: number; - export var SSL_OP_EPHEMERAL_RSA: number; - export var SSL_OP_LEGACY_SERVER_CONNECT: number; - export var SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: number; - export var SSL_OP_MICROSOFT_SESS_ID_BUG: number; - export var SSL_OP_MSIE_SSLV2_RSA_PADDING: number; - export var SSL_OP_NETSCAPE_CA_DN_BUG: number; - export var SSL_OP_NETSCAPE_CHALLENGE_BUG: number; - export var SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: number; - export var SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: number; - export var SSL_OP_NO_COMPRESSION: number; - export var SSL_OP_NO_QUERY_MTU: number; - export var SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: number; - export var SSL_OP_NO_SSLv2: number; - export var SSL_OP_NO_SSLv3: number; - export var SSL_OP_NO_TICKET: number; - export var SSL_OP_NO_TLSv1: number; - export var SSL_OP_NO_TLSv1_1: number; - export var SSL_OP_NO_TLSv1_2: number; - export var SSL_OP_PKCS1_CHECK_1: number; - export var SSL_OP_PKCS1_CHECK_2: number; - export var SSL_OP_SINGLE_DH_USE: number; - export var SSL_OP_SINGLE_ECDH_USE: number; - export var SSL_OP_SSLEAY_080_CLIENT_DH_BUG: number; - export var SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: number; - export var SSL_OP_TLS_BLOCK_PADDING_BUG: number; - export var SSL_OP_TLS_D5_BUG: number; - export var SSL_OP_TLS_ROLLBACK_BUG: number; - export var ENGINE_METHOD_DSA: number; - export var ENGINE_METHOD_DH: number; - export var ENGINE_METHOD_RAND: number; - export var ENGINE_METHOD_ECDH: number; - export var ENGINE_METHOD_ECDSA: number; - export var ENGINE_METHOD_CIPHERS: number; - export var ENGINE_METHOD_DIGESTS: number; - export var ENGINE_METHOD_STORE: number; - export var ENGINE_METHOD_PKEY_METHS: number; - export var ENGINE_METHOD_PKEY_ASN1_METHS: number; - export var ENGINE_METHOD_ALL: number; - export var ENGINE_METHOD_NONE: number; - export var DH_CHECK_P_NOT_SAFE_PRIME: number; - export var DH_CHECK_P_NOT_PRIME: number; - export var DH_UNABLE_TO_CHECK_GENERATOR: number; - export var DH_NOT_SUITABLE_GENERATOR: number; - export var NPN_ENABLED: number; - export var RSA_PKCS1_PADDING: number; - export var RSA_SSLV23_PADDING: number; - export var RSA_NO_PADDING: number; - export var RSA_PKCS1_OAEP_PADDING: number; - export var RSA_X931_PADDING: number; - export var RSA_PKCS1_PSS_PADDING: number; - export var POINT_CONVERSION_COMPRESSED: number; - export var POINT_CONVERSION_UNCOMPRESSED: number; - export var POINT_CONVERSION_HYBRID: number; - export var O_RDONLY: number; - export var O_WRONLY: number; - export var O_RDWR: number; - export var S_IFMT: number; - export var S_IFREG: number; - export var S_IFDIR: number; - export var S_IFCHR: number; - export var S_IFBLK: number; - export var S_IFIFO: number; - export var S_IFSOCK: number; - export var S_IRWXU: number; - export var S_IRUSR: number; - export var S_IWUSR: number; - export var S_IXUSR: number; - export var S_IRWXG: number; - export var S_IRGRP: number; - export var S_IWGRP: number; - export var S_IXGRP: number; - export var S_IRWXO: number; - export var S_IROTH: number; - export var S_IWOTH: number; - export var S_IXOTH: number; - export var S_IFLNK: number; - export var O_CREAT: number; - export var O_EXCL: number; - export var O_NOCTTY: number; - export var O_DIRECTORY: number; - export var O_NOATIME: number; - export var O_NOFOLLOW: number; - export var O_SYNC: number; - export var O_SYMLINK: number; - export var O_DIRECT: number; - export var O_NONBLOCK: number; - export var O_TRUNC: number; - export var O_APPEND: number; - export var F_OK: number; - export var R_OK: number; - export var W_OK: number; - export var X_OK: number; - export var UV_UDP_REUSEADDR: number; -} \ No newline at end of file diff --git a/node/typings/globals/node/typings.json b/node/typings/globals/node/typings.json deleted file mode 100644 index 788506efd..000000000 --- a/node/typings/globals/node/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resolution": "main", - "tree": { - "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/77b1b1709315b03b9b1b67c589d599bebeeef2ee/node/node.d.ts", - "raw": "registry:dt/node#6.0.0+20160709114037", - "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/77b1b1709315b03b9b1b67c589d599bebeeef2ee/node/node.d.ts" - } -} diff --git a/node/typings/globals/q/index.d.ts b/node/typings/globals/q/index.d.ts deleted file mode 100644 index 4449c3184..000000000 --- a/node/typings/globals/q/index.d.ts +++ /dev/null @@ -1,357 +0,0 @@ -// Generated by typings -// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/623f30ab194a3486e014ca39bc7f2089897d6ce4/q/Q.d.ts -declare function Q(promise: Q.IPromise): Q.Promise; -/** - * If value is not a promise, returns a promise that is fulfilled with value. - */ -declare function Q(value: T): Q.Promise; - -declare namespace Q { - interface IPromise { - then(onFulfill?: (value: T) => U | IPromise, onReject?: (error: any) => U | IPromise): IPromise; - } - - interface Deferred { - promise: Promise; - resolve(value?: T): void; - resolve(value?: IPromise): void; - reject(reason: any): void; - notify(value: any): void; - makeNodeResolver(): (reason: any, value: T) => void; - } - - interface Promise { - /** - * Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object. - - * finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished. - */ - fin(finallyCallback: () => any): Promise; - /** - * Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object. - - * finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished. - */ - finally(finallyCallback: () => any): Promise; - - /** - * The then method from the Promises/A+ specification, with an additional progress handler. - */ - then(onFulfill?: (value: T) => U | IPromise, onReject?: (error: any) => U | IPromise, onProgress?: Function): Promise; - - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * - * This is especially useful in conjunction with all - */ - spread(onFulfill: (...args: any[]) => IPromise | U, onReject?: (reason: any) => IPromise | U): Promise; - - fail(onRejected: (reason: any) => U | IPromise): Promise; - - /** - * A sugar method, equivalent to promise.then(undefined, onRejected). - */ - catch(onRejected: (reason: any) => U | IPromise): Promise; - - /** - * A sugar method, equivalent to promise.then(undefined, undefined, onProgress). - */ - progress(onProgress: (progress: any) => any): Promise; - - /** - * Much like then, but with different behavior around unhandled rejection. If there is an unhandled rejection, either because promise is rejected and no onRejected callback was provided, or because onFulfilled or onRejected threw an error or returned a rejected promise, the resulting rejection reason is thrown as an exception in a future turn of the event loop. - * - * This method should be used to terminate chains of promises that will not be passed elsewhere. Since exceptions thrown in then callbacks are consumed and transformed into rejections, exceptions at the end of the chain are easy to accidentally, silently ignore. By arranging for the exception to be thrown in a future turn of the event loop, so that it won't be caught, it causes an onerror event on the browser window, or an uncaughtException event on Node.js's process object. - * - * Exceptions thrown by done will have long stack traces, if Q.longStackSupport is set to true. If Q.onerror is set, exceptions will be delivered there instead of thrown in a future turn. - * - * The Golden Rule of done vs. then usage is: either return your promise to someone else, or if the chain ends with you, call done to terminate it. - */ - done(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any, onProgress?: (progress: any) => any): void; - - /** - * If callback is a function, assumes it's a Node.js-style callback, and calls it as either callback(rejectionReason) when/if promise becomes rejected, or as callback(null, fulfillmentValue) when/if promise becomes fulfilled. If callback is not a function, simply returns promise. - */ - nodeify(callback: (reason: any, value: any) => void): Promise; - - /** - * Returns a promise to get the named property of an object. Essentially equivalent to - * - * promise.then(function (o) { - * return o[propertyName]; - * }); - */ - get(propertyName: String): Promise; - set(propertyName: String, value: any): Promise; - delete(propertyName: String): Promise; - /** - * Returns a promise for the result of calling the named method of an object with the given array of arguments. The object itself is this in the function, just like a synchronous method call. Essentially equivalent to - * - * promise.then(function (o) { - * return o[methodName].apply(o, args); - * }); - */ - post(methodName: String, args: any[]): Promise; - /** - * Returns a promise for the result of calling the named method of an object with the given variadic arguments. The object itself is this in the function, just like a synchronous method call. - */ - invoke(methodName: String, ...args: any[]): Promise; - fapply(args: any[]): Promise; - fcall(...args: any[]): Promise; - - /** - * Returns a promise for an array of the property names of an object. Essentially equivalent to - * - * promise.then(function (o) { - * return Object.keys(o); - * }); - */ - keys(): Promise; - - /** - * A sugar method, equivalent to promise.then(function () { return value; }). - */ - thenResolve(value: U): Promise; - /** - * A sugar method, equivalent to promise.then(function () { throw reason; }). - */ - thenReject(reason: any): Promise; - - /** - * Attaches a handler that will observe the value of the promise when it becomes fulfilled, returning a promise for that same value, perhaps deferred but not replaced by the promise returned by the onFulfilled handler. - */ - tap(onFulfilled: (value: T) => any): Promise; - - timeout(ms: number, message?: string): Promise; - /** - * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. - */ - delay(ms: number): Promise; - - /** - * Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true. - */ - isFulfilled(): boolean; - /** - * Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false. - */ - isRejected(): boolean; - /** - * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. - */ - isPending(): boolean; - - valueOf(): any; - - /** - * Returns a "state snapshot" object, which will be in one of three forms: - * - * - { state: "pending" } - * - { state: "fulfilled", value: } - * - { state: "rejected", reason: } - */ - inspect(): PromiseState; - } - - interface PromiseState { - /** - * "fulfilled", "rejected", "pending" - */ - state: string; - value?: T; - reason?: any; - } - - // If no value provided, returned promise will be of void type - export function when(): Promise; - - // if no fulfill, reject, or progress provided, returned promise will be of same type - export function when(value: T | IPromise): Promise; - - // If a non-promise value is provided, it will not reject or progress - export function when(value: T | IPromise, onFulfilled: (val: T) => U | IPromise, onRejected?: (reason: any) => U | IPromise, onProgress?: (progress: any) => any): Promise; - - /** - * Currently "impossible" (and I use the term loosely) to implement due to TypeScript limitations as it is now. - * See: https://github.com/Microsoft/TypeScript/issues/1784 for discussion on it. - */ - // export function try(method: Function, ...args: any[]): Promise; - - export function fbind(method: (...args: any[]) => T | IPromise, ...args: any[]): (...args: any[]) => Promise; - - export function fcall(method: (...args: any[]) => T, ...args: any[]): Promise; - - export function send(obj: any, functionName: string, ...args: any[]): Promise; - export function invoke(obj: any, functionName: string, ...args: any[]): Promise; - export function mcall(obj: any, functionName: string, ...args: any[]): Promise; - - export function denodeify(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise; - export function nbind(nodeFunction: Function, thisArg: any, ...args: any[]): (...args: any[]) => Promise; - export function nfbind(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise; - export function nfcall(nodeFunction: Function, ...args: any[]): Promise; - export function nfapply(nodeFunction: Function, args: any[]): Promise; - - export function ninvoke(nodeModule: any, functionName: string, ...args: any[]): Promise; - export function npost(nodeModule: any, functionName: string, args: any[]): Promise; - export function nsend(nodeModule: any, functionName: string, ...args: any[]): Promise; - export function nmcall(nodeModule: any, functionName: string, ...args: any[]): Promise; - - /** - * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. - */ - export function all(promises: [IPromise, IPromise, IPromise, IPromise, IPromise, IPromise]): Promise<[A, B, C, D, E, F]>; - /** - * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. - */ - export function all(promises: [IPromise, IPromise, IPromise, IPromise, IPromise]): Promise<[A, B, C, D, E]>; - /** - * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. - */ - export function all(promises: [IPromise, IPromise, IPromise, IPromise]): Promise<[A, B, C, D]>; - /** - * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. - */ - export function all(promises: [IPromise, IPromise, IPromise]): Promise<[A, B, C]>; - /** - * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. - */ - export function all(promises: [IPromise, IPromise]): Promise<[A, B]>; - /** - * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. - */ - export function all(promises: IPromise[]): Promise; - - /** - * Returns a promise for the first of an array of promises to become settled. - */ - export function race(promises: IPromise[]): Promise; - - /** - * Returns a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled, i.e. become either fulfilled or rejected. - */ - export function allSettled(promises: IPromise[]): Promise[]>; - - export function allResolved(promises: IPromise[]): Promise[]>; - - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * This is especially useful in conjunction with all. - */ - export function spread(promises: IPromise[], onFulfilled: (...args: T[]) => U | IPromise, onRejected?: (reason: any) => U | IPromise): Promise; - - /** - * Returns a promise that will have the same result as promise, except that if promise is not fulfilled or rejected before ms milliseconds, the returned promise will be rejected with an Error with the given message. If message is not supplied, the message will be "Timed out after " + ms + " ms". - */ - export function timeout(promise: Promise, ms: number, message?: string): Promise; - - /** - * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. - */ - export function delay(promise: Promise, ms: number): Promise; - /** - * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. - */ - export function delay(value: T, ms: number): Promise; - /** - * Returns a promise that will be fulfilled with undefined after at least ms milliseconds have passed. - */ - export function delay(ms: number): Promise ; - /** - * Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true. - */ - export function isFulfilled(promise: Promise): boolean; - /** - * Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false. - */ - export function isRejected(promise: Promise): boolean; - /** - * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. - */ - export function isPending(promise: Promise): boolean; - - /** - * Returns a "deferred" object with a: - * promise property - * resolve(value) method - * reject(reason) method - * notify(value) method - * makeNodeResolver() method - */ - export function defer(): Deferred; - - /** - * Returns a promise that is rejected with reason. - */ - export function reject(reason?: any): Promise; - - export function Promise(resolver: (resolve: (val: T | IPromise) => void , reject: (reason: any) => void , notify: (progress: any) => void ) => void ): Promise; - - /** - * Creates a new version of func that accepts any combination of promise and non-promise values, converting them to their fulfillment values before calling the original func. The returned version also always returns a promise: if func does a return or throw, then Q.promised(func) will return fulfilled or rejected promise, respectively. - * - * This can be useful for creating functions that accept either promises or non-promise values, and for ensuring that the function always returns a promise even in the face of unintentional thrown exceptions. - */ - export function promised(callback: (...args: any[]) => T): (...args: any[]) => Promise; - - /** - * Returns whether the given value is a Q promise. - */ - export function isPromise(object: any): boolean; - /** - * Returns whether the given value is a promise (i.e. it's an object with a then function). - */ - export function isPromiseAlike(object: any): boolean; - /** - * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. - */ - export function isPending(object: any): boolean; - /** - * If an object is not a promise, it is as "near" as possible. - * If a promise is rejected, it is as "near" as possible too. - * If it’s a fulfilled promise, the fulfillment value is nearer. - * If it’s a deferred promise and the deferred has been resolved, the - * resolution is "nearer". - */ - export function nearer(promise: Promise): T; - - /** - * This is an experimental tool for converting a generator function into a deferred function. This has the potential of reducing nested callbacks in engines that support yield. - */ - export function async(generatorFunction: any): (...args: any[]) => Promise; - export function nextTick(callback: Function): void; - - /** - * A settable property that will intercept any uncaught errors that would otherwise be thrown in the next tick of the event loop, usually as a result of done. Can be useful for getting the full stack trace of an error in browsers, which is not usually possible with window.onerror. - */ - export var onerror: (reason: any) => void; - /** - * A settable property that lets you turn on long stack trace support. If turned on, "stack jumps" will be tracked across asynchronous promise operations, so that if an uncaught error is thrown by done or a rejection reason's stack property is inspected in a rejection callback, a long stack trace is produced. - */ - export var longStackSupport: boolean; - - /** - * Calling resolve with a pending promise causes promise to wait on the passed promise, becoming fulfilled with its fulfillment value or rejected with its rejection reason (or staying pending forever, if the passed promise does). - * Calling resolve with a rejected promise causes promise to be rejected with the passed promise's rejection reason. - * Calling resolve with a fulfilled promise causes promise to be fulfilled with the passed promise's fulfillment value. - * Calling resolve with a non-promise value causes promise to be fulfilled with that value. - */ - export function resolve(object: IPromise): Promise; - /** - * Calling resolve with a pending promise causes promise to wait on the passed promise, becoming fulfilled with its fulfillment value or rejected with its rejection reason (or staying pending forever, if the passed promise does). - * Calling resolve with a rejected promise causes promise to be rejected with the passed promise's rejection reason. - * Calling resolve with a fulfilled promise causes promise to be fulfilled with the passed promise's fulfillment value. - * Calling resolve with a non-promise value causes promise to be fulfilled with that value. - */ - export function resolve(object: T): Promise; - - /** - * Resets the global "Q" variable to the value it has before Q was loaded. - * This will either be undefined if there was no version or the version of Q which was already loaded before. - * @returns { The last version of Q. } - */ - export function noConflict(): typeof Q; -} - -declare module "q" { - export = Q; -} \ No newline at end of file diff --git a/node/typings/globals/q/typings.json b/node/typings/globals/q/typings.json deleted file mode 100644 index 3d59355a8..000000000 --- a/node/typings/globals/q/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resolution": "main", - "tree": { - "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/623f30ab194a3486e014ca39bc7f2089897d6ce4/q/Q.d.ts", - "raw": "registry:dt/q#0.0.0+20160613154756", - "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/623f30ab194a3486e014ca39bc7f2089897d6ce4/q/Q.d.ts" - } -} diff --git a/node/typings/globals/semver/index.d.ts b/node/typings/globals/semver/index.d.ts deleted file mode 100644 index d645d8199..000000000 --- a/node/typings/globals/semver/index.d.ts +++ /dev/null @@ -1,175 +0,0 @@ -// Generated by typings -// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/62da6b4c18258e2ff2b306036a70ac88eec4743e/semver/semver.d.ts -declare namespace SemVerModule { - /** - * Return the parsed version, or null if it's not valid. - */ - function valid(v: string, loose?: boolean): string; - /** - * Returns cleaned (removed leading/trailing whitespace, remove '=v' prefix) and parsed version, or null if version is invalid. - */ - function clean(version: string, loose?: boolean): string; - /** - * Return the version incremented by the release type (major, minor, patch, or prerelease), or null if it's not valid. - */ - function inc(v: string, release: string, loose?: boolean): string; - /** - * Return the major version number. - */ - function major(v: string, loose?: boolean): number; - /** - * Return the minor version number. - */ - function minor(v: string, loose?: boolean): number; - /** - * Return the patch version number. - */ - function patch(v: string, loose?: boolean): number; - - // Comparison - /** - * v1 > v2 - */ - function gt(v1: string, v2: string, loose?: boolean): boolean; - /** - * v1 >= v2 - */ - function gte(v1: string, v2: string, loose?: boolean): boolean; - /** - * v1 < v2 - */ - function lt(v1: string, v2: string, loose?: boolean): boolean; - /** - * v1 <= v2 - */ - function lte(v1: string, v2: string, loose?: boolean): boolean; - /** - * v1 == v2 This is true if they're logically equivalent, even if they're not the exact same string. You already know how to compare strings. - */ - function eq(v1: string, v2: string, loose?: boolean): boolean; - /** - * v1 != v2 The opposite of eq. - */ - function neq(v1: string, v2: string, loose?: boolean): boolean; - /** - * Pass in a comparison string, and it'll call the corresponding semver comparison function. "===" and "!==" do simple string comparison, but are included for completeness. Throws if an invalid comparison string is provided. - */ - function cmp(v1: string, comparator: any, v2: string, loose?: boolean): boolean; - /** - * Return 0 if v1 == v2, or 1 if v1 is greater, or -1 if v2 is greater. Sorts in ascending order if passed to Array.sort(). - */ - function compare(v1: string, v2: string, loose?: boolean): number; - /** - * The reverse of compare. Sorts an array of versions in descending order when passed to Array.sort(). - */ - function rcompare(v1: string, v2: string, loose?: boolean): number; - /** - * Returns difference between two versions by the release type (major, premajor, minor, preminor, patch, prepatch, or prerelease), or null if the versions are the same. - */ - function diff(v1: string, v2: string, loose?: boolean): string; - - // Ranges - /** - * Return the valid range or null if it's not valid - */ - function validRange(range: string, loose?: boolean): string; - /** - * Return true if the version satisfies the range. - */ - function satisfies(version: string, range: string, loose?: boolean): boolean; - /** - * Return the highest version in the list that satisfies the range, or null if none of them do. - */ - function maxSatisfying(versions: string[], range: string, loose?: boolean): string; - /** - * Return true if version is greater than all the versions possible in the range. - */ - function gtr(version: string, range: string, loose?: boolean): boolean; - /** - * Return true if version is less than all the versions possible in the range. - */ - function ltr(version: string, range: string, loose?: boolean): boolean; - /** - * Return true if the version is outside the bounds of the range in either the high or low direction. The hilo argument must be either the string '>' or '<'. (This is the function called by gtr and ltr.) - */ - function outside(version: string, range: string, hilo: string, loose?: boolean): boolean; - - class SemVerBase { - raw: string; - loose: boolean; - format(): string; - inspect(): string; - toString(): string; - } - - class SemVer extends SemVerBase { - constructor(version: string, loose?: boolean); - - major: number; - minor: number; - patch: number; - version: string; - build: string[]; - prerelease: string[]; - - compare(other:SemVer): number; - compareMain(other:SemVer): number; - comparePre(other:SemVer): number; - inc(release: string): SemVer; - } - - class Comparator extends SemVerBase { - constructor(comp: string, loose?: boolean); - - semver: SemVer; - operator: string; - value: boolean; - parse(comp: string): void; - test(version:SemVer): boolean; - } - - class Range extends SemVerBase { - constructor(range: string, loose?: boolean); - - set: Comparator[][]; - parseRange(range: string): Comparator[]; - test(version: SemVer): boolean; - } -} - -interface SemVerStatic { - SemVer(version: string, loose?: boolean): SemVerModule.SemVer; - Comparator(comp: string, loose?: boolean): SemVerModule.Comparator; - Range(range: string, loose?: boolean): SemVerModule.Range; - clean(version: string, loose?: boolean): string; - - SEMVER_SPEC_VERSION: string; - - valid(v: string, loose?: boolean): string; - inc(v: string, release: string, loose?: boolean): string; - major(v: string, loose?: boolean): number; - minor(v: string, loose?: boolean): number; - patch(v: string, loose?: boolean): number; - gt(v1: string, v2: string, loose?: boolean): boolean; - gte(v1: string, v2: string, loose?: boolean): boolean; - lt(v1: string, v2: string, loose?: boolean): boolean; - lte(v1: string, v2: string, loose?: boolean): boolean; - eq(v1: string, v2: string, loose?: boolean): boolean; - neq(v1: string, v2: string, loose?: boolean): boolean; - cmp(v1: string, comparator: any, v2: string, loose?: boolean): boolean; - compare(v1: string, v2: string, loose?: boolean): number; - rcompare(v1: string, v2: string, loose?: boolean): number; - diff(v1: string, v2: string, loose?: boolean): string; - validRange(range: string, loose?: boolean): string; - satisfies(version: string, range: string, loose?: boolean): boolean; - maxSatisfying(versions: string[], range: string, loose?: boolean): string; - gtr(version: string, range: string, loose?: boolean): boolean; - ltr(version: string, range: string, loose?: boolean): boolean; - outside(version: string, range: string, hilo: string, loose?: boolean): boolean; -} - -declare var semver: SemVerStatic; - -declare module "semver" { - export = SemVerModule; -} \ No newline at end of file diff --git a/node/typings/globals/semver/typings.json b/node/typings/globals/semver/typings.json deleted file mode 100644 index 23598e09f..000000000 --- a/node/typings/globals/semver/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resolution": "main", - "tree": { - "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/62da6b4c18258e2ff2b306036a70ac88eec4743e/semver/semver.d.ts", - "raw": "registry:dt/semver#4.3.4+20160608054219", - "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/62da6b4c18258e2ff2b306036a70ac88eec4743e/semver/semver.d.ts" - } -} diff --git a/node/typings/globals/shelljs/index.d.ts b/node/typings/globals/shelljs/index.d.ts deleted file mode 100644 index da6da179f..000000000 --- a/node/typings/globals/shelljs/index.d.ts +++ /dev/null @@ -1,531 +0,0 @@ -// Generated by typings -// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/ae98aa7dd3b0bac69144340c7771be4aca2c4522/shelljs/shelljs.d.ts -declare module "shelljs" -{ - import child = require("child_process"); - - /** - * Changes to directory dir for the duration of the script - * @param {string} dir Directory to change in. - */ - export function cd(dir: string): void; - - /** - * Returns the current directory. - * @return {string} The current directory. - */ - export function pwd(): string; - - /** - * Returns array of files in the given path, or in current directory if no path provided. - * @param {string[]} ...paths Paths to search. - * @return {string[]} An array of files in the given path(s). - */ - export function ls(...paths: string[]): string[]; - - /** - * Returns array of files in the given path, or in current directory if no path provided. - * @param {string} options Available options: -R (recursive), -A (all files, include files beginning with ., except for . and ..) - * @param {string[]} ...paths Paths to search. - * @return {string[]} An array of files in the given path(s). - */ - export function ls(options: string, ...paths: string[]): string[]; - - /** - * Returns array of files in the given path, or in current directory if no path provided. - * @param {string[]} paths Paths to search. - * @return {string[]} An array of files in the given path(s). - */ - export function ls(paths: string[]): string[]; - - /** - * Returns array of files in the given path, or in current directory if no path provided. - * @param {string} options Available options: -R (recursive), -A (all files, include files beginning with ., except for . and ..) - * @param {string[]} paths Paths to search. - * @return {string[]} An array of files in the given path(s). - */ - export function ls(options: string, paths: string[]): string[]; - - /** - * Returns array of all files (however deep) in the given paths. - * @param {string[]} ...path The path(s) to search. - * @return {string[]} An array of all files (however deep) in the given path(s). - */ - export function find(...path: string[]): string[]; - - /** - * Returns array of all files (however deep) in the given paths. - * @param {string[]} path The path(s) to search. - * @return {string[]} An array of all files (however deep) in the given path(s). - */ - export function find(path: string[]): string[]; - - /** - * Copies files. The wildcard * is accepted. - * @param {string} source The source. - * @param {string} dest The destination. - */ - export function cp(source: string, dest: string): void; - - /** - * Copies files. The wildcard * is accepted. - * @param {string[]} source The source. - * @param {string} dest The destination. - */ - export function cp(source: string[], dest: string): void; - - /** - * Copies files. The wildcard * is accepted. - * @param {string} options Available options: -f (force), -r, -R (recursive) - * @param {strin]} source The source. - * @param {string} dest The destination. - */ - export function cp(options: string, source: string, dest: string): void; - - /** - * Copies files. The wildcard * is accepted. - * @param {string} options Available options: -f (force), -r, -R (recursive) - * @param {string[]} source The source. - * @param {string} dest The destination. - */ - export function cp(options: string, source: string[], dest: string): void; - - /** - * Removes files. The wildcard * is accepted. - * @param {string[]} ...files Files to remove. - */ - export function rm(...files: string[]): void; - - /** - * Removes files. The wildcard * is accepted. - * @param {string[]} files Files to remove. - */ - export function rm(files: string[]): void; - - /** - * Removes files. The wildcard * is accepted. - * @param {string} options Available options: -f (force), -r, -R (recursive) - * @param {string[]} ...files Files to remove. - */ - export function rm(options: string, ...files: string[]): void; - - /** - * Removes files. The wildcard * is accepted. - * @param {string} options Available options: -f (force), -r, -R (recursive) - * @param {string[]} ...files Files to remove. - */ - export function rm(options: string, files: string[]): void; - - /** - * Moves files. The wildcard * is accepted. - * @param {string} source The source. - * @param {string} dest The destination. - */ - export function mv(options: string, source: string, dest: string): void; - export function mv(source: string, dest: string): void; - - /** - * Moves files. The wildcard * is accepted. - * @param {string[]} source The source. - * @param {string} dest The destination. - */ - export function mv(source: string[], dest: string): void; - - /** - * Creates directories. - * @param {string[]} ...dir Directories to create. - */ - export function mkdir(...dir: string[]): void; - - /** - * Creates directories. - * @param {string[]} dir Directories to create. - */ - export function mkdir(dir: string[]): void; - - /** - * Creates directories. - * @param {string} options Available options: p (full paths, will create intermediate dirs if necessary) - * @param {string[]} ...dir The directories to create. - */ - export function mkdir(options: string, ...dir: string[]): void; - - /** - * Creates directories. - * @param {string} options Available options: p (full paths, will create intermediate dirs if necessary) - * @param {string[]} dir The directories to create. - */ - export function mkdir(options: string, dir: string[]): void; - - /** - * Evaluates expression using the available primaries and returns corresponding value. - * @param {string} option '-b': true if path is a block device; '-c': true if path is a character device; '-d': true if path is a directory; '-e': true if path exists; '-f': true if path is a regular file; '-L': true if path is a symboilc link; '-p': true if path is a pipe (FIFO); '-S': true if path is a socket - * @param {string} path The path. - * @return {boolean} See option parameter. - */ - export function test(option: string, path: string): boolean; - - /** - * Returns a string containing the given file, or a concatenated string containing the files if more than one file is given (a new line character is introduced between each file). Wildcard * accepted. - * @param {string[]} ...files Files to use. - * @return {string} A string containing the given file, or a concatenated string containing the files if more than one file is given (a new line character is introduced between each file). - */ - export function cat(...files: string[]): string; - - /** - * Returns a string containing the given file, or a concatenated string containing the files if more than one file is given (a new line character is introduced between each file). Wildcard * accepted. - * @param {string[]} files Files to use. - * @return {string} A string containing the given file, or a concatenated string containing the files if more than one file is given (a new line character is introduced between each file). - */ - export function cat(files: string[]): string; - - - // Does not work yet. - export interface String - { - /** - * Analogous to the redirection operator > in Unix, but works with JavaScript strings (such as those returned by cat, grep, etc). Like Unix redirections, to() will overwrite any existing file! - * @param {string} file The file to use. - */ - to(file: string): void; - - /** - * Analogous to the redirect-and-append operator >> in Unix, but works with JavaScript strings (such as those returned by cat, grep, etc). - * @param {string} file The file to append to. - */ - toEnd(file: string): void; - } - - /** - * Reads an input string from file and performs a JavaScript replace() on the input using the given search regex and replacement string or function. Returns the new string after replacement. - * @param {RegExp} searchRegex The regular expression to use for search. - * @param {string} replacement The replacement. - * @param {string} file The file to process. - * @return {string} The new string after replacement. - */ - export function sed(searchRegex: RegExp, replacement: string, file: string): string; - - /** - * Reads an input string from file and performs a JavaScript replace() on the input using the given search regex and replacement string or function. Returns the new string after replacement. - * @param {string} searchRegex The regular expression to use for search. - * @param {string} replacement The replacement. - * @param {string} file The file to process. - * @return {string} The new string after replacement. - */ - export function sed(searchRegex: string, replacement: string, file: string): string; - - /** - * Reads an input string from file and performs a JavaScript replace() on the input using the given search regex and replacement string or function. Returns the new string after replacement. - * @param {string} options Available options: -i (Replace contents of 'file' in-place. Note that no backups will be created!) - * @param {RegExp} searchRegex The regular expression to use for search. - * @param {string} replacement The replacement. - * @param {string} file The file to process. - * @return {string} The new string after replacement. - */ - export function sed(options: string, searchRegex: RegExp, replacement: string, file: string): string; - - /** - * Reads an input string from file and performs a JavaScript replace() on the input using the given search regex and replacement string or function. Returns the new string after replacement. - * @param {string} options Available options: -i (Replace contents of 'file' in-place. Note that no backups will be created!) - * @param {string} searchRegex The regular expression to use for search. - * @param {string} replacement The replacement. - * @param {string} file The file to process. - * @return {string} The new string after replacement. - */ - export function sed(options: string, searchRegex: string, replacement: string, file: string): string; - - /** - * Reads input string from given files and returns a string containing all lines of the file that match the given regex_filter. Wildcard * accepted. - * @param {RegExp} regex_filter The regular expression to use. - * @param {string[]} ...files The files to process. - * @return {string} Returns a string containing all lines of the file that match the given regex_filter. - */ - export function grep(regex_filter: RegExp, ...files: string[]): string; - - /** - * Reads input string from given files and returns a string containing all lines of the file that match the given regex_filter. Wildcard * accepted. - * @param {RegExp} regex_filter The regular expression to use. - * @param {string[]} ...files The files to process. - * @return {string} Returns a string containing all lines of the file that match the given regex_filter. - */ - export function grep(regex_filter: RegExp, files: string[]): string; - - /** - * Reads input string from given files and returns a string containing all lines of the file that match the given regex_filter. Wildcard * accepted. - * @param {string} options Available options: -v (Inverse the sense of the regex and print the lines not matching the criteria.) - * @param {string} regex_filter The regular expression to use. - * @param {string[]} ...files The files to process. - * @return {string} Returns a string containing all lines of the file that match the given regex_filter. - */ - export function grep(options: string, regex_filter: string, ...files: string[]): string; - - /** - * Reads input string from given files and returns a string containing all lines of the file that match the given regex_filter. Wildcard * accepted. - * @param {string} options Available options: -v (Inverse the sense of the regex and print the lines not matching the criteria.) - * @param {string} regex_filter The regular expression to use. - * @param {string[]} files The files to process. - * @return {string} Returns a string containing all lines of the file that match the given regex_filter. - */ - export function grep(options: string, regex_filter: string, files: string[]): string; - - /** - * Searches for command in the system's PATH. On Windows looks for .exe, .cmd, and .bat extensions. - * @param {string} command The command to search for. - * @return {string} Returns string containing the absolute path to the command. - */ - export function which(command: string): string; - - /** - * Prints string to stdout, and returns string with additional utility methods like .to(). - * @param {string[]} ...text The text to print. - * @return {string} Returns the string that was passed as argument. - */ - export function echo(...text: string[]): string; - - /** - * Save the current directory on the top of the directory stack and then cd to dir. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. - * @param {"+N"} dir Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. - * @return {string[]} Returns an array of paths in the stack. - */ - export function pushd(dir: "+N"): string[]; - - /** - * Save the current directory on the top of the directory stack and then cd to dir. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. - * @param {"-N"} dir Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. - * @return {string[]} Returns an array of paths in the stack. - */ - export function pushd(dir: "-N"): string[]; - - /** - * Save the current directory on the top of the directory stack and then cd to dir. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. - * @param {string} dir Makes the current working directory be the top of the stack, and then executes the equivalent of cd dir. - * @return {string[]} Returns an array of paths in the stack. - */ - export function pushd(dir: string): string[]; - - /** - * Save the current directory on the top of the directory stack and then cd to dir. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. - * @param {string} options Available options: -n (Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated) - * @param {"+N"} dir Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. - * @return {string[]} Returns an array of paths in the stack. - */ - export function pushd(options: string, dir: "+N"): string[]; - - /** - * Save the current directory on the top of the directory stack and then cd to dir. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. - * @param {string} options Available options: -n (Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated) - * @param {"-N"} dir Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. - * @return {string[]} Returns an array of paths in the stack. - */ - export function pushd(options: string, dir: "-N"): string[]; - - /** - * Save the current directory on the top of the directory stack and then cd to dir. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. - * @param {string} options Available options: -n (Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated) - * @param {string} dir Makes the current working directory be the top of the stack, and then executes the equivalent of cd dir. - * @return {string[]} Returns an array of paths in the stack. - */ - export function pushd(options: string, dir: string): string[]; - - /** - * When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. - * @param {"+N"} dir Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero. - * @return {string[]} Returns an array of paths in the stack. - */ - export function popd(dir: "+N"): string[]; - - /** - * When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. - * @return {string[]} Returns an array of paths in the stack. - */ - export function popd(): string[]; - - /** - * When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. - * @param {"-N"} dir Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero. - * @return {string[]} Returns an array of paths in the stack. - */ - export function popd(dir: "-N"): string[]; - - /** - * When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. - * @param {string} dir You can only use -N and +N. - * @return {string[]} Returns an array of paths in the stack. - */ - export function popd(dir: string): string[]; - - /** - * When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. - * @param {string} options Available options: -n (Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated) - * @param {"+N"} dir Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero. - * @return {string[]} Returns an array of paths in the stack. - */ - export function popd(options: string, dir: "+N"): string[]; - - /** - * When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. - * @param {string} options Available options: -n (Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated) - * @param {"-N"} dir Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero. - * @return {string[]} Returns an array of paths in the stack. - */ - export function popd(options: string, dir: "-N"): string[]; - - /** - * When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. - * @param {string} options Available options: -n (Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated) - * @param {string} dir You can only use -N and +N. - * @return {string[]} Returns an array of paths in the stack. - */ - export function popd(options: string, dir: string): string[]; - - /** - * Clears the directory stack by deleting all of the elements. - * @param {"-c"} options Clears the directory stack by deleting all of the elements. - * @return {string[]} Returns an array of paths in the stack, or a single path if +N or -N was specified. - */ - export function dirs(options: "-c"): string[]; - - /** - * Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. - * @param {"+N"} options Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero. - * @return {string[]} Returns an array of paths in the stack, or a single path if +N or -N was specified. - */ - export function dirs(options: "+N"): string; - - /** - * Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. - * @param {"-N"} options Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero. - * @return {string[]} Returns an array of paths in the stack, or a single path if +N or -N was specified. - */ - export function dirs(options: "-N"): string; - - /** - * Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. - * @param {string} options Available options: -c, -N, +N. You can only use those. - * @return {any} Returns an array of paths in the stack, or a single path if +N or -N was specified. - */ - export function dirs(options: string): any; - - /** - * Links source to dest. Use -f to force the link, should dest already exist. - * @param {string} source The source. - * @param {string} dest The destination. - */ - export function ln(source: string, dest: string): void; - - /** - * Links source to dest. Use -f to force the link, should dest already exist. - * @param {string} options Available options: s (symlink), f (force) - * @param {string} source The source. - * @param {string} dest The destination. - */ - export function ln(options: string, source: string, dest: string): void; - - /** - * Exits the current process with the given exit code. - * @param {number} code The exit code. - */ - export function exit(code: number): void; - - /** - * Object containing environment variables (both getter and setter). Shortcut to process.env. - */ - export var env: { [key: string]: string }; - - /** - * Executes the given command synchronously. - * @param {string} command The command to execute. - * @return {ExecOutputReturnValue} Returns an object containing the return code and output as string. - */ - export function exec(command: string): ExecOutputReturnValue; - /** - * Executes the given command synchronously. - * @param {string} command The command to execute. - * @param {ExecOptions} options Silence and synchronous options. - * @return {ExecOutputReturnValue | child.ChildProcess} Returns an object containing the return code and output as string, or if {async:true} was passed, a ChildProcess. - */ - export function exec(command: string, options: ExecOptions): ExecOutputReturnValue | child.ChildProcess; - /** - * Executes the given command synchronously. - * @param {string} command The command to execute. - * @param {ExecOptions} options Silence and synchronous options. - * @param {ExecCallback} callback Receives code and output asynchronously. - */ - export function exec(command: string, options: ExecOptions, callback: ExecCallback): child.ChildProcess; - /** - * Executes the given command synchronously. - * @param {string} command The command to execute. - * @param {ExecCallback} callback Receives code and output asynchronously. - */ - export function exec(command: string, callback: ExecCallback): child.ChildProcess; - - export interface ExecCallback { - (code: number, output: string, error?: string): any; - } - - export interface ExecOptions { - silent?: boolean; - async?: boolean; - } - - export interface ExecOutputReturnValue - { - code: number; - output: string; - } - - /** - * Alters the permissions of a file or directory by either specifying the absolute permissions in octal form or expressing the changes in symbols. This command tries to mimic the POSIX behavior as much as possible. Notable exceptions: - * - In symbolic modes, 'a-r' and '-r' are identical. No consideration is given to the umask. - * - There is no "quiet" option since default behavior is to run silent. - * @param {number} octalMode The access mode. Octal. - * @param {string} file The file to use. - */ - export function chmod(octalMode: number, file: string): void; - - /** - * Alters the permissions of a file or directory by either specifying the absolute permissions in octal form or expressing the changes in symbols. This command tries to mimic the POSIX behavior as much as possible. Notable exceptions: - * - In symbolic modes, 'a-r' and '-r' are identical. No consideration is given to the umask. - * - There is no "quiet" option since default behavior is to run silent. - * @param {string} mode The access mode. Can be an octal string or a symbolic mode string. - * @param {string} file The file to use. - */ - export function chmod(mode: string, file: string): void; - - // Non-Unix commands - - /** - * Searches and returns string containing a writeable, platform-dependent temporary directory. Follows Python's tempfile algorithm. - * @return {string} The temp file path. - */ - export function tempdir(): string; - - /** - * Tests if error occurred in the last command. - * @return {string} Returns null if no error occurred, otherwise returns string explaining the error - */ - export function error(): string; - - // Configuration - - interface ShellConfig - { - /** - * Suppresses all command output if true, except for echo() calls. Default is false. - * @type {boolean} - */ - silent: boolean; - - /** - * If true the script will die on errors. Default is false. - * @type {boolean} - */ - fatal: boolean; - } - - /** - * The shelljs configuration. - * @type {ShellConfig} - */ - export var config: ShellConfig; -} \ No newline at end of file diff --git a/node/typings/globals/shelljs/typings.json b/node/typings/globals/shelljs/typings.json deleted file mode 100644 index 9db4a17b7..000000000 --- a/node/typings/globals/shelljs/typings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "resolution": "main", - "tree": { - "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/ae98aa7dd3b0bac69144340c7771be4aca2c4522/shelljs/shelljs.d.ts", - "raw": "registry:dt/shelljs#0.0.0+20160526131156", - "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/ae98aa7dd3b0bac69144340c7771be4aca2c4522/shelljs/shelljs.d.ts" - } -} diff --git a/node/typings/index.d.ts b/node/typings/index.d.ts deleted file mode 100644 index 7fc4fdbc0..000000000 --- a/node/typings/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/// -/// -/// -/// -/// -/// -/// -/// diff --git a/res/UseNode5.ps1 b/res/UseNode5.ps1 deleted file mode 100644 index 9cae99f4a..000000000 --- a/res/UseNode5.ps1 +++ /dev/null @@ -1,18 +0,0 @@ -$ErrorActionPreference='Stop' -$m=[version]'2.115.0' -if (([version]$env:AGENT_VERSION) -lt $m) { throw "Min agent $m" } -$v='5.10.1' -$d="$env:AGENT_TOOLSDIRECTORY\node\$v\x64" -$c="$d.complete" -$u="https://nodejs.org/dist/v5.10.1/win-x64/node" -if (!(Test-Path $c)) { - "rm $d" - ri $d -rec -for -ea 0 - md $d - "downloading" - $w=New-Object System.Net.WebClient - $w.DownloadFile("$u.exe", "$d\node.exe") - $w.DownloadFile("$u.lib", "$d\node.lib") - New-Item $c -Type File -} -"##vso[task.prependpath]$d" From 4847986f752d457e9652d897ccc8e3ee0e68f4e1 Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Wed, 10 Mar 2021 14:45:48 +0300 Subject: [PATCH 196/259] Added PR opening script (#722) --- open-pullrequest.ps1 | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 open-pullrequest.ps1 diff --git a/open-pullrequest.ps1 b/open-pullrequest.ps1 new file mode 100644 index 000000000..25afaa84d --- /dev/null +++ b/open-pullrequest.ps1 @@ -0,0 +1,24 @@ +param( + [Parameter(Mandatory)] + [string] + $SourceBranch +) + +function Get-PullRequest() { + $prInfo = (gh api -X GET repos/:owner/:repo/pulls -F head=":owner:$SourceBranch" -f state=open -f base=master | ConvertFrom-Json) + return $prInfo.html_url +} + +$openedPR = Get-PullRequest + +if ($openedPR.length -ne 0) { + throw "A PR from $SourceBranch to master already exists." +} + +$buildUrl = "$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI$env:SYSTEM_TEAMPROJECT/_build/results?buildId=$env:BUILD_BUILDID&_a=summary" +$body = "This PR was auto-generated with [the localization pipeline build]($buildUrl)." + +gh pr create --head $SourceBranch --title 'Localization update' --body $body + +# Getting a link to the opened PR +$env:PR_LINK = Get-PullRequest From 0347ed8f898a9620b0ef2fc6cb246c97d2f25328 Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Thu, 11 Mar 2021 10:15:26 +0300 Subject: [PATCH 197/259] Temporary localization files renaming (#731) [skip ci] --- .../Strings/resources.resjson/{de-de => _de-DE}/resources.resjson | 0 .../Strings/resources.resjson/{es-es => _es-ES}/resources.resjson | 0 .../Strings/resources.resjson/{fr-fr => _fr-FR}/resources.resjson | 0 .../Strings/resources.resjson/{ja-jp => _ja-JP}/resources.resjson | 0 .../Strings/resources.resjson/{de-de => _de-DE}/resources.resjson | 0 .../Strings/resources.resjson/{es-es => _es-ES}/resources.resjson | 0 .../Strings/resources.resjson/{fr-fr => _fr-FR}/resources.resjson | 0 .../Strings/resources.resjson/{ja-jp => _ja-JP}/resources.resjson | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename node/Strings/resources.resjson/{de-de => _de-DE}/resources.resjson (100%) rename node/Strings/resources.resjson/{es-es => _es-ES}/resources.resjson (100%) rename node/Strings/resources.resjson/{fr-fr => _fr-FR}/resources.resjson (100%) rename node/Strings/resources.resjson/{ja-jp => _ja-JP}/resources.resjson (100%) rename powershell/VstsTaskSdk/Strings/resources.resjson/{de-de => _de-DE}/resources.resjson (100%) rename powershell/VstsTaskSdk/Strings/resources.resjson/{es-es => _es-ES}/resources.resjson (100%) rename powershell/VstsTaskSdk/Strings/resources.resjson/{fr-fr => _fr-FR}/resources.resjson (100%) rename powershell/VstsTaskSdk/Strings/resources.resjson/{ja-jp => _ja-JP}/resources.resjson (100%) diff --git a/node/Strings/resources.resjson/de-de/resources.resjson b/node/Strings/resources.resjson/_de-DE/resources.resjson similarity index 100% rename from node/Strings/resources.resjson/de-de/resources.resjson rename to node/Strings/resources.resjson/_de-DE/resources.resjson diff --git a/node/Strings/resources.resjson/es-es/resources.resjson b/node/Strings/resources.resjson/_es-ES/resources.resjson similarity index 100% rename from node/Strings/resources.resjson/es-es/resources.resjson rename to node/Strings/resources.resjson/_es-ES/resources.resjson diff --git a/node/Strings/resources.resjson/fr-fr/resources.resjson b/node/Strings/resources.resjson/_fr-FR/resources.resjson similarity index 100% rename from node/Strings/resources.resjson/fr-fr/resources.resjson rename to node/Strings/resources.resjson/_fr-FR/resources.resjson diff --git a/node/Strings/resources.resjson/ja-jp/resources.resjson b/node/Strings/resources.resjson/_ja-JP/resources.resjson similarity index 100% rename from node/Strings/resources.resjson/ja-jp/resources.resjson rename to node/Strings/resources.resjson/_ja-JP/resources.resjson diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/de-de/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/_de-DE/resources.resjson similarity index 100% rename from powershell/VstsTaskSdk/Strings/resources.resjson/de-de/resources.resjson rename to powershell/VstsTaskSdk/Strings/resources.resjson/_de-DE/resources.resjson diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/es-es/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/_es-ES/resources.resjson similarity index 100% rename from powershell/VstsTaskSdk/Strings/resources.resjson/es-es/resources.resjson rename to powershell/VstsTaskSdk/Strings/resources.resjson/_es-ES/resources.resjson diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/fr-fr/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/_fr-FR/resources.resjson similarity index 100% rename from powershell/VstsTaskSdk/Strings/resources.resjson/fr-fr/resources.resjson rename to powershell/VstsTaskSdk/Strings/resources.resjson/_fr-FR/resources.resjson diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/ja-jp/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/_ja-JP/resources.resjson similarity index 100% rename from powershell/VstsTaskSdk/Strings/resources.resjson/ja-jp/resources.resjson rename to powershell/VstsTaskSdk/Strings/resources.resjson/_ja-JP/resources.resjson From 7f1b64ab73132c92cc143d5f48ac8b0cf45f0c9c Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Thu, 11 Mar 2021 11:06:51 +0300 Subject: [PATCH 198/259] Renamed localization folders (#732) [skip ci] --- .../Strings/resources.resjson/{_de-DE => de-DE}/resources.resjson | 0 .../Strings/resources.resjson/{_es-ES => es-ES}/resources.resjson | 0 .../Strings/resources.resjson/{_fr-FR => fr-FR}/resources.resjson | 0 .../Strings/resources.resjson/{_ja-JP => ja-JP}/resources.resjson | 0 .../Strings/resources.resjson/{_de-DE => de-DE}/resources.resjson | 0 .../Strings/resources.resjson/{_es-ES => es-ES}/resources.resjson | 0 .../Strings/resources.resjson/{_fr-FR => fr-FR}/resources.resjson | 0 .../Strings/resources.resjson/{_ja-JP => ja-JP}/resources.resjson | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename node/Strings/resources.resjson/{_de-DE => de-DE}/resources.resjson (100%) rename node/Strings/resources.resjson/{_es-ES => es-ES}/resources.resjson (100%) rename node/Strings/resources.resjson/{_fr-FR => fr-FR}/resources.resjson (100%) rename node/Strings/resources.resjson/{_ja-JP => ja-JP}/resources.resjson (100%) rename powershell/VstsTaskSdk/Strings/resources.resjson/{_de-DE => de-DE}/resources.resjson (100%) rename powershell/VstsTaskSdk/Strings/resources.resjson/{_es-ES => es-ES}/resources.resjson (100%) rename powershell/VstsTaskSdk/Strings/resources.resjson/{_fr-FR => fr-FR}/resources.resjson (100%) rename powershell/VstsTaskSdk/Strings/resources.resjson/{_ja-JP => ja-JP}/resources.resjson (100%) diff --git a/node/Strings/resources.resjson/_de-DE/resources.resjson b/node/Strings/resources.resjson/de-DE/resources.resjson similarity index 100% rename from node/Strings/resources.resjson/_de-DE/resources.resjson rename to node/Strings/resources.resjson/de-DE/resources.resjson diff --git a/node/Strings/resources.resjson/_es-ES/resources.resjson b/node/Strings/resources.resjson/es-ES/resources.resjson similarity index 100% rename from node/Strings/resources.resjson/_es-ES/resources.resjson rename to node/Strings/resources.resjson/es-ES/resources.resjson diff --git a/node/Strings/resources.resjson/_fr-FR/resources.resjson b/node/Strings/resources.resjson/fr-FR/resources.resjson similarity index 100% rename from node/Strings/resources.resjson/_fr-FR/resources.resjson rename to node/Strings/resources.resjson/fr-FR/resources.resjson diff --git a/node/Strings/resources.resjson/_ja-JP/resources.resjson b/node/Strings/resources.resjson/ja-JP/resources.resjson similarity index 100% rename from node/Strings/resources.resjson/_ja-JP/resources.resjson rename to node/Strings/resources.resjson/ja-JP/resources.resjson diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/_de-DE/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/de-DE/resources.resjson similarity index 100% rename from powershell/VstsTaskSdk/Strings/resources.resjson/_de-DE/resources.resjson rename to powershell/VstsTaskSdk/Strings/resources.resjson/de-DE/resources.resjson diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/_es-ES/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/es-ES/resources.resjson similarity index 100% rename from powershell/VstsTaskSdk/Strings/resources.resjson/_es-ES/resources.resjson rename to powershell/VstsTaskSdk/Strings/resources.resjson/es-ES/resources.resjson diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/_fr-FR/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/fr-FR/resources.resjson similarity index 100% rename from powershell/VstsTaskSdk/Strings/resources.resjson/_fr-FR/resources.resjson rename to powershell/VstsTaskSdk/Strings/resources.resjson/fr-FR/resources.resjson diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/_ja-JP/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/ja-JP/resources.resjson similarity index 100% rename from powershell/VstsTaskSdk/Strings/resources.resjson/_ja-JP/resources.resjson rename to powershell/VstsTaskSdk/Strings/resources.resjson/ja-JP/resources.resjson From 21dac986b4c7478eb2ed8ffbd60e4fad62904438 Mon Sep 17 00:00:00 2001 From: Egor Bryzgalov Date: Thu, 11 Mar 2021 17:47:05 +0300 Subject: [PATCH 199/259] Fixed notification for the localization pipeline (#736) [skip ci] --- open-pullrequest.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/open-pullrequest.ps1 b/open-pullrequest.ps1 index 25afaa84d..0ddbb4440 100644 --- a/open-pullrequest.ps1 +++ b/open-pullrequest.ps1 @@ -21,4 +21,5 @@ $body = "This PR was auto-generated with [the localization pipeline build]($buil gh pr create --head $SourceBranch --title 'Localization update' --body $body # Getting a link to the opened PR -$env:PR_LINK = Get-PullRequest +$PR_LINK = Get-PullRequest +Write-Host "##vso[task.setvariable variable=PR_LINK]$PR_LINK" From e69409b1c9b038296e8d67954adac50840d9dec6 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Mon, 19 Apr 2021 09:09:30 -0400 Subject: [PATCH 200/259] Update to use agent-supported version of escaping (#753) --- node/taskcommand.ts | 8 ++++---- node/test/commandtests.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/node/taskcommand.ts b/node/taskcommand.ts index ceed1bbe5..a528068fc 100644 --- a/node/taskcommand.ts +++ b/node/taskcommand.ts @@ -91,7 +91,7 @@ export function commandFromString(commandLine) { } function escapedata(s) : string { - return s.replace(/%/g, '%25') + return s.replace(/%/g, '%AZP25') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A'); } @@ -99,11 +99,11 @@ function escapedata(s) : string { function unescapedata(s) : string { return s.replace(/%0D/g, '\r') .replace(/%0A/g, '\n') - .replace(/%25/g, '%'); + .replace(/%AZP25/g, '%'); } function escape(s) : string { - return s.replace(/%/g, '%25') + return s.replace(/%/g, '%AZP25') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A') .replace(/]/g, '%5D') @@ -115,5 +115,5 @@ function unescape(s) : string { .replace(/%0A/g, '\n') .replace(/%5D/g, ']') .replace(/%3B/g, ';') - .replace(/%25/g, '%'); + .replace(/%AZP25/g, '%'); } diff --git a/node/test/commandtests.ts b/node/test/commandtests.ts index bc49bda25..8d0bb0e59 100644 --- a/node/test/commandtests.ts +++ b/node/test/commandtests.ts @@ -68,7 +68,7 @@ describe('Command Tests', function () { var tc = new tcm.TaskCommand('some.cmd', { foo: ';=\r=\n%3B' }, 'dog'); assert(tc, 'TaskCommand constructor works'); var cmdStr = tc.toString(); - assert.equal(cmdStr, '##vso[some.cmd foo=%3B=%0D=%0A%253B;]dog'); + assert.equal(cmdStr, '##vso[some.cmd foo=%3B=%0D=%0A%AZP253B;]dog'); done(); }) @@ -149,7 +149,7 @@ describe('Command Tests', function () { }) it ('parses and unescapes properties', function (done) { - var cmdStr = '##vso[basic.command foo=%3B=%0D=%0A%253B;]dog'; + var cmdStr = '##vso[basic.command foo=%3B=%0D=%0A%AZP253B;]dog'; var tc = tcm.commandFromString(cmdStr); assert.equal(tc.command, 'basic.command', 'cmd should be basic.command'); From d2131e5300e7132b1495d790e3bf3a33459119fe Mon Sep 17 00:00:00 2001 From: Alexander Smolyakov Date: Mon, 19 Apr 2021 19:22:28 +0300 Subject: [PATCH 201/259] Updated package version to 3.1.1 (#756) --- node/package-lock.json | 2 +- node/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 5b425a4d4..f1ae3a418 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.0", + "version": "3.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 74b0842c5..b32e5641b 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.0", + "version": "3.1.1", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From bffffa59aa854d9e87f0886a78e237520b47b360 Mon Sep 17 00:00:00 2001 From: Anatoly Bolshakov Date: Mon, 19 Apr 2021 20:59:52 +0300 Subject: [PATCH 202/259] Bumped extension version (#757) --- node/package-lock.json | 2 +- node/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index f1ae3a418..a4a57a9f8 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.1", + "version": "3.1.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index b32e5641b..489a867a0 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.1", + "version": "3.1.2", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From d81a45343958fa6468fdf7fc1aee63d1b713d7a1 Mon Sep 17 00:00:00 2001 From: Nikita Ezzhev Date: Thu, 22 Apr 2021 13:41:20 +0300 Subject: [PATCH 203/259] Added workflow for automerging PRs from LEGO system (#758) * Added workflow to autocomplete PRs from csigs * Change secret * Fix varaible mapping * Update variable * Change wf name * Add ation only to PRs to Localisation branch --- .github/workflows/localization-automerge.yml | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/localization-automerge.yml diff --git a/.github/workflows/localization-automerge.yml b/.github/workflows/localization-automerge.yml new file mode 100644 index 000000000..eca1b651c --- /dev/null +++ b/.github/workflows/localization-automerge.yml @@ -0,0 +1,25 @@ +name: 'LEGO automerge' + +on: + pull_request: + types: + - opened + branches: + - Localization + +jobs: + worker: + runs-on: ubuntu-latest + + if: github.actor == 'csigs' + steps: + - uses: actions/github-script@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.pulls.merge({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + pull_number: context.payload.pull_request.number, + merge_method: 'squash' + }) From 448ff99ea75b2bbe0d1a24427574520f409cf471 Mon Sep 17 00:00:00 2001 From: Rick Brown Date: Mon, 24 May 2021 16:08:56 +1000 Subject: [PATCH 204/259] Allow mirror URL to be set for NodeJS downloads (#719) Closes issue #718 --- node/buildutils.js | 3 ++- node/mock-test.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/node/buildutils.js b/node/buildutils.js index ac8ef612f..13192e0d9 100644 --- a/node/buildutils.js +++ b/node/buildutils.js @@ -33,7 +33,8 @@ exports.getExternals = function () { // download the same version of node used by the agent // and add node to the PATH - var nodeUrl = 'https://nodejs.org/dist'; + var nodeUrl = process.env['TASK_NODE_URL'] || 'https://nodejs.org/dist'; + nodeUrl = nodeUrl.replace(/\/$/, ''); // ensure there is no trailing slash on the base URL var nodeVersion = 'v10.23.0'; switch (platform) { case 'darwin': diff --git a/node/mock-test.ts b/node/mock-test.ts index 5b56c3e7c..37d379a3b 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -229,7 +229,8 @@ export class MockTestRunner { // Downloads the specified node version to the download destination. Returns a path to node.exe private downloadNode(nodeVersion: string, downloadDestination: string): string { shelljs.rm('-rf', downloadDestination); - const nodeUrl: string = 'https://nodejs.org/dist'; + let nodeUrl: string = process.env['TASK_NODE_URL'] || 'https://nodejs.org/dist'; + nodeUrl = nodeUrl.replace(/\/$/, ''); // ensure there is no trailing slash on the base URL let downloadPath = ''; switch (this.getPlatform()) { case 'darwin': From a9171d9cfeb4fe7b3b0a66413802f24156e56764 Mon Sep 17 00:00:00 2001 From: Anatoly Bolshakov Date: Wed, 26 May 2021 15:19:55 +0300 Subject: [PATCH 205/259] Added Auto assign action (#765) * Added autoassign action * Update .github/workflows/autoAssignABTT.yml Co-authored-by: DaniilShmelev <72494759+DaniilShmelev@users.noreply.github.com> Co-authored-by: DaniilShmelev <72494759+DaniilShmelev@users.noreply.github.com> --- .github/workflows/autoAssignABTT.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/autoAssignABTT.yml diff --git a/.github/workflows/autoAssignABTT.yml b/.github/workflows/autoAssignABTT.yml new file mode 100644 index 000000000..b781039f7 --- /dev/null +++ b/.github/workflows/autoAssignABTT.yml @@ -0,0 +1,24 @@ +name: Auto Assign ABTT to Project Board + +on: + issues: + types: [opened] +env: + MY_GITHUB_TOKEN: ${{ secrets.ABTT_TOKEN }} + +jobs: + assign_one_project: + runs-on: ubuntu-latest + name: Assign to ABTT Project + steps: + - name: "Add triage and area labels" + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: | + "Area: TaskLib" + triage + - name: "Assign newly opened issues to project board" + uses: srggrs/assign-one-project-github-action@1.2.0 + with: + project: 'https://github.com/orgs/microsoft/projects/48' + column_name: 'Backlog' From 7f912e21a0efd546b2f0baf4530cf52aa3692405 Mon Sep 17 00:00:00 2001 From: Anatoly Bolshakov Date: Mon, 31 May 2021 12:46:28 +0300 Subject: [PATCH 206/259] Fixed token (#767) --- .github/workflows/autoAssignABTT.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/autoAssignABTT.yml b/.github/workflows/autoAssignABTT.yml index b781039f7..60c2e04d4 100644 --- a/.github/workflows/autoAssignABTT.yml +++ b/.github/workflows/autoAssignABTT.yml @@ -14,8 +14,9 @@ jobs: - name: "Add triage and area labels" uses: actions-ecosystem/action-add-labels@v1 with: + github_token: ${{ secrets.GITHUB_TOKEN }} labels: | - "Area: TaskLib" + Area: TaskLib triage - name: "Assign newly opened issues to project board" uses: srggrs/assign-one-project-github-action@1.2.0 From 0533838a521fadeee274908e06d512dd73eec261 Mon Sep 17 00:00:00 2001 From: Anatoly Bolshakov Date: Tue, 1 Jun 2021 19:41:39 +0300 Subject: [PATCH 207/259] Localization update (#768) * Added localization pipeline and LocProject.json * Removed en-US * Update localize-pipeline.yml for Azure Pipelines * Update localize-pipeline.yml for Azure Pipelines * Made letter case consistent for languages * LEGO: check in for Localization to temporary branch. (#703) * LEGO: check in for Localization to temporary branch. (#714) * LEGO: check in for Localization to temporary branch. (#720) * Temp renaming * Renamed localization files * Applied enhancements for the localization pipeline (#733) [skip ci] * [Localization] Fixed localization pipeline issue with already localized strings replaced (#737) * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 14646607 Localized file check-in by OneLocBuild Task * LEGO: check in for Localization to temporary branch. (#740) * LEGO: check in for Localization to temporary branch. (#741) * LEGO: check in for Localization to temporary branch. (#742) * LEGO: check in for Localization to temporary branch. (#743) * Temporary renamed files - to resolve conflicts * Temporary renamed * LEGO: check in for Localization to temporary branch. (#745) Co-authored-by: csigs * LEGO: check in for Localization to temporary branch. (#746) Co-authored-by: csigs * LEGO: check in for Localization to temporary branch. (#747) Co-authored-by: csigs * LEGO: check in for Localization to temporary branch. (#748) Co-authored-by: csigs * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 14905562 Localized file check-in by OneLocBuild Task * Returned back original names * Removed redundant locale - test * Removed redundant folders * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 14906155 Localized file check-in by OneLocBuild Task * Returned back changes. Removed redundant * Create PR in OneLocBuild task only on third week of sprint (#755) * Fix localization pipeline * Add missed change * Added option to disable PR creation * Removing Localize folder Co-authored-by: csigs Co-authored-by: Egor Bryzgalov Co-authored-by: csigs Co-authored-by: Nikita Ezzhev --- .../resources.resjson/de-DE/resources.resjson | 10 ++- .../resources.resjson/es-ES/resources.resjson | 10 ++- .../resources.resjson/fr-FR/resources.resjson | 10 ++- .../resources.resjson/it-IT/resources.resjson | 62 ++++++++++--------- .../resources.resjson/ja-JP/resources.resjson | 10 ++- .../resources.resjson/ko-KR/resources.resjson | 62 ++++++++++--------- .../resources.resjson/ru-RU/resources.resjson | 62 ++++++++++--------- .../resources.resjson/zh-CN/resources.resjson | 62 ++++++++++--------- .../resources.resjson/zh-TW/resources.resjson | 62 ++++++++++--------- .../resources.resjson/it-IT/resources.resjson | 34 +++++----- .../resources.resjson/ko-KR/resources.resjson | 34 +++++----- .../resources.resjson/ru-RU/resources.resjson | 34 +++++----- .../resources.resjson/zh-CN/resources.resjson | 34 +++++----- .../resources.resjson/zh-TW/resources.resjson | 34 +++++----- 14 files changed, 287 insertions(+), 233 deletions(-) diff --git a/node/Strings/resources.resjson/de-DE/resources.resjson b/node/Strings/resources.resjson/de-DE/resources.resjson index 75a1a0379..efd6ee72c 100644 --- a/node/Strings/resources.resjson/de-DE/resources.resjson +++ b/node/Strings/resources.resjson/de-DE/resources.resjson @@ -5,11 +5,15 @@ "loc.messages.LIB_MkdirFailedFileExists": "Das Verzeichnis \"%s\" kann nicht erstellt werden. Eine in Konflikt stehende Datei ist vorhanden: \"%s\"", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Das Verzeichnis \"%s\" kann nicht erstellt werden. Das Stammverzeichnis ist nicht vorhanden: %s", "loc.messages.LIB_MkdirFailedInvalidShare": "Das Verzeichnis \"%s\" kann nicht erstellt werden. Es kann nicht überprüft werden, ob das Verzeichnis vorhanden ist: {%s}. Wenn das Verzeichnis eine Dateifreigabe ist, stellen Sie sicher, dass der Freigabename richtig, die Freigabe online und der aktuelle Prozess berechtigt ist, auf die Freigabe zuzugreifen.", - "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", + "loc.messages.LIB_MultilineSecret": "Geheimnisse dürfen nicht mehrere Zeilen enthalten", + "loc.messages.LIB_ProcessError": "Fehler beim Ausführen des Prozesses \"%s\". Möglicherweise konnte der Prozess nicht gestartet werden. Fehler: %s", + "loc.messages.LIB_ProcessExitCode": "Fehler beim Prozess \"%s\" mit Exitcode %s.", + "loc.messages.LIB_ProcessStderr": "Fehler beim Prozess \"%s\": Mindestens eine Zeile wurde in den STDERR-Datenstrom geschrieben.", "loc.messages.LIB_ReturnCode": "Rückgabecode: %d", "loc.messages.LIB_ResourceFileNotExist": "Die Ressourcendatei ist nicht vorhanden: %s", "loc.messages.LIB_ResourceFileAlreadySet": "Die Ressourcendatei wurde bereits festgelegt auf: %s", "loc.messages.LIB_ResourceFileNotSet": "Die Ressourcendatei wurde nicht festgelegt. Die Lokalisierungszeichenfolge für den folgenden Schlüssel wurde nicht gefunden: %s", + "loc.messages.LIB_StdioNotClosed": "Die STDIO-Datenströme wurden nicht innerhalb von %s Sekunden nach dem Beendigungsereignis aus dem Prozess \"%s\" geschlossen. Möglicherweise hat ein untergeordneter Prozess die STDIO-Datenströme geerbt und wurde noch nicht beendet.", "loc.messages.LIB_WhichNotFound_Linux": "Ausführbare Datei nicht gefunden: '%s'. Prüfen Sie, ob der Dateipfad vorhanden ist oder sich die Datei in einem von der PATH-Umgebungsvariablen angegebenen Verzeichnis befindet. Prüfen Sie zudem den Dateimodus, um sicherzustellen, dass die Datei ausführbar ist.", "loc.messages.LIB_WhichNotFound_Win": "Ausführbare Datei nicht gefunden: \"%s\". Prüfen Sie, ob der Dateipfad vorhanden ist oder sich die Datei in einem von der PATH-Umgebungsvariablen angegebenen Verzeichnis befindet. Prüfen Sie zudem, ob die Datei eine gültige Erweiterung für eine ausführbare Datei aufweist.", "loc.messages.LIB_LocStringNotFound": "Die Lokalisierungszeichenfolge für den folgenden Schlüssel wurde nicht gefunden: %s", @@ -25,5 +29,7 @@ "loc.messages.LIB_PathHasNullByte": "Der Pfad darf keine NULL-Bytes enthalten.", "loc.messages.LIB_OperationFailed": "Fehler %s: %s", "loc.messages.LIB_UseFirstGlobMatch": "Mehrere Arbeitsbereichübereinstimmungen. Die erste Übereinstimmung wird verwendet.", - "loc.messages.LIB_MergeTestResultNotSupported": "Das Mergen von Testergebnissen aus mehreren Dateien in einen Testlauf wird von dieser Version des Build-Agents für OSX/Linux nicht unterstützt. Jede Testergebnisdatei wird als separater Testlauf in VSO/TFS veröffentlicht." + "loc.messages.LIB_MergeTestResultNotSupported": "Das Mergen von Testergebnissen aus mehreren Dateien in einen Testlauf wird von dieser Version des Build-Agents für OSX/Linux nicht unterstützt. Jede Testergebnisdatei wird als separater Testlauf in VSO/TFS veröffentlicht.", + "loc.messages.LIB_PlatformNotSupported": "Plattform wird nicht unterstützt: %s", + "loc.messages.LIB_CopyFileFailed": "Error while copying the file. Attempts left: %s" } \ No newline at end of file diff --git a/node/Strings/resources.resjson/es-ES/resources.resjson b/node/Strings/resources.resjson/es-ES/resources.resjson index 6ae378812..24090d0fc 100644 --- a/node/Strings/resources.resjson/es-ES/resources.resjson +++ b/node/Strings/resources.resjson/es-ES/resources.resjson @@ -5,11 +5,15 @@ "loc.messages.LIB_MkdirFailedFileExists": "No se puede crear el directorio '%s'. Hay un conflicto entre archivos: '%s'", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "No se puede crear el directorio '%s'. El directorio raíz no existe: '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "No se puede crear el directorio '%s'. No se puede comprobar si el directorio existe: '%s'. Si el directorio es un recurso compartido de archivos, compruebe que el nombre es correcto, que está en línea y que el proceso actual tiene permiso de acceso a este.", - "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", + "loc.messages.LIB_MultilineSecret": "Los secretos no pueden contener varias líneas.", + "loc.messages.LIB_ProcessError": "Error al intentar ejecutar el proceso \"%s\". Esto puede indicar que no se pudo iniciar el proceso. Error: %s", + "loc.messages.LIB_ProcessExitCode": "Error del proceso \"%s\" con el código de salida %s", + "loc.messages.LIB_ProcessStderr": "Error del proceso \"%s\" porque se escribieron una o varias líneas en la secuencia STDERR", "loc.messages.LIB_ReturnCode": "Código de retorno: %d", "loc.messages.LIB_ResourceFileNotExist": "El archivo de recursos no existe: %s", "loc.messages.LIB_ResourceFileAlreadySet": "El archivo de recursos se ha establecido ya en: %s", "loc.messages.LIB_ResourceFileNotSet": "No se ha establecido el archivo de recursos. No se encuentra la cadena localizada para la clave: %s", + "loc.messages.LIB_StdioNotClosed": "Las secuencias STDIO no se cerraron en un plazo de %s segundos desde el evento de salida del proceso \"%s\". Esto puede indicar que un proceso secundario ha heredado las secuencias STDIO y aún no se ha cerrado.", "loc.messages.LIB_WhichNotFound_Linux": "No se puede encontrar el archivo ejecutable: \"%s\". Compruebe que la ruta de acceso del archivo existe o que el archivo se puede encontrar en un directorio especificado en la variable de entorno PATH. Revise también el modo de archivo para comprobar que el archivo es ejecutable.", "loc.messages.LIB_WhichNotFound_Win": "No se puede encontrar el archivo ejecutable: \"%s\". Compruebe que la ruta de acceso del archivo existe o que el archivo se puede encontrar en un directorio especificado en la variable de entorno PATH. Revise también que el archivo tenga una extensión válida para un archivo ejecutable.", "loc.messages.LIB_LocStringNotFound": "No se encuentra la cadena localizada para la clave: %s", @@ -25,5 +29,7 @@ "loc.messages.LIB_PathHasNullByte": "La ruta de acceso no puede tener bytes nulos", "loc.messages.LIB_OperationFailed": "Error de %s: %s", "loc.messages.LIB_UseFirstGlobMatch": "Hay varias coincidencias en el área de trabajo. Se usará la primera.", - "loc.messages.LIB_MergeTestResultNotSupported": "Esta versión del agente de compilación para OSX/Linux no admite la fusión mediante combinación de resultados de pruebas de varios archivos en una serie de pruebas. Cada archivo de resultados de pruebas se publicará como una serie de pruebas diferente en VSO/TFS." + "loc.messages.LIB_MergeTestResultNotSupported": "Esta versión del agente de compilación para OSX/Linux no admite la fusión mediante combinación de resultados de pruebas de varios archivos en una serie de pruebas. Cada archivo de resultados de pruebas se publicará como una serie de pruebas diferente en VSO/TFS.", + "loc.messages.LIB_PlatformNotSupported": "No se admite la plataforma: %s", + "loc.messages.LIB_CopyFileFailed": "Error while copying the file. Attempts left: %s" } \ No newline at end of file diff --git a/node/Strings/resources.resjson/fr-FR/resources.resjson b/node/Strings/resources.resjson/fr-FR/resources.resjson index 334162833..0893f6e3f 100644 --- a/node/Strings/resources.resjson/fr-FR/resources.resjson +++ b/node/Strings/resources.resjson/fr-FR/resources.resjson @@ -5,11 +5,15 @@ "loc.messages.LIB_MkdirFailedFileExists": "Impossible de créer le répertoire '%s'. Présence d'un fichier en conflit : '%s'", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Impossible de créer le répertoire '%s'. Le répertoire racine n'existe pas : '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "Impossible de créer le répertoire '%s'. Impossible de vérifier l'existence du répertoire : '%s'. Si le répertoire est un partage de fichiers, vérifiez que le nom du partage est correct, que le partage est en ligne, et que le processus actuel est autorisé à accéder au partage.", - "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", + "loc.messages.LIB_MultilineSecret": "Les secrets ne peuvent pas contenir plusieurs lignes", + "loc.messages.LIB_ProcessError": "Erreur durant la tentative d'exécution du processus '%s'. Cela peut indiquer que le processus n'a pas réussi à démarrer. Erreur : %s", + "loc.messages.LIB_ProcessExitCode": "Échec du processus '%s'. Code de sortie : %s", + "loc.messages.LIB_ProcessStderr": "Échec du processus '%s', car une ou plusieurs lignes ont été écrites dans le flux STDERR", "loc.messages.LIB_ReturnCode": "Code de retour : %d", "loc.messages.LIB_ResourceFileNotExist": "Le fichier de ressources n'existe pas : %s", "loc.messages.LIB_ResourceFileAlreadySet": "Le fichier de ressources est déjà défini : %s", "loc.messages.LIB_ResourceFileNotSet": "Le fichier de ressources n'est pas défini. La chaîne localisée de la clé est introuvable : %s", + "loc.messages.LIB_StdioNotClosed": "Les flux STDIO ne se sont pas fermés dans les %s secondes qui ont suivi l'événement exit du processus '%s'. Cela peut indiquer qu'un processus enfant a hérité des flux STDIO et qu'il n'est pas encore sorti.", "loc.messages.LIB_WhichNotFound_Linux": "Impossible de localiser le fichier exécutable : '%s'. Vérifiez si le chemin du fichier existe ou si le fichier peut se trouver dans un répertoire spécifié par la variable d'environnement PATH. Vérifiez également le Mode de Fichier pour déterminer si le fichier est exécutable.", "loc.messages.LIB_WhichNotFound_Win": "Impossible de localiser le fichier exécutable : '%s'. Vérifiez si le chemin du fichier existe ou si le fichier peut se trouver dans un répertoire spécifié par la variable d'environnement PATH. Vérifiez également si le fichier a une extension de fichier exécutable valide.", "loc.messages.LIB_LocStringNotFound": "Chaîne localisée introuvable pour la clé : %s", @@ -25,5 +29,7 @@ "loc.messages.LIB_PathHasNullByte": "Le chemin ne peut pas contenir d'octets de valeur Null", "loc.messages.LIB_OperationFailed": "Échec de %s : %s", "loc.messages.LIB_UseFirstGlobMatch": "Plusieurs espaces de travail correspondants. Utilisation du premier d'entre eux.", - "loc.messages.LIB_MergeTestResultNotSupported": "La fusion des résultats des tests de plusieurs fichiers en une seule série de tests n'est pas prise en charge dans cette version de l'agent de build pour OSX/Linux. Chaque fichier de résultats des tests est publié en tant que série de tests distincte dans VSO/TFS." + "loc.messages.LIB_MergeTestResultNotSupported": "La fusion des résultats des tests de plusieurs fichiers en une seule série de tests n'est pas prise en charge dans cette version de l'agent de build pour OSX/Linux. Chaque fichier de résultats des tests est publié en tant que série de tests distincte dans VSO/TFS.", + "loc.messages.LIB_PlatformNotSupported": "Plateforme non prise en charge : %s", + "loc.messages.LIB_CopyFileFailed": "Error while copying the file. Attempts left: %s" } \ No newline at end of file diff --git a/node/Strings/resources.resjson/it-IT/resources.resjson b/node/Strings/resources.resjson/it-IT/resources.resjson index bd97a92e8..a24444fe1 100644 --- a/node/Strings/resources.resjson/it-IT/resources.resjson +++ b/node/Strings/resources.resjson/it-IT/resources.resjson @@ -1,29 +1,35 @@ -{ - "loc.messages.LIB_UnhandledEx": "Eccezione non gestita: %s", - "loc.messages.LIB_FailOnCode": "Codice restituito dell'errore: %d", - "loc.messages.LIB_MkdirFailed": "Non è possibile creare la directory '%s'. %s", - "loc.messages.LIB_MkdirFailedFileExists": "Non è possibile creare la directory '%s'. Esiste un file in conflitto: '%s'", - "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Non è possibile creare la directory '%s'. La directory radice non esiste: '%s'", - "loc.messages.LIB_MkdirFailedInvalidShare": "Non è possibile creare la directory '%s' perché non è possibile verificarne l'esistenza: '%s'. Se la directory è una condivisione file, verificare che il nome della condivisione sia corretto, che la condivisione sia online e che il processo corrente sia autorizzato ad accedervi.", - "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", - "loc.messages.LIB_ReturnCode": "Codice restituito: %d", - "loc.messages.LIB_ResourceFileNotExist": "Il file di risorse non esiste: %s", - "loc.messages.LIB_ResourceFileAlreadySet": "Il file di risorse è già stato impostato su %s", - "loc.messages.LIB_ResourceFileNotSet": "Il file di risorse non è stato impostato. La stringa localizzata per la chiave %s non è stata trovata", - "loc.messages.LIB_WhichNotFound_Linux": "Il file eseguibile '%s' non è stato trovato. Verificare se il percorso di file esiste o il file è presente in una directory specificata dalla variabile di ambiente PATH. Controllare anche la modalità file per verificare che il file sia eseguibile.", - "loc.messages.LIB_WhichNotFound_Win": "Il file eseguibile '%s' non è stato trovato. Verificare se il percorso di file esiste o il file è presente in una directory specificata dalla variabile di ambiente PATH. Controllare anche che l'estensione sia valida per un file eseguibile.", - "loc.messages.LIB_LocStringNotFound": "La stringa localizzata per la chiave %s non è stata trovata", - "loc.messages.LIB_ParameterIsRequired": "Parametro %s non fornito", - "loc.messages.LIB_InputRequired": "Input richiesto: %s", - "loc.messages.LIB_InvalidPattern": "Criterio non valido: '%s'", - "loc.messages.LIB_EndpointNotExist": "Endpoint non presente: %s", - "loc.messages.LIB_EndpointDataNotExist": "Il parametro %s dei dati dell'endpoint non è presente: %s", - "loc.messages.LIB_EndpointAuthNotExist": "I dati di autenticazione endpoint non sono presenti: %s", - "loc.messages.LIB_InvalidEndpointAuth": "Autenticazione endpoint non valida: %s", - "loc.messages.LIB_InvalidSecureFilesInput": "L'input del file protetto non è valido: %s", - "loc.messages.LIB_PathNotFound": "Percorso %s non trovato: %s", - "loc.messages.LIB_PathHasNullByte": "Il percorso non può contenere byte Null", - "loc.messages.LIB_OperationFailed": "Operazione %s non riuscita: %s", - "loc.messages.LIB_UseFirstGlobMatch": "Sono presenti più corrispondenze dell'area di lavoro. Verrà usata la prima.", - "loc.messages.LIB_MergeTestResultNotSupported": "L'unione di più file risultanti da un'unica esecuzione dei test non è supportata in questa versione dell'agente di compilazione per OS X/Linux. Ogni file dei risultati del test verrà pubblicato come esecuzione dei test separata in VSO/TFS." +{ + "loc.messages.LIB_UnhandledEx": "Eccezione non gestita: %s", + "loc.messages.LIB_FailOnCode": "Codice restituito dell'errore: %d", + "loc.messages.LIB_MkdirFailed": "Non è possibile creare la directory '%s'. %s", + "loc.messages.LIB_MkdirFailedFileExists": "Non è possibile creare la directory '%s'. Esiste un file in conflitto: '%s'", + "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Non è possibile creare la directory '%s'. La directory radice non esiste: '%s'", + "loc.messages.LIB_MkdirFailedInvalidShare": "Non è possibile creare la directory '%s' perché non è possibile verificarne l'esistenza: '%s'. Se la directory è una condivisione file, verificare che il nome della condivisione sia corretto, che la condivisione sia online e che il processo corrente sia autorizzato ad accedervi.", + "loc.messages.LIB_MultilineSecret": "I segreti non possono contenere più righe", + "loc.messages.LIB_ProcessError": "Si è verificato un errore durante il tentativo di eseguire il processo '%s'. Questo errore può indicare che non è stato possibile avviare il processo. Errore: %s", + "loc.messages.LIB_ProcessExitCode": "Il processo '%s' non è riuscito. Codice di uscita: %s", + "loc.messages.LIB_ProcessStderr": "Il processo '%s' non è riuscito perché una o più righe sono state scritte nel flusso STDERR", + "loc.messages.LIB_ReturnCode": "Codice restituito: %d", + "loc.messages.LIB_ResourceFileNotExist": "Il file di risorse non esiste: %s", + "loc.messages.LIB_ResourceFileAlreadySet": "Il file di risorse è già stato impostato su %s", + "loc.messages.LIB_ResourceFileNotSet": "Il file di risorse non è stato impostato. La stringa localizzata per la chiave %s non è stata trovata", + "loc.messages.LIB_StdioNotClosed": "I flussi STDIO non si sono chiusi entro %s secondi dall'evento di uscita dal processo '%s'. Questa condizione può indicare che un processo figlio ha ereditato i flussi STDIO e non è ancora stato terminato.", + "loc.messages.LIB_WhichNotFound_Linux": "Il file eseguibile '%s' non è stato trovato. Verificare se il percorso di file esiste o il file è presente in una directory specificata dalla variabile di ambiente PATH. Controllare anche la modalità file per verificare che il file sia eseguibile.", + "loc.messages.LIB_WhichNotFound_Win": "Il file eseguibile '%s' non è stato trovato. Verificare se il percorso di file esiste o il file è presente in una directory specificata dalla variabile di ambiente PATH. Controllare anche che l'estensione sia valida per un file eseguibile.", + "loc.messages.LIB_LocStringNotFound": "La stringa localizzata per la chiave %s non è stata trovata", + "loc.messages.LIB_ParameterIsRequired": "Parametro %s non fornito", + "loc.messages.LIB_InputRequired": "Input richiesto: %s", + "loc.messages.LIB_InvalidPattern": "Criterio non valido: '%s'", + "loc.messages.LIB_EndpointNotExist": "Endpoint non presente: %s", + "loc.messages.LIB_EndpointDataNotExist": "Il parametro %s dei dati dell'endpoint non è presente: %s", + "loc.messages.LIB_EndpointAuthNotExist": "I dati di autenticazione endpoint non sono presenti: %s", + "loc.messages.LIB_InvalidEndpointAuth": "Autenticazione endpoint non valida: %s", + "loc.messages.LIB_InvalidSecureFilesInput": "L'input del file protetto non è valido: %s", + "loc.messages.LIB_PathNotFound": "Percorso %s non trovato: %s", + "loc.messages.LIB_PathHasNullByte": "Il percorso non può contenere byte Null", + "loc.messages.LIB_OperationFailed": "Operazione %s non riuscita: %s", + "loc.messages.LIB_UseFirstGlobMatch": "Sono presenti più corrispondenze dell'area di lavoro. Verrà usata la prima.", + "loc.messages.LIB_MergeTestResultNotSupported": "L'unione di più file risultanti da un'unica esecuzione dei test non è supportata in questa versione dell'agente di compilazione per OS X/Linux. Ogni file dei risultati del test verrà pubblicato come esecuzione dei test separata in VSO/TFS.", + "loc.messages.LIB_PlatformNotSupported": "Piattaforma non supportata: %s", + "loc.messages.LIB_CopyFileFailed": "Si è verificato un errore durante la copia del file. Tentativi rimasti: %s" } \ No newline at end of file diff --git a/node/Strings/resources.resjson/ja-JP/resources.resjson b/node/Strings/resources.resjson/ja-JP/resources.resjson index 18bbd0d29..e52e13056 100644 --- a/node/Strings/resources.resjson/ja-JP/resources.resjson +++ b/node/Strings/resources.resjson/ja-JP/resources.resjson @@ -5,11 +5,15 @@ "loc.messages.LIB_MkdirFailedFileExists": "ディレクトリ '%s' を作成できません。競合するファイルが存在します: '%s'", "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "ディレクトリ '%s' を作成できません。ルート ディレクトリが存在しません: '%s'", "loc.messages.LIB_MkdirFailedInvalidShare": "ディレクトリ '%s' を作成できません。ディレクトリが存在することを確認できません: '%s'。ディレクトリがファイル共有である場合、その共有名が正しいこと、その共有がオンラインであること、そして現在のプロセスにその共有へのアクセス許可があることをご確認ください。", - "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", + "loc.messages.LIB_MultilineSecret": "シークレットに複数の行を含めることはできません", + "loc.messages.LIB_ProcessError": "プロセス '%s' を実行しようとしているときにエラーが発生しました。プロセスの開始に失敗したおそれがあります。エラー: %s", + "loc.messages.LIB_ProcessExitCode": "プロセス '%s' が終了コード %s で失敗しました", + "loc.messages.LIB_ProcessStderr": "1 つ以上の行が STDERR ストリームに書き込まれたため、プロセス '%s' が失敗しました", "loc.messages.LIB_ReturnCode": "リターン コード: %d", "loc.messages.LIB_ResourceFileNotExist": "リソース ファイルが存在しません: %s", "loc.messages.LIB_ResourceFileAlreadySet": "リソース ファイルは既に %s に設定されています", "loc.messages.LIB_ResourceFileNotSet": "リソース ファイルが設定されておらず、キーの loc 文字列が見つかりません: %s", + "loc.messages.LIB_StdioNotClosed": "STDIO ストリームが、プロセス '%s' の終了イベントから %s 秒以内に終了しませんでした。これは、子プロセスが STDIO ストリームを継承し、まだ終了していないことを示している可能性があります。", "loc.messages.LIB_WhichNotFound_Linux": "実行可能ファイルが見つかりません: '%s'。ファイル パスが存在すること、またはそのファイルが PATH 環境変数で指定されたディレクトリ内にあることをご確認ください。ファイルが実行可能かどうかについてファイル モードもご確認ください。", "loc.messages.LIB_WhichNotFound_Win": "実行可能ファイルが見つかりません: '%s'。ファイル パスが存在すること、またはそのファイルが PATH 環境変数で指定されたディレクトリ内にあることをご確認ください。そのファイルに実行可能ファイルの有効な拡張子がついていることもご確認ください。", "loc.messages.LIB_LocStringNotFound": "キーの loc 文字列が見つかりません: %s", @@ -25,5 +29,7 @@ "loc.messages.LIB_PathHasNullByte": "パスに null バイトを含めることはできません", "loc.messages.LIB_OperationFailed": "失敗しました %s: %s", "loc.messages.LIB_UseFirstGlobMatch": "複数のワークスペースが一致します。最初のワークスペースが使用されます。", - "loc.messages.LIB_MergeTestResultNotSupported": "複数のファイルからのテスト結果を 1 つのテスト実行にマージする処理は、OSX/Linux 用のビルド エージェントのこのバージョンではサポートされていません。各テスト結果ファイルが VSO/TFS で別個のテスト実行として発行されます。" + "loc.messages.LIB_MergeTestResultNotSupported": "複数のファイルからのテスト結果を 1 つのテスト実行にマージする処理は、OSX/Linux 用のビルド エージェントのこのバージョンではサポートされていません。各テスト結果ファイルが VSO/TFS で別個のテスト実行として発行されます。", + "loc.messages.LIB_PlatformNotSupported": "プラットフォームがサポートされていません: %s", + "loc.messages.LIB_CopyFileFailed": "Error while copying the file. Attempts left: %s" } \ No newline at end of file diff --git a/node/Strings/resources.resjson/ko-KR/resources.resjson b/node/Strings/resources.resjson/ko-KR/resources.resjson index 98f410084..a5010b122 100644 --- a/node/Strings/resources.resjson/ko-KR/resources.resjson +++ b/node/Strings/resources.resjson/ko-KR/resources.resjson @@ -1,29 +1,35 @@ -{ - "loc.messages.LIB_UnhandledEx": "처리되지 않음: %s", - "loc.messages.LIB_FailOnCode": "실패 반환 코드: %d", - "loc.messages.LIB_MkdirFailed": "'%s' 디렉터리를 만들 수 없습니다. %s", - "loc.messages.LIB_MkdirFailedFileExists": "'%s' 디렉터리를 만들 수 없습니다. 충돌하는 파일 '%s'이(가) 있습니다.", - "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "'%s' 디렉터리를 만들 수 없습니다. 루트 디렉터리 '%s'이(가) 없습니다.", - "loc.messages.LIB_MkdirFailedInvalidShare": "'%s' 디렉터리를 만들 수 없습니다. '%s' 디렉터리가 있는지 확인할 수 없습니다. 디렉터리가 파일 공유인 경우 공유 이름이 올바르고, 공유가 온라인 상태이며, 현재 프로세스에 공유에 액세스할 수 있는 권한이 있는지 확인하세요.", - "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", - "loc.messages.LIB_ReturnCode": "반환 코드: %d", - "loc.messages.LIB_ResourceFileNotExist": "리소스 파일이 없음: %s", - "loc.messages.LIB_ResourceFileAlreadySet": "리소스 파일이 이미 다음으로 설정됨: %s", - "loc.messages.LIB_ResourceFileNotSet": "리소스 파일이 설정되지 않았고 키에 대한 loc 문자열을 찾을 수 없음: %s", - "loc.messages.LIB_WhichNotFound_Linux": "실행 파일 '%s'을(를) 찾을 수 없습니다. 파일 경로가 있는지 또는 PATH 환경 변수에서 지정한 디렉터리 내에서 파일을 찾을 수 있는지 확인하세요. 또한 파일 모드를 확인하여 파일을 실행할 수 있는지 확인하세요.", - "loc.messages.LIB_WhichNotFound_Win": "실행 파일 '%s'을(를) 찾을 수 없습니다. 파일 경로가 있는지 또는 PATH 환경 변수에서 지정한 디렉터리 내에서 파일을 찾을 수 있는지 확인하세요. 또한 파일이 실행 파일에 대해 올바른 확장명을 가지고 있는지 확인하세요.", - "loc.messages.LIB_LocStringNotFound": "키에 대한 loc 문자열을 찾을 수 없음: %s", - "loc.messages.LIB_ParameterIsRequired": "%s이(가) 제공되지 않음", - "loc.messages.LIB_InputRequired": "입력 필요: %s", - "loc.messages.LIB_InvalidPattern": "잘못된 패턴: '%s'", - "loc.messages.LIB_EndpointNotExist": "끝점이 없음: %s", - "loc.messages.LIB_EndpointDataNotExist": "끝점 데이터 매개 변수 %s이(가) 없음: %s", - "loc.messages.LIB_EndpointAuthNotExist": "끝점 인증 데이터가 없음: %s", - "loc.messages.LIB_InvalidEndpointAuth": "잘못된 끝점 인증: %s", - "loc.messages.LIB_InvalidSecureFilesInput": "보안 파일 입력이 잘못됨: %s", - "loc.messages.LIB_PathNotFound": "%s을(를) 찾을 수 없음: %s", - "loc.messages.LIB_PathHasNullByte": "경로에 null 바이트를 포함할 수 없음", - "loc.messages.LIB_OperationFailed": "%s 실패: %s", - "loc.messages.LIB_UseFirstGlobMatch": "여러 작업 영역이 일치합니다. 첫 번째 작업 영역을 사용하세요.", - "loc.messages.LIB_MergeTestResultNotSupported": "이 OSX/Linux용 빌드 에이전트 버전에서 여러 파일의 테스트 결과를 하나의 테스트 실행으로 병합하는 것을 지원하지 않습니다. 각 테스트 결과 파일은 VSO/TFS에서 별도의 테스트 실행으로 게시됩니다." +{ + "loc.messages.LIB_UnhandledEx": "처리되지 않음: %s", + "loc.messages.LIB_FailOnCode": "실패 반환 코드: %d", + "loc.messages.LIB_MkdirFailed": "'%s' 디렉터리를 만들 수 없습니다. %s", + "loc.messages.LIB_MkdirFailedFileExists": "'%s' 디렉터리를 만들 수 없습니다. 충돌하는 파일 '%s'이(가) 있습니다.", + "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "'%s' 디렉터리를 만들 수 없습니다. 루트 디렉터리 '%s'이(가) 없습니다.", + "loc.messages.LIB_MkdirFailedInvalidShare": "'%s' 디렉터리를 만들 수 없습니다. '%s' 디렉터리가 있는지 확인할 수 없습니다. 디렉터리가 파일 공유인 경우 공유 이름이 올바르고, 공유가 온라인 상태이며, 현재 프로세스에 공유에 액세스할 수 있는 권한이 있는지 확인하세요.", + "loc.messages.LIB_MultilineSecret": "비밀에 여러 줄을 사용할 수 없습니다.", + "loc.messages.LIB_ProcessError": "'%s' 프로세스를 실행할 때 오류가 발생했습니다. 이는 프로세스를 시작하지 못했음을 나타낼 수 있습니다. 오류: %s", + "loc.messages.LIB_ProcessExitCode": "'%s' 프로세스가 실패함(종료 코드 %s)", + "loc.messages.LIB_ProcessStderr": "하나 이상의 줄이 STDERR 스트림에 쓰였으므로 '%s' 프로세스가 실패함", + "loc.messages.LIB_ReturnCode": "반환 코드: %d", + "loc.messages.LIB_ResourceFileNotExist": "리소스 파일이 없음: %s", + "loc.messages.LIB_ResourceFileAlreadySet": "리소스 파일이 이미 다음으로 설정됨: %s", + "loc.messages.LIB_ResourceFileNotSet": "리소스 파일이 설정되지 않았고 키에 대한 loc 문자열을 찾을 수 없음: %s", + "loc.messages.LIB_StdioNotClosed": "STDIO 스트림이 %s초 이내('%s' 프로세스의 종료 이벤트 후)에 닫히지 않았습니다. 이는 자식 프로세스가 STDIO 스트림을 상속했으며 아직 종료되지 않았음을 나타낼 수 있습니다.", + "loc.messages.LIB_WhichNotFound_Linux": "실행 파일 '%s'을(를) 찾을 수 없습니다. 파일 경로가 있는지 또는 PATH 환경 변수에서 지정한 디렉터리 내에서 파일을 찾을 수 있는지 확인하세요. 또한 파일 모드를 확인하여 파일을 실행할 수 있는지 확인하세요.", + "loc.messages.LIB_WhichNotFound_Win": "실행 파일 '%s'을(를) 찾을 수 없습니다. 파일 경로가 있는지 또는 PATH 환경 변수에서 지정한 디렉터리 내에서 파일을 찾을 수 있는지 확인하세요. 또한 파일이 실행 파일에 대해 올바른 확장명을 가지고 있는지 확인하세요.", + "loc.messages.LIB_LocStringNotFound": "키에 대한 loc 문자열을 찾을 수 없음: %s", + "loc.messages.LIB_ParameterIsRequired": "%s이(가) 제공되지 않음", + "loc.messages.LIB_InputRequired": "입력 필요: %s", + "loc.messages.LIB_InvalidPattern": "잘못된 패턴: '%s'", + "loc.messages.LIB_EndpointNotExist": "엔드포인트가 없음: %s", + "loc.messages.LIB_EndpointDataNotExist": "엔드포인트 데이터 매개 변수 %s이(가) 없음: %s", + "loc.messages.LIB_EndpointAuthNotExist": "엔드포인트 인증 데이터가 없음: %s", + "loc.messages.LIB_InvalidEndpointAuth": "잘못된 엔드포인트 인증: %s", + "loc.messages.LIB_InvalidSecureFilesInput": "보안 파일 입력이 잘못됨: %s", + "loc.messages.LIB_PathNotFound": "%s을(를) 찾을 수 없음: %s", + "loc.messages.LIB_PathHasNullByte": "경로에 null 바이트를 포함할 수 없음", + "loc.messages.LIB_OperationFailed": "%s 실패: %s", + "loc.messages.LIB_UseFirstGlobMatch": "여러 작업 영역이 일치합니다. 첫 번째 작업 영역을 사용하세요.", + "loc.messages.LIB_MergeTestResultNotSupported": "이 OSX/Linux용 빌드 에이전트 버전에서 여러 파일의 테스트 결과를 하나의 테스트 실행으로 병합하는 것을 지원하지 않습니다. 각 테스트 결과 파일은 VSO/TFS에서 별도의 테스트 실행으로 게시됩니다.", + "loc.messages.LIB_PlatformNotSupported": "지원되지 않는 플랫폼: %s", + "loc.messages.LIB_CopyFileFailed": "파일을 복사하는 동안 오류가 발생했습니다. 남은 시도 횟수: %s" } \ No newline at end of file diff --git a/node/Strings/resources.resjson/ru-RU/resources.resjson b/node/Strings/resources.resjson/ru-RU/resources.resjson index 2949128f1..d2d2ceb90 100644 --- a/node/Strings/resources.resjson/ru-RU/resources.resjson +++ b/node/Strings/resources.resjson/ru-RU/resources.resjson @@ -1,29 +1,35 @@ -{ - "loc.messages.LIB_UnhandledEx": "Не обработано: %s", - "loc.messages.LIB_FailOnCode": "Код возврата при сбое: %d.", - "loc.messages.LIB_MkdirFailed": "Не удается создать каталог \"%s\". %s", - "loc.messages.LIB_MkdirFailedFileExists": "Не удается создать каталог \"%s\". Существует конфликтующий файл: \"%s\"", - "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Не удалось создать каталог \"%s\". Корневой каталог не существует: \"%s\"", - "loc.messages.LIB_MkdirFailedInvalidShare": "Не удалось создать каталог \"%s\". Не удалось проверить, существует ли каталог \"%s\". Если каталог является файловым ресурсом, убедитесь, что имя ресурса указано правильно, он работает и текущий процесс имеет разрешение на доступ к нему.", - "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", - "loc.messages.LIB_ReturnCode": "Код возврата: %d", - "loc.messages.LIB_ResourceFileNotExist": "Файл ресурсов не существует: %s.", - "loc.messages.LIB_ResourceFileAlreadySet": "Файл ресурсов уже задан: %s.", - "loc.messages.LIB_ResourceFileNotSet": "Файл ресурсов не задан, не удается найти локализованную строку для ключа: %s.", - "loc.messages.LIB_WhichNotFound_Linux": "Не удалось найти исполняемый файл: \"%s\". Убедитесь, что путь к файлу существует и файл можно найти в каталоге, указанном переменной окружения PATH. Также проверьте режим файла, чтобы убедиться, что файл является исполняемым.", - "loc.messages.LIB_WhichNotFound_Win": "Не удалось найти исполняемый файл: \"%s\". Убедитесь, что путь к файлу существует и что файл можно найти в каталоге, указанном переменной окружения PATH. Также убедитесь, что у файла есть допустимое расширение для исполняемого файла.", - "loc.messages.LIB_LocStringNotFound": "Не удается найти локализованную строку для ключа: %s.", - "loc.messages.LIB_ParameterIsRequired": "Не указан %s.", - "loc.messages.LIB_InputRequired": "Требуется ввести данные: %s.", - "loc.messages.LIB_InvalidPattern": "Недопустимый шаблон: \"%s\"", - "loc.messages.LIB_EndpointNotExist": "Конечная точка отсутствует: %s.", - "loc.messages.LIB_EndpointDataNotExist": "Отсутствует параметр %s данных конечной точки: %s", - "loc.messages.LIB_EndpointAuthNotExist": "Отсутствуют данные для проверки подлинности конечной точки: %s", - "loc.messages.LIB_InvalidEndpointAuth": "Недопустимая проверка подлинности конечной точки: %s.", - "loc.messages.LIB_InvalidSecureFilesInput": "Недопустимые входные данные защитного файла: %s", - "loc.messages.LIB_PathNotFound": "Не найден %s: %s.", - "loc.messages.LIB_PathHasNullByte": "Путь не может содержать пустые байты.", - "loc.messages.LIB_OperationFailed": "Сбой %s: %s.", - "loc.messages.LIB_UseFirstGlobMatch": "Несколько рабочих областей совпадает, использована первая рабочая область.", - "loc.messages.LIB_MergeTestResultNotSupported": "Объединение результатов тестов из нескольких файлов в один тестовый запуск не поддерживается в этой версии агента сборки для OSX/Linux. Каждый файл результатов теста будет опубликован в качестве отдельного тестового запуска в VSO/TFS." +{ + "loc.messages.LIB_UnhandledEx": "Не обработано: %s", + "loc.messages.LIB_FailOnCode": "Код возврата при сбое: %d.", + "loc.messages.LIB_MkdirFailed": "Не удается создать каталог \"%s\". %s", + "loc.messages.LIB_MkdirFailedFileExists": "Не удается создать каталог \"%s\". Существует конфликтующий файл: \"%s\"", + "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "Не удалось создать каталог \"%s\". Корневой каталог не существует: \"%s\"", + "loc.messages.LIB_MkdirFailedInvalidShare": "Не удалось создать каталог \"%s\". Не удалось проверить, существует ли каталог \"%s\". Если каталог является файловым ресурсом, убедитесь, что имя ресурса указано правильно, он работает и текущий процесс имеет разрешение на доступ к нему.", + "loc.messages.LIB_MultilineSecret": "Секрет не может быть многострочным", + "loc.messages.LIB_ProcessError": "Произошла ошибка при попытке выполнить процесс \"%s\". Это может означать, что процесс не удалось запустить. Ошибка: %s", + "loc.messages.LIB_ProcessExitCode": "Произошел сбой процесса \"%s\" с кодом выхода %s.", + "loc.messages.LIB_ProcessStderr": "Произошел сбой процесса \"%s\", так как одна или несколько строк были записаны в поток STDERR.", + "loc.messages.LIB_ReturnCode": "Код возврата: %d", + "loc.messages.LIB_ResourceFileNotExist": "Файл ресурсов не существует: %s.", + "loc.messages.LIB_ResourceFileAlreadySet": "Файл ресурсов уже задан: %s.", + "loc.messages.LIB_ResourceFileNotSet": "Файл ресурсов не задан, не удается найти локализованную строку для ключа: %s.", + "loc.messages.LIB_StdioNotClosed": "Потоки STDIO не были закрыты в течение %s с после получения события выхода от процесса \"%s\". Это может свидетельствовать о том, что дочерний процесс унаследовал потоки STDIO и еще не завершил работу.", + "loc.messages.LIB_WhichNotFound_Linux": "Не удалось найти исполняемый файл: \"%s\". Убедитесь, что путь к файлу существует и файл можно найти в каталоге, указанном переменной окружения PATH. Также проверьте режим файла, чтобы убедиться, что файл является исполняемым.", + "loc.messages.LIB_WhichNotFound_Win": "Не удалось найти исполняемый файл: \"%s\". Убедитесь, что путь к файлу существует и что файл можно найти в каталоге, указанном переменной окружения PATH. Также убедитесь, что у файла есть допустимое расширение для исполняемого файла.", + "loc.messages.LIB_LocStringNotFound": "Не удается найти локализованную строку для ключа: %s.", + "loc.messages.LIB_ParameterIsRequired": "Не указан %s.", + "loc.messages.LIB_InputRequired": "Требуется ввести данные: %s.", + "loc.messages.LIB_InvalidPattern": "Недопустимый шаблон: \"%s\"", + "loc.messages.LIB_EndpointNotExist": "Конечная точка отсутствует: %s.", + "loc.messages.LIB_EndpointDataNotExist": "Отсутствует параметр %s данных конечной точки: %s", + "loc.messages.LIB_EndpointAuthNotExist": "Отсутствуют данные для проверки подлинности конечной точки: %s", + "loc.messages.LIB_InvalidEndpointAuth": "Недопустимая проверка подлинности конечной точки: %s.", + "loc.messages.LIB_InvalidSecureFilesInput": "Недопустимые входные данные защитного файла: %s", + "loc.messages.LIB_PathNotFound": "Не найден %s: %s.", + "loc.messages.LIB_PathHasNullByte": "Путь не может содержать пустые байты.", + "loc.messages.LIB_OperationFailed": "Сбой %s: %s.", + "loc.messages.LIB_UseFirstGlobMatch": "Несколько рабочих областей совпадает, использована первая рабочая область.", + "loc.messages.LIB_MergeTestResultNotSupported": "Объединение результатов тестов из нескольких файлов в один тестовый запуск не поддерживается в этой версии агента сборки для OSX/Linux. Каждый файл результатов теста будет опубликован в качестве отдельного тестового запуска в VSO/TFS.", + "loc.messages.LIB_PlatformNotSupported": "Платформа не поддерживается: %s", + "loc.messages.LIB_CopyFileFailed": "Ошибка при копировании файла. Оставшееся число попыток: %s." } \ No newline at end of file diff --git a/node/Strings/resources.resjson/zh-CN/resources.resjson b/node/Strings/resources.resjson/zh-CN/resources.resjson index 6ae8cad0e..b30eb1d81 100644 --- a/node/Strings/resources.resjson/zh-CN/resources.resjson +++ b/node/Strings/resources.resjson/zh-CN/resources.resjson @@ -1,29 +1,35 @@ -{ - "loc.messages.LIB_UnhandledEx": "未处理: %s", - "loc.messages.LIB_FailOnCode": "故障返回代码: %d", - "loc.messages.LIB_MkdirFailed": "无法创建目录“%s”。%s", - "loc.messages.LIB_MkdirFailedFileExists": "无法创建目录“%s”。存在冲突文件:“%s”", - "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "无法创建目录“%s”。根目录不存在:“%s”", - "loc.messages.LIB_MkdirFailedInvalidShare": "无法创建目录“%s”。无法验证“%s”目录是否存在。如果目录是文件共享,请验证共享名称是否正确、共享是否已联机以及当前进程是否有权访问该共享。", - "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", - "loc.messages.LIB_ReturnCode": "返回代码: %d", - "loc.messages.LIB_ResourceFileNotExist": "资源文件不存在: %s", - "loc.messages.LIB_ResourceFileAlreadySet": "资源文件已被设置为 %s", - "loc.messages.LIB_ResourceFileNotSet": "资源文件尚未设置,无法找到关键字的本地字符串: %s", - "loc.messages.LIB_WhichNotFound_Linux": "无法定位可执行文件: \"%s\"。请验证文件路径是否存在或文件是否可在 PATH 环境变量指定的目录内找到。另请检查文件模式以验证文件是否可执行。", - "loc.messages.LIB_WhichNotFound_Win": "无法定位可执行文件: \"%s\"。请验证文件路径是否存在或文件是否可在 PATH 环境变量指定的目录内找到。另请验证该文件是否具有可执行文件的有效扩展名。", - "loc.messages.LIB_LocStringNotFound": "无法找到关键字的本地字符串: %s", - "loc.messages.LIB_ParameterIsRequired": "未提供 %s", - "loc.messages.LIB_InputRequired": "输入必需项: %s", - "loc.messages.LIB_InvalidPattern": "无效的模式: \"%s\"", - "loc.messages.LIB_EndpointNotExist": "终结点不存在: %s", - "loc.messages.LIB_EndpointDataNotExist": "终结点数据参数 %s 不存在: %s", - "loc.messages.LIB_EndpointAuthNotExist": "终结点授权数据不存在: %s", - "loc.messages.LIB_InvalidEndpointAuth": "终结点验证无效: %s", - "loc.messages.LIB_InvalidSecureFilesInput": "无效的安全文件输入: %s", - "loc.messages.LIB_PathNotFound": "找不到 %s: %s", - "loc.messages.LIB_PathHasNullByte": "路径不能包含 null 字节", - "loc.messages.LIB_OperationFailed": "%s 失败: %s", - "loc.messages.LIB_UseFirstGlobMatch": "出现多个工作区匹配时,使用第一个。", - "loc.messages.LIB_MergeTestResultNotSupported": "此版本的 OSX/Linux 生成代理不支持来自某个测试运行的多文件合并测试结果,在 VSO/TFS 中,每个测试结果文件都将作为单独的测试运行进行发布。" +{ + "loc.messages.LIB_UnhandledEx": "未处理: %s", + "loc.messages.LIB_FailOnCode": "故障返回代码: %d", + "loc.messages.LIB_MkdirFailed": "无法创建目录“%s”。%s", + "loc.messages.LIB_MkdirFailedFileExists": "无法创建目录“%s”。存在冲突文件:“%s”", + "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "无法创建目录“%s”。根目录不存在:“%s”", + "loc.messages.LIB_MkdirFailedInvalidShare": "无法创建目录“%s”。无法验证“%s”目录是否存在。如果目录是文件共享,请验证共享名称是否正确、共享是否已联机以及当前进程是否有权访问该共享。", + "loc.messages.LIB_MultilineSecret": "密码不能包含多个行", + "loc.messages.LIB_ProcessError": "尝试执行进程“%s”时出错。这可能表示进程启动失败。错误: %s", + "loc.messages.LIB_ProcessExitCode": "进程“%s”失败,退出代码为 %s", + "loc.messages.LIB_ProcessStderr": "进程“%s”失败,因为已将一行或多行写入 STDERR 流", + "loc.messages.LIB_ReturnCode": "返回代码: %d", + "loc.messages.LIB_ResourceFileNotExist": "资源文件不存在: %s", + "loc.messages.LIB_ResourceFileAlreadySet": "资源文件已被设置为 %s", + "loc.messages.LIB_ResourceFileNotSet": "资源文件尚未设置,无法找到关键字的本地字符串: %s", + "loc.messages.LIB_StdioNotClosed": "STDIO 流在进程“%s”中发生退出事件 %s 秒内未关闭 。这可能表示子进程继承了 STDIO 流且尚未退出。", + "loc.messages.LIB_WhichNotFound_Linux": "无法定位可执行文件: \"%s\"。请验证文件路径是否存在或文件是否可在 PATH 环境变量指定的目录内找到。另请检查文件模式以验证文件是否可执行。", + "loc.messages.LIB_WhichNotFound_Win": "无法定位可执行文件: \"%s\"。请验证文件路径是否存在或文件是否可在 PATH 环境变量指定的目录内找到。另请验证该文件是否具有可执行文件的有效扩展名。", + "loc.messages.LIB_LocStringNotFound": "无法找到关键字的本地字符串: %s", + "loc.messages.LIB_ParameterIsRequired": "未提供 %s", + "loc.messages.LIB_InputRequired": "输入必需项: %s", + "loc.messages.LIB_InvalidPattern": "无效的模式: \"%s\"", + "loc.messages.LIB_EndpointNotExist": "终结点不存在: %s", + "loc.messages.LIB_EndpointDataNotExist": "终结点数据参数 %s 不存在: %s", + "loc.messages.LIB_EndpointAuthNotExist": "终结点授权数据不存在: %s", + "loc.messages.LIB_InvalidEndpointAuth": "终结点验证无效: %s", + "loc.messages.LIB_InvalidSecureFilesInput": "无效的安全文件输入: %s", + "loc.messages.LIB_PathNotFound": "找不到 %s: %s", + "loc.messages.LIB_PathHasNullByte": "路径不能包含 null 字节", + "loc.messages.LIB_OperationFailed": "%s 失败: %s", + "loc.messages.LIB_UseFirstGlobMatch": "出现多个工作区匹配时,使用第一个。", + "loc.messages.LIB_MergeTestResultNotSupported": "此版本的 OSX/Linux 生成代理不支持来自某个测试运行的多文件合并测试结果,在 VSO/TFS 中,每个测试结果文件都将作为单独的测试运行进行发布。", + "loc.messages.LIB_PlatformNotSupported": "平台不受支持: %s", + "loc.messages.LIB_CopyFileFailed": "复制文件时出错。剩余尝试次数: %s" } \ No newline at end of file diff --git a/node/Strings/resources.resjson/zh-TW/resources.resjson b/node/Strings/resources.resjson/zh-TW/resources.resjson index bd12064ec..ecf4ee743 100644 --- a/node/Strings/resources.resjson/zh-TW/resources.resjson +++ b/node/Strings/resources.resjson/zh-TW/resources.resjson @@ -1,29 +1,35 @@ -{ - "loc.messages.LIB_UnhandledEx": "未經處理: %s", - "loc.messages.LIB_FailOnCode": "傳回程式碼失敗: %d", - "loc.messages.LIB_MkdirFailed": "無法建立目錄 '%s'。%s", - "loc.messages.LIB_MkdirFailedFileExists": "無法建立目錄 '%s'。存在衝突的檔案: '%s'", - "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "無法建立目錄 '%s'。根目錄不存在: '%s'", - "loc.messages.LIB_MkdirFailedInvalidShare": "無法建立目錄 '%s'。無法驗證目錄是否存在: '%s'。如果目錄是檔案共用,請驗證共用名稱正確、共用在線上,而且目前的流程具有存取共用的權限。", - "loc.messages.LIB_MultilineSecret": "Secrets cannot contain multiple lines", - "loc.messages.LIB_ReturnCode": "傳回程式碼: %d", - "loc.messages.LIB_ResourceFileNotExist": "資源檔案不存在: %s", - "loc.messages.LIB_ResourceFileAlreadySet": "資源檔案已設定至: %s", - "loc.messages.LIB_ResourceFileNotSet": "尚未設定資源檔案,找不到索引鍵的 loc 字串: %s", - "loc.messages.LIB_WhichNotFound_Linux": "找不到可執行檔: '%s'。請確認檔案路徑存在,或檔案可以在 PATH 環境變數指定的目錄中找到。另請檢查檔案模式,確認檔案可以執行。", - "loc.messages.LIB_WhichNotFound_Win": "找不到可執行檔: '%s'。請確認檔案路徑存在,或檔案可以在 PATH 環境變數指定的目錄中找到。另請確認檔案具備有效的可執行檔副檔名。", - "loc.messages.LIB_LocStringNotFound": "找不到索引鍵的 loc 字串: %s", - "loc.messages.LIB_ParameterIsRequired": "未提供 %s", - "loc.messages.LIB_InputRequired": "需要輸入內容: %s", - "loc.messages.LIB_InvalidPattern": "模式無效: '%s'", - "loc.messages.LIB_EndpointNotExist": "端點不存在: %s", - "loc.messages.LIB_EndpointDataNotExist": "端點資料參數 %s 不存在: %s", - "loc.messages.LIB_EndpointAuthNotExist": "端點驗證資料不存在: %s", - "loc.messages.LIB_InvalidEndpointAuth": "端點驗證無效: %s", - "loc.messages.LIB_InvalidSecureFilesInput": "安全檔案輸入無效: %s", - "loc.messages.LIB_PathNotFound": "找不到 %s: %s", - "loc.messages.LIB_PathHasNullByte": "路徑不能包含 null 位元組", - "loc.messages.LIB_OperationFailed": "%s 失敗: %s", - "loc.messages.LIB_UseFirstGlobMatch": "多個工作區相符。請先使用。", - "loc.messages.LIB_MergeTestResultNotSupported": "OSX/Linux 的此版本組建代理程式不支援將多個檔案的測試結果合併至單一測試回合,每個測試結果檔案將作為個別測試回合在 VSO/TFS 中發行。" +{ + "loc.messages.LIB_UnhandledEx": "未經處理: %s", + "loc.messages.LIB_FailOnCode": "傳回程式碼失敗: %d", + "loc.messages.LIB_MkdirFailed": "無法建立目錄 '%s'。%s", + "loc.messages.LIB_MkdirFailedFileExists": "無法建立目錄 '%s'。存在衝突的檔案: '%s'", + "loc.messages.LIB_MkdirFailedInvalidDriveRoot": "無法建立目錄 '%s'。根目錄不存在: '%s'", + "loc.messages.LIB_MkdirFailedInvalidShare": "無法建立目錄 '%s'。無法驗證目錄是否存在: '%s'。如果目錄是檔案共用,請驗證共用名稱正確、共用在線上,而且目前的流程具有存取共用的權限。", + "loc.messages.LIB_MultilineSecret": "祕密不得包含多個行", + "loc.messages.LIB_ProcessError": "嘗試執行處理序 '%s' 時發生錯誤。這可能表示處理序無法啟動。錯誤: %s", + "loc.messages.LIB_ProcessExitCode": "處理序 '%s' 失敗,結束代碼為 %s", + "loc.messages.LIB_ProcessStderr": "因為 STDERR 資料流中寫入了一或多行程式碼,所以處理序 '%s' 失敗", + "loc.messages.LIB_ReturnCode": "傳回程式碼: %d", + "loc.messages.LIB_ResourceFileNotExist": "資源檔案不存在: %s", + "loc.messages.LIB_ResourceFileAlreadySet": "資源檔案已設定至: %s", + "loc.messages.LIB_ResourceFileNotSet": "尚未設定資源檔案,找不到索引鍵的 loc 字串: %s", + "loc.messages.LIB_StdioNotClosed": "STDIO 資料流未在 %s 秒內關閉 (從處理序 '%s' 結束事件發生後算起)。這可能表示子處理序繼承了 STDIO 資料流且尚未結束。", + "loc.messages.LIB_WhichNotFound_Linux": "找不到可執行檔: '%s'。請確認檔案路徑存在,或檔案可以在 PATH 環境變數指定的目錄中找到。另請檢查檔案模式,確認檔案可以執行。", + "loc.messages.LIB_WhichNotFound_Win": "找不到可執行檔: '%s'。請確認檔案路徑存在,或檔案可以在 PATH 環境變數指定的目錄中找到。另請確認檔案具備有效的可執行檔副檔名。", + "loc.messages.LIB_LocStringNotFound": "找不到索引鍵的 loc 字串: %s", + "loc.messages.LIB_ParameterIsRequired": "未提供 %s", + "loc.messages.LIB_InputRequired": "需要輸入內容: %s", + "loc.messages.LIB_InvalidPattern": "模式無效: '%s'", + "loc.messages.LIB_EndpointNotExist": "端點不存在: %s", + "loc.messages.LIB_EndpointDataNotExist": "端點資料參數 %s 不存在: %s", + "loc.messages.LIB_EndpointAuthNotExist": "端點驗證資料不存在: %s", + "loc.messages.LIB_InvalidEndpointAuth": "端點驗證無效: %s", + "loc.messages.LIB_InvalidSecureFilesInput": "安全檔案輸入無效: %s", + "loc.messages.LIB_PathNotFound": "找不到 %s: %s", + "loc.messages.LIB_PathHasNullByte": "路徑不能包含 null 位元組", + "loc.messages.LIB_OperationFailed": "%s 失敗: %s", + "loc.messages.LIB_UseFirstGlobMatch": "多個工作區相符。請先使用。", + "loc.messages.LIB_MergeTestResultNotSupported": "OSX/Linux 的此版本組建代理程式不支援將多個檔案的測試結果合併至單一測試回合,每個測試結果檔案將作為個別測試回合在 VSO/TFS 中發行。", + "loc.messages.LIB_PlatformNotSupported": "不支援的平台: %s", + "loc.messages.LIB_CopyFileFailed": "複製檔案時發生錯誤。剩餘嘗試次數: %s" } \ No newline at end of file diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson index 77bea5360..6aeb1dfe7 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson @@ -1,18 +1,18 @@ -{ - "loc.messages.PSLIB_AgentVersion0Required": "È richiesta la versione dell'agente {0} o superiore.", - "loc.messages.PSLIB_ContainerPathNotFound0": "Percorso del contenitore non trovato: '{0}'", - "loc.messages.PSLIB_EndpointAuth0": "Credenziali dell'endpoint servizio '{0}'", - "loc.messages.PSLIB_EndpointUrl0": "URL dell'endpoint servizio '{0}'", - "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "L'enumerazione delle sottodirectory per il percorso '{0}' non è riuscita", - "loc.messages.PSLIB_FileNotFound0": "File non trovato: '{0}'", - "loc.messages.PSLIB_Input0": "Input di '{0}'", - "loc.messages.PSLIB_InvalidPattern0": "Criterio non valido: '{0}'", - "loc.messages.PSLIB_LeafPathNotFound0": "Percorso foglia non trovato: '{0}'", - "loc.messages.PSLIB_PathLengthNotReturnedFor0": "La normalizzazione o l'espansione del percorso non è riuscita. Il sottosistema Kernel32 non ha restituito la lunghezza del percorso per '{0}'", - "loc.messages.PSLIB_PathNotFound0": "Percorso non trovato: '{0}'", - "loc.messages.PSLIB_Process0ExitedWithCode1": "Il processo '{0}' è stato terminato ed è stato restituito il codice '{1}'.", - "loc.messages.PSLIB_Required0": "Obbligatorio: {0}", - "loc.messages.PSLIB_StringFormatFailed": "Errore nel formato della stringa.", - "loc.messages.PSLIB_StringResourceKeyNotFound0": "La chiave della risorsa stringa non è stata trovata: '{0}'", - "loc.messages.PSLIB_TaskVariable0": "Variabile dell'attività '{0}'" +{ + "loc.messages.PSLIB_AgentVersion0Required": "È richiesta la versione dell'agente {0} o superiore.", + "loc.messages.PSLIB_ContainerPathNotFound0": "Percorso del contenitore non trovato: '{0}'", + "loc.messages.PSLIB_EndpointAuth0": "Credenziali dell'endpoint servizio '{0}'", + "loc.messages.PSLIB_EndpointUrl0": "URL dell'endpoint servizio '{0}'", + "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "L'enumerazione delle sottodirectory per il percorso '{0}' non è riuscita", + "loc.messages.PSLIB_FileNotFound0": "File non trovato: '{0}'", + "loc.messages.PSLIB_Input0": "Input di '{0}'", + "loc.messages.PSLIB_InvalidPattern0": "Criterio non valido: '{0}'", + "loc.messages.PSLIB_LeafPathNotFound0": "Percorso foglia non trovato: '{0}'", + "loc.messages.PSLIB_PathLengthNotReturnedFor0": "La normalizzazione o l'espansione del percorso non è riuscita. Il sottosistema Kernel32 non ha restituito la lunghezza del percorso per '{0}'", + "loc.messages.PSLIB_PathNotFound0": "Percorso non trovato: '{0}'", + "loc.messages.PSLIB_Process0ExitedWithCode1": "Il processo '{0}' è stato terminato ed è stato restituito il codice '{1}'.", + "loc.messages.PSLIB_Required0": "Obbligatorio: {0}", + "loc.messages.PSLIB_StringFormatFailed": "Errore nel formato della stringa.", + "loc.messages.PSLIB_StringResourceKeyNotFound0": "La chiave della risorsa stringa non è stata trovata: '{0}'", + "loc.messages.PSLIB_TaskVariable0": "Variabile dell'attività '{0}'" } \ No newline at end of file diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson index 17e0d28a8..58fa35393 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson @@ -1,18 +1,18 @@ -{ - "loc.messages.PSLIB_AgentVersion0Required": "에이전트 버전 {0} 이상이 필요합니다.", - "loc.messages.PSLIB_ContainerPathNotFound0": "컨테이너 경로를 찾을 수 없음: '{0}'", - "loc.messages.PSLIB_EndpointAuth0": "'{0}' 서비스 끝점 자격 증명", - "loc.messages.PSLIB_EndpointUrl0": "'{0}' 서비스 끝점 URL", - "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "경로에 대해 하위 디렉터리를 열거하지 못함: '{0}'", - "loc.messages.PSLIB_FileNotFound0": "{0} 파일을 찾을 수 없습니다.", - "loc.messages.PSLIB_Input0": "'{0}' 입력", - "loc.messages.PSLIB_InvalidPattern0": "잘못된 패턴: '{0}'", - "loc.messages.PSLIB_LeafPathNotFound0": "Leaf 경로를 찾을 수 없음: '{0}'", - "loc.messages.PSLIB_PathLengthNotReturnedFor0": "경로 정규화/확장에 실패했습니다. 다음에 대해 Kernel32 subsystem에서 경로 길이를 반환하지 않음: '{0}'", - "loc.messages.PSLIB_PathNotFound0": "경로를 찾을 수 없음: '{0}'", - "loc.messages.PSLIB_Process0ExitedWithCode1": "'{1}' 코드로 '{0}' 프로세스가 종료되었습니다.", - "loc.messages.PSLIB_Required0": "필수: {0}", - "loc.messages.PSLIB_StringFormatFailed": "문자열을 포맷하지 못했습니다.", - "loc.messages.PSLIB_StringResourceKeyNotFound0": "문자열 리소스 키를 찾을 수 없음: '{0}'", - "loc.messages.PSLIB_TaskVariable0": "'{0}' 작업 변수" +{ + "loc.messages.PSLIB_AgentVersion0Required": "에이전트 버전 {0} 이상이 필요합니다.", + "loc.messages.PSLIB_ContainerPathNotFound0": "컨테이너 경로를 찾을 수 없음: '{0}'", + "loc.messages.PSLIB_EndpointAuth0": "'{0}' 서비스 엔드포인트 자격 증명", + "loc.messages.PSLIB_EndpointUrl0": "'{0}' 서비스 엔드포인트 URL", + "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "경로에 대해 하위 디렉터리를 열거하지 못함: '{0}'", + "loc.messages.PSLIB_FileNotFound0": "{0} 파일을 찾을 수 없습니다.", + "loc.messages.PSLIB_Input0": "'{0}' 입력", + "loc.messages.PSLIB_InvalidPattern0": "잘못된 패턴: '{0}'", + "loc.messages.PSLIB_LeafPathNotFound0": "Leaf 경로를 찾을 수 없음: '{0}'", + "loc.messages.PSLIB_PathLengthNotReturnedFor0": "경로 정규화/확장에 실패했습니다. 다음에 대해 Kernel32 subsystem에서 경로 길이를 반환하지 않음: '{0}'", + "loc.messages.PSLIB_PathNotFound0": "경로를 찾을 수 없음: '{0}'", + "loc.messages.PSLIB_Process0ExitedWithCode1": "'{1}' 코드로 '{0}' 프로세스가 종료되었습니다.", + "loc.messages.PSLIB_Required0": "필수: {0}", + "loc.messages.PSLIB_StringFormatFailed": "문자열을 포맷하지 못했습니다.", + "loc.messages.PSLIB_StringResourceKeyNotFound0": "문자열 리소스 키를 찾을 수 없음: '{0}'", + "loc.messages.PSLIB_TaskVariable0": "'{0}' 작업 변수" } \ No newline at end of file diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson index b9c5a2709..335b6fbab 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson @@ -1,18 +1,18 @@ -{ - "loc.messages.PSLIB_AgentVersion0Required": "Требуется версия агента {0} или более поздняя.", - "loc.messages.PSLIB_ContainerPathNotFound0": "Путь к контейнеру не найден: \"{0}\".", - "loc.messages.PSLIB_EndpointAuth0": "Учетные данные конечной точки службы \"{0}\"", - "loc.messages.PSLIB_EndpointUrl0": "URL-адрес конечной точки службы \"{0}\"", - "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "Сбой перечисления подкаталогов для пути: \"{0}\".", - "loc.messages.PSLIB_FileNotFound0": "Файл не найден: \"{0}\".", - "loc.messages.PSLIB_Input0": "Входные данные \"{0}\".", - "loc.messages.PSLIB_InvalidPattern0": "Недопустимый шаблон: \"{0}\".", - "loc.messages.PSLIB_LeafPathNotFound0": "Путь к конечному объекту не найден: \"{0}\".", - "loc.messages.PSLIB_PathLengthNotReturnedFor0": "Сбой нормализации и расширения пути. Длина пути не была возвращена подсистемой Kernel32 для: \"{0}\".", - "loc.messages.PSLIB_PathNotFound0": "Путь не найден: \"{0}\".", - "loc.messages.PSLIB_Process0ExitedWithCode1": "Процесс \"{0}\" завершил работу с кодом \"{1}\".", - "loc.messages.PSLIB_Required0": "Требуется: {0}", - "loc.messages.PSLIB_StringFormatFailed": "Сбой формата строки.", - "loc.messages.PSLIB_StringResourceKeyNotFound0": "Ключ ресурса строки не найден: \"{0}\".", - "loc.messages.PSLIB_TaskVariable0": "Переменная задачи \"{0}\"" +{ + "loc.messages.PSLIB_AgentVersion0Required": "Требуется версия агента {0} или более поздняя.", + "loc.messages.PSLIB_ContainerPathNotFound0": "Путь к контейнеру не найден: \"{0}\".", + "loc.messages.PSLIB_EndpointAuth0": "Учетные данные конечной точки службы \"{0}\"", + "loc.messages.PSLIB_EndpointUrl0": "URL-адрес конечной точки службы \"{0}\"", + "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "Сбой перечисления подкаталогов для пути: \"{0}\".", + "loc.messages.PSLIB_FileNotFound0": "Файл не найден: \"{0}\"", + "loc.messages.PSLIB_Input0": "Входные данные \"{0}\".", + "loc.messages.PSLIB_InvalidPattern0": "Недопустимый шаблон: \"{0}\".", + "loc.messages.PSLIB_LeafPathNotFound0": "Путь к конечному объекту не найден: \"{0}\".", + "loc.messages.PSLIB_PathLengthNotReturnedFor0": "Сбой нормализации и расширения пути. Длина пути не была возвращена подсистемой Kernel32 для: \"{0}\".", + "loc.messages.PSLIB_PathNotFound0": "Путь не найден: \"{0}\".", + "loc.messages.PSLIB_Process0ExitedWithCode1": "Процесс \"{0}\" завершил работу с кодом \"{1}\".", + "loc.messages.PSLIB_Required0": "Требуется: {0}", + "loc.messages.PSLIB_StringFormatFailed": "Сбой формата строки.", + "loc.messages.PSLIB_StringResourceKeyNotFound0": "Ключ ресурса строки не найден: \"{0}\".", + "loc.messages.PSLIB_TaskVariable0": "Переменная задачи \"{0}\"" } \ No newline at end of file diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson index 49d824b50..1d333de7d 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson @@ -1,18 +1,18 @@ -{ - "loc.messages.PSLIB_AgentVersion0Required": "需要代理版本 {0} 或更高版本。", - "loc.messages.PSLIB_ContainerPathNotFound0": "找不到容器路径:“{0}”", - "loc.messages.PSLIB_EndpointAuth0": "“{0}”服务终结点凭据", - "loc.messages.PSLIB_EndpointUrl0": "“{0}”服务终结点 URL", - "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "枚举路径的子目录失败:“{0}”", - "loc.messages.PSLIB_FileNotFound0": "找不到文件: {0}。", - "loc.messages.PSLIB_Input0": "“{0}”输入", - "loc.messages.PSLIB_InvalidPattern0": "无效的模式:“{0}”", - "loc.messages.PSLIB_LeafPathNotFound0": "找不到叶路径:“{0}”", - "loc.messages.PSLIB_PathLengthNotReturnedFor0": "路径规范化/扩展失败。路径长度不是由“{0}”的 Kernel32 子系统返回的", - "loc.messages.PSLIB_PathNotFound0": "找不到路径:“{0}”", - "loc.messages.PSLIB_Process0ExitedWithCode1": "过程“{0}”已退出,代码为“{1}”。", - "loc.messages.PSLIB_Required0": "必需: {0}", - "loc.messages.PSLIB_StringFormatFailed": "字符串格式无效。", - "loc.messages.PSLIB_StringResourceKeyNotFound0": "找不到字符串资源关键字:“{0}”", - "loc.messages.PSLIB_TaskVariable0": "“{0}”任务变量" +{ + "loc.messages.PSLIB_AgentVersion0Required": "需要代理版本 {0} 或更高版本。", + "loc.messages.PSLIB_ContainerPathNotFound0": "找不到容器路径:“{0}”", + "loc.messages.PSLIB_EndpointAuth0": "“{0}”服务终结点凭据", + "loc.messages.PSLIB_EndpointUrl0": "“{0}”服务终结点 URL", + "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "枚举路径的子目录失败:“{0}”", + "loc.messages.PSLIB_FileNotFound0": "找不到文件: {0}。", + "loc.messages.PSLIB_Input0": "“{0}”输入", + "loc.messages.PSLIB_InvalidPattern0": "无效的模式:“{0}”", + "loc.messages.PSLIB_LeafPathNotFound0": "找不到叶路径:“{0}”", + "loc.messages.PSLIB_PathLengthNotReturnedFor0": "路径规范化/扩展失败。路径长度不是由“{0}”的 Kernel32 子系统返回的", + "loc.messages.PSLIB_PathNotFound0": "找不到路径:“{0}”", + "loc.messages.PSLIB_Process0ExitedWithCode1": "过程“{0}”已退出,代码为“{1}”。", + "loc.messages.PSLIB_Required0": "必需: {0}", + "loc.messages.PSLIB_StringFormatFailed": "字符串格式无效。", + "loc.messages.PSLIB_StringResourceKeyNotFound0": "找不到字符串资源关键字:“{0}”", + "loc.messages.PSLIB_TaskVariable0": "“{0}”任务变量" } \ No newline at end of file diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson index 7cbf22e17..512509bdd 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson @@ -1,18 +1,18 @@ -{ - "loc.messages.PSLIB_AgentVersion0Required": "需要代理程式版本 {0} 或更新的版本。", - "loc.messages.PSLIB_ContainerPathNotFound0": "找不到容器路徑: '{0}'", - "loc.messages.PSLIB_EndpointAuth0": "'{0}' 服務端點認證", - "loc.messages.PSLIB_EndpointUrl0": "'{0}' 服務端點 URL", - "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "為路徑列舉子目錄失敗: '{0}'", - "loc.messages.PSLIB_FileNotFound0": "找不到檔案: '{0}'", - "loc.messages.PSLIB_Input0": "'{0}' 輸入", - "loc.messages.PSLIB_InvalidPattern0": "模式無效: '{0}'", - "loc.messages.PSLIB_LeafPathNotFound0": "找不到分葉路徑: '{0}'", - "loc.messages.PSLIB_PathLengthNotReturnedFor0": "路徑正規化/展開失敗。Kernel32 子系統未傳回 '{0}' 的路徑長度", - "loc.messages.PSLIB_PathNotFound0": "找不到路徑: '{0}'", - "loc.messages.PSLIB_Process0ExitedWithCode1": "處理序 '{0}' 以返回碼 '{1}' 結束。", - "loc.messages.PSLIB_Required0": "必要項: {0}", - "loc.messages.PSLIB_StringFormatFailed": "字串格式失敗。", - "loc.messages.PSLIB_StringResourceKeyNotFound0": "找不到字串資源索引鍵: '{0}'", - "loc.messages.PSLIB_TaskVariable0": "'{0}' 工作變數" +{ + "loc.messages.PSLIB_AgentVersion0Required": "需要代理程式版本 {0} 或更新的版本。", + "loc.messages.PSLIB_ContainerPathNotFound0": "找不到容器路徑: '{0}'", + "loc.messages.PSLIB_EndpointAuth0": "'{0}' 服務端點認證", + "loc.messages.PSLIB_EndpointUrl0": "'{0}' 服務端點 URL", + "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "為路徑列舉子目錄失敗: '{0}'", + "loc.messages.PSLIB_FileNotFound0": "找不到檔案: '{0}'", + "loc.messages.PSLIB_Input0": "'{0}' 輸入", + "loc.messages.PSLIB_InvalidPattern0": "模式無效: '{0}'", + "loc.messages.PSLIB_LeafPathNotFound0": "找不到分葉路徑: '{0}'", + "loc.messages.PSLIB_PathLengthNotReturnedFor0": "路徑正規化/展開失敗。Kernel32 子系統未傳回 '{0}' 的路徑長度", + "loc.messages.PSLIB_PathNotFound0": "找不到路徑: '{0}'", + "loc.messages.PSLIB_Process0ExitedWithCode1": "處理序 '{0}' 以返回碼 '{1}' 結束。", + "loc.messages.PSLIB_Required0": "必要項: {0}", + "loc.messages.PSLIB_StringFormatFailed": "字串格式失敗。", + "loc.messages.PSLIB_StringResourceKeyNotFound0": "找不到字串資源索引鍵: '{0}'", + "loc.messages.PSLIB_TaskVariable0": "'{0}' 工作變數" } \ No newline at end of file From bbec8ff7dc74d50a3aecd643fd1e899f3baf3cb6 Mon Sep 17 00:00:00 2001 From: Anatoly Bolshakov Date: Mon, 7 Jun 2021 17:23:21 +0300 Subject: [PATCH 208/259] Fixed issue with shell.cp retry (#769) --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index a4a57a9f8..48864b6f4 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.2", + "version": "3.1.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 489a867a0..e9a5dfa64 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.2", + "version": "3.1.3", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index 9726efa8b..c826bfb05 100644 --- a/node/task.ts +++ b/node/task.ts @@ -742,13 +742,14 @@ export function ls(options: string, paths: string[]): string[] { */ export function cp(source: string, dest: string, options?: string, continueOnError?: boolean, retryCount: number = 0): void { while (retryCount >= 0) { - if (options) { - shell.cp(options, source, dest); - } - else { - shell.cp(source, dest); - } try { + if (options) { + shell.cp(options, source, dest); + } + else { + shell.cp(source, dest); + } + _checkShell('cp', false); break; } catch (e) { From 08eba600f1efd958123e7366644bfe2498450e0b Mon Sep 17 00:00:00 2001 From: DaniilShmelev <72494759+DaniilShmelev@users.noreply.github.com> Date: Tue, 22 Jun 2021 16:28:50 +0300 Subject: [PATCH 209/259] bump package version (#764) --- node/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package.json b/node/package.json index e9a5dfa64..55ba1a131 100644 --- a/node/package.json +++ b/node/package.json @@ -32,8 +32,8 @@ "q": "^1.5.1", "semver": "^5.1.0", "shelljs": "^0.8.4", - "uuid": "^3.0.1", - "sync-request": "6.1.0" + "sync-request": "6.1.0", + "uuid": "^3.0.1" }, "devDependencies": { "@types/minimatch": "3.0.3", From 68bae9ac864fdcbb624a9764f68970d4cfa82352 Mon Sep 17 00:00:00 2001 From: AnnaOpareva <84513972+AnnaOpareva@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:24:30 +0300 Subject: [PATCH 210/259] fix bug with escaped quotes in toolrunner _argStringToArray (#771) * fix escaped quotes in toolrunner * add unit test for escaped quotes * fix output * fix console * format code Co-authored-by: Anna Opareva --- node/test/toolrunnertests.ts | 10 +++++++++- node/toolrunner.ts | 9 ++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index ac4aff1be..4e623bc5c 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -1229,7 +1229,15 @@ describe('Toolrunner Tests', function () { assert.equal((node as any).args.toString(), '--path,/bin/working folder1', 'should be --path /bin/working folder1'); done(); }) - + it('handles escaped quotes', function (done) { + this.timeout(10000); + var node = tl.tool(tl.which('node', true)); + node.line('-TEST="escaped\\\"quotes" -x'); + node.arg('-y'); + assert.equal((node as any).args.length, 3, 'should have 3 args'); + assert.equal((node as any).args.toString(), '-TEST=escaped"quotes,-x,-y', 'should be -TEST=escaped"quotes,-x,-y'); + done(); + }) if (process.platform != 'win32') { it('exec prints [command] (OSX/Linux)', function (done) { this.timeout(10000); diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 8f2e569ad..843dfe2b7 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -96,10 +96,13 @@ export class ToolRunner extends events.EventEmitter { var append = function (c: string) { // we only escape double quotes. - if (escaped && c !== '"') { - arg += '\\'; + if (escaped) { + if (c !== '"') { + arg += '\\'; + } else { + arg.slice(0, -1); + } } - arg += c; escaped = false; } From 078c422e326141ca3b03ef8a26315c985ff3db3c Mon Sep 17 00:00:00 2001 From: AnnaOpareva <84513972+AnnaOpareva@users.noreply.github.com> Date: Wed, 30 Jun 2021 12:11:00 +0300 Subject: [PATCH 211/259] add patch version for PR #771 (#772) * fix escaped quotes in toolrunner * add unit test for escaped quotes * fix output * fix console * format code * new patch version Co-authored-by: Anna Opareva --- node/package-lock.json | 2 +- node/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 48864b6f4..7dda9bcf1 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.3", + "version": "3.1.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 55ba1a131..de8329974 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.3", + "version": "3.1.4", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 80eaeb9ab15421bcffa060bdb8c03b3f61175968 Mon Sep 17 00:00:00 2001 From: Denis Rumyantsev <61472718+DenisRumyantsev@users.noreply.github.com> Date: Fri, 16 Jul 2021 14:48:31 +0300 Subject: [PATCH 212/259] Skipping the file if it was not found (#774) Implemented checking for the presence of a file and skipping it if it does not exist --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 7dda9bcf1..47935c113 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.4", + "version": "3.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index de8329974..1339ac727 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.4", + "version": "3.1.5", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index c826bfb05..2a508c8fa 100644 --- a/node/task.ts +++ b/node/task.ts @@ -857,6 +857,10 @@ 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 + if (!fs.existsSync(item.path)) { + debug(`File "${item.path}" seems to be removed during find operation execution - so skipping it.`); + continue; + } result.push(item.path); // stat the item. the stat info is used further below to determine whether to traverse deeper From 13333c94dd9eb74079376f840d42649b506eb583 Mon Sep 17 00:00:00 2001 From: Denis Rumyantsev <61472718+DenisRumyantsev@users.noreply.github.com> Date: Mon, 19 Jul 2021 12:23:08 +0300 Subject: [PATCH 213/259] Revert "Skipping the file if it was not found (#774)" (#778) This reverts commit 80eaeb9ab15421bcffa060bdb8c03b3f61175968. --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 47935c113..7dda9bcf1 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.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 1339ac727..de8329974 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.4", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index 2a508c8fa..c826bfb05 100644 --- a/node/task.ts +++ b/node/task.ts @@ -857,10 +857,6 @@ 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 - if (!fs.existsSync(item.path)) { - debug(`File "${item.path}" seems to be removed during find operation execution - so skipping it.`); - continue; - } result.push(item.path); // stat the item. the stat info is used further below to determine whether to traverse deeper From 77c834ef3e62f9a43f28a2ef74bd517146a9da27 Mon Sep 17 00:00:00 2001 From: Denis Rumyantsev <61472718+DenisRumyantsev@users.noreply.github.com> Date: Tue, 20 Jul 2021 12:25:44 +0300 Subject: [PATCH 214/259] Retry in the find function for fs.realpathSync method for UNC-paths (#777) Sometimes there are spontaneous issues when working with unc-paths, so retries have been added for them. --- node/internal.ts | 10 +++++++ node/mock-task.ts | 4 +++ node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 54 ++++++++++++++++++++++++++++++++++- node/test/isuncpathtests.ts | 41 ++++++++++++++++++++++++++ node/test/retrytests.ts | 57 +++++++++++++++++++++++++++++++++++++ node/test/tsconfig.json | 6 ++-- 8 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 node/test/isuncpathtests.ts create mode 100644 node/test/retrytests.ts diff --git a/node/internal.ts b/node/internal.ts index 70192ed06..964187b6d 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -768,6 +768,16 @@ export function _loadData(): void { // Internal path helpers. //-------------------------------------------------------------------------------- +/** + * Defines if path is unc-path. + * + * @param path a path to a file. + * @returns true if path starts with double backslash, otherwise returns false. + */ + export function _isUncPath(path: string) { + return /^\\\\[^\\]/.test(path); +} + export function _ensureRooted(root: string, p: string) { if (!root) { throw new Error('ensureRooted() parameter "root" cannot be empty'); diff --git a/node/mock-task.ts b/node/mock-task.ts index 3a30bf464..52af95357 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -287,6 +287,10 @@ export function cp(source: string, dest: string): void { module.exports.debug('copying ' + source + ' to ' + dest); } +export function retry(func: Function, args: any[], retryOptions: task.RetryOptions): any { + module.exports.debug(`trying to execute ${func?.name}(${args.toString()}) with ${retryOptions.retryCount} retries`); +} + export function find(findPath: string): string[] { return mock.getResponse('find', findPath, module.exports.debug); } diff --git a/node/package-lock.json b/node/package-lock.json index 7dda9bcf1..47935c113 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.4", + "version": "3.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index de8329974..1339ac727 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.4", + "version": "3.1.5", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index c826bfb05..9646d9302 100644 --- a/node/task.ts +++ b/node/task.ts @@ -812,6 +812,52 @@ export interface FindOptions { followSymbolicLinks: boolean; } +/** + * Interface for RetryOptions + * + * Contains "continueOnError" and "retryCount" options. + */ +export interface RetryOptions { + + /** + * If true, code still continues to execute when all retries failed. + */ + continueOnError: boolean, + + /** + * Number of retries. + */ + retryCount: number +} + +/** + * Tries to execute a function a specified number of times. + * + * @param func a function to be executed. + * @param args executed function arguments array. + * @param retryOptions optional. Defaults to { continueOnError: false, retryCount: 0 }. + * @returns the same as the usual function. + */ +export function retry(func: Function, args: any[], retryOptions: RetryOptions = { continueOnError: false, retryCount: 0 }): any { + while (retryOptions.retryCount >= 0) { + try { + return func(...args); + } catch (e) { + if (retryOptions.retryCount <= 0) { + if (retryOptions.continueOnError) { + warning(e); + break; + } else { + throw e; + } + } else { + debug(`Attempt to execute function "${func?.name}" failed, retries left: ${retryOptions.retryCount}`); + retryOptions.retryCount--; + } + } + } +} + /** * Recursively finds all paths a given path. Returns an array of paths. * @@ -907,7 +953,13 @@ export function find(findPath: string, options?: FindOptions): string[] { if (options.followSymbolicLinks) { // get the realpath - let realPath: string = fs.realpathSync(item.path); + let realPath: string; + if (im._isUncPath(item.path)) { + // Sometimes there are spontaneous issues when working with unc-paths, so retries have been added for them. + realPath = retry(fs.realpathSync, [item.path], { continueOnError: false, retryCount: 5 }); + } else { + realPath = fs.realpathSync(item.path); + } // fixup the traversal chain to match the item level while (traversalChain.length >= item.level) { diff --git a/node/test/isuncpathtests.ts b/node/test/isuncpathtests.ts new file mode 100644 index 000000000..9beb1cbe3 --- /dev/null +++ b/node/test/isuncpathtests.ts @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +import assert = require('assert'); +import * as im from '../_build/internal'; +import testutil = require('./testutil'); + +describe('Is UNC-path Tests', function () { + + before(function (done) { + try { + testutil.initialize(); + } catch (err) { + assert.fail('Failed to load task lib: ' + err.message); + } + done(); + }); + + after(function () { + }); + + it('checks if path is unc path', (done: MochaDone) => { + this.timeout(1000); + + const paths = [ + { inputPath: '\\server\\path\\to\\file', isUNC: false }, + { inputPath: '\\\\server\\path\\to\\file', isUNC: true }, + { inputPath: '\\\\\\server\\path\\to\\file', isUNC: false }, + { inputPath: '!@#$%^&*()_+', isUNC: false }, + { inputPath: '\\\\\\\\\\\\', isUNC: false }, + { inputPath: '1q2w3e4r5t6y', isUNC: false }, + { inputPath: '', isUNC: false } + ]; + + for (let path of paths) { + assert.deepEqual(im._isUncPath(path.inputPath), path.isUNC); + } + + done(); + }); +}); diff --git a/node/test/retrytests.ts b/node/test/retrytests.ts new file mode 100644 index 000000000..3f4831b13 --- /dev/null +++ b/node/test/retrytests.ts @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +import assert = require('assert'); +import * as tl from '../_build/task'; +import testutil = require('./testutil'); + +describe('Retry Tests', function () { + + before(function (done) { + try { + testutil.initialize(); + } catch (err) { + assert.fail('Failed to load task lib: ' + err.message); + } + done(); + }); + + after(function () { + }); + + it('retries to execute a function', (done: MochaDone) => { + this.timeout(1000); + + const testError = Error('Test Error'); + + function count(num: number) { + return () => num--; + } + + function fail(count: Function) { + if (count()) { + throw testError; + } + + return 'completed'; + } + + function catchError(func: Function, args: any[]) { + try { + func(...args); + } catch (err) { + return err; + } + } + + assert.deepEqual(catchError(tl.retry, [fail, [count(5)], { continueOnError: false, retryCount: 3 }]), testError); + assert.deepEqual(catchError(tl.retry, [fail, [count(5)], { continueOnError: false, retryCount: 4 }]), testError); + assert.deepEqual(tl.retry(fail, [count(5)], { continueOnError: false, retryCount: 5 }), 'completed'); + assert.deepEqual(tl.retry(fail, [count(5)], { continueOnError: false, retryCount: 6 }), 'completed'); + assert.deepEqual(tl.retry(fail, [count(5)], { continueOnError: true, retryCount: 3 }), undefined); + assert.deepEqual(tl.retry(() => 123, [0, 'a', 1, 'b', 2], { continueOnError: false, retryCount: 7 }), 123); + assert.deepEqual(tl.retry((a: string, b: string) => a + b, [''], { continueOnError: false, retryCount: 7 }), 'undefined'); + + done(); + }); +}); diff --git a/node/test/tsconfig.json b/node/test/tsconfig.json index 27dcbee19..3d5dbb2f0 100644 --- a/node/test/tsconfig.json +++ b/node/test/tsconfig.json @@ -21,6 +21,8 @@ "matchtests.ts", "filtertests.ts", "findmatchtests.ts", - "mocktests.ts" + "mocktests.ts", + "retrytests.ts", + "isuncpathtests.ts" ] -} \ No newline at end of file +} From acc731b0bb0f5c6b072e0afe8c4a587947ba479c 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 215/259] 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'); From f30e6baa09d00020411ebd0c5474d1402ea2d647 Mon Sep 17 00:00:00 2001 From: Denis Rumyantsev <61472718+DenisRumyantsev@users.noreply.github.com> Date: Wed, 28 Jul 2021 14:59:41 +0300 Subject: [PATCH 216/259] Review points fixing for 779 PR (#781) --- node/task.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/node/task.ts b/node/task.ts index 9a9f9e1f0..eede0e150 100644 --- a/node/task.ts +++ b/node/task.ts @@ -867,16 +867,16 @@ 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 followSymbolicLink 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 { +function _getStats (path: string, followSymbolicLink: 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) { + if (followSymbolicLink) { try { // use stat (following symlinks) stats = fs.statSync(path); @@ -946,16 +946,19 @@ export function find(findPath: string, options?: FindOptions): string[] { let stats: fs.Stats; try { // `item.path` equals `findPath` for the first item to be processed, when the `result` array is empty - const isPathToSearch = !result.length; + const isPathToSearch: boolean = !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; + // following specified symlinks only if current path equals specified path + const followSpecifiedSymbolicLink: boolean = options.followSpecifiedSymbolicLink && isPathToSearch; + + // following all symlinks or following symlink for the specified path + const followSymbolicLink: boolean = options.followSymbolicLinks || followSpecifiedSymbolicLink; // stat the item. The stat info is used further below to determine whether to traverse deeper - stats = _getStats(item.path, followSymbolicLinks, options.allowBrokenSymbolicLinks); + stats = _getStats(item.path, followSymbolicLink, 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.`); + warning(`No such file or directory: "${item.path}" - skipping.`); continue; } throw err; From d6d14985f832d6d11af8873ed2f1ff8d65f8f483 Mon Sep 17 00:00:00 2001 From: Tatyana Kostromskaya <86476103+tatyana-kostromskaya@users.noreply.github.com> Date: Wed, 4 Aug 2021 09:17:18 +0300 Subject: [PATCH 217/259] Update toolrunnertests.ts (#783) Fix Toolrunner Test "Should handle arguments with quotes properly" --- node/test/toolrunnertests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index 4e623bc5c..d6d2f6850 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -2209,7 +2209,7 @@ describe('Toolrunner Tests', function () { let exeRunner = tl.tool(exePath); exeRunner.line('-TEST1="space test" "-TEST2=%WIN_TEST%" \'-TEST3=value\''); exeRunner.on('stdout', (data) => { - output = data.toString(); + output += data.toString(); }); exeRunner.exec(_testExecOptions).then(function (code) { assert.equal(code, 0, 'return code of cmd should be 0'); From 8d4636791ad4f9b994c4dbb17e503609d3c0cf2b Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 10 Aug 2021 06:40:17 -0400 Subject: [PATCH 218/259] Fix assertAgent call (#647) Co-authored-by: Aleksandr Smolyakov --- node/task.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/task.ts b/node/task.ts index eede0e150..b75ec477f 100644 --- a/node/task.ts +++ b/node/task.ts @@ -2167,7 +2167,7 @@ export function addBuildTag(value: string) { * @returns void */ export function updateReleaseName(name: string) { - assertAgent("2.132"); + assertAgent("2.132.0"); command("release.updatereleasename", null, name); } From 9b03ef7afc587c92c1a845af05774958b00dbf31 Mon Sep 17 00:00:00 2001 From: Alexander Smolyakov Date: Wed, 11 Aug 2021 13:11:24 +0300 Subject: [PATCH 219/259] Bump up task lib version to 3.1.7 (#786) --- node/package-lock.json | 2 +- node/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index f89f29c4a..81c9ead11 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.6", + "version": "3.1.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 282db943a..59ec643a3 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.6", + "version": "3.1.7", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 45747eb60680c826883d772e3539b88e1b587ed5 Mon Sep 17 00:00:00 2001 From: Anatoly Bolshakov Date: Fri, 13 Aug 2021 13:15:30 +0300 Subject: [PATCH 220/259] Localization update (#762) * Added localization pipeline and LocProject.json * Removed en-US * Update localize-pipeline.yml for Azure Pipelines * Update localize-pipeline.yml for Azure Pipelines * Made letter case consistent for languages * LEGO: check in for Localization to temporary branch. (#703) * LEGO: check in for Localization to temporary branch. (#714) * LEGO: check in for Localization to temporary branch. (#720) * Temp renaming * Renamed localization files * Applied enhancements for the localization pipeline (#733) [skip ci] * [Localization] Fixed localization pipeline issue with already localized strings replaced (#737) * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 14646607 Localized file check-in by OneLocBuild Task * LEGO: check in for Localization to temporary branch. (#740) * LEGO: check in for Localization to temporary branch. (#741) * LEGO: check in for Localization to temporary branch. (#742) * LEGO: check in for Localization to temporary branch. (#743) * Temporary renamed files - to resolve conflicts * Temporary renamed * LEGO: check in for Localization to temporary branch. (#745) Co-authored-by: csigs * LEGO: check in for Localization to temporary branch. (#746) Co-authored-by: csigs * LEGO: check in for Localization to temporary branch. (#747) Co-authored-by: csigs * LEGO: check in for Localization to temporary branch. (#748) Co-authored-by: csigs * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 14905562 Localized file check-in by OneLocBuild Task * Returned back original names * Removed redundant locale - test * Removed redundant folders * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 14906155 Localized file check-in by OneLocBuild Task * Returned back changes. Removed redundant * Create PR in OneLocBuild task only on third week of sprint (#755) * Fix localization pipeline * Add missed change * Added option to disable PR creation * Removing Localize folder Co-authored-by: csigs Co-authored-by: Egor Bryzgalov Co-authored-by: csigs Co-authored-by: Nikita Ezzhev From 96ad5c614248a813335d8b4ed78d66b24a81b298 Mon Sep 17 00:00:00 2001 From: kuleshovilya <87485027+kuleshovilya@users.noreply.github.com> Date: Thu, 19 Aug 2021 17:51:53 +0300 Subject: [PATCH 221/259] Change implicit check in getResponse to explicit (#788) * change implicit check for explicit * bump patch version * bump package lock Co-authored-by: Ilya Kuleshov --- node/mock-answer.ts | 3 ++- node/package-lock.json | 2 +- node/package.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/node/mock-answer.ts b/node/mock-answer.ts index 6b67e850f..26e39fde4 100644 --- a/node/mock-answer.ts +++ b/node/mock-answer.ts @@ -47,7 +47,8 @@ export class MockAnswers { const cmd_answer = this._answers[cmd]!; - if (cmd_answer[key]) { + //use this construction to avoid falsy zero + if (cmd_answer[key] != null) { debug('found mock response'); return cmd_answer[key]; } diff --git a/node/package-lock.json b/node/package-lock.json index 81c9ead11..6e7211e68 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.7", + "version": "3.1.8", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 59ec643a3..b5e3c1a7e 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.7", + "version": "3.1.8", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From 0bbb9f8f133f8052aa1cb602c2408470c1fe4afe Mon Sep 17 00:00:00 2001 From: AnnaOpareva <84513972+AnnaOpareva@users.noreply.github.com> Date: Thu, 2 Sep 2021 14:14:34 +0300 Subject: [PATCH 222/259] Fix documentation, comment and condition for getEndpointAuthorization function (#790) * fix doc, comment and condition * bumped version * bump version * fix documentation Co-authored-by: Anna Opareva Co-authored-by: Simon Alling --- node/docs/azure-pipelines-task-lib.md | 2 +- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/node/docs/azure-pipelines-task-lib.md b/node/docs/azure-pipelines-task-lib.md index 6d9b85727..0bf391255 100644 --- a/node/docs/azure-pipelines-task-lib.md +++ b/node/docs/azure-pipelines-task-lib.md @@ -616,7 +616,7 @@ scheme | string | auth scheme such as OAuth or username/password etc... ### task.getEndpointAuthorization (^) Gets the authorization details for a service endpoint -If the authorization was not set and is not optional, it will throw. +If the authorization was not set and is not optional, it will set the task result to Failed. @returns string ```javascript diff --git a/node/package-lock.json b/node/package-lock.json index 6e7211e68..f75d53ff7 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.8", + "version": "3.1.9", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index b5e3c1a7e..fa37f45e8 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.8", + "version": "3.1.9", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index b75ec477f..db216207d 100644 --- a/node/task.ts +++ b/node/task.ts @@ -407,7 +407,7 @@ export interface EndpointAuthorization { /** * Gets the authorization details for a service endpoint - * If the authorization was not set and is not optional, it will throw. + * If the authorization was not set and is not optional, it will set the task result to Failed. * * @param id name of the service endpoint * @param optional whether the url is optional @@ -420,7 +420,7 @@ export function getEndpointAuthorization(id: string, optional: boolean): Endpoin setResult(TaskResult.Failed, loc('LIB_EndpointAuthNotExist', id)); } - debug(id + ' exists ' + (aval !== null)); + debug(id + ' exists ' + (!!aval)); var auth: EndpointAuthorization | undefined; try { From ba1f94be19f958f1f718ac946aef3123c8eee4a8 Mon Sep 17 00:00:00 2001 From: Denis Rumyantsev <61472718+DenisRumyantsev@users.noreply.github.com> Date: Fri, 17 Sep 2021 16:30:38 +0300 Subject: [PATCH 223/259] ubuntu image updated (#795) --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f3ce262fb..447686cb0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -64,7 +64,7 @@ jobs: ################################################# displayName: Linux pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 steps: ################################################################################ From ac8458c96eedba01dcce07fc631fbe3f4fa91d57 Mon Sep 17 00:00:00 2001 From: cima Date: Wed, 6 Oct 2021 08:24:51 +0200 Subject: [PATCH 224/259] #667 Error setting resourceFile: "Maximum call stack size exceeded" (#773) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #667 Error setting resourceFile: "Maximum call stack size exceeded" + Preventing the infinite recustion by not translating warning about missing translation. * Wrong variable in warning output. - Getting rid of unnecessary escaping backslash * Bumping package version * Missing Localization test using mockery The missing file lib.json is simulated via resporting nonexistent file via mockery. Co-authored-by: Martin Šimek Co-authored-by: Anatoly Bolshakov --- node/internal.ts | 4 ++-- node/package-lock.json | 2 +- node/package.json | 2 +- node/test/internalhelpertests.ts | 29 +++++++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/node/internal.ts b/node/internal.ts index 964187b6d..16d656c29 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -200,10 +200,10 @@ export function _loc(key: string, ...param: any[]): string { } else { if (Object.keys(_resourceFiles).length <= 0) { - _warning(_loc('LIB_ResourceFileNotSet', key)); + _warning(`Resource file haven't been set, can't find loc string for key: ${key}`); } else { - _warning(_loc('LIB_LocStringNotFound', key)); + _warning(`Can't find loc string for key: ${key}`); } locString = key; diff --git a/node/package-lock.json b/node/package-lock.json index f75d53ff7..a1c233b60 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.9", + "version": "3.1.10", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index fa37f45e8..7d0057e93 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.9", + "version": "3.1.10", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/test/internalhelpertests.ts b/node/test/internalhelpertests.ts index fbcdfc2c5..de81d52fb 100644 --- a/node/test/internalhelpertests.ts +++ b/node/test/internalhelpertests.ts @@ -7,6 +7,7 @@ import * as path from 'path'; import * as testutil from './testutil'; import * as tl from '../_build/task'; import * as im from '../_build/internal'; +import * as mockery from 'mockery' describe('Internal Path Helper Tests', function () { @@ -401,4 +402,32 @@ describe('Internal Path Helper Tests', function () { done(); }); + + it('ReportMissingStrings', (done: MochaDone) => { + + mockery.registerAllowable('../_build/internal') + const fsMock = { + statSync: function (path) { return null; } + }; + mockery.registerMock('fs', fsMock); + mockery.enable({ useCleanCache: true }) + + const local_im = require('../_build/internal'); + + try{ + const localizedMessage : string = local_im._loc("gizmo", "whatever", "music"); + assert.strictEqual(localizedMessage, "gizmo whatever music"); + + }finally{ + mockery.disable(); + mockery.deregisterAll(); + } + done(); + }); + + it('ReportMissingLocalization', (done: MochaDone) => { + const localizedMessage : string = im._loc("gizmo", "whatever", "music"); + assert.strictEqual(localizedMessage, "gizmo whatever music"); + done(); + }); }); \ No newline at end of file From b901effb8eaf6455b3044d57b0e0b4121aa7aa30 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Fri, 24 Dec 2021 09:01:26 +0100 Subject: [PATCH 225/259] Fix README style typo (#807) --- node/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/README.md b/node/README.md index 6f5e66409..d840ceccd 100644 --- a/node/README.md +++ b/node/README.md @@ -20,7 +20,7 @@ Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/mi ## Node 10 Upgrade Notice Azure DevOps is currently working to establish Node 10 as the new preferred runtime for tasks, upgrading from Node 6. -Relevant work is happening in the `master`` branch and the major version should be used with Node 10 is 3. +Relevant work is happening in the `master` branch and the major version should be used with Node 10 is 3. Previous major version is stored in the `releases/2.x` ### Upgrading to Node 10 From 82f3744beccd53d2832224121a89d1fdff8fdafa Mon Sep 17 00:00:00 2001 From: AndreyIvanov42 <93121155+AndreyIvanov42@users.noreply.github.com> Date: Wed, 16 Feb 2022 16:43:59 +0400 Subject: [PATCH 226/259] Resolve vulnerabilities in dependencies (#815) * update dependencies --- node/package-lock.json | 50 +++- node/package.json | 4 +- powershell/Tests/lib/psRunner.ts | 2 +- powershell/make-util.js | 4 +- powershell/package-lock.json | 481 +++++++++++++++++++++++++++++++ powershell/package.json | 6 +- 6 files changed, 528 insertions(+), 19 deletions(-) create mode 100644 powershell/package-lock.json diff --git a/node/package-lock.json b/node/package-lock.json index a1c233b60..e4a3966f4 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.10", + "version": "3.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -184,6 +184,11 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, "get-port": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", @@ -202,6 +207,14 @@ "path-is-absolute": "^1.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -253,6 +266,14 @@ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" }, + "is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "requires": { + "has": "^1.0.3" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -378,9 +399,9 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "process-nextick-args": { "version": "2.0.1", @@ -428,11 +449,13 @@ } }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "requires": { - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "safe-buffer": { @@ -446,9 +469,9 @@ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" }, "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "requires": { "glob": "^7.0.0", "interpret": "^1.0.0", @@ -463,6 +486,11 @@ "safe-buffer": "~5.1.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, "sync-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", diff --git a/node/package.json b/node/package.json index 7d0057e93..d1a32001d 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.1.10", + "version": "3.2.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", @@ -31,7 +31,7 @@ "mockery": "^1.7.0", "q": "^1.5.1", "semver": "^5.1.0", - "shelljs": "^0.8.4", + "shelljs": "^0.8.5", "sync-request": "6.1.0", "uuid": "^3.0.1" }, diff --git a/powershell/Tests/lib/psRunner.ts b/powershell/Tests/lib/psRunner.ts index abdc55967..4a1dc3c1d 100644 --- a/powershell/Tests/lib/psRunner.ts +++ b/powershell/Tests/lib/psRunner.ts @@ -51,7 +51,7 @@ class PSEngineRunner extends events.EventEmitter { this.emit('starting'); var defer = Q.defer(); - var powershell = shell.which('powershell.exe'); + var powershell = shell.which('powershell.exe').stdout; this._childProcess = child.spawn( powershell, // command [ // args diff --git a/powershell/make-util.js b/powershell/make-util.js index 06c532ea3..aac7268c9 100644 --- a/powershell/make-util.js +++ b/powershell/make-util.js @@ -99,12 +99,12 @@ var ensureTool = function (name, versionArgs, validate) { if (versionArgs) { var result = exec(name + ' ' + versionArgs); if (typeof validate == 'string') { - if (result.output.trim() != validate) { + if (result.stdout.trim() != validate) { throw new Error('expected version: ' + validate); } } else { - validate(result.output.trim()); + validate(result.stdout.trim()); } } diff --git a/powershell/package-lock.json b/powershell/package-lock.json new file mode 100644 index 000000000..35b1b210c --- /dev/null +++ b/powershell/package-lock.json @@ -0,0 +1,481 @@ +{ + "name": "vsts-task-sdk", + "version": "0.12.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "adm-zip": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", + "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "http-basic": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-2.5.1.tgz", + "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", + "dev": true, + "requires": { + "caseless": "~0.11.0", + "concat-stream": "^1.4.6", + "http-response-object": "^1.0.0" + } + }, + "http-response-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-1.1.0.tgz", + "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "requires": { + "asap": "~2.0.3" + } + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "sync-request": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-3.0.1.tgz", + "integrity": "sha1-yqEjWq+Im6UBB2oYNMQ2gwqC+3M=", + "dev": true, + "requires": { + "concat-stream": "^1.4.7", + "http-response-object": "^1.0.1", + "then-request": "^2.0.1" + } + }, + "then-request": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz", + "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", + "dev": true, + "requires": { + "caseless": "~0.11.0", + "concat-stream": "^1.4.7", + "http-basic": "^2.5.1", + "http-response-object": "^1.1.0", + "promise": "^7.1.1", + "qs": "^6.1.0" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.7.tgz", + "integrity": "sha1-NeODjeMckc/h2MIODleF04aTikk=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/powershell/package.json b/powershell/package.json index f4c0381fe..c401e0245 100644 --- a/powershell/package.json +++ b/powershell/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-sdk", - "version": "0.11.0", + "version": "0.12.0", "description": "VSTS Task SDK", "scripts": { "build": "node make.js build", @@ -23,10 +23,10 @@ }, "homepage": "https://github.com/Microsoft/azure-pipelines-task-lib#readme", "devDependencies": { - "adm-zip": "0.4.7", + "adm-zip": "^0.5.9", "mocha": "5.2.0", "q": "1.4.1", - "shelljs": "^0.3.0", + "shelljs": "^0.8.5", "sync-request": "3.0.1", "typescript": "1.8.7" } From 2ad3b09d316bd09aeac32fcd45b7dbb1db37ce15 Mon Sep 17 00:00:00 2001 From: Nikita Ezzhev Date: Fri, 11 Mar 2022 16:04:35 +0300 Subject: [PATCH 227/259] Add optional HostException handler in Invoke-Tool (#822) * Add HostException handler * Update error handling for HostException * Update vmImage for windows part * Update way of processing HostException * Change image to win 2019 * Fix naming --- azure-pipelines.yml | 2 +- powershell/VstsTaskSdk/ToolFunctions.ps1 | 13 +++++++++++-- powershell/package-lock.json | 2 +- powershell/package.json | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 447686cb0..e6fdc0c03 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,7 +9,7 @@ jobs: ################################################# displayName: windows pool: - vmImage: vs2017-win2016 + vmImage: windows-2019 steps: ################################################################################ diff --git a/powershell/VstsTaskSdk/ToolFunctions.ps1 b/powershell/VstsTaskSdk/ToolFunctions.ps1 index 7e62ebd6e..1f236aed0 100644 --- a/powershell/VstsTaskSdk/ToolFunctions.ps1 +++ b/powershell/VstsTaskSdk/ToolFunctions.ps1 @@ -86,7 +86,8 @@ function Invoke-Tool { # TODO: RENAME TO INVOKE-PROCESS? [string]$Arguments, [string]$WorkingDirectory, [System.Text.Encoding]$Encoding, - [switch]$RequireExitCodeZero) + [switch]$RequireExitCodeZero, + [bool]$IgnoreHostException) Trace-EnteringInvocation $MyInvocation $isPushed = $false @@ -104,7 +105,15 @@ function Invoke-Tool { # TODO: RENAME TO INVOKE-PROCESS? $FileName = $FileName.Replace('"', '').Replace("'", "''") Write-Host "##[command]""$FileName"" $Arguments" - Invoke-Expression "& '$FileName' --% $Arguments" + try { + Invoke-Expression "& '$FileName' --% $Arguments" + } catch [System.Management.Automation.Host.HostException] { + if ($IgnoreHostException -eq $False) { + throw + } + + Write-Host "##[warning]Host Exception was thrown by Invoke-Expression, suppress it due IgnoreHostException setting" + } Write-Verbose "Exit code: $LASTEXITCODE" if ($RequireExitCodeZero -and $LASTEXITCODE -ne 0) { Write-Error (Get-LocString -Key PSLIB_Process0ExitedWithCode1 -ArgumentList ([System.IO.Path]::GetFileName($FileName)), $LASTEXITCODE) diff --git a/powershell/package-lock.json b/powershell/package-lock.json index 35b1b210c..2298eb426 100644 --- a/powershell/package-lock.json +++ b/powershell/package-lock.json @@ -1,6 +1,6 @@ { "name": "vsts-task-sdk", - "version": "0.12.0", + "version": "0.13.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/powershell/package.json b/powershell/package.json index c401e0245..f6e132ecc 100644 --- a/powershell/package.json +++ b/powershell/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-sdk", - "version": "0.12.0", + "version": "0.13.0", "description": "VSTS Task SDK", "scripts": { "build": "node make.js build", From 9448530ccb0861863bbef53a52a41652f28ade66 Mon Sep 17 00:00:00 2001 From: Denis Tikhomirov <90906678+denis-tikhomirov@users.noreply.github.com> Date: Fri, 11 Mar 2022 16:21:08 +0300 Subject: [PATCH 228/259] Move notifications about Task-lib Localization PR from Slack to MS Teams - Part 2 (#817) --- open-pullrequest.ps1 | 12 +++++++---- send-notifications.ps1 | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 send-notifications.ps1 diff --git a/open-pullrequest.ps1 b/open-pullrequest.ps1 index 0ddbb4440..d00c05170 100644 --- a/open-pullrequest.ps1 +++ b/open-pullrequest.ps1 @@ -4,14 +4,14 @@ param( $SourceBranch ) +# Getting a created PR. Result object has interface in accordance with article https://docs.github.com/en/rest/reference/pulls#get-a-pull-request function Get-PullRequest() { - $prInfo = (gh api -X GET repos/:owner/:repo/pulls -F head=":owner:$SourceBranch" -f state=open -f base=master | ConvertFrom-Json) - return $prInfo.html_url + return (gh api -X GET repos/:owner/:repo/pulls -F head=":owner:$SourceBranch" -f state=open -f base=master | ConvertFrom-Json) } $openedPR = Get-PullRequest -if ($openedPR.length -ne 0) { +if ($openedPR.html_url.length -ne 0) { throw "A PR from $SourceBranch to master already exists." } @@ -20,6 +20,10 @@ $body = "This PR was auto-generated with [the localization pipeline build]($buil gh pr create --head $SourceBranch --title 'Localization update' --body $body +# Getting a number to the opened PR +$PR_NUMBER = (Get-PullRequest).number +Write-Host "##vso[task.setvariable variable=PR_NUMBER]$PR_NUMBER" + # Getting a link to the opened PR -$PR_LINK = Get-PullRequest +$PR_LINK = (Get-PullRequest).html_url Write-Host "##vso[task.setvariable variable=PR_LINK]$PR_LINK" diff --git a/send-notifications.ps1 b/send-notifications.ps1 new file mode 100644 index 000000000..4de65d33b --- /dev/null +++ b/send-notifications.ps1 @@ -0,0 +1,45 @@ +param( + [Parameter(Mandatory = $true)] + [bool]$IsPRCreated, + [Parameter(Mandatory = $true)] + [string]$RepoName +) + +# Function sends Office 365 connector card to webhook. +# It requires title and message text displyed in card and theme color used to hignlight card. +function Send-Notification { + param ( + [Parameter(Mandatory = $true)] + [string]$titleText, + [Parameter(Mandatory = $true)] + [string]$messageText, + [Parameter(Mandatory = $true)] + [string]$themeColor + ) + + $body = [PSCustomObject]@{ + title = $titleText + text = $messageText + themeColor = $themeColor + } | ConvertTo-Json + + Invoke-RestMethod -Uri $($env:TEAMS_WEBHOOK) -Method Post -Body $body -ContentType 'application/json' +} + +$wikiLink = "[Wiki](https://mseng.visualstudio.com/AzureDevOps/_wiki/wikis/AzureDevOps.wiki/16150/Localization-update)" + +if ($IsPRCreated) { + $pullRequestLink = "[PR $($env:PR_NUMBER)]($($env:PR_LINK))" + $titleText = "Azure Pipelines $RepoName Localization update PR created - ID $($env:PR_NUMBER)" + $messageText = "Created $RepoName Localization update PR. Please review and approve/merge $pullRequestLink. Related article in $wikiLink." + $themeColor = "#FFFF00" +} +else { + $buildUrl = "$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI$env:SYSTEM_TEAMPROJECT/_build/results?buildId=$($env:BUILD_BUILDID)&_a=summary" + $buildLink = "[ID $($env:BUILD_BUILDID)]($($buildUrl))" + $titleText = "Azure Pipelines $RepoName Localization build failed - ID $($env:BUILD_BUILDID)" + $messageText = "Failed to create $RepoName Localization update PR. Please review the results of failed build $buildLink. Related article in $wikiLink." + $themeColor = "#FF0000" +} + +Send-Notification -titleText $titleText -messageText $messageText -themeColor $themeColor From 5ebf55f67908c5b16314ccb60a0c6b94dacd8a0a Mon Sep 17 00:00:00 2001 From: Maxim Zaytsev Date: Tue, 22 Mar 2022 22:12:27 +0300 Subject: [PATCH 229/259] Fix return types for input functions in documentation (#826) --- node/docs/azure-pipelines-task-lib.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/node/docs/azure-pipelines-task-lib.md b/node/docs/azure-pipelines-task-lib.md index 0bf391255..91b4b8407 100644 --- a/node/docs/azure-pipelines-task-lib.md +++ b/node/docs/azure-pipelines-task-lib.md @@ -119,7 +119,7 @@ If required is true and the value is not set, it will throw. @returns string ```javascript -getInput(name:string, required?:boolean):string +getInput(name:string, required?:boolean):string | undefined ``` Param | Type | Description @@ -155,7 +155,7 @@ If check is true and the path does not exist, it will throw. @returns string ```javascript -getPathInput(name:string, required?:boolean, check?:boolean):string +getPathInput(name:string, required?:boolean, check?:boolean):string | undefined ``` Param | Type | Description @@ -211,7 +211,7 @@ Gets a variable value that is defined on the build/release definition or set at @returns string ```javascript -getVariable(name:string):string +getVariable(name:string):string | undefined ``` Param | Type | Description @@ -275,7 +275,7 @@ Requires a 2.115.0 agent or higher. @returns string ```javascript -getTaskVariable(name:string):string +getTaskVariable(name:string):string | undefined ``` Param | Type | Description From a9aa7758a40c63ca7bf03a4d2b09b4929b49de53 Mon Sep 17 00:00:00 2001 From: Keith Banner Date: Wed, 11 May 2022 07:12:15 -0600 Subject: [PATCH 230/259] Added new required functions (#827) * Added new required functions. Return the value instead of the value or undefined. getInputRequired getPathInputRequired getEndpointUrlRequired getEndpointDataParameterRequired getEndpointAuthorizationSchemeRequired getEndpointAuthorizationParameterRequired * Fix undefined in json file * Simplify Required functions. Add tests. Co-authored-by: Keith Banner --- node/docs/azure-pipelines-task-lib.json | 166 +++++- node/docs/azure-pipelines-task-lib.md | 651 ++++++++++++++---------- node/docs/docs.json | 6 + node/mock-task.ts | 10 + node/task.ts | 74 ++- node/test/inputtests.ts | 250 +++++++++ 6 files changed, 871 insertions(+), 286 deletions(-) diff --git a/node/docs/azure-pipelines-task-lib.json b/node/docs/azure-pipelines-task-lib.json index 4b0c6fde2..7b34249d1 100644 --- a/node/docs/azure-pipelines-task-lib.json +++ b/node/docs/azure-pipelines-task-lib.json @@ -640,7 +640,7 @@ } ], "members": {}, - "return": "string", + "return": "string | undefined", "documentation": "Gets a variable value that is defined on the build/release definition or set at runtime.\n\n@returns string" } ] @@ -759,7 +759,7 @@ "getInput": { "name": "getInput", "members": {}, - "documentation": "Gets the value of an input. The value is also trimmed.\nIf required is true and the value is not set, it will throw.\n\n@returns string", + "documentation": "Gets the value of an input. The value is also trimmed.\nIf required is true and the value is not set, it will throw.\n\n@returns string | undefined", "kind": "function", "signatures": [ { @@ -778,8 +778,29 @@ } ], "members": {}, + "return": "string | undefined", + "documentation": "Gets the value of an input. The value is also trimmed.\nIf required is true and the value is not set, it will throw.\n\n@returns string | undefined" + } + ] + }, + "getInputRequired": { + "name": "getInputRequired", + "members": {}, + "documentation": "Gets the value of an input. The value is also trimmed.\nIf the value is not set, it will throw.\n\n@returns string", + "kind": "function", + "signatures": [ + { + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "documentation": "name of the input to get" + } + ], + "members": {}, "return": "string", - "documentation": "Gets the value of an input. The value is also trimmed.\nIf required is true and the value is not set, it will throw.\n\n@returns string" + "documentation": "Gets the value of an input. The value is also trimmed.\nIf the value is not set, it will throw.\n\n@returns string" } ] }, @@ -867,7 +888,7 @@ "getPathInput": { "name": "getPathInput", "members": {}, - "documentation": "Gets the value of a path input\nIt will be quoted for you if it isn't already and contains spaces\nIf required is true and the value is not set, it will throw.\nIf check is true and the path does not exist, it will throw.\n\n@returns string", + "documentation": "Gets the value of a path input\nIt will be quoted for you if it isn't already and contains spaces\nIf required is true and the value is not set, it will throw.\nIf check is true and the path does not exist, it will throw.\n\n@returns string | undefined", "kind": "function", "signatures": [ { @@ -892,15 +913,42 @@ } ], "members": {}, + "return": "string | undefined", + "documentation": "Gets the value of a path input\nIt will be quoted for you if it isn't already and contains spaces\nIf required is true and the value is not set, it will throw.\nIf check is true and the path does not exist, it will throw.\n\n@returns string | undefined" + } + ] + }, + "getPathInputRequired": { + "name": "getPathInputRequired", + "members": {}, + "documentation": "Gets the value of a path input\nIt will be quoted for you if it isn't already and contains spaces\nIf the value is not set, it will throw.\nIf check is true and the path does not exist, it will throw.\n\n@returns string", + "kind": "function", + "signatures": [ + { + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "documentation": "name of the input to get" + }, + { + "name": "check", + "type": "boolean", + "optional": true, + "documentation": "whether path is checked. optional, defaults to false " + } + ], + "members": {}, "return": "string", - "documentation": "Gets the value of a path input\nIt will be quoted for you if it isn't already and contains spaces\nIf required is true and the value is not set, it will throw.\nIf check is true and the path does not exist, it will throw.\n\n@returns string" + "documentation": "Gets the value of a path input\nIt will be quoted for you if it isn't already and contains spaces\nIf the value is not set, it will throw.\nIf check is true and the path does not exist, it will throw.\n\n@returns string" } ] }, "getEndpointUrl": { "name": "getEndpointUrl", "members": {}, - "documentation": "Gets the url for a service endpoint\nIf the url was not set and is not optional, it will throw.\n\n@returns string", + "documentation": "Gets the url for a service endpoint\nIf the url was not set and is not optional, it will throw.\n\n@returns string | undefined", "kind": "function", "signatures": [ { @@ -919,8 +967,29 @@ } ], "members": {}, + "return": "string | undefined", + "documentation": "Gets the url for a service endpoint\nIf the url was not set and is not optional, it will throw.\n\n@returns string | undefined" + } + ] + }, + "getEndpointUrlRequired": { + "name": "getEndpointUrlRequired", + "members": {}, + "documentation": "Gets the url for a service endpoint\nIf the url was not set, it will throw.\n\n@returns string", + "kind": "function", + "signatures": [ + { + "parameters": [ + { + "name": "id", + "type": "string", + "optional": false, + "documentation": "name of the service endpoint" + } + ], + "members": {}, "return": "string", - "documentation": "Gets the url for a service endpoint\nIf the url was not set and is not optional, it will throw.\n\n@returns string" + "documentation": "Gets the url for a service endpoint\nIf the url was not set, it will throw.\n\n@returns string" } ] }, @@ -952,6 +1021,33 @@ } ], "members": {}, + "return": "string | undefined", + "documentation": "" + } + ] + }, + "getEndpointDataParameterRequired": { + "name": "getEndpointDataParameterRequired", + "members": {}, + "documentation": "", + "kind": "function", + "signatures": [ + { + "parameters": [ + { + "name": "id", + "type": "string", + "optional": false, + "documentation": "" + }, + { + "name": "key", + "type": "string", + "optional": false, + "documentation": "" + } + ], + "members": {}, "return": "string", "documentation": "" } @@ -960,7 +1056,7 @@ "getEndpointAuthorizationScheme": { "name": "getEndpointAuthorizationScheme", "members": {}, - "documentation": "Gets the endpoint authorization scheme for a service endpoint\nIf the endpoint authorization scheme is not set and is not optional, it will throw.\n\n@returns {string} value of the endpoint authorization scheme", + "documentation": "Gets the endpoint authorization scheme for a service endpoint\nIf the endpoint authorization scheme is not set and is not optional, it will throw.\n\n@returns {string} value of the endpoint authorization scheme or undefined", "kind": "function", "signatures": [ { @@ -979,15 +1075,36 @@ } ], "members": {}, + "return": "string | undefined", + "documentation": "Gets the endpoint authorization scheme for a service endpoint\nIf the endpoint authorization scheme is not set and is not optional, it will throw.\n\n@returns {string} value of the endpoint authorization scheme or undefined" + } + ] + }, + "getEndpointAuthorizationSchemeRequired": { + "name": "getEndpointAuthorizationSchemeRequired", + "members": {}, + "documentation": "Gets the endpoint authorization scheme for a service endpoint\nIf the endpoint authorization scheme is not set, it will throw.\n\n@returns {string} value of the endpoint authorization scheme", + "kind": "function", + "signatures": [ + { + "parameters": [ + { + "name": "id", + "type": "string", + "optional": false, + "documentation": "name of the service endpoint" + } + ], + "members": {}, "return": "string", - "documentation": "Gets the endpoint authorization scheme for a service endpoint\nIf the endpoint authorization scheme is not set and is not optional, it will throw.\n\n@returns {string} value of the endpoint authorization scheme" + "documentation": "Gets the endpoint authorization scheme for a service endpoint\nIf the endpoint authorization scheme is not set, it will throw.\n\n@returns {string} value of the endpoint authorization scheme" } ] }, "getEndpointAuthorizationParameter": { "name": "getEndpointAuthorizationParameter", "members": {}, - "documentation": "Gets the endpoint authorization parameter value for a service endpoint with specified key\nIf the endpoint authorization parameter is not set and is not optional, it will throw.\n\n@returns {string} value of the endpoint authorization parameter value", + "documentation": "Gets the endpoint authorization parameter value for a service endpoint with specified key\nIf the endpoint authorization parameter is not set and is not optional, it will throw.\n\n@returns {string} value of the endpoint authorization parameter value or undefined", "kind": "function", "signatures": [ { @@ -1012,8 +1129,35 @@ } ], "members": {}, + "return": "string | undefined", + "documentation": "Gets the endpoint authorization parameter value for a service endpoint with specified key\nIf the endpoint authorization parameter is not set and is not optional, it will throw.\n\n@returns {string} value of the endpoint authorization parameter value or undefined" + } + ] + }, + "getEndpointAuthorizationParameterRequired": { + "name": "getEndpointAuthorizationParameterRequired", + "members": {}, + "documentation": "Gets the endpoint authorization parameter value for a service endpoint with specified key\nIf the endpoint authorization parameter is not set, it will throw.\n\n@returns {string} value of the endpoint authorization parameter value", + "kind": "function", + "signatures": [ + { + "parameters": [ + { + "name": "id", + "type": "string", + "optional": false, + "documentation": "name of the service endpoint" + }, + { + "name": "key", + "type": "string", + "optional": false, + "documentation": "key to find the endpoint authorization parameter" + } + ], + "members": {}, "return": "string", - "documentation": "Gets the endpoint authorization parameter value for a service endpoint with specified key\nIf the endpoint authorization parameter is not set and is not optional, it will throw.\n\n@returns {string} value of the endpoint authorization parameter value" + "documentation": "Gets the endpoint authorization parameter value for a service endpoint with specified key\nIf the endpoint authorization parameter is not set, it will throw.\n\n@returns {string} value of the endpoint authorization parameter value" } ] }, diff --git a/node/docs/azure-pipelines-task-lib.md b/node/docs/azure-pipelines-task-lib.md index 91b4b8407..b39d9e61f 100644 --- a/node/docs/azure-pipelines-task-lib.md +++ b/node/docs/azure-pipelines-task-lib.md @@ -1,30 +1,32 @@ # AZURE-PIPELINES-TASK-LIB TYPESCRIPT API - + ## Dependencies A [cross platform agent](https://github.com/Microsoft/vso-agent) OR a TFS 2015 Update 2 Windows agent (or higher) is required to run a Node task end-to-end. However, an agent is not required for interactively testing the task. - + ## Importing For now, the built azure-pipelines-task-lib (in _build) should be packaged with your task in a node_modules folder - + The build generates a azure-pipelines-task-lib.d.ts file for use when compiling tasks In the example below, it is in a folder named definitions above the tasks lib - + ``` /// import tl = require('azure-pipelines-task-lib/task') ``` - + ## [Release notes](releases.md) - +
- + ## Index - + ### Input Functions (v) - + getInput
+getInputRequired
getBoolInput
getPathInput
+getPathInputRequired
filePathSupplied
getDelimitedInput
getVariable
@@ -33,9 +35,9 @@ import tl = require('azure-pipelines-task-lib/task') setVariable
getTaskVariable
setTaskVariable
- + ### Execution (v) - + tool
ToolRunner.arg
ToolRunner.line
@@ -48,27 +50,31 @@ import tl = require('azure-pipelines-task-lib/task') exec
execSync
setResult
- + ### Service Connections (v) - + getEndpointUrl
+getEndpointUrlRequired
getEndpointDataParameter
+getEndpointDataParameterRequired
getEndpointAuthorizationScheme
+getEndpointAuthorizationSchemeRequired
getEndpointAuthorizationParameter
+getEndpointAuthorizationParameterRequired
EndpointAuthorization
getEndpointAuthorization
- + ### Secrets (v) - + setSecret
- + ### Secure Files (v) - + getSecureFileName
getSecureFileTicket
- + ### Disk Functions (v) - + which
checkPath
exist
@@ -84,35 +90,35 @@ import tl = require('azure-pipelines-task-lib/task') resolve
stats
writeFile
- + ### Globbing (v) - + MatchOptions
match
findMatch
filter
legacyFindFiles
- + ### Localization (v) - + setResourcePath
loc
- + ### Proxy (v) - + getHttpProxyConfiguration
- +
- + ## Input Functions - + --- - + Functions for retrieving inputs for the task
- + ### task.getInput (^) Gets the value of an input. The value is also trimmed. If required is true and the value is not set, it will throw. @@ -121,15 +127,31 @@ If required is true and the value is not set, it will throw. ```javascript getInput(name:string, required?:boolean):string | undefined ``` - + Param | Type | Description --- | --- | --- name | string | name of the input to get required | boolean | whether input is required. optional, defaults to false - + +
+
+ +### task.getInputRequired (^) +Gets the value of an input. The value is also trimmed. +If the value is not set, it will throw. + +@returns string +```javascript +getInputRequired(name:string):string +``` + +Param | Type | Description +--- | --- | --- +name | string | name of the input to get +
- + ### task.getBoolInput (^) Gets the value of an input and converts to a bool. Convenience. If required is true and the value is not set, it will throw. @@ -138,15 +160,15 @@ If required is true and the value is not set, it will throw. ```javascript getBoolInput(name:string, required?:boolean):boolean ``` - + Param | Type | Description --- | --- | --- name | string | name of the bool input to get required | boolean | whether input is required. optional, defaults to false - +
- + ### task.getPathInput (^) Gets the value of a path input It will be quoted for you if it isn't already and contains spaces @@ -157,16 +179,35 @@ If check is true and the path does not exist, it will throw. ```javascript getPathInput(name:string, required?:boolean, check?:boolean):string | undefined ``` - + Param | Type | Description --- | --- | --- name | string | name of the input to get required | boolean | whether input is required. optional, defaults to false -check | boolean | whether path is checked. optional, defaults to false - +check | boolean | whether path is checked. optional, defaults to false + +
+
+ +### task.getPathInputRequired (^) +Gets the value of a path input +It will be quoted for you if it isn't already and contains spaces +If the value is not set, it will throw. +If check is true and the path does not exist, it will throw. + +@returns string +```javascript +getPathInputRequired(name:string, check?:boolean):string +``` + +Param | Type | Description +--- | --- | --- +name | string | name of the input to get +check | boolean | whether path is checked. optional, defaults to false +
- + ### task.filePathSupplied (^) Checks whether a path inputs value was supplied by the user File paths are relative with a picker, so an empty path is the root of the repo. @@ -176,14 +217,14 @@ Useful if you need to condition work (like append an arg) if a value was supplie ```javascript filePathSupplied(name:string):boolean ``` - + Param | Type | Description --- | --- | --- name | string | name of the path input to check - +
- + ### task.getDelimitedInput (^) Gets the value of an input and splits the value using a delimiter (space, comma, etc). Empty values are removed. This function is useful for splitting an input containing a simple @@ -196,16 +237,16 @@ If required is true and the value is not set, it will throw. ```javascript getDelimitedInput(name:string, delim:string, required?:boolean):string[] ``` - + Param | Type | Description --- | --- | --- name | string | name of the input to get delim | string | delimiter to split on required | boolean | whether input is required. optional, defaults to false - +
- + ### task.getVariable (^) Gets a variable value that is defined on the build/release definition or set at runtime. @@ -213,26 +254,26 @@ Gets a variable value that is defined on the build/release definition or set at ```javascript getVariable(name:string):string | undefined ``` - + Param | Type | Description --- | --- | --- name | string | name of the variable to get - +
- + ### task.VariableInfo (^) -Snapshot of a variable at the time when getVariables was called. - +Snapshot of a variable at the time when getVariables was called. + Property | Type | Description --- | --- | --- -name | string | -value | string | -secret | boolean | - +name | string | +value | string | +secret | boolean | +
- + ### task.getVariables (^) Gets a snapshot of the current state of all job variables available to the task. Requires a 2.104.1 agent or higher for full functionality. @@ -248,10 +289,10 @@ Limitations on an agent prior to 2.104.1: ```javascript getVariables():VariableInfo[] ``` - +
- + ### task.setVariable (^) Sets a variable which will be available to subsequent tasks as well. @@ -259,16 +300,16 @@ Sets a variable which will be available to subsequent tasks as well. ```javascript setVariable(name:string, val:string, secret?:boolean):void ``` - + Param | Type | Description --- | --- | --- name | string | name of the variable to set val | string | value to set secret | boolean | whether variable is secret. Multi\-line secrets are not allowed. Optional, defaults to false - +
- + ### task.getTaskVariable (^) Gets a variable value that is set by previous step from the same wrapper task. Requires a 2.115.0 agent or higher. @@ -277,14 +318,14 @@ Requires a 2.115.0 agent or higher. ```javascript getTaskVariable(name:string):string | undefined ``` - + Param | Type | Description --- | --- | --- name | string | name of the variable to get - +
- + ### task.setTaskVariable (^) Sets a task variable which will only be available to subsequent steps belong to the same wrapper task. Requires a 2.115.0 agent or higher. @@ -293,23 +334,23 @@ Requires a 2.115.0 agent or higher. ```javascript setTaskVariable(name:string, val:string, secret?:boolean):void ``` - + Param | Type | Description --- | --- | --- name | string | name of the variable to set val | string | value to set secret | boolean | whether variable is secret. optional, defaults to false - - + +
- + ## Execution - + --- - + Tasks typically execute a series of tools (cli) and set the result of the task based on the outcome of those - + ```javascript /// import tl = require('azure-pipelines-task-lib/task'); @@ -327,7 +368,7 @@ catch (err) { ```
- + ### task.tool (^) Convenience factory to create a ToolRunner. @@ -335,31 +376,31 @@ Convenience factory to create a ToolRunner. ```javascript tool(tool:string):ToolRunner ``` - + Param | Type | Description --- | --- | --- tool | string | path to tool to exec - +
- + ### toolrunner.ToolRunner.arg (^) Add argument -Append an argument or an array of arguments +Append an argument or an array of arguments returns ToolRunner for chaining @returns ToolRunner ```javascript arg(val:string | string[]):ToolRunner ``` - + Param | Type | Description --- | --- | --- val | string \| string\[\] | string cmdline or array of strings - +
- + ### toolrunner.ToolRunner.line (^) Parses an argument line into one or more arguments e.g. .line('"arg one" two -z') is equivalent to .arg(['arg one', 'two', '-z']) @@ -369,14 +410,14 @@ returns ToolRunner for chaining ```javascript line(val:string):ToolRunner ``` - + Param | Type | Description --- | --- | --- val | string | string argument line - +
- + ### toolrunner.ToolRunner.argIf (^) Add argument(s) if a condition is met Wraps arg(). See arg for details @@ -386,26 +427,26 @@ returns ToolRunner for chaining ```javascript argIf(condition:any, val:any):this ``` - + Param | Type | Description --- | --- | --- condition | any | boolean condition val | any | string cmdline or array of strings - +
- + ### toolrunner.IExecOptions (^) Interface for exec options - + Property | Type | Description --- | --- | --- -failOnStdErr | boolean | optional. whether to fail if output to stderr. defaults to false -ignoreReturnCode | boolean | optional. defaults to failing on non zero. ignore will not fail leaving it up to the caller - +failOnStdErr | boolean | optional. whether to fail if output to stderr. defaults to false +ignoreReturnCode | boolean | optional. defaults to failing on non zero. ignore will not fail leaving it up to the caller +
- + ### toolrunner.ToolRunner.exec (^) Exec a tool. Output will be streamed to the live console. @@ -415,60 +456,60 @@ Returns promise with return code ```javascript exec(options?:IExecOptions):any ``` - + Param | Type | Description --- | --- | --- options | IExecOptions | optional exec options. See IExecOptions - +
- + ### toolrunner.ToolRunner.execSync (^) -Exec a tool synchronously. +Exec a tool synchronously. Output will be *not* be streamed to the live console. It will be returned after execution is complete. -Appropriate for short running tools +Appropriate for short running tools Returns IExecSyncResult with output and return code @returns IExecSyncResult ```javascript execSync(options?:IExecSyncOptions):IExecSyncResult ``` - + Param | Type | Description --- | --- | --- options | IExecSyncOptions | optional exec options. See IExecSyncOptions - +
- + ### toolrunner.ToolRunner.pipeExecOutputToTool (^) Pipe output of exec() to another tool @returns {ToolRunner} ```javascript pipeExecOutputToTool(tool:ToolRunner, file?:string):ToolRunner ``` - + Param | Type | Description --- | --- | --- -tool | ToolRunner | +tool | ToolRunner | file | string | optional filename to additionally stream the output to. - +
- + ### toolrunner.IExecSyncResult (^) Interface for exec results returned from synchronous exec functions - + Property | Type | Description --- | --- | --- -stdout | string | standard output -stderr | string | error output -code | number | return code -error | Error | Error on failure - +stdout | string | standard output +stderr | string | error output +code | number | return code +error | Error | Error on failure +
- + ### task.exec (^) Exec a tool. Convenience wrapper over ToolRunner to exec with args in one call. Output will be streamed to the live console. @@ -478,36 +519,36 @@ Returns promise with return code ```javascript exec(tool:string, args:any, options?:IExecOptions):any ``` - + Param | Type | Description --- | --- | --- tool | string | path to tool to exec args | any | an arg string or array of args options | IExecOptions | optional exec options. See IExecOptions - +
- + ### task.execSync (^) Exec a tool synchronously. Convenience wrapper over ToolRunner to execSync with args in one call. Output will be *not* be streamed to the live console. It will be returned after execution is complete. -Appropriate for short running tools +Appropriate for short running tools Returns IExecResult with output and return code @returns IExecSyncResult ```javascript execSync(tool:string, args:string | string[], options?:IExecSyncOptions):IExecSyncResult ``` - + Param | Type | Description --- | --- | --- tool | string | path to tool to exec args | string \| string\[\] | an arg string or array of args options | IExecSyncOptions | optional exec options. See IExecSyncOptions - +
- + ### task.setResult (^) Sets the result of the task. Execution will continue. @@ -518,102 +559,164 @@ If multiple calls are made to setResult the most pessimistic call wins (Failed) ```javascript setResult(result:TaskResult, message:string):void ``` - + Param | Type | Description --- | --- | --- -result | TaskResult | TaskResult enum of Succeeded, SucceededWithIssues or Failed. +result | TaskResult | TaskResult enum of Succeeded, SucceededWithIssues or Failed. message | string | A message which will be logged as an error issue if the result is Failed. - - + +
- + ## Service Connections - + --- - + Retrieve service connections (previously called "service endpoints") and authorization details
- + ### task.getEndpointUrl (^) Gets the url for a service endpoint If the url was not set and is not optional, it will throw. @returns string ```javascript -getEndpointUrl(id:string, optional:boolean):string +getEndpointUrl(id:string, optional:boolean):string | undefined ``` - + Param | Type | Description --- | --- | --- id | string | name of the service endpoint optional | boolean | whether the url is optional - + +
+
+ +### task.getEndpointUrlRequired (^) +Gets the url for a service endpoint +If the url was not set, it will throw. + +@returns string +```javascript +getEndpointUrlRequired(id:string):string +``` + +Param | Type | Description +--- | --- | --- +id | string | name of the service endpoint +
- + ### task.getEndpointDataParameter (^) ```javascript -getEndpointDataParameter(id:string, key:string, optional:boolean):string +getEndpointDataParameter(id:string, key:string, optional:boolean):string | undefined ``` - + Param | Type | Description --- | --- | --- -id | string | -key | string | -optional | boolean | - +id | string | +key | string | +optional | boolean | + +
+
+ +### task.getEndpointDataParameterRequired (^) +```javascript +getEndpointDataParameterRequired(id:string, key:string):string +``` + +Param | Type | Description +--- | --- | --- +id | string | +key | string | +
- + ### task.getEndpointAuthorizationScheme (^) Gets the endpoint authorization scheme for a service endpoint If the endpoint authorization scheme is not set and is not optional, it will throw. @returns {string} value of the endpoint authorization scheme ```javascript -getEndpointAuthorizationScheme(id:string, optional:boolean):string +getEndpointAuthorizationScheme(id:string, optional:boolean):string | undefined ``` - + Param | Type | Description --- | --- | --- id | string | name of the service endpoint optional | boolean | whether the endpoint authorization scheme is optional - + +
+
+ +### task.getEndpointAuthorizationSchemeRequired (^) +Gets the endpoint authorization scheme for a service endpoint +If the endpoint authorization scheme is not set, it will throw. + +@returns {string} value of the endpoint authorization scheme +```javascript +getEndpointAuthorizationSchemeRequired(id:string):string +``` + +Param | Type | Description +--- | --- | --- +id | string | name of the service endpoint +
- + ### task.getEndpointAuthorizationParameter (^) Gets the endpoint authorization parameter value for a service endpoint with specified key If the endpoint authorization parameter is not set and is not optional, it will throw. @returns {string} value of the endpoint authorization parameter value ```javascript -getEndpointAuthorizationParameter(id:string, key:string, optional:boolean):string +getEndpointAuthorizationParameter(id:string, key:string, optional:boolean):string | undefined ``` - + Param | Type | Description --- | --- | --- id | string | name of the service endpoint key | string | key to find the endpoint authorization parameter optional | boolean | optional whether the endpoint authorization scheme is optional - + +
+
+ +### task.getEndpointAuthorizationParameterRequired (^) +Gets the endpoint authorization parameter value for a service endpoint with specified key +If the endpoint authorization parameter is not set, it will throw. + +@returns {string} value of the endpoint authorization parameter value +```javascript +getEndpointAuthorizationParameterRequired(id:string, key:string):string +``` + +Param | Type | Description +--- | --- | --- +id | string | name of the service endpoint +key | string | key to find the endpoint authorization parameter +
- + ### task.EndpointAuthorization (^) Interface for EndpointAuthorization Contains a schema and a string/string dictionary of auth data - + Property | Type | Description --- | --- | --- -parameters | \{ \[key: string\]: string; \} | dictionary of auth data -scheme | string | auth scheme such as OAuth or username/password etc... - +parameters | \{ \[key: string\]: string; \} | dictionary of auth data +scheme | string | auth scheme such as OAuth or username/password etc... +
- + ### task.getEndpointAuthorization (^) Gets the authorization details for a service endpoint If the authorization was not set and is not optional, it will set the task result to Failed. @@ -622,46 +725,46 @@ If the authorization was not set and is not optional, it will set the task resul ```javascript getEndpointAuthorization(id:string, optional:boolean):EndpointAuthorization ``` - + Param | Type | Description --- | --- | --- id | string | name of the service endpoint optional | boolean | whether the url is optional - - + +
- + ## Secrets - + --- - + Functions for managing pipeline secrets
- + ### task.setSecret (^) Registers a value with the logger, so the value will be masked from the logs. Multi-line secrets are not allowed. ```javascript setSecret(val:string):void ``` - + Param | Type | Description --- | --- | --- val | string | value to register - - + +
- + ## Secure Files - + --- - + Retrieve secure files details required to download the file
- + ### task.getSecureFileName (^) Gets the name for a secure file @@ -669,14 +772,14 @@ Gets the name for a secure file ```javascript getSecureFileName(id:string):string ``` - + Param | Type | Description --- | --- | --- id | string | secure file id - +
- + ### task.getSecureFileTicket (^) Gets the secure file ticket that can be used to download the secure file contents @@ -684,23 +787,23 @@ Gets the secure file ticket that can be used to download the secure file content ```javascript getSecureFileTicket(id:string):string ``` - + Param | Type | Description --- | --- | --- id | string | name of the secure file - - + +
- + ## Disk Functions - + --- - + Functions for disk operations
- + ### task.which (^) Returns path of a tool had the tool actually been invoked. Resolves via paths. If you check and the tool does not exist, it will throw. @@ -709,15 +812,15 @@ If you check and the tool does not exist, it will throw. ```javascript which(tool:string, check?:boolean):string ``` - + Param | Type | Description --- | --- | --- tool | string | name of the tool check | boolean | whether to check if tool exists - +
- + ### task.checkPath (^) Checks whether a path exists. If the path does not exist, it will throw. @@ -726,78 +829,78 @@ If the path does not exist, it will throw. ```javascript checkPath(p:string, name:string):void ``` - + Param | Type | Description --- | --- | --- p | string | path to check name | string | name only used in error message to identify the path - +
- + ### task.exist (^) Returns whether a path exists. -@returns boolean +@returns boolean ```javascript exist(path:string):boolean ``` - + Param | Type | Description --- | --- | --- path | string | path to check - +
- + ### task.cd (^) Change working directory. -@returns void +@returns void ```javascript cd(path:string):void ``` - + Param | Type | Description --- | --- | --- path | string | new working directory path - +
- + ### task.cp (^) Copies a file or folder. ```javascript cp(source:string, dest:string, options?:string, continueOnError?:boolean):void ``` - + Param | Type | Description --- | --- | --- source | string | source path dest | string | destination path -options | string | string \-r, \-f or \-rf for recursive and force +options | string | string \-r, \-f or \-rf for recursive and force continueOnError | boolean | optional. whether to continue on error retryCount | number | optional. Retry count to copy the file. It might help to resolve intermittent issues e.g. with UNC target paths on a remote host. - +
- + ### task.mv (^) Moves a path. ```javascript mv(source:string, dest:string, options?:string, continueOnError?:boolean):void ``` - + Param | Type | Description --- | --- | --- source | string | source path dest | string | destination path -options | string | string \-f or \-n for force and no clobber +options | string | string \-f or \-n for force and no clobber continueOnError | boolean | optional. whether to continue on error - +
- + ### task.mkdirP (^) Make a directory. Creates the full path with folders in between Will throw if it fails @@ -806,27 +909,27 @@ Will throw if it fails ```javascript mkdirP(p:string):void ``` - + Param | Type | Description --- | --- | --- p | string | path to create - +
- + ### task.FindOptions (^) Interface for FindOptions Contains properties to control whether to follow symlinks - + Property | Type | Description --- | --- | --- allowBrokenSymbolicLinks | boolean | When true, broken symbolic link will not cause an error. followSpecifiedSymbolicLink | boolean | Equivalent to the \-H command line option. Indicates whether to traverse descendants if the specified path is a symbolic link directory. Does not cause nested symbolic link directories to be traversed. followSymbolicLinks | boolean | Equivalent to the \-L command line option. Indicates whether to traverse descendants of symbolic link directories. - +
- + ### task.find (^) Recursively finds all paths a given path. Returns an array of paths. @@ -834,15 +937,15 @@ Recursively finds all paths a given path. Returns an array of paths. ```javascript find(findPath:string, options?:FindOptions):string[] ``` - + Param | Type | Description --- | --- | --- findPath | string | path to search options | FindOptions | optional. defaults to \{ followSymbolicLinks: true \}. following soft links is generally appropriate unless deleting files. - +
- + ### task.rmRF (^) Remove a path recursively with force Returns whether it succeeds @@ -851,14 +954,14 @@ Returns whether it succeeds ```javascript rmRF(path:string):void ``` - + Param | Type | Description --- | --- | --- path | string | path to remove - +
- + ### task.pushd (^) Change working directory and push it on the stack @@ -866,14 +969,14 @@ Change working directory and push it on the stack ```javascript pushd(path:string):void ``` - + Param | Type | Description --- | --- | --- path | string | new working directory path - +
- + ### task.popd (^) Change working directory back to previously pushed directory @@ -881,10 +984,10 @@ Change working directory back to previously pushed directory ```javascript popd():void ``` - +
- + ### task.resolve (^) Resolves a sequence of paths or path segments into an absolute path. Calls node.js path.resolve() @@ -893,89 +996,89 @@ Allows L0 testing with consistent path formats on Mac/Linux and Windows in the m ```javascript resolve(pathSegments:any[]):string ``` - + Param | Type | Description --- | --- | --- -pathSegments | any\[\] | - +pathSegments | any\[\] | +
- + ### task.stats (^) -Get's stat on a path. +Get's stat on a path. Useful for checking whether a file or directory. Also getting created, modified and accessed time. see [fs.stat](https://nodejs.org/api/fs.html#fs_class_fs_stats) -@returns fsStat +@returns fsStat ```javascript stats(path:string):FsStats ``` - + Param | Type | Description --- | --- | --- path | string | path to check - +
- + ### task.writeFile (^) ```javascript writeFile(file:string, data:any, options?:string | FsOptions):void ``` - + Param | Type | Description --- | --- | --- -file | string | -data | any | -options | string \| FsOptions | - - +file | string | +data | any | +options | string \| FsOptions | + +
- + ## Globbing - + --- - + Functions for matching file paths
- + ### task.MatchOptions (^) - + Property | Type | Description --- | --- | --- -debug | boolean | -nobrace | boolean | -noglobstar | boolean | -dot | boolean | -noext | boolean | -nocase | boolean | -nonull | boolean | -matchBase | boolean | -nocomment | boolean | -nonegate | boolean | -flipNegate | boolean | - +debug | boolean | +nobrace | boolean | +noglobstar | boolean | +dot | boolean | +noext | boolean | +nocase | boolean | +nonull | boolean | +matchBase | boolean | +nocomment | boolean | +nonegate | boolean | +flipNegate | boolean | +
- + ### task.match (^) Applies glob patterns to a list of paths. Supports interleaved exclude patterns. ```javascript match(list:string[], patterns:string[] | string, patternRoot?:string, options?:MatchOptions):string[] ``` - + Param | Type | Description --- | --- | --- list | string\[\] | array of paths patterns | string\[\] \| string | patterns to apply. supports interleaved exclude patterns. patternRoot | string | optional. default root to apply to unrooted patterns. not applied to basename\-only patterns when matchBase:true. options | MatchOptions | optional. defaults to \{ dot: true, nobrace: true, nocase: process.platform == 'win32' \}. - +
- + ### task.findMatch (^) Determines the find root from a list of patterns. Performs the find and then applies the glob patterns. Supports interleaved exclude patterns. Unrooted patterns are rooted using defaultRoot, unless @@ -984,31 +1087,31 @@ defaultRoot is used as the find root. ```javascript findMatch(defaultRoot:string, patterns:string[] | string, findOptions?:FindOptions, matchOptions?:MatchOptions):string[] ``` - + Param | Type | Description --- | --- | --- defaultRoot | string | default path to root unrooted patterns. falls back to System.DefaultWorkingDirectory or process.cwd\(\). patterns | string\[\] \| string | pattern or array of patterns to apply findOptions | FindOptions | defaults to \{ followSymbolicLinks: true \}. following soft links is generally appropriate unless deleting files. matchOptions | MatchOptions | defaults to \{ dot: true, nobrace: true, nocase: process.platform == 'win32' \} - +
- + ### task.filter (^) Filter to apply glob patterns ```javascript filter(pattern:string, options?:MatchOptions):(element: string, indexed: number, array: string[]) => boolean ``` - + Param | Type | Description --- | --- | --- pattern | string | pattern to apply options | MatchOptions | optional. defaults to \{ dot: true, nobrace: true, nocase: process.platform == 'win32' \}. - +
- + ### task.legacyFindFiles (^) 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. @@ -1017,24 +1120,24 @@ when porting tasks to Node from the PowerShell or PowerShell3 execution handler. ```javascript legacyFindFiles(rootDirectory:string, pattern:string, includeFiles?:boolean, includeDirectories?:boolean):string[] ``` - + Param | Type | Description --- | --- | --- rootDirectory | string | path to root unrooted patterns with pattern | string | include and exclude patterns includeFiles | boolean | whether to include files in the result. defaults to true when includeFiles and includeDirectories are both false includeDirectories | boolean | whether to include directories in the result - - + +
- + ## Localization - + --- - + Localization is optional but is supported using these functions at runtime - + ```javascript /// @@ -1054,7 +1157,7 @@ var errMsg = tl.loc('FailedWithReturnCode', code)); ```
- + ### task.setResourcePath (^) Sets the location of the resources json. This is typically the task.json file. Call once at the beginning of the script before any calls to loc. @@ -1063,14 +1166,14 @@ Call once at the beginning of the script before any calls to loc. ```javascript setResourcePath(path:string):void ``` - + Param | Type | Description --- | --- | --- path | string | Full path to the json. - +
- + ### task.loc (^) Gets the localized string from the json resource file. Optionally formats with additional params. @@ -1078,24 +1181,24 @@ Gets the localized string from the json resource file. Optionally formats with ```javascript loc(key:string, param:any[]):string ``` - + Param | Type | Description --- | --- | --- key | string | key of the resources string in the resource file param | any\[\] | additional params for formatting the string - - + +
- + ## Proxy - + --- - + Funtions for web proxy settings
- + ### task.getHttpProxyConfiguration (^) Gets http proxy configuration used by Build/Release agent @@ -1103,8 +1206,8 @@ Gets http proxy configuration used by Build/Release agent ```javascript getHttpProxyConfiguration(requestUrl?:string):ProxyConfiguration ``` - + Param | Type | Description --- | --- | --- -requestUrl | string | - +requestUrl | string | + diff --git a/node/docs/docs.json b/node/docs/docs.json index aac80228a..34be6ceb0 100644 --- a/node/docs/docs.json +++ b/node/docs/docs.json @@ -12,8 +12,10 @@ "Summary": "Functions for retrieving inputs for the task", "Document": [ "task.getInput", + "task.getInputRequired", "task.getBoolInput", "task.getPathInput", + "task.getPathInputRequired", "task.filePathSupplied", "task.getDelimitedInput", "task.getVariable", @@ -46,9 +48,13 @@ "Summary": "Retrieve service connections (previously called \"service endpoints\") and authorization details", "Document": [ "task.getEndpointUrl", + "task.getEndpointUrlRequired", "task.getEndpointDataParameter", + "task.getEndpointDataParameterRequired", "task.getEndpointAuthorizationScheme", + "task.getEndpointAuthorizationSchemeRequired", "task.getEndpointAuthorizationParameter", + "task.getEndpointAuthorizationParameterRequired", "task.EndpointAuthorization", "task.getEndpointAuthorization" ] diff --git a/node/mock-task.ts b/node/mock-task.ts index 52af95357..9ddb3a5a8 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -60,6 +60,7 @@ module.exports.setSecret = task.setSecret; module.exports.getTaskVariable = task.getTaskVariable; module.exports.setTaskVariable = task.setTaskVariable; module.exports.getInput = task.getInput; +module.exports.getInputRequired = task.getInputRequired; module.exports.getBoolInput = task.getBoolInput; module.exports.getDelimitedInput = task.getDelimitedInput; module.exports.filePathSupplied = task.filePathSupplied; @@ -75,13 +76,22 @@ function getPathInput(name: string, required?: boolean, check?: boolean): string } module.exports.getPathInput = getPathInput; +function getPathInputRequired(name: string, check?: boolean): string { + return getPathInput(name, true, check)!; +} +module.exports.getPathInputRequired = getPathInputRequired; + //----------------------------------------------------- // Endpoint Helpers //----------------------------------------------------- module.exports.getEndpointUrl = task.getEndpointUrl; +module.exports.getEndpointUrlRequired = task.getEndpointUrlRequired; module.exports.getEndpointDataParameter = task.getEndpointDataParameter; +module.exports.getEndpointDataParameterRequired = task.getEndpointDataParameterRequired; module.exports.getEndpointAuthorizationScheme = task.getEndpointAuthorizationScheme; +module.exports.getEndpointAuthorizationSchemeRequired = task.getEndpointAuthorizationSchemeRequired; module.exports.getEndpointAuthorizationParameter = task.getEndpointAuthorizationParameter; +module.exports.getEndpointAuthorizationParameterRequired = task.getEndpointAuthorizationParameterRequired; module.exports.getEndpointAuthorization = task.getEndpointAuthorization; // TODO: should go away when task lib diff --git a/node/task.ts b/node/task.ts index db216207d..b2daf5cc7 100644 --- a/node/task.ts +++ b/node/task.ts @@ -153,7 +153,7 @@ export function getVariables(): VariableInfo[] { /** * Sets a variable which will be available to subsequent tasks as well. - * + * * @param name name of the variable to set * @param val value to set * @param secret whether variable is secret. Multi-line secrets are not allowed. Optional, defaults to false @@ -228,6 +228,17 @@ export function getInput(name: string, required?: boolean): string | undefined { return inval; } +/** + * Gets the value of an input. + * If the value is not set, it will throw. + * + * @param name name of the input to get + * @returns string + */ +export function getInputRequired(name: string): string { + return getInput(name, true)!; +} + /** * Gets the value of an input and converts to a bool. Convenience. * If required is true and the value is not set, it will throw. @@ -310,6 +321,20 @@ export function getPathInput(name: string, required?: boolean, check?: boolean): return inval; } +/** + * Gets the value of a path input + * It will be quoted for you if it isn't already and contains spaces + * If the value is not set, it will throw. + * If check is true and the path does not exist, it will throw. + * + * @param name name of the input to get + * @param check whether path is checked. optional, defaults to false + * @returns string + */ +export function getPathInputRequired(name: string, check?: boolean): string { + return getPathInput(name, true, check)!; +} + //----------------------------------------------------- // Endpoint Helpers //----------------------------------------------------- @@ -333,6 +358,17 @@ export function getEndpointUrl(id: string, optional: boolean): string | undefine return urlval; } +/** + * Gets the url for a service endpoint + * If the url was not set, it will throw. + * + * @param id name of the service endpoint + * @returns string + */ +export function getEndpointUrlRequired(id: string): string { + return getEndpointUrl(id, false)!; +} + /* * Gets the endpoint data parameter value with specified key for a service endpoint * If the endpoint data parameter was not set and is not optional, it will throw. @@ -353,6 +389,18 @@ export function getEndpointDataParameter(id: string, key: string, optional: bool return dataParamVal; } +/* + * Gets the endpoint data parameter value with specified key for a service endpoint + * If the endpoint data parameter was not set, it will throw. + * + * @param id name of the service endpoint + * @param key of the parameter + * @returns {string} value of the endpoint data parameter + */ +export function getEndpointDataParameterRequired(id: string, key: string): string { + return getEndpointDataParameter(id, key, false)!; +} + /** * Gets the endpoint authorization scheme for a service endpoint * If the endpoint authorization scheme is not set and is not optional, it will throw. @@ -372,6 +420,17 @@ export function getEndpointAuthorizationScheme(id: string, optional: boolean): s return authScheme; } +/** + * Gets the endpoint authorization scheme for a service endpoint + * If the endpoint authorization scheme is not set, it will throw. + * + * @param id name of the service endpoint + * @returns {string} value of the endpoint authorization scheme + */ +export function getEndpointAuthorizationSchemeRequired(id: string): string { + return getEndpointAuthorizationScheme(id, false)!; +} + /** * Gets the endpoint authorization parameter value for a service endpoint with specified key * If the endpoint authorization parameter is not set and is not optional, it will throw. @@ -391,6 +450,19 @@ export function getEndpointAuthorizationParameter(id: string, key: string, optio debug(id + ' auth param ' + key + ' = ' + authParam); return authParam; } + +/** + * Gets the endpoint authorization parameter value for a service endpoint with specified key + * If the endpoint authorization parameter is not set, it will throw. + * + * @param id name of the service endpoint + * @param key key to find the endpoint authorization parameter + * @returns {string} value of the endpoint authorization parameter value + */ +export function getEndpointAuthorizationParameterRequired(id: string, key: string): string { + return getEndpointAuthorizationParameter(id, key, false)!; +} + /** * Interface for EndpointAuthorization * Contains a schema and a string/string dictionary of auth data diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index 656e636f3..aa0fa1509 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -60,6 +60,44 @@ describe('Input Tests', function () { done(); }) + + it('gets input value', function (done) { + this.timeout(1000); + + process.env['INPUT_UNITTESTINPUT'] = 'test value'; + im._loadData(); + + var inval = tl.getInputRequired('UnitTestInput'); + assert.strictEqual(inval, 'test value'); + + done(); + }) + it('should clear input envvar', function (done) { + this.timeout(1000); + + process.env['INPUT_UNITTESTINPUT'] = 'test value'; + im._loadData(); + var inval = tl.getInputRequired('UnitTestInput'); + assert.strictEqual(inval, 'test value'); + assert(!process.env['INPUT_UNITTESTINPUT'], 'input envvar should be cleared'); + + done(); + }) + it('required input throws', function (done) { + this.timeout(1000); + + var worked: boolean = false; + try { + var inval = tl.getInputRequired('SomeUnsuppliedRequiredInput'); + worked = true; + } + catch (err) { } + + assert(!worked, 'req input should have not have worked'); + + done(); + }) + // getVariable tests it('gets a variable', function (done) { this.timeout(1000); @@ -416,6 +454,33 @@ describe('Input Tests', function () { done(); }) + it('gets a required endpoint url', function (done) { + this.timeout(1000); + + process.env['ENDPOINT_URL_id1'] = 'http://url'; + im._loadData(); + + var url = tl.getEndpointUrlRequired('id1'); + assert.equal(url, 'http://url', 'url should match'); + + done(); + }) + it('required endpoint url throws', function (done) { + this.timeout(1000); + + im._loadData(); + + var worked: boolean = false; + try { + var url = tl.getEndpointUrlRequired('SomeUnsuppliedRequiredInput'); + worked = true; + } + catch (err) { } + + assert(!worked, 'req endpoint url should have not have worked'); + + done(); + }) it('gets an endpoint auth', function (done) { this.timeout(1000); @@ -472,6 +537,34 @@ describe('Input Tests', function () { done(); }) + it('gets required endpoint auth scheme', function (done) { + this.timeout(1000); + process.env['ENDPOINT_AUTH_SCHEME_id1'] = 'scheme1'; + im._loadData(); + + var data = tl.getEndpointAuthorizationSchemeRequired('id1'); + assert(data, 'should return a string value'); + assert.equal(data, 'scheme1', 'should be correct scheme'); + assert(!process.env['ENDPOINT_AUTH_SCHEME_id1'], 'should clear auth envvar'); + + done(); + }) + it('required endpoint auth scheme throws', function (done) { + this.timeout(1000); + + im._loadData(); + + var worked: boolean = false; + try { + var data = tl.getEndpointAuthorizationSchemeRequired('SomeUnsuppliedRequiredInput'); + worked = true; + } + catch (err) { } + + assert(!worked, 'req endpoint auth scheme should have not have worked'); + + done(); + }) it('gets endpoint auth parameters', function (done) { this.timeout(1000); process.env['ENDPOINT_AUTH_PARAMETER_id1_PARAM1'] = 'value1'; @@ -493,6 +586,33 @@ describe('Input Tests', function () { done(); }) + it('gets required endpoint auth parameters', function (done) { + this.timeout(1000); + process.env['ENDPOINT_AUTH_PARAMETER_id1_PARAM1'] = 'value1'; + im._loadData(); + + var data = tl.getEndpointAuthorizationParameterRequired('id1', 'param1'); + assert(data, 'should return a string value'); + assert.equal(data, 'value1', 'should be correct auth param'); + assert(!process.env['ENDPOINT_AUTH_PARAMETER_id1_PARAM1'], 'should clear auth envvar'); + + done(); + }) + it('throws if endpoint auth parameter is not set', function (done) { + this.timeout(1000); + im._loadData(); + + var worked: boolean = false; + try { + var data = tl.getEndpointAuthorizationParameterRequired('id1', 'noparam'); + worked = true; + } + catch (err) { } + + assert(!worked, 'get endpoint authorization parameter should have not have worked'); + + done(); + }) it('gets an endpoint data', function (done) { this.timeout(1000); process.env['ENDPOINT_DATA_id1_PARAM1'] = 'val1'; @@ -513,6 +633,32 @@ describe('Input Tests', function () { done(); }) + it('gets required endpoint data', function (done) { + this.timeout(1000); + process.env['ENDPOINT_DATA_id1_PARAM1'] = 'val1'; + im._loadData(); + + var data = tl.getEndpointDataParameterRequired('id1', 'param1'); + assert(data, 'should return a string value'); + assert.equal(data, 'val1', 'should be correct object'); + + done(); + }) + it('throws if endpoint data is not set', function (done) { + this.timeout(1000); + im._loadData(); + + var worked: boolean = false; + try { + var data = tl.getEndpointDataParameterRequired('id1', 'noparam'); + worked = true; + } + catch (err) { } + + assert(!worked, 'get endpoint data should have not have worked'); + + done(); + }) // getSecureFileName/getSecureFileTicket/getSecureFiles tests it('gets a secure file name', function (done) { this.timeout(1000); @@ -745,6 +891,110 @@ describe('Input Tests', function () { done(); }) + // getPathInputRequired tests + it('gets path input required value', function (done) { + this.timeout(1000); + + var inputValue = 'test.txt' + process.env['INPUT_PATH1'] = inputValue; + im._loadData(); + + var path = tl.getPathInputRequired('path1', /*check=*/false); + assert(path, 'should return a path'); + assert.equal(path, inputValue, 'test path value'); + + done(); + }) + it('throws if required path not supplied', function (done) { + this.timeout(1000); + + var stdStream = testutil.createStringStream(); + tl.setStdStream(stdStream); + + var worked: boolean = false; + try { + var path = tl.getPathInputRequired(null, /*check=*/false); + worked = true; + } + catch (err) { } + + assert(!worked, 'req path should have not have worked'); + + done(); + }) + it('get required path invalid checked throws', function (done) { + this.timeout(1000); + + var stdStream = testutil.createStringStream(); + tl.setStdStream(stdStream); + + var worked: boolean = false; + try { + var path = tl.getPathInputRequired('some_missing_path', /*check=*/true); + worked = true; + } + catch (err) { } + + assert(!worked, 'invalid checked path should have not have worked'); + + done(); + }) + it('gets path input required value with space', function (done) { + this.timeout(1000); + + var inputValue = 'file name.txt'; + var expectedValue = 'file name.txt'; + process.env['INPUT_PATH1'] = inputValue; + im._loadData(); + + var path = tl.getPathInputRequired('path1', /*check=*/false); + assert(path, 'should return a path'); + assert.equal(path, expectedValue, 'returned ' + path + ', expected: ' + expectedValue); + + done(); + }) + it('gets path required value with check and exist', function (done) { + this.timeout(1000); + + var errStream = testutil.createStringStream(); + tl.setErrStream(errStream); + + var inputValue = __filename; + process.env['INPUT_PATH1'] = inputValue; + im._loadData(); + + var path = tl.getPathInputRequired('path1', /*check=*/true); + assert(path, 'should return a path'); + assert.equal(path, inputValue, 'test path value'); + + var errMsg = errStream.getContents(); + assert(errMsg === "", "no err") + + done(); + }) + it('gets path required value with check and not exist', function (done) { + this.timeout(1000); + + var stdStream = testutil.createStringStream(); + tl.setStdStream(stdStream); + + var inputValue = "someRandomFile.txt"; + process.env['INPUT_PATH1'] = inputValue; + im._loadData(); + + var worked: boolean = false; + try { + var path = tl.getPathInputRequired('path1', /*check=*/true); + worked = true; + } + catch (err) { + assert(err.message.indexOf("Not found") >= 0, "error should have said Not found"); + } + assert(!worked, 'invalid checked path should have not have worked'); + + done(); + }) + // filePathSupplied tests it('filePathSupplied checks not supplied', function (done) { this.timeout(1000); From 7486d51579399a750da4b4be768dbd9a9edbd54e Mon Sep 17 00:00:00 2001 From: Svetlana Maliugina <101865756+SvetlanaMaliugina@users.noreply.github.com> Date: Tue, 17 May 2022 13:50:22 +0300 Subject: [PATCH 231/259] Bump patch version (#837) --- node/package-lock.json | 2 +- node/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index e4a3966f4..70db4479c 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.2.0", + "version": "3.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index d1a32001d..0bede97d8 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.2.0", + "version": "3.2.1", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From fb3443891f1ca4bca1a1eb457268e5a670abc90f Mon Sep 17 00:00:00 2001 From: kirill-ivlev <102740624+kirill-ivlev@users.noreply.github.com> Date: Wed, 25 May 2022 16:43:07 +0400 Subject: [PATCH 232/259] added formatted proxy URL (#834) * added formatted proxy URL * version increment --- node/docs/proxy.md | 19 ++++++++++++ node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 26 +++++++++++++++- node/test/gethttpproxytests.ts | 56 ++++++++++++++++++++++++++++++++++ node/test/tsconfig.json | 3 +- 6 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 node/test/gethttpproxytests.ts diff --git a/node/docs/proxy.md b/node/docs/proxy.md index 11c6a2495..7c6eb437e 100644 --- a/node/docs/proxy.md +++ b/node/docs/proxy.md @@ -14,6 +14,7 @@ export interface ProxyConfiguration { proxyUsername?: string; proxyPassword?: string; proxyBypassHosts?: string[]; + proxyFormattedUrl: string; } ``` @@ -61,6 +62,24 @@ async function run() { run(); ``` +For some external applications executed from the shell, you might need to set an environment variable that contains a formatted URL +in the following format: protocol://user:password@hostname:port +You can retrieve such configuration directly from task-lib: +```typescript +import tl = require('azure-pipelines-task-lib/task'); + +async function run() { + let proxy = tl.getProxyConfiguration() + + process.env['http_proxy'] = proxy.proxyFormattedUrl; + process.env['https_proxy'] = proxy.proxyFormattedUrl; + const gitPath: string = tl.which('git'); + const gitPull = tl.tool(gitPath); + await gitPull.exec() +} + +run(); +``` #### PowerShell Lib Method for retrieve proxy settings in PowerShell lib diff --git a/node/package-lock.json b/node/package-lock.json index 70db4479c..4f9cf9b6d 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.2.1", + "version": "3.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 0bede97d8..6e17c92ec 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.2.1", + "version": "3.3.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index b2daf5cc7..aeb52c885 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1839,11 +1839,33 @@ export function findMatch(defaultRoot: string, patterns: string[] | string, find export interface ProxyConfiguration { proxyUrl: string; + /** + * Proxy URI formated as: protocol://username:password@hostname:port + * + * For tools that require setting proxy configuration in the single environment variable + */ + proxyFormattedUrl: string; proxyUsername?: string; proxyPassword?: string; proxyBypassHosts?: string[]; } +/** + * Build Proxy URL in the following format: protocol://username:password@hostname:port + * @param proxyUrl Url address of the proxy server (eg: http://example.com) + * @param proxyUsername Proxy username (optional) + * @param proxyPassword Proxy password (optional) + * @returns string + */ +function getProxyFormattedUrl(proxyUrl: string, proxyUsername: string | undefined, proxyPassword: string | undefined): string { + const parsedUrl: URL = new URL(proxyUrl); + let proxyAddress: string = `${parsedUrl.protocol}//${parsedUrl.host}`; + if (proxyUsername) { + proxyAddress = `${parsedUrl.protocol}//${proxyUsername}:${proxyPassword}@${parsedUrl.host}`; + } + return proxyAddress; +} + /** * Gets http proxy configuration used by Build/Release agent * @@ -1869,11 +1891,13 @@ export function getHttpProxyConfiguration(requestUrl?: string): ProxyConfigurati return null; } else { + const proxyAddress = getProxyFormattedUrl(proxyUrl, proxyUsername, proxyPassword) return { proxyUrl: proxyUrl, proxyUsername: proxyUsername, proxyPassword: proxyPassword, - proxyBypassHosts: proxyBypassHosts + proxyBypassHosts: proxyBypassHosts, + proxyFormattedUrl: proxyAddress }; } } diff --git a/node/test/gethttpproxytests.ts b/node/test/gethttpproxytests.ts new file mode 100644 index 000000000..b68488412 --- /dev/null +++ b/node/test/gethttpproxytests.ts @@ -0,0 +1,56 @@ +import * as assert from 'assert'; +import * as tl from '../_build/task'; + +enum ProxyEnvironmentEnum { + proxyUrl = 'AGENT_PROXYURL', + proxyUsername = 'AGENT_PROXYUSERNAME', + proxyPassword = 'AGENT_PROXYPASSWORD', + proxyBypass = 'AGENT_PROXYBYPASSLIST' +} + +describe('GetHttpProxyConfiguration Tests', () => { + const proxyHost: string = 'proxy.example.com'; + const proxyPort: number = 8888; + const proxyUrl: string = `http://${proxyHost}:${proxyPort}`; + const proxyUsername: string = 'proxyUser'; + const proxyPassword: string = 'proxyPassword'; + const proxyByPass: string[] = ['http://bypass.example.com']; + const formatedUrlWithoutCrednetials = proxyUrl; + const fortmatedUrlWithCredentials = `http://${proxyUsername}:${proxyPassword}@${proxyHost}:${proxyPort}`; + + it('returns a valid proxy configuration if no credentials set', () => { + process.env[ProxyEnvironmentEnum.proxyUrl] = proxyUrl; + process.env[ProxyEnvironmentEnum.proxyBypass] = JSON.stringify(proxyByPass); + const expected: tl.ProxyConfiguration = { + proxyUrl: proxyUrl, + proxyBypassHosts: proxyByPass, + proxyUsername: undefined, + proxyPassword: undefined, + proxyFormattedUrl: formatedUrlWithoutCrednetials + } + const result = tl.getHttpProxyConfiguration(); + assert.deepStrictEqual(result, expected, 'it should have valid configuration'); + }); + + it('returns valid proxy configuration if credentials set', () => { + process.env[ProxyEnvironmentEnum.proxyUrl] = proxyUrl; + process.env[ProxyEnvironmentEnum.proxyUsername] = proxyUsername; + process.env[ProxyEnvironmentEnum.proxyPassword] = proxyPassword; + process.env[ProxyEnvironmentEnum.proxyBypass] = JSON.stringify(proxyByPass); + const expected: tl.ProxyConfiguration = { + proxyUrl: proxyUrl, + proxyBypassHosts: proxyByPass, + proxyFormattedUrl: fortmatedUrlWithCredentials, + proxyPassword: proxyPassword, + proxyUsername: proxyUsername + } + const result = tl.getHttpProxyConfiguration(); + assert.deepStrictEqual(result, expected, 'it should have credentials in formatted url'); + }); + + it('returns null if host should be bypassed', () => { + process.env[ProxyEnvironmentEnum.proxyUrl] = proxyUrl; + const result = tl.getHttpProxyConfiguration(proxyByPass[0]); + assert.strictEqual(result, null, 'result should be null'); + }); +}) \ No newline at end of file diff --git a/node/test/tsconfig.json b/node/test/tsconfig.json index 3d5dbb2f0..98230ecfc 100644 --- a/node/test/tsconfig.json +++ b/node/test/tsconfig.json @@ -23,6 +23,7 @@ "findmatchtests.ts", "mocktests.ts", "retrytests.ts", - "isuncpathtests.ts" + "isuncpathtests.ts", + "gethttpproxytests.ts" ] } From b72e14acdd6ac4803cdb1812bed44ce7fe58f5d8 Mon Sep 17 00:00:00 2001 From: kirill-ivlev <102740624+kirill-ivlev@users.noreply.github.com> Date: Wed, 25 May 2022 23:44:56 +0400 Subject: [PATCH 233/259] Update minimatch to version 3.0.5 to fix vulnerability (#836) * version bump --- node/package-lock.json | 23 ++++++++++++++++------- node/package.json | 4 ++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 4f9cf9b6d..c998d4bf5 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.3.0", + "version": "3.3.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -91,9 +91,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "brace-expansion": { "version": "1.1.11", @@ -293,9 +293,9 @@ } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", "requires": { "brace-expansion": "^1.1.7" } @@ -358,6 +358,15 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", diff --git a/node/package.json b/node/package.json index 6e17c92ec..2048e72b5 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.3.0", + "version": "3.3.1", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", @@ -27,7 +27,7 @@ }, "homepage": "https://github.com/Microsoft/azure-pipelines-task-lib", "dependencies": { - "minimatch": "3.0.4", + "minimatch": "3.0.5", "mockery": "^1.7.0", "q": "^1.5.1", "semver": "^5.1.0", From ad2d00fb6aa5d7d33d59426a8c80c41a4493a7a8 Mon Sep 17 00:00:00 2001 From: "microsoft-github-policy-service[bot]" <77245923+microsoft-github-policy-service[bot]@users.noreply.github.com> Date: Fri, 3 Jun 2022 14:49:33 +0400 Subject: [PATCH 234/259] Microsoft mandatory file (#839) Co-authored-by: microsoft-github-policy-service[bot] <77245923+microsoft-github-policy-service[bot]@users.noreply.github.com> Co-authored-by: Konstantin Tyukalov <52399739+KonstantinTyukalov@users.noreply.github.com> --- SECURITY.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..869fdfe2b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + From d4cc5339667e426c9dee50d2c2c655a40b901986 Mon Sep 17 00:00:00 2001 From: AndreyIvanov42 <93121155+AndreyIvanov42@users.noreply.github.com> Date: Mon, 11 Jul 2022 13:57:57 +0400 Subject: [PATCH 235/259] Update pipeline to include a step for publishing artifacts. (#848) * Update pipeline * Update azure-pipelines.yml * nodeversion as variable Co-authored-by: Andrey Ivanov --- azure-pipelines-steps-node.yml | 16 +++++ azure-pipelines-steps-test-build.yml | 10 ++++ azure-pipelines.yml | 90 +++++++--------------------- 3 files changed, 49 insertions(+), 67 deletions(-) create mode 100644 azure-pipelines-steps-node.yml create mode 100644 azure-pipelines-steps-test-build.yml diff --git a/azure-pipelines-steps-node.yml b/azure-pipelines-steps-node.yml new file mode 100644 index 000000000..8e2591383 --- /dev/null +++ b/azure-pipelines-steps-node.yml @@ -0,0 +1,16 @@ +parameters: +- name: nodeVersion + type: string + +steps: + # npm install + - task: Npm@1 + displayName: (azure-pipelines-task-lib) npm install + inputs: + command: install + workingDir: node + + - task: NodeTool@0 + displayName: (azure-pipelines-task-lib) use node ${{parameters.nodeVersion}} + inputs: + versionSpec: ${{parameters.nodeVersion}} diff --git a/azure-pipelines-steps-test-build.yml b/azure-pipelines-steps-test-build.yml new file mode 100644 index 000000000..38ca9e3e9 --- /dev/null +++ b/azure-pipelines-steps-test-build.yml @@ -0,0 +1,10 @@ +steps: + # test + - script: node make.js test + workingDirectory: node + displayName: (azure-pipelines-task-lib) node make.js test + + # build + - script: node make.js build + displayName: (azure-pipelines-task-lib) node make.js build + workingDirectory: node diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e6fdc0c03..9bafc145f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,6 +3,9 @@ trigger: - features/* - releases/* +variables: + nodeVersion: 10.23.0 + jobs: ################################################# - job: windows @@ -16,31 +19,22 @@ jobs: # azure-pipelines-task-lib ################################################################################ - # npm install - - task: Npm@1 - displayName: (azure-pipelines-task-lib) npm install - inputs: - command: install - workingDir: node + - template: azure-pipelines-steps-node.yml + parameters: + nodeVersion: $(nodeVersion) - # use node 10 - - task: NodeTool@0 - displayName: (azure-pipelines-task-lib) use node 10.23.0 - inputs: - versionSpec: "10.23.0" - - # build/test + # test - script: | chcp 437 node make.js test workingDirectory: node displayName: (azure-pipelines-task-lib) node make.js test - # build/test + # build - script: | chcp 437 - node make.js test - displayName: (azure-pipelines-task-lib) node make.js test + node make.js build + displayName: (azure-pipelines-task-lib) node make.js build workingDirectory: node ################################################################################ @@ -66,33 +60,17 @@ jobs: pool: vmImage: ubuntu-18.04 - steps: - ################################################################################ - # azure-pipelines-task-lib - ################################################################################ - - # npm install - - task: Npm@1 - displayName: (azure-pipelines-task-lib) npm install - inputs: - command: install - workingDir: node + steps: + - template: azure-pipelines-steps-node.yml + parameters: + nodeVersion: $(nodeVersion) + - template: azure-pipelines-steps-test-build.yml - # use node 10 - - task: NodeTool@0 - displayName: (azure-pipelines-task-lib) use node 10.23.0 + - task: PublishPipelineArtifact@1 inputs: - versionSpec: "10.23.0" - - # build/test - - script: node make.js test - workingDirectory: node - displayName: (azure-pipelines-task-lib) node make.js test - - # build/test - - script: node make.js test - displayName: (azure-pipelines-task-lib) node make.js test - workingDirectory: node + targetPath: 'node/_build' + artifactType: 'pipeline' + artifactName: 'npm-package' # For CI runs on master, automatically publish packages - bash: | @@ -113,29 +91,7 @@ jobs: vmImage: macOS-10.15 steps: - ################################################################################ - # azure-pipelines-task-lib - ################################################################################ - - # npm install - - task: Npm@1 - displayName: (azure-pipelines-task-lib) npm install - inputs: - command: install - workingDir: node - - # use node 10 - - task: NodeTool@0 - displayName: (azure-pipelines-task-lib) use node 10.23.0 - inputs: - versionSpec: "10.23.0" - - # build/test - - script: node make.js test - workingDirectory: node - displayName: (azure-pipelines-task-lib) node make.js test - - # build/test - - script: node make.js test - displayName: (azure-pipelines-task-lib) node make.js test - workingDirectory: node + - template: azure-pipelines-steps-node.yml + parameters: + nodeVersion: $(nodeVersion) + - template: azure-pipelines-steps-test-build.yml From 333a5f97773f19aa74495c9091ee880aa478fae2 Mon Sep 17 00:00:00 2001 From: AndreyIvanov42 <93121155+AndreyIvanov42@users.noreply.github.com> Date: Mon, 18 Jul 2022 16:09:15 +0400 Subject: [PATCH 236/259] Update contributing.md (#847) --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ed8e74deb..3385d692e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,13 @@ # Instructions for Contributing Code +## Current status + +Since at the moment work is proceeding on migrating to Node16, you need to remember the following: + +- master branch for SDK v3 (stable and targets Node10) +- releases/4.x branch for SDK v4 (preview and targets Node16) +- Changes that were merged for SDK v3 should be cherry-picked for SDK v4 once they are merged to the master branch + ## Contributing bug fixes We are currently accepting contributions in the form of bug fixes. A bug must have an issue tracking it in the issue tracker. Your pull request should include a link to the bug that you are fixing. If you've submitted a PR for a bug, please post a comment in the bug to avoid duplication of effort. From 35bcc3f831a6a8b7281eff17ac9a290cb9626e74 Mon Sep 17 00:00:00 2001 From: AndreyIvanov42 <93121155+AndreyIvanov42@users.noreply.github.com> Date: Wed, 27 Jul 2022 18:09:18 +0200 Subject: [PATCH 237/259] updated docs (#853) Co-authored-by: Andrey Ivanov --- node/docs/minagent.md | 4 ++++ node/docs/nodeEnvironment.md | 10 ++++++++++ node/docs/nodeVersioning.md | 6 +++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/node/docs/minagent.md b/node/docs/minagent.md index c72aa5f93..2d30abfc0 100644 --- a/node/docs/minagent.md +++ b/node/docs/minagent.md @@ -26,6 +26,10 @@ Use the details below to determine when specific agent features were added: * `node` handler - Added in 1.95.1. Used node v5.10.1. - Updated in 2.117.0 to use node v6.10.3. +* `node10` handler + - Added in 2.144.0. Used node v10.x +* `node16` handler + - Added in 2.206.1. Used node v16.x * `powershell3` handler - Added in 1.95.1 - Updated in 1.97 to propagate `Data` property for endpoints diff --git a/node/docs/nodeEnvironment.md b/node/docs/nodeEnvironment.md index 3e0e2a4dc..b729283cf 100644 --- a/node/docs/nodeEnvironment.md +++ b/node/docs/nodeEnvironment.md @@ -12,6 +12,16 @@ To leverage this capability, simply add `Node10` as an execution target: }, ``` +With agent version 2.206.1 Node 16 can be used. + +``` +"execution": { + "Node16": { + "target": "path/to/entry" + } +}, +``` + Existing `Node` execution targets will still resolve to a Node 6 environment for now to maintain back-compat. ### Testing your task diff --git a/node/docs/nodeVersioning.md b/node/docs/nodeVersioning.md index 9c69015ca..91a805ce1 100644 --- a/node/docs/nodeVersioning.md +++ b/node/docs/nodeVersioning.md @@ -2,16 +2,16 @@ ## Agent Node Handler -The agent currently has 2 different node handlers that it can use to execute node tasks: Node 6 and Node 10. +The agent currently has 3 different node handlers that it can use to execute node tasks: Node 6, Node 10, Node 16. The handler used depends on the `execution` property specified in the tasks `task.json`. -If the `execution` property is specified to be `Node`, the task will run on the Node 6 handler, if it is specified to be `Node10` it will run on the Node 10 handler. +If the `execution` property is specified to be `Node`, the task will run on the Node 6 handler, for `Node10` it will run on the Node 10 handler, for `Node16` it will run on the Node 16 handler. ## Mock-test Node Handler [Unit testing](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops#step-2-unit-testing-your-task-scripts) of tasks can be done using the task-lib's built in mock-task functionality. To ensure tests are run in the same environment as the agent, this library looks for a `task.json` file in the same directory as the supplied task entry point. If no `task.json` is found it searches all ancestor directories as well. -If the `task.json` is still not found, the library defaults to Node 10, otherwise it uses the appropriate handler based on the `execution` property. +If the `task.json` is still not found, the library defaults to Node 16, otherwise it uses the appropriate handler based on the `execution` property. If this version of node is not found on the path, the library downloads the appropriate version. ### Behavior overrides From ee582bc9b6c7ed2749929b0d66bc34af831e0eea Mon Sep 17 00:00:00 2001 From: Maxim Zaytsev Date: Tue, 23 Aug 2022 10:13:04 +0200 Subject: [PATCH 238/259] [Repo config] Add `CODEOWNERS` file (#855) --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..90f5aacd6 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Global rule: +* @microsoft/akvelon-build-task-team @microsoft/azure-pipelines-platform From 5adadaedd70e68a4130f339e0faf8b8a9a72d273 Mon Sep 17 00:00:00 2001 From: Roman-Shchukin <111063382+Roman-Shchukin@users.noreply.github.com> Date: Wed, 12 Oct 2022 08:43:03 +0200 Subject: [PATCH 239/259] Enable task authors to determine whether the task is running on a hosted agent, or not. (#869) * Added function getAgentMode * Function getAgentMode added to documentation * Fix of documentation --- node/docs/azure-pipelines-task-lib.json | 14 ++++++++++++++ node/docs/azure-pipelines-task-lib.md | 13 +++++++++++++ node/docs/docs.json | 3 ++- node/mock-answer.ts | 1 + node/mock-task.ts | 4 ++++ node/task.ts | 22 ++++++++++++++++++++++ 6 files changed, 56 insertions(+), 1 deletion(-) diff --git a/node/docs/azure-pipelines-task-lib.json b/node/docs/azure-pipelines-task-lib.json index 7b34249d1..16e076153 100644 --- a/node/docs/azure-pipelines-task-lib.json +++ b/node/docs/azure-pipelines-task-lib.json @@ -680,6 +680,20 @@ } ] }, + "getAgentMode": { + "name": "getAgentMode", + "members": {}, + "documentation": "Gets a agent hosted mode: Unknown, SelfHosted or MsHosted.\nRequires a 2.212.0 agent or higher for full functionality. With lower version returns AgentHostedMode.Unknown value. \n\n@returns AgentHostedMode", + "kind": "function", + "signatures": [ + { + "parameters": [], + "members": {}, + "return": "AgentHostedMode", + "documentation": "Gets a agent hosted mode: Unknown, SelfHosted or MsHosted.\nRequires a 2.212.0 agent or higher for full functionality. With lower version returns AgentHostedMode.Unknown value. \n\n@returns AgentHostedMode" + } + ] + }, "setVariable": { "name": "setVariable", "members": {}, diff --git a/node/docs/azure-pipelines-task-lib.md b/node/docs/azure-pipelines-task-lib.md index b39d9e61f..f94a8913f 100644 --- a/node/docs/azure-pipelines-task-lib.md +++ b/node/docs/azure-pipelines-task-lib.md @@ -35,6 +35,7 @@ import tl = require('azure-pipelines-task-lib/task') setVariable
getTaskVariable
setTaskVariable
+getAgentMode
### Execution (v) @@ -290,6 +291,18 @@ Limitations on an agent prior to 2.104.1: getVariables():VariableInfo[] ``` +
+
+ +### task.getAgentMode (^) +Gets a agent hosted mode: Unknown, SelfHosted or MsHosted. +Requires a 2.212.0 agent or higher for full functionality. With lower version returns AgentHostedMode.Unknown value. + +@returns AgentHostedMode +```javascript +getAgentMode():AgentHostedMode +``` +
diff --git a/node/docs/docs.json b/node/docs/docs.json index 34be6ceb0..3b7fd154d 100644 --- a/node/docs/docs.json +++ b/node/docs/docs.json @@ -23,7 +23,8 @@ "task.getVariables", "task.setVariable", "task.getTaskVariable", - "task.setTaskVariable" + "task.setTaskVariable", + "task.getAgentMode" ] }, "Execution": { diff --git a/node/mock-answer.ts b/node/mock-answer.ts index 26e39fde4..ec7d0b7f9 100644 --- a/node/mock-answer.ts +++ b/node/mock-answer.ts @@ -14,6 +14,7 @@ export interface TaskLibAnswers { find?: { [key: string]: string[] }, findMatch?: { [key: string]: string[] }, getPlatform?: { [key: string]: task.Platform }, + getAgentMode?: { [key: string]: task.AgentHostedMode }, legacyFindFiles?: { [key: string]: string[] }, ls?: { [key: string]: string }, osType?: { [key: string]: string }, diff --git a/node/mock-task.ts b/node/mock-task.ts index 9ddb3a5a8..fc374d3bd 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -222,6 +222,10 @@ export function getPlatform(): task.Platform { return mock.getResponse('getPlatform', 'getPlatform', module.exports.debug); } +export function getAgentMode(): task.AgentHostedMode { + return mock.getResponse('getAgentMode', 'getAgentMode', module.exports.debug); +} + export function cwd(): string { return mock.getResponse('cwd', 'cwd', module.exports.debug); } diff --git a/node/task.ts b/node/task.ts index aeb52c885..3facba81a 100644 --- a/node/task.ts +++ b/node/task.ts @@ -51,6 +51,12 @@ export enum Platform { Linux } +export enum AgentHostedMode { + Unknown, + SelfHosted, + MsHosted +} + //----------------------------------------------------- // General Helpers //----------------------------------------------------- @@ -659,6 +665,22 @@ export function getPlatform(): Platform { } } +/** + * Return hosted type of Agent + * @returns {AgentHostedMode} + */ +export function getAgentMode(): AgentHostedMode { + let agentCloudId = getVariable('Agent.CloudId'); + + if (agentCloudId === undefined) + return AgentHostedMode.Unknown; + + if (agentCloudId) + return AgentHostedMode.MsHosted; + + return AgentHostedMode.SelfHosted; +} + /** * Returns the process's current working directory. * see [process.cwd](https://nodejs.org/api/process.html#process_process_cwd) From 22ba7dd6ad106a690afe671d1dbd806c17921d59 Mon Sep 17 00:00:00 2001 From: Roman-Shchukin <111063382+Roman-Shchukin@users.noreply.github.com> Date: Tue, 1 Nov 2022 09:50:14 +0100 Subject: [PATCH 240/259] Updated mockery and mocha because of vulnerabilities (#875) --- node/package-lock.json | 636 ++++++++++++++++++++++++++++++++++++----- node/package.json | 8 +- 2 files changed, 562 insertions(+), 82 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index c998d4bf5..6e5a3110d 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.3.1", + "version": "3.3.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -80,6 +80,49 @@ "@types/node": "*" } }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -95,6 +138,12 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -104,6 +153,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -115,11 +173,80 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -150,23 +277,80 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, "form-data": { @@ -184,11 +368,24 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "get-port": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", @@ -207,6 +404,21 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -215,10 +427,16 @@ "function-bind": "^1.1.1" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "http-basic": { @@ -266,6 +484,15 @@ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-core-module": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -274,11 +501,84 @@ "has": "^1.0.3" } }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, "mime-db": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", @@ -300,93 +600,95 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" }, "dependencies": { - "commander": { - "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "brace-expansion": "^1.1.7" } } } }, "mockery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/mockery/-/mockery-1.7.0.tgz", - "integrity": "sha1-9O3g2HUMHJcnwnLqLGBiniyaHE8=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mockery/-/mockery-2.1.0.tgz", + "integrity": "sha512-9VkOmxKlWXoDO/h1jDZaS4lH33aWfRiJiNT/tKj+8OGzrcFDLo8d0syGdbsc3Bc4GvRXPb+NMMvojotmuGJTvA==" }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "once": { @@ -397,11 +699,35 @@ "wrappy": "1" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "parse-cache-control": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", "integrity": "sha1-juqz5U+laSD+Fro493+iGqzC104=" }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -412,6 +738,12 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -435,6 +767,15 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -449,6 +790,15 @@ "util-deprecate": "~1.0.1" } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -457,6 +807,12 @@ "resolve": "^1.1.6" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, "resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -477,6 +833,15 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "shelljs": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", @@ -487,6 +852,17 @@ "rechoir": "^0.6.2" } }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -495,6 +871,30 @@ "safe-buffer": "~5.1.0" } }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -543,6 +943,15 @@ } } }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -564,10 +973,81 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/node/package.json b/node/package.json index 2048e72b5..1af918122 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.3.1", + "version": "3.3.2", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", @@ -28,7 +28,7 @@ "homepage": "https://github.com/Microsoft/azure-pipelines-task-lib", "dependencies": { "minimatch": "3.0.5", - "mockery": "^1.7.0", + "mockery": "^2.1.0", "q": "^1.5.1", "semver": "^5.1.0", "shelljs": "^0.8.5", @@ -37,13 +37,13 @@ }, "devDependencies": { "@types/minimatch": "3.0.3", - "@types/mocha": "^5.0.0", + "@types/mocha": "^5.2.7", "@types/mockery": "^1.4.29", "@types/node": "^10.17.0", "@types/q": "^1.5.4", "@types/semver": "^7.3.4", "@types/shelljs": "^0.8.8", - "mocha": "5.2.0", + "mocha": "9.2.2", "typescript": "^4.0.0" } } From e05ff6fd82667ceaeb218a7fd70427313a4db916 Mon Sep 17 00:00:00 2001 From: Alexander Batishchev Date: Tue, 1 Nov 2022 13:13:01 -0700 Subject: [PATCH 241/259] Updated PSLIB_InvalidPattern0 to be more readable (#876) * Updated PSLIB_InvalidPattern0 to be more readable * Update LegacyFindFunctions.ps1 Co-authored-by: Kirill Ivlev <102740624+kirill-ivlev@users.noreply.github.com> --- powershell/VstsTaskSdk/LegacyFindFunctions.ps1 | 2 +- .../Strings/resources.resjson/en-US/resources.resjson | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/powershell/VstsTaskSdk/LegacyFindFunctions.ps1 b/powershell/VstsTaskSdk/LegacyFindFunctions.ps1 index f6aaa5a09..9e9e9ecc2 100644 --- a/powershell/VstsTaskSdk/LegacyFindFunctions.ps1 +++ b/powershell/VstsTaskSdk/LegacyFindFunctions.ps1 @@ -132,7 +132,7 @@ function Find-Files { } # Validate pattern does not end with a \. - if ($pattern[$pattern.Length - 1] -eq [System.IO.Path]::DirectorySeparatorChar) { + if ($pattern.EndsWith([System.IO.Path]::DirectorySeparatorChar)) { throw (Get-LocString -Key PSLIB_InvalidPattern0 -ArgumentList $pattern) } diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/en-US/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/en-US/resources.resjson index 66c17bc8d..fb2fa4e2a 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/en-US/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/en-US/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "Enumerating subdirectories failed for path: '{0}'", "loc.messages.PSLIB_FileNotFound0": "File not found: '{0}'", "loc.messages.PSLIB_Input0": "'{0}' input", - "loc.messages.PSLIB_InvalidPattern0": "Invalid pattern: '{0}'", + "loc.messages.PSLIB_InvalidPattern0": "Path cannot end with a directory separator character: '{0}'", "loc.messages.PSLIB_LeafPathNotFound0": "Leaf path not found: '{0}'", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "Path normalization/expansion failed. The path length was not returned by the Kernel32 subsystem for: '{0}'", "loc.messages.PSLIB_PathNotFound0": "Path not found: '{0}'", @@ -15,4 +15,4 @@ "loc.messages.PSLIB_StringFormatFailed": "String format failed.", "loc.messages.PSLIB_StringResourceKeyNotFound0": "String resource key not found: '{0}'", "loc.messages.PSLIB_TaskVariable0": "'{0}' task variable" -} \ No newline at end of file +} From 9ba99acd102ba825041bcc8aaed8f6e9a69791ca Mon Sep 17 00:00:00 2001 From: Konstantin Tyukalov <52399739+KonstantinTyukalov@users.noreply.github.com> Date: Wed, 9 Nov 2022 19:17:08 +0400 Subject: [PATCH 242/259] Localization update (#887) * Added localization pipeline and LocProject.json * Removed en-US * Update localize-pipeline.yml for Azure Pipelines * Update localize-pipeline.yml for Azure Pipelines * Made letter case consistent for languages * LEGO: check in for Localization to temporary branch. (#703) * LEGO: check in for Localization to temporary branch. (#714) * LEGO: check in for Localization to temporary branch. (#720) * Temp renaming * Renamed localization files * Applied enhancements for the localization pipeline (#733) [skip ci] * [Localization] Fixed localization pipeline issue with already localized strings replaced (#737) * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 14646607 Localized file check-in by OneLocBuild Task * LEGO: check in for Localization to temporary branch. (#740) * LEGO: check in for Localization to temporary branch. (#741) * LEGO: check in for Localization to temporary branch. (#742) * LEGO: check in for Localization to temporary branch. (#743) * Temporary renamed files - to resolve conflicts * Temporary renamed * LEGO: check in for Localization to temporary branch. (#745) Co-authored-by: csigs * LEGO: check in for Localization to temporary branch. (#746) Co-authored-by: csigs * LEGO: check in for Localization to temporary branch. (#747) Co-authored-by: csigs * LEGO: check in for Localization to temporary branch. (#748) Co-authored-by: csigs * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 14905562 Localized file check-in by OneLocBuild Task * Returned back original names * Removed redundant locale - test * Removed redundant folders * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 14906155 Localized file check-in by OneLocBuild Task * Returned back changes. Removed redundant * Create PR in OneLocBuild task only on third week of sprint (#755) * Fix localization pipeline * Add missed change * Added option to disable PR creation * Add OneLocBuild removal to the localization pipeline (#804) * Add OneLocBuild removal to the pipeline * fix ignore error to double if Co-authored-by: Ilya Kuleshov * Localization update (#802) * Removing Localize folder * Revert "Removing Localize folder" Co-authored-by: Ilya Kuleshov * Removed OneLocBuild folder * Move notifications about Task-lib Localization PR from Slack to MS Teams - Part 1 (#816) * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 18673037 (#879) * Juno: check in to lego/hb_a4aa9cc4-603b-418e-91f1-700184175625_20221105085105779. (#881) * Juno: check in to lego/hb_a4aa9cc4-603b-418e-91f1-700184175625_20221106085044954. (#882) * Localized file check-in by OneLocBuild Task: Build definition ID 10947: Build ID 18697074 (#883) * Update LocProject file * Update LocProject * Remove lowercase loc strings on windows * Removing Localize and OneLocBuild folder Co-authored-by: Anatolii Bolshakov (Akvelon INC) Co-authored-by: csigs Co-authored-by: Egor Bryzgalov Co-authored-by: Anatoly Bolshakov Co-authored-by: csigs Co-authored-by: Nikita Ezzhev Co-authored-by: kuleshovilya <87485027+kuleshovilya@users.noreply.github.com> Co-authored-by: Ilya Kuleshov Co-authored-by: Denis Tikhomirov <90906678+denis-tikhomirov@users.noreply.github.com> Co-authored-by: Maksim Petrov --- .../Strings/resources.resjson/de-DE/resources.resjson | 2 +- .../Strings/resources.resjson/es-ES/resources.resjson | 2 +- .../Strings/resources.resjson/fr-FR/resources.resjson | 2 +- .../Strings/resources.resjson/it-IT/resources.resjson | 2 +- .../Strings/resources.resjson/ja-JP/resources.resjson | 2 +- .../Strings/resources.resjson/ko-KR/resources.resjson | 2 +- .../Strings/resources.resjson/ru-RU/resources.resjson | 2 +- .../Strings/resources.resjson/zh-CN/resources.resjson | 2 +- .../Strings/resources.resjson/zh-TW/resources.resjson | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/de-DE/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/de-DE/resources.resjson index 248b674d6..90e1f1554 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/de-DE/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/de-DE/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "Fehler beim Aufzählen von Unterverzeichnissen für den folgenden Pfad: \"{0}\"", "loc.messages.PSLIB_FileNotFound0": "Die Datei wurde nicht gefunden: \"{0}\".", "loc.messages.PSLIB_Input0": "\"{0}\"-Eingabe", - "loc.messages.PSLIB_InvalidPattern0": "Ungültiges Muster: \"{0}\"", + "loc.messages.PSLIB_InvalidPattern0": "Der Pfad darf nicht mit einem Verzeichnistrennzeichen enden: „{0}“", "loc.messages.PSLIB_LeafPathNotFound0": "Der Blattpfad wurde nicht gefunden: \"{0}\".", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "Fehler bei der Normalisierung bzw. Erweiterung des Pfads. Die Pfadlänge wurde vom Kernel32-Subsystem nicht zurückgegeben für: \"{0}\"", "loc.messages.PSLIB_PathNotFound0": "Der Pfad wurde nicht gefunden: \"{0}\".", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/es-ES/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/es-ES/resources.resjson index b79ac21aa..e17ac0c0a 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/es-ES/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/es-ES/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "No se pudieron enumerar los subdirectorios de la ruta de acceso: '{0}'", "loc.messages.PSLIB_FileNotFound0": "Archivo no encontrado: '{0}'", "loc.messages.PSLIB_Input0": "Entrada '{0}'", - "loc.messages.PSLIB_InvalidPattern0": "Patrón no válido: '{0}'", + "loc.messages.PSLIB_InvalidPattern0": "La ruta de acceso no puede terminar con un carácter separador de directorios: '{0}'", "loc.messages.PSLIB_LeafPathNotFound0": "No se encuentra la ruta de acceso de la hoja: '{0}'", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "No se pudo normalizar o expandir la ruta de acceso. El subsistema Kernel32 no devolvió la longitud de la ruta de acceso para: '{0}'", "loc.messages.PSLIB_PathNotFound0": "No se encuentra la ruta de acceso: '{0}'", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/fr-FR/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/fr-FR/resources.resjson index dc2da0521..bf964d5c5 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/fr-FR/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/fr-FR/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "Échec de l'énumération des sous-répertoires pour le chemin : '{0}'", "loc.messages.PSLIB_FileNotFound0": "Fichier introuvable : {0}.", "loc.messages.PSLIB_Input0": "Entrée '{0}'", - "loc.messages.PSLIB_InvalidPattern0": "Modèle non valide : '{0}'", + "loc.messages.PSLIB_InvalidPattern0": "Le chemin d’accès ne peut pas se terminer par un caractère de séparateur de répertoire : « {0} »", "loc.messages.PSLIB_LeafPathNotFound0": "Le chemin feuille est introuvable : '{0}'", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "Échec de la normalisation/l'expansion du chemin. La longueur du chemin n'a pas été retournée par le sous-système Kernel32 pour : '{0}'", "loc.messages.PSLIB_PathNotFound0": "Chemin introuvable : '{0}'", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson index 6aeb1dfe7..1e69d7453 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/it-IT/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "L'enumerazione delle sottodirectory per il percorso '{0}' non è riuscita", "loc.messages.PSLIB_FileNotFound0": "File non trovato: '{0}'", "loc.messages.PSLIB_Input0": "Input di '{0}'", - "loc.messages.PSLIB_InvalidPattern0": "Criterio non valido: '{0}'", + "loc.messages.PSLIB_InvalidPattern0": "Il percorso non può terminare con un carattere separatore di directory: '{0}'", "loc.messages.PSLIB_LeafPathNotFound0": "Percorso foglia non trovato: '{0}'", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "La normalizzazione o l'espansione del percorso non è riuscita. Il sottosistema Kernel32 non ha restituito la lunghezza del percorso per '{0}'", "loc.messages.PSLIB_PathNotFound0": "Percorso non trovato: '{0}'", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/ja-JP/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/ja-JP/resources.resjson index 9f2f9feae..9c5102b95 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/ja-JP/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/ja-JP/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "パス '{0}' のサブディレクトリを列挙できませんでした", "loc.messages.PSLIB_FileNotFound0": "ファイルが見つかりません: '{0}'", "loc.messages.PSLIB_Input0": "'{0}' 入力", - "loc.messages.PSLIB_InvalidPattern0": "使用できないパターンです: '{0}'", + "loc.messages.PSLIB_InvalidPattern0": "パスの末尾をディレクトリ区切り文字にすることはできません: '{0}'", "loc.messages.PSLIB_LeafPathNotFound0": "リーフ パスが見つかりません: '{0}'", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "パスの正規化/展開に失敗しました。Kernel32 サブシステムからパス '{0}' の長さが返されませんでした", "loc.messages.PSLIB_PathNotFound0": "パスが見つかりません: '{0}'", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson index 58fa35393..a66485ecb 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/ko-KR/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "경로에 대해 하위 디렉터리를 열거하지 못함: '{0}'", "loc.messages.PSLIB_FileNotFound0": "{0} 파일을 찾을 수 없습니다.", "loc.messages.PSLIB_Input0": "'{0}' 입력", - "loc.messages.PSLIB_InvalidPattern0": "잘못된 패턴: '{0}'", + "loc.messages.PSLIB_InvalidPattern0": "경로는 디렉터리 구분 문자로 끝날 수 없습니다. '{0}'", "loc.messages.PSLIB_LeafPathNotFound0": "Leaf 경로를 찾을 수 없음: '{0}'", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "경로 정규화/확장에 실패했습니다. 다음에 대해 Kernel32 subsystem에서 경로 길이를 반환하지 않음: '{0}'", "loc.messages.PSLIB_PathNotFound0": "경로를 찾을 수 없음: '{0}'", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson index 335b6fbab..1157f2f27 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/ru-RU/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "Сбой перечисления подкаталогов для пути: \"{0}\".", "loc.messages.PSLIB_FileNotFound0": "Файл не найден: \"{0}\"", "loc.messages.PSLIB_Input0": "Входные данные \"{0}\".", - "loc.messages.PSLIB_InvalidPattern0": "Недопустимый шаблон: \"{0}\".", + "loc.messages.PSLIB_InvalidPattern0": "Путь не может заканчиваться символом разделителя каталогов: \"{0}\"", "loc.messages.PSLIB_LeafPathNotFound0": "Путь к конечному объекту не найден: \"{0}\".", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "Сбой нормализации и расширения пути. Длина пути не была возвращена подсистемой Kernel32 для: \"{0}\".", "loc.messages.PSLIB_PathNotFound0": "Путь не найден: \"{0}\".", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson index 1d333de7d..901b46b7a 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-CN/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "枚举路径的子目录失败:“{0}”", "loc.messages.PSLIB_FileNotFound0": "找不到文件: {0}。", "loc.messages.PSLIB_Input0": "“{0}”输入", - "loc.messages.PSLIB_InvalidPattern0": "无效的模式:“{0}”", + "loc.messages.PSLIB_InvalidPattern0": "路径不能以目录分隔符结尾:“{0}”", "loc.messages.PSLIB_LeafPathNotFound0": "找不到叶路径:“{0}”", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "路径规范化/扩展失败。路径长度不是由“{0}”的 Kernel32 子系统返回的", "loc.messages.PSLIB_PathNotFound0": "找不到路径:“{0}”", diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson index 512509bdd..ac2765879 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/zh-TW/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "為路徑列舉子目錄失敗: '{0}'", "loc.messages.PSLIB_FileNotFound0": "找不到檔案: '{0}'", "loc.messages.PSLIB_Input0": "'{0}' 輸入", - "loc.messages.PSLIB_InvalidPattern0": "模式無效: '{0}'", + "loc.messages.PSLIB_InvalidPattern0": "路徑不能以目錄分隔符號結尾: '{0}'", "loc.messages.PSLIB_LeafPathNotFound0": "找不到分葉路徑: '{0}'", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "路徑正規化/展開失敗。Kernel32 子系統未傳回 '{0}' 的路徑長度", "loc.messages.PSLIB_PathNotFound0": "找不到路徑: '{0}'", From 50bd43152c9103dd06be0e25b4be4ddc5dcfc5fe Mon Sep 17 00:00:00 2001 From: Pavlo Andriiesh Date: Tue, 22 Nov 2022 09:55:37 +0200 Subject: [PATCH 243/259] Include uncought exceptions stack trace to the output logs (#895) --- node/package.json | 2 +- node/task.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/node/package.json b/node/package.json index 1af918122..4d33254b8 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.3.2", + "version": "3.4.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index 3facba81a..d948b4b73 100644 --- a/node/task.ts +++ b/node/task.ts @@ -105,6 +105,7 @@ export function setResult(result: TaskResult, message: string, done?: boolean): // process.on('uncaughtException', (err: Error) => { setResult(TaskResult.Failed, loc('LIB_UnhandledEx', err.message)); + error(String(err.stack)); }); //----------------------------------------------------- From 73e95156c33338e9705d03e119c69245e3ea718e Mon Sep 17 00:00:00 2001 From: Roman-Shchukin <111063382+Roman-Shchukin@users.noreply.github.com> Date: Thu, 24 Nov 2022 13:57:00 +0100 Subject: [PATCH 244/259] Pipeline fixing (#897) * Pipeline fixing * Indentation fix * Formating * Node version was reverted --- azure-pipelines-steps-node.yml | 18 ++++----- azure-pipelines-steps-test-build.yml | 12 +++--- azure-pipelines.yml | 56 ++++++---------------------- 3 files changed, 27 insertions(+), 59 deletions(-) diff --git a/azure-pipelines-steps-node.yml b/azure-pipelines-steps-node.yml index 8e2591383..a7444bfb8 100644 --- a/azure-pipelines-steps-node.yml +++ b/azure-pipelines-steps-node.yml @@ -4,13 +4,13 @@ parameters: steps: # npm install - - task: Npm@1 - displayName: (azure-pipelines-task-lib) npm install - inputs: - command: install - workingDir: node +- task: Npm@1 + displayName: npm install + inputs: + command: install + workingDir: node - - task: NodeTool@0 - displayName: (azure-pipelines-task-lib) use node ${{parameters.nodeVersion}} - inputs: - versionSpec: ${{parameters.nodeVersion}} +- task: NodeTool@0 + displayName: use node ${{parameters.nodeVersion}} + inputs: + versionSpec: ${{parameters.nodeVersion}} diff --git a/azure-pipelines-steps-test-build.yml b/azure-pipelines-steps-test-build.yml index 38ca9e3e9..0789bb88a 100644 --- a/azure-pipelines-steps-test-build.yml +++ b/azure-pipelines-steps-test-build.yml @@ -1,10 +1,10 @@ steps: # test - - script: node make.js test - workingDirectory: node - displayName: (azure-pipelines-task-lib) node make.js test +- script: node make.js test + workingDirectory: node + displayName: node make.js test # build - - script: node make.js build - displayName: (azure-pipelines-task-lib) node make.js build - workingDirectory: node +- script: node make.js build + displayName: node make.js build + workingDirectory: node diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9bafc145f..42914e225 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,8 +4,10 @@ trigger: - releases/* variables: - nodeVersion: 10.23.0 - +- group: npm-tokens +- name: nodeVersion + value: '10.23.0' + jobs: ################################################# - job: windows @@ -15,44 +17,11 @@ jobs: vmImage: windows-2019 steps: - ################################################################################ - # azure-pipelines-task-lib - ################################################################################ - - template: azure-pipelines-steps-node.yml parameters: nodeVersion: $(nodeVersion) - - # test - - script: | - chcp 437 - node make.js test - workingDirectory: node - displayName: (azure-pipelines-task-lib) node make.js test - - # build - - script: | - chcp 437 - node make.js build - displayName: (azure-pipelines-task-lib) node make.js build - workingDirectory: node - - ################################################################################ - # VstsTaskSdk - ################################################################################ - - # npm install - - task: Npm@1 - displayName: (VstsTaskSdk) npm install - inputs: - command: install - workingDir: powershell - - # npm test - - script: npm test - displayName: (VstsTaskSdk) npm test - workingDirectory: powershell - + - template: azure-pipelines-steps-test-build.yml + ################################################# - job: linux ################################################# @@ -74,14 +43,13 @@ jobs: # For CI runs on master, automatically publish packages - bash: | - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc - cd _build + echo //registry.npmjs.org/:_authToken=\${NPM_TOKEN} > .npmrc npm publish || true # Ignore publish failures, usually will happen because package already exists - displayName: (azure-pipelines-task-lib) npm publish - workingDirectory: node - condition: and(succeeded(), in(variables['build.reason'], 'IndividualCI', 'BatchedCI', 'Manual'), in(variables['build.sourcebranchname'], 'master', 'releases/3.x')) + displayName: npm publish + workingDirectory: node/_build + condition: and(succeeded(), in(variables['build.reason'], 'IndividualCI', 'BatchedCI', 'Manual'), in(variables['build.sourcebranch'], 'refs/heads/master')) env: - NPM_TOKEN: $(npmPublishToken) + NPM_TOKEN: $(npm-automation.token) ################################################# - job: macOS @@ -94,4 +62,4 @@ jobs: - template: azure-pipelines-steps-node.yml parameters: nodeVersion: $(nodeVersion) - - template: azure-pipelines-steps-test-build.yml + - template: azure-pipelines-steps-test-build.yml \ No newline at end of file From 3bf465d3520ea8ac7b952328a49410c1c14a3d47 Mon Sep 17 00:00:00 2001 From: Konstantin Tyukalov <52399739+KonstantinTyukalov@users.noreply.github.com> Date: Fri, 2 Dec 2022 14:31:17 +0400 Subject: [PATCH 245/259] Merge task lib 4.x to master (#903) * Migrating to Node 16 (#844) * create new release/4.x branch * release for 4 version * update pipeline * update package.json * fix test and build commands * fixed tests on node18 * change image version for linux * fix test for linux * add separete test for linux * node v16.13 * add v16.13.0 to test * test on windows-2022, macOS-11 * separate branch for node16 * remove temporary code * update package.json * changed pipeline * updated pipeline * Microsoft mandatory file (#839) * Update pipeline to include a step for publishing artifacts. (#848) (#849) * Update pipeline * Update azure-pipelines.yml * nodeversion as variable * Added node16 to schema (#852) Co-authored-by: Andrey Ivanov * bump version (#862) Co-authored-by: Andrey Ivanov * Update ci-cd for publishing npm package (#863) * fix npm publish Co-authored-by: Andrey Ivanov * fix add registry (#864) Co-authored-by: Andrey Ivanov * fix ToolRunner - _getSpawnSyncOptions (#873) * Mockery version is updated from 1.7.0 to 2.1.0 because of vulnerability (#878) * Fixed release path (#888) * Improvement of the release condition (#889) * Added test task with test condition. Negative test * Added test task with test condition. Positive test * Fix path * Fix path * Fix path * Changed build.sourcebranchname -> build.sourcebranch and path to branch * Fix path * Version changed (#894) * Pipeline fixing (#893) * Token test * Test token * Publish test * call order changed * Nmp token syntax reverted * Changed windows job * Script clearing * Uncommented condition * Token changed * Test - off publish condition * Switched on publish condition * Package version was reverted * Add escape for more correct working * Add changelog * Update package-lock version * Bump tl version to 4.1.0 * Update changelog * Remove PS task lib changes from node changelog * Update changelog for 4.0.1-preview Co-authored-by: AndreyIvanov42 <93121155+AndreyIvanov42@users.noreply.github.com> Co-authored-by: Andrey Ivanov Co-authored-by: Denis Rumyantsev Co-authored-by: Roman-Shchukin <111063382+Roman-Shchukin@users.noreply.github.com> --- CONTRIBUTING.md | 3 +- azure-pipelines.yml | 28 +++--- node/CHANGELOG.md | 34 +++++++ node/buildutils.js | 2 +- node/mock-test.ts | 25 +++-- node/package-lock.json | 45 ++------- node/package.json | 8 +- node/test/dirtests.ts | 98 +++++++++---------- .../{node14task => node16task}/task.json | 4 +- node/test/filtertests.ts | 18 ++-- node/test/findmatchtests.ts | 56 +++++------ node/test/inputtests.ts | 4 +- node/test/internalhelpertests.ts | 12 +-- node/test/isuncpathtests.ts | 2 +- node/test/legacyfindfilestests.ts | 44 ++++----- node/test/matchtests.ts | 36 +++---- node/test/mocktests.ts | 10 +- node/test/retrytests.ts | 2 +- node/test/toolrunnertests.ts | 16 ++- node/toolrunner.ts | 1 + node/vault.ts | 2 +- tasks.schema.json | 9 ++ 22 files changed, 243 insertions(+), 216 deletions(-) create mode 100644 node/CHANGELOG.md rename node/test/fakeTasks/{node14task => node16task}/task.json (66%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3385d692e..515a6bd54 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,4 +38,5 @@ Your pull request should: * At least one test should fail in the absence of your non-test code changes. If your PR does not match this criteria, please specify why * Tests should include reasonable permutations of the target fix/change * Include baseline changes with your change - * All changed code must have 100% code coverage \ No newline at end of file + * All changed code must have 100% code coverage + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 42914e225..ff7e5648d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,10 +4,10 @@ trigger: - releases/* variables: -- group: npm-tokens -- name: nodeVersion - value: '10.23.0' - + - group: npm-tokens + - name: nodeVersion + value: '16.13.0' + jobs: ################################################# - job: windows @@ -17,11 +17,12 @@ jobs: vmImage: windows-2019 steps: - - template: azure-pipelines-steps-node.yml - parameters: - nodeVersion: $(nodeVersion) - - template: azure-pipelines-steps-test-build.yml - + - template: azure-pipelines-steps-node.yml + parameters: + nodeVersion: $(nodeVersion) + + - template: azure-pipelines-steps-test-build.yml + ################################################# - job: linux ################################################# @@ -59,7 +60,8 @@ jobs: vmImage: macOS-10.15 steps: - - template: azure-pipelines-steps-node.yml - parameters: - nodeVersion: $(nodeVersion) - - template: azure-pipelines-steps-test-build.yml \ No newline at end of file + - template: azure-pipelines-steps-node.yml + parameters: + nodeVersion: $(nodeVersion) + + - template: azure-pipelines-steps-test-build.yml diff --git a/node/CHANGELOG.md b/node/CHANGELOG.md new file mode 100644 index 000000000..4691bcbc1 --- /dev/null +++ b/node/CHANGELOG.md @@ -0,0 +1,34 @@ +# Node.js task lib changes + +## 3.x + +### 3.3.1 + +- Update minimatch to version 3.0.5 to fix vulnerability - [#836](https://github.com/microsoft/azure-pipelines-task-lib/pull/836) + +### 3.4.0 + +- Updated mockery and mocha dependencies - [#875](https://github.com/microsoft/azure-pipelines-task-lib/pull/875) + +- Include uncought exceptions stack trace to the output logs - [#895](https://github.com/microsoft/azure-pipelines-task-lib/pull/895) + +## 4.x + +### 4.0.0-preview + +- Introduced support for node 16 task handler - [#844](https://github.com/microsoft/azure-pipelines-task-lib/pull/844) + +### 4.0.1-preview + +- Added node16 to task.schema.json - [#852](https://github.com/microsoft/azure-pipelines-task-lib/pull/852) +- fix ToolRunner - _getSpawnSyncOptions - [#873](https://github.com/microsoft/azure-pipelines-task-lib/pull/873) + +### 4.0.2 + +- Updated mockery because of vulnerabilities - [#878](https://github.com/microsoft/azure-pipelines-task-lib/pull/878) + +## 4.1.0 + +Backported from ver.`3.4.0`: + +- Include uncought exceptions stack trace to the output logs - [#895](https://github.com/microsoft/azure-pipelines-task-lib/pull/895) diff --git a/node/buildutils.js b/node/buildutils.js index 13192e0d9..1ecd94bbe 100644 --- a/node/buildutils.js +++ b/node/buildutils.js @@ -35,7 +35,7 @@ exports.getExternals = function () { // and add node to the PATH var nodeUrl = process.env['TASK_NODE_URL'] || 'https://nodejs.org/dist'; nodeUrl = nodeUrl.replace(/\/$/, ''); // ensure there is no trailing slash on the base URL - var nodeVersion = 'v10.23.0'; + var nodeVersion = 'v16.13.0'; switch (platform) { case 'darwin': var nodeArchivePath = downloadArchive(nodeUrl + '/' + nodeVersion + '/node-' + nodeVersion + '-darwin-x64.tar.gz'); diff --git a/node/mock-test.ts b/node/mock-test.ts index 37d379a3b..4d973387e 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -143,20 +143,17 @@ export class MockTestRunner { let downloadVersion: string; switch (version) { - case 5: - downloadVersion = 'v5.10.1'; - break; case 6: downloadVersion = 'v6.17.1'; break; case 10: downloadVersion = 'v10.21.0'; break; - case 14: - downloadVersion = 'v14.11.0'; + case 16: + downloadVersion = 'v16.13.0'; break; default: - throw new Error('Invalid node version, must be 5, 6, 10, or 14 (received ' + version + ')'); + throw new Error('Invalid node version, must be 6, 10, or 16 (received ' + version + ')'); } // Install node in home directory if it isn't already there. @@ -170,12 +167,12 @@ export class MockTestRunner { } } - // Determines the correct version of node to use based on the contents of the task's task.json. Defaults to Node 14. + // Determines the correct version of node to use based on the contents of the task's task.json. Defaults to Node 16. private getNodeVersion(): number { const taskJsonPath: string = this.getTaskJsonPath(); if (!taskJsonPath) { - console.warn('Unable to find task.json, defaulting to use Node 14'); - return 10; + console.warn('Unable to find task.json, defaulting to use Node 16'); + return 16; } const taskJsonContents = fs.readFileSync(taskJsonPath, { encoding: 'utf-8' }); const taskJson: object = JSON.parse(taskJsonContents); @@ -188,9 +185,9 @@ export class MockTestRunner { ); const keys = Object.keys(execution); for (let i = 0; i < keys.length; i++) { - if (keys[i].toLowerCase() == 'node14') { - // Prefer node 14 and return immediately. - return 14; + if (keys[i].toLowerCase() == 'node16') { + // Prefer node 16 and return immediately. + return 16; } else if (keys[i].toLowerCase() == 'node10') { // Prefer node 10 and return immediately. return 10; @@ -200,8 +197,8 @@ export class MockTestRunner { } if (!nodeVersionFound) { - console.warn('Unable to determine execution type from task.json, defaulting to use Node 10'); - return 10; + console.warn('Unable to determine execution type from task.json, defaulting to use Node 16'); + return 16; } return 6; diff --git a/node/package-lock.json b/node/package-lock.json index 6e5a3110d..7bf741552 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.3.2", + "version": "4.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -37,9 +37,9 @@ "dev": true }, "@types/mocha": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", - "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", "dev": true }, "@types/mockery": { @@ -49,9 +49,9 @@ "dev": true }, "@types/node": { - "version": "10.17.44", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.44.tgz", - "integrity": "sha512-vHPAyBX1ffLcy4fQHmDyIUMUb42gHZjPHU66nhvbMzAWJqHnySGZ6STwN3rwrnSd1FHB0DI/RWgGELgKSYRDmw==" + "version": "16.11.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.39.tgz", + "integrity": "sha512-K0MsdV42vPwm9L6UwhIxMAOmcvH/1OoVkZyCgEtVu4Wx7sElGloy/W7kMBNe/oJ7V/jW9BVt1F6RahH6e7tPXw==" }, "@types/q": { "version": "1.5.4", @@ -392,9 +392,9 @@ "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=" }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -632,31 +632,6 @@ "yargs-unparser": "2.0.0" }, "dependencies": { - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, "minimatch": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", diff --git a/node/package.json b/node/package.json index 4d33254b8..aa7d571bb 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "3.4.0", + "version": "4.1.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", @@ -37,13 +37,13 @@ }, "devDependencies": { "@types/minimatch": "3.0.3", - "@types/mocha": "^5.2.7", + "@types/mocha": "^9.1.1", "@types/mockery": "^1.4.29", - "@types/node": "^10.17.0", + "@types/node": "^16.11.39", "@types/q": "^1.5.4", "@types/semver": "^7.3.4", "@types/shelljs": "^0.8.8", - "mocha": "9.2.2", + "mocha": "^9.2.2", "typescript": "^4.0.0" } } diff --git a/node/test/dirtests.ts b/node/test/dirtests.ts index 9321eb539..2d5f32cd7 100644 --- a/node/test/dirtests.ts +++ b/node/test/dirtests.ts @@ -27,11 +27,11 @@ describe('Dir Operation Tests', function () { // this test verifies the expected version of node is being used to run the tests. // 5.10.1 is what ships in the 1.x and 2.x agent. - it('is expected version', (done: MochaDone) => { + it('is expected version', (done) => { this.timeout(1000); console.log('node version: ' + process.version); - const supportedNodeVersions = ['v5.10.1', 'v6.10.3', 'v6.17.1', 'v8.9.1', 'v10.17.0', 'v10.18.0', 'v10.23.0', 'v14.11.0']; + const supportedNodeVersions = ['v16.13.0']; if (supportedNodeVersions.indexOf(process.version) === -1) { assert.fail(`expected node node version to be one of ${supportedNodeVersions.map(o => o).join(', ')}. actual: ` + process.version); } @@ -562,7 +562,7 @@ describe('Dir Operation Tests', function () { } // find tests - it('returns hidden files with find', (done: MochaDone) => { + it('returns hidden files with find', (done) => { this.timeout(3000); // create the following layout: @@ -588,7 +588,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('returns depth first find', (done: MochaDone) => { + it('returns depth first find', (done) => { this.timeout(1000); // create the following layout: @@ -621,7 +621,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('returns empty when not exists', (done: MochaDone) => { + it('returns empty when not exists', (done) => { this.timeout(1000); let itemPaths: string[] = tl.find(path.join(testutil.getTestTemp(), 'nosuch')); @@ -630,7 +630,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('does not follow specified symlink', (done: MochaDone) => { + it('does not follow specified symlink', (done) => { this.timeout(1000); // create the following layout: @@ -649,7 +649,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('follows specified symlink when -H', (done: MochaDone) => { + it('follows specified symlink when -H', (done) => { this.timeout(1000); // create the following layout: @@ -671,7 +671,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('follows specified symlink when -L', (done: MochaDone) => { + it('follows specified symlink when -L', (done) => { this.timeout(1000); // create the following layout: @@ -693,7 +693,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('does not follow symlink', (done: MochaDone) => { + it('does not follow symlink', (done) => { this.timeout(1000); // create the following layout: @@ -716,7 +716,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('does not follow symlink when -H', (done: MochaDone) => { + it('does not follow symlink when -H', (done) => { this.timeout(1000); // create the following layout: @@ -741,7 +741,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('follows symlink when -L', (done: MochaDone) => { + it('follows symlink when -L', (done) => { this.timeout(1000); // create the following layout: @@ -767,7 +767,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('allows broken symlink', (done: MochaDone) => { + it('allows broken symlink', (done) => { this.timeout(1000); // create the following layout: @@ -794,7 +794,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('allows specified broken symlink', (done: MochaDone) => { + it('allows specified broken symlink', (done) => { this.timeout(1000); // create the following layout: @@ -812,7 +812,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('allows nested broken symlink when -H', (done: MochaDone) => { + it('allows nested broken symlink when -H', (done) => { this.timeout(1000); // create the following layout: @@ -841,7 +841,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('allows specified broken symlink with -H', (done: MochaDone) => { + it('allows specified broken symlink with -H', (done) => { this.timeout(1000); // create the following layout: @@ -862,7 +862,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('does not allow specified broken symlink when only -H', (done: MochaDone) => { + it('does not allow specified broken symlink when only -H', (done) => { this.timeout(1000); // create the following layout: @@ -887,7 +887,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('does not allow broken symlink when only -L', (done: MochaDone) => { + it('does not allow broken symlink when only -L', (done) => { this.timeout(1000); // create the following layout: @@ -910,7 +910,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('does not allow specied broken symlink when only -L', (done: MochaDone) => { + it('does not allow specied broken symlink when only -L', (done) => { this.timeout(1000); // create the following layout: @@ -935,7 +935,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('allow broken symlink with -L', (done: MochaDone) => { + it('allow broken symlink with -L', (done) => { this.timeout(1000); // create the following layout: @@ -966,7 +966,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('allow specified broken symlink with -L', (done: MochaDone) => { + it('allow specified broken symlink with -L', (done) => { this.timeout(1000); // create the following layout: @@ -988,7 +988,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('detects cycle', (done: MochaDone) => { + it('detects cycle', (done) => { this.timeout(1000); // create the following layout: @@ -1009,7 +1009,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('detects cycle starting from symlink', (done: MochaDone) => { + it('detects cycle starting from symlink', (done) => { this.timeout(1000); // create the following layout: @@ -1030,7 +1030,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('detects deep cycle starting from middle', (done: MochaDone) => { + it('detects deep cycle starting from middle', (done) => { this.timeout(1000); // create the following layout: @@ -1069,7 +1069,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('default options', (done: MochaDone) => { + it('default options', (done) => { this.timeout(1000); // create the following layout: @@ -1099,7 +1099,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('default options do not allow broken symlinks', (done: MochaDone) => { + it('default options do not allow broken symlinks', (done) => { this.timeout(1000); // create the following layout: @@ -1121,7 +1121,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('empty find path returns empty array', (done: MochaDone) => { + it('empty find path returns empty array', (done) => { this.timeout(1000); let actual: string[] = tl.find(''); @@ -1131,7 +1131,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('normalizes find path', (done: MochaDone) => { + it('normalizes find path', (done) => { this.timeout(1000); // create the following layout: @@ -1220,7 +1220,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('fails if mkdirP with conflicting file path', (done: MochaDone) => { + it('fails if mkdirP with conflicting file path', (done) => { this.timeout(1000); let testPath = path.join(testutil.getTestTemp(), 'mkdirP_conflicting_file_path'); @@ -1238,7 +1238,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('fails if mkdirP with conflicting parent file path', (done: MochaDone) => { + it('fails if mkdirP with conflicting parent file path', (done) => { this.timeout(1000); let testPath = path.join(testutil.getTestTemp(), 'mkdirP_conflicting_parent_file_path', 'dir'); @@ -1256,7 +1256,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('no-ops if mkdirP directory exists', (done: MochaDone) => { + it('no-ops if mkdirP directory exists', (done) => { this.timeout(1000); let testPath = path.join(testutil.getTestTemp(), 'mkdirP_dir_exists'); @@ -1268,7 +1268,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('no-ops if mkdirP with symlink directory', (done: MochaDone) => { + it('no-ops if mkdirP with symlink directory', (done) => { this.timeout(1000); // create the following layout: @@ -1294,7 +1294,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('no-ops if mkdirP with parent symlink directory', (done: MochaDone) => { + it('no-ops if mkdirP with parent symlink directory', (done) => { this.timeout(1000); // create the following layout: @@ -1320,7 +1320,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('breaks if mkdirP loop out of control', (done: MochaDone) => { + it('breaks if mkdirP loop out of control', (done) => { this.timeout(1000); let testPath = path.join(testutil.getTestTemp(), 'mkdirP_failsafe', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'); @@ -1432,7 +1432,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes file with rmRF', (done: MochaDone) => { + it('removes file with rmRF', (done) => { this.timeout(1000); let file: string = path.join(testutil.getTestTemp(), 'rmRF_file'); @@ -1444,7 +1444,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes hidden folder with rmRF', (done: MochaDone) => { + it('removes hidden folder with rmRF', (done) => { this.timeout(1000); let directory: string = path.join(testutil.getTestTemp(), '.rmRF_directory'); @@ -1456,7 +1456,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes hidden file with rmRF', (done: MochaDone) => { + it('removes hidden file with rmRF', (done) => { this.timeout(1000); let file: string = path.join(testutil.getTestTemp(), '.rmRF_file'); @@ -1468,7 +1468,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes symlink folder with rmRF', (done: MochaDone) => { + it('removes symlink folder with rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1494,7 +1494,7 @@ describe('Dir Operation Tests', function () { // creating a symlink to a file on Windows requires elevated if (os.platform() != 'win32') { - it('removes symlink file with rmRF', (done: MochaDone) => { + it('removes symlink file with rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1515,7 +1515,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes symlink file with missing source using rmRF', (done: MochaDone) => { + it('removes symlink file with missing source using rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1548,7 +1548,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes symlink level 2 file with rmRF', (done: MochaDone) => { + it('removes symlink level 2 file with rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1573,7 +1573,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes nested symlink file with rmRF', (done: MochaDone) => { + it('removes nested symlink file with rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1601,7 +1601,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes deeply nested symlink file with rmRF', (done: MochaDone) => { + it('removes deeply nested symlink file with rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1632,7 +1632,7 @@ describe('Dir Operation Tests', function () { }); } - it('removes symlink folder with missing source using rmRF', (done: MochaDone) => { + it('removes symlink folder with missing source using rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1651,7 +1651,7 @@ describe('Dir Operation Tests', function () { // remove the real directory fs.unlinkSync(realFile); fs.rmdirSync(realDirectory); - assert.throws(() => { fs.statSync(symlinkDirectory) }, (err) => err.code == 'ENOENT', 'stat should throw'); + assert.throws(() => { fs.statSync(symlinkDirectory) }, (err: NodeJS.ErrnoException) => err.code == 'ENOENT', 'stat should throw'); // remove the symlink directory tl.rmRF(symlinkDirectory); @@ -1668,7 +1668,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes symlink level 2 folder with rmRF', (done: MochaDone) => { + it('removes symlink level 2 folder with rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1700,7 +1700,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes nested symlink folder with rmRF', (done: MochaDone) => { + it('removes nested symlink folder with rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1728,7 +1728,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes deeply nested symlink folder with rmRF', (done: MochaDone) => { + it('removes deeply nested symlink folder with rmRF', (done) => { this.timeout(1000); // create the following layout: @@ -1758,7 +1758,7 @@ describe('Dir Operation Tests', function () { done(); }); - it('removes hidden file with rmRF', (done: MochaDone) => { + it('removes hidden file with rmRF', (done) => { this.timeout(1000); let file: string = path.join(testutil.getTestTemp(), '.rmRF_file'); @@ -1845,7 +1845,7 @@ describe('Dir Operation Tests', function () { }); // cp tests - it('copies file using -f', (done: MochaDone) => { + it('copies file using -f', (done) => { this.timeout(1000); let root: string = path.join(testutil.getTestTemp(), 'cp_with_-f'); diff --git a/node/test/fakeTasks/node14task/task.json b/node/test/fakeTasks/node16task/task.json similarity index 66% rename from node/test/fakeTasks/node14task/task.json rename to node/test/fakeTasks/node16task/task.json index a4825b6ec..4342d1603 100644 --- a/node/test/fakeTasks/node14task/task.json +++ b/node/test/fakeTasks/node16task/task.json @@ -1,8 +1,8 @@ { "id": "id", - "name": "Node14Task", + "name": "Node16Task", "execution": { - "Node14": { + "Node16": { "target": "usedotnet.js" } } diff --git a/node/test/filtertests.ts b/node/test/filtertests.ts index 04ea46474..110a7a4a9 100644 --- a/node/test/filtertests.ts +++ b/node/test/filtertests.ts @@ -20,7 +20,7 @@ describe('Filter Tests', function () { after(function () { }); - it('applies default option nobrace true', (done: MochaDone) => { + it('applies default option nobrace true', (done) => { this.timeout(1000); let list = [ @@ -38,7 +38,7 @@ describe('Filter Tests', function () { done(); }); - it('applies default option noglobstar false', (done: MochaDone) => { + it('applies default option noglobstar false', (done) => { this.timeout(1000); let list = [ @@ -58,7 +58,7 @@ describe('Filter Tests', function () { done(); }); - it('applies default option dot true', (done: MochaDone) => { + it('applies default option dot true', (done) => { this.timeout(1000); let list = [ @@ -75,7 +75,7 @@ describe('Filter Tests', function () { done(); }); - it('applies default option noext false', (done: MochaDone) => { + it('applies default option noext false', (done) => { this.timeout(1000); let list = [ @@ -94,7 +94,7 @@ describe('Filter Tests', function () { done(); }); - it('applies default option nocase based on platform', (done: MochaDone) => { + it('applies default option nocase based on platform', (done) => { this.timeout(1000); let list = [ @@ -114,7 +114,7 @@ describe('Filter Tests', function () { done(); }); - it('applies default option matchBase false', (done: MochaDone) => { + it('applies default option matchBase false', (done) => { this.timeout(1000); let list = [ @@ -131,7 +131,7 @@ describe('Filter Tests', function () { done(); }); - it('applies default option nocomment false', (done: MochaDone) => { + it('applies default option nocomment false', (done) => { this.timeout(1000); let list = [ @@ -145,7 +145,7 @@ describe('Filter Tests', function () { done(); }); - it('applies default option nonegate false', (done: MochaDone) => { + it('applies default option nonegate false', (done) => { this.timeout(1000); let list = [ @@ -162,7 +162,7 @@ describe('Filter Tests', function () { done(); }); - it('supports custom options', (done: MochaDone) => { + it('supports custom options', (done) => { this.timeout(1000); let list = [ diff --git a/node/test/findmatchtests.ts b/node/test/findmatchtests.ts index a0bcfc185..b09d231f9 100644 --- a/node/test/findmatchtests.ts +++ b/node/test/findmatchtests.ts @@ -23,7 +23,7 @@ describe('Find and Match Tests', function () { after(function () { }); - it('single pattern', (done: MochaDone) => { + it('single pattern', (done) => { this.timeout(1000); // create the following layout: @@ -46,7 +46,7 @@ describe('Find and Match Tests', function () { done(); }); - it('aggregates matches', (done: MochaDone) => { + it('aggregates matches', (done) => { this.timeout(1000); // create the following layout: @@ -73,7 +73,7 @@ describe('Find and Match Tests', function () { done(); }); - it('supports path not found', (done: MochaDone) => { + it('supports path not found', (done) => { this.timeout(1000); let root: string = path.join(testutil.getTestTemp(), 'find-and-match_supports-path-not-found'); @@ -89,7 +89,7 @@ describe('Find and Match Tests', function () { done(); }); - it('does not duplicate matches', (done: MochaDone) => { + it('does not duplicate matches', (done) => { this.timeout(1000); // create the following layout: @@ -121,7 +121,7 @@ describe('Find and Match Tests', function () { done(); }); - it('supports interleaved exclude patterns', (done: MochaDone) => { + it('supports interleaved exclude patterns', (done) => { this.timeout(1000); // create the following layout: @@ -168,7 +168,7 @@ describe('Find and Match Tests', function () { done(); }); - it('applies default match options', (done: MochaDone) => { + it('applies default match options', (done) => { this.timeout(1000); // create the following layout: @@ -252,7 +252,7 @@ describe('Find and Match Tests', function () { done(); }); - it('trims patterns', (done: MochaDone) => { + it('trims patterns', (done) => { this.timeout(1000); // create the following layout: @@ -274,7 +274,7 @@ describe('Find and Match Tests', function () { done(); }); - it('skips empty patterns', (done: MochaDone) => { + it('skips empty patterns', (done) => { this.timeout(1000); // create the following layout: @@ -298,7 +298,7 @@ describe('Find and Match Tests', function () { done(); }); - it('supports nocomment true', (done: MochaDone) => { + it('supports nocomment true', (done) => { this.timeout(1000); // create the following layout: @@ -320,7 +320,7 @@ describe('Find and Match Tests', function () { done(); }); - it('supports nobrace false', (done: MochaDone) => { + it('supports nobrace false', (done) => { this.timeout(1000); // create the following layout: @@ -345,7 +345,7 @@ describe('Find and Match Tests', function () { done(); }); - it('brace escaping platform-specific', (done: MochaDone) => { + it('brace escaping platform-specific', (done) => { this.timeout(1000); // create the following layout: @@ -380,7 +380,7 @@ describe('Find and Match Tests', function () { }); - it('supports nonegate true', (done: MochaDone) => { + it('supports nonegate true', (done) => { this.timeout(1000); // create the following layout: @@ -402,7 +402,7 @@ describe('Find and Match Tests', function () { done(); }); - it('supports flipNegate true', (done: MochaDone) => { + it('supports flipNegate true', (done) => { this.timeout(1000); // create the following layout: @@ -424,7 +424,7 @@ describe('Find and Match Tests', function () { done(); }); - it('supports matchBase include patterns', (done: MochaDone) => { + it('supports matchBase include patterns', (done) => { this.timeout(1000); // create the following layout: @@ -457,7 +457,7 @@ describe('Find and Match Tests', function () { done(); }); - it('supports matchBase include patterns with glob', (done: MochaDone) => { + it('supports matchBase include patterns with glob', (done) => { this.timeout(1000); // create the following layout: @@ -490,7 +490,7 @@ describe('Find and Match Tests', function () { done(); }); - it('supports matchBase exlude pattern', (done: MochaDone) => { + it('supports matchBase exlude pattern', (done) => { this.timeout(1000); // create the following layout: @@ -527,7 +527,7 @@ describe('Find and Match Tests', function () { done(); }); - it('counts leading negate markers', (done: MochaDone) => { + it('counts leading negate markers', (done) => { this.timeout(1000); // create the following layout: @@ -564,7 +564,7 @@ describe('Find and Match Tests', function () { done(); }); - it('trims whitespace after trimming negate markers', (done: MochaDone) => { + it('trims whitespace after trimming negate markers', (done) => { this.timeout(1000); // create the following layout: @@ -587,7 +587,7 @@ describe('Find and Match Tests', function () { done(); }); - it('evaluates comments before expanding braces', (done: MochaDone) => { + it('evaluates comments before expanding braces', (done) => { this.timeout(1000); // create the following layout: @@ -616,7 +616,7 @@ describe('Find and Match Tests', function () { done(); }); - it('evaluates negation before expanding braces', (done: MochaDone) => { + it('evaluates negation before expanding braces', (done) => { this.timeout(1000); // create the following layout: @@ -641,7 +641,7 @@ describe('Find and Match Tests', function () { done(); }); - it('evaluates comments before negation', (done: MochaDone) => { + it('evaluates comments before negation', (done) => { this.timeout(1000); // create the following layout: @@ -667,7 +667,7 @@ describe('Find and Match Tests', function () { done(); }); - it('escapes default root when rooting patterns', (done: MochaDone) => { + it('escapes default root when rooting patterns', (done) => { this.timeout(1000); // create the following layout: @@ -741,7 +741,7 @@ describe('Find and Match Tests', function () { done(); }); - it('applies default find options', (done: MochaDone) => { + it('applies default find options', (done) => { this.timeout(1000); // create the following layout: @@ -763,7 +763,7 @@ describe('Find and Match Tests', function () { done(); }); - it('supports custom find options', (done: MochaDone) => { + it('supports custom find options', (done) => { this.timeout(1000); // create the following layout: @@ -787,7 +787,7 @@ describe('Find and Match Tests', function () { done(); }); - it('default root falls back to System.DefaultWorkingDirectory', (done: MochaDone) => { + it('default root falls back to System.DefaultWorkingDirectory', (done) => { this.timeout(1000); let originalSystemDefaultWorkingDirectory = process.env['SYSTEM_DEFAULTWORKINGDIRECTORY']; @@ -815,7 +815,7 @@ describe('Find and Match Tests', function () { done(); }); - it('default root falls back to cwd', (done: MochaDone) => { + it('default root falls back to cwd', (done) => { this.timeout(1000); let originalSystemDefaultWorkingDirectory = process.env['SYSTEM_DEFAULTWORKINGDIRECTORY']; @@ -853,7 +853,7 @@ describe('Find and Match Tests', function () { } } - it('ensurePatternRooted()', (done: MochaDone) => { + it('ensurePatternRooted()', (done) => { this.timeout(1000); if (process.platform == 'win32') { @@ -932,7 +932,7 @@ describe('Find and Match Tests', function () { assert.deepEqual(actual, expected); } - it('getFindInfoFromPattern()', (done: MochaDone) => { + it('getFindInfoFromPattern()', (done) => { this.timeout(1000); // basename diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index aa0fa1509..76213d7fd 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -133,7 +133,7 @@ describe('Input Tests', function () { done(); }) - it('gets a variable with special characters', (done: MochaDone) => { + it('gets a variable with special characters', (done) => { this.timeout(1000); let expected = 'Value of var with special chars'; @@ -156,7 +156,7 @@ describe('Input Tests', function () { done(); }) - it('sets a variable with special chars as an env var', (done: MochaDone) => { + it('sets a variable with special chars as an env var', (done) => { this.timeout(1000); let expected = 'Set value of var with special chars'; diff --git a/node/test/internalhelpertests.ts b/node/test/internalhelpertests.ts index de81d52fb..10d62ee3e 100644 --- a/node/test/internalhelpertests.ts +++ b/node/test/internalhelpertests.ts @@ -32,7 +32,7 @@ describe('Internal Path Helper Tests', function () { `expected ensureRooted for input <${path}> to yield <${expected}>`); } - it('ensureRooted roots paths', (done: MochaDone) => { + it('ensureRooted roots paths', (done) => { this.timeout(1000); if (process.platform == 'win32') { @@ -120,7 +120,7 @@ describe('Internal Path Helper Tests', function () { `expected getDirectoryName for input <${path}> to yield <${expected}>`); } - it('getDirectoryName interprets directory name from paths', (done: MochaDone) => { + it('getDirectoryName interprets directory name from paths', (done) => { this.timeout(1000); assertDirectoryName(null, ''); @@ -244,7 +244,7 @@ describe('Internal Path Helper Tests', function () { `expected isRooted for input <${path}> to yield <${expected}>`); } - it('isRooted detects root', (done: MochaDone) => { + it('isRooted detects root', (done) => { this.timeout(1000); if (process.platform == 'win32') { @@ -335,7 +335,7 @@ describe('Internal Path Helper Tests', function () { `expected normalizeSeparators for input <${path}> to yield <${expected}>`); } - it('normalizeSeparators', (done: MochaDone) => { + it('normalizeSeparators', (done) => { this.timeout(1000); if (process.platform == 'win32') { @@ -403,7 +403,7 @@ describe('Internal Path Helper Tests', function () { done(); }); - it('ReportMissingStrings', (done: MochaDone) => { + it('ReportMissingStrings', (done) => { mockery.registerAllowable('../_build/internal') const fsMock = { @@ -425,7 +425,7 @@ describe('Internal Path Helper Tests', function () { done(); }); - it('ReportMissingLocalization', (done: MochaDone) => { + it('ReportMissingLocalization', (done) => { const localizedMessage : string = im._loc("gizmo", "whatever", "music"); assert.strictEqual(localizedMessage, "gizmo whatever music"); done(); diff --git a/node/test/isuncpathtests.ts b/node/test/isuncpathtests.ts index 9beb1cbe3..714ee3ed7 100644 --- a/node/test/isuncpathtests.ts +++ b/node/test/isuncpathtests.ts @@ -19,7 +19,7 @@ describe('Is UNC-path Tests', function () { after(function () { }); - it('checks if path is unc path', (done: MochaDone) => { + it('checks if path is unc path', (done) => { this.timeout(1000); const paths = [ diff --git a/node/test/legacyfindfilestests.ts b/node/test/legacyfindfilestests.ts index 89c7fd05b..01c1b36e0 100644 --- a/node/test/legacyfindfilestests.ts +++ b/node/test/legacyfindfilestests.ts @@ -24,7 +24,7 @@ describe('Legacy Find Files Tests', function () { after(function () { }); - it('supports directory name single char wildcard', (done: MochaDone) => { + it('supports directory name single char wildcard', (done) => { this.timeout(1000); // create the following layout: @@ -55,7 +55,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports directory name wildcard', (done: MochaDone) => { + it('supports directory name wildcard', (done) => { this.timeout(1000); // create the following layout: @@ -86,7 +86,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports exclude patterns', (done: MochaDone) => { + it('supports exclude patterns', (done) => { this.timeout(1000); // create the following layout: @@ -127,7 +127,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports file name single char wildcard', (done: MochaDone) => { + it('supports file name single char wildcard', (done) => { this.timeout(1000); // create the following layout: @@ -154,7 +154,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports file name wildcard', (done: MochaDone) => { + it('supports file name wildcard', (done) => { this.timeout(1000); // create the following layout: @@ -181,7 +181,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports globstar', (done: MochaDone) => { + it('supports globstar', (done) => { this.timeout(1000); // create the following layout: @@ -214,7 +214,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports include directories', (done: MochaDone) => { + it('supports include directories', (done) => { this.timeout(1000); // create the following layout: @@ -243,7 +243,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports include directories only', (done: MochaDone) => { + it('supports include directories only', (done) => { this.timeout(1000); // create the following layout: @@ -270,7 +270,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports inter-segment wildcard', (done: MochaDone) => { + it('supports inter-segment wildcard', (done) => { this.timeout(1000); // create the following layout: @@ -310,7 +310,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('unions matches', (done: MochaDone) => { + it('unions matches', (done) => { this.timeout(1000); // create the following layout: @@ -338,7 +338,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('has platform-specific case sensitivity', (done: MochaDone) => { + it('has platform-specific case sensitivity', (done) => { this.timeout(1000); // create the following layout: @@ -373,7 +373,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports literal ; in pattern', (done: MochaDone) => { + it('supports literal ; in pattern', (done) => { this.timeout(1000); // create the following layout: @@ -395,7 +395,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports literal ; in rootDirectory', (done: MochaDone) => { + it('supports literal ; in rootDirectory', (done) => { this.timeout(1000); // create the following layout: @@ -419,7 +419,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports pattern is ;', (done: MochaDone) => { + it('supports pattern is ;', (done) => { this.timeout(1000); // create the following layout: @@ -441,7 +441,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('does not support pattern with trailing slash', (done: MochaDone) => { + it('does not support pattern with trailing slash', (done) => { this.timeout(1000); let pattern = path.join(__dirname, 'hello', 'world') + '/'; @@ -455,7 +455,7 @@ describe('Legacy Find Files Tests', function () { } }); - it('has platform-specific support for pattern with trailing backslash', (done: MochaDone) => { + it('has platform-specific support for pattern with trailing backslash', (done) => { this.timeout(1000); if (process.platform == 'win32') { @@ -493,7 +493,7 @@ describe('Legacy Find Files Tests', function () { } }); - it('follows symlink dirs', (done: MochaDone) => { + it('follows symlink dirs', (done) => { this.timeout(1000); // create the following layout: @@ -528,7 +528,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports alternate include syntax', (done: MochaDone) => { + it('supports alternate include syntax', (done) => { this.timeout(1000); // create the following layout: @@ -556,7 +556,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('appends root directory', (done: MochaDone) => { + it('appends root directory', (done) => { this.timeout(1000); // create the following layout: @@ -584,7 +584,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports hidden files', (done: MochaDone) => { + it('supports hidden files', (done) => { this.timeout(1000); // create the following layout: @@ -611,7 +611,7 @@ describe('Legacy Find Files Tests', function () { done(); }); - it('supports hidden folders', (done: MochaDone) => { + it('supports hidden folders', (done) => { this.timeout(1000); // create the following layout: @@ -662,7 +662,7 @@ describe('Legacy Find Files Tests', function () { `pattern '${pattern}' should not match path '${path}'`); } - it('converts patterns to RegExp', (done: MochaDone) => { + it('converts patterns to RegExp', (done) => { let tlAny = tl as any; if (process.platform == 'win32') { // should convert to forward slashes diff --git a/node/test/matchtests.ts b/node/test/matchtests.ts index 3aff887c9..373cc2b53 100644 --- a/node/test/matchtests.ts +++ b/node/test/matchtests.ts @@ -20,7 +20,7 @@ describe('Match Tests', function () { after(function () { }); - it('single pattern', (done: MochaDone) => { + it('single pattern', (done) => { this.timeout(1000); let list: string[] = [ @@ -38,7 +38,7 @@ describe('Match Tests', function () { done(); }); - it('aggregates matches', (done: MochaDone) => { + it('aggregates matches', (done) => { this.timeout(1000); let list: string[] = [ @@ -59,7 +59,7 @@ describe('Match Tests', function () { done(); }); - it('does not duplicate matches', (done: MochaDone) => { + it('does not duplicate matches', (done) => { this.timeout(1000); let list: string[] = [ @@ -83,7 +83,7 @@ describe('Match Tests', function () { done(); }); - it('preserves order', (done: MochaDone) => { + it('preserves order', (done) => { this.timeout(1000); let list: string[] = [ @@ -112,7 +112,7 @@ describe('Match Tests', function () { done(); }); - it('supports interleaved exclude patterns', (done: MochaDone) => { + it('supports interleaved exclude patterns', (done) => { this.timeout(1000); let list: string[] = [ @@ -144,7 +144,7 @@ describe('Match Tests', function () { done(); }); - it('applies default options', (done: MochaDone) => { + it('applies default options', (done) => { this.timeout(1000); let list: string[] = [ @@ -200,7 +200,7 @@ describe('Match Tests', function () { done(); }); - it('trims patterns', (done: MochaDone) => { + it('trims patterns', (done) => { this.timeout(1000); let list: string[] = [ @@ -219,7 +219,7 @@ describe('Match Tests', function () { done(); }); - it('skips empty patterns', (done: MochaDone) => { + it('skips empty patterns', (done) => { this.timeout(1000); let list: string[] = [ @@ -241,7 +241,7 @@ describe('Match Tests', function () { done(); }); - it('supports nocomment true', (done: MochaDone) => { + it('supports nocomment true', (done) => { this.timeout(1000); let list: string[] = [ @@ -260,7 +260,7 @@ describe('Match Tests', function () { done(); }); - it('supports nonegate true', (done: MochaDone) => { + it('supports nonegate true', (done) => { this.timeout(1000); let list: string[] = [ @@ -279,7 +279,7 @@ describe('Match Tests', function () { done(); }); - it('supports flipNegate true', (done: MochaDone) => { + it('supports flipNegate true', (done) => { this.timeout(1000); let list: string[] = [ @@ -298,7 +298,7 @@ describe('Match Tests', function () { done(); }); - it('counts leading negate markers', (done: MochaDone) => { + it('counts leading negate markers', (done) => { this.timeout(1000); let list: string[] = [ @@ -327,7 +327,7 @@ describe('Match Tests', function () { done(); }); - it('trims whitespace after trimming negate markers', (done: MochaDone) => { + it('trims whitespace after trimming negate markers', (done) => { this.timeout(1000); let list: string[] = [ @@ -347,7 +347,7 @@ describe('Match Tests', function () { done(); }); - it('evaluates comments before negation', (done: MochaDone) => { + it('evaluates comments before negation', (done) => { this.timeout(1000); let list: string[] = [ @@ -369,7 +369,7 @@ describe('Match Tests', function () { done(); }); - it('applies pattern root for include patterns', (done: MochaDone) => { + it('applies pattern root for include patterns', (done) => { this.timeout(1000); let list: string[] = [ @@ -394,7 +394,7 @@ describe('Match Tests', function () { done(); }); - it('applies pattern root for exclude patterns', (done: MochaDone) => { + it('applies pattern root for exclude patterns', (done) => { this.timeout(1000); let list: string[] = [ @@ -421,7 +421,7 @@ describe('Match Tests', function () { done(); }); - it('does not apply pattern root for basename matchBase include patterns', (done: MochaDone) => { + it('does not apply pattern root for basename matchBase include patterns', (done) => { this.timeout(1000); let list: string[] = [ @@ -448,7 +448,7 @@ describe('Match Tests', function () { done(); }); - it('does not apply pattern root for basename matchBase exclude patterns', (done: MochaDone) => { + it('does not apply pattern root for basename matchBase exclude patterns', (done) => { this.timeout(1000); let list: string[] = [ diff --git a/node/test/mocktests.ts b/node/test/mocktests.ts index a6cef55a1..6c8e44095 100644 --- a/node/test/mocktests.ts +++ b/node/test/mocktests.ts @@ -162,13 +162,13 @@ describe('Mock Tests', function () { done(); }) - it('Mock loc returns key', (done: MochaDone) => { + it('Mock loc returns key', (done) => { let actual = mt.loc('STR_KEY'); assert.equal(actual, 'loc_mock_STR_KEY'); done(); }) - it('Mock loc returns key and args', (done: MochaDone) => { + it('Mock loc returns key and args', (done) => { let actual = mt.loc('STR_KEY', false, 2, 'three'); assert.equal(actual, 'loc_mock_STR_KEY false 2 three'); done(); @@ -318,13 +318,13 @@ describe('Mock Tests', function () { done(); }) - it('MockTest handles node 14 tasks correctly', function (done) { + it('MockTest handles node 16 tasks correctly', function (done) { this.timeout(30000); - const runner = new mtm.MockTestRunner(path.join(__dirname, 'fakeTasks', 'node14task', 'entry.js')); + const runner = new mtm.MockTestRunner(path.join(__dirname, 'fakeTasks', 'node16task', 'entry.js')); const nodePath = runner.nodePath; assert(nodePath, 'node path should have been correctly set'); const version = ncp.execSync(nodePath + ' -v').toString().trim(); - assert(semver.satisfies(version, '14.x'), 'Downloaded node version should be Node 14 instead of ' + version); + assert(semver.satisfies(version, '16.x'), 'Downloaded node version should be Node 16 instead of ' + version); done(); }) }); diff --git a/node/test/retrytests.ts b/node/test/retrytests.ts index 3f4831b13..bedd68d1a 100644 --- a/node/test/retrytests.ts +++ b/node/test/retrytests.ts @@ -19,7 +19,7 @@ describe('Retry Tests', function () { after(function () { }); - it('retries to execute a function', (done: MochaDone) => { + it('retries to execute a function', (done) => { this.timeout(1000); const testError = Error('Test Error'); diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index d6d2f6850..e54ccce30 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -1378,8 +1378,12 @@ describe('Toolrunner Tests', function () { // validate stdout assert.equal( output.trim(), - "args[0]: 'myarg1'\r\n" - + "args[1]: 'myarg2'"); + "args[0]: 'args'\r\n" + + "args[1]: 'exe'\r\n" + + "args[2]: 'with'\r\n" + + "args[3]: 'spaces.exe'\r\n" + + "args[4]: 'myarg1'\r\n" + + "args[5]: 'myarg2'"); done(); }) .fail(function (err) { @@ -1641,8 +1645,12 @@ describe('Toolrunner Tests', function () { // validate stdout assert.equal( result.stdout.trim(), - "args[0]: 'myarg1'\r\n" - + "args[1]: 'myarg2'"); + "args[0]: 'args'\r\n" + + "args[1]: 'exe'\r\n" + + "args[2]: 'with'\r\n" + + "args[3]: 'spaces.exe'\r\n" + + "args[4]: 'myarg1'\r\n" + + "args[5]: 'myarg2'") done(); }); diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 843dfe2b7..8e0607c7b 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -594,6 +594,7 @@ export class ToolRunner extends events.EventEmitter { private _getSpawnSyncOptions(options: IExecSyncOptions): child.SpawnSyncOptions { let result = {}; + result.maxBuffer = 1024 * 1024 * 1024; result.cwd = options.cwd; result.env = options.env; result.shell = options.shell; diff --git a/node/vault.ts b/node/vault.ts index f0a39534a..f5c8bdf80 100644 --- a/node/vault.ts +++ b/node/vault.ts @@ -67,7 +67,7 @@ export class Vault { var encryptedText = ivDataBuffer.slice(16); var decipher = crypto.createDecipheriv(algorithm, key, iv); - var dec = decipher.update(encryptedText,encryptEncoding,unencryptedEncoding); + var dec = decipher.update(encryptedText); var decFinal = decipher.final(unencryptedEncoding); secret = dec + decFinal; diff --git a/tasks.schema.json b/tasks.schema.json index 414d4157b..08dcde4d4 100644 --- a/tasks.schema.json +++ b/tasks.schema.json @@ -385,6 +385,9 @@ "additionalProperties": false, "description": "Execution options for this task (on Pre-Job stage)", "properties": { + "Node16": { + "$ref": "#/definitions/executionObject" + }, "Node10": { "$ref": "#/definitions/executionObject" }, @@ -404,6 +407,9 @@ "additionalProperties": false, "description": "Execution options for this task", "properties": { + "Node16": { + "$ref": "#/definitions/executionObject" + }, "Node10": { "$ref": "#/definitions/executionObject" }, @@ -423,6 +429,9 @@ "additionalProperties": false, "description": "Execution options for this task (on Post-Job stage)", "properties": { + "Node16": { + "$ref": "#/definitions/executionObject" + }, "Node10": { "$ref": "#/definitions/executionObject" }, From 5f76bee724ad65d0186392fd2407dfd119fa4332 Mon Sep 17 00:00:00 2001 From: Maxim Zaytsev Date: Sat, 3 Dec 2022 19:18:09 +0100 Subject: [PATCH 246/259] Fixed Readme for npm package (#904) --- node/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/node/README.md b/node/README.md index d840ceccd..5cb32dd49 100644 --- a/node/README.md +++ b/node/README.md @@ -13,9 +13,9 @@ Cross platform tasks are written in TypeScript. It is the preferred way to writ Step by Step: [Create Task](https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=vsts) -Documentation: [TypeScript API](docs/azure-pipelines-task-lib.md), [task JSON schema](https://aka.ms/vsts-tasks.schema.json) +Documentation: [TypeScript API](https://github.com/microsoft/azure-pipelines-task-lib/blob/master/node/docs/azure-pipelines-task-lib.md), [task JSON schema](https://aka.ms/vsts-tasks.schema.json) -Guidance: [Finding Files](docs/findingfiles.md), [Minimum agent version](docs/minagent.md), [Proxy](docs/proxy.md), [Certificate](docs/cert.md) +Guidance: [Finding Files](https://github.com/microsoft/azure-pipelines-task-lib/blob/master/node/docs/findingfiles.md), [Minimum agent version](https://github.com/microsoft/azure-pipelines-task-lib/blob/master/node/docs/minagent.md), [Proxy](https://github.com/microsoft/azure-pipelines-task-lib/blob/master/node/docs/proxy.md), [Certificate](https://github.com/microsoft/azure-pipelines-task-lib/blob/master/node/docs/cert.md) ## Node 10 Upgrade Notice @@ -37,7 +37,7 @@ The [ShellScript Task](https://github.com/Microsoft/azure-pipelines-tasks/tree/m We are accepting contributions and we try to stay on top of issues. -[Contribution Guide](../CONTRIBUTING.md). +[Contribution Guide](https://github.com/microsoft/azure-pipelines-task-lib/blob/master/CONTRIBUTING.md). [Logging Issues](https://github.com/Microsoft/azure-pipelines-task-lib/issues) @@ -63,9 +63,9 @@ Set environment variable TASK_TEST_TRACE=1 to display test output. We also maintain a PowerShell library for Windows task development. -Library: [Powershell Library](../powershell) +Library: [Powershell Library](https://github.com/microsoft/azure-pipelines-task-lib/tree/master/powershell) -Usage: [Consuming the SDK](../powershell/Docs/Consuming.md) +Usage: [Consuming the SDK](https://github.com/microsoft/azure-pipelines-task-lib/blob/master/powershell/Docs/Consuming.md) ## Third Party Notices To generate/update third party notice file run: From 0d2f17bf6a299c0d784a85dbf273974052a70b7b Mon Sep 17 00:00:00 2001 From: Dmitrii Bobreshev <106314398+DmitriiBobreshev@users.noreply.github.com> Date: Mon, 9 Jan 2023 12:36:55 +0100 Subject: [PATCH 247/259] Added unhandledRejection event (#912) The event "unhandledRejection" was added on process.on because when Promise is rejected and there is no catch the error will not be thrown and process.on('uncaughtException') won't call for Node10 but it will emit "uncaughtException" for node 16. So we need to listen to "unhandledRejection" and throw the error to make sure the error is thrown and the result will be similar. --- node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 7bf741552..247aa110b 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.1.0", + "version": "4.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index aa7d571bb..8f0cb19e8 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.1.0", + "version": "4.2.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index d948b4b73..74013b9e5 100644 --- a/node/task.ts +++ b/node/task.ts @@ -108,6 +108,20 @@ process.on('uncaughtException', (err: Error) => { error(String(err.stack)); }); +// +// Catching unhandled rejections from promises and rethrowing them as exceptions +// For example, a promise that is rejected but not handled by a .catch() handler in node 10 +// doesn't cause an uncaughtException but causes in Node 16. +// For types definitions(Error | Any) see https://nodejs.org/docs/latest-v16.x/api/process.html#event-unhandledrejection +// +process.on('unhandledRejection', (reason: Error | any) => { + if (reason instanceof Error) { + throw reason; + } else { + throw new Error(reason); + } +}); + //----------------------------------------------------- // Loc Helpers //----------------------------------------------------- From 7d04ce6cdbd7a760ebfe3338ae9d8a6480dd1e59 Mon Sep 17 00:00:00 2001 From: Dmitrii Bobreshev <106314398+DmitriiBobreshev@users.noreply.github.com> Date: Mon, 16 Jan 2023 13:08:51 +0100 Subject: [PATCH 248/259] Updated CHANGELOG.md (#915) --- node/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/node/CHANGELOG.md b/node/CHANGELOG.md index 4691bcbc1..722d4bee3 100644 --- a/node/CHANGELOG.md +++ b/node/CHANGELOG.md @@ -32,3 +32,7 @@ Backported from ver.`3.4.0`: - Include uncought exceptions stack trace to the output logs - [#895](https://github.com/microsoft/azure-pipelines-task-lib/pull/895) + +## 4.2.0 + +- Added unhandledRejection event - [#912](https://github.com/microsoft/azure-pipelines-task-lib/pull/912) \ No newline at end of file From 149de35abb4ee930cf512c3d88d9178f277fb3fe Mon Sep 17 00:00:00 2001 From: Konstantin Tyukalov <52399739+KonstantinTyukalov@users.noreply.github.com> Date: Mon, 27 Feb 2023 19:29:38 +0400 Subject: [PATCH 249/259] Described types for `argIf` (#920) * Improve types for argIf command * Bump package version * Update changelog --- node/CHANGELOG.md | 6 +++++- node/package-lock.json | 2 +- node/package.json | 2 +- node/toolrunner.ts | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/node/CHANGELOG.md b/node/CHANGELOG.md index 722d4bee3..b53208324 100644 --- a/node/CHANGELOG.md +++ b/node/CHANGELOG.md @@ -35,4 +35,8 @@ Backported from ver.`3.4.0`: ## 4.2.0 -- Added unhandledRejection event - [#912](https://github.com/microsoft/azure-pipelines-task-lib/pull/912) \ No newline at end of file +- Added unhandledRejection event - [#912](https://github.com/microsoft/azure-pipelines-task-lib/pull/912) + +## 4.3.0 + +- Described types for `argIf` - [#920](https://github.com/microsoft/azure-pipelines-task-lib/pull/920) diff --git a/node/package-lock.json b/node/package-lock.json index 247aa110b..1e91f0715 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.2.0", + "version": "4.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 8f0cb19e8..d5ea2f748 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.2.0", + "version": "4.3.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 8e0607c7b..7fbfb0635 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -853,7 +853,7 @@ export class ToolRunner extends events.EventEmitter { * @param val string cmdline or array of strings * @returns ToolRunner */ - public argIf(condition: any, val: any) { + public argIf(condition: unknown, val: string | string[]): ToolRunner { if (condition) { this.arg(val); } From 6505de9a108b04e9b8a81423c78081441ae1dc14 Mon Sep 17 00:00:00 2001 From: Maxim Zaytsev Date: Wed, 15 Mar 2023 17:42:17 +0100 Subject: [PATCH 250/259] Update autoAssignAbtt workflow (#923) --- .github/workflows/autoAssignABTT.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/autoAssignABTT.yml b/.github/workflows/autoAssignABTT.yml index 60c2e04d4..ceff0146e 100644 --- a/.github/workflows/autoAssignABTT.yml +++ b/.github/workflows/autoAssignABTT.yml @@ -2,9 +2,8 @@ name: Auto Assign ABTT to Project Board on: issues: - types: [opened] -env: - MY_GITHUB_TOKEN: ${{ secrets.ABTT_TOKEN }} + types: + - opened jobs: assign_one_project: @@ -18,8 +17,9 @@ jobs: labels: | Area: TaskLib triage + - name: "Assign newly opened issues to project board" - uses: srggrs/assign-one-project-github-action@1.2.0 + uses: actions/add-to-project@v0.4.1 with: - project: 'https://github.com/orgs/microsoft/projects/48' - column_name: 'Backlog' + project-url: https://github.com/orgs/microsoft/projects/755 + github-token: ${{ secrets.ABTT_TOKEN }} From 2915809e2a7e354b83d3c80ac8889781e2fe1590 Mon Sep 17 00:00:00 2001 From: Dmitrii Bobreshev <106314398+DmitriiBobreshev@users.noreply.github.com> Date: Mon, 20 Mar 2023 12:22:53 +0100 Subject: [PATCH 251/259] [Bug 2029503](P2): [Component Governance Alert] - CVE-2022-24999 in qs 6.9.4. Severity: High #4370 (#924) - Bumped qs version --- node/package-lock.json | 50 ++++++++++++++++++++++++++++++++++++++---- node/package.json | 2 +- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 1e91f0715..a0e33acda 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.3.0", + "version": "4.3.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -173,6 +173,15 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -386,6 +395,16 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, "get-port": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", @@ -433,6 +452,11 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -666,6 +690,11 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -738,9 +767,12 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, "qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "requires": { + "side-channel": "^1.0.4" + } }, "randombytes": { "version": "2.1.0", @@ -827,6 +859,16 @@ "rechoir": "^0.6.2" } }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", diff --git a/node/package.json b/node/package.json index d5ea2f748..33320c826 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.3.0", + "version": "4.3.1", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", From f7e17959900884f773e65b0e3d3d81ba69b6371f Mon Sep 17 00:00:00 2001 From: Konstantin Tyukalov <52399739+KonstantinTyukalov@users.noreply.github.com> Date: Fri, 31 Mar 2023 16:09:17 +0400 Subject: [PATCH 252/259] Remove note about v4 task lib status (#919) * Remove note about v4 task lib status * Fix formatting --- CONTRIBUTING.md | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 515a6bd54..0f3481b6a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,5 @@ # Instructions for Contributing Code -## Current status - -Since at the moment work is proceeding on migrating to Node16, you need to remember the following: - -- master branch for SDK v3 (stable and targets Node10) -- releases/4.x branch for SDK v4 (preview and targets Node16) -- Changes that were merged for SDK v3 should be cherry-picked for SDK v4 once they are merged to the master branch - ## Contributing bug fixes We are currently accepting contributions in the form of bug fixes. A bug must have an issue tracking it in the issue tracker. Your pull request should include a link to the bug that you are fixing. If you've submitted a PR for a bug, please post a comment in the bug to avoid duplication of effort. @@ -22,21 +14,20 @@ Design changes will not be accepted at this time. If you have a design change pr You will need to complete a Contributor License Agreement (CLA). Briefly, this agreement testifies that you are granting us permission to use the submitted change according to the terms of the project's license, and that the work being submitted is under appropriate copyright. -Please submit a Contributor License Agreement (CLA) before submitting a pull request. You may visit https://cla.microsoft.com to sign digitally. +Please submit a Contributor License Agreement (CLA) before submitting a pull request. You may visit to sign digitally. ## Housekeeping -Your pull request should: +Your pull request should: * Include a description of what your change intends to do -* Be a child commit of a reasonably recent commit in the **master** branch - * Requests need not be a single commit, but should be a linear sequence of commits (i.e. no merge commits in your PR) +* Be a child commit of a reasonably recent commit in the **master** branch + * Requests need not be a single commit, but should be a linear sequence of commits (i.e. no merge commits in your PR) * It is desirable, but not necessary, for the tests to pass at each commit -* Have clear commit messages - * e.g. "Refactor feature", "Fix issue", "Add tests for issue" -* Include adequate tests - * At least one test should fail in the absence of your non-test code changes. If your PR does not match this criteria, please specify why - * Tests should include reasonable permutations of the target fix/change - * Include baseline changes with your change - * All changed code must have 100% code coverage - +* Have clear commit messages + * e.g. "Refactor feature", "Fix issue", "Add tests for issue" +* Include adequate tests + * At least one test should fail in the absence of your non-test code changes. If your PR does not match this criteria, please specify why + * Tests should include reasonable permutations of the target fix/change + * Include baseline changes with your change + * All changed code must have 100% code coverage From eac6dd4c2379ca559ebadfad3b8c7bb9f4910719 Mon Sep 17 00:00:00 2001 From: Denis Rumyantsev Date: Fri, 21 Apr 2023 16:54:54 +0200 Subject: [PATCH 253/259] update ubuntu image (#925) --- azure-pipelines.yml | 8 ++++---- node/test/dirtests.ts | 23 +++++------------------ node/test/toolrunnertests.ts | 3 ++- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ff7e5648d..13dbb8179 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -14,7 +14,7 @@ jobs: ################################################# displayName: windows pool: - vmImage: windows-2019 + vmImage: windows-2022 steps: - template: azure-pipelines-steps-node.yml @@ -28,9 +28,9 @@ jobs: ################################################# displayName: Linux pool: - vmImage: ubuntu-18.04 + vmImage: ubuntu-22.04 - steps: + steps: - template: azure-pipelines-steps-node.yml parameters: nodeVersion: $(nodeVersion) @@ -57,7 +57,7 @@ jobs: ################################################# displayName: macOS pool: - vmImage: macOS-10.15 + vmImage: macOS-12 steps: - template: azure-pipelines-steps-node.yml diff --git a/node/test/dirtests.ts b/node/test/dirtests.ts index 2d5f32cd7..cfea46310 100644 --- a/node/test/dirtests.ts +++ b/node/test/dirtests.ts @@ -1381,33 +1381,20 @@ describe('Dir Operation Tests', function () { tl.mkdirP(testPath); assert(shell.test('-d', testPath), 'directory created'); - // can't remove folder with locked file on windows + // starting from windows-2022, + // can remove folder with locked file on windows as well, + // using the command `rd /s /q ` var filePath = path.join(testPath, 'file.txt'); fs.appendFileSync(filePath, 'some data'); assert(shell.test('-e', filePath), 'file exists'); var fd = fs.openSync(filePath, 'r'); - var worked = false; - try { - tl.rmRF(testPath); - worked = true; - } - catch (err) { } - - if (os.platform() === 'win32') { - assert(!worked, 'should not work on windows'); - assert(shell.test('-e', testPath), 'directory still exists'); - } - else { - assert(worked, 'should work on nix'); - assert(!shell.test('-e', testPath), 'directory removed'); - } - - fs.closeSync(fd); tl.rmRF(testPath); assert(!shell.test('-e', testPath), 'directory removed'); + fs.closeSync(fd); + done(); }); diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index e54ccce30..f534a2787 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -190,7 +190,8 @@ describe('Toolrunner Tests', function () { ls.exec(_testExecOptions) .then(function (code) { var contents = stdStream.getContents(); - assert(contents.indexOf('exec tool: /bin/ls') >= 0, 'should exec ls'); + const usr = os.platform() === 'linux' ? '/usr' : ''; + assert(contents.indexOf(`exec tool: ${usr}/bin/ls`) >= 0, 'should exec ls'); assert.equal(code, 0, 'return code of ls should be 0'); done(); }) From 4086d6bef172fe1329d3006ccacc324e05baac1e Mon Sep 17 00:00:00 2001 From: Sergey Koryshev Date: Wed, 3 May 2023 10:49:29 +0200 Subject: [PATCH 254/259] Update ReleaseNotes for PowerShell's VstsTaskSdk (#934) * updated release notes * bumped version up to 0.14.0 * made small changes --- powershell/Docs/ReleaseNotes.md | 11 +++++++++++ powershell/package-lock.json | 2 +- powershell/package.json | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/powershell/Docs/ReleaseNotes.md b/powershell/Docs/ReleaseNotes.md index 3b8820fbd..b4180428a 100644 --- a/powershell/Docs/ReleaseNotes.md +++ b/powershell/Docs/ReleaseNotes.md @@ -1,5 +1,16 @@ # Release Notes +## 0.14.0 +* Improved error handling in function `Find-Files` + +## 0.13.0 +* Added parameter `IgnoreHostException` for function `Invoke-Tool` to suppress `System.Management.Automation.Host.HostException` + +## 0.12.0 +* Fixed issue when Find-Match in powershell does not support searching a pattern in a path that does not exist (#365) +* Resolved issue with loading `Newtonsoft.Json.dll` +* Made `Write-LoggingCommand` exported + ## 0.11.0 * Added input functions `Get-SecureFileTicket` and `Get-SecureFileName`. * Added missing agent commands. diff --git a/powershell/package-lock.json b/powershell/package-lock.json index 2298eb426..01ac6abef 100644 --- a/powershell/package-lock.json +++ b/powershell/package-lock.json @@ -1,6 +1,6 @@ { "name": "vsts-task-sdk", - "version": "0.13.0", + "version": "0.14.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/powershell/package.json b/powershell/package.json index f6e132ecc..6064ef0c8 100644 --- a/powershell/package.json +++ b/powershell/package.json @@ -1,6 +1,6 @@ { "name": "vsts-task-sdk", - "version": "0.13.0", + "version": "0.14.0", "description": "VSTS Task SDK", "scripts": { "build": "node make.js build", From ab676de8207ae472532e84765d3c9c5e290cb85b Mon Sep 17 00:00:00 2001 From: Konstantin Tyukalov <52399739+KonstantinTyukalov@users.noreply.github.com> Date: Fri, 16 Jun 2023 12:41:52 +0400 Subject: [PATCH 255/259] Add `getBoolFeatureFlag` (#936) * Add getBoolFeatureFlag * Add mock export * Bump package to 4.4.0 * Update changelog * Add tests * Add default value param * Fix tests * Make false as default + upd description --- node/CHANGELOG.md | 8 +++++++ node/mock-task.ts | 1 + node/package-lock.json | 2 +- node/package.json | 2 +- node/task.ts | 28 +++++++++++++++++++---- node/test/inputtests.ts | 50 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 85 insertions(+), 6 deletions(-) diff --git a/node/CHANGELOG.md b/node/CHANGELOG.md index b53208324..6dfd4d457 100644 --- a/node/CHANGELOG.md +++ b/node/CHANGELOG.md @@ -40,3 +40,11 @@ Backported from ver.`3.4.0`: ## 4.3.0 - Described types for `argIf` - [#920](https://github.com/microsoft/azure-pipelines-task-lib/pull/920) + +## 4.3.1 + +- Resolve CVE-2022-24999 in qs 6.9.4 [#924](https://github.com/microsoft/azure-pipelines-task-lib/pull/924) + +## 4.4.0 + +- Add `getBoolFeatureFlag` [#936](https://github.com/microsoft/azure-pipelines-task-lib/pull/936) diff --git a/node/mock-task.ts b/node/mock-task.ts index fc374d3bd..8b2b0d37f 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -62,6 +62,7 @@ module.exports.setTaskVariable = task.setTaskVariable; module.exports.getInput = task.getInput; module.exports.getInputRequired = task.getInputRequired; module.exports.getBoolInput = task.getBoolInput; +module.exports.getBoolFeatureFlag = task.getBoolFeatureFlag; module.exports.getDelimitedInput = task.getDelimitedInput; module.exports.filePathSupplied = task.filePathSupplied; diff --git a/node/package-lock.json b/node/package-lock.json index a0e33acda..dbe46d6c4 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.3.1", + "version": "4.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/node/package.json b/node/package.json index 33320c826..31484d447 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.3.1", + "version": "4.4.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", diff --git a/node/task.ts b/node/task.ts index 74013b9e5..3ecbebfde 100644 --- a/node/task.ts +++ b/node/task.ts @@ -273,6 +273,26 @@ export function getBoolInput(name: string, required?: boolean): boolean { return (getInput(name, required) || '').toUpperCase() == "TRUE"; } +/** + * Gets the value of an feature flag and converts to a bool. + * + * @param name name of the feature flag to get. + * @param defaultValue default value of the feature flag in case it's not found in env. (optional. Default value = false) + * @returns boolean + */ +export function getBoolFeatureFlag(ffName: string, defaultValue: boolean = false): boolean { + const ffValue = process.env[ffName]; + + if (!ffValue) { + debug(`Feature flag ${ffName} not found. Returning ${defaultValue} as default.`); + return defaultValue; + } + + debug(`Feature flag ${ffName} = ${ffValue}`); + + return ffValue.toLowerCase() === "true"; +} + /** * Gets the value of an input and splits the value using a delimiter (space, comma, etc). * Empty values are removed. This function is useful for splitting an input containing a simple @@ -647,8 +667,8 @@ export function stats(path: string): FsStats { export const exist = im._exist; export function writeFile(file: string, data: string | Buffer, options?: BufferEncoding | fs.WriteFileOptions) { - if (typeof(options) === 'string'){ - fs.writeFileSync(file, data, {encoding: options as BufferEncoding}); + if (typeof (options) === 'string') { + fs.writeFileSync(file, data, { encoding: options as BufferEncoding }); } else { fs.writeFileSync(file, data, options); @@ -689,7 +709,7 @@ export function getAgentMode(): AgentHostedMode { if (agentCloudId === undefined) return AgentHostedMode.Unknown; - + if (agentCloudId) return AgentHostedMode.MsHosted; @@ -980,7 +1000,7 @@ export function retry(func: Function, args: any[], retryOptions: RetryOptions = * @param allowBrokenSymbolicLinks when true, broken symbolic link will not cause an error. * @returns fs.Stats */ -function _getStats (path: string, followSymbolicLink: boolean, allowBrokenSymbolicLinks: boolean): fs.Stats { +function _getStats(path: string, followSymbolicLink: 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; diff --git a/node/test/inputtests.ts b/node/test/inputtests.ts index 76213d7fd..51109f555 100644 --- a/node/test/inputtests.ts +++ b/node/test/inputtests.ts @@ -1150,4 +1150,54 @@ describe('Input Tests', function () { assert.equal(tl.getInput('SomeInput'), 'some input value'); done(); }) + + describe('Feature flags tests', () => { + + ([ + ["true", true], + ["TRUE", true], + ["TruE", true], + ["false", false], + ["treu", false], + ["fasle", false], + ["On", false], + ["", false], + [undefined, false] + ] as [string, boolean][]) + .forEach( + ( + [ + input, + expectedResult + ] + ) => { + it(`Should return ${expectedResult} if feature flag env is ${input}`, () => { + const ffName = "SOME_TEST_FF" + process.env[ffName] = input + + const ffValue = tl.getBoolFeatureFlag(ffName, false); + + assert.equal(ffValue, expectedResult); + }) + } + ); + + it(`Should return default value if feature flag env is empty`, () => { + const ffName = "SOME_TEST_FF" + process.env[ffName] = "" + + const ffValue = tl.getBoolFeatureFlag(ffName, true); + + assert.equal(ffValue, true); + }) + + it(`Should return default value if feature flag env is not specified`, () => { + const ffName = "SOME_TEST_FF" + delete process.env[ffName]; + + const ffValue = tl.getBoolFeatureFlag(ffName, true); + + assert.equal(ffValue, true); + }) + }); }); From ff62c9b5a80b53e480e4dbfe87ace11244240dba Mon Sep 17 00:00:00 2001 From: ivanduplenskikh <115665590+ivanduplenskikh@users.noreply.github.com> Date: Thu, 6 Jul 2023 12:41:55 +0300 Subject: [PATCH 256/259] #940 (#941) Added overload signature. When the result argument is TaskResult.Succeeded so the message can be optional --- node/task.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/node/task.ts b/node/task.ts index 3ecbebfde..42ac50ccd 100644 --- a/node/task.ts +++ b/node/task.ts @@ -80,6 +80,8 @@ export const setErrStream = im._setErrStream; * from agent version 2.142.0 or higher (otherwise will no-op). * @returns void */ +export function setResult(result: TaskResult.Succeeded, message?: string, done?: boolean): void; +export function setResult(result: Exclude, message: string, done?: boolean): void; export function setResult(result: TaskResult, message: string, done?: boolean): void { debug('task result: ' + TaskResult[result]); From 44e727df221e785e8f72fbf0b061ff9651204b09 Mon Sep 17 00:00:00 2001 From: Aleksandr Levochkin <107044793+aleksandrlevochkin@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:47:05 +0200 Subject: [PATCH 257/259] refactor: remove Q library from Powershell SDK in favor of native promises (#944) * Removed Q library in favor of native promises from pwsh subfolder * Changed function name inside a block comment to prevent it from being parsed in a test --- powershell/Tests/L0/_suite.ts | 2 - powershell/Tests/lib/psRunner.ts | 17 +- .../VstsTaskSdk/LocalizationFunctions.ps1 | 4 +- .../resources.resjson/en-US/resources.resjson | 4 +- powershell/definitions/Q.d.ts | 386 ------------------ powershell/make.js | 4 +- powershell/package-lock.json | 6 - powershell/package.json | 1 - 8 files changed, 13 insertions(+), 411 deletions(-) delete mode 100644 powershell/definitions/Q.d.ts diff --git a/powershell/Tests/L0/_suite.ts b/powershell/Tests/L0/_suite.ts index 64c97c981..27b9bd0eb 100644 --- a/powershell/Tests/L0/_suite.ts +++ b/powershell/Tests/L0/_suite.ts @@ -1,8 +1,6 @@ /// /// -/// -import Q = require('q'); import assert = require('assert'); import psRunner = require('../lib/psRunner'); import path = require('path'); diff --git a/powershell/Tests/lib/psRunner.ts b/powershell/Tests/lib/psRunner.ts index 4a1dc3c1d..ec143d5f6 100644 --- a/powershell/Tests/lib/psRunner.ts +++ b/powershell/Tests/lib/psRunner.ts @@ -1,7 +1,5 @@ /// -/// -import Q = require('q'); import events = require('events'); import fs = require('fs'); import path = require('path'); @@ -21,7 +19,7 @@ class PSEngineRunner extends events.EventEmitter { private _childProcess: child.ChildProcess; private _errors: string[]; - private _runDeferred: Q.Deferred; + private _runDeferred: Promise; public stderr: string; public stdout: string; @@ -39,7 +37,7 @@ class PSEngineRunner extends events.EventEmitter { .then(() => { done(); }) - .fail((err) => { + .catch((err) => { done(err); }); } @@ -50,7 +48,6 @@ class PSEngineRunner extends events.EventEmitter { } this.emit('starting'); - var defer = Q.defer(); var powershell = shell.which('powershell.exe').stdout; this._childProcess = child.spawn( powershell, // command @@ -74,9 +71,9 @@ class PSEngineRunner extends events.EventEmitter { // Check for special ouput indicating end of test. if (('' + data).indexOf('_END_OF_TEST_ce10a77a_') >= 0) { if (this._errors.length > 0) { - this._runDeferred.reject(this._errors.join('\n')); + this._runDeferred = Promise.reject(this._errors.join('\n')); } else { - this._runDeferred.resolve(null); + this._runDeferred = Promise.resolve(); } } else if (data != '\n') { if (('' + data).match(/##vso\[task.logissue .*type=error/)) { @@ -98,12 +95,12 @@ class PSEngineRunner extends events.EventEmitter { }); } - private runPromise(psPath: string): Q.Promise { + private runPromise(psPath: string): Promise { this.emit('running test'); this._errors = []; - this._runDeferred = Q.defer(); + this._runDeferred = Promise.resolve(); this._childProcess.stdin.write(psPath + '\n') - return >this._runDeferred.promise; + return this._runDeferred; } } diff --git a/powershell/VstsTaskSdk/LocalizationFunctions.ps1 b/powershell/VstsTaskSdk/LocalizationFunctions.ps1 index 6f3b6bb06..b5549700f 100644 --- a/powershell/VstsTaskSdk/LocalizationFunctions.ps1 +++ b/powershell/VstsTaskSdk/LocalizationFunctions.ps1 @@ -60,10 +60,10 @@ function Get-LocString { <# .SYNOPSIS -Imports resource strings for use with Get-VstsLocString. +Imports resource strings for use with GetVstsLocString. .DESCRIPTION -Imports resource strings for use with Get-VstsLocString. The imported strings are stored in an internal resource string dictionary. Optionally, if a separate resource file for the current culture exists, then the localized strings from that file then imported (overlaid) into the same internal resource string dictionary. +Imports resource strings for use with GetVstsLocString. The imported strings are stored in an internal resource string dictionary. Optionally, if a separate resource file for the current culture exists, then the localized strings from that file then imported (overlaid) into the same internal resource string dictionary. Resource strings from the SDK are prefixed with "PSLIB_". This prefix should be avoided for custom resource strings. diff --git a/powershell/VstsTaskSdk/Strings/resources.resjson/en-US/resources.resjson b/powershell/VstsTaskSdk/Strings/resources.resjson/en-US/resources.resjson index fb2fa4e2a..66c17bc8d 100644 --- a/powershell/VstsTaskSdk/Strings/resources.resjson/en-US/resources.resjson +++ b/powershell/VstsTaskSdk/Strings/resources.resjson/en-US/resources.resjson @@ -6,7 +6,7 @@ "loc.messages.PSLIB_EnumeratingSubdirectoriesFailedForPath0": "Enumerating subdirectories failed for path: '{0}'", "loc.messages.PSLIB_FileNotFound0": "File not found: '{0}'", "loc.messages.PSLIB_Input0": "'{0}' input", - "loc.messages.PSLIB_InvalidPattern0": "Path cannot end with a directory separator character: '{0}'", + "loc.messages.PSLIB_InvalidPattern0": "Invalid pattern: '{0}'", "loc.messages.PSLIB_LeafPathNotFound0": "Leaf path not found: '{0}'", "loc.messages.PSLIB_PathLengthNotReturnedFor0": "Path normalization/expansion failed. The path length was not returned by the Kernel32 subsystem for: '{0}'", "loc.messages.PSLIB_PathNotFound0": "Path not found: '{0}'", @@ -15,4 +15,4 @@ "loc.messages.PSLIB_StringFormatFailed": "String format failed.", "loc.messages.PSLIB_StringResourceKeyNotFound0": "String resource key not found: '{0}'", "loc.messages.PSLIB_TaskVariable0": "'{0}' task variable" -} +} \ No newline at end of file diff --git a/powershell/definitions/Q.d.ts b/powershell/definitions/Q.d.ts deleted file mode 100644 index f47aa4f42..000000000 --- a/powershell/definitions/Q.d.ts +++ /dev/null @@ -1,386 +0,0 @@ -// Type definitions for Q -// Project: https://github.com/kriskowal/q -// Definitions by: Barrie Nemetchek , Andrew Gaspar , John Reilly -// Definitions: https://github.com/borisyankov/DefinitelyTyped - -/** - * If value is a Q promise, returns the promise. - * If value is a promise from another library it is coerced into a Q promise (where possible). - */ -declare function Q(promise: Q.IPromise): Q.Promise; -/** - * If value is not a promise, returns a promise that is fulfilled with value. - */ -declare function Q(value: T): Q.Promise; - -declare module Q { - interface IPromise { - then(onFulfill: (value: T) => IPromise, onReject?: (reason: any) => IPromise): IPromise; - then(onFulfill: (value: T) => IPromise, onReject?: (reason: any) => U): IPromise; - then(onFulfill: (value: T) => U, onReject?: (reason: any) => IPromise): IPromise; - then(onFulfill: (value: T) => U, onReject?: (reason: any) => U): IPromise; - } - - interface Deferred { - promise: Promise; - resolve(value: T): void; - reject(reason: any): void; - notify(value: any): void; - makeNodeResolver(): (reason: any, value: T) => void; - } - - interface Promise { - /** - * Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object. - - * finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished. - */ - fin(finallyCallback: () => any): Promise; - /** - * Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object. - - * finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished. - */ - finally(finallyCallback: () => any): Promise; - - /** - * The then method from the Promises/A+ specification, with an additional progress handler. - */ - then(onFulfill: (value: T) => IPromise, onReject?: (reason: any) => IPromise, onProgress?: Function): Promise; - /** - * The then method from the Promises/A+ specification, with an additional progress handler. - */ - then(onFulfill: (value: T) => IPromise, onReject?: (reason: any) => U, onProgress?: Function): Promise; - /** - * The then method from the Promises/A+ specification, with an additional progress handler. - */ - then(onFulfill: (value: T) => U, onReject?: (reason: any) => IPromise, onProgress?: Function): Promise; - /** - * The then method from the Promises/A+ specification, with an additional progress handler. - */ - then(onFulfill: (value: T) => U, onReject?: (reason: any) => U, onProgress?: Function): Promise; - - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * - * This is especially useful in conjunction with all - */ - spread(onFulfilled: Function, onRejected?: Function): Promise; - - fail(onRejected: (reason: any) => IPromise): Promise; - fail(onRejected: (reason: any) => U): Promise; - /** - * A sugar method, equivalent to promise.then(undefined, onRejected). - */ - catch(onRejected: (reason: any) => U): Promise; - /** - * A sugar method, equivalent to promise.then(undefined, onRejected). - */ - catch(onRejected: (reason: any) => IPromise): Promise; - - /** - * A sugar method, equivalent to promise.then(undefined, undefined, onProgress). - */ - progress(onProgress: (progress: any) => any): Promise; - - /** - * Much like then, but with different behavior around unhandled rejection. If there is an unhandled rejection, either because promise is rejected and no onRejected callback was provided, or because onFulfilled or onRejected threw an error or returned a rejected promise, the resulting rejection reason is thrown as an exception in a future turn of the event loop. - * - * This method should be used to terminate chains of promises that will not be passed elsewhere. Since exceptions thrown in then callbacks are consumed and transformed into rejections, exceptions at the end of the chain are easy to accidentally, silently ignore. By arranging for the exception to be thrown in a future turn of the event loop, so that it won't be caught, it causes an onerror event on the browser window, or an uncaughtException event on Node.js's process object. - * - * Exceptions thrown by done will have long stack traces, if Q.longStackSupport is set to true. If Q.onerror is set, exceptions will be delivered there instead of thrown in a future turn. - * - * The Golden Rule of done vs. then usage is: either return your promise to someone else, or if the chain ends with you, call done to terminate it. - */ - done(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any, onProgress?: (progress: any) => any): void; - - /** - * If callback is a function, assumes it's a Node.js-style callback, and calls it as either callback(rejectionReason) when/if promise becomes rejected, or as callback(null, fulfillmentValue) when/if promise becomes fulfilled. If callback is not a function, simply returns promise. - */ - nodeify(callback: (reason: any, value: any) => void): Promise; - - /** - * Returns a promise to get the named property of an object. Essentially equivalent to - * - * promise.then(function (o) { - * return o[propertyName]; - * }); - */ - get(propertyName: String): Promise; - set(propertyName: String, value: any): Promise; - delete(propertyName: String): Promise; - /** - * Returns a promise for the result of calling the named method of an object with the given array of arguments. The object itself is this in the function, just like a synchronous method call. Essentially equivalent to - * - * promise.then(function (o) { - * return o[methodName].apply(o, args); - * }); - */ - post(methodName: String, args: any[]): Promise; - /** - * Returns a promise for the result of calling the named method of an object with the given variadic arguments. The object itself is this in the function, just like a synchronous method call. - */ - invoke(methodName: String, ...args: any[]): Promise; - fapply(args: any[]): Promise; - fcall(...args: any[]): Promise; - - /** - * Returns a promise for an array of the property names of an object. Essentially equivalent to - * - * promise.then(function (o) { - * return Object.keys(o); - * }); - */ - keys(): Promise; - - /** - * A sugar method, equivalent to promise.then(function () { return value; }). - */ - thenResolve(value: U): Promise; - /** - * A sugar method, equivalent to promise.then(function () { throw reason; }). - */ - thenReject(reason: any): Promise; - timeout(ms: number, message?: string): Promise; - /** - * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. - */ - delay(ms: number): Promise; - - /** - * Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true. - */ - isFulfilled(): boolean; - /** - * Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false. - */ - isRejected(): boolean; - /** - * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. - */ - isPending(): boolean; - - valueOf(): any; - - /** - * Returns a "state snapshot" object, which will be in one of three forms: - * - * - { state: "pending" } - * - { state: "fulfilled", value: } - * - { state: "rejected", reason: } - */ - inspect(): PromiseState; - } - - interface PromiseState { - /** - * "fulfilled", "rejected", "pending" - */ - state: string; - value?: T; - reason?: any; - } - - // If no value provided, returned promise will be of void type - export function when(): Promise; - - // if no fulfill, reject, or progress provided, returned promise will be of same type - export function when(value: IPromise): Promise; - export function when(value: T): Promise; - - // If a non-promise value is provided, it will not reject or progress - export function when(value: T, onFulfilled: (val: T) => IPromise): Promise; - export function when(value: T, onFulfilled: (val: T) => U): Promise; - - export function when(value: IPromise, onFulfilled: (val: T) => IPromise, onRejected?: (reason: any) => IPromise, onProgress?: (progress: any) => any): Promise; - export function when(value: IPromise, onFulfilled: (val: T) => IPromise, onRejected?: (reason: any) => U, onProgress?: (progress: any) => any): Promise; - export function when(value: IPromise, onFulfilled: (val: T) => U, onRejected?: (reason: any) => IPromise, onProgress?: (progress: any) => any): Promise; - export function when(value: IPromise, onFulfilled: (val: T) => U, onRejected?: (reason: any) => U, onProgress?: (progress: any) => any): Promise; - - //export function try(method: Function, ...args: any[]): Promise; // <- This is broken currently - not sure how to fix. - - export function fbind(method: (...args: any[]) => IPromise, ...args: any[]): (...args: any[]) => Promise; - export function fbind(method: (...args: any[]) => T, ...args: any[]): (...args: any[]) => Promise; - - export function fcall(method: (...args: any[]) => T, ...args: any[]): Promise; - - export function send(obj: any, functionName: string, ...args: any[]): Promise; - export function invoke(obj: any, functionName: string, ...args: any[]): Promise; - export function mcall(obj: any, functionName: string, ...args: any[]): Promise; - - export function denodeify(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise; - export function nbind(nodeFunction: Function, thisArg: any, ...args: any[]): (...args: any[]) => Promise; - export function nfbind(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise; - export function nfcall(nodeFunction: Function, ...args: any[]): Promise; - export function nfapply(nodeFunction: Function, args: any[]): Promise; - - export function ninvoke(nodeModule: any, functionName: string, ...args: any[]): Promise; - export function npost(nodeModule: any, functionName: string, args: any[]): Promise; - export function nsend(nodeModule: any, functionName: string, ...args: any[]): Promise; - export function nmcall(nodeModule: any, functionName: string, ...args: any[]): Promise; - - /** - * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. - */ - export function all(promises: IPromise[]): Promise; - /** - * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. - */ - export function all(promises: any[]): Promise; - - /** - * Returns a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled, i.e. become either fulfilled or rejected. - */ - export function allSettled(promises: IPromise[]): Promise[]>; - /** - * Returns a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled, i.e. become either fulfilled or rejected. - */ - export function allSettled(promises: any[]): Promise[]>; - - export function allResolved(promises: IPromise[]): Promise[]>; - export function allResolved(promises: any[]): Promise[]>; - - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * This is especially useful in conjunction with all. - */ - export function spread(promises: any[], onFulfilled: (...args: any[]) => IPromise, onRejected?: (reason: any) => IPromise): Promise; - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * This is especially useful in conjunction with all. - */ - export function spread(promises: any[], onFulfilled: (...args: any[]) => IPromise, onRejected?: (reason: any) => U): Promise; - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * This is especially useful in conjunction with all. - */ - export function spread(promises: any[], onFulfilled: (...args: any[]) => U, onRejected?: (reason: any) => IPromise): Promise; - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * This is especially useful in conjunction with all. - */ - export function spread(promises: any[], onFulfilled: (...args: any[]) => U, onRejected?: (reason: any) => U): Promise; - - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * This is especially useful in conjunction with all. - */ - export function spread(promises: IPromise[], onFulfilled: (...args: T[]) => IPromise, onRejected?: (reason: any) => IPromise): Promise; - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * This is especially useful in conjunction with all. - */ - export function spread(promises: IPromise[], onFulfilled: (...args: T[]) => IPromise, onRejected?: (reason: any) => U): Promise; - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * This is especially useful in conjunction with all. - */ - export function spread(promises: IPromise[], onFulfilled: (...args: T[]) => U, onRejected?: (reason: any) => IPromise): Promise; - /** - * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. - * This is especially useful in conjunction with all. - */ - export function spread(promises: IPromise[], onFulfilled: (...args: T[]) => U, onRejected?: (reason: any) => U): Promise; - - /** - * Returns a promise that will have the same result as promise, except that if promise is not fulfilled or rejected before ms milliseconds, the returned promise will be rejected with an Error with the given message. If message is not supplied, the message will be "Timed out after " + ms + " ms". - */ - export function timeout(promise: Promise, ms: number, message?: string): Promise; - - /** - * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. - */ - export function delay(promise: Promise, ms: number): Promise; - /** - * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. - */ - export function delay(value: T, ms: number): Promise; - /** - * Returns a promise that will be fulfilled with undefined after at least ms milliseconds have passed. - */ - export function delay(ms: number): Promise ; - /** - * Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true. - */ - export function isFulfilled(promise: Promise): boolean; - /** - * Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false. - */ - export function isRejected(promise: Promise): boolean; - /** - * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. - */ - export function isPending(promise: Promise): boolean; - - /** - * Returns a "deferred" object with a: - * promise property - * resolve(value) method - * reject(reason) method - * notify(value) method - * makeNodeResolver() method - */ - export function defer(): Deferred; - - /** - * Returns a promise that is rejected with reason. - */ - export function reject(reason?: any): Promise; - - export function Promise(resolver: (resolve: (val: IPromise) => void , reject: (reason: any) => void , notify: (progress: any) => void ) => void ): Promise; - export function Promise(resolver: (resolve: (val: T) => void , reject: (reason: any) => void , notify: (progress: any) => void ) => void ): Promise; - - /** - * Creates a new version of func that accepts any combination of promise and non-promise values, converting them to their fulfillment values before calling the original func. The returned version also always returns a promise: if func does a return or throw, then Q.promised(func) will return fulfilled or rejected promise, respectively. - * - * This can be useful for creating functions that accept either promises or non-promise values, and for ensuring that the function always returns a promise even in the face of unintentional thrown exceptions. - */ - export function promised(callback: (...args: any[]) => T): (...args: any[]) => Promise; - - /** - * Returns whether the given value is a Q promise. - */ - export function isPromise(object: any): boolean; - /** - * Returns whether the given value is a promise (i.e. it's an object with a then function). - */ - export function isPromiseAlike(object: any): boolean; - /** - * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. - */ - export function isPending(object: any): boolean; - - /** - * This is an experimental tool for converting a generator function into a deferred function. This has the potential of reducing nested callbacks in engines that support yield. - */ - export function async(generatorFunction: any): (...args: any[]) => Promise; - export function nextTick(callback: Function): void; - - /** - * A settable property that will intercept any uncaught errors that would otherwise be thrown in the next tick of the event loop, usually as a result of done. Can be useful for getting the full stack trace of an error in browsers, which is not usually possible with window.onerror. - */ - export var onerror: (reason: any) => void; - /** - * A settable property that lets you turn on long stack trace support. If turned on, "stack jumps" will be tracked across asynchronous promise operations, so that if an uncaught error is thrown by done or a rejection reason's stack property is inspected in a rejection callback, a long stack trace is produced. - */ - export var longStackSupport: boolean; - - /** - * Calling resolve with a pending promise causes promise to wait on the passed promise, becoming fulfilled with its fulfillment value or rejected with its rejection reason (or staying pending forever, if the passed promise does). - * Calling resolve with a rejected promise causes promise to be rejected with the passed promise's rejection reason. - * Calling resolve with a fulfilled promise causes promise to be fulfilled with the passed promise's fulfillment value. - * Calling resolve with a non-promise value causes promise to be fulfilled with that value. - */ - export function resolve(object: IPromise): Promise; - /** - * Calling resolve with a pending promise causes promise to wait on the passed promise, becoming fulfilled with its fulfillment value or rejected with its rejection reason (or staying pending forever, if the passed promise does). - * Calling resolve with a rejected promise causes promise to be rejected with the passed promise's rejection reason. - * Calling resolve with a fulfilled promise causes promise to be fulfilled with the passed promise's fulfillment value. - * Calling resolve with a non-promise value causes promise to be fulfilled with that value. - */ - export function resolve(object: T): Promise; -} - -declare module "q" { - export = Q; -} diff --git a/powershell/make.js b/powershell/make.js index e5d0b1cde..56c4f66c0 100644 --- a/powershell/make.js +++ b/powershell/make.js @@ -66,8 +66,8 @@ target.test = function() { target.build(); util.mkdir('-p', testPath); - util.run(`tsc --outDir "${testPath}" --module commonjs --rootDir Tests Tests/lib/psRunner.ts`); - util.run(`tsc --outDir "${testPath}" --rootDir Tests Tests/L0/_suite.ts`); + util.run(`tsc --outDir "${testPath}" --module commonjs --target es6 --rootDir Tests Tests/lib/psRunner.ts`); + util.run(`tsc --outDir "${testPath}" --module commonjs --target es6 --rootDir Tests Tests/L0/_suite.ts`); util.cp('-r', path.join('Tests', '*'), testPath); util.run('mocha "' + path.join(testPath, 'L0', '_suite.js') + '"'); } diff --git a/powershell/package-lock.json b/powershell/package-lock.json index 01ac6abef..5712393fb 100644 --- a/powershell/package-lock.json +++ b/powershell/package-lock.json @@ -326,12 +326,6 @@ "asap": "~2.0.3" } }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, "qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", diff --git a/powershell/package.json b/powershell/package.json index 6064ef0c8..95b8d4a56 100644 --- a/powershell/package.json +++ b/powershell/package.json @@ -25,7 +25,6 @@ "devDependencies": { "adm-zip": "^0.5.9", "mocha": "5.2.0", - "q": "1.4.1", "shelljs": "^0.8.5", "sync-request": "3.0.1", "typescript": "1.8.7" From bff4def1dd5797efdbde1efb48dc2231ad496206 Mon Sep 17 00:00:00 2001 From: Jamie Magee Date: Tue, 29 Aug 2023 02:39:02 -0700 Subject: [PATCH 258/259] refactor: replace `Q` with native promises (#905) * refactor: replace `Q` with native promises * Changed how promises were deferred to fix failing tests * Bumped version to 5.0.0 * Switched to using promise constructors instead of custom Deferred class * Continue support of Q until the release of v5. Marked the methods that make use of Q as deprecated for now * Added JSDoc deprecation annotation * Updated version and changelog * Updated changelog message * Apply review suggestion Co-authored-by: Jamie Magee * Tests for execAsync --------- Co-authored-by: Aleksandr Levochkin Co-authored-by: Aleksandr Levochkin <107044793+aleksandrlevochkin@users.noreply.github.com> Co-authored-by: Aleksandr Levockin (Akvelon INC) --- node/CHANGELOG.md | 4 + node/mock-task.ts | 13 +- node/mock-toolrunner.ts | 110 +- node/package-lock.json | 10 +- node/package.json | 4 +- node/task.ts | 28 + node/test/toolrunnerTestsWithExecAsync.ts | 1632 +++++++++++++++++++++ node/test/toolrunnertests.ts | 110 +- node/test/tsconfig.json | 1 + node/toolrunner.ts | 315 +++- node/vault.ts | 1 - 11 files changed, 2159 insertions(+), 69 deletions(-) create mode 100644 node/test/toolrunnerTestsWithExecAsync.ts diff --git a/node/CHANGELOG.md b/node/CHANGELOG.md index 6dfd4d457..2e79dd0c6 100644 --- a/node/CHANGELOG.md +++ b/node/CHANGELOG.md @@ -48,3 +48,7 @@ Backported from ver.`3.4.0`: ## 4.4.0 - Add `getBoolFeatureFlag` [#936](https://github.com/microsoft/azure-pipelines-task-lib/pull/936) + +## 4.5.0 + +- Added `execAsync` methods that return native promises. Marked `exec` methods that return promises from the Q library as deprecated [#905](https://github.com/microsoft/azure-pipelines-task-lib/pull/905) diff --git a/node/mock-task.ts b/node/mock-task.ts index 8b2b0d37f..c6a77ee3f 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -1,4 +1,3 @@ - import Q = require('q'); import path = require('path'); import fs = require('fs'); @@ -335,6 +334,18 @@ export function exec(tool: string, args: any, options?: trm.IExecOptions): Q.Pro return tr.exec(options); } +//----------------------------------------------------- +// Exec convenience wrapper +//----------------------------------------------------- +export function execAsync(tool: string, args: any, options?: trm.IExecOptions): Promise { + var toolPath = which(tool, true); + var tr: trm.ToolRunner = this.tool(toolPath); + if (args) { + tr.arg(args); + } + return tr.execAsync(options); +} + export function execSync(tool: string, args: any, options?: trm.IExecSyncOptions): trm.IExecSyncResult { var toolPath = which(tool, true); var tr: trm.ToolRunner = this.tool(toolPath); diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 4c6c9d237..8f5a3380a 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -1,4 +1,3 @@ - import Q = require('q'); import os = require('os'); import events = require('events'); @@ -162,6 +161,113 @@ export class ToolRunner extends events.EventEmitter { // Exec - use for long running tools where you need to stream live output as it runs // returns a promise with return code. // + public execAsync(options?: IExecOptions): Promise { + this._debug('exec tool: ' + this.toolPath); + this._debug('Arguments:'); + this.args.forEach((arg) => { + this._debug(' ' + arg); + }); + + var success = true; + options = options || {}; + + var ops: IExecOptions = { + cwd: options.cwd || process.cwd(), + env: options.env || process.env, + silent: options.silent || false, + outStream: options.outStream || process.stdout, + errStream: options.errStream || process.stderr, + failOnStdErr: options.failOnStdErr || false, + ignoreReturnCode: options.ignoreReturnCode || false, + windowsVerbatimArguments: options.windowsVerbatimArguments + }; + + var argString = this.args.join(' ') || ''; + var cmdString = this.toolPath; + if (argString) { + cmdString += (' ' + argString); + } + + // Using split/join to replace the temp path + cmdString = this.ignoreTempPath(cmdString); + + if (!ops.silent) { + if(this.pipeOutputToTool) { + var pipeToolArgString = this.pipeOutputToTool.args.join(' ') || ''; + var pipeToolCmdString = this.ignoreTempPath(this.pipeOutputToTool.toolPath); + if(pipeToolArgString) { + pipeToolCmdString += (' ' + pipeToolArgString); + } + + cmdString += ' | ' + pipeToolCmdString; + } + + ops.outStream.write('[command]' + cmdString + os.EOL); + } + + // TODO: filter process.env + var res = mock.getResponse('exec', cmdString, debug); + if (res.stdout) { + this.emit('stdout', res.stdout); + if (!ops.silent) { + ops.outStream.write(res.stdout + os.EOL); + } + const stdLineArray = res.stdout.split(os.EOL); + for (const line of stdLineArray.slice(0, -1)) { + this.emit('stdline', line); + } + if(stdLineArray.length > 0 && stdLineArray[stdLineArray.length - 1].length > 0) { + this.emit('stdline', stdLineArray[stdLineArray.length - 1]); + } + } + + if (res.stderr) { + this.emit('stderr', res.stderr); + + success = !ops.failOnStdErr; + if (!ops.silent) { + var s = ops.failOnStdErr ? ops.errStream : ops.outStream; + s.write(res.stderr + os.EOL); + } + const stdErrArray = res.stderr.split(os.EOL); + for (const line of stdErrArray.slice(0, -1)) { + this.emit('errline', line); + } + if (stdErrArray.length > 0 && stdErrArray[stdErrArray.length - 1].length > 0) { + this.emit('errline', stdErrArray[stdErrArray.length - 1]); + } + } + + + var code = res.code; + + if (!ops.silent) { + ops.outStream.write('rc:' + res.code + os.EOL); + } + + if (code != 0 && !ops.ignoreReturnCode) { + success = false; + } + + if (!ops.silent) { + ops.outStream.write('success:' + success + os.EOL); + } + + return new Promise((resolve, reject) => { + if (success) { + resolve(code); + } + else { + reject(new Error(this.toolPath + ' failed with return code: ' + code)); + } + }); + } + + /** + * Exec - use for long running tools where you need to stream live output as it runs + * @deprecated use `execAsync` instead + * @returns a promise with return code. + */ public exec(options?: IExecOptions): Q.Promise { var defer = Q.defer(); @@ -270,8 +376,6 @@ export class ToolRunner extends events.EventEmitter { // but also has limits. For example, no live output and limited to max buffer // public execSync(options?: IExecSyncOptions): IExecSyncResult { - var defer = Q.defer(); - this._debug('exec tool: ' + this.toolPath); this._debug('Arguments:'); this.args.forEach((arg) => { diff --git a/node/package-lock.json b/node/package-lock.json index dbe46d6c4..21cad67f2 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.4.0", + "version": "4.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -49,9 +49,9 @@ "dev": true }, "@types/node": { - "version": "16.11.39", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.39.tgz", - "integrity": "sha512-K0MsdV42vPwm9L6UwhIxMAOmcvH/1OoVkZyCgEtVu4Wx7sElGloy/W7kMBNe/oJ7V/jW9BVt1F6RahH6e7tPXw==" + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" }, "@types/q": { "version": "1.5.4", @@ -764,7 +764,7 @@ "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" }, "qs": { "version": "6.11.1", diff --git a/node/package.json b/node/package.json index 31484d447..cf32c9e09 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "azure-pipelines-task-lib", - "version": "4.4.0", + "version": "4.5.0", "description": "Azure Pipelines Task SDK", "main": "./task.js", "typings": "./task.d.ts", @@ -39,7 +39,7 @@ "@types/minimatch": "3.0.3", "@types/mocha": "^9.1.1", "@types/mockery": "^1.4.29", - "@types/node": "^16.11.39", + "@types/node": "^10.17.0", "@types/q": "^1.5.4", "@types/semver": "^7.3.4", "@types/shelljs": "^0.8.8", diff --git a/node/task.ts b/node/task.ts index 42ac50ccd..a07af639b 100644 --- a/node/task.ts +++ b/node/task.ts @@ -1447,6 +1447,34 @@ export function rmRF(inputPath: string): void { * @param options optional exec options. See IExecOptions * @returns number */ +export function execAsync(tool: string, args: any, options?: trm.IExecOptions): Promise { + let tr: trm.ToolRunner = this.tool(tool); + tr.on('debug', (data: string) => { + debug(data); + }); + + if (args) { + if (args instanceof Array) { + tr.arg(args); + } + else if (typeof (args) === 'string') { + tr.line(args) + } + } + return tr.execAsync(options); +} + +/** + * Exec a tool. Convenience wrapper over ToolRunner to exec with args in one call. + * Output will be streamed to the live console. + * Returns promise with return code + * + * @deprecated Use the {@link execAsync} method that returns a native Javascript Promise instead + * @param tool path to tool to exec + * @param args an arg string or array of args + * @param options optional exec options. See IExecOptions + * @returns number + */ export function exec(tool: string, args: any, options?: trm.IExecOptions): Q.Promise { let tr: trm.ToolRunner = this.tool(tool); tr.on('debug', (data: string) => { diff --git a/node/test/toolrunnerTestsWithExecAsync.ts b/node/test/toolrunnerTestsWithExecAsync.ts new file mode 100644 index 000000000..1b532e092 --- /dev/null +++ b/node/test/toolrunnerTestsWithExecAsync.ts @@ -0,0 +1,1632 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +import assert = require('assert'); +import child_process = require('child_process'); +import fs = require('fs'); +import path = require('path'); +import os = require('os'); +import stream = require('stream'); +import * as tl from '../_build/task'; +import * as trm from '../_build/toolrunner'; + +import testutil = require('./testutil'); + +describe('Toolrunner Tests With ExecAsync', function () { + + before(function (done) { + try { + testutil.initialize(); + } + catch (err) { + assert.fail('Failed to load task lib: ' + err.message); + } + done(); + }); + + after(function () { + + }); + + it('Exec convenience with stdout', function (done) { + this.timeout(10000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + if (os.platform() === 'win32') { + tl.execAsync('cmd', '/c echo \'azure-pipelines-task-lib\'', _testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of cmd should be 0'); + done(); + }) + .catch(function (err) { + done(err); + }); + } + else { + tl.execAsync('ls', '-l -a', _testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of ls should be 0'); + done(); + }) + .catch(function (err) { + done(err); + }); + } + }) + it('ToolRunner writes debug', function (done) { + this.timeout(10000); + + var stdStream = testutil.createStringStream(); + tl.setStdStream(stdStream); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + if (os.platform() === 'win32') { + var cmdPath = tl.which('cmd', true); + var cmd = tl.tool(cmdPath); + cmd.arg('/c echo \'azure-pipelines-task-lib\''); + + cmd.execAsync(_testExecOptions) + .then(function (code) { + var contents = stdStream.getContents(); + assert(contents.indexOf('exec tool: ' + cmdPath) >= 0, 'should exec cmd'); + assert.equal(code, 0, 'return code of cmd should be 0'); + done(); + }) + .catch(function (err) { + done(err); + }); + } + else { + var ls = tl.tool(tl.which('ls', true)); + ls.arg('-l'); + ls.arg('-a'); + + ls.execAsync(_testExecOptions) + .then(function (code) { + var contents = stdStream.getContents(); + const usr = os.platform() === 'linux' ? '/usr' : ''; + assert(contents.indexOf(`exec tool: ${usr}/bin/ls`) >= 0, 'should exec ls'); + assert.equal(code, 0, 'return code of ls should be 0'); + done(); + }) + .catch(function (err) { + done(err); + }); + } + }) + it('Execs with stdout', function (done) { + this.timeout(10000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + var output = ''; + if (os.platform() === 'win32') { + var cmd = tl.tool(tl.which('cmd', true)) + .arg('/c') + .arg('echo \'azure-pipelines-task-lib\''); + + cmd.on('stdout', (data) => { + output = data.toString(); + }); + + cmd.execAsync(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of cmd should be 0'); + assert(output && output.length > 0, 'should have emitted stdout'); + done(); + }) + .catch(function (err) { + done(err); + }); + } + else { + var ls = tl.tool(tl.which('ls', true)); + ls.arg('-l'); + ls.arg('-a'); + + ls.on('stdout', (data) => { + output = data.toString(); + }); + + ls.execAsync(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of ls should be 0'); + assert(output && output.length > 0, 'should have emitted stdout'); + done(); + }) + .catch(function (err) { + done(err); + }); + } + }) + it('Fails on return code 1 with stderr', function (done) { + this.timeout(10000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + if (os.platform() === 'win32') { + var cmd = tl.tool(tl.which('cmd', true)); + cmd.arg('/c notExist'); + + var output = ''; + cmd.on('stderr', (data) => { + output = data.toString(); + }); + + var succeeded = false; + cmd.execAsync(_testExecOptions) + .then(function (code) { + succeeded = true; + assert.fail('should not have succeeded'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err.message.indexOf('failed with exit code 1') >= 0, `expected error message to indicate "failed with exit code 1". actual error message: "${err}"`); + assert(output && output.length > 0, 'should have emitted stderr'); + done(); + } + }) + .catch(function (err) { + done(err); + }); + } + else { + var bash = tl.tool(tl.which('bash', true)); + bash.arg('--noprofile'); + bash.arg('--norc'); + bash.arg('-c'); + bash.arg('echo hello from STDERR 1>&2 ; exit 123'); + var output = ''; + bash.on('stderr', (data) => { + output = data.toString(); + }); + + var succeeded = false; + bash.execAsync(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('should not have succeeded'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err.message.indexOf('failed with exit code 123') >= 0, `expected error message to indicate "failed with exit code 123". actual error message: "${err}"`); + assert(output && output.length > 0, 'should have emitted stderr'); + done(); + } + }) + .catch(function (err) { + done(err); + }); + } + }) + it('Succeeds on stderr by default', function (done) { + this.timeout(10000); + + var scriptPath = path.join(__dirname, 'scripts', 'stderroutput.js'); + var ls = tl.tool(tl.which('node', true)); + ls.arg(scriptPath); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + ls.execAsync(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'should have succeeded on stderr'); + done(); + }) + .catch(function (err) { + done(new Error('did not succeed on stderr')) + }) + }) + it('Fails on stderr if specified', function (done) { + this.timeout(10000); + + var scriptPath = path.join(__dirname, 'scripts', 'stderroutput.js'); + var node = tl.tool(tl.which('node', true)) + .arg(scriptPath); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: true, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + } + + var output = ''; + node.on('stderr', (data) => { + output = data.toString(); + }); + + var succeeded = false; + node.execAsync(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('should not have succeeded'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err.message.indexOf('one or more lines were written to the STDERR stream') >= 0, `expected error message to indicate "one or more lines were written to the STDERR stream". actual error message: "${err}"`); + assert(output && output.length > 0, 'should have emitted stderr'); + done(); + } + }) + .catch(function (err) { + done(err); + }); + }) + it('Fails when process fails to launch', function (done) { + this.timeout(10000); + + var tool = tl.tool(tl.which('node', true)); + var _testExecOptions = { + cwd: path.join(testutil.getTestTemp(), 'nosuchdir'), + env: {}, + silent: false, + failOnStdErr: true, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + } + + var output = ''; + tool.on('stderr', (data) => { + output = data.toString(); + }); + + var succeeded = false; + tool.execAsync(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('should not have succeeded'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err.message.indexOf('This may indicate the process failed to start') >= 0, `expected error message to indicate "This may indicate the process failed to start". actual error message: "${err}"`); + done(); + } + }) + .catch(function (err) { + done(err); + }); + }) + it('Handles child process holding streams open', function (done) { + this.timeout(10000); + + let semaphorePath = path.join(testutil.getTestTemp(), 'child-process-semaphore.txt'); + fs.writeFileSync(semaphorePath, ''); + + let nodePath = tl.which('node', true); + let scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js'); + let shell: trm.ToolRunner; + if (os.platform() == 'win32') { + shell = tl.tool(tl.which('cmd.exe', true)) + .arg('/D') // Disable execution of AutoRun commands from registry. + .arg('/E:ON') // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. + .arg('/V:OFF') // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. + .arg('/S') // Will cause first and last quote after /C to be stripped. + .arg('/C') + .arg(`"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}""`); + } + else { + shell = tl.tool(tl.which('bash', true)) + .arg('-c') + .arg(`node '${scriptPath}' 'file=${semaphorePath}' &`); + } + + let toolRunnerDebug = []; + shell.on('debug', function (data) { + toolRunnerDebug.push(data); + }); + + process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = "500"; // 0.5 seconds + + let options = { + cwd: __dirname, + env: process.env, + silent: false, + failOnStdErr: true, + ignoreReturnCode: false, + outStream: process.stdout, + errStream: process.stdout, + windowsVerbatimArguments: true + }; + + shell.execAsync(options) + .then(function () { + assert(toolRunnerDebug.filter((x) => x.indexOf('STDIO streams did not close') >= 0).length == 1, 'Did not find expected debug message'); + done(); + }) + .catch(function (err) { + done(err); + }) + .finally(function () { + fs.unlinkSync(semaphorePath); + delete process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY']; + }); + }) + it('Handles child process holding streams open and non-zero exit code', function (done) { + this.timeout(10000); + + let semaphorePath = path.join(testutil.getTestTemp(), 'child-process-semaphore.txt'); + fs.writeFileSync(semaphorePath, ''); + + let nodePath = tl.which('node', true); + let scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js'); + let shell: trm.ToolRunner; + if (os.platform() == 'win32') { + shell = tl.tool(tl.which('cmd.exe', true)) + .arg('/D') // Disable execution of AutoRun commands from registry. + .arg('/E:ON') // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. + .arg('/V:OFF') // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. + .arg('/S') // Will cause first and last quote after /C to be stripped. + .arg('/C') + .arg(`"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}"" & exit /b 123`); + } + else { + shell = tl.tool(tl.which('bash', true)) + .arg('-c') + .arg(`node '${scriptPath}' 'file=${semaphorePath}' & exit 123`); + } + + let toolRunnerDebug = []; + shell.on('debug', function (data) { + toolRunnerDebug.push(data); + }); + + process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = "500"; // 0.5 seconds + + let options = { + cwd: __dirname, + env: process.env, + silent: false, + failOnStdErr: true, + ignoreReturnCode: false, + outStream: process.stdout, + errStream: process.stdout, + windowsVerbatimArguments: true + }; + + shell.execAsync(options) + .then(function () { + done(new Error('should not have been successful')); + done(); + }) + .catch(function (err) { + assert(toolRunnerDebug.filter((x) => x.indexOf('STDIO streams did not close') >= 0).length == 1, 'Did not find expected debug message'); + assert(err.message.indexOf('failed with exit code 123') >= 0); + done(); + }) + .catch(function (err) { + done(err); + }) + .finally(function () { + fs.unlinkSync(semaphorePath); + delete process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY']; + }); + }) + it('Handles child process holding streams open and stderr', function (done) { + this.timeout(10000); + + let semaphorePath = path.join(testutil.getTestTemp(), 'child-process-semaphore.txt'); + fs.writeFileSync(semaphorePath, ''); + + let nodePath = tl.which('node', true); + let scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js'); + let shell: trm.ToolRunner; + if (os.platform() == 'win32') { + shell = tl.tool(tl.which('cmd.exe', true)) + .arg('/D') // Disable execution of AutoRun commands from registry. + .arg('/E:ON') // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. + .arg('/V:OFF') // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. + .arg('/S') // Will cause first and last quote after /C to be stripped. + .arg('/C') + .arg(`"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}"" & echo hi 1>&2`); + } + else { + shell = tl.tool(tl.which('bash', true)) + .arg('-c') + .arg(`node '${scriptPath}' 'file=${semaphorePath}' & echo hi 1>&2`); + } + + let toolRunnerDebug = []; + shell.on('debug', function (data) { + toolRunnerDebug.push(data); + }); + + process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'] = "500"; // 0.5 seconds + + let options = { + cwd: __dirname, + env: process.env, + silent: false, + failOnStdErr: true, + ignoreReturnCode: false, + outStream: process.stdout, + errStream: process.stdout, + windowsVerbatimArguments: true + }; + + shell.execAsync(options) + .then(function () { + done(new Error('should not have been successful')); + done(); + }) + .catch(function (err) { + assert(toolRunnerDebug.filter((x) => x.indexOf('STDIO streams did not close') >= 0).length == 1, 'Did not find expected debug message'); + assert(err.message.indexOf('failed because one or more lines were written to the STDERR stream') >= 0); + done(); + }) + .catch(function (err) { + done(err); + }) + .finally(function () { + fs.unlinkSync(semaphorePath); + delete process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY']; + }); + }) + it('Exec pipe output to another tool, succeeds if both tools succeed', function (done) { + this.timeout(30000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + if (os.platform() === 'win32') { + var matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('line 2'); // match value + var outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe); + + var output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + outputExe.execAsync(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('line 2') >= 0, 'should have emitted stdout ' + output); + done(); + }) + .catch(function (err) { + done(err); + }); + } + else { + var grep = tl.tool(tl.which('grep', true)); + grep.arg('node'); + + var ps = tl.tool(tl.which('ps', true)); + ps.arg('ax'); + ps.pipeExecOutputToTool(grep); + + var output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + ps.execAsync(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('node') >= 0, 'should have emitted stdout ' + output); + done(); + }) + .catch(function (err) { + done(err); + }); + } + }) + it('Exec pipe output to another tool, fails if first tool fails', function (done) { + this.timeout(20000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + if (os.platform() === 'win32') { + var matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('line 2'); // match value + var outputExe = tl.tool(compileOutputExe()) + .arg('1') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe); + + var output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + var succeeded = false; + outputExe.execAsync(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('print-output.exe | findstr "line 2" was a bad command and it did not fail'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err && err.message && err.message.indexOf('print-output.exe') >= 0, 'error from print-output.exe is not reported'); + done(); + } + }) + .catch(function (err) { + done(err); + }); + } + else { + var grep = tl.tool(tl.which('grep', true)); + grep.arg('ssh'); + + var ps = tl.tool(tl.which('ps', true)); + ps.arg('bad'); + ps.pipeExecOutputToTool(grep); + + var output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + var succeeded = false; + ps.execAsync(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('ps bad | grep ssh was a bad command and it did not fail'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + //assert(output && output.length > 0 && output.indexOf('ps: illegal option') >= 0, `error output "ps: illegal option" is expected. actual "${output}"`); + assert(err && err.message && err.message.indexOf('/bin/ps') >= 0, 'error from ps is not reported'); + done(); + } + }) + .catch(function (err) { + done(err); + }) + } + }) + it('Exec pipe output to another tool, fails if second tool fails', function (done) { + this.timeout(20000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + if (os.platform() === 'win32') { + var matchExe = tl.tool(compileMatchExe()) + .arg('1') // exit code + .arg('line 2') // match value + .arg('some error message'); // error + var outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe); + + var output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + var errOut = ''; + outputExe.on('stderr', (data) => { + errOut += data.toString(); + }); + + var succeeded = false; + outputExe.execAsync(_testExecOptions) + .then(function (code) { + succeeded = true; + assert.fail('print-output.exe 0 "line 1" "line 2" "line 3" | match-input.exe 1 "line 2" "some error message" was a bad command and it did not fail'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(errOut && errOut.length > 0 && errOut.indexOf('some error message') >= 0, 'error output from match-input.exe is expected'); + assert(err && err.message && err.message.indexOf('match-input.exe') >= 0, 'error from find does not match expeced. actual: ' + err.message); + done(); + } + }) + .catch(function (err) { + done(err); + }); + } + else { + var grep = tl.tool(tl.which('grep', true)); + grep.arg('--?'); + + var node = tl.tool(tl.which('node', true)) + .arg('-e') + .arg('console.log("line1"); setTimeout(function () { console.log("line2"); }, 200);'); // allow long enough to hook up stdout to stdin + node.pipeExecOutputToTool(grep); + + var output = ''; + node.on('stdout', (data) => { + output += data.toString(); + }); + + var errOut = ''; + node.on('stderr', (data) => { + errOut += data.toString(); + }) + + var succeeded = false; + node.execAsync(_testExecOptions) + .then(function (code) { + succeeded = true; + assert.fail('node [...] | grep --? was a bad command and it did not fail'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(errOut && errOut.length > 0 && errOut.indexOf('grep: unrecognized option') >= 0, 'error output from ps command is expected'); + // grep is /bin/grep on Linux and /usr/bin/grep on OSX + assert(err && err.message && err.message.match(/\/[usr\/]?bin\/grep/), 'error from grep is not reported. actual: ' + err.message); + done(); + } + }) + .catch(function (err) { + done(err); + }); + } + }) + it('Exec pipe output to file and another tool, succeeds if both tools succeed', function (done) { + this.timeout(20000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + const testFile = path.join(testutil.getTestTemp(), 'BothToolsSucceed.log'); + + if (os.platform() === 'win32') { + var matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('line 2'); // match value + var outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe, testFile); + + var output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + outputExe.execAsync(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('line 2') >= 0, 'should have emitted stdout ' + output); + assert(fs.existsSync(testFile), 'Log of first tool output is created when both tools succeed'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('line 2') >= 0, 'Log file of first tool should have stdout from first tool: ' + fileContents); + done(); + }) + .catch(function (err) { + done(err); + }); + } + else { + var grep = tl.tool(tl.which('grep', true)); + grep.arg('node'); + + var ps = tl.tool(tl.which('ps', true)); + ps.arg('ax'); + ps.pipeExecOutputToTool(grep, testFile); + + var output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + ps.execAsync(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('node') >= 0, 'should have emitted stdout ' + output); + assert(fs.existsSync(testFile), 'Log of first tool output is created when both tools succeed'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('PID') >= 0, 'Log of first tool should have stdout from first tool: ' + fileContents); + done(); + }) + .catch(function (err) { + done(err); + }); + } + }) + it('Exec pipe output to file and another tool, fails if first tool fails', function (done) { + this.timeout(20000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + const testFile = path.join(testutil.getTestTemp(), 'FirstToolFails.log'); + + if (os.platform() === 'win32') { + var matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('line 2'); // match value + var outputExe = tl.tool(compileOutputExe()) + .arg('1') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe, testFile); + + var output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + var succeeded = false; + outputExe.execAsync(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('print-output.exe | findstr "line 2" was a bad command and it did not fail'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err && err.message && err.message.indexOf('print-output.exe') >= 0, 'error from print-output.exe is not reported'); + assert(fs.existsSync(testFile), 'Log of first tool output is created when first tool fails'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('line 3') >= 0, 'Error from first tool should be written to log file: ' + fileContents); + done(); + } + }) + .catch(function (err) { + done(err); + }); + } + else { + var grep = tl.tool(tl.which('grep', true)); + grep.arg('ssh'); + + var ps = tl.tool(tl.which('ps', true)); + ps.arg('bad'); + ps.pipeExecOutputToTool(grep, testFile); + + var output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + var succeeded = false; + ps.execAsync(_testExecOptions) + .then(function () { + succeeded = true; + assert.fail('ps bad | grep ssh was a bad command and it did not fail'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(err && err.message && err.message.indexOf('/bin/ps') >= 0, 'error from ps is not reported'); + assert(fs.existsSync(testFile), 'Log of first tool output is created when first tool fails'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('illegal option') >= 0 || fileContents.indexOf('unsupported option') >= 0, + 'error from first tool should be written to log file: ' + fileContents); + done(); + } + }) + .catch(function (err) { + done(err); + }) + } + }) + it('Exec pipe output to file and another tool, fails if second tool fails', function (done) { + this.timeout(20000); + + var _testExecOptions = { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + const testFile = path.join(testutil.getTestTemp(), 'SecondToolFails.log'); + + if (os.platform() === 'win32') { + var matchExe = tl.tool(compileMatchExe()) + .arg('1') // exit code + .arg('line 2') // match value + .arg('some error message'); // error + var outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('line 2') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe, testFile); + + var output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + var errOut = ''; + outputExe.on('stderr', (data) => { + errOut += data.toString(); + }); + + var succeeded = false; + outputExe.execAsync(_testExecOptions) + .then(function (code) { + succeeded = true; + assert.fail('print-output.exe 0 "line 1" "line 2" "line 3" | match-input.exe 1 "line 2" "some error message" was a bad command and it did not fail'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(errOut && errOut.length > 0 && errOut.indexOf('some error message') >= 0, 'error output from match-input.exe is expected'); + assert(err && err.message && err.message.indexOf('match-input.exe') >= 0, 'error from find does not match expeced. actual: ' + err.message); + assert(fs.existsSync(testFile), 'Log of first tool output is created when second tool fails'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('some error message') < 0, 'error from second tool should not be in the log for first tool: ' + fileContents); + done(); + } + }) + .catch(function (err) { + done(err); + }); + } + else { + var grep = tl.tool(tl.which('grep', true)); + grep.arg('--?'); + + var ps = tl.tool(tl.which('ps', true)); + ps.arg('ax'); + ps.pipeExecOutputToTool(grep, testFile); + + var output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + var errOut = ''; + ps.on('stderr', (data) => { + errOut += data.toString(); + }) + + var succeeded = false; + ps.execAsync(_testExecOptions) + .then(function (code) { + succeeded = true; + assert.fail('ps ax | grep --? was a bad command and it did not fail'); + }) + .catch(function (err) { + if (succeeded) { + done(err); + } + else { + assert(errOut && errOut.length > 0 && errOut.indexOf('grep: unrecognized option') >= 0, 'error output from ps command is expected'); + // grep is /bin/grep on Linux and /usr/bin/grep on OSX + assert(err && err.message && err.message.match(/\/[usr\/]?bin\/grep/), 'error from grep is not reported. actual: ' + err.message); + assert(fs.existsSync(testFile), 'Log of first tool output is created when second tool fails'); + const fileContents = fs.readFileSync(testFile); + assert(fileContents.indexOf('unrecognized option') < 0, 'error from second tool should not be in the first tool log file: ' + fileContents); + done(); + } + }) + .catch(function (err) { + done(err); + }); + } + }) + + if (process.platform != 'win32') { + it('exec prints [command] (OSX/Linux)', function (done) { + this.timeout(10000); + let bash = tl.tool(tl.which('bash')) + .arg('--norc') + .arg('--noprofile') + .arg('-c') + .arg('echo hello world'); + let outStream = testutil.createStringStream(); + let options = { outStream: outStream, windowsVerbatimArguments: true }; + let output = ''; + bash.on('stdout', (data) => { + output += data.toString(); + }); + bash.execAsync(options) + .then(function (code) { + assert.equal(code, 0, 'return code should be 0'); + // validate the [command] header + assert.equal( + outStream.getContents().split(os.EOL)[0], + `[command]${tl.which('bash')} --norc --noprofile -c echo hello world`); + // validate stdout + assert.equal( + output.trim(), + 'hello world'); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + } + else { // process.platform == 'win32' + + // -------------------------- + // exec arg tests (Windows) + // -------------------------- + + it('exec .exe AND verbatim args (Windows)', function (done) { + this.timeout(10000); + + // the echo built-in is a good tool for this test + let exePath = process.env.ComSpec; + let exeRunner = tl.tool(exePath) + .arg('/c') + .arg('echo') + .arg('helloworld') + .arg('hello:"world again"'); + let outStream = testutil.createStringStream(); + let options = { outStream: outStream, windowsVerbatimArguments: true }; + let output = ''; + exeRunner.on('stdout', (data) => { + output += data.toString(); + }); + exeRunner.execAsync(options) + .then(function (code) { + assert.equal(code, 0, 'return code should be 0'); + // validate the [command] header + assert.equal( + outStream.getContents().split(os.EOL)[0], + `[command]"${exePath}" /c echo helloworld hello:"world again"`); + // validate stdout + assert.equal( + output.trim(), + 'helloworld hello:"world again"'); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it('exec .exe AND arg quoting (Windows)', function (done) { + this.timeout(10000); + + // the echo built-in is a good tool for this test + let exePath = process.env.ComSpec; + let exeRunner = tl.tool(exePath) + .arg('/c') + .arg('echo') + .arg('helloworld') + .arg('hello world') + .arg('hello:"world again"') + .arg('hello,world'); // "," should not be quoted for .exe (should be for .cmd) + let outStream = testutil.createStringStream(); + let options = { outStream: outStream }; + let output = ''; + exeRunner.on('stdout', (data) => { + output += data.toString(); + }); + exeRunner.execAsync(options) + .then(function (code) { + assert.equal(code, 0, 'return code should be 0'); + // validate the [command] header + assert.equal( + outStream.getContents().split(os.EOL)[0], + '[command]' + exePath + ' /c echo' + + ' helloworld' + + ' "hello world"' + + ' "hello:\\"world again\\""' + + ' hello,world'); + // validate stdout + assert.equal( + output.trim(), + 'helloworld' + + ' "hello world"' + + ' "hello:\\"world again\\""' + + ' hello,world'); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it('exec .exe with space AND verbatim args (Windows)', function (done) { + this.timeout(20000); + + // this test validates the quoting that tool runner adds around the tool path + // when using the windowsVerbatimArguments option. otherwise the target process + // interprets the args as starting after the first space in the tool path. + let exePath = compileArgsExe('print args exe with spaces.exe'); + let exeRunner = tl.tool(exePath) + .arg('myarg1 myarg2'); + let outStream = testutil.createStringStream(); + let options = { outStream: outStream, windowsVerbatimArguments: true }; + let output = ''; + exeRunner.on('stdout', (data) => { + output += data.toString(); + }); + exeRunner.execAsync(options) + .then(function (code) { + assert.equal(code, 0, 'return code should be 0'); + // validate the [command] header + assert.equal( + outStream.getContents().split(os.EOL)[0], + `[command]"${exePath}" myarg1 myarg2`); + // validate stdout + assert.equal( + output.trim(), + "args[0]: 'args'\r\n" + + "args[1]: 'exe'\r\n" + + "args[2]: 'with'\r\n" + + "args[3]: 'spaces.exe'\r\n" + + "args[4]: 'myarg1'\r\n" + + "args[5]: 'myarg2'"); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it('exec .cmd with space AND verbatim args (Windows)', function (done) { + this.timeout(10000); + + // this test validates the quoting that tool runner adds around the script path. + // otherwise cmd.exe will not be able to resolve the path to the script. + let cmdPath = path.join(__dirname, 'scripts', 'print args cmd with spaces.cmd'); + let cmdRunner = tl.tool(cmdPath) + .arg('arg1 arg2') + .arg('arg3'); + let outStream = testutil.createStringStream(); + let options = { outStream: outStream, windowsVerbatimArguments: true }; + let output = ''; + cmdRunner.on('stdout', (data) => { + output += data.toString(); + }); + cmdRunner.execAsync(options) + .then(function (code) { + assert.equal(code, 0, 'return code should be 0'); + // validate the [command] header + assert.equal( + outStream.getContents().split(os.EOL)[0], + `[command]${process.env.ComSpec} /D /S /C ""${cmdPath}" arg1 arg2 arg3"`); + // validate stdout + assert.equal( + output.trim(), + 'args[0]: "arg1"\r\n' + + 'args[1]: "arg2"\r\n' + + 'args[2]: "arg3"'); + done(); + }) + .catch(function (err) { + done(err); + }); + }); + + it('exec .cmd with space AND arg with space (Windows)', function (done) { + this.timeout(10000); + + // this test validates the command is wrapped in quotes (i.e. cmd.exe /S /C ""). + // otherwise the leading quote (around the script with space path) would be stripped + // and cmd.exe would not be able to resolve the script path. + let cmdPath = path.join(__dirname, 'scripts', 'print args cmd with spaces.cmd'); + let cmdRunner = tl.tool(cmdPath) + .arg('my arg 1') + .arg('my arg 2'); + let outStream = testutil.createStringStream(); + let options = { outStream: outStream }; + let output = ''; + cmdRunner.on('stdout', (data) => { + output += data.toString(); + }); + cmdRunner.execAsync(options) + .then(function (code) { + assert.equal(code, 0, 'return code should be 0'); + // validate the [command] header + assert.equal( + outStream.getContents().split(os.EOL)[0], + `[command]${process.env.ComSpec} /D /S /C ""${cmdPath}" "my arg 1" "my arg 2""`); + // validate stdout + assert.equal( + output.trim(), + 'args[0]: "my arg 1"\r\n' + + 'args[1]: "my arg 2"'); + done(); + }) + .catch(function (err) { + done(err); + }) + }); + + it('exec .cmd AND arg quoting (Windows)', function (done) { + this.timeout(10000); + + // this test validates .cmd quoting rules are applied, not the default libuv rules + let cmdPath = path.join(__dirname, 'scripts', 'print args cmd with spaces.cmd'); + let cmdRunner = tl.tool(cmdPath) + .arg('helloworld') + .arg('hello world') + .arg('hello\tworld') + .arg('hello&world') + .arg('hello(world') + .arg('hello)world') + .arg('hello[world') + .arg('hello]world') + .arg('hello{world') + .arg('hello}world') + .arg('hello^world') + .arg('hello=world') + .arg('hello;world') + .arg('hello!world') + .arg('hello\'world') + .arg('hello+world') + .arg('hello,world') + .arg('hello`world') + .arg('hello~world') + .arg('hello|world') + .arg('helloworld') + .arg('hello:"world again"') + .arg('hello world\\'); + let outStream = testutil.createStringStream(); + let options = { outStream: outStream }; + let output = ''; + cmdRunner.on('stdout', (data) => { + output += data.toString(); + }); + cmdRunner.execAsync(options) + .then(function (code) { + assert.equal(code, 0, 'return code should be 0'); + // validate the [command] header + assert.equal( + outStream.getContents().split(os.EOL)[0], + '[command]' + process.env.ComSpec + ' /D /S /C ""' + cmdPath + '"' + + ' helloworld' + + ' "hello world"' + + ' "hello\tworld"' + + ' "hello&world"' + + ' "hello(world"' + + ' "hello)world"' + + ' "hello[world"' + + ' "hello]world"' + + ' "hello{world"' + + ' "hello}world"' + + ' "hello^world"' + + ' "hello=world"' + + ' "hello;world"' + + ' "hello!world"' + + ' "hello\'world"' + + ' "hello+world"' + + ' "hello,world"' + + ' "hello`world"' + + ' "hello~world"' + + ' "hello|world"' + + ' "helloworld"' + + ' "hello:""world again"""' + + ' "hello world\\\\"' + + '"'); + // validate stdout + assert.equal( + output.trim(), + 'args[0]: "helloworld"\r\n' + + 'args[1]: "hello world"\r\n' + + 'args[2]: "hello\tworld"\r\n' + + 'args[3]: "hello&world"\r\n' + + 'args[4]: "hello(world"\r\n' + + 'args[5]: "hello)world"\r\n' + + 'args[6]: "hello[world"\r\n' + + 'args[7]: "hello]world"\r\n' + + 'args[8]: "hello{world"\r\n' + + 'args[9]: "hello}world"\r\n' + + 'args[10]: "hello^world"\r\n' + + 'args[11]: "hello=world"\r\n' + + 'args[12]: "hello;world"\r\n' + + 'args[13]: "hello!world"\r\n' + + 'args[14]: "hello\'world"\r\n' + + 'args[15]: "hello+world"\r\n' + + 'args[16]: "hello,world"\r\n' + + 'args[17]: "hello`world"\r\n' + + 'args[18]: "hello~world"\r\n' + + 'args[19]: "hello|world"\r\n' + + 'args[20]: "hello"\r\n' + + 'args[21]: "hello>world"\r\n' + + 'args[22]: "hello:world again"\r\n' + + 'args[23]: "hello world\\\\"'); + done(); + }) + .catch(function (err) { + done(err); + }) + }); + + // ------------------------------- + // exec pipe arg tests (Windows) + // ------------------------------- + + it('exec pipe .cmd to .exe AND arg quoting (Windows)', function (done) { + this.timeout(10000); + + let cmdPath = path.join(__dirname, 'scripts', 'print args cmd with spaces.cmd'); + let cmdRunner = tl.tool(cmdPath) + .arg('"hello world"'); + + let exePath = path.join(process.env.windir, 'System32', 'find.exe'); + let exeRunner = tl.tool(exePath) + .arg('hello world'); + + let outStream = testutil.createStringStream(); + let options = { outStream: outStream }; + let output = ''; + cmdRunner.on('stdout', (data) => { + output += data.toString(); + }); + cmdRunner.pipeExecOutputToTool(exeRunner); + cmdRunner.execAsync(options) + .then(function (code) { + assert.equal(code, 0, 'return code should be 0'); + // validate the [command] header + assert.equal( + outStream.getContents().split(os.EOL)[0], + '[command]' + process.env.ComSpec + ' /D /S /C ""' + cmdPath + '" """hello world""""' + + ' | ' + exePath + ' "hello world"'); + // validate stdout + assert.equal( + output.trim(), + 'args[0]: "hello world"'); + done(); + }) + .catch(function (err) { + done(err); + }) + }); + + it('exec pipe .cmd to .exe AND verbatim args (Windows)', function (done) { + this.timeout(10000); + + let cmdPath = path.join(__dirname, 'scripts', 'print args cmd with spaces.cmd'); + let cmdRunner = tl.tool(cmdPath) + .arg('hello world'); + + let exePath = path.join(process.env.windir, 'System32', 'find.exe'); + let exeRunner = tl.tool(exePath) + .arg('"world"'); + + let outStream = testutil.createStringStream(); + let options = { outStream: outStream, windowsVerbatimArguments: true }; + let output = ''; + cmdRunner.on('stdout', (data) => { + output += data.toString(); + }); + cmdRunner.pipeExecOutputToTool(exeRunner); + cmdRunner.execAsync(options) + .then(function (code) { + assert.equal(code, 0, 'return code should be 0'); + // validate the [command] header + assert.equal( + outStream.getContents().split(os.EOL)[0], + '[command]' + process.env.ComSpec + ' /D /S /C ""' + cmdPath + '" hello world"' + + ' | "' + exePath + '" "world"'); + // validate stdout + assert.equal( + output.trim(), + 'args[1]: "world"'); + done(); + }) + .catch(function (err) { + done(err); + }) + }); + } + + // function to compile a .NET program on Windows. + let compileExe = (sourceFileName: string, targetFileName: string): string => { + let directory = path.join(testutil.getTestTemp(), sourceFileName); + tl.mkdirP(directory); + let exePath = path.join(directory, targetFileName); + + // short-circuit if already compiled + try { + fs.statSync(exePath); + return exePath; + } + catch (err) { + if (err.code != 'ENOENT') { + throw err; + } + } + + let sourceFile = path.join(__dirname, 'scripts', sourceFileName); + let cscPath = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe'; + fs.statSync(cscPath); + child_process.execFileSync( + cscPath, + [ + '/target:exe', + `/out:${exePath}`, + sourceFile + ]); + return exePath; + } + + describe('Executing inside shell', function () { + + let tempPath: string = testutil.getTestTemp(); + let _testExecOptions: trm.IExecOptions; + + before (function () { + _testExecOptions = { + cwd: __dirname, + env: { + WIN_TEST: 'test value', + TESTPATH: tempPath, + TEST_NODE: 'node', + TEST: 'test value' + }, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + shell: true, + outStream: testutil.getNullStream(), + errStream: testutil.getNullStream() + }; + + }) + + it('Exec inside shell', function (done) { + this.timeout(10000); + + let output: string = ''; + if (os.platform() === 'win32') { + let exePath = compileArgsExe('print args with spaces.exe'); + let exeRunner = tl.tool(exePath); + exeRunner.line('%WIN_TEST%'); + exeRunner.on('stdout', (data) => { + output = data.toString(); + }); + exeRunner.execAsync(_testExecOptions).then(function (code) { + assert.equal(code, 0, 'return code of cmd should be 0'); + assert.equal(output.trim(), 'args[0]: \'test value\'', 'Command should return \"args[0]: \'test value\'\"'); + done(); + }) + .catch(function (err) { + done(err); + }); + } + else { + let statRunner = tl.tool('stat'); + statRunner.line('$TESTPATH'); + statRunner.on('stdout', (data) => { + output = data.toString(); + }); + statRunner.execAsync(_testExecOptions).then(function (code) { + assert.equal(code, 0, 'return code of stat should be 0'); + assert(output.includes(tempPath), `Result should include \'${tempPath}\'`); + done(); + }) + .catch(function (err) { + done(err); + }); + } + }); + it('Exec pipe output to another tool inside shell, succeeds if both tools succeed', function (done) { + this.timeout(30000); + + if (os.platform() === 'win32') { + const matchExe = tl.tool(compileMatchExe()) + .arg('0') // exit code + .arg('test value'); // match value + const outputExe = tl.tool(compileOutputExe()) + .arg('0') // exit code + .arg('line 1') + .arg('"%WIN_TEST%"') + .arg('line 3'); + outputExe.pipeExecOutputToTool(matchExe); + + let output = ''; + outputExe.on('stdout', (data) => { + output += data.toString(); + }); + + outputExe.execAsync(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('test value') >= 0, 'should have emitted stdout ' + output); + done(); + }) + .catch(function (err) { + done(err); + }); + } + else { + const grep = tl.tool(tl.which('grep', true)); + grep.arg('$TEST_NODE'); + + const ps = tl.tool(tl.which('ps', true)); + ps.arg('ax'); + ps.pipeExecOutputToTool(grep); + + let output = ''; + ps.on('stdout', (data) => { + output += data.toString(); + }); + + ps.execAsync(_testExecOptions) + .then(function (code) { + assert.equal(code, 0, 'return code of exec should be 0'); + assert(output && output.length > 0 && output.indexOf('node') >= 0, 'should have emitted stdout ' + output); + done(); + }) + .catch(function (err) { + done(err); + }); + } + }); + it('Should handle arguments with quotes properly', function (done) { + this.timeout(10000); + + let output: string = ''; + if (os.platform() === 'win32') { + let exePath = compileArgsExe('print args with spaces.exe'); + let exeRunner = tl.tool(exePath); + exeRunner.line('-TEST1="space test" "-TEST2=%WIN_TEST%" \'-TEST3=value\''); + exeRunner.on('stdout', (data) => { + output += data.toString(); + }); + exeRunner.execAsync(_testExecOptions).then(function (code) { + assert.equal(code, 0, 'return code of cmd should be 0'); + assert.equal(output.trim(), 'args[0]: \'-TEST1=space test\'\r\n' + + 'args[1]: \'-TEST2=test value\'\r\n' + + 'args[2]: \'\'-TEST3=value\'\''); + done(); + }) + .catch(function (err) { + done(err); + }); + } + else { + let statRunner = tl.tool('echo'); + statRunner.line('-TEST1="$TEST;test" "-TEST2=/one/two/three" \'-TEST3=out:$TEST\''); + statRunner.on('stdout', (data) => { + output = data.toString(); + }); + statRunner.execAsync(_testExecOptions).then(function (code) { + assert.equal(code, 0, 'return code of stat should be 0'); + assert.equal(output, '-TEST1=test value;test -TEST2=/one/two/three -TEST3=out:$TEST\n'); + done(); + }) + .catch(function (err) { + done(err); + }); + } + }); + }) + + // function to compile a .NET program that prints the command line args. + // the helper program is used to validate that command line args are passed correctly. + let compileArgsExe = (targetFileName: string): string => { + return compileExe('print-args-exe.cs', targetFileName); + } + + // function to compile a .NET program that matches input lines. + // the helper program is used on Windows to validate piping output between tools. + let compileMatchExe = (): string => { + return compileExe('match-input-exe.cs', 'match-input.exe'); + } + + // function to compile a .NET program that prints lines. + // the helper program is used on Windows to validate piping output between tools. + let compileOutputExe = (): string => { + return compileExe('print-output-exe.cs', 'print-output.exe'); + } +}); diff --git a/node/test/toolrunnertests.ts b/node/test/toolrunnertests.ts index f534a2787..ac0e0eeb1 100644 --- a/node/test/toolrunnertests.ts +++ b/node/test/toolrunnertests.ts @@ -135,7 +135,7 @@ describe('Toolrunner Tests', function () { assert.equal(code, 0, 'return code of cmd should be 0'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -145,7 +145,7 @@ describe('Toolrunner Tests', function () { assert.equal(code, 0, 'return code of ls should be 0'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -178,7 +178,7 @@ describe('Toolrunner Tests', function () { assert.equal(code, 0, 'return code of cmd should be 0'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -195,7 +195,7 @@ describe('Toolrunner Tests', function () { assert.equal(code, 0, 'return code of ls should be 0'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -229,7 +229,7 @@ describe('Toolrunner Tests', function () { assert(output && output.length > 0, 'should have emitted stdout'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -248,7 +248,7 @@ describe('Toolrunner Tests', function () { assert(output && output.length > 0, 'should have emitted stdout'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -281,7 +281,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('should not have succeeded'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -291,7 +291,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -312,7 +312,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('should not have succeeded'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -322,7 +322,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -348,7 +348,7 @@ describe('Toolrunner Tests', function () { assert.equal(code, 0, 'should have succeeded on stderr'); done(); }) - .fail(function (err) { + .catch(function (err) { done(new Error('did not succeed on stderr')) }) }) @@ -380,7 +380,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('should not have succeeded'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -390,7 +390,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); }) @@ -419,7 +419,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('should not have succeeded'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -428,7 +428,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); }) @@ -479,7 +479,7 @@ describe('Toolrunner Tests', function () { assert(toolRunnerDebug.filter((x) => x.indexOf('STDIO streams did not close') >= 0).length == 1, 'Did not find expected debug message'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }) .finally(function () { @@ -534,12 +534,12 @@ describe('Toolrunner Tests', function () { done(new Error('should not have been successful')); done(); }) - .fail(function (err) { + .catch(function (err) { assert(toolRunnerDebug.filter((x) => x.indexOf('STDIO streams did not close') >= 0).length == 1, 'Did not find expected debug message'); assert(err.message.indexOf('failed with exit code 123') >= 0); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }) .finally(function () { @@ -594,12 +594,12 @@ describe('Toolrunner Tests', function () { done(new Error('should not have been successful')); done(); }) - .fail(function (err) { + .catch(function (err) { assert(toolRunnerDebug.filter((x) => x.indexOf('STDIO streams did not close') >= 0).length == 1, 'Did not find expected debug message'); assert(err.message.indexOf('failed because one or more lines were written to the STDERR stream') >= 0); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }) .finally(function () { @@ -642,7 +642,7 @@ describe('Toolrunner Tests', function () { assert(output && output.length > 0 && output.indexOf('line 2') >= 0, 'should have emitted stdout ' + output); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -665,7 +665,7 @@ describe('Toolrunner Tests', function () { assert(output && output.length > 0 && output.indexOf('node') >= 0, 'should have emitted stdout ' + output); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -705,7 +705,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('print-output.exe | findstr "line 2" was a bad command and it did not fail'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -714,7 +714,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -737,7 +737,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('ps bad | grep ssh was a bad command and it did not fail'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -747,7 +747,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }) } @@ -793,7 +793,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('print-output.exe 0 "line 1" "line 2" "line 3" | match-input.exe 1 "line 2" "some error message" was a bad command and it did not fail'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -803,7 +803,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -832,7 +832,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('node [...] | grep --? was a bad command and it did not fail'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -843,7 +843,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -888,7 +888,7 @@ describe('Toolrunner Tests', function () { assert(fileContents.indexOf('line 2') >= 0, 'Log file of first tool should have stdout from first tool: ' + fileContents); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -914,7 +914,7 @@ describe('Toolrunner Tests', function () { assert(fileContents.indexOf('PID') >= 0, 'Log of first tool should have stdout from first tool: ' + fileContents); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -956,7 +956,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('print-output.exe | findstr "line 2" was a bad command and it did not fail'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -968,7 +968,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -991,7 +991,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('ps bad | grep ssh was a bad command and it did not fail'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -1004,7 +1004,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }) } @@ -1052,7 +1052,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('print-output.exe 0 "line 1" "line 2" "line 3" | match-input.exe 1 "line 2" "some error message" was a bad command and it did not fail'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -1065,7 +1065,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -1093,7 +1093,7 @@ describe('Toolrunner Tests', function () { succeeded = true; assert.fail('ps ax | grep --? was a bad command and it did not fail'); }) - .fail(function (err) { + .catch(function (err) { if (succeeded) { done(err); } @@ -1107,7 +1107,7 @@ describe('Toolrunner Tests', function () { done(); } }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -1266,7 +1266,7 @@ describe('Toolrunner Tests', function () { 'hello world'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); }); @@ -1306,7 +1306,7 @@ describe('Toolrunner Tests', function () { 'helloworld hello:"world again"'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); }); @@ -1349,7 +1349,7 @@ describe('Toolrunner Tests', function () { + ' hello,world'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); }); @@ -1387,7 +1387,7 @@ describe('Toolrunner Tests', function () { + "args[5]: 'myarg2'"); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); }); @@ -1422,7 +1422,7 @@ describe('Toolrunner Tests', function () { + 'args[2]: "arg3"'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); }); @@ -1457,7 +1457,7 @@ describe('Toolrunner Tests', function () { + 'args[1]: "my arg 2"'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }) }); @@ -1559,7 +1559,7 @@ describe('Toolrunner Tests', function () { + 'args[23]: "hello world\\\\"'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }) }); @@ -1833,7 +1833,7 @@ describe('Toolrunner Tests', function () { 'args[0]: "hello world"'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }) }); @@ -1870,7 +1870,7 @@ describe('Toolrunner Tests', function () { 'args[1]: "world"'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }) }); @@ -2136,7 +2136,7 @@ describe('Toolrunner Tests', function () { assert.equal(output.trim(), 'args[0]: \'test value\'', 'Command should return \"args[0]: \'test value\'\"'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -2151,7 +2151,7 @@ describe('Toolrunner Tests', function () { assert(output.includes(tempPath), `Result should include \'${tempPath}\'`); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -2181,7 +2181,7 @@ describe('Toolrunner Tests', function () { assert(output && output.length > 0 && output.indexOf('test value') >= 0, 'should have emitted stdout ' + output); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -2204,7 +2204,7 @@ describe('Toolrunner Tests', function () { assert(output && output.length > 0 && output.indexOf('node') >= 0, 'should have emitted stdout ' + output); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -2227,7 +2227,7 @@ describe('Toolrunner Tests', function () { + 'args[2]: \'\'-TEST3=value\'\''); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } @@ -2242,7 +2242,7 @@ describe('Toolrunner Tests', function () { assert.equal(output, '-TEST1=test value;test -TEST2=/one/two/three -TEST3=out:$TEST\n'); done(); }) - .fail(function (err) { + .catch(function (err) { done(err); }); } diff --git a/node/test/tsconfig.json b/node/test/tsconfig.json index 98230ecfc..1a820766b 100644 --- a/node/test/tsconfig.json +++ b/node/test/tsconfig.json @@ -16,6 +16,7 @@ "legacyfindfilestests.ts", "vaulttests.ts", "toolrunnertests.ts", + "toolrunnerTestsWithExecAsync.ts", "cctests.ts", "loctests.ts", "matchtests.ts", diff --git a/node/toolrunner.ts b/node/toolrunner.ts index 7fbfb0635..9705e0f45 100644 --- a/node/toolrunner.ts +++ b/node/toolrunner.ts @@ -1,5 +1,3 @@ - - import Q = require('q'); import os = require('os'); import events = require('events'); @@ -602,6 +600,204 @@ export class ToolRunner extends events.EventEmitter { return result; } + private execWithPipingAsync(pipeOutputToTool: ToolRunner, options?: IExecOptions): Promise { + this._debug('exec tool: ' + this.toolPath); + this._debug('arguments:'); + this.args.forEach((arg) => { + this._debug(' ' + arg); + }); + + let success = true; + const optionsNonNull = this._cloneExecOptions(options); + + if (!optionsNonNull.silent) { + optionsNonNull.outStream!.write(this._getCommandString(optionsNonNull) + os.EOL); + } + + let cp: child.ChildProcess; + let toolPath: string = pipeOutputToTool.toolPath; + let toolPathFirst: string; + let successFirst = true; + let returnCodeFirst: number; + let fileStream: fs.WriteStream | null; + let waitingEvents: number = 0; // number of process or stream events we are waiting on to complete + let returnCode: number = 0; + let error: any; + + toolPathFirst = this.toolPath; + + // Following node documentation example from this link on how to pipe output of one process to another + // https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options + + //start the child process for both tools + waitingEvents++; + var cpFirst = child.spawn( + this._getSpawnFileName(optionsNonNull), + this._getSpawnArgs(optionsNonNull), + this._getSpawnOptions(optionsNonNull)); + + waitingEvents ++; + cp = child.spawn( + pipeOutputToTool._getSpawnFileName(optionsNonNull), + pipeOutputToTool._getSpawnArgs(optionsNonNull), + pipeOutputToTool._getSpawnOptions(optionsNonNull)); + + fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null; + + return new Promise((resolve, reject) => { + if (fileStream) { + waitingEvents++; + fileStream.on('finish', () => { + waitingEvents--; //file write is complete + fileStream = null; + if(waitingEvents == 0) { + if (error) { + reject(error); + } else { + resolve(returnCode); + } + } + }); + fileStream.on('error', (err: Error) => { + waitingEvents--; //there were errors writing to the file, write is done + this._debug(`Failed to pipe output of ${toolPathFirst} to file ${this.pipeOutputToFile}. Error = ${err}`); + fileStream = null; + if(waitingEvents == 0) { + if (error) { + reject(error); + } else { + resolve(returnCode); + } + } + }); + } + + //pipe stdout of first tool to stdin of second tool + cpFirst.stdout?.on('data', (data: Buffer) => { + try { + if (fileStream) { + fileStream.write(data); + } + cp.stdin?.write(data); + } catch (err) { + this._debug('Failed to pipe output of ' + toolPathFirst + ' to ' + toolPath); + this._debug(toolPath + ' might have exited due to errors prematurely. Verify the arguments passed are valid.'); + } + }); + cpFirst.stderr?.on('data', (data: Buffer) => { + if (fileStream) { + fileStream.write(data); + } + successFirst = !optionsNonNull.failOnStdErr; + if (!optionsNonNull.silent) { + var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream! : optionsNonNull.outStream!; + s.write(data); + } + }); + cpFirst.on('error', (err: Error) => { + waitingEvents--; //first process is complete with errors + if (fileStream) { + fileStream.end(); + } + cp.stdin?.end(); + error = new Error(toolPathFirst + ' failed. ' + err.message); + if(waitingEvents == 0) { + reject(error); + } + }); + cpFirst.on('close', (code: number, signal: any) => { + waitingEvents--; //first process is complete + if (code != 0 && !optionsNonNull.ignoreReturnCode) { + successFirst = false; + returnCodeFirst = code; + returnCode = returnCodeFirst; + } + this._debug('success of first tool:' + successFirst); + if (fileStream) { + fileStream.end(); + } + cp.stdin?.end(); + if(waitingEvents == 0) { + if (error) { + reject(error); + } else { + resolve(returnCode); + } + } + }); + + var stdbuffer: string = ''; + cp.stdout?.on('data', (data: Buffer) => { + this.emit('stdout', data); + + if (!optionsNonNull.silent) { + optionsNonNull.outStream!.write(data); + } + + this._processLineBuffer(data, stdbuffer, (line: string) => { + this.emit('stdline', line); + }); + }); + + var errbuffer: string = ''; + cp.stderr?.on('data', (data: Buffer) => { + this.emit('stderr', data); + + success = !optionsNonNull.failOnStdErr; + if (!optionsNonNull.silent) { + var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream! : optionsNonNull.outStream!; + s.write(data); + } + + this._processLineBuffer(data, errbuffer, (line: string) => { + this.emit('errline', line); + }); + }); + + cp.on('error', (err: Error) => { + waitingEvents--; //process is done with errors + error = new Error(toolPath + ' failed. ' + err.message); + if(waitingEvents == 0) { + reject(error); + } + }); + + cp.on('close', (code: number, signal: any) => { + waitingEvents--; //process is complete + this._debug('rc:' + code); + returnCode = code; + + if (stdbuffer.length > 0) { + this.emit('stdline', stdbuffer); + } + + if (errbuffer.length > 0) { + this.emit('errline', errbuffer); + } + + if (code != 0 && !optionsNonNull.ignoreReturnCode) { + success = false; + } + + this._debug('success:' + success); + + if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools + error = new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst); + } else if (!success) { + error = new Error(toolPath + ' failed with return code: ' + code); + } + + if(waitingEvents == 0) { + if (error) { + reject(error); + } else { + resolve(returnCode); + } + } + }); + }); + } + private execWithPiping(pipeOutputToTool: ToolRunner, options?: IExecOptions): Q.Promise { var defer = Q.defer(); @@ -881,6 +1077,121 @@ export class ToolRunner extends events.EventEmitter { * @param options optional exec options. See IExecOptions * @returns number */ + public execAsync(options?: IExecOptions): Promise { + if (this.pipeOutputToTool) { + return this.execWithPipingAsync(this.pipeOutputToTool, options); + } + + this._debug('exec tool: ' + this.toolPath); + this._debug('arguments:'); + this.args.forEach((arg) => { + this._debug(' ' + arg); + }); + + const optionsNonNull = this._cloneExecOptions(options); + if (!optionsNonNull.silent) { + optionsNonNull.outStream!.write(this._getCommandString(optionsNonNull) + os.EOL); + } + + let state = new ExecState(optionsNonNull, this.toolPath); + state.on('debug', (message: string) => { + this._debug(message); + }); + + let cp = child.spawn(this._getSpawnFileName(options), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(options)); + this.childProcess = cp; + // it is possible for the child process to end its last line without a new line. + // because stdout is buffered, this causes the last line to not get sent to the parent + // stream. Adding this event forces a flush before the child streams are closed. + cp.stdout?.on('finish', () => { + if (!optionsNonNull.silent) { + optionsNonNull.outStream!.write(os.EOL); + } + }); + + var stdbuffer: string = ''; + cp.stdout?.on('data', (data: Buffer) => { + this.emit('stdout', data); + + if (!optionsNonNull.silent) { + optionsNonNull.outStream!.write(data); + } + + this._processLineBuffer(data, stdbuffer, (line: string) => { + this.emit('stdline', line); + }); + }); + + + var errbuffer: string = ''; + cp.stderr?.on('data', (data: Buffer) => { + state.processStderr = true; + this.emit('stderr', data); + + if (!optionsNonNull.silent) { + var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream! : optionsNonNull.outStream!; + s.write(data); + } + + this._processLineBuffer(data, errbuffer, (line: string) => { + this.emit('errline', line); + }); + }); + + cp.on('error', (err: Error) => { + state.processError = err.message; + state.processExited = true; + state.processClosed = true; + state.CheckComplete(); + }); + + cp.on('exit', (code: number, signal: any) => { + state.processExitCode = code; + state.processExited = true; + this._debug(`Exit code ${code} received from tool '${this.toolPath}'`); + state.CheckComplete() + }); + + cp.on('close', (code: number, signal: any) => { + state.processExitCode = code; + state.processExited = true; + state.processClosed = true; + this._debug(`STDIO streams have closed for tool '${this.toolPath}'`) + state.CheckComplete(); + }); + + return new Promise((resolve, reject) => { + state.on('done', (error: Error, exitCode: number) => { + if (stdbuffer.length > 0) { + this.emit('stdline', stdbuffer); + } + + if (errbuffer.length > 0) { + this.emit('errline', errbuffer); + } + + cp.removeAllListeners(); + + if (error) { + reject(error); + } + else { + resolve(exitCode); + } + }); + }); + } + + /** + * Exec a tool. + * Output will be streamed to the live console. + * Returns promise with return code + * + * @deprecated Use the `execAsync` method that returns a native Javascript promise instead + * @param tool path to tool to exec + * @param options optional exec options. See IExecOptions + * @returns number + */ public exec(options?: IExecOptions): Q.Promise { if (this.pipeOutputToTool) { return this.execWithPiping(this.pipeOutputToTool, options); diff --git a/node/vault.ts b/node/vault.ts index f5c8bdf80..c7fad048e 100644 --- a/node/vault.ts +++ b/node/vault.ts @@ -1,5 +1,4 @@ -import Q = require('q'); import fs = require('fs'); import path = require('path'); import crypto = require('crypto'); From 21cdd395d8a108261ea14ead976960fd003ce65d Mon Sep 17 00:00:00 2001 From: Marcin Date: Tue, 29 Aug 2023 16:04:49 -0700 Subject: [PATCH 259/259] Added typing information. --- node/internal.ts | 4 ++-- node/mock-task.ts | 2 +- node/mock-test.ts | 6 +++--- node/mock-toolrunner.ts | 4 ++-- node/task.ts | 4 ++-- node/taskcommand.ts | 14 +++++++------- node/tsconfig.json | 6 ++++-- node/types/global.d.ts | 20 ++++++++++++++++++++ 8 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 node/types/global.d.ts diff --git a/node/internal.ts b/node/internal.ts index 16d656c29..d017a605f 100644 --- a/node/internal.ts +++ b/node/internal.ts @@ -53,11 +53,11 @@ export function _writeLine(str: string): void { _outStream.write(str + os.EOL); } -export function _setStdStream(stdStream): void { +export function _setStdStream(stdStream: NodeJS.WriteStream): void { _outStream = stdStream; } -export function _setErrStream(errStream): void { +export function _setErrStream(errStream: NodeJS.WriteStream): void { _errStream = errStream; } diff --git a/node/mock-task.ts b/node/mock-task.ts index c6a77ee3f..91d4b4c95 100644 --- a/node/mock-task.ts +++ b/node/mock-task.ts @@ -268,7 +268,7 @@ export function checkPath(p: string, name: string): void { // - inject system.debug info // - have option to switch internal impl (shelljs now) //----------------------------------------------------- -export function mkdirP(p): void { +export function mkdirP(p: string): void { module.exports.debug('creating path: ' + p); } diff --git a/node/mock-test.ts b/node/mock-test.ts index 4d973387e..19d44f52e 100644 --- a/node/mock-test.ts +++ b/node/mock-test.ts @@ -23,7 +23,7 @@ export class MockTestRunner { public nodePath = ''; public stdout = ''; public stderr = ''; - public cmdlines = {}; + public cmdlines: {[key:string]: boolean} = {}; public invokedToolCount = 0; public succeeded = false; public errorIssues: string[] = []; @@ -175,10 +175,10 @@ export class MockTestRunner { return 16; } const taskJsonContents = fs.readFileSync(taskJsonPath, { encoding: 'utf-8' }); - const taskJson: object = JSON.parse(taskJsonContents); + const taskJson: {[key:string]: string} = JSON.parse(taskJsonContents); let nodeVersionFound = false; - const execution: object = ( + const execution = ( taskJson['execution'] || taskJson['prejobexecution'] || taskJson['postjobexecution'] diff --git a/node/mock-toolrunner.ts b/node/mock-toolrunner.ts index 8f5a3380a..f2e622717 100644 --- a/node/mock-toolrunner.ts +++ b/node/mock-toolrunner.ts @@ -30,7 +30,7 @@ export interface IExecSyncResult { error: Error; } -export function debug(message) { +export function debug(message: string) { // do nothing, overridden } @@ -48,7 +48,7 @@ export class ToolRunner extends events.EventEmitter { private args: string[]; private pipeOutputToTool: ToolRunner | undefined; - private _debug(message) { + private _debug(message: string) { debug(message); this.emit('debug', message); } diff --git a/node/task.ts b/node/task.ts index a07af639b..42c833330 100644 --- a/node/task.ts +++ b/node/task.ts @@ -94,7 +94,7 @@ export function setResult(result: TaskResult, message: string, done?: boolean): } // task.complete - var properties = { 'result': TaskResult[result] }; + var properties: {[key:string]: string} = { 'result': TaskResult[result] }; if (done) { properties['done'] = 'true'; } @@ -1967,7 +1967,7 @@ export function getHttpProxyConfiguration(requestUrl?: string): ProxyConfigurati let bypass: boolean = false; if (requestUrl) { - proxyBypassHosts.forEach(bypassHost => { + proxyBypassHosts.forEach( (bypassHost: string) => { if (new RegExp(bypassHost, 'i').test(requestUrl)) { bypass = true; } diff --git a/node/taskcommand.ts b/node/taskcommand.ts index a528068fc..4fe5d67d8 100644 --- a/node/taskcommand.ts +++ b/node/taskcommand.ts @@ -9,7 +9,7 @@ let CMD_PREFIX = '##vso['; export class TaskCommand { - constructor(command, properties, message) { + constructor(command: string, properties: {[key: string]: string}, message: string) { if (!command) { command = 'missing.command'; } @@ -51,7 +51,7 @@ export class TaskCommand { } } -export function commandFromString(commandLine) { +export function commandFromString(commandLine: string) { var preLen = CMD_PREFIX.length; var lbPos = commandLine.indexOf('['); var rbPos = commandLine.indexOf(']'); @@ -62,7 +62,7 @@ export function commandFromString(commandLine) { var spaceIdx = cmdInfo.indexOf(' '); var command = cmdInfo; - var properties = {}; + var properties: {[key: string]: string} = {}; if (spaceIdx > 0) { command = cmdInfo.trim().substring(0, spaceIdx); @@ -90,19 +90,19 @@ export function commandFromString(commandLine) { return cmd; } -function escapedata(s) : string { +function escapedata(s: string) : string { return s.replace(/%/g, '%AZP25') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A'); } -function unescapedata(s) : string { +function unescapedata(s: string) : string { return s.replace(/%0D/g, '\r') .replace(/%0A/g, '\n') .replace(/%AZP25/g, '%'); } -function escape(s) : string { +function escape(s: string) : string { return s.replace(/%/g, '%AZP25') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A') @@ -110,7 +110,7 @@ function escape(s) : string { .replace(/;/g, '%3B'); } -function unescape(s) : string { +function unescape(s: string) : string { return s.replace(/%0D/g, '\r') .replace(/%0A/g, '\n') .replace(/%5D/g, ']') diff --git a/node/tsconfig.json b/node/tsconfig.json index 4631cc0c6..b6a1a95e8 100644 --- a/node/tsconfig.json +++ b/node/tsconfig.json @@ -4,12 +4,14 @@ "module": "commonjs", "declaration": true, "moduleResolution": "node", - "strictNullChecks": true + "strictNullChecks": true, + "noImplicitAny": true, }, "files": [ "index.ts", "mock-task.ts", "mock-run.ts", - "mock-test.ts" + "mock-test.ts", + "types/global.d.ts" ] } \ No newline at end of file diff --git a/node/types/global.d.ts b/node/types/global.d.ts new file mode 100644 index 000000000..88b353176 --- /dev/null +++ b/node/types/global.d.ts @@ -0,0 +1,20 @@ +declare module NodeJS { + interface Global { + _vsts_task_lib_loaded?: boolean; + // proxy related + _vsts_task_lib_proxy_url?: string; + _vsts_task_lib_proxy_username?: string; + _vsts_task_lib_proxy_bypass?: string; + _vsts_task_lib_proxy_password?: string; + _vsts_task_lib_proxy?: boolean; + // cert related + _vsts_task_lib_cert_ca?: string; + _vsts_task_lib_cert_clientcert?: string; + _vsts_task_lib_cert_key?: string; + _vsts_task_lib_cert_archive?: string; + _vsts_task_lib_cert_archiveFile?: string; + _vsts_task_lib_cert_passphrase?: string; + _vsts_task_lib_cert?: boolean; + _vsts_task_lib_skip_cert_validation?: boolean; + } + } \ No newline at end of file