diff --git a/Tasks/CmdLine/Strings/resources.resjson/en-US/resources.resjson b/Tasks/CmdLine/Strings/resources.resjson/en-US/resources.resjson
index 9ad4c73ae6eb..117a4a17913f 100644
--- a/Tasks/CmdLine/Strings/resources.resjson/en-US/resources.resjson
+++ b/Tasks/CmdLine/Strings/resources.resjson/en-US/resources.resjson
@@ -1,16 +1,16 @@
{
"loc.friendlyName": "Command Line",
"loc.helpMarkDown": "[More Information](https://go.microsoft.com/fwlink/?LinkID=613735)",
- "loc.description": "Run a command line with arguments",
- "loc.instanceNameFormat": "Run $(filename)",
+ "loc.description": "Run a command line script using cmd.exe on Windows and bash on Mac and Linux.",
+ "loc.instanceNameFormat": "Command Line Script",
+ "loc.releaseNotes": "Script task consistency. Added support for multiple lines.",
"loc.group.displayName.advanced": "Advanced",
- "loc.input.label.filename": "Tool",
- "loc.input.help.filename": "Tool name to run. Tool should be found in your path. Optionally, a fully qualified path can be supplied but that relies on that being present on the agent.
Note: You can use **$(Build.SourcesDirectory)**\\\\ if you want the path relative to repo.",
- "loc.input.label.arguments": "Arguments",
- "loc.input.help.arguments": "Arguments passed to the tool. Use double quotes to escape spaces.",
- "loc.input.label.workingFolder": "Working folder",
- "loc.input.label.failOnStandardError": "Fail on Standard Error",
- "loc.input.help.failOnStandardError": "If this is true, this task will fail if any errors are written to the StandardError stream.",
+ "loc.input.label.script": "Script",
+ "loc.input.label.workingDirectory": "Working Directory",
+ "loc.input.label.failOnStderr": "Fail on Standard Error",
+ "loc.input.help.failOnStderr": "If this is true, this task will fail if any errors are written to the StandardError stream.",
"loc.messages.CmdLineReturnCode": "%s exited with return code: %d",
- "loc.messages.CmdLineFailed": "%s failed with error: %s"
+ "loc.messages.CmdLineFailed": "%s failed with error: %s",
+ "loc.messages.PS_ExitCode": "powershell exited with code '{0}'.",
+ "loc.messages.PS_UnableToDetermineExitCode": "Unexpected exception. Unable to determine the exit code from powershell."
}
\ No newline at end of file
diff --git a/Tasks/CmdLine/cmdline.ps1 b/Tasks/CmdLine/cmdline.ps1
new file mode 100644
index 000000000000..87b15dddb03d
--- /dev/null
+++ b/Tasks/CmdLine/cmdline.ps1
@@ -0,0 +1,130 @@
+[CmdletBinding()]
+param()
+
+Trace-VstsEnteringInvocation $MyInvocation
+try {
+ Import-VstsLocStrings "$PSScriptRoot\task.json"
+
+ # TODO MOVE ASSERT-VSTSAGENT TO THE TASK LIB AND LOC
+ function Assert-VstsAgent {
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory = $true)]
+ [version]$Minimum)
+
+ if ($Minimum -lt ([version]'2.104.1')) {
+ Write-Error "Assert-Agent requires the parameter to be 2.104.1 or higher"
+ return
+ }
+
+ $agent = Get-VstsTaskVariable -Name 'agent.version'
+ if (!$agent -or (([version]$agent) -lt $Minimum)) {
+ Write-Error "Agent version $Minimum or higher is required."
+ }
+ }
+
+ # Get inputs.
+ $input_failOnStderr = Get-VstsInput -Name 'failOnStderr' -AsBool
+ $input_script = Get-VstsInput -Name 'script'
+ $input_workingDirectory = Get-VstsInput -Name 'workingDirectory' -Require
+ Assert-VstsPath -LiteralPath $input_workingDirectory -PathType 'Container'
+
+ # Generate the script contents.
+ #TODO: CONVERT NEWLINES?
+ $contents = "$input_script"
+
+ # Write the script to disk.
+ Assert-VstsAgent -Minimum '2.115.0'
+ $tempDirectory = Get-VstsTaskVariable -Name 'agent.tempDirectory' -Require
+ Assert-VstsPath -LiteralPath $tempDirectory -PathType 'Container'
+ $filePath = [System.IO.Path]::Combine($tempDirectory, "$([System.Guid]::NewGuid()).cmd")
+ $null = [System.IO.File]::WriteAllText(
+ $filePath,
+ $contents.ToString(),
+ ([System.Console]::OutputEncoding))
+
+ # Prepare the external command values.
+ $cmdPath = $env:ComSpec
+ Assert-VstsPath -LiteralPath $cmdPath -PathType Leaf
+ # Command line switches:
+ # /Q Turns echo off.
+ # /D Disable execution of AutoRun commands from registry.
+ # /E:ON Enable command extensions. Note, command extensions are enabled
+ # by default, unless disabled via registry.
+ # /V:OFF Disable delayed environment expansion. Note, delayed environment
+ # expansion is disabled by default, unless enabled via registry.
+ # /S Will cause first and last quote after /C to be stripped.
+ #
+ # TODO: ADD EXPLANATION WHY "CALL" IS REQUIRED
+ $arguments = "/Q /D /E:ON /V:OFF /S /C `"CALL `"$filePath`"`""
+ $splat = @{
+ 'FileName' = $cmdPath
+ 'Arguments' = $arguments
+ 'WorkingDirectory' = $input_workingDirectory
+ }
+
+ # Switch to "Continue".
+ $global:ErrorActionPreference = 'Continue'
+ $failed = $false
+
+ # Run the script.
+ if (!$input_failOnStderr) {
+ Invoke-VstsTool @splat
+ } else {
+ $inError = $false
+ $errorLines = New-Object System.Text.StringBuilder
+ Invoke-VstsTool @splat 2>&1 |
+ ForEach-Object {
+ if ($_ -is [System.Management.Automation.ErrorRecord]) {
+ # Buffer the error lines.
+ $failed = $true
+ $inError = $true
+ $null = $errorLines.AppendLine("$($_.Exception.Message)")
+
+ # Write to verbose to mitigate if the process hangs.
+ Write-Verbose "STDERR: $($_.Exception.Message)"
+ } else {
+ # Flush the error buffer.
+ if ($inError) {
+ $inError = $false
+ $message = $errorLines.ToString().Trim()
+ $null = $errorLines.Clear()
+ if ($message) {
+ Write-VstsTaskError -Message $message
+ }
+ }
+
+ Write-Host "$_"
+ }
+ }
+
+ # Flush the error buffer one last time.
+ if ($inError) {
+ $inError = $false
+ $message = $errorLines.ToString().Trim()
+ $null = $errorLines.Clear()
+ if ($message) {
+ Write-VstsTaskError -Message $message
+ }
+ }
+ }
+
+ # Fail on $LASTEXITCODE
+ if (!(Test-Path -LiteralPath 'variable:\LASTEXITCODE')) {
+ $failed = $true
+ Write-Verbose "Unable to determine exit code"
+ Write-VstsTaskError -Message (Get-VstsLocString -Key 'PS_UnableToDetermineExitCode')
+ } else {
+ if ($LASTEXITCODE -ne 0) {
+ $failed = $true
+ Write-VstsTaskError -Message (Get-VstsLocString -Key 'PS_ExitCode' -ArgumentList $LASTEXITCODE)
+ }
+ }
+
+ # Fail if any errors.
+ if ($failed) {
+ Write-VstsSetResult -Result 'Failed' -Message "Error detected" -DoNotThrow
+ }
+} finally {
+ Trace-VstsLeavingInvocation $MyInvocation
+}
\ No newline at end of file
diff --git a/Tasks/CmdLine/make.json b/Tasks/CmdLine/make.json
new file mode 100644
index 000000000000..8ba5fd228ba1
--- /dev/null
+++ b/Tasks/CmdLine/make.json
@@ -0,0 +1,24 @@
+{
+ "externals": {
+ "nugetv2": [
+ {
+ "name": "VstsTaskSdk",
+ "version": "0.8.1",
+ "repository": "https://www.powershellgallery.com/api/v2/",
+ "cp": [
+ {
+ "source": [
+ "*.ps1",
+ "*.psd1",
+ "*.psm1",
+ "lib.json",
+ "Strings"
+ ],
+ "dest": "ps_modules/VstsTaskSdk/",
+ "options": "-R"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/Tasks/CmdLine/task.json b/Tasks/CmdLine/task.json
index df667d9ce077..6df50ca1e12e 100644
--- a/Tasks/CmdLine/task.json
+++ b/Tasks/CmdLine/task.json
@@ -2,7 +2,7 @@
"id": "D9BAFED4-0B18-4F58-968D-86655B4D2CE9",
"name": "CmdLine",
"friendlyName": "Command Line",
- "description": "Run a command line with arguments",
+ "description": "Run a command line script using cmd.exe on Windows and bash on Mac and Linux.",
"helpMarkDown": "[More Information](https://go.microsoft.com/fwlink/?LinkID=613735)",
"category": "Utility",
"visibility": [
@@ -15,10 +15,12 @@
],
"author": "Microsoft Corporation",
"version": {
- "Major": 1,
- "Minor": 1,
- "Patch": 3
+ "Major": 2,
+ "Minor": 120,
+ "Patch": 0
},
+ "releaseNotes": "Script task consistency. Added support for multiple lines.",
+ "preview": true,
"groups": [
{
"name": "advanced",
@@ -28,31 +30,28 @@
],
"inputs": [
{
- "name": "filename",
- "type": "string",
- "label": "Tool",
- "defaultValue": "",
+ "name": "script",
+ "type": "multiLine",
+ "label": "Script",
"required": true,
- "helpMarkDown": "Tool name to run. Tool should be found in your path. Optionally, a fully qualified path can be supplied but that relies on that being present on the agent.
Note: You can use **$(Build.SourcesDirectory)**\\\\ if you want the path relative to repo."
- },
- {
- "name": "arguments",
- "type": "string",
- "label": "Arguments",
- "defaultValue": "",
- "helpMarkDown": "Arguments passed to the tool. Use double quotes to escape spaces.",
- "required": false
+ "defaultValue": "echo Write your commands here\necho Use the Environment input below to map secret variables into environment variables",
+ "properties": {
+ "resizable": "true",
+ "rows": "10",
+ "maxLength": "5000"
+ },
+ "helpMarkDown": ""
},
{
- "name": "workingFolder",
+ "name": "workingDirectory",
"type": "filePath",
- "label": "Working folder",
+ "label": "Working Directory",
"defaultValue": "",
"required": false,
"groupName": "advanced"
},
{
- "name": "failOnStandardError",
+ "name": "failOnStderr",
"type": "boolean",
"label": "Fail on Standard Error",
"defaultValue": "false",
@@ -61,12 +60,10 @@
"groupName": "advanced"
}
],
- "instanceNameFormat": "Run $(filename)",
+ "instanceNameFormat": "Command Line Script",
"execution": {
- "Process": {
- "target": "$(filename)",
- "argumentFormat": "$(arguments)",
- "workingDirectory": "$(workingFolder)",
+ "PowerShell3": {
+ "target": "cmdline.ps1",
"platforms": [
"windows"
]
@@ -78,6 +75,8 @@
},
"messages": {
"CmdLineReturnCode": "%s exited with return code: %d",
- "CmdLineFailed": "%s failed with error: %s"
+ "CmdLineFailed": "%s failed with error: %s",
+ "PS_ExitCode": "powershell exited with code '{0}'.",
+ "PS_UnableToDetermineExitCode": "Unexpected exception. Unable to determine the exit code from powershell."
}
}
\ No newline at end of file
diff --git a/Tasks/CmdLine/task.loc.json b/Tasks/CmdLine/task.loc.json
index ef83eeb7572c..ba661dc49517 100644
--- a/Tasks/CmdLine/task.loc.json
+++ b/Tasks/CmdLine/task.loc.json
@@ -15,10 +15,12 @@
],
"author": "Microsoft Corporation",
"version": {
- "Major": 1,
- "Minor": 1,
- "Patch": 3
+ "Major": 2,
+ "Minor": 120,
+ "Patch": 0
},
+ "releaseNotes": "ms-resource:loc.releaseNotes",
+ "preview": true,
"groups": [
{
"name": "advanced",
@@ -28,45 +30,40 @@
],
"inputs": [
{
- "name": "filename",
- "type": "string",
- "label": "ms-resource:loc.input.label.filename",
- "defaultValue": "",
+ "name": "script",
+ "type": "multiLine",
+ "label": "ms-resource:loc.input.label.script",
"required": true,
- "helpMarkDown": "ms-resource:loc.input.help.filename"
- },
- {
- "name": "arguments",
- "type": "string",
- "label": "ms-resource:loc.input.label.arguments",
- "defaultValue": "",
- "helpMarkDown": "ms-resource:loc.input.help.arguments",
- "required": false
+ "defaultValue": "echo Write your commands here\necho Use the Environment input below to map secret variables into environment variables",
+ "properties": {
+ "resizable": "true",
+ "rows": "10",
+ "maxLength": "5000"
+ },
+ "helpMarkDown": ""
},
{
- "name": "workingFolder",
+ "name": "workingDirectory",
"type": "filePath",
- "label": "ms-resource:loc.input.label.workingFolder",
+ "label": "ms-resource:loc.input.label.workingDirectory",
"defaultValue": "",
"required": false,
"groupName": "advanced"
},
{
- "name": "failOnStandardError",
+ "name": "failOnStderr",
"type": "boolean",
- "label": "ms-resource:loc.input.label.failOnStandardError",
+ "label": "ms-resource:loc.input.label.failOnStderr",
"defaultValue": "false",
"required": false,
- "helpMarkDown": "ms-resource:loc.input.help.failOnStandardError",
+ "helpMarkDown": "ms-resource:loc.input.help.failOnStderr",
"groupName": "advanced"
}
],
"instanceNameFormat": "ms-resource:loc.instanceNameFormat",
"execution": {
- "Process": {
- "target": "$(filename)",
- "argumentFormat": "$(arguments)",
- "workingDirectory": "$(workingFolder)",
+ "PowerShell3": {
+ "target": "cmdline.ps1",
"platforms": [
"windows"
]
@@ -78,6 +75,8 @@
},
"messages": {
"CmdLineReturnCode": "ms-resource:loc.messages.CmdLineReturnCode",
- "CmdLineFailed": "ms-resource:loc.messages.CmdLineFailed"
+ "CmdLineFailed": "ms-resource:loc.messages.CmdLineFailed",
+ "PS_ExitCode": "ms-resource:loc.messages.PS_ExitCode",
+ "PS_UnableToDetermineExitCode": "ms-resource:loc.messages.PS_UnableToDetermineExitCode"
}
}
\ No newline at end of file
diff --git a/make-options.json b/make-options.json
index 2a757638e83e..20dab2cf8bda 100644
--- a/make-options.json
+++ b/make-options.json
@@ -16,7 +16,6 @@
"Chef",
"ChefKnife",
"CMake",
- "CmdLine",
"CocoaPods",
"CopyFiles",
"CopyFilesOverSSH",