deploy: rename deploy -> binary folder

This commit is contained in:
Brian Mann
2017-06-29 14:06:45 -04:00
parent 93ba23fe2f
commit be211af10e
13 changed files with 1 additions and 1 deletions

162
scripts/binary/ask.coffee Normal file
View File

@@ -0,0 +1,162 @@
_ = require("lodash")
fs = require("fs-extra")
glob = require("glob")
Promise = require("bluebird")
inquirer = require("inquirer")
glob = Promise.promisify(glob)
prompt = (questions) ->
Promise.resolve(inquirer.prompt(questions))
fs = Promise.promisifyAll(fs)
getZipFile = ->
[{
name: "zipFile"
type: "string"
message: "Which zip file should we upload?"
}]
getPlatformQuestion = ->
[{
name: "platform"
type: "list"
message: "Which OS should we deploy?"
choices: [{
name: "Mac"
value: "darwin"
},{
name: "Linux"
value: "linux"
}]
}]
getQuestions = (version) ->
[{
name: "publish"
type: "list"
message: "Publish a new version? (currently: #{version})"
choices: [{
name: "Yes: set a new version and deploy new version."
value: true
},{
name: "No: just override the current deployed version."
value: false
}]
},{
name: "version"
type: "input"
message: "Bump version to...? (currently: #{version})"
default: ->
a = version.split(".")
v = a[a.length - 1]
v = Number(v) + 1
a.splice(a.length - 1, 1, v)
a.join(".")
when: (answers) ->
answers.publish
}]
getReleases = (releases) ->
[{
name: "release"
type: "list"
message: "Release which version?"
choices: _.map releases, (r) ->
{
name: r
value: r
}
}]
getVersions = (releases) ->
[{
name: "version"
type: "list"
message: "Bump to which version?"
choices: _.map releases, (r) ->
{
name: r
value: r
}
}]
getBumpTasks = ->
[{
name: "task"
type: "list"
message: "Which bump task?"
choices: [{
name: "Bump Cypress Version for all CI providers"
value: "version"
},{
name: "Run All Projects for all CI providers"
value: "run"
}]
}]
deployNewVersion = ->
fs.readJsonAsync("./package.json")
.then (json) =>
prompt(getQuestions(json.version))
.then (answers) ->
## set the new version if we're publishing!
## update our own local package.json as well
if answers.publish
# @updateLocalPackageJson(answers.version, json).then ->
answers.version
else
json.version
whichZipFile = ->
prompt(getZipFile())
.get("zipFile")
whichVersion = (distDir) ->
## realpath returns the absolute full path
glob("*/package.json", {cwd: distDir, realpath: true})
.map (pkg) =>
fs.readJsonAsync(pkg)
.get("version")
.then (versions) =>
versions = _.uniq(versions)
prompt(getVersions(versions))
.get("version")
whichRelease = (distDir) ->
## realpath returns the absolute full path
glob("*/package.json", {cwd: distDir, realpath: true})
.map (pkg) =>
fs.readJsonAsync(pkg)
.get("version")
.then (versions) =>
versions = _.uniq(versions)
prompt(getReleases(versions))
.get("release")
whichPlatform = ->
prompt(getPlatformQuestion())
.get("platform")
whichBumpTask = ->
prompt(getBumpTasks())
.get("task")
module.exports = {
getZipFile
getPlatformQuestion
getQuestions
getReleases
getVersions
getBumpTasks
deployNewVersion
whichZipFile
whichVersion
whichRelease
whichPlatform
whichBumpTask
}

452
scripts/binary/base.coffee Normal file
View File

