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.
- package/bin/cloudron-appstore +14 -6
- package/bin/cloudron-build +2 -2
- package/package.json +1 -1
- package/src/appstore-actions.js +71 -104
package/bin/cloudron-appstore
CHANGED
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
5
|
+
const appstoreActions = require('../src/appstore-actions.js'),
|
|
6
|
+
Command = require('commander').Command;
|
|
7
7
|
|
|
8
|
-
|
|
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('
|
|
31
|
-
.description('Tag the repo
|
|
32
|
-
.action(appstoreActions.
|
|
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')
|
package/bin/cloudron-build
CHANGED
|
@@ -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
package/src/appstore-actions.js
CHANGED
|
@@ -25,7 +25,7 @@ exports = module.exports = {
|
|
|
25
25
|
revoke,
|
|
26
26
|
approve,
|
|
27
27
|
|
|
28
|
-
|
|
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=${
|
|
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(
|
|
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(
|
|
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)
|
|
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(
|
|
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)
|
|
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
|
|
273
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
373
|
-
const
|
|
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 ${
|
|
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
|
-
|
|
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
|
|
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)
|
|
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
|
-
}
|