Skip to content
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

Error handling improvements for Update #488

Open
wants to merge 2 commits into
base: legacy
Choose a base branch
from
Open
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
9 changes: 7 additions & 2 deletions lib/binaries/chrome_xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,17 @@ export class ChromeXml extends XmlConfigSource {

/**
* Gets a specific item from the XML.
*
* Resolves with URL, or rejects if there is an error retrieving the XML, or the requested
* was not found in the list.
*/
private getSpecificChromeDriverVersion(inputVersion: string): Promise<BinaryUrl> {
return this.getVersionList().then(list => {
const specificVersion = getValidSemver(inputVersion);
if (specificVersion === '') {
throw new Error(`version ${inputVersion} ChromeDriver does not exist`)
const msg = `Requested version ${inputVersion} does not look like a Chrome version.
Expected e.g. "2.34" or "89.0.4389.0".`;
throw new Error(msg);
}
let itemFound = '';
for (let item of list) {
Expand Down Expand Up @@ -117,7 +122,7 @@ export class ChromeXml extends XmlConfigSource {
}
}
if (itemFound == '') {
return {url: '', version: inputVersion};
throw new Error(`There is no chromedriver available for version ${specificVersion}.`);
} else {
return {url: Config.cdnUrls().chrome + itemFound, version: inputVersion};
}
Expand Down
20 changes: 16 additions & 4 deletions lib/cli/programs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as minimist from 'minimist';
import {Logger} from './logger';
import {MinimistArgs, Option, Options} from './options';

import {Args, MinimistArgs, Option, Options} from './options';

const logger = new Logger('program');

/**
* Dictionary that maps the command and the program.
Expand Down Expand Up @@ -71,11 +72,22 @@ export class Program {
* method.
* @param args The arguments that will be parsed to run the method.
*/
run(json: JSON): Promise<void> {
run(json: JSON): void;
run(json: JSON, testing: true): Promise<void>;
run(json: JSON, testing?: true): void|Promise<void> {
for (let opt in this.options) {
this.options[opt].value = this.getValue_(opt, json);
}
return Promise.resolve(this.runMethod(this.options));
const promise = Promise.resolve(this.runMethod(this.options));
if (testing) {
return promise;
} else {
promise.catch(err => {
// Exit gracefully when the program promise rejects
logger.error(err);
process.exit(-1);
});
}
}

private getValue_(key: string, json: JSON): number|boolean|string {
Expand Down
98 changes: 39 additions & 59 deletions lib/cmds/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as rimraf from 'rimraf';
import {AndroidSDK, Appium, Binary, ChromeDriver, GeckoDriver, IEDriver, Standalone} from '../binaries';
import {Logger, Options, Program} from '../cli';
import {Config} from '../config';
import {Downloader, FileManager} from '../files';
import {FileManager} from '../files';
import {HttpUtils} from '../http_utils';
import {spawn} from '../utils';

Expand Down Expand Up @@ -117,99 +117,79 @@ function update(options: Options): Promise<void> {
HttpUtils.assignOptions({ignoreSSL, proxy});
let verbose = options[Opt.VERBOSE].getBoolean();

// setup versions for binaries
let binaries = FileManager.setupBinaries(options[Opt.ALTERNATE_CDN].getString());
binaries[Standalone.id].versionCustom = options[Opt.VERSIONS_STANDALONE].getString();
binaries[ChromeDriver.id].versionCustom = options[Opt.VERSIONS_CHROME].getString();
if (options[Opt.VERSIONS_IE]) {
binaries[IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString();
}
if (options[Opt.VERSIONS_GECKO]) {
binaries[GeckoDriver.id].versionCustom = options[Opt.VERSIONS_GECKO].getString();
}
binaries[AndroidSDK.id].versionCustom = options[Opt.VERSIONS_ANDROID].getString();
binaries[Appium.id].versionCustom = options[Opt.VERSIONS_APPIUM].getString();

// if the file has not been completely downloaded, download it
// else if the file has already been downloaded, unzip the file, rename it, and give it
// permissions
if (standalone) {
let binary: Standalone = binaries[Standalone.id];
promises.push(FileManager.downloadFile(binary, outputDir)
.then<void>((downloaded: boolean) => {
if (!downloaded) {
logger.info(
binary.name + ': file exists ' +
path.resolve(outputDir, binary.filename()));
logger.info(binary.name + ': ' + binary.filename() + ' up to date');
}
})
.then(() => {
updateBrowserFile(binary, outputDir);
}));
binary.versionCustom = options[Opt.VERSIONS_STANDALONE].getString();
promises.push(FileManager.downloadFile(binary, outputDir).then(downloaded => {
if (!downloaded) {
logger.info(binary.name + ': file exists ' + path.resolve(outputDir, binary.filename()));
logger.info(binary.name + ': ' + binary.filename() + ' up to date');
}
updateBrowserFile(binary, outputDir);
}));
}
if (chrome) {
let binary: ChromeDriver = binaries[ChromeDriver.id];
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => {
return Promise.resolve(updateBrowserFile(binary, outputDir));
}));
binary.versionCustom = options[Opt.VERSIONS_CHROME].getString();
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL)
.then(() => updateBrowserFile(binary, outputDir)));
}
if (gecko) {
let binary: GeckoDriver = binaries[GeckoDriver.id];
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => {
return Promise.resolve(updateBrowserFile(binary, outputDir));
}));
if (options[Opt.VERSIONS_GECKO]) {
binary.versionCustom = options[Opt.VERSIONS_GECKO].getString();
}
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL)
.then(() => updateBrowserFile(binary, outputDir)));
}
if (ie64) {
let binary: IEDriver = binaries[IEDriver.id];
if (options[Opt.VERSIONS_IE]) {
binary.versionCustom = options[Opt.VERSIONS_IE].getString();
}
binary.osarch = Config.osArch(); // Win32 or x64
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => {
return Promise.resolve(updateBrowserFile(binary, outputDir));
}));
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL)
.then(() => updateBrowserFile(binary, outputDir)));
}
if (ie32) {
let binary: IEDriver = binaries[IEDriver.id];
binary.osarch = 'Win32';
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => {
return Promise.resolve(updateBrowserFile(binary, outputDir));
}));
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL)
.then(() => updateBrowserFile(binary, outputDir)));
}
if (android) {
let binary = binaries[AndroidSDK.id];
binary.versionCustom = options[Opt.VERSIONS_ANDROID].getString();
let sdk_path = path.resolve(outputDir, binary.executableFilename());
let oldAVDList: string;

updateBrowserFile(binary, outputDir);
promises.push(q.nfcall(fs.readFile, path.resolve(sdk_path, 'available_avds.json'))
.then(
(oldAVDs: string) => {
oldAVDList = oldAVDs;
},
() => {
oldAVDList = '[]';
})
.then(() => {
return updateBinary(binary, outputDir, proxy, ignoreSSL);
})
.then<void>(() => {
initializeAndroid(
path.resolve(outputDir, binary.executableFilename()),
android_api_levels, android_architectures, android_platforms,
android_accept_licenses, binaries[AndroidSDK.id].versionCustom,
JSON.parse(oldAVDList), logger, verbose);
}));
promises.push(
q.nfcall(fs.readFile, path.resolve(sdk_path, 'available_avds.json'))
.then((oldAVDs: string) => oldAVDList = oldAVDs, () => oldAVDList = '[]')
.then(() => updateBinary(binary, outputDir, proxy, ignoreSSL))
.then(
() => initializeAndroid(
path.resolve(outputDir, binary.executableFilename()), android_api_levels,
android_architectures, android_platforms, android_accept_licenses,
binary.versionCustom, JSON.parse(oldAVDList), logger, verbose)));
}
if (ios) {
checkIOS(logger);
}
if (android || ios) {
installAppium(binaries[Appium.id], outputDir);
updateBrowserFile(binaries[Appium.id], outputDir);
const binary = binaries[Appium.id];
binary.versionCustom = options[Opt.VERSIONS_APPIUM].getString();
installAppium(binary, outputDir);
updateBrowserFile(binary, outputDir);
}

