mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-22 15:12:27 -05:00
@@ -0,0 +1,283 @@
|
||||
const _ = require('lodash')
|
||||
let fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
// we wrap glob to handle EMFILE error
|
||||
let glob = require('glob')
|
||||
const Promise = require('bluebird')
|
||||
const retry = require('bluebird-retry')
|
||||
const la = require('lazy-ass')
|
||||
const check = require('check-more-types')
|
||||
const execa = require('execa')
|
||||
const R = require('ramda')
|
||||
const prettyMs = require('pretty-ms')
|
||||
const pluralize = require('pluralize')
|
||||
const debug = require('debug')('cypress:binary')
|
||||
const externalUtils = require('./3rd-party')
|
||||
|
||||
fs = Promise.promisifyAll(fs)
|
||||
glob = Promise.promisify(glob)
|
||||
|
||||
const DEFAULT_PATHS = 'package.json'.split(' ')
|
||||
|
||||
const pathToPackageJson = function (packageFolder) {
|
||||
la(check.unemptyString(packageFolder), 'expected package path', packageFolder)
|
||||
|
||||
return path.join(packageFolder, 'package.json')
|
||||
}
|
||||
|
||||
const createCLIExecutable = (command) => {
|
||||
return (function (args, cwd, env = {}) {
|
||||
const commandToExecute = `${command} ${args.join(' ')}`
|
||||
|
||||
console.log(commandToExecute)
|
||||
if (cwd) {
|
||||
console.log('in folder:', cwd)
|
||||
}
|
||||
|
||||
la(check.maybe.string(cwd), 'invalid CWD string', cwd)
|
||||
|
||||
return execa(command, args, { stdio: 'inherit', cwd, env })
|
||||
// if everything is ok, resolve with nothing
|
||||
.then(R.always(undefined))
|
||||
.catch((result) => {
|
||||
const msg = `${commandToExecute} failed with exit code: ${result.code}`
|
||||
|
||||
throw new Error(msg)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const yarn = createCLIExecutable('yarn')
|
||||
const npx = createCLIExecutable('npx')
|
||||
|
||||
const runAllBuild = _.partial(npx, ['lerna', 'run', 'build-prod', '--ignore', 'cli'])
|
||||
|
||||
// removes transpiled JS files in the original package folders
|
||||
const runAllCleanJs = _.partial(npx, ['lerna', 'run', 'clean-js', '--ignore', 'cli'])
|
||||
|
||||
const copyAllToDist = function (distDir) {
|
||||
const copyRelativePathToDist = function (relative) {
|
||||
const dest = path.join(distDir, relative)
|
||||
|
||||
return retry(() => {
|
||||
console.log(relative, '->', dest)
|
||||
|
||||
return fs.copyAsync(relative, dest)
|
||||
})
|
||||
}
|
||||
|
||||
const copyPackage = function (pkg) {
|
||||
console.log('** copy package: %s **', pkg)
|
||||
|
||||
// copies the package to dist
|
||||
// including the default paths
|
||||
// and any specified in package.json files
|
||||
return Promise.resolve(fs.readJsonAsync(pathToPackageJson(pkg)))
|
||||
.then((json) => {
|
||||
// grab all the files that match "files" wildcards
|
||||
// but without all negated files ("!src/**/*.spec.js" for example)
|
||||
// and default included paths
|
||||
// and convert to relative paths
|
||||
return DEFAULT_PATHS
|
||||
.concat(json.files || [])
|
||||
.concat(json.main || [])
|
||||
}).then((pkgFileMasks) => {
|
||||
debug('for pkg %s have the following file masks %o', pkg, pkgFileMasks)
|
||||
const globOptions = {
|
||||
cwd: pkg, // search in the package folder
|
||||
absolute: false, // and return relative file paths
|
||||
followSymbolicLinks: false, // do not follow symlinks
|
||||
}
|
||||
|
||||
return externalUtils.globby(pkgFileMasks, globOptions)
|
||||
}).map((foundFileRelativeToPackageFolder) => {
|
||||
return path.join(pkg, foundFileRelativeToPackageFolder)
|
||||
})
|
||||
.tap(debug)
|
||||
.map(copyRelativePathToDist, { concurrency: 1 })
|
||||
}
|
||||
|
||||
// fs-extra concurrency tests (copyPackage / copyRelativePathToDist)
|
||||
// 1/1 41688
|
||||
// 1/5 42218
|
||||
// 1/10 42566
|
||||
// 2/1 45041
|
||||
// 2/2 43589
|
||||
// 3/3 51399
|
||||
|
||||
// cp -R concurrency tests
|
||||
// 1/1 65811
|
||||
|
||||
const started = new Date()
|
||||
|
||||
return fs.ensureDirAsync(distDir)
|
||||
.then(() => {
|
||||
const globs = ['./packages/*', './npm/*']
|
||||
const globOptions = {
|
||||
onlyFiles: false,
|
||||
}
|
||||
|
||||
return Promise.resolve(externalUtils.globby(globs, globOptions))
|
||||
.map(copyPackage, { concurrency: 1 })
|
||||
}).then(() => {
|
||||
console.log('Finished Copying %dms', new Date() - started)
|
||||
|
||||
return console.log('')
|
||||
})
|
||||
}
|
||||
|
||||
// replaces local npm version 0.0.0-development
|
||||
// with the path to the package
|
||||
// we need to do this instead of just changing the symlink (like we do for require('@packages/...'))
|
||||
// so the packages actually get installed to node_modules and work with peer dependencies
|
||||
const replaceLocalNpmVersions = function (basePath) {
|
||||
const visited = []
|
||||
|
||||
const updateNpmPackage = function (pkg) {
|
||||
if (!visited.includes(pkg)) {
|
||||
visited.push(pkg)
|
||||
|
||||
return updatePackageJson(`./npm/${pkg}/package.json`)
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const updatePackageJson = function (pattern) {
|
||||
return Promise.resolve(glob(pattern, { cwd: basePath }))
|
||||
.map((pkgPath) => {
|
||||
const pkgJsonPath = path.join(basePath, pkgPath)
|
||||
|
||||
return fs.readJsonAsync(pkgJsonPath)
|
||||
.then((json) => {
|
||||
const { dependencies } = json
|
||||
let shouldWriteFile = false
|
||||
|
||||
if (dependencies) {
|
||||
return Promise.all(_.map(dependencies, (version, pkg) => {
|
||||
const parsedPkg = /(@cypress\/)(.*)/g.exec(pkg)
|
||||
|
||||
if (parsedPkg && parsedPkg.length === 3 && version === '0.0.0-development') {
|
||||
const pkgName = parsedPkg[2]
|
||||
|
||||
json.dependencies[`@cypress/${pkgName}`] = `file:${path.join(basePath, 'npm', pkgName)}`
|
||||
shouldWriteFile = true
|
||||
|
||||
return updateNpmPackage(pkgName)
|
||||
}
|
||||
}))
|
||||
.then(() => {
|
||||
if (shouldWriteFile) {
|
||||
return fs.writeJsonAsync(pkgJsonPath, json, { spaces: 2 })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return updatePackageJson('./packages/*/package.json')
|
||||
}
|
||||
|
||||
const forceNpmInstall = function (packagePath, packageToInstall) {
|
||||
console.log('Force installing %s', packageToInstall)
|
||||
console.log('in %s', packagePath)
|
||||
la(check.unemptyString(packageToInstall), 'missing package to install')
|
||||
|
||||
return yarn(['install', '--force', packageToInstall], packagePath)
|
||||
}
|
||||
|
||||
const removeDevDependencies = function (packageFolder) {
|
||||
const packagePath = pathToPackageJson(packageFolder)
|
||||
|
||||
console.log('removing devDependencies from %s', packagePath)
|
||||
|
||||
return fs.readJsonAsync(packagePath)
|
||||
.then((json) => {
|
||||
delete json.devDependencies
|
||||
|
||||
return fs.writeJsonAsync(packagePath, json, { spaces: 2 })
|
||||
})
|
||||
}
|
||||
|
||||
const retryGlobbing = function (pathToPackages, delay = 1000) {
|
||||
const retryGlob = () => {
|
||||
return glob(pathToPackages)
|
||||
.catch({ code: 'EMFILE' }, () => {
|
||||
// wait, then retry
|
||||
return Promise
|
||||
.delay(delay)
|
||||
.then(retryGlob)
|
||||
})
|
||||
}
|
||||
|
||||
return retryGlob()
|
||||
}
|
||||
|
||||
// installs all packages given a wildcard
|
||||
// pathToPackages would be something like "C:\projects\cypress\dist\win32\packages\*"
|
||||
const npmInstallAll = function (pathToPackages) {
|
||||
console.log(`npmInstallAll packages in ${pathToPackages}`)
|
||||
|
||||
const started = new Date()
|
||||
|
||||
const retryNpmInstall = function (pkg) {
|
||||
console.log('installing %s', pkg)
|
||||
console.log('NODE_ENV is %s', process.env.NODE_ENV)
|
||||
|
||||
// force installing only PRODUCTION dependencies
|
||||
// https://docs.npmjs.com/cli/install
|
||||
const npmInstall = _.partial(yarn, ['install', '--production'])
|
||||
|
||||
return npmInstall(pkg, { NODE_ENV: 'production' })
|
||||
.catch({ code: 'EMFILE' }, () => {
|
||||
return Promise
|
||||
.delay(1000)
|
||||
.then(() => {
|
||||
return retryNpmInstall(pkg)
|
||||
})
|
||||
}).catch((err) => {
|
||||
console.log(err, err.code)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
const printFolders = (folders) => {
|
||||
return console.log('found %s', pluralize('folder', folders.length, true))
|
||||
}
|
||||
|
||||
// only installs production dependencies
|
||||
return retryGlobbing(pathToPackages)
|
||||
.tap(printFolders)
|
||||
.mapSeries((packageFolder) => {
|
||||
return removeDevDependencies(packageFolder)
|
||||
.then(() => {
|
||||
return retryNpmInstall(packageFolder)
|
||||
})
|
||||
}).then(() => {
|
||||
const end = new Date()
|
||||
|
||||
return console.log('Finished NPM Installing', prettyMs(end - started))
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
runAllBuild,
|
||||
|
||||
copyAllToDist,
|
||||
|
||||
npmInstallAll,
|
||||
|
||||
runAllCleanJs,
|
||||
|
||||
forceNpmInstall,
|
||||
|
||||
replaceLocalNpmVersions,
|
||||
}
|
||||
|
||||
if (!module.parent) {
|
||||
console.log('demo force install')
|
||||
forceNpmInstall('packages/server', '@ffmpeg-installer/win32-x64')
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
// we wrap glob to handle EMFILE error
|
||||
import Promise from 'bluebird'
|
||||
import la from 'lazy-ass'
|
||||
import check from 'check-more-types'
|
||||
import execa from 'execa'
|
||||
import R from 'ramda'
|
||||
import debugLib from 'debug'
|
||||
|
||||
import externalUtils, { globby } from './3rd-party'
|
||||
|
||||
const debug = debugLib('cypress:binary')
|
||||
|
||||
const pathToPackageJson = function (packageFolder) {
|
||||
la(check.unemptyString(packageFolder), 'expected package path', packageFolder)
|
||||
|
||||
return path.join(packageFolder, 'package.json')
|
||||
}
|
||||
|
||||
const createCLIExecutable = (command) => {
|
||||
return function (args, cwd = undefined, env = {}) {
|
||||
const commandToExecute = `${command} ${args.join(' ')}`
|
||||
|
||||
console.log(commandToExecute)
|
||||
if (cwd) {
|
||||
console.log('in folder:', cwd)
|
||||
}
|
||||
|
||||
// la(check.maybe.string(cwd), 'invalid CWD string', cwd)
|
||||
|
||||
return execa(command, args, { stdio: 'inherit', cwd })
|
||||
// if everything is ok, resolve with nothing
|
||||
.then(R.always(undefined))
|
||||
.catch((result) => {
|
||||
const msg = `${commandToExecute} failed with exit code: ${result.code}`
|
||||
|
||||
throw new Error(msg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const yarn = createCLIExecutable('yarn')
|
||||
|
||||
export const runAllBuild = _.partial(yarn, ['lerna', 'run', 'build-prod', '--ignore', 'cli'])
|
||||
|
||||
export const runAllCleanJs = _.partial(yarn, ['lerna', 'run', 'clean-js', '--ignore', 'cli'])
|
||||
|
||||
export async function copyAllToDist (distDir: string) {
|
||||
await fs.ensureDir(distDir)
|
||||
|
||||
const started = new Date().valueOf()
|
||||
const globbed = await externalUtils.globby(['./packages/*', './npm/*'], {
|
||||
onlyFiles: false,
|
||||
})
|
||||
|
||||
for (const pkg of globbed) {
|
||||
// copies the package to dist
|
||||
// including the default paths
|
||||
// and any specified in package.json files
|
||||
const json = await fs.readJSON(pathToPackageJson(pkg))
|
||||
|
||||
// grab all the files that match "files" wildcards
|
||||
// but without all negated files ("!src/**/*.spec.js" for example)
|
||||
// and default included paths
|
||||
// and convert to relative paths
|
||||
const pkgFileMasks = [].concat(json.files || []).concat(json.main || [])
|
||||
|
||||
debug('for pkg %s have the following file masks %o', pkg, pkgFileMasks)
|
||||
const foundFileRelativeToPackageFolder = await externalUtils.globby(pkgFileMasks, {
|
||||
cwd: pkg, // search in the package folder
|
||||
absolute: false, // and return relative file paths
|
||||
followSymbolicLinks: false, // do not follow symlinks
|
||||
})
|
||||
|
||||
console.log(`Copying ${pkg} to ${path.join(distDir, pkg)}`)
|
||||
|
||||
// fs-extra concurrency tests (copyPackage / copyRelativePathToDist)
|
||||
// 1/1 41688
|
||||
// 1/5 42218
|
||||
// 1/10 42566
|
||||
// 2/1 45041
|
||||
// 2/2 43589
|
||||
// 3/3 51399
|
||||
|
||||
// cp -R concurrency tests
|
||||
// 1/1 65811
|
||||
for (const relativeFile of foundFileRelativeToPackageFolder) {
|
||||
const dest = path.join(distDir, pkg, relativeFile)
|
||||
|
||||
await fs.copy(path.join(pkg, relativeFile), dest, { recursive: true })
|
||||
}
|
||||
|
||||
try {
|
||||
// Strip out dev-dependencies & scripts for everything in /packages so we can yarn install in there
|
||||
await fs.writeJson(path.join(distDir, pkg, 'package.json'), _.omit(json, [
|
||||
'scripts',
|
||||
'devDependencies',
|
||||
'lint-staged',
|
||||
'engines',
|
||||
]), { spaces: 2 })
|
||||
} catch (e) {
|
||||
if (!e.message.includes('ENOENT')) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Finished Copying %dms', new Date().valueOf() - started)
|
||||
}
|
||||
|
||||
// replaces local npm version 0.0.0-development
|
||||
// with the path to the package
|
||||
// we need to do this instead of just changing the symlink (like we do for require('@packages/...'))
|
||||
// so the packages actually get installed to node_modules and work with peer dependencies
|
||||
export const replaceLocalNpmVersions = async function (basePath: string) {
|
||||
const visited = new Set<string>()
|
||||
|
||||
const pkgPaths = await globby('./packages/*/package.json', { cwd: basePath })
|
||||
|
||||
async function updatePackageJson (pkg: string) {
|
||||
const pkgJsonPath = path.join(basePath, pkg)
|
||||
|
||||
visited.add(pkgJsonPath)
|
||||
const json = await fs.readJson(pkgJsonPath)
|
||||
|
||||
const { dependencies } = json
|
||||
|
||||
if (dependencies) {
|
||||
let shouldWriteFile = false
|
||||
|
||||
for (const [depName, version] of Object.entries(dependencies)) {
|
||||
if (!depName.startsWith('@cypress/') || version !== '0.0.0-development') {
|
||||
continue
|
||||
}
|
||||
|
||||
const [, localPkg] = depName.split('/')
|
||||
|
||||
const localPkgJsonPath = path.join(basePath, 'npm', localPkg)
|
||||
|
||||
dependencies[`@cypress/${localPkg}`] = `file:${localPkgJsonPath}`
|
||||
if (!visited.has(localPkgJsonPath)) {
|
||||
await updatePackageJson(`./npm/${localPkg}/package.json`)
|
||||
}
|
||||
|
||||
shouldWriteFile = true
|
||||
}
|
||||
if (shouldWriteFile) {
|
||||
await fs.writeJson(pkgJsonPath, json, { spaces: 2 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(pkgPaths.map(updatePackageJson))
|
||||
|
||||
return Array.from(visited)
|
||||
}
|
||||
|
||||
export async function removeLocalNpmDirs (distPath: string, except: string[]) {
|
||||
const toRemove = await globby(`${distPath}/npm/*`, {
|
||||
ignore: except.map((e) => e.replace('/package.json', '')),
|
||||
onlyDirectories: true,
|
||||
})
|
||||
|
||||
for (const dir of toRemove) {
|
||||
await fs.remove(dir)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user