Files
cypress/scripts/binary/zip.coffee
2020-03-26 13:30:38 -04:00

177 lines
5.0 KiB
CoffeeScript

Promise = require("bluebird")
os = require("os")
execa = require("execa")
path = require("path")
la = require("lazy-ass")
fs = require("fs")
R = require("ramda")
filesize = require("filesize")
# prints disk usage numbers using "du" utility
# available on Linux and Mac
printFileSizes = (folder) ->
console.log("File sizes in #{folder}")
paths = path.join(folder, "*")
options = {
stdio: "inherit",
shell: true
}
execa("du -hs #{paths}", options)
# resolves with zipped filename
macZip = (src, dest) ->
printFileSizes(src)
.then () ->
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}"
options = {
stdio: "inherit",
shell: true
}
console.log(zip)
onZipFinished = () ->
console.log("✅ ditto finished")
onError = (err) ->
console.error("⛔️ could not zip #{src} into #{dest}")
console.error(err.message)
throw err
execa(zip, options)
.then onZipFinished
.then R.always(dest)
.catch onError
megaBytes = (bytes) ->
1024 * 1024 * bytes
checkZipSize = (zipPath) ->
stats = fs.statSync(zipPath)
zipSize = filesize(stats.size, {round: 0})
console.log("zip file size #{zipSize}")
MAX_ALLOWED_SIZE_MB = if os.platform() == "win32" then 245 else 170
MAX_ZIP_FILE_SIZE = megaBytes(MAX_ALLOWED_SIZE_MB)
if stats.size > MAX_ZIP_FILE_SIZE
throw new Error("Zip file is too large: #{zipSize} (#{stats.size} bytes) exceeds #{MAX_ZIP_FILE_SIZE} bytes")
linuxZipAction = (parentFolder, dest, relativeSource) ->
console.log("zipping #{parentFolder}")
cmd = "cd #{parentFolder} && zip -r9 #{dest} #{relativeSource}"
console.log("linux zip: #{cmd}")
onZipFinished = () ->
console.log("✅ zip finished")
onError = (err) ->
console.error("⛔️ could not zip #{relativeSource} in folder #{parentFolder}")
console.error("to produce #{dest}")
console.error(err.message)
throw err
execa(cmd, {shell: true})
.then onZipFinished
.then R.always(dest)
.then R.tap(checkZipSize)
.catch onError
# src is built folder with packed Cypress application
# like /root/app/build/linux-unpacked or build/win-unpacked
# and we want to always have /root/app/build/Cypress
renameFolder = (src) ->
parentFolder = path.dirname(src)
folderName = path.basename(src)
if folderName is "Cypress"
console.log('nothing to rename, folder "%s" ends with Cypress', src)
return Promise.resolve(src)
renamed = path.join(parentFolder, "Cypress")
console.log("renaming #{src} to #{renamed}")
fs.promises.rename(src, renamed)
.then R.always(renamed)
# resolves with zipped filename
linuxZip = (src, dest) ->
# in Linux switch to the folder containing source folder
la(path.isAbsolute(src), "source path should be absolute", src)
la(path.isAbsolute(dest), "destination path should be absolute", dest)
# on Linux, make sure the folder name is "Cypress" first
renameFolder(src)
.then (renamedSource) ->
printFileSizes(renamedSource)
.then R.always(renamedSource)
.then (renamedSource) ->
console.log("will zip folder #{renamedSource}")
parentFolder = path.dirname(renamedSource)
relativeSource = path.basename(renamedSource)
linuxZipAction(parentFolder, dest, relativeSource)
# resolves with zipped filename
windowsZipAction = (src, dest) ->
# use 7Zip to zip
# http://www.7-zip.org/
# zips entire source directory including top level folder name
# Cypress/
# foo.txt
# creates cypress.zip for example
# unzip cypress.zip to get back the folder
# Cypress/
# foo.txt
cmd = "7z a #{dest} #{src}"
console.log("windows zip: #{cmd}")
onZipFinished = () ->
console.log("✅ 7z finished")
onError = (err) ->
console.error("⛔️ could not zip #{src} into #{dest}")
console.error(err.message)
throw err
execa(cmd, {shell: true})
.then onZipFinished
.then R.always(dest)
.then R.tap(checkZipSize)
.catch onError
windowsZip = (src, dest) ->
renameFolder(src)
.then (renamedSource) ->
windowsZipAction(renamedSource, dest)
zippers = {
linux: linuxZip
darwin: macZip
win32: windowsZip
}
module.exports = {
# zip Cypress folder to create destination zip file
# uses tool depending on the platform
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)
checkZipSize
}