@@ -0,0 +1,452 @@
_ = require("lodash")
gulpCoffee = require("gulp-coffee")
fs = require("fs-extra")
cp = require("child_process")
path = require("path")
gulp = require("gulp")
glob = require("glob")
chalk = require("chalk")
expect = require("chai").expect
Promise = require("bluebird")
obfuscator = require("obfuscator")
# runSequence = require("run-sequence")
cypressElectron = require("@packages/electron")
log = require("./log")
meta = require("./meta")
pkg = require("../../package.json")
konfig = require("@packages/server/lib/konfig")
appData = require("@packages/server/lib/util/app_data")
Fixtures = require("@packages/server/test/support/helpers/fixtures")
# pkgr = Promise.promisify(pkgr)
fs = Promise.promisifyAll(fs)
glob = Promise.promisify(glob)
zipName = "cypress.zip"
DEFAULT_PATHS = "node_modules package.json".split(" ")
pathToPackageJson = (pkg) ->
path.join(pkg, "package.json")
class Base
constructor: (os, @options = {}) ->
_.defaults @options, {
version: null
}
@zipName = zipName
@osName = os
@uploadOsName = @getUploadNameByOs(os)
buildPathToAppFolder: ->
meta.buildDir(@osName)
buildPathToZip: ->
path.join @buildPathToAppFolder(), @zipName
getUploadNameByOs: (os) ->
{
darwin: "osx64"
linux: "linux64"
win32: "win64"
}[os]
getVersion: ->
@options.version ? fs.readJsonSync(@distDir("package.json")).version
copyPackages: ->
@log("#copyPackages")
dist = @distDir()
copy = (src, dest) =>
dest ?= src
dest = @distDir(dest.slice(1))
fs.copyAsync(src, dest)
copyRelativePathToDist = (relative) ->
dest = path.join(dist, relative)
console.log(relative, "->", dest)
# copy = ->
# new Promise (resolve, reject) ->
# cp.spawn("cp", ["-R", relative, dest], {stdio: "inherit"})
# .on "error", reject
# .on "exit", resolve
# if relative.includes(".")
# copy()
# else
# fs.ensureDirAsync(dest)
# .then(copy)
fs.copyAsync(relative, dest)
copyPackage = (pkg) ->
## copies the package to dist
## including the default paths
## and any specified in package.json files
fs.readJsonAsync(pathToPackageJson(pkg))
.then (json) ->
## grab all the files
## and default included paths
## and convert to relative paths
DEFAULT_PATHS
.concat(json.files or [])
.map (file) ->
path.join(pkg, file)
.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
started = new Date()
fs
.removeAsync(dist)
.bind(@)
.then ->
fs.ensureDirAsync(dist)
.then ->
glob("./packages/*")
.map(copyPackage, {concurrency: 1})
.then ->
console.log("Finished Copying", new Date() - started)
prunePackages: ->
pathToDistPackages = @distDir("packages", "*")
## 1,060,495,784 bytes (1.54 GB on disk) for 179,156 items
## 313,416,512 bytes (376.6 MB on disk) for 23,576 items
prune = (pkg) ->
console.log("prune", pkg)
new Promise (resolve, reject) ->
cp.spawn("npm", ["prune", "--production"], {
cwd: pkg
stdio: "inherit"
})
.on("error", reject)
.on("exit", (code) ->
if code is 0
resolve()
else
reject(new Error("'npm prune --production' on #{pkg} failed with exit code: #{code}"))
)
## prunes out all of the devDependencies
## from what was copied
glob(pathToDistPackages)
.map(prune)
convertCoffeeToJs: ->
@log("#convertCoffeeToJs")
## grab everything in src
## convert to js
new Promise (resolve, reject) =>
gulp.src(@distDir("lib", "**", "*.coffee"))
.pipe gulpCoffee()
.pipe gulp.dest(@distDir("lib"))
.on("end", resolve)
.on("error", reject)
distDir: (args...) ->
args = _.compact [meta.distDir, @osName, args...]
path.join args...
obfuscate: ->
@log("#obfuscate")
## obfuscate all the js files
new Promise (resolve, reject) =>
## grab all of the js files
files = glob.sync @distDir("src/**/*.js")
## root is src
## entry is cypress.js
## files are all the js files
opts = {root: @distDir("src"), entry: @distDir("src/index.js"), files: files}
obfuscator.concatenate opts, (err, obfuscated) =>
return reject(err) if err
## move to lib
fs.writeFileSync(@distDir("index.js"), obfuscated)
resolve(obfuscated)
cleanupSrc: ->
@log("#cleanupSrc")
fs.removeAsync @distDir("/src")
cleanupPlatform: ->
@log("#cleanupPlatform")
cleanup = =>
Promise.all([
fs.removeAsync path.join(meta.distDir, @osName)
fs.removeAsync path.join(meta.buildDir, @osName)
])
cleanup().catch(cleanup)
symlinkPackages: ->
@log("#symlinkPackages")
dist = @distDir()
pathToPackages = path.join('node_modules', '@')
pathToDistPackages = @distDir("packages", "*")
symlink = (pkg) ->
# console.log(pkg, dist)
## strip off the initial './'
## ./packages/foo -> node_modules/@packages/foo
dest = path.join(dist, "node_modules", "@packages", path.basename(pkg))
fs.ensureSymlinkAsync(pkg, dest)
glob(pathToDistPackages)
.map(symlink)
# // glob all of the names of packages
# glob('./packages/*')
# .map((folder) => {
# // strip off the initial './'
# // ./packages/foo -> node_modules/@packages/foo
# const dest = pathToPackages + folder.slice(2)
#
# console.log('symlinking', folder, '->', dest)
#
# return fs.ensureSymlinkAsync(folder, dest)
# })
## add tests around this method
createRootPackage: ->
version = @options.version
@log("#createRootPackage #{version}")
fs.outputJsonAsync(@distDir("package.json"), {
name: "cypress"
productName: "Cypress",
version: version
main: "index.js"
scripts: {}
env: "production"
})
.then =>
str = "require('./packages/server')"
fs.outputFileAsync(@distDir("index.js"), str)
npmInstall: ->
@log("#npmInstall")
new Promise (resolve, reject) =>
attempts = 0
npmInstall = =>
attempts += 1
cp.exec "npm install --production", {cwd: @distDir()}, (err, stdout, stderr) ->
if err
if attempts is 3
fs.writeFileSync("./npm-install.log", stderr)
return reject(err)
console.log chalk.red("'npm install' failed, retrying")
return npmInstall()
resolve()
npmInstall()
elBuilder: ->
@log("#elBuilder")
fs.readJsonAsync(@distDir("package.json"))
.then (json) =>
cypressElectron.install({
dir: @distDir()
dist: @buildPathForElectron()
platform: @osName
"app-version": json.version
})
uploadFixtureToS3: ->
@log("#uploadFixtureToS3")
@uploadToS3("osx64", "fixture")
getManifest: ->
requestPromise(konfig("desktop_manifest_url")).then (resp) ->
console.log resp
fixture: (cb) ->
@dist()
.then(@uploadFixtureToS3)
.then(@cleanupPlatform)
.then ->
@log("Fixture Complete!", "green")
cb?()
.catch (err) ->
@log("Fixture Failed!", "red")
console.log err
log: ->
log.apply(@, arguments)
buildPackages: ->
@log("#buildPackages")
new Promise (resolve, reject) ->
console.log(process.cwd())
## build all the packages except for
## cli and docs
cp.spawn("npm", ["run", "all", "build", "--", "--skip-packages", "cli,docs"], { stdio: "inherit" })
.on "error", reject
.on "exit", (code) ->
if code is 0
resolve()
else
reject(new Error("'npm run build' failed with exit code: #{code}"))
_runProjectTest: ->
@log("#runProjectTest")
Fixtures.scaffold()
e2e = Fixtures.projectPath("e2e")
runProjectTest = =>
new Promise (resolve, reject) =>
env = _.omit(process.env, "CYPRESS_ENV")
sp = cp.spawn @buildPathToAppExecutable(), ["--run-project=#{e2e}", "--spec=cypress/integration/simple_passing_spec.coffee"], {stdio: "inherit", env: env}
sp.on "exit", (code) ->
if code is 0
resolve()
else
reject(new Error("running project tests failed with: '#{code}' errors."))
runProjectTest()
.then ->
Fixtures.remove()
_runFailingProjectTest: ->
@log("#runFailingProjectTest")
Fixtures.scaffold()
e2e = Fixtures.projectPath("e2e")
verifyScreenshots = =>
screenshot1 = path.join(e2e, "cypress", "screenshots", "simple failing spec -- fails1.png")
screenshot2 = path.join(e2e, "cypress", "screenshots", "simple failing spec -- fails2.png")
Promise.all([
fs.statAsync(screenshot1)
fs.statAsync(screenshot2)
])
runProjectTest = =>
new Promise (resolve, reject) =>
env = _.omit(process.env, "CYPRESS_ENV")
sp = cp.spawn @buildPathToAppExecutable(), ["--run-project=#{e2e}", "--spec=cypress/integration/simple_failing_spec.coffee"], {stdio: "inherit", env: env}
sp.on "exit", (code) ->
if code is 2
resolve()
else
reject(new Error("running project tests failed with: '#{code}' errors."))
runProjectTest()
.then(verifyScreenshots)
.then ->
Fixtures.remove()
_runSmokeTest: ->
@log("#runSmokeTest")
smokeTest = =>
new Promise (resolve, reject) =>
rand = "" + Math.random()
executable = @buildPathToAppExecutable()
console.log("executable path #{executable}")
cp.exec "#{executable} --smoke-test --ping=#{rand}", (err, stdout, stderr) ->
stdout = stdout.replace(/\s/, "")
if err
console.error("smoke test failed with error %s", err.message)
return reject(err)
if stdout isnt rand
throw new Error("Stdout: '#{stdout}' did not match the random number: '#{rand}'")
else
console.log("smokeTest passes")
resolve()
verifyAppPackage = =>
new Promise (resolve, reject) =>
console.log("verifyAppPackage")
cp.exec "#{@buildPathToAppExecutable()} --return-pkg", (err, stdout, stderr) ->
return reject(err) if err
stdout = JSON.parse(stdout)
try
expect(stdout.env).to.eq("production")
catch err
console.error("failed to verify app via --return-pkg")
console.log(stdout)
return reject(err)
console.log("app verified")
resolve()
smokeTest()
# TODO refactor verifying app package
# .then(verifyAppPackage)
cleanupCy: ->
appData.removeSymlink()
build: ->
Promise
.bind(@)
# .then(@cleanupPlatform)
# .then(@buildPackages)
# .then(@copyPackages)
# .then(@prunePackages)
.then(@createRootPackage)
.then(@symlinkPackages)
# .then(@convertCoffeeToJs)
# .then(@obfuscate)
# .then(@cleanupSrc)
# .then(@npmInstall)
# .then(@npmInstall)
# .then(@elBuilder)
# .then(@runSmokeTest)
# .then(@runProjectTest)
# .then(@runFailingProjectTest)
# .then(@cleanupCy)
# .then(@codeSign) ## codesign after running smoke tests due to changing .cy
# .then(@verifyAppCanOpen)
.return(@)
module.exports = Base

