cloudron 5.11.6 → 5.11.7

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.
@@ -2,10 +2,17 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- const { program } = require('commander'),
6
- appstoreActions = require('../src/appstore-actions.js');
5
+ const appstoreActions = require('../src/appstore-actions.js'),
6
+ Command = require('commander').Command;
7
7
 
8
- program.version(require('../package.json').version);
8
+ const version = require('../package.json').version;
9
+
10
+ const program = new Command();
11
+ program.version(version);
12
+
13
+ // global options. IMPORTANT: These cannot conflict with global options!
14
+ program
15
+ .option('--appstore-token <token>', 'AppStore token');
9
16
 
10
17
  program.command('login')
11
18
  .description('Login to the appstore')
@@ -27,9 +34,9 @@ program.command('approve')
27
34
  .option('--appstore-id <appid@version>', 'Appstore id and version')
28
35
  .action(appstoreActions.approve);
29
36
 
30
- program.command('publish <version>')
31
- .description('Tag the repo and publish')
32
- .action(appstoreActions.publish);
37
+ program.command('tag <version>')
38
+ .description('Tag the repo')
39
+ .action(appstoreActions.tag);
33
40
 
34
41
  program.command('revoke')
35
42
  .description('Revoke a published app version')
@@ -47,6 +54,7 @@ program.command('upload')
47
54
  .action(appstoreActions.upload);
48
55
 
49
56
  program.command('versions')
57
+ .alias('list')
50
58
  .description('List published versions')
51
59
  .option('--appstore-id <id>', 'Appstore id')
52
60
  .option('--raw', 'Dump versions as json')
@@ -12,9 +12,9 @@ function collectArgs(value, collected) {
12
12
  return collected;
13
13
  }
14
14
 
15
- // global options
15
+ // global options. IMPORTANT: These cannot conflict with global options!
16
16
  program.option('--server <server>', 'Cloudron domain')
17
- .option('--token, --build-service-token <token>', 'Build service token')
17
+ .option('--build-token, --build-service-token <token>', 'Build service token')
18
18
  .option('--url, --set-build-service [buildservice url]', 'Set build service URL. This build service is automatically used for future calls from this project');
19
19
 
20
20
  program.command('build', { isDefault: true })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudron",
3
- "version": "5.11.6",
3
+ "version": "5.11.7",
4
4
  "license": "MIT",
5
5
  "description": "Cloudron Commandline Tool",
6
6
  "main": "main.js",
@@ -25,7 +25,7 @@ exports = module.exports = {
25
25
  revoke,
26
26
  approve,
27
27
 
28
- publish
28
+ tag
29
29
  };
30
30
 
31
31
  const NO_MANIFEST_FOUND_ERROR_STRING = 'No CloudronManifest.json found';
@@ -36,10 +36,12 @@ function requestError(response) {
36
36
  return `${response.statusCode} message: ${response.body.message || JSON.stringify(response.body)}`; // body is sometimes just a string like in 401
37
37
  }
38
38
 
