unzip.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. 'use strict';
  2. var la = require('lazy-ass');
  3. var is = require('check-more-types');
  4. var cp = require('child_process');
  5. var os = require('os');
  6. var yauzl = require('yauzl');
  7. var debug = require('debug')('cypress:cli:unzip');
  8. var extract = require('extract-zip');
  9. var Promise = require('bluebird');
  10. var readline = require('readline');
  11. var _require = require('../errors'),
  12. throwFormErrorText = _require.throwFormErrorText,
  13. errors = _require.errors;
  14. var fs = require('../fs');
  15. var util = require('../util');
  16. var unzipTools = {
  17. extract: extract
  18. // expose this function for simple testing
  19. };var unzip = function unzip(_ref) {
  20. var zipFilePath = _ref.zipFilePath,
  21. installDir = _ref.installDir,
  22. progress = _ref.progress;
  23. debug('unzipping from %s', zipFilePath);
  24. debug('into', installDir);
  25. if (!zipFilePath) {
  26. throw new Error('Missing zip filename');
  27. }
  28. var startTime = Date.now();
  29. var yauzlDoneTime = 0;
  30. return fs.ensureDirAsync(installDir).then(function () {
  31. return new Promise(function (resolve, reject) {
  32. return yauzl.open(zipFilePath, function (err, zipFile) {
  33. yauzlDoneTime = Date.now();
  34. if (err) return reject(err);
  35. var total = zipFile.entryCount;
  36. debug('zipFile entries count', total);
  37. var started = new Date();
  38. var percent = 0;
  39. var count = 0;
  40. var notify = function notify(percent) {
  41. var elapsed = +new Date() - +started;
  42. var eta = util.calculateEta(percent, elapsed);
  43. progress.onProgress(percent, util.secsRemaining(eta));
  44. };
  45. var tick = function tick() {
  46. count += 1;
  47. percent = count / total * 100;
  48. var displayPercent = percent.toFixed(0);
  49. return notify(displayPercent);
  50. };
  51. var unzipWithNode = function unzipWithNode() {
  52. debug('unzipping with node.js (slow)');
  53. var endFn = function endFn(err) {
  54. if (err) {
  55. return reject(err);
  56. }
  57. return resolve();
  58. };
  59. var opts = {
  60. dir: installDir,
  61. onEntry: tick
  62. };
  63. return unzipTools.extract(zipFilePath, opts, endFn);
  64. };
  65. var unzipWithUnzipTool = function unzipWithUnzipTool() {
  66. debug('unzipping via `unzip`');
  67. var inflatingRe = /inflating:/;
  68. var sp = cp.spawn('unzip', ['-o', zipFilePath, '-d', installDir]);
  69. sp.on('error', unzipWithNode);
  70. sp.on('close', function (code) {
  71. if (code === 0) {
  72. percent = 100;
  73. notify(percent);
  74. return resolve();
  75. }
  76. debug('`unzip` failed %o', { code: code });
  77. return unzipWithNode();
  78. });
  79. sp.stdout.on('data', function (data) {
  80. if (inflatingRe.test(data)) {
  81. return tick();
  82. }
  83. });
  84. sp.stderr.on('data', function (data) {
  85. debug('`unzip` stderr %s', data);
  86. });
  87. };
  88. // we attempt to first unzip with the native osx
  89. // ditto because its less likely to have problems
  90. // with corruption, symlinks, or icons causing failures
  91. // and can handle resource forks
  92. // http://automatica.com.au/2011/02/unzip-mac-os-x-zip-in-terminal/
  93. var unzipWithOsx = function unzipWithOsx() {
  94. debug('unzipping via `ditto`');
  95. var copyingFileRe = /^copying file/;
  96. var sp = cp.spawn('ditto', ['-xkV', zipFilePath, installDir]);
  97. // f-it just unzip with node
  98. sp.on('error', unzipWithNode);
  99. sp.on('close', function (code) {
  100. if (code === 0) {
  101. // make sure we get to 100% on the progress bar
  102. // because reading in lines is not really accurate
  103. percent = 100;
  104. notify(percent);
  105. return resolve();
  106. }
  107. debug('`ditto` failed %o', { code: code });
  108. return unzipWithNode();
  109. });
  110. return readline.createInterface({
  111. input: sp.stderr
  112. }).on('line', function (line) {
  113. if (copyingFileRe.test(line)) {
  114. return tick();
  115. }
  116. });
  117. };
  118. switch (os.platform()) {
  119. case 'darwin':
  120. return unzipWithOsx();
  121. case 'linux':
  122. return unzipWithUnzipTool();
  123. case 'win32':
  124. return unzipWithNode();
  125. default:
  126. return;
  127. }
  128. });
  129. }).tap(function () {
  130. debug('unzip completed %o', {
  131. yauzlMs: yauzlDoneTime - startTime,
  132. unzipMs: Date.now() - yauzlDoneTime
  133. });
  134. });
  135. });
  136. };
  137. var start = function start(_ref2) {
  138. var zipFilePath = _ref2.zipFilePath,
  139. installDir = _ref2.installDir,
  140. progress = _ref2.progress;
  141. la(is.unemptyString(installDir), 'missing installDir');
  142. if (!progress) {
  143. progress = { onProgress: function onProgress() {
  144. return {};
  145. } };
  146. }
  147. return fs.pathExists(installDir).then(function (exists) {
  148. if (exists) {
  149. debug('removing existing unzipped binary', installDir);
  150. return fs.removeAsync(installDir);
  151. }
  152. }).then(function () {
  153. return unzip({ zipFilePath: zipFilePath, installDir: installDir, progress: progress });
  154. }).catch(throwFormErrorText(errors.failedUnzip));
  155. };
  156. module.exports = {
  157. start: start,
  158. utils: {
  159. unzip: unzip,
  160. unzipTools: unzipTools
  161. }
  162. };