cloudron 7.0.0 → 7.0.2
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 +3 -9
- package/bin/cloudron-build +1 -1
- package/package.json +1 -1
- package/src/actions.js +24 -6
- package/src/build-actions.js +30 -18
package/bin/cloudron
CHANGED
|
@@ -19,8 +19,9 @@ program.version(pkg.version);
|
|
|
19
19
|
// global options
|
|
20
20
|
program.option('--server <server>', 'Cloudron domain')
|
|
21
21
|
.option('--token <token>', 'Cloudron token')
|
|
22
|
-
.option('--allow-selfsigned', 'Accept self signed SSL certificate')
|
|
23
|
-
.option('--accept-selfsigned', 'Accept self signed SSL certificate')
|
|
22
|
+
.option('--allow-selfsigned', 'Accept self signed SSL certificate') // do not removed, used in e2e!
|
|
23
|
+
.option('--accept-selfsigned', 'Accept self signed SSL certificate')
|
|
24
|
+
.option('--no-wait', 'Do not wait for the operation to finish');
|
|
24
25
|
|
|
25
26
|
// these are separate binaries since global options are not applicable
|
|
26
27
|
program.command('appstore', 'Cloudron appstore commands');
|
|
@@ -158,7 +159,6 @@ program.command('init')
|
|
|
158
159
|
program.command('install')
|
|
159
160
|
.description('Install or update app')
|
|
160
161
|
.option('--image <docker image>', 'Docker image')
|
|
161
|
-
.option('--no-wait', 'Wait for healthcheck to succeed [false]')
|
|
162
162
|
.option('-p, --port-bindings [PORT=port,...]', 'Query/Set port bindings')
|
|
163
163
|
.option('-l, --location <domain>', 'Subdomain or full domain')
|
|
164
164
|
.option('-s, --secondary-domains [DOMAIN=domain,...]', 'Set secondary domains')
|
|
@@ -226,26 +226,22 @@ program.command('repair')
|
|
|
226
226
|
.description('Repair an installed application (re-configure)')
|
|
227
227
|
.option('--app <id/location>', 'App id or location')
|
|
228
228
|
.option('--image <docker image>', 'Docker image')
|
|
229
|
-
.option('--no-wait', 'Wait for healthcheck to succeed [false]')
|
|
230
229
|
.action(actions.repair);
|
|
231
230
|
|
|
232
231
|
program.command('restore')
|
|
233
232
|
.description('Restore app from known backup')
|
|
234
233
|
.option('--app <id/location>', 'App id or location')
|
|
235
234
|
.option('--backup <backup>', 'Backup id')
|
|
236
|
-
.option('--no-wait', 'Wait for healthcheck to succeed [false]')
|
|
237
235
|
.action(actions.restore);
|
|
238
236
|
|
|
239
237
|
program.command('restart')
|
|
240
238
|
.description('Restart an installed application')
|
|
241
239
|
.option('--app <id/location>', 'App id or location')
|
|
242
|
-
.option('--no-wait', 'Wait for healthcheck to succeed [false]')
|
|
243
240
|
.action(actions.restart);
|
|
244
241
|
|
|
245
242
|
program.command('set-location')
|
|
246
243
|
.description('Set the location of an app')
|
|
247
244
|
.option('--app <id/location>', 'App id or location')
|
|
248
|
-
.option('--no-wait', 'Wait for healthcheck to succeed [false]')
|
|
249
245
|
.option('-p, --port-bindings [PORT=port,...]', 'Query port bindings')
|
|
250
246
|
.option('-l, --location <location>', 'Location')
|
|
251
247
|
.option('-s, --secondary-domains [DOMAIN=domain,...]', 'Query/Set secondary domains')
|
|
@@ -256,7 +252,6 @@ program.command('set-location')
|
|
|
256
252
|
program.command('start')
|
|
257
253
|
.description('Start an installed application')
|
|
258
254
|
.option('--app <id/location>', 'App id or location')
|
|
259
|
-
.option('--no-wait', 'Wait for healthcheck to succeed [false]')
|
|
260
255
|
.action(actions.start);
|
|
261
256
|
|
|
262
257
|
program.command('stop')
|
|
@@ -280,7 +275,6 @@ program.command('update')
|
|
|
280
275
|
.option('--appstore-id <appid[@version]>', 'Use app from the store')
|
|
281
276
|
.option('--image <docker image>', 'Docker image')
|
|
282
277
|
.option('--no-backup', 'Skip backup [false]')
|
|
283
|
-
.option('--no-wait', 'Wait for healthcheck to succeed [false]')
|
|
284
278
|
.option('--no-force', 'Match appstore id and manifest id before updating', true)
|
|
285
279
|
.action(actions.update);
|
|
286
280
|
|
package/bin/cloudron-build
CHANGED
|
@@ -26,7 +26,7 @@ program.command('build', { isDefault: true })
|
|
|
26
26
|
.description('Build an app. This is the default subcommand')
|
|
27
27
|
.option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectArgs, [])
|
|
28
28
|
.option('-f, --file <dockerfile>', 'Name of the Dockerfile')
|
|
29
|
-
.option('--
|
|
29
|
+
.option('--repository [repository url]', 'Change the repository. This url is stored for future builds for this project. e.g registry/username/projectname')
|
|
30
30
|
.option('--no-cache', 'Do not use cache')
|
|
31
31
|
.option('--no-push', 'Do not push built image to registry')
|
|
32
32
|
.option('--raw', 'Raw output build log')
|
package/package.json
CHANGED
package/src/actions.js
CHANGED
|
@@ -266,7 +266,7 @@ async function stopApp(app, options) {
|
|
|
266
266
|
const response = await request.send({});
|
|
267
267
|
if (response.status !== 202) throw `Failed to stop app: ${requestError(response)}`;
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
return response.body.taskId;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
async function startApp(app, options) {
|
|
@@ -277,7 +277,7 @@ async function startApp(app, options) {
|
|
|
277
277
|
const response = await request.send({});
|
|
278
278
|
if (response.status !== 202) throw `Failed to start app: ${requestError(response)}`;
|
|
279
279
|
|
|
280
|
-
|
|
280
|
+
return response.body.taskId;
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
async function restartApp(app, options) {
|
|
@@ -288,7 +288,7 @@ async function restartApp(app, options) {
|
|
|
288
288
|
const response = await request.send({});
|
|
289
289
|
if (response.status !== 202) throw `Failed to restart app: ${requestError(response)}`;
|
|
290
290
|
|
|
291
|
-
|
|
291
|
+
return response.body.taskId;
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
async function authenticate(adminFqdn, username, password, options) {
|
|
@@ -722,6 +722,8 @@ async function install(localOptions, cmd) {
|
|
|
722
722
|
|
|
723
723
|
console.log('App is being installed.');
|
|
724
724
|
|
|
725
|
+
if (!options.wait) return;
|
|
726
|
+
|
|
725
727
|
await waitForFinishInstallation(appId, response.body.taskId, options);
|
|
726
728
|
console.log('\n\nApp is installed.');
|
|
727
729
|
} catch (error) {
|
|
@@ -815,6 +817,8 @@ async function setLocation(localOptions, cmd) {
|
|
|
815
817
|
const response = await request.send(data);
|
|
816
818
|
if (response.status !== 202) return exit(`Failed to configure app: ${requestError(response)}`);
|
|
817
819
|
|
|
820
|
+
if (!options.wait) return;
|
|
821
|
+
|
|
818
822
|
await waitForTask(response.body.taskId, options);
|
|
819
823
|
await waitForHealthy(app.id, options);
|
|
820
824
|
console.log('\n\nApp configured');
|
|
@@ -924,6 +928,8 @@ async function debugApp(args, localOptions, cmd) {
|
|
|
924
928
|
const response = await request.send(data);
|
|
925
929
|
if (response.status !== 202) return exit(`Failed to set debug mode: ${requestError(response)}`);
|
|
926
930
|
|
|
931
|
+
if (!options.wait) return;
|
|
932
|
+
|
|
927
933
|
await waitForTask(response.body.taskId, options);
|
|
928
934
|
|
|
929
935
|
const memoryLimit = options.limitMemory ? 0 : -1;
|
|
@@ -958,6 +964,8 @@ async function repair(localOptions, cmd) {
|
|
|
958
964
|
const response = await request.send(data);
|
|
959
965
|
if (response.status !== 202) return exit(`Failed to set repair mode: ${requestError(response)}`);
|
|
960
966
|
|
|
967
|
+
if (!options.wait) return;
|
|
968
|
+
|
|
961
969
|
process.stdout.write('\n => ' + 'Waiting for app to be repaired ');
|
|
962
970
|
|
|
963
971
|
await waitForFinishInstallation(app.id, response.body.taskId, options);
|
|
@@ -992,6 +1000,8 @@ async function uninstall(localOptions, cmd) {
|
|
|
992
1000
|
const response = await request.send({});
|
|
993
1001
|
if (response.status !== 202) return exit(`Failed to uninstall app: ${requestError(response)}`);
|
|
994
1002
|
|
|
1003
|
+
if (!options.wait) return;
|
|
1004
|
+
|
|
995
1005
|
process.stdout.write('\n => ' + 'Waiting for app to be uninstalled ');
|
|
996
1006
|
|
|
997
1007
|
await waitForTask(response.body.taskId, options);
|
|
@@ -1117,7 +1127,9 @@ async function restart(localOptions, cmd) {
|
|
|
1117
1127
|
const options = cmd.optsWithGlobals();
|
|
1118
1128
|
const app = await getApp(options);
|
|
1119
1129
|
if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
|
|
1120
|
-
await restartApp(app, options);
|
|
1130
|
+
const taskId = await restartApp(app, options);
|
|
1131
|
+
if (!options.wait) return;
|
|
1132
|
+
await waitForTask(taskId, options);
|
|
1121
1133
|
await waitForHealthy(app.id, options);
|
|
1122
1134
|
console.log('\n\nApp restarted');
|
|
1123
1135
|
} catch (error) {
|
|
@@ -1130,7 +1142,9 @@ async function start(localOptions, cmd) {
|
|
|
1130
1142
|
const options = cmd.optsWithGlobals();
|
|
1131
1143
|
const app = await getApp(options);
|
|
1132
1144
|
if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
|
|
1133
|
-
await startApp(app, options);
|
|
1145
|
+
const taskId = await startApp(app, options);
|
|
1146
|
+
if (!options.wait) return;
|
|
1147
|
+
await waitForTask(taskId, options);
|
|
1134
1148
|
await waitForHealthy(app.id, options);
|
|
1135
1149
|
console.log('\n\nApp started');
|
|
1136
1150
|
} catch (error) {
|
|
@@ -1143,7 +1157,9 @@ async function stop(localOptions, cmd) {
|
|
|
1143
1157
|
const options = cmd.optsWithGlobals();
|
|
1144
1158
|
const app = await getApp(options);
|
|
1145
1159
|
if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
|
|
1146
|
-
await stopApp(app, options);
|
|
1160
|
+
const taskId = await stopApp(app, options);
|
|
1161
|
+
if (!options.wait) return;
|
|
1162
|
+
await waitForTask(taskId, options);
|
|
1147
1163
|
console.log('\n\nApp stopped');
|
|
1148
1164
|
} catch (error) {
|
|
1149
1165
|
exit(error);
|
|
@@ -1169,6 +1185,8 @@ async function backupCreate(localOptions, cmd) {
|
|
|
1169
1185
|
const response = await request.send({ backupSiteId });
|
|
1170
1186
|
if (response.status !== 202) return exit(`Failed to start backup: ${requestError(response)}`);
|
|
1171
1187
|
|
|
1188
|
+
if (!options.wait) return;
|
|
1189
|
+
|
|
1172
1190
|
// FIXME: this should be waitForHealthCheck but the box code incorrectly modifies the installationState
|
|
1173
1191
|
await waitForFinishBackup(app.id, response.body.taskId, options);
|
|
1174
1192
|
console.log('\n\nApp is backed up');
|
package/src/build-actions.js
CHANGED
|
@@ -16,6 +16,20 @@ import stream from 'stream/promises';
|
|
|
16
16
|
import superagent from '@cloudron/superagent';
|
|
17
17
|
import tar from 'tar-fs';
|
|
18
18
|
|
|
19
|
+
function getEffectiveBuildServiceConfig(options) {
|
|
20
|
+
const buildServiceConfig = config.getBuildServiceConfig();
|
|
21
|
+
|
|
22
|
+
if (options.buildServiceUrl) {
|
|
23
|
+
buildServiceConfig.type = 'remote';
|
|
24
|
+
buildServiceConfig.url = options.buildServiceUrl;
|
|
25
|
+
if (buildServiceConfig.url.indexOf('://') === -1) buildServiceConfig.url = `https://${buildServiceConfig.url}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (options.buildServiceToken) buildServiceConfig.token = options.buildServiceToken;
|
|
29
|
+
|
|
30
|
+
return buildServiceConfig;
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
function requestError(response) {
|
|
20
34
|
if (response.status === 401 || response.status === 403) return 'Invalid token. Use cloudron build login again.';
|
|
21
35
|
|
|
@@ -26,9 +40,9 @@ function requestError(response) {
|
|
|
26
40
|
async function login(localOptions, cmd) {
|
|
27
41
|
const options = cmd.optsWithGlobals();
|
|
28
42
|
|
|
29
|
-
const buildServiceConfig =
|
|
43
|
+
const buildServiceConfig = getEffectiveBuildServiceConfig(options);
|
|
30
44
|
|
|
31
|
-
let url =
|
|
45
|
+
let url = buildServiceConfig.url;
|
|
32
46
|
if (!url) {
|
|
33
47
|
url = await readline.question('Build Service URL: ', {});
|
|
34
48
|
if (!url) return exit('No build service URL provided.');
|
|
@@ -37,7 +51,7 @@ async function login(localOptions, cmd) {
|
|
|
37
51
|
|
|
38
52
|
console.log(`Build Service login (${url}):`);
|
|
39
53
|
|
|
40
|
-
const token =
|
|
54
|
+
const token = buildServiceConfig.token || await readline.question('Token: ', {});
|
|
41
55
|
|
|
42
56
|
const response = await superagent.get(`${url}/api/v1/profile`).query({ accessToken: token }).ok(() => true);
|
|
43
57
|
if (response.status === 401 || response.status === 403) return exit(`Authentication error: ${requestError(response)}`);
|
|
@@ -191,8 +205,8 @@ async function buildLocal(manifest, sourceDir, appConfig, options) {
|
|
|
191
205
|
config.setAppBuildConfig(sourceDir, appConfig);
|
|
192
206
|
}
|
|
193
207
|
|
|
194
|
-
async function buildRemote(manifest, sourceDir, appConfig, options) {
|
|
195
|
-
console.log('Using build service',
|
|
208
|
+
async function buildRemote(manifest, sourceDir, appConfig, options, buildServiceConfig) {
|
|
209
|
+
console.log('Using build service', buildServiceConfig.url);
|
|
196
210
|
|
|
197
211
|
let tag;
|
|
198
212
|
if (options.tag) {
|
|
@@ -211,8 +225,6 @@ async function buildRemote(manifest, sourceDir, appConfig, options) {
|
|
|
211
225
|
else if (fs.existsSync(`${sourceDir}/Dockerfile.cloudron`)) dockerfile = 'Dockerfile.cloudron';
|
|
212
226
|
else if (fs.existsSync(`${sourceDir}/cloudron/Dockerfile`)) dockerfile = 'cloudron/Dockerfile';
|
|
213
227
|
|
|
214
|
-
const buildServiceConfig = config.getBuildServiceConfig();
|
|
215
|
-
|
|
216
228
|
console.log(`Building ${dockerfile} as ${dockerImage}`);
|
|
217
229
|
|
|
218
230
|
const sourceArchiveFilePath = path.join(os.tmpdir(), path.basename(sourceDir) + '.tar.gz');
|
|
@@ -282,12 +294,12 @@ async function build(localOptions, cmd) {
|
|
|
282
294
|
const sourceDir = path.dirname(manifestFilePath);
|
|
283
295
|
|
|
284
296
|
const appConfig = config.getAppBuildConfig(sourceDir);
|
|
285
|
-
const buildServiceConfig =
|
|
297
|
+
const buildServiceConfig = getEffectiveBuildServiceConfig(options);
|
|
286
298
|
|
|
287
299
|
let repository = appConfig.repository;
|
|
288
|
-
if (!repository || options.
|
|
289
|
-
if (typeof options.
|
|
290
|
-
repository = options.
|
|
300
|
+
if (!repository || options.repository) {
|
|
301
|
+
if (typeof options.repository === 'string') {
|
|
302
|
+
repository = options.repository;
|
|
291
303
|
} else {
|
|
292
304
|
repository = await readline.question(`Enter docker repository (e.g registry/username/${manifest.id || path.basename(sourceDir)}): `, {});
|
|
293
305
|
if (!repository) exit('No repository provided');
|
|
@@ -303,7 +315,7 @@ async function build(localOptions, cmd) {
|
|
|
303
315
|
|
|
304
316
|
appConfig.gitCommit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); // when the build gets saved, save the gitCommit also
|
|
305
317
|
if (buildServiceConfig.type === 'remote' && buildServiceConfig.url) {
|
|
306
|
-
await buildRemote(manifest, sourceDir, appConfig, options);
|
|
318
|
+
await buildRemote(manifest, sourceDir, appConfig, options, buildServiceConfig);
|
|
307
319
|
} else {
|
|
308
320
|
await buildLocal(manifest, sourceDir, appConfig, options);
|
|
309
321
|
}
|
|
@@ -312,7 +324,7 @@ async function build(localOptions, cmd) {
|
|
|
312
324
|
async function logs(localOptions, cmd) {
|
|
313
325
|
const options = cmd.optsWithGlobals();
|
|
314
326
|
|
|
315
|
-
const buildServiceConfig =
|
|
327
|
+
const buildServiceConfig = getEffectiveBuildServiceConfig(options);
|
|
316
328
|
if (buildServiceConfig.type !== 'remote') return exit('This command only works with the build service. Use cloudron build login first.');
|
|
317
329
|
|
|
318
330
|
if (!options.id) return exit('buildId is required');
|
|
@@ -324,7 +336,7 @@ async function logs(localOptions, cmd) {
|
|
|
324
336
|
async function status(localOptions, cmd) {
|
|
325
337
|
const options = cmd.optsWithGlobals();
|
|
326
338
|
|
|
327
|
-
const buildServiceConfig =
|
|
339
|
+
const buildServiceConfig = getEffectiveBuildServiceConfig(options);
|
|
328
340
|
if (buildServiceConfig.type !== 'remote') return exit('This command only works with the build service. Use cloudron build login first.');
|
|
329
341
|
|
|
330
342
|
if (!options.id) return exit('buildId is required');
|
|
@@ -337,7 +349,7 @@ async function status(localOptions, cmd) {
|
|
|
337
349
|
async function push(localOptions, cmd) {
|
|
338
350
|
const options = cmd.optsWithGlobals();
|
|
339
351
|
|
|
340
|
-
const buildServiceConfig =
|
|
352
|
+
const buildServiceConfig = getEffectiveBuildServiceConfig(options);
|
|
341
353
|
if (buildServiceConfig.type !== 'remote') return exit('This command only works with the build service. Use cloudron build login first.');
|
|
342
354
|
|
|
343
355
|
if (!options.id) return exit('buildId is required');
|
|
@@ -377,10 +389,10 @@ async function clear(/* localOptions, cmd */) {
|
|
|
377
389
|
config.unsetAppBuildConfig(sourceDir);
|
|
378
390
|
}
|
|
379
391
|
|
|
380
|
-
async function info(
|
|
381
|
-
|
|
392
|
+
async function info(localOptions, cmd) {
|
|
393
|
+
const options = cmd.optsWithGlobals();
|
|
382
394
|
|
|
383
|
-
const buildService =
|
|
395
|
+
const buildService = getEffectiveBuildServiceConfig(options);
|
|
384
396
|
console.log(`Build service type: ${buildService.type || 'local'}`);
|
|
385
397
|
if (buildService.type === 'remote') console.log(`Build service URL: ${buildService.url}`);
|
|
386
398
|
|