Completed
Push — master ( 2815a2...a35fd3 )
by Thomas
27s
created

Kit.checkRequirements   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
c 1
b 0
f 0
nc 3
nop 4
dl 0
loc 23
rs 8.5906
1
'use strict'
2
3
const util = require('../util')
4
const output = require('../output')
5
const helpers = require('../run-helpers')
6
const pkg = require('../../package.json')
7
8
const fs = require('fs')
9
const os = require('os')
10
const path = require('path')
11
const https = require('https')
12
const name = require('git-user-name')
13
const email = require('git-user-email')
14
const child = require('child_process')
15
const unzip = require('unzip-stream')
16
const rimraf = require('rimraf')
17
const jsonfile = require('jsonfile')
18
const mkdirp = require('mkdirp')
19
const compareVersions = require('compare-versions')
20
21
var Kit = {}
22
23
function extractZip (response, zipName, containerPath, callback, namespace, url) {
24
  // Create cache directory
25
  var tmpdir = path.join(os.tmpdir(), 'inc-v' + pkg.version, namespace)
26
  try {
27
    mkdirp.sync(tmpdir)
28
  } catch (e) {
29
    return callback(e)
30
  }
31
32
  return response
33
    .pipe(unzip.Extract({
34
      path: tmpdir
35
    }))
36
    .on('error', function (err) {
37
      callback(err)
38
    })
39
    .on('close', function () {
40
      setTimeout(function () {
41
        try {
42
          // At this point, there is a good possibility that all files got
43
          // extracted to a subdirectory, which we should correct
44
          var zipPath = path.join(tmpdir, zipName)
45
          if (!fs.existsSync(zipPath)) {
46
            callback(new Error('Error extracting zip file'))
47
            return
48
          }
49
50
          // Check requirements
51
          var json = jsonfile.readFileSync(path.join(zipPath, 'module.json'))
52
          var res = Kit.checkRequirements(json, namespace, url, zipPath)
53
          if (res !== true) {
54
            callback(res)
55
            rimraf.sync(zipPath)
56
            process.exit()
57
          }
58
59
          fs.renameSync(zipPath, path.resolve(containerPath))
60
61
          // Calling back home
62
          callback(null)
63
        } catch (e) {
64
          callback(e)
65
        }
66
      }, 500)
67
    })
68
}
69
70
function downloadZip (zipUrl, zipName, containerPath, callback, namespace, origUrl) {
71
  https.get(zipUrl, function (response) {
72
    if (response.statusCode >= 200 && response.statusCode < 300) {
73
      extractZip(response, zipName, containerPath, callback, namespace, origUrl || zipUrl)
74
      return
75
    }
76
    if (response.headers.location) {
77
      downloadZip(response.headers.location, zipName, containerPath, callback, namespace, zipUrl)
78
      return
79
    }
80
81
    callback(new Error(response.statusCode + ' ' + response.statusMessage))
82
  }).on('error', function (err) {
83
    callback(err)
84
  })
85
}
86
87
function postProcess (slug) {
88
  // Remove stock files
89
  try { fs.unlinkSync(path.join(slug, 'LICENSE.md')) } catch (e) {}
90
  try { fs.unlinkSync(path.join(slug, 'LICENSE')) } catch (e) {}
91
  try { fs.unlinkSync(path.join(slug, 'README')) } catch (e) {}
92
  try { rimraf.sync(path.join(slug, '.git')) } catch (e) {}
93
94
  // Write readme.md
95
  fs.writeFileSync(path.join(slug, 'README.md'), '# ' +
96
    util.boxNameDisplay(slug) + '\n\nA Incluable app for ' + util.boxNameDisplay(slug) + '.', 'utf8')
97
98
  // Download IDE helper
99
  var ideSpinner = output.wait('Downloading code completion helper')
100
  util.downloadHelper(function () {
101
    ideSpinner(true)
102
103
    // Git init
104
    var gitSpinner = output.wait('Initializing local Git repository')
105
    child.exec('cd "' + slug + '" && git init', function (err) {
106
      gitSpinner(err ? err : true)
107
    })
108
  }, slug)
109
}
110
111
Kit.download = function (kitName, targetPath, callback) {
112
  var containerPath = path.resolve(targetPath)
113
  var kitNameResolved = this.resolveName(kitName)
114
  var namespace = kitNameResolved.split('/')[0]
115
  var kitNameSimplified = kitNameResolved.split('/')[1]
116
  var zipUrl = 'https://github.com/' + kitNameResolved + '/archive/master.zip'
117
118
  downloadZip(zipUrl, kitNameSimplified + '-master', containerPath, callback, namespace)
119
}
120
121
Kit.resolveName = function (kitName) {
122
  kitName = kitName.replace(/\s/ig, '')
123
124
  if (kitName.indexOf('/') > 0) {
125
    // Repo name already includes user/org, so skip any other processing
126
    return kitName
127
  }
128
129
  // Prepend 'starter-'
130
  if (kitName.indexOf('starter-') !== 0) {
131
    kitName = 'starter-' + kitName
132
  }
133
134
  // Prepend org name
135
  return 'includable-modules/' + kitName
136
}
137
138
Kit.checkRequirements = function (json, namespace, url, zipPath) {
139
  // If not exists, no need to check anything
140
  if (!('_starter' in json) || typeof json._starter !== 'object') {
141
    return true
142
  }
143
144
  // Check minimal CLI version to run this starter kit
145
  if ('minVersion' in json._starter && compareVersions(json._starter.minVersion, pkg.version) === 1) {
146
    return 'Starter kit requires version ' + json._starter.minVersion + ' of ' +
147
      'Includable CLI, but you have ' + pkg.version + '. Please update!'
148
  }
149
150
  // Save starter.json meta data
151
  mkdirp.sync(path.join(zipPath, '.inc', 'meta'))
152
  json._starter.sourceUrl = url
153
  json._starter.namespace = namespace
154
  jsonfile.writeFileSync(path.join(zipPath, '.inc', 'meta', 'starter.json'), json._starter)
155
156
  // Remove starter meta data
157
  delete json['_starter']
158
159
  return true
160
}
161
162
Kit.create = function (kitName, slug) {
163
  // Get slug
164
  var userName = (util.getSystemUsername() || 'user').toLowerCase()
165
  if (slug.match(/^[a-z0-9-]+\/[a-z0-9-]+$/)) {
166
    userName = slug.split('/')[0]
167
    slug = slug.split('/')[1]
168
  }
169
  if (!slug.match(/^[a-z0-9-]+$/)) {
170
    output.err('Module name should only contain lowercase letters, numbers and dashes.')
171
    return
172
  }
173
174
  // Check if directory doesn't already exist
175
  var stopSpinner = output.wait('Downloading template \'' + kitName + '\'')
176
  if (fs.existsSync(slug)) {
177
    stopSpinner('Directory \'' + slug + '\' already exists!')
178
    return
179
  }
180
181
  try {
182
    Kit.download(kitName, slug, function (err) {
183
      if (err) {
184
        stopSpinner(err)
185
        return
186
      }
187
      stopSpinner(true)
188
189
      // Write module.json
190
      var json = jsonfile.readFileSync(path.join(slug, 'module.json'))
191
      json = util.moduleJSON(slug, userName, name({
192
        type: 'global'
193
      }), email({
194
        type: 'global'
195
      }), json)
196
      fs.writeFileSync(path.join(slug, 'module.json'), JSON.stringify(json, null, 3), 'utf8')
197
198
      // Write composer.json
199
      if (!fs.existsSync(path.join(slug, 'composer.json'))) {
200
        fs.writeFileSync(path.join(slug, 'composer.json'), JSON.stringify({
201
          'require': {}
202
        }, null, 3), 'utf8')
203
      }
204
205
      // Write version file
206
      mkdirp.sync(path.join(slug, '.inc'))
207
      fs.writeFileSync(path.join(slug, '.inc', 'version'), '11', 'utf8')
208
209
      // Scripts
210
      helpers.startChildProcess('post-create', function () {
211
        postProcess(slug)
212
      }, slug)
213
    })
214
  } catch (e) {
215
    stopSpinner(e)
216
  }
217
}
218
219
module.exports = Kit
220