@@ -1080,8 +1080,11 @@ const {
10801080 LOCAL_BINARY_FOLDER,
10811081 PLATFORMS,
10821082 LOCAL_BINARY_NAME,
1083+ LOCAL_BINARY_ZIP,
10831084 LOCAL_LOG_FILE_PREFIX,
10841085 LOCAL_BINARY_TRIGGER,
1086+ RETRY_DELAY_BINARY,
1087+ BINARY_MAX_TRIES,
10851088 ALLOWED_INPUT_VALUES: {
10861089 LOCAL_TESTING,
10871090 },
@@ -1107,18 +1110,23 @@ class BinaryControl {
11071110 * platform and the architecture
11081111 */
11091112 _decidePlatformAndBinary() {
1113+ this.binaryFolder = path.resolve(
1114+ process.env.GITHUB_WORKSPACE,
1115+ '..', '..', '..',
1116+ '_work',
1117+ 'binary',
1118+ LOCAL_BINARY_FOLDER,
1119+ this.platform,
1120+ );
11101121 switch (this.platform) {
11111122 case PLATFORMS.DARWIN:
11121123 this.binaryLink = BINARY_LINKS.DARWIN;
1113- this.binaryFolder = path.resolve(process.env.HOME, 'work', 'binary', LOCAL_BINARY_FOLDER, this.platform);
11141124 break;
11151125 case PLATFORMS.LINUX:
11161126 this.binaryLink = os.arch() === 'x32' ? BINARY_LINKS.LINUX_32 : BINARY_LINKS.LINUX_64;
1117- this.binaryFolder = path.resolve(process.env.HOME, 'work', 'binary', LOCAL_BINARY_FOLDER, this.platform);
11181127 break;
11191128 case PLATFORMS.WIN32:
11201129 this.binaryLink = BINARY_LINKS.WINDOWS;
1121- this.binaryFolder = path.resolve(process.env.GITHUB_WORKSPACE, '..', '..', 'work', 'binary', LOCAL_BINARY_FOLDER, this.platform);
11221130 break;
11231131 default:
11241132 throw Error(`Unsupported Platform: ${this.platform}. No BrowserStackLocal binary found.`);
@@ -1207,23 +1215,42 @@ class BinaryControl {
12071215 };
12081216 }
12091217
1218+ async _removeAnyStaleBinary() {
1219+ const binaryZip = path.resolve(this.binaryFolder, LOCAL_BINARY_ZIP);
1220+ const previousLocalBinary = path.resolve(
1221+ this.binaryFolder,
1222+ `${LOCAL_BINARY_NAME}${this.platform === PLATFORMS.WIN32 ? '.exe' : ''}`,
1223+ );
1224+ await Promise.all([io.rmRF(binaryZip), io.rmRF(previousLocalBinary)]);
1225+ }
1226+
12101227 /**
12111228 * Downloads the Local Binary, extracts it and adds it in the PATH variable
12121229 */
12131230 async downloadBinary() {
1214- if (Utils.checkToolInCache(LOCAL_BINARY_NAME)) {
1231+ const cachedBinaryPath = Utils.checkToolInCache(LOCAL_BINARY_NAME, '1.0.0');
1232+ if (cachedBinaryPath) {
12151233 core.info('BrowserStackLocal binary already exists in cache. Using that instead of downloading again...');
1234+ // A cached tool is persisted across runs. But the PATH is reset back to its original
1235+ // state between each run. Thus, adding the cached tool path back to PATH again.
1236+ core.addPath(cachedBinaryPath);
12161237 return;
12171238 }
1239+
12181240 try {
12191241 await this._makeDirectory();
1242+ core.debug('BrowserStackLocal binary not found in cache. Deleting any stale/existing binary before downloading...');
1243+ await this._removeAnyStaleBinary();
1244+
12201245 core.info('Downloading BrowserStackLocal binary...');
1221- const downloadPath = await tc.downloadTool(this.binaryLink, path.resolve(this.binaryFolder, 'binaryZip'));
1246+ const downloadPath = await tc.downloadTool(
1247+ this.binaryLink,
1248+ path.resolve(this.binaryFolder, LOCAL_BINARY_ZIP),
1249+ );
12221250 const extractedPath = await tc.extractZip(downloadPath, this.binaryFolder);
12231251 core.info(`BrowserStackLocal binary downloaded & extracted successfuly at: ${extractedPath}`);
12241252 const cachedPath = await tc.cacheDir(extractedPath, LOCAL_BINARY_NAME, '1.0.0');
12251253 core.addPath(cachedPath);
1226- this.binaryPath = extractedPath;
12271254 } catch (e) {
12281255 throw Error(`BrowserStackLocal binary could not be downloaded due to ${e.message}`);
12291256 }
@@ -1233,35 +1260,47 @@ class BinaryControl {
12331260 * Starts Local Binary using the args generated for this action
12341261 */
12351262 async startBinary() {
1236- try {
1237- this._generateArgsForBinary();
1238- let { localIdentifier } = this.stateForBinary;
1239- localIdentifier = localIdentifier ? `with local-identifier=${localIdentifier}` : '';
1240- core.info(`Starting local tunnel ${localIdentifier} in daemon mode...`);
1263+ this._generateArgsForBinary();
1264+ let { localIdentifier } = this.stateForBinary;
1265+ localIdentifier = localIdentifier ? `with local-identifier=${localIdentifier}` : '';
1266+ core.info(`Starting local tunnel ${localIdentifier} in daemon mode...`);
12411267
1242- const { output, error } = await this._triggerBinary(LOCAL_TESTING.START);
1268+ let triesAvailable = BINARY_MAX_TRIES;
1269+
1270+ while (triesAvailable--) {
1271+ try {
1272+ // eslint-disable-next-line no-await-in-loop
1273+ const { output, error } = await this._triggerBinary(LOCAL_TESTING.START);
1274+
1275+ if (!error) {
1276+ const outputParsed = JSON.parse(output);
1277+ if (outputParsed.state === LOCAL_BINARY_TRIGGER.START.CONNECTED) {
1278+ core.info(`Local tunnel status: ${outputParsed.message}`);
1279+ return;
1280+ }
12431281
1244- if (!error) {
1245- const outputParsed = JSON.parse(output);
1246- if (outputParsed.state === LOCAL_BINARY_TRIGGER.START.CONNECTED) {
1247- core.info(`Local tunnel status: ${outputParsed.message}`);
1248- } else {
12491282 throw Error(JSON.stringify(outputParsed.message));
1283+ } else {
1284+ throw Error(JSON.stringify(error));
1285+ }
1286+ } catch (e) {
1287+ if (triesAvailable) {
1288+ core.info(`Error in starting local tunnel: ${e.message}. Trying again in 5 seconds...`);
1289+ // eslint-disable-next-line no-await-in-loop
1290+ await Utils.sleepFor(RETRY_DELAY_BINARY);
1291+ } else {
1292+ throw Error(`Local tunnel could not be started. Error message from binary: ${e.message}`);
12501293 }
1251- } else {
1252- throw Error(JSON.stringify(error));
12531294 }
1254- } catch (e) {
1255- throw Error(`Local tunnel could not be started. Error message from binary: ${e.message}`);
12561295 }
12571296 }
12581297
12591298 /**
12601299 * Stops Local Binary using the args generated for this action
12611300 */
12621301 async stopBinary() {
1302+ this._generateArgsForBinary();
12631303 try {
1264- this._generateArgsForBinary();
12651304 let { localIdentifier } = this.stateForBinary;
12661305 localIdentifier = localIdentifier ? `with local-identifier=${localIdentifier}` : '';
12671306 core.info(`Stopping local tunnel ${localIdentifier} in daemon mode...`);
@@ -7230,9 +7269,15 @@ class Utils {
72307269 delete process.env[environmentVariable];
72317270 }
72327271
7233- static checkToolInCache(toolName) {
7234- const toolCache = tc.findAllVersions(toolName);
7235- return toolCache.length !== 0;
7272+ static checkToolInCache(toolName, version) {
7273+ const toolCachePath = tc.find(toolName, version);
7274+ return toolCachePath;
7275+ }
7276+
7277+ static async sleepFor(ms) {
7278+ let parsedMilliseconds = parseFloat(ms);
7279+ parsedMilliseconds = parsedMilliseconds > 0 ? parsedMilliseconds : 0;
7280+ return new Promise((resolve) => setTimeout(resolve, parsedMilliseconds));
72367281 }
72377282}
72387283
@@ -13444,10 +13489,14 @@ module.exports = {
1344413489 },
1344513490 },
1344613491
13492+ BINARY_MAX_TRIES: 3,
13493+ RETRY_DELAY_BINARY: 5000,
13494+
1344713495 RESTRICTED_LOCAL_ARGS: ['k', 'key', 'local-identifier', 'daemon', 'only-automate', 'verbose', 'log-file', 'ci-plugin'],
1344813496
1344913497 LOCAL_BINARY_FOLDER: 'LocalBinaryFolder',
1345013498 LOCAL_BINARY_NAME: 'BrowserStackLocal',
13499+ LOCAL_BINARY_ZIP: 'binaryZip',
1345113500 LOCAL_LOG_FILE_PREFIX: 'BrowserStackLocal',
1345213501};
1345313502
0 commit comments