refactor(Cypress): Moved Cypress to the main folder of the project, as I don't need a sub project for now.
This commit is contained in:
449
node_modules/cypress/lib/tasks/install.js
generated
vendored
Normal file
449
node_modules/cypress/lib/tasks/install.js
generated
vendored
Normal file
@@ -0,0 +1,449 @@
|
||||
"use strict";
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
const os = require('os');
|
||||
|
||||
const url = require('url');
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const chalk = require('chalk');
|
||||
|
||||
const debug = require('debug')('cypress:cli');
|
||||
|
||||
const {
|
||||
Listr
|
||||
} = require('listr2');
|
||||
|
||||
const Promise = require('bluebird');
|
||||
|
||||
const logSymbols = require('log-symbols');
|
||||
|
||||
const {
|
||||
stripIndent
|
||||
} = require('common-tags');
|
||||
|
||||
const fs = require('../fs');
|
||||
|
||||
const download = require('./download');
|
||||
|
||||
const util = require('../util');
|
||||
|
||||
const state = require('./state');
|
||||
|
||||
const unzip = require('./unzip');
|
||||
|
||||
const logger = require('../logger');
|
||||
|
||||
const {
|
||||
throwFormErrorText,
|
||||
errors
|
||||
} = require('../errors');
|
||||
|
||||
const verbose = require('../VerboseRenderer');
|
||||
|
||||
const getNpmArgv = () => {
|
||||
const json = process.env.npm_config_argv;
|
||||
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug('found npm argv json %o', json);
|
||||
|
||||
try {
|
||||
return JSON.parse(json).original || [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}; // attempt to discover the version specifier used to install Cypress
|
||||
// for example: "^5.0.0", "https://cdn.cypress.io/...", ...
|
||||
|
||||
|
||||
const getVersionSpecifier = (startDir = path.resolve(__dirname, '../..')) => {
|
||||
const argv = getNpmArgv();
|
||||
|
||||
if (argv) {
|
||||
const tgz = _.find(argv, t => t.endsWith('cypress.tgz'));
|
||||
|
||||
if (tgz) {
|
||||
return tgz;
|
||||
}
|
||||
}
|
||||
|
||||
const getVersionSpecifierFromPkg = dir => {
|
||||
debug('looking for versionSpecifier %o', {
|
||||
dir
|
||||
});
|
||||
|
||||
const tryParent = () => {
|
||||
const parentPath = path.resolve(dir, '..');
|
||||
|
||||
if (parentPath === dir) {
|
||||
debug('reached FS root with no versionSpecifier found');
|
||||
return;
|
||||
}
|
||||
|
||||
return getVersionSpecifierFromPkg(parentPath);
|
||||
};
|
||||
|
||||
return fs.readJSON(path.join(dir, 'package.json')).catch(() => ({})).then(pkg => {
|
||||
const specifier = _.chain(['dependencies', 'devDependencies', 'optionalDependencies']).map(prop => _.get(pkg, `${prop}.cypress`)).compact().first().value();
|
||||
|
||||
return specifier || tryParent();
|
||||
});
|
||||
}; // recurse through parent directories until package.json with `cypress` is found
|
||||
|
||||
|
||||
return getVersionSpecifierFromPkg(startDir).then(versionSpecifier => {
|
||||
debug('finished looking for versionSpecifier', {
|
||||
versionSpecifier
|
||||
});
|
||||
return versionSpecifier;
|
||||
});
|
||||
};
|
||||
|
||||
const betaNpmUrlRe = /^\/beta\/npm\/(?<version>[0-9.]+)\/(?<artifactSlug>[^/]+)\/cypress\.tgz$/; // convert a prerelease NPM package .tgz URL to the corresponding binary .zip URL
|
||||
|
||||
const getBinaryUrlFromPrereleaseNpmUrl = npmUrl => {
|
||||
let parsed;
|
||||
|
||||
try {
|
||||
parsed = url.parse(npmUrl);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matches = betaNpmUrlRe.exec(parsed.pathname);
|
||||
|
||||
if (parsed.hostname !== 'cdn.cypress.io' || !matches) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
version,
|
||||
artifactSlug
|
||||
} = matches.groups;
|
||||
parsed.pathname = `/beta/binary/${version}/${os.platform()}-${os.arch()}/${artifactSlug}/cypress.zip`;
|
||||
return parsed.format();
|
||||
};
|
||||
|
||||
const alreadyInstalledMsg = () => {
|
||||
if (!util.isPostInstall()) {
|
||||
logger.log(stripIndent`
|
||||
Skipping installation:
|
||||
|
||||
Pass the ${chalk.yellow('--force')} option if you'd like to reinstall anyway.
|
||||
`);
|
||||
}
|
||||
};
|
||||
|
||||
const displayCompletionMsg = () => {
|
||||
// check here to see if we are globally installed
|
||||
if (util.isInstalledGlobally()) {
|
||||
// if we are display a warning
|
||||
logger.log();
|
||||
logger.warn(stripIndent`
|
||||
${logSymbols.warning} Warning: It looks like you\'ve installed Cypress globally.
|
||||
|
||||
This will work, but it'\s not recommended.
|
||||
|
||||
The recommended way to install Cypress is as a devDependency per project.
|
||||
|
||||
You should probably run these commands:
|
||||
|
||||
- ${chalk.cyan('npm uninstall -g cypress')}
|
||||
- ${chalk.cyan('npm install --save-dev cypress')}
|
||||
`);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.log();
|
||||
logger.log('You can now open Cypress by running:', chalk.cyan(path.join('node_modules', '.bin', 'cypress'), 'open'));
|
||||
logger.log();
|
||||
logger.log(chalk.grey('https://on.cypress.io/installing-cypress'));
|
||||
logger.log();
|
||||
};
|
||||
|
||||
const downloadAndUnzip = ({
|
||||
version,
|
||||
installDir,
|
||||
downloadDir
|
||||
}) => {
|
||||
const progress = {
|
||||
throttle: 100,
|
||||
onProgress: null
|
||||
};
|
||||
const downloadDestination = path.join(downloadDir, `cypress-${process.pid}.zip`);
|
||||
const rendererOptions = getRendererOptions(); // let the user know what version of cypress we're downloading!
|
||||
|
||||
logger.log(`Installing Cypress ${chalk.gray(`(version: ${version})`)}`);
|
||||
logger.log();
|
||||
const tasks = new Listr([{
|
||||
options: {
|
||||
title: util.titleize('Downloading Cypress')
|
||||
},
|
||||
task: (ctx, task) => {
|
||||
// as our download progresses indicate the status
|
||||
progress.onProgress = progessify(task, 'Downloading Cypress');
|
||||
return download.start({
|
||||
version,
|
||||
downloadDestination,
|
||||
progress
|
||||
}).then(redirectVersion => {
|
||||
if (redirectVersion) version = redirectVersion;
|
||||
debug(`finished downloading file: ${downloadDestination}`);
|
||||
}).then(() => {
|
||||
// save the download destination for unzipping
|
||||
util.setTaskTitle(task, util.titleize(chalk.green('Downloaded Cypress')), rendererOptions.renderer);
|
||||
});
|
||||
}
|
||||
}, unzipTask({
|
||||
progress,
|
||||
zipFilePath: downloadDestination,
|
||||
installDir,
|
||||
rendererOptions
|
||||
}), {
|
||||
options: {
|
||||
title: util.titleize('Finishing Installation')
|
||||
},
|
||||
task: (ctx, task) => {
|
||||
const cleanup = () => {
|
||||
debug('removing zip file %s', downloadDestination);
|
||||
return fs.removeAsync(downloadDestination);
|
||||
};
|
||||
|
||||
return cleanup().then(() => {
|
||||
debug('finished installation in', installDir);
|
||||
util.setTaskTitle(task, util.titleize(chalk.green('Finished Installation'), chalk.gray(installDir)), rendererOptions.renderer);
|
||||
});
|
||||
}
|
||||
}], {
|
||||
rendererOptions
|
||||
}); // start the tasks!
|
||||
|
||||
return Promise.resolve(tasks.run());
|
||||
};
|
||||
|
||||
const start = (options = {}) => {
|
||||
debug('installing with options %j', options);
|
||||
|
||||
_.defaults(options, {
|
||||
force: false
|
||||
});
|
||||
|
||||
const pkgVersion = util.pkgVersion();
|
||||
let needVersion = pkgVersion;
|
||||
let binaryUrlOverride;
|
||||
debug('version in package.json is', needVersion); // let this environment variable reset the binary version we need
|
||||
|
||||
if (util.getEnv('CYPRESS_INSTALL_BINARY')) {
|
||||
// because passed file paths are often double quoted
|
||||
// and might have extra whitespace around, be robust and trim the string
|
||||
const trimAndRemoveDoubleQuotes = true;
|
||||
const envVarVersion = util.getEnv('CYPRESS_INSTALL_BINARY', trimAndRemoveDoubleQuotes);
|
||||
debug('using environment variable CYPRESS_INSTALL_BINARY "%s"', envVarVersion);
|
||||
|
||||
if (envVarVersion === '0') {
|
||||
debug('environment variable CYPRESS_INSTALL_BINARY = 0, skipping install');
|
||||
logger.log(stripIndent`
|
||||
${chalk.yellow('Note:')} Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.`);
|
||||
logger.log();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
binaryUrlOverride = envVarVersion;
|
||||
}
|
||||
|
||||
if (util.getEnv('CYPRESS_CACHE_FOLDER')) {
|
||||
const envCache = util.getEnv('CYPRESS_CACHE_FOLDER');
|
||||
logger.log(stripIndent`
|
||||
${chalk.yellow('Note:')} Overriding Cypress cache directory to: ${chalk.cyan(envCache)}
|
||||
|
||||
Previous installs of Cypress may not be found.
|
||||
`);
|
||||
logger.log();
|
||||
}
|
||||
|
||||
const installDir = state.getVersionDir(pkgVersion);
|
||||
const cacheDir = state.getCacheDir();
|
||||
const binaryDir = state.getBinaryDir(pkgVersion);
|
||||
return fs.ensureDirAsync(cacheDir).catch({
|
||||
code: 'EACCES'
|
||||
}, err => {
|
||||
return throwFormErrorText(errors.invalidCacheDirectory)(stripIndent`
|
||||
Failed to access ${chalk.cyan(cacheDir)}:
|
||||
|
||||
${err.message}
|
||||
`);
|
||||
}).then(() => {
|
||||
return Promise.all([state.getBinaryPkgAsync(binaryDir).then(state.getBinaryPkgVersion), getVersionSpecifier()]);
|
||||
}).then(([binaryVersion, versionSpecifier]) => {
|
||||
if (!binaryUrlOverride && versionSpecifier) {
|
||||
const computedBinaryUrl = getBinaryUrlFromPrereleaseNpmUrl(versionSpecifier);
|
||||
|
||||
if (computedBinaryUrl) {
|
||||
debug('computed binary url from version specifier %o', {
|
||||
computedBinaryUrl,
|
||||
needVersion
|
||||
});
|
||||
binaryUrlOverride = computedBinaryUrl;
|
||||
}
|
||||
}
|
||||
|
||||
needVersion = binaryUrlOverride || needVersion;
|
||||
debug('installed version is', binaryVersion, 'version needed is', needVersion);
|
||||
|
||||
if (!binaryVersion) {
|
||||
debug('no binary installed under cli version');
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.log();
|
||||
logger.log(stripIndent`
|
||||
Cypress ${chalk.green(binaryVersion)} is installed in ${chalk.cyan(installDir)}
|
||||
`);
|
||||
logger.log();
|
||||
|
||||
if (options.force) {
|
||||
debug('performing force install over existing binary');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (binaryVersion === needVersion || !util.isSemver(needVersion)) {
|
||||
// our version matches, tell the user this is a noop
|
||||
alreadyInstalledMsg();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).then(shouldInstall => {
|
||||
// noop if we've been told not to download
|
||||
if (!shouldInstall) {
|
||||
debug('Not downloading or installing binary');
|
||||
return;
|
||||
}
|
||||
|
||||
if (needVersion !== pkgVersion) {
|
||||
logger.log(chalk.yellow(stripIndent`
|
||||
${logSymbols.warning} Warning: Forcing a binary version different than the default.
|
||||
|
||||
The CLI expected to install version: ${chalk.green(pkgVersion)}
|
||||
|
||||
Instead we will install version: ${chalk.green(needVersion)}
|
||||
|
||||
These versions may not work properly together.
|
||||
`));
|
||||
logger.log();
|
||||
} // see if version supplied is a path to a binary
|
||||
|
||||
|
||||
return fs.pathExistsAsync(needVersion).then(exists => {
|
||||
if (exists) {
|
||||
return path.extname(needVersion) === '.zip' ? needVersion : false;
|
||||
}
|
||||
|
||||
const possibleFile = util.formAbsolutePath(needVersion);
|
||||
debug('checking local file', possibleFile, 'cwd', process.cwd());
|
||||
return fs.pathExistsAsync(possibleFile).then(exists => {
|
||||
// if this exists return the path to it
|
||||
// else false
|
||||
if (exists && path.extname(possibleFile) === '.zip') {
|
||||
return possibleFile;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}).then(pathToLocalFile => {
|
||||
if (pathToLocalFile) {
|
||||
const absolutePath = path.resolve(needVersion);
|
||||
debug('found local file at', absolutePath);
|
||||
debug('skipping download');
|
||||
const rendererOptions = getRendererOptions();
|
||||
return new Listr([unzipTask({
|
||||
progress: {
|
||||
throttle: 100,
|
||||
onProgress: null
|
||||
},
|
||||
zipFilePath: absolutePath,
|
||||
installDir,
|
||||
rendererOptions
|
||||
})], {
|
||||
rendererOptions
|
||||
}).run();
|
||||
}
|
||||
|
||||
if (options.force) {
|
||||
debug('Cypress already installed at', installDir);
|
||||
debug('but the installation was forced');
|
||||
}
|
||||
|
||||
debug('preparing to download and unzip version ', needVersion, 'to path', installDir);
|
||||
const downloadDir = os.tmpdir();
|
||||
return downloadAndUnzip({
|
||||
version: needVersion,
|
||||
installDir,
|
||||
downloadDir
|
||||
});
|
||||
}) // delay 1 sec for UX, unless we are testing
|
||||
.then(() => {
|
||||
return Promise.delay(1000);
|
||||
}).then(displayCompletionMsg);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
start,
|
||||
_getVersionSpecifier: getVersionSpecifier,
|
||||
_getBinaryUrlFromPrereleaseNpmUrl: getBinaryUrlFromPrereleaseNpmUrl
|
||||
};
|
||||
|
||||
const unzipTask = ({
|
||||
zipFilePath,
|
||||
installDir,
|
||||
progress,
|
||||
rendererOptions
|
||||
}) => {
|
||||
return {
|
||||
options: {
|
||||
title: util.titleize('Unzipping Cypress')
|
||||
},
|
||||
task: (ctx, task) => {
|
||||
// as our unzip progresses indicate the status
|
||||
progress.onProgress = progessify(task, 'Unzipping Cypress');
|
||||
return unzip.start({
|
||||
zipFilePath,
|
||||
installDir,
|
||||
progress
|
||||
}).then(() => {
|
||||
util.setTaskTitle(task, util.titleize(chalk.green('Unzipped Cypress')), rendererOptions.renderer);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const progessify = (task, title) => {
|
||||
// return higher order function
|
||||
return (percentComplete, remaining) => {
|
||||
percentComplete = chalk.white(` ${percentComplete}%`); // pluralize seconds remaining
|
||||
|
||||
remaining = chalk.gray(`${remaining}s`);
|
||||
util.setTaskTitle(task, util.titleize(title, percentComplete, remaining), getRendererOptions().renderer);
|
||||
};
|
||||
}; // if we are running in CI then use
|
||||
// the verbose renderer else use
|
||||
// the default
|
||||
|
||||
|
||||
const getRendererOptions = () => {
|
||||
let renderer = util.isCi() ? verbose : 'default';
|
||||
|
||||
if (logger.logLevel() === 'silent') {
|
||||
renderer = 'silent';
|
||||
}
|
||||
|
||||
return {
|
||||
renderer
|
||||
};
|
||||
};
|
Reference in New Issue
Block a user