cloudron 6.0.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,32 +1,14 @@
1
- /* jshint node:true */
2
-
3
- 'use strict';
4
-
5
- const assert = require('assert'),
6
- config = require('./config.js'),
7
- execSync = require('child_process').execSync,
8
- fs = require('fs'),
9
- { exit, locateManifest } = require('./helper.js'),
10
- manifestFormat = require('@cloudron/manifest-format'),
11
- path = require('path'),
12
- readline = require('./readline.js'),
13
- safe = require('safetydance'),
14
- superagent = require('@cloudron/superagent'),
15
- Table = require('easy-table');
16
-
17
- exports = module.exports = {
18
- login,
19
- logout,
20
- info,
21
- listVersions,
22
- submit,
23
- upload,
24
- revoke,
25
- approve,
26
- verifyManifest,
27
-
28
- notify
29
- };
1
+ import assert from 'assert';
2
+ import * as config from './config.js';
3
+ import { execSync } from 'child_process';
4
+ import fs from 'fs';
5
+ import { exit, locateManifest, parseChangelog } from './helper.js';
6
+ import manifestFormat from '@cloudron/manifest-format';
7
+ import path from 'path';
8
+ import * as readline from './readline.js';
9
+ import safe from '@cloudron/safetydance';
10
+ import superagent from '@cloudron/superagent';
11
+ import Table from 'easy-table';
30
12
 
31
13
  const NO_MANIFEST_FOUND_ERROR_STRING = 'No CloudronManifest.json found';
32
14
 
@@ -156,29 +138,6 @@ async function listVersions(localOptions, cmd) {
156
138
  console.log(t.toString());
157
139
  }
158
140
 