39
- function createRequest(method, apiPath) {
39
+ function createRequest(method, apiPath, options) {
40
+ const token = options.appstoreToken || config.appStoreToken();
41
+
40
42
  let url = `${config.appStoreOrigin()}${apiPath}`;
41
43
  if (url.includes('?')) url += '&'; else url += '?';
42
- url += `accessToken=${config.appStoreToken()}`;
44
+ url += `accessToken=${token}`;
43
45
  const request = superagent(method, url);
44
46
  request.retry(3);
45
47
  request.ok(() => true);
@@ -63,7 +65,7 @@ async function getAppstoreId(appstoreId) {
63
65
  return [manifest.id, manifest.version];
64
66
  }
65
67
 
66
- async function authenticate(options) {
68
+ async function authenticate(options) { // maybe we can use options.token to valid using a profile call?
67
69
  if (!options.hideBanner) {
68
70
  const webDomain = config.appStoreOrigin().replace('https://api.', '');
69
71
  console.log(`${webDomain} login` + ` (If you do not have one, sign up at https://${webDomain}/console.html#/register)`);
@@ -101,7 +103,8 @@ async function authenticate(options) {
101
103
  console.log('Login successful.');
102
104
  }
103
105
 
104
- async function login(options) {
106
+ async function login(localOptions, cmd) {
107
+ const options = cmd.optsWithGlobals();
105
108
  await authenticate(options);
106
109
  }
107
110
 
@@ -110,11 +113,12 @@ function logout() {
110
113
  console.log('Done.');
111
114
  }
112
115
 
113
- async function info(options) {
116
+ async function info(localOptions, cmd) {
117
+ const options = cmd.optsWithGlobals();
114
118
  const [id, version] = await getAppstoreId(options.appstoreId);
115
119
 
116
- const response = await createRequest('GET', `/api/v1/developers/apps/${id}/versions/${version}`);
117
- if (response.statusCode !== 200) throw new Error(`Failed to list domains: ${requestError(response)}`);
120
+ const response = await createRequest('GET', `/api/v1/developers/apps/${id}/versions/${version}`, options);
121
+ if (response.statusCode !== 200) return exit(new Error(`Failed to list versions: ${requestError(response)}`));
118
122
 
119
123
  const manifest = response.body.manifest;
120
124
  console.log('id: %s', manifest.id);
@@ -125,11 +129,12 @@ async function info(options) {
125
129
  console.log('contactEmail: %s', manifest.contactEmail);
126
130
  }
127
131
 
128
- async function listVersions(options) {
132
+ async function listVersions(localOptions, cmd) {
133
+ const options = cmd.optsWithGlobals();
129
134
  const [id] = await getAppstoreId(options.appstoreId);
130
135
 
131
- const response = await createRequest('GET', `/api/v1/developers/apps/${id}/versions`);
132
- if (response.statusCode !== 200) throw new Error(`Failed to list domains: ${requestError(response)}`);
136
+ const response = await createRequest('GET', `/api/v1/developers/apps/${id}/versions`, options);
137
+ if (response.statusCode !== 200) return exit(new Error(`Failed to list versions: ${requestError(response)}`));
133
138
 
134
139
  if (response.body.versions.length === 0) return console.log('No versions found.');
135
140
 
@@ -150,17 +155,6 @@ async function listVersions(options) {
150
155
  console.log(t.toString());
151
156
  }
152
157
 
153
- async function addApp(manifest, baseDir) {
154
- assert.strictEqual(typeof manifest, 'object');
155
- assert.strictEqual(typeof baseDir, 'string');
156
-
157
- const request = createRequest('POST', '/api/v1/developers/apps');
158
- request.send({ id: manifest.id });
159
- const response = await request;
160
- if (response.statusCode === 409) return; // already exists
161
- if (response.statusCode !== 201) return exit(`Failed to add app: ${requestError(response)}`);
162
- }
163
-
164
158
  function parseChangelog(file, version) {
165
159
  let changelog = '';
166
160
  const data = safe.fs.readFileSync(file, 'utf8');
@@ -184,9 +178,10 @@ function parseChangelog(file, version) {
184
178
  return changelog;
185
179
  }
186
180
 
187
- async function addVersion(manifest, baseDir) {
181
+ async function addVersion(manifest, baseDir, options) {
188
182
  assert.strictEqual(typeof manifest, 'object');
189
183
  assert.strictEqual(typeof baseDir, 'string');
184
+ assert.strictEqual(typeof options, 'object');
190
185
 
191
186
  let iconFilePath = null;
192
187
  if (manifest.icon) {
@@ -220,7 +215,7 @@ async function addVersion(manifest, baseDir) {
220
215
  if (!manifest.changelog) throw new Error('Bad changelog format or missing changelog for this version');
221
216
  }
222
217
 
223
- const request = createRequest('POST', `/api/v1/developers/apps/${manifest.id}/versions`);
218
+ const request = createRequest('POST', `/api/v1/developers/apps/${manifest.id}/versions`, options);
224
219
  if (iconFilePath) request.attach('icon', iconFilePath);
225
220
  request.attach('manifest', Buffer.from(JSON.stringify(manifest)), 'manifest');
226
221
  const response = await request;
@@ -228,9 +223,10 @@ async function addVersion(manifest, baseDir) {
228
223
  if (response.statusCode !== 204) throw new Error(`Failed to publish version: ${requestError(response)}`);
229
224
  }
230
225
 
231
- async function updateVersion(manifest, baseDir) {
226
+ async function updateVersion(manifest, baseDir, options) {
232
227
  assert.strictEqual(typeof manifest, 'object');
233
228
  assert.strictEqual(typeof baseDir, 'string');
229
+ assert.strictEqual(typeof options, 'object');
234
230
 
235
231
  let iconFilePath = null;
236
232
  if (manifest.icon) {
@@ -262,59 +258,15 @@ async function updateVersion(manifest, baseDir) {
262
258
  if (!manifest.changelog) throw new Error('Could not read changelog or missing version changes');
263
259
  }
264
260
 
265
- const request = createRequest('PUT', `/api/v1/developers/apps/${manifest.id}/versions/${manifest.version}`);
261
+ const request = createRequest('PUT', `/api/v1/developers/apps/${manifest.id}/versions/${manifest.version}`, options);
266
262
  if (iconFilePath) request.attach('icon', iconFilePath);
267
263
  request.attach('manifest', Buffer.from(JSON.stringify(manifest)), 'manifest');
268
264
  const response = await request;
269
265
  if (response.statusCode !== 204) throw new Error(`Failed to publish version: ${requestError(response)}`);
270
266
  }
271
267
 
272
- async function revokeVersion(appstoreId, version) {
273
- assert.strictEqual(typeof appstoreId, 'string');
274
- assert.strictEqual(typeof version, 'string');
275
-
276
- const response = await createRequest('POST', `/api/v1/developers/apps/${appstoreId}/versions/${version}/revoke`);
277
- if (response.statusCode !== 200) return exit(`Failed to revoke version: ${requestError(response)}`);
278
-
279
- console.log('version revoked.');
280
- }
281
-
282
- async function approveVersion(appstoreId, version) {
283
- assert.strictEqual(typeof appstoreId, 'string');
284
- assert.strictEqual(typeof version, 'string');
285
-
286
- const response = await createRequest('POST', `/api/v1/developers/apps/${appstoreId}/versions/${version}/approve`);
287
- if (response.statusCode !== 200) return exit(`Failed to approve version: ${requestError(response)}`);
288
-
289
- console.log('Approved.');
290
- console.log('');
291
-
292
- const response2 = await createRequest('GET', `/api/v1/developers/apps/${appstoreId}/versions/${version}`);
293
- if (response2.statusCode !== 200) return exit(`Failed to list apps: ${requestError(response)}`);
294
-
295
- console.log('Changelog for forum update: ' + response2.body.manifest.forumUrl);
296
- console.log('');
297
- console.log('[' + version + ']');
298
- console.log(response2.body.manifest.changelog);
299
- console.log('');
300
- }
301
-
302
- async function submitAppForReview(manifest) {
303
- assert.strictEqual(typeof manifest, 'object');
304
-
305
- const response = await createRequest('POST', `/api/v1/developers/apps/${manifest.id}/versions/${manifest.version}/submit`);
306
- if (response.statusCode === 404) {
307
- console.log(`No version ${manifest.version} found. Please use 'cloudron apsptore upload' first`);
308
- return exit('Failed to submit app for review.');
309
- }
310
-
311
- if (response.statusCode !== 200) return exit(`Failed to submit app: ${requestError(response)}`);
312
-
313
- console.log('App submitted for review.');
314
- console.log('You will receive an email when approved.');
315
- }
316
-
317
- async function upload(options) {
268
+ async function upload(localOptions, cmd) {
269
+ const options = cmd.optsWithGlobals();
318
270
  // try to find the manifest of this project
319
271
  const manifestFilePath = locateManifest();
320
272
  if (!manifestFilePath) return exit(NO_MANIFEST_FOUND_ERROR_STRING);
@@ -341,14 +293,20 @@ async function upload(options) {
341
293
 
342
294
  // ensure the app is known on the appstore side
343
295
  const baseDir = path.dirname(manifestFilePath);
344
- await addApp(manifest, baseDir);
296
+
297
+ const request = createRequest('POST', '/api/v1/developers/apps', options);
298
+ request.send({ id: manifest.id });
299
+ const response = await request;
300
+ if (response.statusCode !== 409 && response.statusCode !== 201) return exit(`Failed to add app: ${requestError(response)}`); // 409 means already exists
345
301
  console.log(`Uploading ${manifest.id}@${manifest.version} (dockerImage: ${manifest.dockerImage}) for testing`);
346
302
 
347
- const [error2] = await safe(options.force ? updateVersion(manifest, baseDir) : addVersion(manifest, baseDir));
303
+ const [error2] = await safe(options.force ? updateVersion(manifest, baseDir, options) : addVersion(manifest, baseDir, options));
348
304
  if (error2) return exit(error2);
349
305
  }
350
306
 
351
- function submit() {
307
+ async function submit(localOptions, cmd) {
308
+ const options = cmd.optsWithGlobals();
309
+
352
310
  // try to find the manifest of this project
353
311
  const manifestFilePath = locateManifest();
354
312
  if (!manifestFilePath) return exit(NO_MANIFEST_FOUND_ERROR_STRING);
@@ -358,28 +316,57 @@ function submit() {
358
316
 
359
317
  const manifest = result.manifest;
360
318
 
361
- submitAppForReview(manifest, exit);
319
+ const response = await createRequest('POST', `/api/v1/developers/apps/${manifest.id}/versions/${manifest.version}/submit`, options);
320
+ if (response.statusCode === 404) {
321
+ console.log(`No version ${manifest.version} found. Please use 'cloudron apsptore upload' first`);
322
+ return exit('Failed to submit app for review.');
323
+ }
324
+
325
+ if (response.statusCode !== 200) return exit(`Failed to submit app: ${requestError(response)}`);
326
+
327
+ console.log('App submitted for review.');
328
+ console.log('You will receive an email when approved.');
362
329
  }
363
330
 
364
- async function revoke(options) {
331
+ async function revoke(localOptions, cmd) {
332
+ const options = cmd.optsWithGlobals();
365
333
  const [id, version] = await getAppstoreId(options.appstoreId);
366
334
  if (!version) return exit('--appstore-id must be of the format id@version');
367
335
 
368
336
  console.log(`Revoking ${id}@${version}`);
369
- await revokeVersion(id, version);
337
+
338
+ const response = await createRequest('POST', `/api/v1/developers/apps/${id}/versions/${version}/revoke`, options);
339
+ if (response.statusCode !== 200) return exit(`Failed to revoke version: ${requestError(response)}`);
340
+
341
+ console.log('version revoked.');
370
342
  }
371
343
 
372
- async function approve(options) {
373
- const [id, version] = await getAppstoreId(options.appstoreId);
344
+ async function approve(localOptions, cmd) {
345
+ const options = cmd.optsWithGlobals();
346
+
347
+ const [appstoreId, version] = await getAppstoreId(options.appstoreId);
374
348
 
375
349
  if (!version) return exit('--appstore-id must be of the format id@version');
376
350
 
377
- console.log(`Approving ${id}@${version}`);
351
+ console.log(`Approving ${appstoreId}@${version}`);
352
+
353
+ const response = await createRequest('POST', `/api/v1/developers/apps/${appstoreId}/versions/${version}/approve`, options);
354
+ if (response.statusCode !== 200) return exit(`Failed to approve version: ${requestError(response)}`);
355
+
356
+ console.log('Approved.');
357
+ console.log('');
358
+
359
+ const response2 = await createRequest('GET', `/api/v1/developers/apps/${appstoreId}/versions/${version}`, options);
360
+ if (response2.statusCode !== 200) return exit(`Failed to list apps: ${requestError(response)}`);
378
361
 
379
- await approveVersion(id, version);
362
+ console.log('Changelog for forum update: ' + response2.body.manifest.forumUrl);
363
+ console.log('');
364
+ console.log('[' + version + ']');
365
+ console.log(response2.body.manifest.changelog);
366
+ console.log('');
380
367
  }
381
368
 
382
- async function tagRepository(version) {
369
+ async function tag(version) {
383
370
  const basename = `${path.basename(process.cwd())}`;
384
371
  if (!basename.endsWith('-app')) return exit('Does not look like a app repo. Has to end with -app');
385
372
 
@@ -392,7 +379,7 @@ async function tagRepository(version) {
392
379
  if (!manifestFilePath) return exit('Could not locate CloudronManifest.json');
393
380
 
394
381
  const result = manifestFormat.parseFile(manifestFilePath);
395
- if (result.error) throw new Error(`Invalid CloudronManifest.json: ${result.error.message}`);
382
+ if (result.error) return exit(new Error(`Invalid CloudronManifest.json: ${result.error.message}`));
396
383
  const { manifest } = result;
397
384
 
398
385
  const latestVersion = latestTag.match(/v(.*)/)[1];
@@ -440,23 +427,3 @@ async function tagRepository(version) {
440
427
 
441
428
  console.log(`Created tag v${version} and pushed branch ${branch}`);
442
429
  }
443
-
444
- async function publish(version, options) {
445
- await tagRepository(version);
446
-
447
- const manifestFilePath = locateManifest();
448
- if (!manifestFilePath) return exit('Could not locate CloudronManifest.json');
449
-
450
- const latestRenovateCommit = safe.child_process.execSync('git log -n 1 --committer=renovatebot@cloudron.io --pretty="format:%h,%aI,%s"', { encoding: 'utf8' });
451
- if (!latestRenovateCommit) return exit('Could not find a commit from renovate bot');
452
-
453
- const [ abbrevHash, commitDate ] = latestRenovateCommit.split(',');
454
- const cleanDate = commitDate.replace(/T.*/, '').replace(/[-]/g, '');
455
- const repoDir = path.dirname(manifestFilePath);
456
- const repoName = path.basename(repoDir).replace('-app', '');
457
- const dockerImage = options.image || `cloudron/${repoName}:${cleanDate}-${abbrevHash}`;
458
-
459
- await upload({ image: dockerImage, force: false });
460
- await submit();
461
- await approve();
462
- }