200
scripts/binary/build.coffee Normal file
View File

@@ -0,0 +1,200 @@
_ = require("lodash")
fs = require("fs-extra")
del = require("del")
path = require("path")
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")
electron = require("@packages/electron")
meta = require("./meta")
packages = require("./util/packages")
Darwin = require("./darwin")
Linux = require("./linux")
fs = Promise.promisifyAll(fs)
logger = (msg) ->
console.log(chalk.yellow(msg), chalk.bgWhite(chalk.black(platform)))
runDarwinSmokeTest = ->
darwin = new Darwin("darwin")
darwin.runSmokeTest()
runLinuxSmokeTest = ->
linux = new Linux("linux")
linux.runSmokeTest()
smokeTests = {
darwin: runDarwinSmokeTest,
linux: runLinuxSmokeTest
}
module.exports = (platform, version) ->
distDir = _.partial(meta.distDir, platform)
buildDir = _.partial(meta.buildDir, platform)
buildAppDir = _.partial(meta.buildAppDir, platform)
log = _.partialRight(logger, platform)
cleanupPlatform = ->
log("#cleanupPlatform")
cleanup = =>
fs.removeAsync(distDir())
cleanup()
.catch(cleanup)
buildPackages = ->
log("#buildPackages")
packages.runAllBuild()
.then(packages.runAllBuildJs)
copyPackages = ->
log("#copyPackages")
packages.copyAllToDist(distDir())
npmInstallPackages = ->
log("#npmInstallPackages")
packages.npmInstallAll(distDir("packages", "*"))
createRootPackage = ->
log("#createRootPackage", platform, version)
fs.outputJsonAsync(distDir("package.json"), {
name: "cypress"
productName: "Cypress",
version: version
main: "index.js"
scripts: {}
env: "production"
})
.then =>
str = """
process.env.CYPRESS_ENV = 'production'
require('./packages/server')
"""
fs.outputFileAsync(distDir("index.js"), str)
symlinkPackages = ->
log("#symlinkPackages")
packages.symlinkAll(distDir("packages", "*", "package.json"), distDir)
removeTypeScript = ->
## remove the .ts files in our packages
log("#removeTypeScript")
del([
## include coffee 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)
symlinkBuildPackages = ->
log("#symlinkBuildPackages")
wildCard = buildAppDir("packages", "*", "package.json")
console.log("packages", wildCard)
packages.symlinkAll(
wildCard,
buildAppDir
)
symlinkDistPackages = ->
log("#symlinkDistPackages")
packages.symlinkAll(
distDir("packages", "*", "package.json"),
distDir
)
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
"app-version": version
})
runSmokeTest = ->
log("#runSmokeTest")
# console.log("skipping smoke test for now")
smokeTest = smokeTests[platform]
smokeTest()
Promise.resolve()
.then(cleanupPlatform)
.then(buildPackages)
.then(copyPackages)
.then(npmInstallPackages)
.then(createRootPackage)
.then(symlinkPackages)
.then(convertCoffeeToJs)
.then(removeTypeScript)
.then(cleanJs)
.then(symlinkDistPackages)
.then(elBuilder)
.then(symlinkBuildPackages)
.then(runSmokeTest)
# older build steps
# .then(@runProjectTest)
# .then(@runFailingProjectTest)
# .then(@cleanupCy)
# .then(@codeSign) ## codesign after running smoke tests due to changing .cy
# .then(@verifyAppCanOpen)
.return({
buildDir: buildDir()
})

