diff --git a/Tasks/BashV3/Tests/L0.ts b/Tasks/BashV3/Tests/L0.ts index 9d3a2aa7865c..0c1f3828935b 100644 --- a/Tasks/BashV3/Tests/L0.ts +++ b/Tasks/BashV3/Tests/L0.ts @@ -20,6 +20,7 @@ describe('Bash Suite', function () { it('Runs an inline script correctly', (done: MochaDone) => { this.timeout(5000); + delete process.env['AZP_BASHV3_OLD_SOURCE_BEHAVIOR']; let tp: string = path.join(__dirname, 'L0Inline.js'); let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); @@ -35,6 +36,30 @@ describe('Bash Suite', function () { it('Runs a checked in script correctly', (done: MochaDone) => { this.timeout(5000); + delete process.env['AZP_BASHV3_OLD_SOURCE_BEHAVIOR']; + let tp: string = path.join(__dirname, 'L0External.js'); + let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); + + tr.run(); + + runValidations(() => { + assert(tr.succeeded, 'Bash should have succeeded.'); + assert(tr.stderr.length === 0, 'Bash should not have written to stderr'); + if (process.platform === 'win32') { + // This is different on windows because we change the script name to make sure the normalization call is happening. + assert(tr.stdout.indexOf(`Writing bash 'temp/path/script' to temp/path/fileName.sh`) > 0, 'Bash should have written the script to a file'); + } else { + assert(tr.stdout.indexOf(`Writing bash 'path/to/script' to temp/path/fileName.sh`) > 0, 'Bash should have written the script to a file'); + } + + assert(tr.stdout.indexOf('my script output') > 0,'Bash should have correctly run the script'); + }, tr, done); + }); + + it('Runs a checked in script correctly when using the old behavior', (done: MochaDone) => { + this.timeout(5000); + + process.env['AZP_BASHV3_OLD_SOURCE_BEHAVIOR'] = "true"; let tp: string = path.join(__dirname, 'L0External.js'); let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); @@ -57,6 +82,7 @@ describe('Bash Suite', function () { it('Adds arguments to the script', (done: MochaDone) => { this.timeout(5000); + delete process.env['AZP_BASHV3_OLD_SOURCE_BEHAVIOR']; let tp: string = path.join(__dirname, 'L0Args.js'); let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); @@ -67,9 +93,9 @@ describe('Bash Suite', function () { assert(tr.stderr.length === 0, 'Bash should not have written to stderr'); if (process.platform === 'win32') { // This is different on windows because we change the script name to make sure the normalization call is happening. - assert(tr.stdout.indexOf(`Writing . 'temp/path/script' myCustomArg to temp/path/fileName.sh`) > 0, 'Bash should have written the script to a file'); + assert(tr.stdout.indexOf(`Writing bash 'temp/path/script' myCustomArg to temp/path/fileName.sh`) > 0, 'Bash should have written the script to a file'); } else { - assert(tr.stdout.indexOf(`Writing . 'path/to/script' myCustomArg to temp/path/fileName.sh`) > 0, 'Bash should have written the script to a file'); + assert(tr.stdout.indexOf(`Writing bash 'path/to/script' myCustomArg to temp/path/fileName.sh`) > 0, 'Bash should have written the script to a file'); } assert(tr.stdout.indexOf('my script output') > 0,'Bash should have correctly run the script'); diff --git a/Tasks/BashV3/Tests/L0Args.ts b/Tasks/BashV3/Tests/L0Args.ts index ddc13527eb0e..c9bcd46089b3 100644 --- a/Tasks/BashV3/Tests/L0Args.ts +++ b/Tasks/BashV3/Tests/L0Args.ts @@ -51,7 +51,8 @@ let a: ma.TaskLibAnswers = { 'path/to/script': { isFile() { return true; - } + }, + mode: '777' } } }; diff --git a/Tasks/BashV3/Tests/L0External.ts b/Tasks/BashV3/Tests/L0External.ts index 09eb1128354c..78f6a469ad2e 100644 --- a/Tasks/BashV3/Tests/L0External.ts +++ b/Tasks/BashV3/Tests/L0External.ts @@ -50,7 +50,8 @@ let a: ma.TaskLibAnswers = { 'path/to/script': { isFile() { return true; - } + }, + mode: '777' } } }; diff --git a/Tasks/BashV3/bash.ts b/Tasks/BashV3/bash.ts index 9a4b091c77bf..96a62847409b 100644 --- a/Tasks/BashV3/bash.ts +++ b/Tasks/BashV3/bash.ts @@ -1,6 +1,5 @@ import fs = require('fs'); import path = require('path'); -import os = require('os'); import tl = require('azure-pipelines-task-lib/task'); import tr = require('azure-pipelines-task-lib/toolrunner'); var uuidV4 = require('uuid/v4'); @@ -45,8 +44,10 @@ async function run() { let input_filePath: string; let input_arguments: string; let input_script: string; + let old_source_behavior: boolean; let input_targetType: string = tl.getInput('targetType') || ''; if (input_targetType.toUpperCase() == 'FILEPATH') { + old_source_behavior = !!process.env['AZP_BASHV3_OLD_SOURCE_BEHAVIOR']; input_filePath = tl.getPathInput('filePath', /*required*/ true); if (!tl.stats(input_filePath).isFile()) { throw new Error(tl.loc('JS_InvalidFilePath', input_filePath)); @@ -72,7 +73,26 @@ async function run() { targetFilePath = input_filePath; } - contents = `. '${targetFilePath.replace("'", "'\\''")}' ${input_arguments}`.trim(); + // Choose 1 of 3 behaviors: + // If they've set old_source_behavior, source the script. This is what we used to do and needs to hang around forever for back compat reasons + // If the executable bit is set, execute the script. This is our new desired behavior. + // If the executable bit is not set, source the script and warn. The user should either make it executable or pin to the old behavior. + // See https://github.com/Microsoft/azure-pipelines-tasks/blob/master/docs/bashnote.md + if (old_source_behavior) { + contents = `. '${targetFilePath.replace("'", "'\\''")}' ${input_arguments}`.trim(); + } else { + // Check if executable bit is set + const stats: fs.Stats = tl.stats(input_filePath); + // Check file's executable bit. + if ((stats.mode & 1) > 0) { + contents = `bash '${targetFilePath.replace("'", "'\\''")}' ${input_arguments}`.trim(); + } + else { + tl.debug(`File permissions: ${stats.mode}`); + tl.warning('Executable bit is not set on target script, sourcing instead of executing. More info at https://github.com/Microsoft/azure-pipelines-tasks/blob/master/docs/bashnote.md'); + contents = `. '${targetFilePath.replace("'", "'\\''")}' ${input_arguments}`.trim(); + } + } console.log(tl.loc('JS_FormattedCommand', contents)); } else { @@ -167,4 +187,4 @@ async function run() { } } -run(); +run(); \ No newline at end of file diff --git a/Tasks/BashV3/task.json b/Tasks/BashV3/task.json index 567e3653dbfc..afd56123f12a 100644 --- a/Tasks/BashV3/task.json +++ b/Tasks/BashV3/task.json @@ -18,7 +18,7 @@ "version": { "Major": 3, "Minor": 163, - "Patch": 0 + "Patch": 1 }, "releaseNotes": "Script task consistency. Added support for multiple lines and added support for Windows.", "minimumAgentVersion": "2.115.0", diff --git a/Tasks/BashV3/task.loc.json b/Tasks/BashV3/task.loc.json index 82b6d906c8e1..1e9d73a6775f 100644 --- a/Tasks/BashV3/task.loc.json +++ b/Tasks/BashV3/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 3, "Minor": 163, - "Patch": 0 + "Patch": 1 }, "releaseNotes": "ms-resource:loc.releaseNotes", "minimumAgentVersion": "2.115.0",