Skip to content

Commit 5c92158

Browse files
[WI 2129366][ShellScriptV2] Add explicit error message for OOM error (#19557)
* [US 2129366] Add 137 explicit error message for ShellScriptV2 task * Bump task version * Fix L0 exit 1 test --------- Co-authored-by: Kirill Ivlev <[email protected]>
1 parent 66d959d commit 5c92158

File tree

16 files changed

+214
-157
lines changed

16 files changed

+214
-157
lines changed

Tasks/ShellScriptV2/Strings/resources.resjson/en-US/resources.resjson

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
"loc.input.label.failOnStandardError": "Fail on Standard Error",
1616
"loc.input.help.failOnStandardError": "If this is true, this task will fail if any errors are written to the StandardError stream.",
1717
"loc.messages.BashReturnCode": "Bash exited with return code: %d",
18-
"loc.messages.BashFailed": "Bash failed with error: %s"
18+
"loc.messages.BashFailed": "Bash failed with error: %s",
19+
"loc.messages.BashFailedWithCode137": "Bash exited with code 137, which means it ran out of memory. Make sure the agent (container) host has sufficient memory configured."
1920
}

Tasks/ShellScriptV2/Tests/L0.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ describe('ShellScript L0 Suite', function () {
4242
assert(tr.ran('/usr/local/bin/bash /script.sh arg1 arg2'), 'it should have run ShellScript');
4343
assert(tr.invokedToolCount == 1, 'should have only run ShellScript');
4444

45-
var expectedErr = '/usr/local/bin/bash failed with return code: 1';
46-
47-
assert(tr.stdOutContained(expectedErr), 'should have said: ' + expectedErr);
45+
assert(tr.stdOutContained('loc_mock_BashFailed 1'), 'should have said: loc_mock_BashFailed 1');
4846
// failOnStdErr not set
4947
assert(!tr.stderr, 'should not have written to stderr');
5048
assert(tr.failed, 'task should have failed');

Tasks/ShellScriptV2/shellscript.ts

+30-12
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import path = require('path');
22
import tl = require('azure-pipelines-task-lib/task');
33
import trm = require('azure-pipelines-task-lib/toolrunner');
44

5-
async function run() {
6-
try {
5+
async function run() {
6+
try {
77
tl.setResourcePath(path.join( __dirname, 'task.json'));
88

9-
var bash: trm.ToolRunner = tl.tool(tl.which('bash', true));
9+
const bash: trm.ToolRunner = tl.tool(tl.which('bash', true));
1010

11-
var scriptPath: string = tl.getPathInput('scriptPath', true, true);
12-
var cwd: string = tl.getPathInput('cwd', true, false);
11+
const scriptPath: string = tl.getPathInput('scriptPath', true, true);
12+
let cwd: string = tl.getPathInput('cwd', true, false);
1313

1414
// if user didn't supply a cwd (advanced), then set cwd to folder script is in.
1515
// All "script" tasks should do this
@@ -26,15 +26,33 @@ async function run() {
2626

2727
// determines whether output to stderr will fail a task.
2828
// some tools write progress and other warnings to stderr. scripts can also redirect.
29-
var failOnStdErr: boolean = tl.getBoolInput('failOnStandardError', false);
29+
let failOnStdErr: boolean = tl.getBoolInput('failOnStandardError', false);
30+
31+
const options = <trm.IExecOptions>{
32+
failOnStdErr: failOnStdErr,
33+
ignoreReturnCode: true
34+
};
35+
36+
let exitCode: number = await bash.exec(options);
37+
38+
let result = tl.TaskResult.Succeeded;
39+
40+
if (exitCode !== 0)
41+
{
42+
if (exitCode == 137) {
43+
tl.error(tl.loc('BashFailedWithCode137'));
44+
}
45+
else {
46+
tl.error(tl.loc('BashFailed', exitCode));
47+
}
48+
result = tl.TaskResult.Failed;
49+
}
3050

31-
var code: number = await bash.exec(<any>{failOnStdErr: failOnStdErr});
32-
tl.setResult(tl.TaskResult.Succeeded, tl.loc('BashReturnCode', code));
51+
tl.setResult(result, tl.loc('BashReturnCode', exitCode));
52+
}
53+
catch (err: any) {
54+
tl.setResult(tl.TaskResult.Failed, err.message, true);
3355
}
34-
catch(err) {
35-
tl.error(err.message);
36-
tl.setResult(tl.TaskResult.Failed, tl.loc('BashFailed', err.message));
37-
}
3856
}
3957

4058
run();

Tasks/ShellScriptV2/task.json

+90-89
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,95 @@
11
{
2-
"id": "6C731C3C-3C68-459A-A5C9-BDE6E6595B5B",
3-
"name": "ShellScript",
4-
"friendlyName": "Shell script",
5-
"description": "Run a shell script using Bash",
6-
"helpUrl": "https://docs.microsoft.com/azure/devops/pipelines/tasks/utility/shell-script",
7-
"helpMarkDown": "[Learn more about this task](https://go.microsoft.com/fwlink/?LinkID=613738)",
8-
"category": "Utility",
9-
"visibility": [
10-
"Build",
11-
"Release"
12-
],
13-
"runsOn": [
14-
"Agent",
15-
"DeploymentGroup"
16-
],
17-
"author": "Microsoft Corporation",
18-
"version": {
19-
"Major": 2,
20-
"Minor": 231,
21-
"Patch": 0
2+
"id": "6C731C3C-3C68-459A-A5C9-BDE6E6595B5B",
3+
"name": "ShellScript",
4+
"friendlyName": "Shell script",
5+
"description": "Run a shell script using Bash",
6+
"helpUrl": "https://docs.microsoft.com/azure/devops/pipelines/tasks/utility/shell-script",
7+
"helpMarkDown": "[Learn more about this task](https://go.microsoft.com/fwlink/?LinkID=613738)",
8+
"category": "Utility",
9+
"visibility": [
10+
"Build",
11+
"Release"
12+
],
13+
"runsOn": [
14+
"Agent",
15+
"DeploymentGroup"
16+
],
17+
"author": "Microsoft Corporation",
18+
"version": {
19+
"Major": 2,
20+
"Minor": 236,
21+
"Patch": 0
22+
},
23+
"demands": [
24+
"sh"
25+
],
26+
"instanceNameFormat": "Shell Script $(scriptPath)",
27+
"groups": [
28+
{
29+
"name": "advanced",
30+
"displayName": "Advanced",
31+
"isExpanded": false
32+
}
33+
],
34+
"inputs": [
35+
{
36+
"name": "scriptPath",
37+
"type": "filePath",
38+
"label": "Script Path",
39+
"defaultValue": "",
40+
"required": true,
41+
"helpMarkDown": "Relative path from repo root of the shell script file to run."
42+
},
43+
{
44+
"name": "args",
45+
"type": "string",
46+
"label": "Arguments",
47+
"defaultValue": "",
48+
"required": false,
49+
"helpMarkDown": "Arguments passed to the shell script"
50+
},
51+
{
52+
"name": "disableAutoCwd",
53+
"type": "boolean",
54+
"label": "Specify Working Directory",
55+
"defaultValue": "false",
56+
"required": false,
57+
"helpMarkDown": "The default behavior is to set the working directory to the script location. This enables you to optionally specify a different working directory.",
58+
"groupName": "advanced"
2259
},
23-
"demands": [
24-
"sh"
25-
],
26-
"instanceNameFormat": "Shell Script $(scriptPath)",
27-
"groups": [
28-
{
29-
"name": "advanced",
30-
"displayName": "Advanced",
31-
"isExpanded": false
32-
}
33-
],
34-
"inputs": [
35-
{
36-
"name": "scriptPath",
37-
"type": "filePath",
38-
"label": "Script Path",
39-
"defaultValue": "",
40-
"required": true,
41-
"helpMarkDown": "Relative path from repo root of the shell script file to run."
42-
},
43-
{
44-
"name": "args",
45-
"type": "string",
46-
"label": "Arguments",
47-
"defaultValue": "",
48-
"required": false,
49-
"helpMarkDown": "Arguments passed to the shell script"
50-
},
51-
{
52-
"name": "disableAutoCwd",
53-
"type": "boolean",
54-
"label": "Specify Working Directory",
55-
"defaultValue": "false",
56-
"required": false,
57-
"helpMarkDown": "The default behavior is to set the working directory to the script location. This enables you to optionally specify a different working directory.",
58-
"groupName": "advanced"
59-
},
60-
{
61-
"name": "cwd",
62-
"type": "filePath",
63-
"label": "Working Directory",
64-
"defaultValue": "",
65-
"required": false,
66-
"visibleRule": "disableAutoCwd = true",
67-
"helpMarkDown": "Current working directory where the script is run. Empty is the root of the repo (build) or artifacts (release), which is $(System.DefaultWorkingDirectory).",
68-
"groupName": "advanced"
69-
},
70-
{
71-
"name": "failOnStandardError",
72-
"type": "boolean",
73-
"label": "Fail on Standard Error",
74-
"defaultValue": "false",
75-
"required": false,
76-
"helpMarkDown": "If this is true, this task will fail if any errors are written to the StandardError stream.",
77-
"groupName": "advanced"
78-
}
79-
],
80-
"execution": {
81-
"Node10": {
82-
"target": "shellscript.js",
83-
"argumentFormat": ""
84-
},
85-
"Node16": {
86-
"target": "shellscript.js",
87-
"argumentFormat": ""
88-
}
60+
{
61+
"name": "cwd",
62+
"type": "filePath",
63+
"label": "Working Directory",
64+
"defaultValue": "",
65+
"required": false,
66+
"visibleRule": "disableAutoCwd = true",
67+
"helpMarkDown": "Current working directory where the script is run. Empty is the root of the repo (build) or artifacts (release), which is $(System.DefaultWorkingDirectory).",
68+
"groupName": "advanced"
69+
},
70+
{
71+
"name": "failOnStandardError",
72+
"type": "boolean",
73+
"label": "Fail on Standard Error",
74+
"defaultValue": "false",
75+
"required": false,
76+
"helpMarkDown": "If this is true, this task will fail if any errors are written to the StandardError stream.",
77+
"groupName": "advanced"
78+
}
79+
],
80+
"execution": {
81+
"Node10": {
82+
"target": "shellscript.js",
83+
"argumentFormat": ""
8984
},
90-
"messages": {
91-
"BashReturnCode": "Bash exited with return code: %d",
92-
"BashFailed": "Bash failed with error: %s"
85+
"Node16": {
86+
"target": "shellscript.js",
87+
"argumentFormat": ""
9388
}
89+
},
90+
"messages": {
91+
"BashReturnCode": "Bash exited with return code: %d",
92+
"BashFailed": "Bash failed with error: %s",
93+
"BashFailedWithCode137": "Bash exited with code 137, which means it ran out of memory. Make sure the agent (container) host has sufficient memory configured."
94+
}
9495
}

Tasks/ShellScriptV2/task.loc.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"author": "Microsoft Corporation",
1818
"version": {
1919
"Major": 2,
20-
"Minor": 231,
20+
"Minor": 236,
2121
"Patch": 0
2222
},
2323
"demands": [
@@ -89,6 +89,7 @@
8989
},
9090
"messages": {
9191
"BashReturnCode": "ms-resource:loc.messages.BashReturnCode",
92-
"BashFailed": "ms-resource:loc.messages.BashFailed"
92+
"BashFailed": "ms-resource:loc.messages.BashFailed",
93+
"BashFailedWithCode137": "ms-resource:loc.messages.BashFailedWithCode137"
9394
}
9495
}
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Default|2.231.0
2-
Node20-225|2.231.1
1+
Default|2.236.0
2+
Node20-225|2.236.1

_generated/ShellScriptV2/Strings/resources.resjson/en-US/resources.resjson

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
"loc.input.label.failOnStandardError": "Fail on Standard Error",
1616
"loc.input.help.failOnStandardError": "If this is true, this task will fail if any errors are written to the StandardError stream.",
1717
"loc.messages.BashReturnCode": "Bash exited with return code: %d",
18-
"loc.messages.BashFailed": "Bash failed with error: %s"
18+
"loc.messages.BashFailed": "Bash failed with error: %s",
19+
"loc.messages.BashFailedWithCode137": "Bash exited with code 137, which means it ran out of memory. Make sure the agent (container) host has sufficient memory configured."
1920
}

_generated/ShellScriptV2/Tests/L0.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ describe('ShellScript L0 Suite', function () {
4242
assert(tr.ran('/usr/local/bin/bash /script.sh arg1 arg2'), 'it should have run ShellScript');
4343
assert(tr.invokedToolCount == 1, 'should have only run ShellScript');
4444

45-
var expectedErr = '/usr/local/bin/bash failed with return code: 1';
46-
47-
assert(tr.stdOutContained(expectedErr), 'should have said: ' + expectedErr);
45+
assert(tr.stdOutContained('loc_mock_BashFailed 1'), 'should have said: loc_mock_BashFailed 1');
4846
// failOnStdErr not set
4947
assert(!tr.stderr, 'should not have written to stderr');
5048
assert(tr.failed, 'task should have failed');

_generated/ShellScriptV2/shellscript.ts

+30-12
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import path = require('path');
22
import tl = require('azure-pipelines-task-lib/task');
33
import trm = require('azure-pipelines-task-lib/toolrunner');
44

5-
async function run() {
6-
try {
5+
async function run() {
6+
try {
77
tl.setResourcePath(path.join( __dirname, 'task.json'));
88

9-
var bash: trm.ToolRunner = tl.tool(tl.which('bash', true));
9+
const bash: trm.ToolRunner = tl.tool(tl.which('bash', true));
1010

11-
var scriptPath: string = tl.getPathInput('scriptPath', true, true);
12-
var cwd: string = tl.getPathInput('cwd', true, false);
11+
const scriptPath: string = tl.getPathInput('scriptPath', true, true);
12+
let cwd: string = tl.getPathInput('cwd', true, false);
1313

1414
// if user didn't supply a cwd (advanced), then set cwd to folder script is in.
1515
// All "script" tasks should do this
@@ -26,15 +26,33 @@ async function run() {
2626

2727
// determines whether output to stderr will fail a task.
2828
// some tools write progress and other warnings to stderr. scripts can also redirect.
29-
var failOnStdErr: boolean = tl.getBoolInput('failOnStandardError', false);
29+
let failOnStdErr: boolean = tl.getBoolInput('failOnStandardError', false);
30+
31+
const options = <trm.IExecOptions>{
32+
failOnStdErr: failOnStdErr,
33+
ignoreReturnCode: true
34+
};
35+
36+
let exitCode: number = await bash.exec(options);
37+
38+
let result = tl.TaskResult.Succeeded;
39+
40+
if (exitCode !== 0)
41+
{
42+
if (exitCode == 137) {
43+
tl.error(tl.loc('BashFailedWithCode137'));
44+
}
45+
else {
46+
tl.error(tl.loc('BashFailed', exitCode));
47+
}
48+
result = tl.TaskResult.Failed;
49+
}
3050

31-
var code: number = await bash.exec(<any>{failOnStdErr: failOnStdErr});
32-
tl.setResult(tl.TaskResult.Succeeded, tl.loc('BashReturnCode', code));
51+
tl.setResult(result, tl.loc('BashReturnCode', exitCode));
52+
}
53+
catch (err: any) {
54+
tl.setResult(tl.TaskResult.Failed, err.message, true);
3355
}
34-
catch(err) {
35-
tl.error(err.message);
36-
tl.setResult(tl.TaskResult.Failed, tl.loc('BashFailed', err.message));
37-
}
3856
}
3957

4058
run();

_generated/ShellScriptV2/task.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"author": "Microsoft Corporation",
1818
"version": {
1919
"Major": 2,
20-
"Minor": 231,
20+
"Minor": 236,
2121
"Patch": 0
2222
},
2323
"demands": [
@@ -89,10 +89,11 @@
8989
},
9090
"messages": {
9191
"BashReturnCode": "Bash exited with return code: %d",
92-
"BashFailed": "Bash failed with error: %s"
92+
"BashFailed": "Bash failed with error: %s",
93+
"BashFailedWithCode137": "Bash exited with code 137, which means it ran out of memory. Make sure the agent (container) host has sufficient memory configured."
9394
},
9495
"_buildConfigMapping": {
95-
"Default": "2.231.0",
96-
"Node20-225": "2.231.1"
96+
"Default": "2.236.0",
97+
"Node20-225": "2.236.1"
9798
}
9899
}

0 commit comments

Comments
 (0)