View File

@@ -0,0 +1,67 @@
_ = require("lodash")
fs = require("fs-extra")
Promise = require("bluebird")
bumpercar = require("@cypress/bumpercar")
path = require("path")
fs = Promise.promisifyAll(fs)
PROVIDERS = {
circle: [
"cypress-io/cypress-dashboard"
"cypress-io/cypress-core-example"
"cypress-io/cypress-core-desktop-gui"
"cypress-io/cypress-example-kitchensink"
"cypress-io/cypress-example-todomvc"
"cypress-io/cypress-example-piechopper"
"cypress-io/cypress-example-recipes"
"cypress-io/cypress-example-node-versions"
"cypress-io/cypress-example-module-api"
"cypress-io/cypress-test-ci-environments"
]
travis: [
# "cypress-io/cypress-dashboard"
"cypress-io/cypress-core-example"
"cypress-io/cypress-core-desktop-gui"
"cypress-io/cypress-example-kitchensink"
"cypress-io/cypress-example-todomvc"
"cypress-io/cypress-example-piechopper"
"cypress-io/cypress-example-recipes"
]
}
awaitEachProjectAndProvider = (fn) ->
ciJson = path.join(__dirname, "support/ci.json")
creds = fs.readJsonSync(ciJson, "utf8")
fs.readJsonAsync(ciJson)
.then (creds) ->
## configure a new Bumpercar
car = bumpercar.create({
providers: {
travis: {
githubToken: creds.githubToken
}
circle: {
circleToken: creds.circleToken
}
}
})
.then ->
_.map PROVIDERS, (projects, provider) ->
Promise.map projects, (project) ->
fn(project, provider)
.all()
module.exports = {
version: (version) ->
awaitEachProjectAndProvider (project, provider) ->
car.updateProjectEnv(project, provider, {
CYPRESS_VERSION: version
})
run: ->
awaitEachProjectAndProvider (project, provider) ->
car.runProject(project, provider)
}

