-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bf85e8f
commit 10703df
Showing
17 changed files
with
4,338 additions
and
132 deletions.
There are no files selected for viewing
27 changes: 15 additions & 12 deletions
27
Tasks/powerShell/Strings/resources.resjson/en-US/resources.resjson
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,21 @@ | ||
{ | ||
"loc.friendlyName": "PowerShell", | ||
"loc.helpMarkDown": "[More Information](https://go.microsoft.com/fwlink/?LinkID=613736)", | ||
"loc.description": "Run a PowerShell script", | ||
"loc.description": "Run a PowerShell script on Windows, Mac, or Linux.", | ||
"loc.instanceNameFormat": "PowerShell Script", | ||
"loc.releaseNotes": "Script task consistency.", | ||
"loc.group.displayName.advanced": "Advanced", | ||
"loc.input.label.scriptType": "Type", | ||
"loc.input.help.scriptType": "Type of the script: File Path or Inline Script", | ||
"loc.input.label.scriptName": "Script Path", | ||
"loc.input.help.scriptName": "Path of the script to execute. Should be fully qualified path or relative to the default working directory.", | ||
"loc.input.label.arguments": "Arguments", | ||
"loc.input.help.arguments": "Arguments passed to the PowerShell script. Either ordinal parameters or named parameters", | ||
"loc.input.label.workingFolder": "Working folder", | ||
"loc.input.help.workingFolder": "Current working directory when script is run. Defaults to the folder where the script is located.", | ||
"loc.input.label.inlineScript": "Inline Script", | ||
"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 error pipeline, or if any data is written to the Standard Error stream. Otherwise the task will rely solely on $LASTEXITCODE and the exit code to determine failure." | ||
"loc.input.label.script": "Script", | ||
"loc.input.label.errorActionPreference": "ErrorActionPreference", | ||
"loc.input.help.errorActionPreference": "Prepends the line `$ErrorActionPreference = 'VALUE'` at the top of your script.", | ||
"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 error pipeline, or if any data is written to the Standard Error stream. Otherwise the task will rely on the exit code to determine failure.", | ||
"loc.input.label.ignoreLASTEXITCODE": "Ignore $LASTEXITCODE", | ||
"loc.input.help.ignoreLASTEXITCODE": "If this is false, the line `if ((Test-Path -LiteralPath variable:\\LASTEXITCODE)) { exit $LASTEXITCODE }` is appended to the end of your script. This will cause the last exit code from an external command to be propagated as the exit code of powershell. Otherwise the line is not appended to the end of your script.", | ||
"loc.input.label.workingDirectory": "Working Directory", | ||
"loc.input.help.workingDirectory": "Working directory where the script is run.", | ||
"loc.messages.JS_InvalidErrorActionPreference": "Invalid ErrorActionPreference '%s'. The value must be one of: 'Stop', 'Continue', or 'SilentlyContinue'", | ||
"loc.messages.PS_ExitCode": "powershell exited with code '{0}'.", | ||
"loc.messages.PS_InvalidErrorActionPreference": "Invalid ErrorActionPreference '{0}'. The value must be one of: 'Stop', 'Continue', or 'SilentlyContinue'", | ||
"loc.messages.PS_UnableToDetermineExitCode": "Unexpected exception. Unable to determine the exit code from powershell." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "vsts-powershell-task", | ||
"version": "1.0.0", | ||
"description": "VSTS PowerShell Task", | ||
"main": "powershell.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/Microsoft/vsts-tasks.git" | ||
}, | ||
"author": "Microsoft Corporation", | ||
"license": "MIT", | ||
"dependencies": { | ||
"uuid": "^3.0.1", | ||
"vsts-task-lib": "2.0.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
[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_errorActionPreference = Get-VstsInput -Name 'errorActionPreference' -Default 'Stop' | ||
switch ($input_errorActionPreference.ToUpperInvariant()) { | ||
'STOP' { } | ||
'CONTINUE' { } | ||
'SILENTLYCONTINUE' { } | ||
default { | ||
Write-Error (Get-VstsLocString -Key 'PS_InvalidErrorActionPreference' -ArgumentList $input_errorActionPreference) | ||
} | ||
} | ||
$input_failOnStderr = Get-VstsInput -Name 'failOnStderr' -AsBool | ||
$input_ignoreLASTEXITCODE = Get-VstsInput -Name 'ignoreLASTEXITCODE' -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. | ||
$contents = @() | ||
$contents += "`$ErrorActionPreference = '$input_errorActionPreference'" | ||
$contents += $input_script | ||
if (!$ignoreLASTEXITCODE) { | ||
$contents += 'if (!(Test-Path -LiteralPath variable:\LASTEXITCODE)) {' | ||
$contents += ' Write-Verbose ''Last exit code is not set.''' | ||
$contents += '} else {' | ||
$contents += ' Write-Verbose (''$LASTEXITCODE: {0}'' -f $LASTEXITCODE)' | ||
$contents += ' exit $LASTEXITCODE' | ||
$contents += '}' | ||
} | ||
|
||
# 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()).ps1") | ||
$joinedContents = [System.String]::Join( | ||
([System.Environment]::NewLine), | ||
$contents) | ||
$null = [System.IO.File]::WriteAllText( | ||
$filePath, | ||
$joinedContents, | ||
([System.Text.Encoding]::UTF8)) | ||
|
||
# Prepare the external command values. | ||
$powershellPath = (Get-Command -Name powershell.exe -CommandType Application).Path | ||
Assert-VstsPath -LiteralPath $powershellPath -PathType 'Leaf' | ||
$arguments = "-NoLogo -Sta -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -File `"$filePath`"" | ||
$splat = @{ | ||
'FileName' = $powershellPath | ||
'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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import fs = require('fs'); | ||
import path = require('path'); | ||
import os = require('os'); | ||
import tl = require('vsts-task-lib/task'); | ||
import tr = require('vsts-task-lib/toolrunner'); | ||
var uuidV4 = require('uuid/v4'); | ||
|
||
async function run() { | ||
try { | ||
tl.setResourcePath(path.join(__dirname, 'task.json')); | ||
|
||
// Get inputs. | ||
let errorActionPreference: string = tl.getInput('errorActionPreference', false) || 'Stop'; | ||
switch (errorActionPreference.toUpperCase()) { | ||
case 'STOP': | ||
case 'CONTINUE': | ||
case 'SILENTLYCONTINUE': | ||
break; | ||
default: | ||
throw new Error(tl.loc('JS_InvalidErrorActionPreference', errorActionPreference)); | ||
} | ||
let executionPolicy: string = tl.getInput('executionPolicy', false) || 'Unrestricted'; | ||
let failOnStderr = tl.getBoolInput('failOnStderr', false); | ||
let ignoreExitCode = tl.getBoolInput('ignoreExitCode', false); | ||
let ignoreLASTEXITCODE = tl.getBoolInput('ignoreLASTEXITCODE', false); | ||
let script: string = tl.getInput('script', false) || ''; | ||
let workingDirectory = tl.getPathInput('workingDirectory', /*required*/ true, /*check*/ true); | ||
|
||
// Generate the script contents. | ||
let contents: string[] = []; | ||
contents.push(`$ErrorActionPreference = '${errorActionPreference}'`); | ||
contents.push(script); | ||
if (!ignoreLASTEXITCODE) { | ||
contents.push(`if (!(Test-Path -LiteralPath variable:\LASTEXITCODE)) {`); | ||
contents.push(` Write-Verbose 'Last exit code is not set.'`); | ||
contents.push(`} else {`); | ||
contents.push(` Write-Verbose ('$LASTEXITCODE: {0}' -f $LASTEXITCODE)`); | ||
contents.push(` exit $LASTEXITCODE`); | ||
contents.push(`}`); | ||
} | ||
|
||
// Write the script to disk. | ||
tl.assertAgent('2.115.0'); | ||
let tempDirectory = tl.getVariable('agent.tempDirectory'); | ||
tl.checkPath(tempDirectory, `${tempDirectory} (agent.tempDirectory)`); | ||
let filePath = path.join(tempDirectory, uuidV4() + '.ps1'); | ||
await fs.writeFile( | ||
filePath, | ||
'\ufeff' + contents.join(os.EOL), // Prepend the Unicode BOM character. | ||
{ encoding: 'utf8' }); // Since UTF8 encoding is specified, node will | ||
// // encode the BOM into its UTF8 binary sequence. | ||
|
||
// Run the script. | ||
let powershell = tl.tool(tl.which('powershell', true)) | ||
.arg('-NoLogo') | ||
.arg(`-Sta`) | ||
.arg('-NoProfile') | ||
.arg('-NonInteractive') | ||
.arg('-ExecutionPolicy') | ||
.arg(executionPolicy) | ||
.arg('-File') | ||
.arg(filePath); | ||
let options = <tr.IExecOptions>{ | ||
cwd: workingDirectory, | ||
failOnStdErr: failOnStderr, | ||
ignoreReturnCode: ignoreExitCode | ||
}; | ||
await powershell.exec(options); | ||
} | ||
catch (err) { | ||
tl.setResult(tl.TaskResult.Failed, err.message || 'run() failed'); | ||
} | ||
} | ||
|
||
run(); |
Oops, something went wrong.