Skip to content

Commit 543d1e7

Browse files
Merge pull request #233 from roshan04/build_stacktace_for_cli
Show stacktrace in case of build level failures
2 parents bc94c60 + 1ec1716 commit 543d1e7

File tree

5 files changed

+89
-16
lines changed

5 files changed

+89
-16
lines changed

bin/commands/runs.js

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ const archiver = require("../helpers/archiver"),
1616
reportGenerator = require('../helpers/reporterHTML').reportGenerator,
1717
{initTimeComponents, instrumentEventTime, markBlockStart, markBlockEnd, getTimeComponents} = require('../helpers/timeComponents'),
1818
downloadBuildArtifacts = require('../helpers/buildArtifacts').downloadBuildArtifacts,
19+
downloadBuildStacktrace = require('../helpers/downloadBuildStacktrace').downloadBuildStacktrace,
1920
updateNotifier = require('update-notifier'),
2021
pkg = require('../../package.json');
22+
const { getStackTraceUrl } = require('../helpers/sync/syncSpecsLogs');
2123

2224
module.exports = function run(args, rawArgs) {
2325
let bsConfigPath = utils.getConfigPath(args.cf);
@@ -181,15 +183,32 @@ module.exports = function run(args, rawArgs) {
181183
await new Promise(resolve => setTimeout(resolve, 5000));
182184

183185
// download build artifacts
184-
if (utils.nonEmptyArray(bsConfig.run_settings.downloads)) {
185-
await downloadBuildArtifacts(bsConfig, data.build_id, args, rawArgs);
186+
if (exitCode != Constants.BUILD_FAILED_EXIT_CODE) {
187+
if (utils.nonEmptyArray(bsConfig.run_settings.downloads)) {
188+
await downloadBuildArtifacts(bsConfig, data.build_id, args, rawArgs);
189+
}
190+
191+
// Generate custom report!
192+
reportGenerator(bsConfig, data.build_id, args, rawArgs, function(){
193+
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs);
194+
utils.handleSyncExit(exitCode, data.dashboard_url);
195+
});
196+
} else {
197+
let stacktraceUrl = getStackTraceUrl();
198+
downloadBuildStacktrace(stacktraceUrl).then((message) => {
199+
utils.sendUsageReport(bsConfig, args, message, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs);
200+
}).catch((err) => {
201+
let message = `Downloading build stacktrace failed with statuscode: ${err}. Please visit ${data.dashboard_url} for additional details.`;
202+
logger.error(message);
203+
utils.sendUsageReport(bsConfig, args, message, Constants.messageTypes.ERROR, null, buildReportData, rawArgs);
204+
}).finally(() =>{
205+
let terminalWidth = (process.stdout.columns) * 0.9;
206+
let lineSeparator = "\n" + "-".repeat(terminalWidth);
207+
console.log(lineSeparator)
208+
logger.info(Constants.userMessages.BUILD_FAILED_ERROR)
209+
process.exitCode = Constants.BUILD_FAILED_EXIT_CODE;
210+
});
186211
}
187-
188-
// Generate custom report!
189-
reportGenerator(bsConfig, data.build_id, args, rawArgs, function(){
190-
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs);
191-
utils.handleSyncExit(exitCode, data.dashboard_url);
192-
});
193212
});
194213
} else if (utils.nonEmptyArray(bsConfig.run_settings.downloads)) {
195214
logger.info(Constants.userMessages.ASYNC_DOWNLOADS.replace('<build-id>', data.build_id));

bin/helpers/constants.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
let config = require("./config");
2+
let chalk = require('chalk');
23

34
const syncCLI = {
45
FAILED_SPEC_DETAILS_COL_HEADER: ['Spec', 'Status', 'Browser', 'BrowserStack Session ID'],
@@ -61,6 +62,7 @@ const userMessages = {
6162
DOWNLOAD_BUILD_ARTIFACTS_SUCCESS: "Your build artifact(s) have been successfully downloaded in '<user-path>/build_artifacts/<build-id>' directory",
6263
LATEST_SYNTAX_TO_ACTUAL_VERSION_MESSAGE: "Your build will run using Cypress <actualVersion> as you had specified <latestSyntaxVersion>.<frameworkUpgradeMessage> Read more about supported versions here: http://browserstack.com/docs/automate/cypress/supported-versions",
6364
PROCESS_KILL_MESSAGE: "Stopping the CLI and the execution of the build on BrowserStack",
65+
BUILD_FAILED_ERROR: "The above stacktrace has been thrown by Cypress when we tried to run your build. If your test suite requires npm dependencies then please specify them on browserstack.json. Read more at " + chalk.blueBright("https://www.browserstack.com/docs/automate/cypress/npm-packages") + ". Also, we recommend you to try running the build locally using ‘cypress run’ and if it works fine then please reach out to support at " + chalk.blueBright("https://www.browserstack.com/contact#technical-support")
6466
};
6567

6668
const validationMessages = {
@@ -225,6 +227,8 @@ const AUTH_REGEX = /"auth" *: *{[\s\S]*?}/g
225227

226228
const ERROR_EXIT_CODE = 1;
227229

230+
const BUILD_FAILED_EXIT_CODE = 3;
231+
228232
const REDACTED = "[REDACTED]";
229233

230234
const REDACTED_AUTH =`auth: { "username": ${REDACTED}, "access_key": ${REDACTED} }`;
@@ -248,5 +252,6 @@ module.exports = Object.freeze({
248252
LATEST_VERSION_SYNTAX_REGEX,
249253
ERROR_EXIT_CODE,
250254
AUTH_REGEX,
251-
REDACTED_AUTH
255+
REDACTED_AUTH,
256+
BUILD_FAILED_EXIT_CODE
252257
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict'
2+
const request = require('request');
3+
4+
const downloadBuildStacktrace = async (url) => {
5+
return new Promise(async (resolve, reject) => {
6+
request.get(url).on('response', function (response) {
7+
if(response.statusCode == 200) {
8+
response.pipe(process.stdout);
9+
let error = null;
10+
process.stdout.on('error', (err) => {
11+
error = err;
12+
process.stdout.close();
13+
reject(response.statusCode);
14+
});
15+
process.stdout.on('close', async () => {
16+
if(!error) {
17+
resolve("Build stacktrace downloaded successfully");
18+
}
19+
});
20+
} else {
21+
reject(response.statusCode);
22+
}
23+
}).on('end', () => {
24+
resolve("Build stacktrace downloaded successfully");
25+
});
26+
});
27+
};
28+
29+
exports.downloadBuildStacktrace = downloadBuildStacktrace;

bin/helpers/sync/syncSpecsLogs.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ const request = require("request"),
99
tableStream = require('table').createStream,
1010
chalk = require('chalk');
1111

12-
let whileLoop = true, whileTries = config.retries, options, timeout = 3000, n = 2, tableConfig, stream, endTime, startTime = Date.now();
12+
let whileLoop = true, whileTries = config.retries, options, timeout = 3000, n = 2, tableConfig, stream, endTime, startTime = Date.now(), buildStarted = false;
1313
let specSummary = {
14+
"buildError": null,
1415
"specs": [],
1516
"duration": null
1617
}
@@ -106,11 +107,15 @@ let printSpecsStatus = (bsConfig, buildDetails, rawArgs) => {
106107
},
107108
function(err, result) { // when loop ends
108109
if (err) {
110+
if(err.status == 204) {
111+
reject(specSummary.exitCode);
112+
} else {
109113
utils.sendUsageReport(bsConfig, {}, `buildId: ${buildDetails.build_id}`, 'error', 'sync_cli_error', err, rawArgs);
114+
}
110115
}
111116
logger.info(lineSeparator);
112117
specSummary.duration = endTime - startTime
113-
resolve(specSummary)
118+
resolve(specSummary);
114119
}
115120
);
116121
});
@@ -145,21 +150,34 @@ let whileProcess = (whilstCallback) => {
145150
whileLoop = false;
146151
endTime = Date.now();
147152
showSpecsStatus(body);
148-
return whilstCallback(null, body);
153+
return specSummary.exitCode == Constants.BUILD_FAILED_EXIT_CODE ?
154+
whilstCallback({ status: 204, message: "No specs ran in the build"} ) : whilstCallback(null, body);
149155
default:
150156
whileLoop = false;
151157
return whilstCallback({ status: response.statusCode, message: body });
152158
}
153159
});
154160
}
155161