View File

@@ -0,0 +1,59 @@
fs = require("fs-extra")
cp = require("child_process")
path = require("path")
sign = require("electron-osx-sign")
plist = require("plist")
Promise = require("bluebird")
meta = require("./meta")
Base = require("./base")
sign = Promise.promisify(sign)
fs = Promise.promisifyAll(fs)
class Darwin extends Base
buildPathForElectron: ->
@buildPathToAppFolder()
buildPathToApp: ->
path.join @buildPathToAppFolder(), "Cypress.app"
buildPathToAppExecutable: ->
path.join @buildPathToApp(), "Contents", "MacOS", "Cypress"
buildPathToAppResources: ->
path.join @buildPathToApp(), "Contents", "Resources", "app"
runSmokeTest: ->
@_runSmokeTest()
runProjectTest: ->
@_runProjectTest()
runFailingProjectTest: ->
@_runFailingProjectTest()
codeSign: ->
@log("#codeSign")
sign({
app: @buildPathToApp()
platform: "darwin"
verbose: true
})
verifyAppCanOpen: ->
@log("#verifyAppCanOpen")
new Promise (resolve, reject) =>
sp = cp.spawn "spctl", ["-a", "-vvvv", @buildPathToApp()], {stdio: "inherit"}
sp.on "exit", (code) ->
if code is 0
resolve()
else
reject new Error("Verifying App via GateKeeper failed")
deploy: ->
@build()
.return(@)
module.exports = Darwin

162
scripts/binary/index.coffee Normal file
View File

@@ -0,0 +1,162 @@
## store the cwd
cwd = process.cwd()
path = require("path")
_ = require("lodash")
os = require("os")
chalk = require("chalk")
Promise = require("bluebird")
minimist = require("minimist")
la = require("lazy-ass")
check = require("check-more-types")
zip = require("./zip")
ask = require("./ask")
bump = require("./bump")
meta = require("./meta")
build = require("./build")
upload = require("./upload")
Base = require("./base")
Linux = require("./linux")
Darwin = require("./darwin")
success = (str) ->
console.log chalk.bgGreen(" " + chalk.black(str) + " ")
fail = (str) ->
console.log chalk.bgRed(" " + chalk.black(str) + " ")
zippedFilename = (platform) ->
# TODO use .tar.gz for linux archive. For now to preserve
# same file format as before use .zip
if platform is "linux" then "cypress.zip" else "cypress.zip"
# goes through the list of properties and asks relevant question
# resolves with all relevant options set
# if the property already exists, skips the question
askMissingOptions = (properties = []) ->
return (options = {}) ->
questions = {
platform: ask.whichPlatform,
version: ask.deployNewVersion,
# note: zip file might not be absolute
zip: ask.whichZipFile
}
reducer = (memo, property) ->
if (check.has(memo, property)) then return memo
question = questions[property]
if (!check.fn(question)) then return memo
la(check.fn(question), "cannot find question for property", property)
question(memo[property])
.then (answer) ->
memo[property] = answer
memo
Promise.reduce(properties, reducer, options)
## hack for @packages/server modifying cwd
process.chdir(cwd)
deploy = {
meta: meta
Base: Base
Darwin: Darwin
Linux: Linux
# getPlatform: (platform, options) ->
# platform ?= os.platform()
#
# Platform = @[platform.slice(0, 1).toUpperCase() + platform.slice(1)]
#
# throw new Error("Platform: '#{platform}' not found") if not Platform
#
# options ?= @parseOptions(process.argv.slice(2))
#
# (new Platform(platform, options))
parseOptions: (argv) ->
opts = minimist(argv)
opts.runTests = false if opts["skip-tests"]
opts
bump: ->
ask.whichBumpTask()
.then (task) ->
switch task
when "run"
bump.run()
when "version"
ask.whichVersion(meta.distDir)
.then (v) ->
bump.version(v)
release: ->
## read off the argv
options = @parseOptions(process.argv)
release = (version) =>
upload.s3Manifest(version)
.then ->
success("Release Complete")
.catch (err) ->
fail("Release Failed")
reject(err)
if v = options.version
release(v)
else
ask.whichRelease(meta.distDir)
.then(release)
build: (options) ->
console.log('#build')
if !options then options = @parseOptions(process.argv)
askMissingOptions(['version', 'platform'])(options)
.then () ->
build(options.platform, options.version)
zip: (options) ->
console.log('#zip')
if !options then options = @parseOptions(process.argv)
askMissingOptions(['platform'])(options)
.then (options) ->
zipDir = meta.zipDir(options.platform)
options.zip = path.resolve(zippedFilename(options.platform))
zip.ditto(zipDir, options.zip)
upload: (options) ->
console.log('#upload')
if !options then options = @parseOptions(process.argv)
askMissingOptions(['version', 'platform', 'zip'])(options)
.then (options) ->
la(check.unemptyString(options.zip),
"missing zipped filename", options)
options.zip = path.resolve(options.zip)
options
.then (options) ->
console.log("Need to upload file %s", options.zip)
console.log("for platform %s version %s",
options.platform, options.version)
upload.toS3({
zipFile: options.zip,
version: options.version,
platform: options.platform
})
# goes through the entire pipeline:
# - build
# - zip
# - upload
deploy: ->
options = @parseOptions(process.argv)
askMissingOptions(['version', 'platform'])(options)
.then(build)
.then(() => @zip(options))
# assumes options.zip contains the zipped filename
.then(upload)
}
module.exports = _.bindAll(deploy, _.functions(deploy))

