1 |
|
2 | var Configuration = require('../../Configuration.js');
|
3 | var cst = require('../../../constants.js');
|
4 | var Common = require('../../Common');
|
5 | var forEachLimit = require('async/forEachLimit');
|
6 | const sexec = require('../../tools/sexec.js')
|
7 |
|
8 | var path = require('path');
|
9 | var fs = require('fs');
|
10 | var os = require('os');
|
11 | var spawn = require('child_process').spawn;
|
12 | var exec = require('child_process').exec;
|
13 | var execSync = require('child_process').execSync;
|
14 |
|
15 | module.exports = {
|
16 | install,
|
17 | uninstall,
|
18 | start,
|
19 | publish,
|
20 | package
|
21 | }
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | function install(PM2, module_filepath, opts, cb) {
|
34 |
|
35 | if (module_filepath.includes('http') === true) {
|
36 | var target_file = module_filepath.split('/').pop()
|
37 | var target_filepath = path.join(os.tmpdir(), target_file)
|
38 |
|
39 | opts.install_url = module_filepath
|
40 |
|
41 | return retrieveRemote(module_filepath, target_filepath, (err) => {
|
42 | if (err) {
|
43 | Common.errMod(err)
|
44 | process.exit(1)
|
45 | }
|
46 | installLocal(PM2, target_filepath, opts, cb)
|
47 | })
|
48 | }
|
49 |
|
50 |
|
51 | installLocal(PM2, module_filepath, opts, cb)
|
52 | }
|
53 |
|
54 | function retrieveRemote(url, dest, cb) {
|
55 | Common.logMod(`Retrieving remote package ${url}...`)
|
56 |
|
57 | var wget = spawn('wget', [url, '-O', dest, '-q'], {
|
58 | stdio : 'inherit',
|
59 | env: process.env,
|
60 | shell : true
|
61 | })
|
62 |
|
63 | wget.on('error', (err) => {
|
64 | console.error(err.stack || err)
|
65 | })
|
66 |
|
67 | wget.on('close', (code) => {
|
68 | if (code !== 0)
|
69 | return cb(new Error('Could not download'))
|
70 | return cb(null)
|
71 | })
|
72 | }
|
73 |
|
74 | function installLocal(PM2, module_filepath, opts, cb) {
|
75 | Common.logMod(`Installing package ${module_filepath}`)
|
76 |
|
77 |
|
78 | getModuleName(module_filepath, function(err, module_name) {
|
79 | if (err) return cb(err)
|
80 |
|
81 | Common.logMod(`Module name is ${module_name}`)
|
82 |
|
83 | Common.logMod(`Depackaging module...`)
|
84 |
|
85 | var install_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
|
86 |
|
87 | require('mkdirp').sync(install_path)
|
88 |
|
89 | var install_instance = spawn('tar', ['zxf', module_filepath, '-C', install_path, '--strip-components 1'], {
|
90 | stdio : 'inherit',
|
91 | env: process.env,
|
92 | shell : true
|
93 | })
|
94 |
|
95 | install_instance.on('close', function(code) {
|
96 | Common.logMod(`Module depackaged in ${install_path}`)
|
97 | if (code == 0)
|
98 | return runInstall(PM2, install_path, module_name, opts, cb)
|
99 | return PM2.exitCli(1)
|
100 | });
|
101 |
|
102 | install_instance.on('error', function (err) {
|
103 | console.error(err.stack || err);
|
104 | });
|
105 | })
|
106 | }
|
107 |
|
108 | function deleteModulePath(module_name) {
|
109 | var sanitized = module_name.replace(/\./g, '')
|
110 | execSync(`rm -r ${path.join(cst.DEFAULT_MODULE_PATH, module_name)}`, { silent: true })
|
111 | }
|
112 |
|
113 | function runInstall(PM2, target_path, module_name, opts, cb) {
|
114 | var config_file = path.join(target_path, 'package.json')
|
115 | var conf
|
116 |
|
117 | try {
|
118 | conf = require(config_file)
|
119 | module_name = conf.name
|
120 | } catch(e) {
|
121 | Common.errMod(new Error('Cannot find package.json file with name attribute at least'));
|
122 | }
|
123 |
|
124 |
|
125 | opts.started_as_module = true
|
126 | opts.cwd = target_path
|
127 |
|
128 | if (needPrefix(conf))
|
129 | opts.name_prefix = module_name
|
130 |
|
131 | if (opts.install) {
|
132 | Common.logMod(`Running YARN install...`)
|
133 |
|
134 | sexec(`cd ${target_path} ; yarn install`, {silent: false}, function(code) {
|
135 |
|
136 | Common.logMod(`Starting ${target_path}`)
|
137 | PM2.start(conf, opts, function(err, data) {
|
138 | if (err) return cb(err)
|
139 |
|
140 | Configuration.setSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`, {
|
141 | source: 'tarball',
|
142 | install_url: opts.install_url,
|
143 | installed_at: Date.now()
|
144 | })
|
145 |
|
146 | Common.logMod(`Module INSTALLED and STARTED`)
|
147 | return cb(null, 'Module installed & Started')
|
148 | })
|
149 | })
|
150 | }
|
151 | else {
|
152 | PM2.start(conf, opts, function(err, data) {
|
153 | if (err) return cb(err)
|
154 |
|
155 | Configuration.setSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`, {
|
156 | source: 'tarball',
|
157 | install_url: opts.install_url,
|
158 | installed_at: Date.now()
|
159 | })
|
160 |
|
161 | Common.logMod(`Module INSTALLED and STARTED`)
|
162 | return cb(null, 'Module installed & Started')
|
163 | })
|
164 | }
|
165 | }
|
166 |
|
167 | function start(PM2, module_name, cb) {
|
168 | var module_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
|
169 | Common.printOut(cst.PREFIX_MSG_MOD + 'Starting TAR module ' + module_name);
|
170 | var package_json_path = path.join(module_path, 'package.json');
|
171 | var module_conf = Configuration.getSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`)
|
172 |
|
173 | try {
|
174 | var conf = require(package_json_path)
|
175 | } catch(e) {
|
176 | Common.printError(`Could not find package.json as ${package_json_path}`)
|
177 | return cb()
|
178 | }
|
179 |
|
180 | var opts = {};
|
181 |
|
182 | opts.started_as_module = true
|
183 | opts.cwd = module_path
|
184 |
|
185 | if (module_conf.install_url)
|
186 | opts.install_url = module_conf.install_url
|
187 |
|
188 | if (needPrefix(conf))
|
189 | opts.name_prefix = module_name
|
190 |
|
191 | PM2.start(conf, opts, function(err, data) {
|
192 | if (err) {
|
193 | Common.printError(`Could not start ${module_name} ${module_path}`)
|
194 | return cb()
|
195 | }
|
196 |
|
197 | Common.printOut(`${cst.PREFIX_MSG_MOD} Module ${module_name} STARTED`)
|
198 | return cb();
|
199 | })
|
200 | }
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 | function uninstall(PM2, module_name, cb) {
|
207 | var module_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
|
208 |
|
209 | Common.logMod(`Removing ${module_name} from auto startup`)
|
210 |
|
211 | try {
|
212 | var pkg = require(path.join(module_path, 'package.json'))
|
213 | } catch(e) {
|
214 | Common.errMod('Could not retrieve module package.json');
|
215 | return cb(e)
|
216 | }
|
217 |
|
218 | var apps = pkg.apps || pkg.pm2
|
219 | apps = [].concat(apps);
|
220 |
|
221 | |
222 |
|
223 |
|
224 | forEachLimit(apps, 1, (app, next) => {
|
225 | var app_name
|
226 |
|
227 | if (!app.name) {
|
228 | Common.renderApplicationName(app)
|
229 | }
|
230 |
|
231 | if (apps.length > 1)
|
232 | app_name = `${module_name}:${app.name}`
|
233 | else if (apps.length == 1 && pkg.name != apps[0].name)
|
234 | app_name = `${module_name}:${app.name}`
|
235 | else
|
236 | app_name = app.name
|
237 |
|
238 | PM2._operate('deleteProcessId', app_name, () => {
|
239 | deleteModulePath(module_name)
|
240 | next()
|
241 | })
|
242 | }, () => {
|
243 | Configuration.unsetSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`)
|
244 | cb(null)
|
245 | })
|
246 | }
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 | function getModuleName(module_filepath, cb) {
|
253 | var tmp_folder = path.join(os.tmpdir(), cst.MODULE_BASEFOLDER)
|
254 |
|
255 | var install_instance = spawn('tar', ['zxf', module_filepath, '-C', os.tmpdir(), `${cst.MODULE_BASEFOLDER}/package.json`], {
|
256 | stdio : 'inherit',
|
257 | env: process.env,
|
258 | shell : true
|
259 | })
|
260 |
|
261 | install_instance.on('close', function(code) {
|
262 | try {
|
263 | var pkg = JSON.parse(fs.readFileSync(path.join(tmp_folder, `package.json`)))
|
264 | return cb(null, pkg.name)
|
265 | } catch(e) {
|
266 | return cb(e)
|
267 | }
|
268 | });
|
269 | }
|
270 |
|
271 | function package(module_path, target_path, cb) {
|
272 | var base_folder = path.dirname(module_path)
|
273 | var module_folder_name = path.basename(module_path)
|
274 | var pkg = require(path.join(module_path, 'package.json'))
|
275 | var pkg_name = `${module_folder_name}-v${pkg.version.replace(/\./g, '-')}.tar.gz`
|
276 | var target_fullpath = path.join(target_path, pkg_name)
|
277 |
|
278 | var cmd = `tar zcf ${target_fullpath} -C ${base_folder} --transform 's,${module_folder_name},module,' ${module_folder_name}`
|
279 |
|
280 | Common.logMod(`Gziping ${module_path} to ${target_fullpath}`)
|
281 |
|
282 | var tar = exec(cmd, (err, sto, ste) => {
|
283 | if (err) {
|
284 | console.log(sto.toString().trim())
|
285 | console.log(ste.toString().trim())
|
286 | }
|
287 | })
|
288 |
|
289 | tar.on('close', function (code) {
|
290 | cb(code == 0 ? null : code, {
|
291 | package_name: pkg_name,
|
292 | path: target_fullpath
|
293 | })
|
294 | })
|
295 | }
|
296 |
|
297 | function publish(PM2, folder, cb) {
|
298 | var target_folder = folder ? path.resolve(folder) : process.cwd()
|
299 |
|
300 | try {
|
301 | var pkg = JSON.parse(fs.readFileSync(path.join(target_folder, 'package.json')).toString())
|
302 | } catch(e) {
|
303 | Common.errMod(`${process.cwd()} module does not contain any package.json`)
|
304 | process.exit(1)
|
305 | }
|
306 |
|
307 | if (!pkg.name) throw new Error('Attribute name should be present')
|
308 | if (!pkg.version) throw new Error('Attribute version should be present')
|
309 | if (!pkg.pm2 && !pkg.apps) throw new Error('Attribute apps should be present')
|
310 |
|
311 | var current_path = target_folder
|
312 | var module_name = path.basename(current_path)
|
313 | var target_path = os.tmpdir()
|
314 |
|
315 | Common.logMod(`Starting publishing procedure for ${module_name}@${pkg.version}`)
|
316 |
|
317 | package(current_path, target_path, (err, res) => {
|
318 | if (err) {
|
319 | Common.errMod('Can\'t package, exiting')
|
320 | process.exit(1)
|
321 | }
|
322 |
|
323 | Common.logMod(`Package [${pkg.name}] created in path ${res.path}`)
|
324 |
|
325 | var data = {
|
326 | module_data: {
|
327 | file: res.path,
|
328 | content_type: 'content/gzip'
|
329 | },
|
330 | id: pkg.name,
|
331 | name: pkg.name,
|
332 | version: pkg.version
|
333 | };
|
334 |
|
335 | var uri = `${PM2.pm2_configuration.registry}/api/v1/modules`
|
336 | Common.logMod(`Sending Package to remote ${pkg.name} ${uri}`)
|
337 |
|
338 | require('needle')
|
339 | .post(uri, data, { multipart: true }, function(err, res, body) {
|
340 | if (err) {
|
341 | Common.errMod(err)
|
342 | process.exit(1)
|
343 | }
|
344 | if (res.statusCode !== 200) {
|
345 | Common.errMod(`${pkg.name}-${pkg.version}: ${res.body.msg}`)
|
346 | process.exit(1)
|
347 | }
|
348 | Common.logMod(`Module ${module_name} published under version ${pkg.version}`)
|
349 | process.exit(0)
|
350 | })
|
351 | })
|
352 | }
|
353 |
|
354 | function needPrefix(conf) {
|
355 | if ((conf.apps && conf.apps.length > 1) ||
|
356 | (conf.pm2 && conf.pm2.length > 1) ||
|
357 | (conf.apps.length == 1 && conf.name != conf.apps[0].name))
|
358 | return true
|
359 | return false
|
360 | }
|