162+
let getStackTraceUrl = () => {
163+
return specSummary.buildError
164+
}
165+
156166
let showSpecsStatus = (data) => {
157167
let specData = JSON.parse(data);
158168
specData.forEach(specDetails => {
159169
if (specDetails == "created") {
160-
printInitialLog();
170+
return;
171+
} else if (specDetails["stacktrace_url"]) {
172+
specSummary.exitCode = Constants.BUILD_FAILED_EXIT_CODE;
173+
specSummary.buildError = specDetails["stacktrace_url"]
174+
winstonLogger.error(chalk.red(specDetails["message"]));
161175
} else {
162-
printSpecData(JSON.parse(specDetails))
176+
if(!buildStarted) {
177+
buildStarted = true
178+
printInitialLog();
179+
}
180+
printSpecData(JSON.parse(specDetails));
163181
}
164182
});
165183
}
@@ -208,3 +226,4 @@ let getStatus = (status) => {
208226
}
209227

210228
exports.printSpecsStatus = printSpecsStatus;
229+
exports.getStackTraceUrl = getStackTraceUrl;

test/unit/bin/helpers/sync/syncSpecsLogs.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,14 @@ describe("syncSpecsLogs", () => {
181181
context("showSpecsStatus", () => {
182182
const showSpecsStatus = syncSpecsLogs.__get__("showSpecsStatus");
183183

184-
it('should print initial log for running specs when it is the 1st polling response', () => {
184+
it('should not print initial log for running specs when it is the 1st polling response', () => {
185185
let data = JSON.stringify(["created"])
186186
var printInitialLog = sandbox.stub();
187187
syncSpecsLogs.__set__('printInitialLog', printInitialLog);
188188

189189
showSpecsStatus(data);
190190

191-
expect(printInitialLog.calledOnce).to.be.true;
191+
expect(printInitialLog.calledOnce).to.be.false;
192192
});
193193

194194
it('should print spec details when spec related data is sent in polling response', () => {
@@ -202,6 +202,7 @@ describe("syncSpecsLogs", () => {
202202

203203
it('should print initial and spec details when spec related data is sent in polling response', () => {
204204
let specResult = JSON.stringify({"path": "path"})
205+
syncSpecsLogs.__set__('buildStarted', false)
205206
let data = JSON.stringify(["created", specResult])
206207
var printSpecData = sandbox.stub();
207208
syncSpecsLogs.__set__('printSpecData', printSpecData);

0 commit comments

Comments
 (0)