text: const Promise = require('bluebird')
  const os = require('os')
  const execa = require('execa')
  const path = require('path')
  const la = require('lazy-ass')
  const fs = require('fs')
  const filesize = require('filesize')
  
  // prints disk usage numbers using "du" utility
  // available on Linux and Mac
  const printFileSizes = function (folder) {
    console.log(`File sizes in ${folder}`)
    const paths = path.join(folder, '*')
    const options = {
      stdio: 'inherit',
      shell: true,
    }
  
    return execa(`du -hs ${paths}`, options)
  }
  
  // resolves with zipped filename
  const macZip = (src, dest) => {
    return 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/
      //        ...
      const zip = `ditto -c -k --sequesterRsrc --keepParent ${src} ${dest}`
      const options = {
        stdio: 'inherit',
        shell: true,
      }
  
      console.log(zip)
  
      const onZipFinished = () => {
        return console.log('✅ ditto finished')
      }
  
      const onError = function (err) {
        console.error(`⛔️ could not zip ${src} into ${dest}`)
        console.error(err.message)
        throw err
      }
  
      return execa(zip, options)
      .then(onZipFinished)
      .then(() => dest)
      .catch(onError)
    })
  }
  
  const megaBytes = (bytes) => {
    return 1024 * 1024 * bytes
  }
  
  const checkZipSize = function (zipPath) {
    const stats = fs.statSync(zipPath)
    const zipSize = filesize(stats.size, { round: 0 })
  
    console.log(`zip file size ${zipSize}`)
    // Before you modify these max sizes, check and see what you did that might have
    // done to increase the size of the binary, and if you do need to change it,
    // call it out in the PR description / comments
    const MAX_ALLOWED_SIZE_MB = os.platform() === 'win32' ? 295 : 200
    const 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`)
    }
  }
  
  const linuxZipAction = function (parentFolder, dest, relativeSource) {
    console.log(`zipping ${parentFolder}`)
    const cmd = `cd ${parentFolder} && zip -r9 ${dest} ${relativeSource}`
  
    console.log(`linux zip: ${cmd}`)
  
    const onZipFinished = () => {
      return console.log('✅ zip finished')
    }
  
    const onError = function (err) {
      console.error(`⛔️ could not zip ${relativeSource} in folder ${parentFolder}`)
      console.error(`to produce ${dest}`)
      console.error(err.message)
      throw err
    }
  
    return execa(cmd, { shell: true })
    .then(onZipFinished)
    .then(() => dest)
    .then((val) => {
      checkZipSize(val)
  
      return val
    })
    .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
  const renameFolder = function (src) {
    const parentFolder = path.dirname(src)
    const folderName = path.basename(src)
  
    if (folderName === 'Cypress') {
      console.log('nothing to rename, folder "%s" ends with Cypress', src)
  
      return Promise.resolve(src)
    }
  
    const renamed = path.join(parentFolder, 'Cypress')
  
    console.log(`renaming ${src} to ${renamed}`)
  
    return fs.promises.rename(src, renamed)
    .then(() => renamed)
  }
  
  // resolves with zipped filename
  const linuxZip = function (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
    return renameFolder(src)
    .then((renamedSource) => {
      return printFileSizes(renamedSource)
      .then(() => renamedSource)
    }).then((renamedSource) => {
      console.log(`will zip folder ${renamedSource}`)
      const parentFolder = path.dirname(renamedSource)
      const relativeSource = path.basename(renamedSource)
  
      return linuxZipAction(parentFolder, dest, relativeSource)
    })
  }
  
  // resolves with zipped filename
  const windowsZipAction = function (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
    const cmd = `7z a ${dest} ${src}`
  
    console.log(`windows zip: ${cmd}`)
  
    const onZipFinished = () => {
      return console.log('✅ 7z finished')
    }
  
    const onError = function (err) {
      console.error(`⛔️ could not zip ${src} into ${dest}`)
      console.error(err.message)
      throw err
    }
  
    return execa(cmd, { shell: true })
    .then(onZipFinished)
    .then(() => dest)
    .then((val) => {
      checkZipSize(val)
  
      return val
    })
    .catch(onError)
  }
  
  const windowsZip = (src, dest) => {
    return renameFolder(src)
    .then((renamedSource) => {
      return windowsZipAction(renamedSource, dest)
    })
  }
  
  const 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) {
      const platform = os.platform()
  
      console.log('#zip', platform)
      console.log('Zipping %s into %s', src, dest)
  
      const zipper = zippers[platform]
  
      if (!zipper) {
        throw new Error(`Missing zip function for platform ${platform}`)
      }
  
      return zipper(src, dest)
    },
  
    checkZipSize,
  }
  