Skip to content

Show stacktrace in case of build level failures #233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions bin/commands/runs.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ const archiver = require("../helpers/archiver"),
reportGenerator = require('../helpers/reporterHTML').reportGenerator,
{initTimeComponents, instrumentEventTime, markBlockStart, markBlockEnd, getTimeComponents} = require('../helpers/timeComponents'),
downloadBuildArtifacts = require('../helpers/buildArtifacts').downloadBuildArtifacts,
downloadBuildStacktrace = require('../helpers/downloadBuildStacktrace').downloadBuildStacktrace,
updateNotifier = require('update-notifier'),
pkg = require('../../package.json');
const { getStackTraceUrl } = require('../helpers/sync/syncSpecsLogs');

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

// download build artifacts
if (utils.nonEmptyArray(bsConfig.run_settings.downloads)) {
await downloadBuildArtifacts(bsConfig, data.build_id, args, rawArgs);
if (exitCode != Constants.BUILD_FAILED_EXIT_CODE) {
if (utils.nonEmptyArray(bsConfig.run_settings.downloads)) {
await downloadBuildArtifacts(bsConfig, data.build_id, args, rawArgs);
}

// Generate custom report!
reportGenerator(bsConfig, data.build_id, args, rawArgs, function(){
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs);
utils.handleSyncExit(exitCode, data.dashboard_url);
});
} else {
let stacktraceUrl = getStackTraceUrl();
downloadBuildStacktrace(stacktraceUrl).then((message) => {
utils.sendUsageReport(bsConfig, args, message, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs);
}).catch((err) => {
let message = `Downloading build stacktrace failed with statuscode: ${err}. Please visit ${data.dashboard_url} for additional details.`;
logger.error(message);
utils.sendUsageReport(bsConfig, args, message, Constants.messageTypes.ERROR, null, buildReportData, rawArgs);
}).finally(() =>{
let terminalWidth = (process.stdout.columns) * 0.9;
let lineSeparator = "\n" + "-".repeat(terminalWidth);
console.log(lineSeparator)
logger.info(Constants.userMessages.BUILD_FAILED_ERROR)
process.exitCode = Constants.BUILD_FAILED_EXIT_CODE;
});
}