118
scripts/binary/linux.coffee Normal file
View File

@@ -0,0 +1,118 @@
fs = require("fs")
cp = require("child_process")
path = require("path")
Xvfb = require("xvfb")
chalk = require("chalk")
vagrant = require("vagrant")
Promise = require("bluebird")
Base = require("./base")
fs = Promise.promisifyAll(fs)
vagrant.debug = true
["rsync", "rsync-auto", "rsync-back"].forEach (cmd) ->
vagrant[cmd] = vagrant._runWithArgs(cmd)
class Linux extends Base
buildPathForElectron: ->
@buildPathToApp()
buildPathToApp: ->
path.join @buildPathToAppFolder() #, "Cypress"
buildPathToAppExecutable: ->
path.join @buildPathToApp(), "Cypress"
buildPathToAppResources: ->
path.join @buildPathToApp(), "resources", "app"
codeSign: ->
Promise.resolve()
runProjectTest: ->
@_runProjectTest()
.catch (err) =>
@tryXvfb(@_runProjectTest)
runFailingProjectTest: ->
@_runFailingProjectTest()
.catch (err) =>
@tryXvfb(@_runFailingProjectTest)
tryXvfb: (p) ->
console.log("tryXvfb")
xvfb = new Xvfb()
xvfb = Promise.promisifyAll(xvfb)
xvfb.startAsync()
.then (xvfbProcess) =>
console.log("executing in xvfb process %j", xvfbProcess)
Promise.try(p.bind(@))
.finally ->
console.log("stopping xvfb")
xvfb.stopAsync()
runSmokeTest: ->
## if we fail assume perhaps
## its due to not starting xvfb
@_runSmokeTest()
.catch =>
@tryXvfb(@_runSmokeTest)
npm: ->
new Promise (resolve, reject) ->
vagrant.ssh ["-c", "cd /cypress-app && npm install"], (code) ->
if code isnt 0
reject("vagrant.rsync failed!")
else
resolve()
rsync: ->
new Promise (resolve, reject) ->
vagrant.rsync (code) ->
if code isnt 0
reject("vagrant.rsync failed!")
else
resolve()
rsyncBack: ->
new Promise (resolve, reject) ->
vagrant["rsync-back"] (code) ->
if code isnt 0
reject("vagrant.rsync-back failed!")
else
resolve()
deploy: ->
version = @options.version
getOpts = =>
if @options.runTests is false
"--skip-tests"
else
""
deploy = =>
new Promise (resolve, reject) =>
ssh = ->
vagrant.ssh ["-c", "cd /cypress-app && ./node_modules/.bin/gulp build --version #{version} #{getOpts()}"], (code) ->
if code isnt 0
reject("vagrant.ssh gulp build failed!")
else
resolve()
vagrant.status (code) ->
if code isnt 0
vagrant.up (code) ->
reject("vagrant.up failed!") if code isnt 0
ssh()
else
ssh()
@npm()
.bind(@)
.then(@rsync)
.then(deploy)
.then(@rsyncBack)
.return(@)
module.exports = Linux

View File

@@ -0,0 +1,6 @@
chalk = require("chalk")
module.exports = (msg, color = "yellow") ->
return if process.env["NODE_ENV"] is "test"
console.log chalk[color](msg), chalk.bgWhite(chalk.black(@osName))

View File

@@ -0,0 +1,48 @@
path = require("path")
la = require("lazy-ass")
check = require("check-more-types")
isValidPlatform = check.oneOf(["darwin", "linux"])
## returns a path into the /build directory
## the output folder should look something like this
## build/
## <platform>/ = linux or darwin
## ... platform-specific files
buildDir = (platform, args...) ->
la(isValidPlatform(platform), "invalid platform", platform)
switch platform
when "darwin"
path.resolve("build", platform, args...)
when "linux"
path.resolve("build", platform, "Cypress", args...)
## returns a path into the /dist directory
distDir = (platform, args...) ->
path.resolve("dist", platform, args...)
## returns folder to zip before uploading
zipDir = (platform) ->
switch platform
when "darwin"
buildDir(platform, "Cypress.app")
when "linux"
buildDir(platform)
## returns a path into the /build/*/app directory
## specific to each platform
buildAppDir = (platform, args...) ->
switch platform
when "darwin"
buildDir(platform, "Cypress.app", "Contents", "resources", "app", args...)
when "linux"
buildDir(platform, "resources", "app", args...)
module.exports = {
isValidPlatform
buildDir
distDir
zipDir
buildAppDir
cacheDir: path.join(process.cwd(), "cache")
}