return Promise.all(promises).then(() => {
writeBrowserFile(outputDir);
});
return Promise.all(promises as Promise<void>[]).then(() => writeBrowserFile(outputDir));
}

function updateBinary<T extends Binary>(
Expand Down
121 changes: 58 additions & 63 deletions lib/files/downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,70 +40,65 @@ export class Downloader {
let resContentLength: number;

return new Promise<boolean>((resolve, reject) => {
req = request(options);
req.on('response', response => {
if (response.statusCode === 200) {
resContentLength = +response.headers['content-length'];
if (contentLength === resContentLength) {
// if the size is the same, do not download and stop here
response.destroy();
resolve(false);
} else {
let curl = outputDir + '/' + fileName + ' ' + options.url;
if (HttpUtils.requestOpts.proxy) {
let pathUrl = url.parse(options.url.toString()).path;
let host = url.parse(options.url.toString()).host;
let newFileUrl = url.resolve(HttpUtils.requestOpts.proxy, pathUrl);
curl = outputDir + '/' + fileName + ' \'' + newFileUrl +
'\' -H \'host:' + host + '\'';
}
if (HttpUtils.requestOpts.ignoreSSL) {
curl = 'k ' + curl;
}
logger.info('curl -o' + curl);
req = request(options);
req.on('response', response => {
if (response.statusCode === 200) {
resContentLength = +response.headers['content-length'];
if (contentLength === resContentLength) {
// if the size is the same, do not download and stop here
response.destroy();
resolve(false);
} else {
let curl = outputDir + '/' + fileName + ' ' + options.url;
if (HttpUtils.requestOpts.proxy) {
let pathUrl = url.parse(options.url.toString()).path;
let host = url.parse(options.url.toString()).host;
let newFileUrl = url.resolve(HttpUtils.requestOpts.proxy, pathUrl);
curl =
outputDir + '/' + fileName + ' \'' + newFileUrl + '\' -H \'host:' + host + '\'';
}
if (HttpUtils.requestOpts.ignoreSSL) {
curl = 'k ' + curl;
}
logger.info('curl -o' + curl);

// only pipe if the headers are different length
file = fs.createWriteStream(filePath);
req.pipe(file);
file.on('close', () => {
fs.stat(filePath, (error, stats) => {
if (error) {
(error as any).msg = 'Error: Got error ' + error + ' from ' + fileUrl;
return reject(error);
}
if (stats.size != resContentLength) {
(error as any).msg = 'Error: corrupt download for ' + fileName +
'. Please re-run webdriver-manager update';
fs.unlinkSync(filePath);
reject(error);
}
if (callback) {
callback(binary, outputDir, fileName);
}
resolve(true);
});
});
}
// only pipe if the headers are different length
file = fs.createWriteStream(filePath);
req.pipe(file);
file.on('close', () => {
fs.stat(filePath, (error, stats) => {
if (error) {
(error as any).msg = 'Error: Got error ' + error + ' from ' + fileUrl;
return reject(error);
}
if (stats.size != resContentLength) {
(error as any).msg = 'Error: corrupt download for ' + fileName +
'. Please re-run webdriver-manager update';
fs.unlinkSync(filePath);
reject(error);
}
if (callback) {
callback(binary, outputDir, fileName);
}
resolve(true);
});
});
}

} else {
let error = new Error();
(error as any).msg =
'Expected response code 200, received: ' + response.statusCode;
reject(error);
}
});
req.on('error', error => {
if ((error as any).code === 'ETIMEDOUT') {
(error as any).msg = 'Connection timeout downloading: ' + fileUrl +
'. Default timeout is 4 minutes.';
} else if ((error as any).connect) {
(error as any).msg = 'Could not connect to the server to download: ' + fileUrl;
}
reject(error);
});
})
.catch(error => {
logger.error((error as any).msg || (error as any).message);
});
} else {
let error = new Error('Expected response code 200, received: ' + response.statusCode);
reject(error);
}
});
req.on('error', error => {
if ((error as any).code === 'ETIMEDOUT') {
(error as any).msg =
'Connection timeout downloading: ' + fileUrl + '. Default timeout is 4 minutes.';
} else if ((error as any).connect) {
(error as any).msg = 'Could not connect to the server to download: ' + fileUrl;
}
reject(error);
});
});
}
}
Loading