// Generate custom report!
reportGenerator(bsConfig, data.build_id, args, rawArgs, function(){
utils.sendUsageReport(bsConfig, args, `${message}\n${dashboardLink}`, Constants.messageTypes.SUCCESS, null, buildReportData, rawArgs);
utils.handleSyncExit(exitCode, data.dashboard_url);
});
});
} else if (utils.nonEmptyArray(bsConfig.run_settings.downloads)) {
logger.info(Constants.userMessages.ASYNC_DOWNLOADS.replace('<build-id>', data.build_id));
Expand Down
7 changes: 6 additions & 1 deletion bin/helpers/constants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
let config = require("./config");
let chalk = require('chalk');

const syncCLI = {
FAILED_SPEC_DETAILS_COL_HEADER: ['Spec', 'Status', 'Browser', 'BrowserStack Session ID'],
Expand Down Expand Up @@ -61,6 +62,7 @@ const userMessages = {
DOWNLOAD_BUILD_ARTIFACTS_SUCCESS: "Your build artifact(s) have been successfully downloaded in '<user-path>/build_artifacts/<build-id>' directory",
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",
PROCESS_KILL_MESSAGE: "Stopping the CLI and the execution of the build on BrowserStack",
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")
};

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

const ERROR_EXIT_CODE = 1;

const BUILD_FAILED_EXIT_CODE = 3;

const REDACTED = "[REDACTED]";

const REDACTED_AUTH =`auth: { "username": ${REDACTED}, "access_key": ${REDACTED} }`;
Expand All @@ -248,5 +252,6 @@ module.exports = Object.freeze({
LATEST_VERSION_SYNTAX_REGEX,
ERROR_EXIT_CODE,
AUTH_REGEX,
REDACTED_AUTH
REDACTED_AUTH,
BUILD_FAILED_EXIT_CODE
});
29 changes: 29 additions & 0 deletions bin/helpers/downloadBuildStacktrace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict'
const request = require('request');

const downloadBuildStacktrace = async (url) => {
return new Promise(async (resolve, reject) => {
request.get(url).on('response', function (response) {
if(response.statusCode == 200) {
response.pipe(process.stdout);
let error = null;
process.stdout.on('error', (err) => {
error = err;
process.stdout.close();
reject(response.statusCode);
});
process.stdout.on('close', async () => {
if(!error) {
resolve("Build stacktrace downloaded successfully");
}
});
} else {
reject(response.statusCode);
}
}).on('end', () => {
resolve("Build stacktrace downloaded successfully");
});
});
};

exports.downloadBuildStacktrace = downloadBuildStacktrace;
29 changes: 24 additions & 5 deletions bin/helpers/sync/syncSpecsLogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ const request = require("request"),
tableStream = require('table').createStream,
chalk = require('chalk');

let whileLoop = true, whileTries = config.retries, options, timeout = 3000, n = 2, tableConfig, stream, endTime, startTime = Date.now();
let whileLoop = true, whileTries = config.retries, options, timeout = 3000, n = 2, tableConfig, stream, endTime, startTime = Date.now(), buildStarted = false;
let specSummary = {
"buildError": null,
"specs": [],
"duration": null
}
Expand Down Expand Up @@ -106,11 +107,15 @@ let printSpecsStatus = (bsConfig, buildDetails, rawArgs) => {
},
function(err, result) { // when loop ends
if (err) {
if(err.status == 204) {
reject(specSummary.exitCode);
} else {
utils.sendUsageReport(bsConfig, {}, `buildId: ${buildDetails.build_id}`, 'error', 'sync_cli_error', err, rawArgs);
}
}
logger.info(lineSeparator);
specSummary.duration = endTime - startTime
resolve(specSummary)
resolve(specSummary);
}
);
});
Expand Down Expand Up @@ -145,21 +150,34 @@ let whileProcess = (whilstCallback) => {
whileLoop = false;
endTime = Date.now();
showSpecsStatus(body);
return whilstCallback(null, body);
return specSummary.exitCode == Constants.BUILD_FAILED_EXIT_CODE ?
whilstCallback({ status: 204, message: "No specs ran in the build"} ) : whilstCallback(null, body);
default:
whileLoop = false;
return whilstCallback({ status: response.statusCode, message: body });
}
});
}

let getStackTraceUrl = () => {
return specSummary.buildError
}

let showSpecsStatus = (data) => {
let specData = JSON.parse(data);
specData.forEach(specDetails => {
if (specDetails == "created") {
printInitialLog();
return;
} else if (specDetails["stacktrace_url"]) {
specSummary.exitCode = Constants.BUILD_FAILED_EXIT_CODE;
specSummary.buildError = specDetails["stacktrace_url"]
winstonLogger.error(chalk.red(specDetails["message"]));
} else {
printSpecData(JSON.parse(specDetails))
if(!buildStarted) {
buildStarted = true
printInitialLog();
}
printSpecData(JSON.parse(specDetails));
}
});
}
Expand Down Expand Up @@ -208,3 +226,4 @@ let getStatus = (status) => {
}

exports.printSpecsStatus = printSpecsStatus;
exports.getStackTraceUrl = getStackTraceUrl;
5 changes: 3 additions & 2 deletions test/unit/bin/helpers/sync/syncSpecsLogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,14 @@ describe("syncSpecsLogs", () => {
context("showSpecsStatus", () => {
const showSpecsStatus = syncSpecsLogs.__get__("showSpecsStatus");

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

showSpecsStatus(data);

expect(printInitialLog.calledOnce).to.be.true;
expect(printInitialLog.calledOnce).to.be.false;
});

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

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