View File

@@ -0,0 +1,140 @@
awspublish = require('gulp-awspublish')
rename = require('gulp-rename')
debug = require('gulp-debug')
fs = require("fs-extra")
cp = require("child_process")
path = require("path")
gulp = require("gulp")
human = require("human-interval")
konfig = require("@packages/server/lib/konfig")
Promise = require("bluebird")
meta = require("./meta")
la = require("lazy-ass")
check = require("check-more-types")
fs = Promise.promisifyAll(fs)
uploadNames = {
darwin: "osx64"
linux: "linux64"
win32: "win64"
}
getUploadNameByOs = (os) ->
name = uploadNames[os]
if not name
throw new Error("Cannot find upload name for OS #{os}")
name
module.exports = {
getPublisher: ->
aws = @getAwsObj()
awspublish.create {
httpOptions: {
timeout: human("10 minutes")
}
params: {
Bucket: aws.bucket
}
accessKeyId: aws.key
secretAccessKey: aws.secret
}
getAwsObj: ->
fs.readJsonSync("./support/aws-credentials.json")
# store uploaded application in subfolders by platform and version
# something like desktop/0.20.1/osx64/
getUploadDirName: ({version, platform}) ->
aws = @getAwsObj()
osName = getUploadNameByOs(platform)
dirName = [aws.folder, version, osName, null].join("/")
console.log("target directory %s", dirName)
dirName
purgeCache: ({zipFile, version, platform}) ->
la(check.unemptyString(platform), "missing platform", platform)
new Promise (resolve, reject) =>
zipName = path.basename(zipFile)
url = [konfig('cdn_url'), "desktop", version, platform, zipName].join("/")
console.log("purging url", url)
configFile = path.resolve("support", ".cfcli.yml")
cp.exec "cfcli purgefile -c #{configFile} #{url}", (err, stdout, stderr) ->
if err
console.error("Could not purge #{url}")
console.error(err.message)
return reject(err)
console.log("#purgeCache: #{url}")
resolve()
createRemoteManifest: (folder, version) ->
## TODO: refactor this
zipName = "cypress.zip"
getUrl = (uploadOsName) ->
{
url: [konfig('cdn_url'), folder, version, uploadOsName, zipName].join("/")
}
obj = {
name: "Cypress"
version: version
packages: {
mac: getUrl("osx64")
win: getUrl("win64")
linux64: getUrl("linux64")
}
}
src = path.join(meta.buildDir, "manifest.json")
fs.outputJsonAsync(src, obj).return(src)
s3Manifest: (version) ->
publisher = @getPublisher()
aws = @getAwsObj()
headers = {}
headers["Cache-Control"] = "no-cache"
new Promise (resolve, reject) =>
@createRemoteManifest(aws.folder, version).then (src) ->
gulp.src(src)
.pipe rename (p) ->
p.dirname = aws.folder + "/" + p.dirname
p
.pipe debug()
.pipe publisher.publish(headers)
.pipe awspublish.reporter()
.on "error", reject
.on "end", resolve
toS3: ({zipFile, version, platform}) ->
console.log("#uploadToS3 ⏳")
la(check.unemptyString(version), "expected version string", version)
la(check.unemptyString(zipFile), "expected zip filename", zipFile)
la(meta.isValidPlatform(platform), "invalid platform", platform)
upload = =>
new Promise (resolve, reject) =>
publisher = @getPublisher()
headers = {}
headers["Cache-Control"] = "no-cache"
gulp.src(zipFile)
.pipe rename (p) =>
p.dirname = @getUploadDirName({version, platform})
p
.pipe debug()
.pipe publisher.publish(headers)
.pipe awspublish.reporter()
.on "error", reject
.on "end", resolve
upload()
.then =>
@purgeCache({zipFile, version, platform})
}

View File