159
- function parseChangelog(file, version) {
160
- let changelog = '';
161
- const data = safe.fs.readFileSync(file, 'utf8');
162
- if (!data) return null;
163
- const lines = data.split('\n');
164
-
165
- version = version.replace(/-.*/, ''); // remove any prerelease
166
-
167
- let i;
168
- for (i = 0; i < lines.length; i++) {
169
- if (lines[i] === '[' + version + ']') break;
170
- }
171
-
172
- for (i = i + 1; i < lines.length; i++) {
173
- if (lines[i] === '') continue;
174
- if (lines[i][0] === '[') break;
175
-
176
- changelog += lines[i] + '\n';
177
- }
178
-
179
- return changelog;
180
- }
181
-
182
141
  async function addVersion(manifest, baseDir, options) {
183
142
  assert.strictEqual(typeof manifest, 'object');
184
143
  assert.strictEqual(typeof baseDir, 'string');
@@ -210,8 +169,7 @@ async function addVersion(manifest, baseDir, options) {
210
169
  }
211
170
 
212
171
  if (manifest.changelog.slice(0, 7) === 'file://') {
213
- let changelogPath = manifest.changelog.slice(7);
214
- changelogPath = path.isAbsolute(changelogPath) ? changelogPath : path.join(baseDir, changelogPath);
172
+ const changelogPath = path.isAbsolute(manifest.changelog.slice(7)) ? manifest.changelog.slice(7) : path.join(baseDir, manifest.changelog.slice(7));
215
173
  manifest.changelog = parseChangelog(changelogPath, manifest.version);
216
174
  if (!manifest.changelog) throw new Error('Bad changelog format or missing changelog for this version');
217
175
  }
@@ -277,7 +235,7 @@ async function verifyManifest(localOptions, cmd) {
277
235
  const manifest = result.manifest;
278
236
 
279
237
  const sourceDir = path.dirname(manifestFilePath);
280
- const appConfig = config.getAppConfig(sourceDir);
238
+ const appConfig = config.getAppBuildConfig(sourceDir);
281
239
 
282
240
  // image can be passed in options for buildbot
283
241
  if (options.image) {
@@ -324,7 +282,7 @@ async function upload(localOptions, cmd) {
324
282
  const manifest = result.manifest;
325
283
 
326
284
  const sourceDir = path.dirname(manifestFilePath);
327
- const appConfig = config.getAppConfig(sourceDir);
285
+ const appConfig = config.getAppBuildConfig(sourceDir);
328
286
 
329
287
  // image can be passed in options for buildbot
330
288
  if (options.image) {
@@ -467,11 +425,11 @@ async function notify() {
467
425
  if (result.error) return exit(new Error(`Invalid CloudronManifest.json: ${result.error.message}`));
468
426
  const { manifest } = result;
469
427
 
470
- let postContent = null;
428
+ let postContent;
471
429
  if (manifest.changelog.slice(0, 7) === 'file://') {
472
430
  const baseDir = path.dirname(manifestFilePath);
473
- let changelogPath = manifest.changelog.slice(7);
474
- changelogPath = path.isAbsolute(changelogPath) ? changelogPath : path.join(baseDir, changelogPath);
431
+ const manifestChangelogPath = manifest.changelog.slice(7);
432
+ const changelogPath = path.isAbsolute(manifestChangelogPath) ? manifestChangelogPath : path.join(baseDir, manifestChangelogPath);
475
433
  const changelog = parseChangelog(changelogPath, manifest.version);
476
434
  if (!changelog) return exit('Bad changelog format or missing changelog for this version');
477
435
  postContent = `[${manifest.version}]\n${changelog}\n`;
@@ -511,3 +469,17 @@ async function notify() {
511
469
  if (postResponse.status !== 200) return exit(`Unable to create changelog post: ${requestError(postResponse)}`);
512
470
  console.log('Posted to forum');
513
471
  }
472
+
473
+ export default {
474
+ login,
475
+ logout,
476
+ info,
477
+ listVersions,
478
+ submit,
479
+ upload,
480
+ revoke,
481
+ approve,
482
+ verifyManifest,
483
+
484
+ notify
485
+ };
@@ -1,24 +1,14 @@
1
- #!/usr/bin/env node
2
-
3
- 'use strict';
4
-
5
- exports = module.exports = {
6
- encrypt,
7
- decrypt,
8
- encryptFilename,
9
- decryptFilename,
10
- decryptDir
11
- };
12
-
13
- const assert = require('assert'),
14
- crypto = require('crypto'),
15
- debug = require('debug')('cloudron-backup'),
16
- fs = require('fs'),
17
- path = require('path'),
18
- readlinePromises = require('readline/promises'),
19
- safe = require('safetydance'),
20
- stream = require('stream/promises'),
21
- TransformStream = require('stream').Transform;
1
+ import assert from 'assert';
2
+ import crypto from 'crypto';
3
+ import createDebug from 'debug';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import readlinePromises from 'readline/promises';
7
+ import safe from '@cloudron/safetydance';
8
+ import stream from 'stream/promises';
9
+ import { Transform as TransformStream } from 'stream';
10
+
11
+ const debug = createDebug('cloudron-backup');
22
12
 
23
13
  function encryptFilePath(filePath, encryption) {
24
14
  assert.strictEqual(typeof filePath, 'string');
@@ -52,9 +42,9 @@ function decryptFilePath(filePath, encryption) {
52
42
  const buffer = Buffer.from(part, 'base64');
53
43
  const iv = buffer.subarray(0, 16);
54
44
 
55
- const decrypt = crypto.createDecipheriv('aes-256-cbc', Buffer.from(encryption.filenameKey, 'hex'), iv);
56
- const plainText = decrypt.update(buffer.subarray(16));
57
- const plainTextString = Buffer.concat([ plainText, decrypt.final() ]).toString('utf8');
45
+ const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(encryption.filenameKey, 'hex'), iv);
46
+ const plainText = decipher.update(buffer.subarray(16));
47
+ const plainTextString = Buffer.concat([ plainText, decipher.final() ]).toString('utf8');
58
48
  const hmac = crypto.createHmac('sha256', Buffer.from(encryption.filenameHmacKey, 'hex'));
59
49
  if (!hmac.update(plainTextString).digest().subarray(0, 16).equals(iv)) return { error: new Error(`mac error decrypting part ${part} of path ${filePath}`) };
60
50
 
@@ -332,3 +322,11 @@ async function decryptFilename(filePath, options) {
332
322
  if (error) return exit(error);
333
323
  console.log(decryptedFilePath);
334
324
  }
325
+
326
+ export default {
327
+ encrypt,
328
+ decrypt,
329
+ encryptFilename,
330
+ decryptFilename,
331
+ decryptDir
332
+ };
@@ -1,31 +1,20 @@
1
- 'use strict';
2
-
3
- exports = module.exports = {
4
- login,
5
- build,
6
- logs,
7
- status,
8
- push
9
- };
10
-
11
- const assert = require('assert'),
12
- config = require('./config.js'),
13
- crypto = require('crypto'),
14
- { EventSource } = require('eventsource'),
15
- execSync = require('child_process').execSync,
16
- exit = require('./helper.js').exit,
17
- fs = require('fs'),
18
- helper = require('./helper.js'),
19
- manifestFormat = require('@cloudron/manifest-format'),
20
- micromatch = require('micromatch'),
21
- os = require('os'),
22
- path = require('path'),
23
- readline = require('./readline.js'),
24
- safe = require('safetydance'),
25
- stream = require('stream/promises'),
26
- superagent = require('@cloudron/superagent'),
27
- tar = require('tar-fs'),
28
- { URL } = require('url');
1
+ import assert from 'assert';
2
+ import * as config from './config.js';
3
+ import crypto from 'crypto';
4
+ import { EventSource } from 'eventsource';
5
+ import { execSync } from 'child_process';
6
+ import { exit } from './helper.js';
7
+ import fs from 'fs';
8
+ import * as helper from './helper.js';
9
+ import manifestFormat from '@cloudron/manifest-format';
10
+ import micromatch from 'micromatch';
11
+ import os from 'os';
12
+ import path from 'path';
13
+ import * as readline from './readline.js';
14
+ import safe from '@cloudron/safetydance';
15
+ import stream from 'stream/promises';
16
+ import superagent from '@cloudron/superagent';
17
+ import tar from 'tar-fs';
29
18
 
30
19
  function requestError(response) {
31
20
  if (response.status === 401 || response.status === 403) return 'Invalid token. Use cloudron build login again.';
@@ -33,68 +22,42 @@ function requestError(response) {
33
22
  return `${response.status} message: ${response.body?.message || response.text || null}`;
34
23
  }
35
24
 
36
- // analyzes options and merges with any existing build service config
37
- async function resolveBuildServiceConfig(options) {
38
- const buildService = config.getBuildServiceConfig();
39
- if (!buildService.type) buildService.type = 'local'; // default
40
-
41
- if (options.local) {
42
- buildService.type = 'local';
43
- } else if (options.setBuildService) { // stash for future use
44
- buildService.token = null;
45
- buildService.url = null;
46
- buildService.type = 'remote';
47
-
48
- let url;
49
- if (typeof options.setBuildService === 'string') {
50
- url = options.setBuildService;
51
- } else {
52
- url = await readline.question('Enter build service URL: ', { });
53
- }
54
-
55
- if (url.indexOf('://') === -1) url = `https://${url}`;
56
- buildService.url = url;
57
- }
58
-
59
- if (options.buildServiceToken) buildService.token = options.buildServiceToken;
60
-
61
- config.setBuildServiceConfig(buildService);
62
-
63
- return buildService;
64
- }
65
25
 
66
26
  async function login(localOptions, cmd) {
67
27
  const options = cmd.optsWithGlobals();
68
28
 
69
- const buildServiceConfig = await resolveBuildServiceConfig(options);
29
+ const buildServiceConfig = config.getBuildServiceConfig();
30
+
31
+ let url = options.buildServiceUrl || buildServiceConfig.url;
32
+ if (!url) {
33
+ url = await readline.question('Build Service URL: ', {});
34
+ if (!url) return exit('No build service URL provided.');
35
+ }
36
+ if (url.indexOf('://') === -1) url = `https://${url}`;
70
37
 
71
- console.log('Build Service login' + ` (${buildServiceConfig.url}):`);
38
+ console.log(`Build Service login (${url}):`);
72
39
 
73
40
  const token = options.buildServiceToken || await readline.question('Token: ', {});
74
41
 
75
- const response = await superagent.get(`${buildServiceConfig.url}/api/v1/profile`).query({ accessToken: token }).ok(() => true);
42
+ const response = await superagent.get(`${url}/api/v1/profile`).query({ accessToken: token }).ok(() => true);
76
43
  if (response.status === 401 || response.status === 403) return exit(`Authentication error: ${requestError(response)}`);
77
44
  if (response.status !== 200) return exit(`Unexpected response: ${requestError(response)}`);
78
45
 
79
- buildServiceConfig.token = token;
80
- config.setBuildServiceConfig(buildServiceConfig);
46
+ config.setBuildServiceConfig({ type: 'remote', url, token });
81
47
 
82
48
  console.log('Login successful.');
83
49
  }
84
50
 
85
- async function followBuildLog(buildId, raw) {
51
+ function logout() {
52
+ config.setBuildServiceConfig({});
53
+ console.log('Logged out.');
54
+ }
55
+
56
+ async function followBuildLog(buildServiceConfig, buildId, raw) {
86
57
  assert.strictEqual(typeof buildId, 'string');
87
58
  assert.strictEqual(typeof raw, 'boolean');
88
59
 
89
- const rawUrl = config.getBuildServiceConfig().url;
90
- let tmp;
91
- try {
92
- tmp = new URL(rawUrl);
93
- } catch {
94
- tmp = new URL(`http://${rawUrl}`); // if it fails, prepend http://
95
- }
96
-
97
- const es = new EventSource(`${tmp.origin}/api/v1/builds/${buildId}/logstream?accessToken=${config.getBuildServiceConfig().token}`);
60
+ const es = new EventSource(`${buildServiceConfig.url}/api/v1/builds/${buildId}/logstream?accessToken=${buildServiceConfig.token}`);
98
61
 
99
62
  let prevId = null, prevWasStatus = false;
100
63
 
@@ -155,9 +118,7 @@ async function followBuildLog(buildId, raw) {
155
118
  });
156
119
  }
157
120
 
158
- async function getStatus(buildId) {
159
- const buildServiceConfig = config.getBuildServiceConfig();
160
-
121
+ async function getStatus(buildServiceConfig, buildId) {
161
122
  const response2 = await superagent.get(`${buildServiceConfig.url}/api/v1/builds/${buildId}`)
162
123
  .query({ accessToken: buildServiceConfig.token })
163
124
  .ok(() => true);
@@ -165,7 +126,7 @@ async function getStatus(buildId) {
165
126
  return response2.body.status;
166
127
  }
167
128
 
168
- function dockerignoreMatcher(dockerignorePath) {
129
+ export function dockerignoreMatcher(dockerignorePath) {
169
130
  let patterns = [];
170
131
 
171
132
  if (fs.existsSync(dockerignorePath)) {
@@ -183,8 +144,8 @@ function dockerignoreMatcher(dockerignorePath) {
183
144
  patterns = patterns.filter(function (line) { return line.length !== 0; });
184
145
  }
185
146
 
186
- return function ignore(path) {
187
- return micromatch([ path ], patterns, { dot: true }).length == 1;
147
+ return function ignore(filePath) {
148
+ return micromatch([ filePath ], patterns, { dot: true }).length == 1;
188
149
  };
189
150
  }
190
151
 
@@ -225,9 +186,9 @@ async function buildLocal(manifest, sourceDir, appConfig, options) {
225
186
  const match = /.*@sha256:(.*)/.exec(result.trim());
226
187
  if (!match) exit('Failed to detect sha256');
227
188
 
228
- appConfig.dockerImage = `${dockerImage}`;
189
+ appConfig.dockerImage = dockerImage;
229
190
  appConfig.dockerImageSha256 = match[1]; // stash this separately for now
230
- config.setAppConfig(sourceDir, appConfig);
191
+ config.setAppBuildConfig(sourceDir, appConfig);
231
192
  }
232
193
 
233
194
  async function buildRemote(manifest, sourceDir, appConfig, options) {
@@ -290,16 +251,16 @@ async function buildRemote(manifest, sourceDir, appConfig, options) {
290
251
  const buildId = response.body.id;
291
252
  console.log(`BuildId: ${buildId}`);
292
253
 
293
- const [logsError] = await safe(followBuildLog(buildId, !!options.raw));
254
+ const [logsError] = await safe(followBuildLog(buildServiceConfig, buildId, !!options.raw));
294
255
  if (logsError) console.log(`Failed to get logs: ${logsError.message}`);
295
256
 
296
- const [statusError, status] = await safe(getStatus(buildId));
257
+ const [statusError, buildStatus] = await safe(getStatus(buildServiceConfig, buildId));
297
258
  if (statusError) return exit(`Failed to get status: ${statusError.message}`);
298
- if (status !== 'success') return exit('Failed to build app. See log output above.');
259
+ if (buildStatus !== 'success') return exit('Failed to build app. See log output above.');
299
260
 
300
261
  appConfig.dockerImage = dockerImage;
301
262
  // appConfig.dockerImageSha256 = match[1]; // stash this separately for now
302
- config.setAppConfig(sourceDir, appConfig);
263
+ config.setAppBuildConfig(sourceDir, appConfig);
303
264
 
304
265
  console.log(`Docker image: ${dockerImage}`);
305
266
  console.log('\nBuild successful');
@@ -320,8 +281,8 @@ async function build(localOptions, cmd) {
320
281
  const manifest = result.manifest;
321
282
  const sourceDir = path.dirname(manifestFilePath);
322
283
 
323
- const appConfig = config.getAppConfig(sourceDir);
324
- const buildServiceConfig = await resolveBuildServiceConfig(options);
284
+ const appConfig = config.getAppBuildConfig(sourceDir);
285
+ const buildServiceConfig = config.getBuildServiceConfig();
325
286
 
326
287
  let repository = appConfig.repository;
327
288
  if (!repository || options.setRepository) {
@@ -337,41 +298,48 @@ async function build(localOptions, cmd) {
337
298
  if (parts.length > 1) exit(`repository should not be a URL. Try again without ${parts[0]}://`);
338
299
 
339
300
  appConfig.repository = repository;
340
- config.setAppConfig(sourceDir, appConfig);
301
+ config.setAppBuildConfig(sourceDir, appConfig);
341
302
  }
342
303
 
343
304
  appConfig.gitCommit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); // when the build gets saved, save the gitCommit also
344
- if (buildServiceConfig.type === 'local') {
345
- await buildLocal(manifest, sourceDir, appConfig, options);
346
- } else if (buildServiceConfig.type === 'remote' && buildServiceConfig.url) {
305
+ if (buildServiceConfig.type === 'remote' && buildServiceConfig.url) {
347
306
  await buildRemote(manifest, sourceDir, appConfig, options);
348
307
  } else {
349
- exit('Unknown build service type or missing build service url. Rerun with --reset-build-service');
308
+ await buildLocal(manifest, sourceDir, appConfig, options);
350
309
  }
351
310
  }
352
311
 
353
312
  async function logs(localOptions, cmd) {
354
313
  const options = cmd.optsWithGlobals();
355
314
 
315
+ const buildServiceConfig = config.getBuildServiceConfig();
316
+ if (buildServiceConfig.type !== 'remote') return exit('This command only works with the build service. Use cloudron build login first.');
317
+
356
318
  if (!options.id) return exit('buildId is required');
357
319
 
358
- const [logsError] = await safe(followBuildLog(options.id, !!options.raw));
320
+ const [logsError] = await safe(followBuildLog(buildServiceConfig, options.id, !!options.raw));
359
321
  if (logsError) console.log(`Failed to get logs: ${logsError.message}`);
360
322
  }
361
323
 
362
324
  async function status(localOptions, cmd) {
363
325
  const options = cmd.optsWithGlobals();
364
326
 
327
+ const buildServiceConfig = config.getBuildServiceConfig();
328
+ if (buildServiceConfig.type !== 'remote') return exit('This command only works with the build service. Use cloudron build login first.');
329
+
365
330
  if (!options.id) return exit('buildId is required');
366
331
 
367
- const [statusError, status] = await safe(getStatus(options.id));
332
+ const [statusError, buildStatus] = await safe(getStatus(buildServiceConfig, options.id));
368
333
  if (statusError) return exit(`Failed to get status: ${statusError.message}`);
369
- console.log(status);
334
+ console.log(buildStatus);
370
335
  }
371
336
 
372
337
  async function push(localOptions, cmd) {
373
338
  const options = cmd.optsWithGlobals();
374
339
 
340
+ const buildServiceConfig = config.getBuildServiceConfig();
341
+ if (buildServiceConfig.type !== 'remote') return exit('This command only works with the build service. Use cloudron build login first.');
342
+
375
343
  if (!options.id) return exit('buildId is required');
376
344
  let repository, tag;
377
345
  if (options.image) {
@@ -383,18 +351,64 @@ async function push(localOptions, cmd) {
383
351
  tag = options.tag;
384
352
  }
385
353
 
386
- const buildServiceConfig = await resolveBuildServiceConfig(options);
387
-
388
354
  const response = await superagent.post(`${buildServiceConfig.url}/api/v1/builds/${options.id}/push`)
389
355
  .query({ accessToken: buildServiceConfig.token })
390
356
  .send({ dockerImageRepo: repository, dockerImageTag: tag })
391
357
  .ok(() => true);
392
358
  if (response.status !== 201) return exit(`Failed to push: ${requestError(response)}`);
393
359
 
394
- const [logsError] = await safe(followBuildLog(options.id, !!options.raw));
360
+ const [logsError] = await safe(followBuildLog(buildServiceConfig, options.id, !!options.raw));
395
361
  if (logsError) console.log(`Failed to get logs: ${logsError.message}`);
396
362
 
397
- const [statusError, status] = await safe(getStatus(options.id));
363
+ const [statusError, buildStatus] = await safe(getStatus(buildServiceConfig, options.id));
398
364
  if (statusError) return exit(`Failed to get status: ${statusError.message}`);
399
- if (status !== 'success') return exit('Failed to push app. See log output above.');
365
+ if (buildStatus !== 'success') return exit('Failed to push app. See log output above.');
366
+ }
367
+
368
+ async function clear(/* localOptions, cmd */) {
369
+ // const options = cmd.optsWithGlobals();
370
+
371
+ // try to find the manifest of this project
372
+ const manifestFilePath = helper.locateManifest();
373
+ if (!manifestFilePath) return exit('No CloudronManifest.json found');
374
+
375
+ const sourceDir = path.dirname(manifestFilePath);
376
+
377
+ config.unsetAppBuildConfig(sourceDir);
400
378
  }
379
+
380
+ async function info(/* localOptions, cmd */) {
381
+ // const options = cmd.optsWithGlobals();
382
+
383
+ const buildService = config.getBuildServiceConfig();
384
+ console.log(`Build service type: ${buildService.type || 'local'}`);
385
+ if (buildService.type === 'remote') console.log(`Build service URL: ${buildService.url}`);
386
+
387
+ // try to find the manifest of this project
388
+ const manifestFilePath = helper.locateManifest();
389
+ if (!manifestFilePath) return exit();
390
+
391
+ const sourceDir = path.dirname(manifestFilePath);
392
+ const appConfig = config.getAppBuildConfig(sourceDir);
393
+
394
+ console.log('Build info');
395
+ if (appConfig?.dockerImage) {
396
+ console.log(` Image: ${appConfig.dockerImage}`); // repo:tag
397
+ console.log(` Git commit: ${appConfig.gitCommit || '-'}`);
398
+ console.log(` Image SHA256: ${appConfig.dockerImageSha256 || '-'}`);
399
+ } else {
400
+ console.log(' Image: not built');
401
+ }
402
+ }
403
+
404
+ export default {
405
+ login,
406
+ logout,
407
+ build,
408
+ logs,
409
+ status,
410
+ push,
411
+ clear,
412
+ info,
413
+ dockerignoreMatcher
414
+ };
package/src/completion.js CHANGED
@@ -1,11 +1,7 @@
1
- /* jshint node:true */
1
+ import * as helper from './helper.js';
2
+ import util from 'util';
2
3
 
3
- 'use strict';
4
-
5
- const helper = require('./helper.js'),
6
- util = require('util');
7
-
8
- exports = module.exports = function (options, cmd) {
4
+ export default function (options, cmd) {
9
5
  var completion = '';
10
6
 
11
7
  const commands = [];
@@ -39,4 +35,4 @@ exports = module.exports = function (options, cmd) {
39
35
  }
40
36
 
41
37
  console.log(completion);
42
- };
38
+ }