install.js

Maintainability

68.69

Lines of code

197

Created with Raphaël 2.1.002550751002014-11-28

2014-11-28
Maintainability: 68.69

Created with Raphaël 2.1.00501001502002014-11-28

2014-11-28
Lines of Code: 197

Difficulty

38.46

Estimated Errors

2.08

Function weight

By Complexity

Created with Raphaël 2.1.0copyIntoPlace4

By SLOC

Created with Raphaël 2.1.0<anonymous>44
1
// Shamelessly copied from The Obvious Corporation
2
 
3
/*
4
 * This simply fetches the right version of chromedriver for the current platform.
5
 */
6
 
7
'use strict';
8
 
9
var cp = require('child_process');
10
var fs = require('fs');
11
var http = require('http');
12
var kew = require('kew');
13
var path = require('path');
14
var url = require('url');
15
var rimraf = require('rimraf').sync;
16
var AdmZip = require('adm-zip');
17
var helper = require('./lib/chromedriver');
18
var ncp = require('ncp');
19
var npmconf = require('npmconf');
20
var util = require('util');
21
 
22
var libPath = path.join(__dirname, 'lib', 'bin', 'chromedriver');
23
var downloadUrl = 'http://chromedriver.storage.googleapis.com/2.9/chromedriver_';
24
 
25
if (process.platform === 'linux' && process.arch === 'x64') {
26
  downloadUrl += 'linux64.zip';
27
} else if (process.platform === 'linux') {
28
  downloadUrl += 'linux32.zip';
29
} else if (process.platform === 'darwin') {
30
  downloadUrl += 'mac32.zip';
31
} else if (process.platform === 'win32') {
32
  downloadUrl += 'win32.zip';
33
} else {
34
  console.log('Unexpected platform or architecture:', process.platform, process.arch);
35
  process.exit(1);
36
}
37
 
38
var fileName = downloadUrl.split('/').pop();
39
 
40
function getRequestOptions(proxyUrl) {
41
  if (proxyUrl) {
42
    var options = url.parse(proxyUrl);
43
    options.path = downloadUrl;
44
    options.headers = { Host: url.parse(downloadUrl).host };
45
    // turn basic authorization into proxy-authorization
46
    if (options.auth) {
47
      options.headers['Proxy-Authorization'] = 'Basic ' + new Buffer(options.auth).toString('base64');
48
      delete options.auth;
49
    }
50
 
51
    return options;
52
  } else {
53
    return url.parse(downloadUrl);
54
  }
55
}
56
 
57
function requestBinary(requestOptions, filePath) {
58
  var deferred = kew.defer();
59
 
60
  var count = 0;
61
  var notifiedCount = 0;
62
  var outFile = fs.openSync(filePath, 'w');
63
 
64
  var client = http.get(requestOptions, function (response) {
65
    var status = response.statusCode;
66
    console.log('Receiving...');
67
 
68
    if (status === 200) {
69
      response.addListener('data',   function (data) {
70
        fs.writeSync(outFile, data, 0, data.length, null);
71
        count += data.length;
72
        if ((count - notifiedCount) > 800000) {
73
          console.log('Received ' + Math.floor(count / 1024) + 'K...');
74
          notifiedCount = count;
75
        }
76
      });
77
 
78
      response.addListener('end',   function () {
79
        console.log('Received ' + Math.floor(count / 1024) + 'K total.');
80
        fs.closeSync(outFile);
81
        deferred.resolve(true);
82
      });
83
 
84
    } else {
85
      client.abort();
86
      deferred.reject('Error with http request: ' + util.inspect(response.headers));
87
    }
88
  });
89
 
90
  return deferred.promise;
91
}
92
 
93
function extractDownload(filePath, tmpPath) {
94
  var deferred = kew.defer();
95
  var options = {cwd: tmpPath};
96
 
97
  rimraf(libPath);
98
 
99
  if (filePath.substr(-4) === '.zip') {
100
    console.log('Extracting zip contents');
101
    try {
102
      var zip = new AdmZip(filePath);
103
      zip.extractAllTo(path.dirname(filePath), true);
104
      deferred.resolve(true);
105
    } catch (e) {
106
      console.log(e);
107
      deferred.reject('Error extracting archive ' + e.stack);
108
    }
109
 
110
  } else {
111
    console.log('Extracting tar contents (via spawned process)');
112
    cp.execFile('tar', ['jxf', filePath], options, function (err) {
113
      if (err) {
114
        deferred.reject('Error extracting archive ' + err.stack);
115
      } else {
116
        deferred.resolve(true);
117
      }
118
    });
119
  }
120
  return deferred.promise;
121
}
122
 
123
function copyIntoPlace(tmpPath, targetPath) {
124
  var deferred = kew.defer();
125
  // Look for the extracted directory, so we can rename it.
126
  var files = fs.readdirSync(tmpPath);
127
  for (var i = 0; i < files.length; i++) {
128
    var file = path.join(tmpPath, files[i]);
129
    if (fs.statSync(file).isFile() && file.search('zip') === -1) {
130
      console.log('Renaming extracted folder', file, '->', targetPath);
131
      if (process.platform === 'win32') {
132
        targetPath = targetPath + '.exe';
133
      }
134
      ncp(file, targetPath, deferred.makeNodeResolver());
135
      break;
136
    }
137
  }
138
  return deferred.promise;
139
}
140
 
141
function fixFilePermissions() {
142
  // Check that the binary is user-executable and fix it if it isn't (problems with unzip library)
143
  if (process.platform !== 'win32') {
144
    var stat = fs.statSync(helper.path);
145
    // 64 == 0100 (no octal literal in strict mode)
146
    if (!(stat.mode & 64)) {
147
      console.log('Fixing file permissions');
148
      fs.chmodSync(helper.path, '755');
149
    }
150
  }
151
}
152
 
153
function mkdir(name) {
154
  var dir = path.dirname(name);
155
  if (!fs.existsSync(dir)) {
156
    fs.mkdirSync(dir);
157
  }
158
}
159
 
160
npmconf.load(function(err, conf) {
161
  if (err) {
162
    console.log('Error loading npm config');
163
    console.error(err);
164
    process.exit(1);
165
    return;
166
  }
167
 
168
  var tmpPath = path.join(conf.get('tmp'), 'chromedriver');
169
  var downloadedFile = path.join(tmpPath, fileName);
170
 
171
  var promise= kew.resolve(true);
172
 
173
  // Start the install.
174
  if (!fs.existsSync(downloadedFile)) {
175
    promise = promise.then(function () {
176
      rimraf(tmpPath);
177
      mkdir(downloadedFile);
178
      console.log('Downlaoading', downloadUrl);
179
      console.log('Saving to', downloadedFile);
180
      return requestBinary(getRequestOptions(conf.get('proxy')), downloadedFile);
181
    });
182
 
183
  } else {
184
    console.log('Download already available at', downloadedFile);
185
  }
186
 
187
  promise.then(function () {
188
    return extractDownload(downloadedFile, tmpPath);
189
  })
190
  .then(function () {
191
    return copyIntoPlace(tmpPath, libPath);
192
  })
193
  .then(function () {
194
    return fixFilePermissions();
195
  })
196
  .then(function () {
197
    console.log('Done. Chromedriver binary available at', helper.path);
198
  })
199
  .fail(function (err) {
200
    console.error('Chromedriver installation failed', err.stack);
201
    process.exit(1);
202
  });
203
});