@@ -0,0 +1,167 @@
_ = require("lodash")
fs = require("fs-extra")
cp = require("child_process")
path = require("path")
glob = require("glob")
Promise = require("bluebird")
la = require("lazy-ass")
check = require("check-more-types")
fs = Promise.promisifyAll(fs)
glob = Promise.promisify(glob)
DEFAULT_PATHS = "package.json".split(" ")
pathToPackageJson = (pkg) ->
path.join(pkg, "package.json")
npmRun = (args, cwd) ->
new Promise (resolve, reject) ->
reject = _.once(reject)
cp.spawn("npm", args, { stdio: "inherit", cwd })
.on "error", reject
.on "exit", (code) ->
if code is 0
resolve()
else
msg = "npm " + args.join(" ") + " failed with exit code: #{code}"
reject(new Error(msg))
runAllBuildJs = _.partial(npmRun, ["run", "all", "build-js"])
# removes transpiled JS files in the original package folders
runAllCleanJs = _.partial(npmRun, ["run", "all", "clean-js"])
# builds all the packages except for cli and docs
runAllBuild = _.partial(npmRun,
["run", "all", "build", "--", "--serial", "--skip-packages", "cli,docs"])
copyAllToDist = (distDir) ->
copyRelativePathToDist = (relative) ->
dest = path.join(distDir, relative)
console.log(relative, "->", dest)
fs.copyAsync(relative, dest)
copyPackage = (pkg) ->
## copies the package to dist
## including the default paths
## and any specified in package.json files
fs.readJsonAsync(pathToPackageJson(pkg))
.then (json) ->
## grab all the files
## and default included paths
## and convert to relative paths
DEFAULT_PATHS
.concat(json.files or [])
.concat(json.main or [])
.map (file) ->
path.join(pkg, file)
.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
started = new Date()
fs.ensureDirAsync(distDir)
.then ->
glob("./packages/*")
.map(copyPackage, {concurrency: 1})
.then ->
console.log("Finished Copying", new Date() - started)
npmInstallAll = (pathToPackages) ->
## 1,060,495,784 bytes (1.54 GB on disk) for 179,156 items
## 313,416,512 bytes (376.6 MB on disk) for 23,576 items
console.log("npmInstallAll packages in #{pathToPackages}")
started = new Date()
retryGlobbing = ->
glob(pathToPackages)
.catch {code: "EMFILE"}, ->
## wait 1 second then retry
Promise
.delay(1000)
.then(retryGlobbing)
retryNpmInstall = (pkg) ->
npmInstall = _.partial(npmRun, ["install", "--production"])
npmInstall(pkg)
.catch {code: "EMFILE"}, ->
Promise
.delay(1000)
.then ->
retryNpmInstall(pkg)
.catch (err) ->
console.log(err, err.code)
throw err
## prunes out all of the devDependencies
## from what was copied
retryGlobbing()
.map(retryNpmInstall)
.then ->
console.log("Finished NPM Installing", new Date() - started)
removePackageJson = (filename) ->
if filename.endsWith("/package.json") then path.dirname(filename) else filename
ensureFoundSomething = (files) ->
if files.length == 0
throw new Error("Could not find any files")
files
symlinkAll = (pathToDistPackages, pathTo) ->
console.log("symlink these packages", pathToDistPackages)
la(check.unemptyString(pathToDistPackages),
"missing paths to dist packages", pathToDistPackages)
baseDir = path.dirname(pathTo())
toBase = path.relative.bind(null, baseDir)
symlink = (pkg) ->
# console.log(pkg, dist)
## strip off the initial './'
## ./packages/foo -> node_modules/@packages/foo
pkg = removePackageJson(pkg)
dest = pathTo("node_modules", "@packages", path.basename(pkg))
console.log(toBase(pkg), "link ->", toBase(dest))
fs.ensureSymlinkAsync(pkg, dest)
.catch((err) ->
if not err.message.includes "EEXIST"
throw err
)
glob(pathToDistPackages)
.then(ensureFoundSomething)
.map(symlink)
module.exports = {
runAllBuild
runAllBuildJs
copyAllToDist
npmInstallAll
symlinkAll
runAllCleanJs
}

61
scripts/binary/zip.coffee Normal file
View File

@@ -0,0 +1,61 @@
cp = require("child_process")
Promise = require("bluebird")
os = require("os")
execa = require("execa")
# resolves with zipped filename
macZip = (src, dest) ->
new Promise (resolve, reject) =>
if os.platform() != "darwin"
throw new Error("Can only zip on Mac platform")
# Ditto (Mac) options
# http://www.unix.com/man-page/OSX/1/ditto/
# -c create archive
# -k set archive format to PKZip
# --sequesterRsrc When creating a PKZip archive, preserve resource
# forks and HFS meta-data in the subdirectory __MACOSX
# --keepParent when zipping folder "foo", makes the folder
# the top level in the archive
# foo.zip
# foo/
# ...
zip = "ditto -c -k --sequesterRsrc --keepParent #{src} #{dest}"
console.log(zip)
cp.exec zip, {}, (err, stdout, stderr) ->
return reject(err) if err
console.log("✅ ditto zip finished")
resolve(dest)
# resolves with zipped filename
linuxZip = (src, dest) ->
cmd = "tar -zcvf #{dest} #{src}"
console.log("linux zip: #{cmd}")
execa.shell(cmd)
.then((result) ->
console.log("✅ tar finished")
dest
)
.catch((err) ->
console.error("⛔️ could not zip #{src} into #{dest}")
console.error(err.message)
throw err
)
zippers = {
# until the CLI tool can unzip both ".zip" and ".tar.gz" files,
# must use Mac platform to build the .zip file
linux: macZip,
darwin: macZip
}
module.exports = {
ditto: (src, dest) ->
platform = os.platform()
console.log("#zip", platform)
console.log("Zipping %s into %s", src, dest)
zipper = zippers[platform]
if !zipper
throw new Error("Missing zip function for platform #{platform}")
zipper(src, dest)
}