Skip to content

Commit

Permalink
cli: unnzip Cypress using unzip utility on Linux (cypress-io#5851)
Browse files Browse the repository at this point in the history
* unzip using 'unzip' utility on linux

* add example unzip test
  • Loading branch information
flotwig authored and santoshyadavdev committed Dec 4, 2019
1 parent 12d52a5 commit 45fa76e
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 10 deletions.
76 changes: 66 additions & 10 deletions cli/lib/tasks/unzip.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const is = require('check-more-types')
const cp = require('child_process')
const os = require('os')
const yauzl = require('yauzl')
const debug = require('debug')('cypress:cli')
const debug = require('debug')('cypress:cli:unzip')
const extract = require('extract-zip')
const Promise = require('bluebird')
const readline = require('readline')
Expand All @@ -12,6 +12,10 @@ const { throwFormErrorText, errors } = require('../errors')
const fs = require('../fs')
const util = require('../util')

const unzipTools = {
extract,
}

// expose this function for simple testing
const unzip = ({ zipFilePath, installDir, progress }) => {
debug('unzipping from %s', zipFilePath)
Expand All @@ -21,15 +25,17 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
throw new Error('Missing zip filename')
}

const startTime = Date.now()
let yauzlDoneTime = 0

return fs.ensureDirAsync(installDir)
.then(() => {
return new Promise((resolve, reject) => {
return yauzl.open(zipFilePath, (err, zipFile) => {
yauzlDoneTime = Date.now()

if (err) return reject(err)

// debug('zipfile.paths:', zipFile)
// zipFile.on('entry', debug)
// debug(zipFile.readEntry())
const total = zipFile.entryCount

debug('zipFile entries count', total)
Expand Down Expand Up @@ -57,6 +63,8 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
}

const unzipWithNode = () => {
debug('unzipping with node.js (slow)')

const endFn = (err) => {
if (err) {
return reject(err)
Expand All @@ -70,15 +78,50 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
onEntry: tick,
}

return extract(zipFilePath, opts, endFn)
return unzipTools.extract(zipFilePath, opts, endFn)
}

//# we attempt to first unzip with the native osx
//# ditto because its less likely to have problems
//# with corruption, symlinks, or icons causing failures
//# and can handle resource forks
//# http://automatica.com.au/2011/02/unzip-mac-os-x-zip-in-terminal/
const unzipWithUnzipTool = () => {
debug('unzipping via `unzip`')

const inflatingRe = /inflating:/

const sp = cp.spawn('unzip', ['-o', zipFilePath, '-d', installDir])

sp.on('error', unzipWithNode)

sp.on('close', (code) => {
if (code === 0) {
percent = 100
notify(percent)

return resolve()
}

debug('`unzip` failed %o', { code })

return unzipWithNode()
})

sp.stdout.on('data', (data) => {
if (inflatingRe.test(data)) {
return tick()
}
})

sp.stderr.on('data', (data) => {
debug('`unzip` stderr %s', data)
})
}

// we attempt to first unzip with the native osx
// ditto because its less likely to have problems
// with corruption, symlinks, or icons causing failures
// and can handle resource forks
// http://automatica.com.au/2011/02/unzip-mac-os-x-zip-in-terminal/
const unzipWithOsx = () => {
debug('unzipping via `ditto`')

const copyingFileRe = /^copying file/

const sp = cp.spawn('ditto', ['-xkV', zipFilePath, installDir])
Expand All @@ -96,6 +139,8 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
return resolve()
}

debug('`ditto` failed %o', { code })

return unzipWithNode()
})

Expand All @@ -113,13 +158,20 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
case 'darwin':
return unzipWithOsx()
case 'linux':
return unzipWithUnzipTool()
case 'win32':
return unzipWithNode()
default:
return
}
})
})
.tap(() => {
debug('unzip completed %o', {
yauzlMs: yauzlDoneTime - startTime,
unzipMs: Date.now() - yauzlDoneTime,
})
})
})
}

Expand Down Expand Up @@ -147,4 +199,8 @@ const start = ({ zipFilePath, installDir, progress }) => {

module.exports = {
start,
utils: {
unzip,
unzipTools,
},
}
38 changes: 38 additions & 0 deletions cli/test/lib/tasks/unzip_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ require('../../spec_helper')
const os = require('os')
const path = require('path')
const snapshot = require('../../support/snapshot')
const cp = require('child_process')

const fs = require(`${lib}/fs`)
const util = require(`${lib}/util`)
Expand Down Expand Up @@ -63,4 +64,41 @@ describe('lib/tasks/unzip', function () {
return fs.statAsync(installDir)
})
})

// NOTE: hmm, running this test for some reason breaks 4 tests in verify_spec.js with very weird errors
context.skip('on linux', () => {
beforeEach(() => {
os.platform.returns('linux')
})

it('can try unzip first then fall back to node unzip', function () {
sinon.stub(unzip.utils.unzipTools, 'extract').resolves()

const unzipChildProcess = {
on: sinon.stub(),
stdout: {
on () {},
},
stderr: {
on () {},
},
}

unzipChildProcess.on.withArgs('error').yieldsAsync(0)
unzipChildProcess.on.withArgs('close').yieldsAsync(0)
sinon.stub(cp, 'spawn').withArgs('unzip').returns(unzipChildProcess)

const zipFilePath = path.join('test', 'fixture', 'example.zip')

return unzip
.start({
zipFilePath,
installDir,
})
.then(() => {
expect(cp.spawn).to.have.been.calledWith('unzip')
expect(unzip.utils.unzipTools.extract).to.be.calledWith(zipFilePath)
})
})
})
})

0 comments on commit 45fa76e

Please sign in to comment.