mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-04 05:29:45 -06:00
322 lines
8.7 KiB
CoffeeScript
322 lines
8.7 KiB
CoffeeScript
_ = require("lodash")
|
|
fse = require("fs-extra")
|
|
os = require("os")
|
|
del = require("del")
|
|
path = require("path")
|
|
cp = require("child_process")
|
|
gulp = require("gulp")
|
|
chalk = require("chalk")
|
|
Promise = require("bluebird")
|
|
gulpDebug = require("gulp-debug")
|
|
gulpCoffee = require("gulp-coffee")
|
|
gulpTypeScript = require("gulp-typescript")
|
|
pluralize = require("pluralize")
|
|
vinylPaths = require("vinyl-paths")
|
|
coffee = require("@packages/coffee")
|
|
execa = require("execa")
|
|
electron = require("@packages/electron")
|
|
debug = require("debug")("cypress:binary")
|
|
R = require("ramda")
|
|
la = require("lazy-ass")
|
|
check = require("check-more-types")
|
|
humanInterval = require("human-interval")
|
|
|
|
meta = require("./meta")
|
|
smoke = require("./smoke")
|
|
packages = require("./util/packages")
|
|
xvfb = require("../../cli/lib/exec/xvfb")
|
|
linkPackages = require('../link-packages')
|
|
|
|
rootPackage = require("@packages/root")
|
|
|
|
fs = Promise.promisifyAll(fse)
|
|
|
|
logger = (msg, platform) ->
|
|
time = new Date()
|
|
timeStamp = time.toLocaleTimeString()
|
|
console.log(timeStamp, chalk.yellow(msg), chalk.blue(platform))
|
|
|
|
logBuiltAllPackages = () ->
|
|
console.log("built all packages")
|
|
|
|
logBuiltAllJs = () ->
|
|
console.log("built all JS")
|
|
|
|
# can pass options to better control the build
|
|
# for example
|
|
# skipClean - do not delete "dist" folder before build
|
|
buildCypressApp = (platform, version, options = {}) ->
|
|
la(check.unemptyString(version), "missing version to build", version)
|
|
|
|
distDir = _.partial(meta.distDir, platform)
|
|
buildDir = _.partial(meta.buildDir, platform)
|
|
buildAppDir = _.partial(meta.buildAppDir, platform)
|
|
|
|
log = _.partialRight(logger, platform)
|
|
|
|
testVersion = (folderNameFn) -> () ->
|
|
dir = folderNameFn()
|
|
la(check.unemptyString(dir), "missing folder for platform", platform)
|
|
console.log("testing package version in folder", dir)
|
|
execa("node", ["index.js", "--version"], {
|
|
cwd: dir
|
|
}).then (result) ->
|
|
console.log('built version', result.stdout)
|
|
la(check.unemptyString(result.stdout), 'missing output', result)
|
|
la(result.stdout == version, "different version reported",
|
|
result.stdout, "from input version to build", version)
|
|
|
|
canBuildInDocker = ->
|
|
platform is "linux" and os.platform() is "darwin"
|
|
|
|
badPlatformMismatch = ->
|
|
console.error("⛔️ cannot build #{platform} from #{os.platform()}")
|
|
console.error("⛔️ should use matching platform to build it")
|
|
console.error("program arguments")
|
|
console.error(process.argv)
|
|
|
|
checkPlatform = ->
|
|
log("#checkPlatform")
|
|
if platform is os.platform()
|
|
return
|
|
|
|
console.log("trying to build #{platform} from #{os.platform()}")
|
|
if platform is "linux" and os.platform() is "darwin"
|
|
console.log("npm run binary-build-linux")
|
|
Promise.reject(new Error("Build platform mismatch"))
|
|
|
|
cleanupPlatform = ->
|
|
log("#cleanupPlatform")
|
|
|
|
if options.skipClean
|
|
log("skipClean")
|
|
return
|
|
|
|
cleanup = ->
|
|
dir = distDir()
|
|
la(check.unemptyString(dir), "empty dist dir", dir, "for platform", platform)
|
|
fs.removeAsync(distDir())
|
|
|
|
cleanup()
|
|
.catch(cleanup)
|
|
|
|
buildPackages = ->
|
|
log("#buildPackages")
|
|
|
|
packages.runAllBuild()
|
|
# Promise.resolve()
|
|
.then(R.tap(logBuiltAllPackages))
|
|
.then(packages.runAllBuildJs)
|
|
.then(R.tap(logBuiltAllJs))
|
|
|
|
copyPackages = ->
|
|
log("#copyPackages")
|
|
|
|
packages.copyAllToDist(distDir())
|
|
|
|
npmInstallPackages = ->
|
|
log("#npmInstallPackages")
|
|
|
|
packages.npmInstallAll(distDir("packages", "*"), options)
|
|
|
|
createRootPackage = ->
|
|
log("#createRootPackage #{platform} #{version}")
|
|
|
|
fs.outputJsonAsync(distDir("package.json"), {
|
|
name: "cypress"
|
|
productName: "Cypress",
|
|
description: rootPackage.description
|
|
version: version
|
|
main: "index.js"
|
|
scripts: {}
|
|
env: "production"
|
|
})
|
|
.then =>
|
|
str = """
|
|
process.env.CYPRESS_ENV = process.env.CYPRESS_ENV || 'production'
|
|
require('./packages/server')
|
|
"""
|
|
|
|
fs.outputFileAsync(distDir("index.js"), str)
|
|
|
|
copyPackageProxies = (destinationFolder) ->
|
|
() ->
|
|
log("#copyPackageProxies")
|
|
la(check.fn(destinationFolder),
|
|
"missing destination folder function", destinationFolder)
|
|
dest = destinationFolder("node_modules", "@packages")
|
|
la(check.unemptyString(dest), "missing destination folder", dest)
|
|
source = path.join(process.cwd(), "node_modules", "@packages")
|
|
fs.unlinkAsync(dest).catch(_.noop)
|
|
.then(() ->
|
|
console.log("Copying #{source} to #{dest}")
|
|
fs.copyAsync(source, dest)
|
|
)
|
|
|
|
removeTypeScript = ->
|
|
## remove the .ts files in our packages
|
|
log("#removeTypeScript")
|
|
del([
|
|
## include ts files of packages
|
|
distDir("**", "*.ts")
|
|
|
|
## except those in node_modules
|
|
"!" + distDir("**", "node_modules", "**", "*.ts")
|
|
])
|
|
.then (paths) ->
|
|
console.log(
|
|
"deleted %d TS %s",
|
|
paths.length,
|
|
pluralize("file", paths.length)
|
|
)
|
|
console.log(paths)
|
|
|
|
cleanJs = ->
|
|
log("#cleanJs")
|
|
|
|
packages.runAllCleanJs()
|
|
|
|
convertCoffeeToJs = ->
|
|
log("#convertCoffeeToJs")
|
|
|
|
## grab everything in src
|
|
## convert to js
|
|
new Promise (resolve, reject) =>
|
|
gulp.src([
|
|
## include coffee files of packages
|
|
distDir("**", "*.coffee")
|
|
|
|
## except those in node_modules
|
|
"!" + distDir("**", "node_modules", "**", "*.coffee")
|
|
])
|
|
.pipe vinylPaths(del)
|
|
.pipe(gulpDebug())
|
|
.pipe gulpCoffee({
|
|
coffee: coffee
|
|
})
|
|
.pipe gulp.dest(distDir())
|
|
.on("end", resolve)
|
|
.on("error", reject)
|
|
|
|
elBuilder = ->
|
|
log("#elBuilder")
|
|
dir = distDir()
|
|
dist = buildDir()
|
|
console.log("from #{dir}")
|
|
console.log("into #{dist}")
|
|
|
|
electron.install({
|
|
dir
|
|
dist
|
|
platform
|
|
appVersion: version
|
|
})
|
|
|
|
removeDevElectronApp = ->
|
|
log("#removeDevElectronApp")
|
|
# when we copy packages/electron, we get the "dist" folder with
|
|
# empty Electron app, symlinked to our server folder
|
|
# in production build, we do not need this link, and it
|
|
# would not work anyway with code signing
|
|
|
|
# hint: you can see all symlinks in the build folder
|
|
# using "find build/darwin/Cypress.app/ -type l -ls"
|
|
console.log("platform", platform)
|
|
electronDistFolder = meta.buildAppDir(platform, "packages", "electron", "dist")
|
|
la(check.unemptyString(electronDistFolder),
|
|
"empty electron dist folder for platform", platform)
|
|
|
|
console.log("Removing unnecessary folder '#{electronDistFolder}'")
|
|
fs.removeAsync(electronDistFolder).catch(_.noop)
|
|
|
|
runSmokeTests = ->
|
|
log("#runSmokeTests")
|
|
|
|
run = ->
|
|
smoke.test(meta.buildAppExecutable(platform))
|
|
|
|
if xvfb.isNeeded()
|
|
xvfb.start()
|
|
.then(run)
|
|
.finally(xvfb.stop)
|
|
else
|
|
run()
|
|
|
|
codeSign = Promise.method ->
|
|
if platform isnt "darwin"
|
|
# do we need to code sign on Windows?
|
|
return
|
|
|
|
appFolder = meta.zipDir(platform)
|
|
fiveMinutes = humanInterval("5 minutes")
|
|
|
|
execaBuild = Promise.method ->
|
|
log("#codeSign #{appFolder}")
|
|
|
|
execa('build', ["--publish", "never", "--prepackaged", appFolder], {
|
|
stdio: "inherit"
|
|
})
|
|
.catch (err) ->
|
|
## ignore canceled errors
|
|
if err.isCanceled
|
|
return
|
|
|
|
throw err
|
|
|
|
## try to build and if we timeout in 5 minutes
|
|
## then try again - which sometimes happens in
|
|
## circle CI
|
|
b = execaBuild()
|
|
|
|
b
|
|
.timeout(fiveMinutes)
|
|
.catch Promise.TimeoutError, (err) ->
|
|
console.log(
|
|
chalk.red("timed out signing binary after #{fiveMinutes}ms. retrying...")
|
|
)
|
|
|
|
b.cancel()
|
|
|
|
execaBuild()
|
|
|
|
|
|
verifyAppCanOpen = ->
|
|
if (platform != "darwin") then return Promise.resolve()
|
|
|
|
appFolder = meta.zipDir(platform)
|
|
log("#verifyAppCanOpen #{appFolder}")
|
|
new Promise (resolve, reject) =>
|
|
args = ["-a", "-vvvv", appFolder]
|
|
debug("cmd: spctl #{args.join(' ')}")
|
|
sp = cp.spawn "spctl", args, {stdio: "inherit"}
|
|
sp.on "exit", (code) ->
|
|
if code is 0
|
|
resolve()
|
|
else
|
|
reject new Error("Verifying App via GateKeeper failed")
|
|
|
|
Promise.resolve()
|
|
.then(checkPlatform)
|
|
.then(cleanupPlatform)
|
|
.then(buildPackages)
|
|
.then(copyPackages)
|
|
.then(npmInstallPackages)
|
|
.then(createRootPackage)
|
|
.then(copyPackageProxies(distDir))
|
|
.then(convertCoffeeToJs)
|
|
.then(removeTypeScript)
|
|
.then(cleanJs)
|
|
.then(testVersion(distDir))
|
|
.then(elBuilder) # should we delete everything in the buildDir()?
|
|
.then(removeDevElectronApp)
|
|
.then(copyPackageProxies(buildAppDir))
|
|
.then(testVersion(buildAppDir))
|
|
.then(runSmokeTests)
|
|
.then(codeSign) ## codesign after running smoke tests due to changing .cy
|
|
.then(verifyAppCanOpen)
|
|
.return({
|
|
buildDir: buildDir()
|
|
})
|
|
|
|
module.exports = buildCypressApp
|