cloudron 4.15.3 → 5.1.0

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 CHANGED
@@ -3,10 +3,11 @@
3
3
  'use strict';
4
4
 
5
5
  const actions = require('../src/actions.js'),
6
+ backupTools = require('../src/backup-tools.js'),
6
7
  buildActions = require('../src/build-actions.js'),
7
8
  completion = require('../src/completion.js'),
8
9
  config = require('../src/config.js'),
9
- program = require('commander'),
10
+ Command = require('commander').Command,
10
11
  safe = require('safetydance'),
11
12
  semver = require('semver'),
12
13
  superagent = require('superagent'),
@@ -16,30 +17,72 @@ const version = require('../package.json').version;
16
17
 
17
18
  // ensure node version
18
19
  if (!semver.satisfies(process.version, require('../package.json').engines.node)) {
19
- console.error('Your nodejs version is not compatible. Please installe nodejs', require('../package.json').engines.node);
20
+ console.error('Your nodejs version is not compatible. Please install nodejs', require('../package.json').engines.node);
20
21
  process.exit(1);
21
22
  }
22
23
 
24
+ const program = new Command();
25
+
23
26
  function collectArgs(value, collected) {
24
27
  collected.push(value);
25
28
  return collected;
26
29
  }
27
30
 
28
- // TODO when updating to commander v8 we require https://github.com/tj/commander.js/pull/1670
29
- program.version(version)
30
- .option('--server <server>', 'Cloudron domain')
31
+ program.version(version);
32
+
33
+ // global options
34
+ program.option('--server <server>', 'Cloudron domain')
31
35
  .option('--token <token>', 'Cloudron token')
32
36
  .option('--allow-selfsigned', 'Accept self signed SSL certificate')
33
37
  .option('--accept-selfsigned', 'Accept self signed SSL certificate');
34
38
 
39
+ // this is a separate binary since global options are not applicable
35
40
  program.command('appstore', 'Cloudron appstore commands');
36
41
 
37
- program.command('backup', 'App backup commands');
42
+ const backupCommand = program.command('backup')
43
+ .description('App backup commands');
44
+
45
+ backupCommand.command('create')
46
+ .description('Create new app backup')
47
+ .option('--app <id>', 'App id')
48
+ .action(actions.backupCreate);
49
+
50
+ backupCommand.command('list')
51
+ .description('List all backups for specified app')
52
+ .option('--raw', 'Print raw json output')
53
+ .option('--app <id>', 'App id')
54
+ .action(actions.backupList);
55
+
56
+ backupCommand.command('decrypt <file>')
57
+ .description('Decrypt an encrypted file')
58
+ .option('--password <password>', 'password')
59
+ .action(backupTools.decrypt);
60
+
61
+ backupCommand.command('decrypt-dir <indir> <outdir>')
62
+ .description('Decrypt an encrypted directory')
63
+ .option('--password <password>', 'password')
64
+ .action(backupTools.decryptDir);
65
+
66
+ backupCommand.command('decrypt-filename <path>')
67
+ .description('Encrypt a file name')
68
+ .option('--password <password>', 'password')
69
+ .action(backupTools.decryptFilename);
70
+
71
+ backupCommand.command('encrypt <input>')
72
+ .description('Encrypt a file')
73
+ .option('--password <password>', 'password')
74
+ .action(backupTools.encrypt);
75
+
76
+ backupCommand.command('encrypt-filename <path>')
77
+ .description('Encrypt a file name')
78
+ .option('--password <password>', 'password')
79
+ .action(backupTools.encryptFilename);
38
80
 
39
81
  program.command('completion')
40
82
  .description('Shows completion for your shell')
41
83
  .action(completion);
42
84
 
85
+ // should probably move to separate binary since globals don't apply
43
86
  program.command('build')
44
87
  .description('Build an app')
45
88
  .option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectArgs, [])
@@ -83,7 +126,28 @@ program.command('debug [cmd...]')
83
126
  .option('--limit-memory', 'Enforces app memory limit. Default is to not limit memory in debug mode.')
84
127
  .action(actions.debug);
85
128
 
86
- program.command('env', 'App environment commands');
129
+ const envCommand = program.command('env')
130
+ .description('App environment commands');
131
+
132
+ envCommand.command('get <name>')
133
+ .description('Get environment variables')
134
+ .option('--app <id>', 'App id')
135
+ .action(actions.envGet);
136
+
137
+ envCommand.command('list')
138
+ .description('List environment variables')
139
+ .option('--app <id>', 'App id')
140
+ .action(actions.envList);
141
+
142
+ envCommand.command('set <KEY=value...>')
143
+ .description('Set environment variables')
144
+ .option('--app <id>', 'App id')
145
+ .action(actions.envSet);
146
+
147
+ envCommand.command('unset <KEY...>')
148
+ .description('Unset environment variables')
149
+ .option('--app <id>', 'App id')
150
+ .action(actions.envUnset);
87
151
 
88
152
  program.command('exec [cmd...]')
89
153
  .description('Exec a command in an application')
@@ -122,6 +186,7 @@ program.command('inspect')
122
186
 
123
187
  program.command('init')
124
188
  .description('Creates a new CloudronManifest.json and Dockerfile')
189
+ .option('--appstore', 'Appstore template')
125
190
  .action(actions.init);
126
191
 
127
192
  program.command('install')
@@ -254,11 +319,4 @@ program.command('update')
254
319
  }
255
320
 
256
321
  program.parse(process.argv);
257
-
258
- const knownCommand = program.commands.some(function (command) { return command._name === process.argv[2] || command._alias === process.argv[2]; });
259
- if (!knownCommand) {
260
- console.log('Unknown command: ' + process.argv[2] + '.\nTry ' + 'cloudron help');
261
- process.exit(1);
262
- }
263
322
  })();
264
-
@@ -59,22 +59,4 @@ program.command('versions')
59
59
  .option('--raw', 'Dump versions as json')
60
60
  .action(appstoreActions.listVersions);
61
61
 
62
- if (!process.argv.slice(2).length) {
63
- program.outputHelp();
64
- } else { // https://github.com/tj/commander.js/issues/338
65
- // deal first with global flags!
66
- program.parse(process.argv);
67
-
68
- if (process.argv[2] === 'help') {
69
- return program.outputHelp();
70
- }
71
-
72
- var knownCommand = program.commands.some(function (command) { return command._name === process.argv[2] || command._alias === process.argv[2]; });
73
- if (!knownCommand) {
74
- console.log('Unknown command: ' + process.argv[2] + '.\nTry ' + 'cloudron appstore help');
75
- process.exit(1);
76
- }
77
- return;
78
- }
79
-
80
62
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudron",
3
- "version": "4.15.3",
3
+ "version": "5.1.0",
4
4
  "license": "MIT",
5
5
  "description": "Cloudron Commandline Tool",
6
6
  "main": "main.js",
@@ -17,15 +17,15 @@
17
17
  },
18
18
  "author": "Cloudron Developers <support@cloudron.io>",
19
19
  "dependencies": {
20
- "async": "^3.2.3",
21
- "cloudron-manifestformat": "^5.15.2",
22
- "commander": "^6.1.0",
23
- "debug": "^4.3.3",
20
+ "async": "^3.2.4",
21
+ "cloudron-manifestformat": "^5.17.0",
22
+ "commander": "^9.4.0",
23
+ "debug": "^4.3.4",
24
24
  "delay": "^5.0.0",
25
25
  "easy-table": "^1.2.0",
26
- "ejs": "^3.1.6",
27
- "eventsource": "^1.1.0",
28
- "micromatch": "^4.0.4",
26
+ "ejs": "^3.1.8",
27
+ "eventsource": "^2.0.2",
28
+ "micromatch": "^4.0.5",
29
29
  "once": "^1.4.0",
30
30
  "open": "^8.4.0",
31
31
  "progress": "^2.0.3",
@@ -33,9 +33,9 @@
33
33
  "readline-sync": "^1.4.10",
34
34
  "safetydance": "^2.2.0",
35
35
  "split": "^1.0.1",
36
- "superagent": "^7.0.2",
36
+ "superagent": "^8.0.0",
37
37
  "tar-fs": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.12.0.tgz",
38
- "underscore": "^1.13.2"
38
+ "underscore": "^1.13.4"
39
39
  },
40
40
  "engines": {
41
41
  "node": ">= 14.x.x"
@@ -43,7 +43,7 @@
43
43
  "devDependencies": {
44
44
  "expect.js": "^0.3.1",
45
45
  "memorystream": "^0.3.1",
46
- "mocha": "^9.1.3",
46
+ "mocha": "^10.0.0",
47
47
  "rimraf": "^3.0.2"
48
48
  }
49
49
  }
package/src/actions.js CHANGED
@@ -4,9 +4,9 @@ const assert = require('assert'),
4
4
  config = require('./config.js'),
5
5
  delay = require('delay'),
6
6
  ejs = require('ejs'),
7
+ { exit, locateManifest } = require('./helper.js'),
7
8
  EventSource = require('eventsource'),
8
9
  fs = require('fs'),
9
- helper = require('./helper.js'),
10
10
  https = require('https'),
11
11
  manifestFormat = require('cloudron-manifestformat'),
12
12
  opn = require('open'),
@@ -14,7 +14,6 @@ const assert = require('assert'),
14
14
  path = require('path'),
15
15
  ProgressBar = require('progress'),
16
16
  ProgressStream = require('progress-stream'),
17
- querystring = require('querystring'),
18
17
  readlineSync = require('readline-sync'),
19
18
  safe = require('safetydance'),
20
19
  spawn = require('child_process').spawn,
@@ -25,8 +24,6 @@ const assert = require('assert'),
25
24
  zlib = require('zlib'),
26
25
  _ = require('underscore');
27
26
 
28
- const exit = helper.exit;
29
-
30
27
  exports = module.exports = {
31
28
  list,
32
29
  login,
@@ -66,13 +63,13 @@ const NO_APP_FOUND_ERROR_STRING = 'Could not determine app. Use --app to specify
66
63
 
67
64
  // options for the request module
68
65
  function requestOptions(options) {
69
- const adminFqdn = options.parent.server || config.apiEndpoint();
66
+ const adminFqdn = options.server || config.apiEndpoint();
70
67
 
71
68
  // ensure config can return the correct section
72
69
  config.setActive(adminFqdn);
73
70
 
74
- const token = options.parent.token || config.token();
75
- const rejectUnauthorized = !(options.parent.allowSelfsigned || options.parent.acceptSelfsigned || config.allowSelfsigned());
71
+ const token = options.token || config.token();
72
+ const rejectUnauthorized = !(options.allowSelfsigned || options.acceptSelfsigned || config.allowSelfsigned());
76
73
 
77
74
  if (!adminFqdn && !token) return exit('Login with "cloudron login" first'); // a bit rough to put this here!
78
75
 
@@ -180,7 +177,7 @@ async function getApp(options) {
180
177
  const app = options.app || null;
181
178
 
182
179
  if (!app) { // determine based on repository name given during 'build'
183
- const manifestFilePath = helper.locateManifest();
180
+ const manifestFilePath = locateManifest();
184
181
 
185
182
  if (!manifestFilePath) throw new Error(NO_APP_FOUND_ERROR_STRING);
186
183
 
@@ -369,7 +366,7 @@ async function authenticate(adminFqdn, username, password, options) {
369
366
  return response.body.accessToken;
370
367
  }
371
368
 
372
- async function login(adminFqdn, options) {
369
+ async function login(adminFqdn, localOptions, cmd) {
373
370
  if (!adminFqdn) adminFqdn = readlineSync.question('Cloudron Domain (e.g. my.example.com): ', {});
374
371
  if (!adminFqdn) return exit('');
375
372
 
@@ -378,7 +375,9 @@ async function login(adminFqdn, options) {
378
375
 
379
376
  config.setActive(adminFqdn);
380
377
 
381
- const rejectUnauthorized = !(options.parent.allowSelfsigned || options.parent.acceptSelfsigned);
378
+ const options = cmd.optsWithGlobals();
379
+
380
+ const rejectUnauthorized = !(options.allowSelfsigned || options.acceptSelfsigned);
382
381
  let token = config.token();
383
382
  if (token) { // check if the token is not expired
384
383
  const request = superagent.get(`https://${adminFqdn}/api/v1/profile?access_token=${token}`)
@@ -408,7 +407,7 @@ async function login(adminFqdn, options) {
408
407
  config.setActive(adminFqdn);
409
408
  config.setApiEndpoint(adminFqdn);
410
409
  config.setToken(token);
411
- config.setAllowSelfsigned(options.parent.allowSelfsigned || options.parent.acceptSelfsigned);
410
+ config.setAllowSelfsigned(cmd.optsWithGlobals().allowSelfsigned || cmd.optsWithGlobals().acceptSelfsigned);
412
411
  config.set('cloudrons.default', adminFqdn);
413
412
 
414
413
  console.log('Login successful.');
@@ -421,7 +420,8 @@ function logout() {
421
420
  console.log('Logged out.');
422
421
  }
423
422
 
424
- async function open(options) {
423
+ async function open(localOptions, cmd) {
424
+ const options = cmd.optsWithGlobals();
425
425
  const [error, app] = await safe(getApp(options));
426
426
  if (error) return exit(error);
427
427
 
@@ -430,9 +430,8 @@ async function open(options) {
430
430
  opn(`https://${app.fqdn}`);
431
431
  }
432
432
 
433
- async function list(options) {
434
- helper.verifyArguments(arguments);
435
-
433
+ async function list(localOptions, cmd) {
434
+ const options = cmd.optsWithGlobals();
436
435
  const [error, response] = await safe(createRequest('GET', '/api/v1/apps', options));
437
436
  if (error) return exit(error);
438
437
  if (response.statusCode !== 200) return exit(`Failed to list apps: ${requestError(response)}`);
@@ -533,7 +532,7 @@ async function getManifest(appstoreId) {
533
532
 
534
533
  if (appstoreId) return await downloadManifest(appstoreId);
535
534
 
536
- const manifestFilePath = helper.locateManifest();
535
+ const manifestFilePath = locateManifest();
537
536
  if (!manifestFilePath) throw new Error('No CloudronManifest.json found');
538
537
 
539
538
  const result = manifestFormat.parseFile(manifestFilePath);
@@ -551,8 +550,8 @@ async function getManifest(appstoreId) {
551
550
  return { manifest: result.manifest, manifestFilePath };
552
551
  }
553
552
 
554
- async function install(options) {
555
- helper.verifyArguments(arguments);
553
+ async function install(localOptions, cmd) {
554
+ const options = cmd.optsWithGlobals();
556
555
 
557
556
  try {
558
557
  const result = await getManifest(options.appstoreId || '');
@@ -661,8 +660,8 @@ async function install(options) {
661
660
  }
662
661
  }
663
662
 
664
- async function configure(options) {
665
- helper.verifyArguments(arguments);
663
+ async function configure(localOptions, cmd) {
664
+ const options = cmd.optsWithGlobals();
666
665
 
667
666
  try {
668
667
  const app = await getApp(options);
@@ -741,8 +740,8 @@ async function configure(options) {
741
740
  }
742
741
  }
743
742
 
744
- async function update(options) {
745
- helper.verifyArguments(arguments);
743
+ async function update(localOptions, cmd) {
744
+ const options = cmd.optsWithGlobals();
746
745
 
747
746
  try {
748
747
  const app = await getApp(options);
@@ -798,15 +797,16 @@ async function update(options) {
798
797
  }
799
798
  }
800
799
 
801
- async function debug(cmd, options) {
800
+ async function debug(args, localOptions, cmd) {
802
801
  try {
802
+ const options = cmd.optsWithGlobals();
803
803
  const app = await getApp(options);
804
804
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
805
805
 
806
806
  const data = {
807
807
  debugMode: options.disable ? null : {
808
808
  readonlyRootfs: options.readonly ? true : false,
809
- cmd: parseDebugCommand(cmd.join(' ').trim())
809
+ cmd: parseDebugCommand(args.join(' ').trim())
810
810
  }
811
811
  };
812
812
 
@@ -835,8 +835,9 @@ async function debug(cmd, options) {
835
835
  }
836
836
  }
837
837
 
838
- async function repair(options) {
838
+ async function repair(localOptions, cmd) {
839
839
  try {
840
+ const options = cmd.optsWithGlobals();
840
841
  const app = await getApp(options);
841
842
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
842
843
 
@@ -863,10 +864,9 @@ async function repair(options) {
863
864
  }
864
865
  }
865
866
 
866
- async function cancel(options) {
867
- helper.verifyArguments(arguments);
868
-
867
+ async function cancel(localOptions, cmd) {
869
868
  try {
869
+ const options = cmd.optsWithGlobals();
870
870
  const app = await getApp(options);
871
871
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
872
872
  if (!app.taskId) return exit('No active task.');
@@ -877,10 +877,9 @@ async function cancel(options) {
877
877
  }
878
878
  }
879
879
 
880
- async function uninstall(options) {
881
- helper.verifyArguments(arguments);
882
-
880
+ async function uninstall(localOptions, cmd) {
883
881
  try {
882
+ const options = cmd.optsWithGlobals();
884
883
  const app = await getApp(options);
885
884
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
886
885
 
@@ -919,9 +918,8 @@ function logPrinter(obj) {
919
918
  console.log('%s - %s', ts, message);
920
919
  }
921
920
 
922
- async function logs(options) {
923
- helper.verifyArguments(arguments);
924
-
921
+ async function logs(localOptions, cmd) {
922
+ const options = cmd.optsWithGlobals();
925
923
  const { adminFqdn, token, rejectUnauthorized } = requestOptions(options);
926
924
  const lines = options.lines || 500;
927
925
  const tail = !!options.tail;
@@ -969,9 +967,8 @@ async function logs(options) {
969
967
  }
970
968
  }
971
969
 
972
- async function status(options) {
973
- helper.verifyArguments(arguments);
974
-
970
+ async function status(localOptions, cmd) {
971
+ const options = cmd.optsWithGlobals();
975
972
  const [error, app] = await safe(getApp(options));
976
973
  if (error) return exit(error);
977
974
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
@@ -987,10 +984,9 @@ async function status(options) {
987
984
  console.log();
988
985
  }
989
986
 
990
- async function inspect(options) {
991
- helper.verifyArguments(arguments);
992
-
987
+ async function inspect(localOptions, cmd) {
993
988
  try {
989
+ const options = cmd.optsWithGlobals();
994
990
  const response = await createRequest('GET', '/api/v1/apps', options);
995
991
  if (response.statusCode !== 200) return exit(`Failed to list apps: ${requestError(response)}`);
996
992
 
@@ -1003,7 +999,7 @@ async function inspect(options) {
1003
999
  apps.push(response2.body);
1004
1000
  }
1005
1001
 
1006
- const { adminFqdn } = requestOptions(options);
1002
+ const { adminFqdn } = requestOptions(cmd.optsWithGlobals());
1007
1003
  console.log(JSON.stringify({
1008
1004
  apiEndpoint: adminFqdn,
1009
1005
  appStoreOrigin: config.appStoreOrigin(),
@@ -1014,10 +1010,9 @@ async function inspect(options) {
1014
1010
  }
1015
1011
  }
1016
1012
 
1017
- async function restart(options) {
1018
- helper.verifyArguments(arguments);
1019
-
1013
+ async function restart(localOptions, cmd) {
1020
1014
  try {
1015
+ const options = cmd.optsWithGlobals();
1021
1016
  const app = await getApp(options);
1022
1017
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1023
1018
  await restartApp(app, options);
@@ -1028,10 +1023,9 @@ async function restart(options) {
1028
1023
  }
1029
1024
  }
1030
1025
 
1031
- async function start(options) {
1032
- helper.verifyArguments(arguments);
1033
-
1026
+ async function start(localOptions, cmd) {
1034
1027
  try {
1028
+ const options = cmd.optsWithGlobals();
1035
1029
  const app = await getApp(options);
1036
1030
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1037
1031
  await startApp(app, options);
@@ -1042,10 +1036,9 @@ async function start(options) {
1042
1036
  }
1043
1037
  }
1044
1038
 
1045
- async function stop(options) {
1046
- helper.verifyArguments(arguments);
1047
-
1039
+ async function stop(localOptions, cmd) {
1048
1040
  try {
1041
+ const options = cmd.optsWithGlobals();
1049
1042
  const app = await getApp(options);
1050
1043
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1051
1044
  await stopApp(app, options);
@@ -1055,10 +1048,9 @@ async function stop(options) {
1055
1048
  }
1056
1049
  }
1057
1050
 
1058
- async function backupCreate(options) {
1059
- helper.verifyArguments(arguments);
1060
-
1051
+ async function backupCreate(localOptions, cmd) {
1061
1052
  try {
1053
+ const options = cmd.optsWithGlobals();
1062
1054
  const app = await getApp(options);
1063
1055
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1064
1056
 
@@ -1073,10 +1065,9 @@ async function backupCreate(options) {
1073
1065
  }
1074
1066
  }
1075
1067
 
1076
- async function backupList(options) {
1077
- helper.verifyArguments(arguments);
1078
-
1068
+ async function backupList(localOptions, cmd) {
1079
1069
  try {
1070
+ const options = cmd.optsWithGlobals();
1080
1071
  const app = await getApp(options);
1081
1072
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1082
1073
 
@@ -1122,10 +1113,9 @@ async function getLastBackup(app, options) {
1122
1113
  return response.body.backups[0].id;
1123
1114
  }
1124
1115
 
1125
- async function restore(options) {
1126
- helper.verifyArguments(arguments);
1127
-
1116
+ async function restore(localOptions, cmd) {
1128
1117
  try {
1118
+ const options = cmd.optsWithGlobals();
1129
1119
  const app = await getApp(options);
1130
1120
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1131
1121
 
@@ -1150,10 +1140,9 @@ async function restore(options) {
1150
1140
  }
1151
1141
  }
1152
1142
 
1153
- async function importApp(options) {
1154
- helper.verifyArguments(arguments);
1155
-
1143
+ async function importApp(localOptions, cmd) {
1156
1144
  try {
1145
+ const options = cmd.optsWithGlobals();
1157
1146
  const app = await getApp(options);
1158
1147
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1159
1148
 
@@ -1206,10 +1195,9 @@ async function importApp(options) {
1206
1195
  }
1207
1196
  }
1208
1197
 
1209
- async function exportApp(options) {
1210
- helper.verifyArguments(arguments);
1211
-
1198
+ async function exportApp(localOptions, cmd) {
1212
1199
  try {
1200
+ const options = cmd.optsWithGlobals();
1213
1201
  const app = await getApp(options);
1214
1202
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1215
1203
 
@@ -1228,10 +1216,9 @@ async function exportApp(options) {
1228
1216
  }
1229
1217
  }
1230
1218
 
1231
- async function clone(options) {
1232
- helper.verifyArguments(arguments);
1233
-
1219
+ async function clone(localOptions, cmd) {
1234
1220
  try {
1221
+ const options = cmd.optsWithGlobals();
1235
1222
  const app = await getApp(options);
1236
1223
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1237
1224
 
@@ -1290,11 +1277,12 @@ function demuxStream(stream, stdout, stderr) {
1290
1277
  // echo "sauce" | cloudron exec -- bash -c "cat - > /app/data/sauce" - test with binary files. should disable tty
1291
1278
  // cat ~/tmp/fantome.tar.gz | cloudron exec -- bash -c "tar xvf - -C /tmp" - must show an error
1292
1279
  // cat ~/tmp/fantome.tar.gz | cloudron exec -- bash -c "tar zxf - -C /tmp" - must extrack ok
1293
- async function exec(cmd, options) {
1294
- let stdin = options._stdin || process.stdin; // hack for 'push', 'pull' to reuse this function
1295
- let stdout = options._stdout || process.stdout;
1280
+ async function exec(args, localOptions, cmd) {
1281
+ let stdin = localOptions._stdin || process.stdin; // hack for 'push', 'pull' to reuse this function
1282
+ let stdout = localOptions._stdout || process.stdout; // hack for 'push', 'pull' to reuse this function
1296
1283
 
1297
- let tty = !!options.const;
1284
+ const options = cmd.optsWithGlobals();
1285
+ let tty = !!options.tty;
1298
1286
 
1299
1287
  const [error, app] = await safe(getApp(options));
1300
1288
  if (error) return exit(error);
@@ -1302,26 +1290,37 @@ async function exec(cmd, options) {
1302
1290
 
1303
1291
  if (app.installationState !== 'installed') exit('App is not yet running. Try again later.');
1304
1292
 
1305
- if (cmd.length === 0) {
1306
- cmd = [ '/bin/bash' ];
1293
+ if (args.length === 0) {
1294
+ args = [ '/bin/bash' ];
1307
1295
  tty = true; // override
1308
1296
  }
1309
1297
 
1310
1298
  if (tty && !stdin.isTTY) exit('stdin is not tty');
1311
1299
 
1312
- const { adminFqdn, token, rejectUnauthorized } = requestOptions(options);
1300
+ const request = createRequest('POST', `/api/v1/apps/${app.id}/exec`, options);
1301
+ const response = await request.send({ cmd: args, tty });
1302
+ if (response.statusCode !== 200) return exit(`Failed to create exec: ${requestError(response)}`);
1303
+ const execId = response.body.id;
1304
+
1305
+ async function exitWithCode() {
1306
+ const response2 = await createRequest('GET', `/api/v1/apps/${app.id}/exec/${execId}`, options);
1307
+ if (response2.statusCode !== 200) return exit(`Failed to get exec code: ${requestError(response2)}`);
1308
+
1309
+ process.exit(response2.body.exitCode);
1310
+ }
1311
+
1312
+ const { adminFqdn, token, rejectUnauthorized } = requestOptions(cmd.optsWithGlobals());
1313
1313
 
1314
- const query = {
1315
- rows: stdout.rows,
1316
- columns: stdout.columns,
1314
+ const searchParams = new URLSearchParams({
1315
+ rows: stdout.rows || 24,
1316
+ columns: stdout.columns || 80,
1317
1317
  access_token: token,
1318
- cmd: JSON.stringify(cmd),
1319
- tty: tty
1320
- };
1318
+ tty
1319
+ });
1321
1320
 
1322
1321
  const req = https.request({
1323
1322
  hostname: adminFqdn,
1324
- path: `/api/v1/apps/${app.id}/exec?` + querystring.stringify(query),
1323
+ path: `/api/v1/apps/${app.id}/exec/${execId}/start?${searchParams.toString()}`,
1325
1324
  method: 'GET',
1326
1325
  headers: {
1327
1326
  'Connection': 'Upgrade',
@@ -1345,8 +1344,8 @@ async function exec(cmd, options) {
1345
1344
  stdin.setRawMode(true);
1346
1345
  stdin.pipe(socket, { end: false }); // the remote will close the connection
1347
1346
  socket.pipe(stdout); // in tty mode, stdout/stderr is merged
1348
- socket.on('end', exit); // server closed the socket
1349
- } else {// create stdin process on demand
1347
+ socket.on('end', exitWithCode); // server closed the socket
1348
+ } else { // create stdin process on demand
1350
1349
  if (typeof stdin === 'function') stdin = stdin();
1351
1350
 
1352
1351
  stdin.on('data', function (d) {
@@ -1361,7 +1360,7 @@ async function exec(cmd, options) {
1361
1360
  socket.write(buf);
1362
1361
  });
1363
1362
 
1364
- stdout.on('close', () => process.exit()); // this is only emitted when stdout is a file and not the terminal
1363
+ stdout.on('close', exitWithCode); // this is only emitted when stdout is a file and not the terminal
1365
1364
 
1366
1365
  demuxStream(socket, stdout, process.stderr); // can get separate streams in non-tty mode
1367
1366
  socket.on('end', function () { // server closed the socket
@@ -1371,7 +1370,7 @@ async function exec(cmd, options) {
1371
1370
  socket.end();
1372
1371
 
1373
1372
  // process._getActiveHandles(); process._getActiveRequests();
1374
- if (stdout === process.stdout) setImmediate(() => process.exit()); // otherwise, we rely on the 'close' event above
1373
+ if (stdout === process.stdout) setImmediate(exitWithCode); // otherwise, we rely on the 'close' event above
1375
1374
  });
1376
1375
  }
1377
1376
  });
@@ -1380,30 +1379,30 @@ async function exec(cmd, options) {
1380
1379
  req.end(); // this makes the request
1381
1380
  }
1382
1381
 
1383
- function push(localDir, remote, options) {
1382
+ function push(localDir, remote, localOptions, cmd) {
1384
1383
  // deal with paths prefixed with ~
1385
- var local = localDir.replace(/^~(?=$|\/|\\)/, os.homedir());
1386
- var stat = fs.existsSync(path.resolve(local)) ? fs.lstatSync(local) : null;
1384
+ const local = localDir.replace(/^~(?=$|\/|\\)/, os.homedir());
1385
+ const stat = fs.existsSync(path.resolve(local)) ? fs.lstatSync(local) : null;
1387
1386
 
1388
1387
  if (stat && stat.isDirectory()) {
1389
1388
  // Create a functor for stdin. If no data event handlers are attached, and there are no stream.pipe() destinations, and the stream is
1390
1389
  // switched into flowing mode, then data will be lost. So, we have to start the tarzip only when exec is ready to attach event handlers.
1391
- options._stdin = function () {
1390
+ localOptions._stdin = function () {
1392
1391
  var tarzip = spawn('tar', ['zcf', '-', '-C', path.dirname(local), path.basename(local)], { stdio: 'pipe' });
1393
1392
  return tarzip.stdout;
1394
1393
  };
1395
1394
 
1396
- exec(['tar', 'zxvf', '-', '-C', remote], options);
1395
+ exec(['tar', 'zxvf', '-', '-C', remote], localOptions, cmd);
1397
1396
  } else {
1398
1397
  if (local === '-') {
1399
- options._stdin = process.stdin;
1398
+ localOptions._stdin = process.stdin;
1400
1399
  } else if (stat) {
1401
- var progress = new ProgressStream({ length: stat.size, time: 1000 });
1400
+ const progress = new ProgressStream({ length: stat.size, time: 1000 });
1402
1401
 
1403
- options._stdin = progress;
1402
+ localOptions._stdin = progress;
1404
1403
  fs.createReadStream(local).pipe(progress);
1405
1404
 
1406
- var bar = new ProgressBar('Uploading [:bar] :percent: :etas', {
1405
+ const bar = new ProgressBar('Uploading [:bar] :percent: :etas', {
1407
1406
  complete: '=',
1408
1407
  incomplete: ' ',
1409
1408
  width: 100,
@@ -1415,48 +1414,52 @@ function push(localDir, remote, options) {
1415
1414
  exit('local file ' + local + ' does not exist');
1416
1415
  }
1417
1416
 
1418
- options._stdin.on('error', function (error) { exit('Error pushing', error); });
1417
+ localOptions._stdin.on('error', function (error) { exit('Error pushing', error); });
1419
1418
 
1420
1419
  if (remote.endsWith('/')) { // dir
1421
1420
  remote = remote + '/' + path.basename(local); // do not use path.join as we want this to be a UNIX path
1422
1421
  }
1423
1422
 
1424
- exec(['bash', '-c', `cat - > "${remote}"`], options);
1423
+ exec(['bash', '-c', `cat - > "${remote}"`], localOptions, cmd);
1425
1424
  }
1426
1425
  }
1427
1426
 
1428
- function pull(remote, local, options) {
1427
+ function pull(remote, local, localOptions, cmd) {
1429
1428
  if (remote.endsWith('/')) { // dir
1430
1429
  var untar = tar.extract(local); // local directory is created if it doesn't exist!
1431
1430
  var unzip = zlib.createGunzip();
1432
1431
 
1433
1432
  unzip.pipe(untar);
1434
- options._stdout = unzip;
1433
+ localOptions._stdout = unzip;
1435
1434
 
1436
- exec(['tar', 'zcf', '-', '-C', remote, '.'], options);
1435
+ exec(['tar', 'zcf', '-', '-C', remote, '.'], localOptions, cmd);
1437
1436
  } else {
1438
1437
  if (fs.existsSync(local) && fs.lstatSync(local).isDirectory()) {
1439
1438
  local = path.join(local, path.basename(remote));
1440
- options._stdout = fs.createWriteStream(local);
1439
+ localOptions._stdout = fs.createWriteStream(local);
1441
1440
  } else if (local === '-') {
1442
- options._stdout = process.stdout;
1441
+ localOptions._stdout = process.stdout;
1443
1442
  } else {
1444
- options._stdout = fs.createWriteStream(local);
1443
+ localOptions._stdout = fs.createWriteStream(local);
1445
1444
  }
1446
1445
 
1447
- options._stdout.on('error', function (error) { exit('Error pulling', error); });
1446
+ localOptions._stdout.on('error', function (error) { exit('Error pulling', error); });
1448
1447
 
1449
- exec(['cat', remote], options);
1448
+ exec(['cat', remote], localOptions, cmd);
1450
1449
  }
1451
1450
  }
1452
1451
 
1453
- function init() {
1454
- const manifestFilePath = helper.locateManifest();
1452
+ function init(localOptions, cmd) {
1453
+ const options = cmd.optsWithGlobals();
1454
+ const manifestFilePath = locateManifest();
1455
1455
  if (manifestFilePath && path.dirname(manifestFilePath) === process.cwd()) return exit('CloudronManifest.json already exists in current directory');
1456
1456
 
1457
- const manifestTemplate = fs.readFileSync(path.join(__dirname, 'templates/', 'CloudronManifest.json.ejs'), 'utf8');
1457
+ const manifestTemplateFilename = options.appstore ? 'CloudronManifest.appstore.json.ejs' : 'CloudronManifest.json.ejs';
1458
+
1459
+ const manifestTemplate = fs.readFileSync(path.join(__dirname, 'templates/', manifestTemplateFilename), 'utf8');
1458
1460
  const dockerfileTemplate = fs.readFileSync(path.join(__dirname, 'templates/', 'Dockerfile.ejs'), 'utf8');
1459
1461
  const dockerignoreTemplate = fs.readFileSync(path.join(__dirname, 'templates/', 'dockerignore.ejs'), 'utf8');
1462
+ const startShTemplate = fs.readFileSync(path.join(__dirname, 'templates/', 'start.sh.ejs'), 'utf8');
1460
1463
 
1461
1464
  const data = {
1462
1465
  version: '0.1.0',
@@ -1481,6 +1484,21 @@ function init() {
1481
1484
  fs.writeFileSync('.dockerignore', dockerignore, 'utf8');
1482
1485
  }
1483
1486
 
1487
+ if (fs.existsSync('start.sh')) {
1488
+ console.log('start.sh already exists, skipping');
1489
+ } else {
1490
+ const dockerignore = ejs.render(startShTemplate, {});
1491
+ fs.writeFileSync('start.sh', dockerignore, 'utf8');
1492
+ fs.chmodSync('start.sh', 0o0775);
1493
+ }
1494
+
1495
+ if (options.appstore) {
1496
+ if (!fs.existsSync('.gitignore')) fs.writeFileSync('.gitignore', 'node_modules/\n', 'utf8');
1497
+ if (!fs.existsSync('DESCRIPTION.md')) fs.writeFileSync('DESCRIPTION.md', '## About\n\nThis app changes everything\n\n', 'utf8');
1498
+ if (!fs.existsSync('POSTINSTALL.md')) fs.writeFileSync('POSTINSTALL.md', 'Post installation information\n\n', 'utf8');
1499
+ if (!fs.existsSync('CHANGELOG')) fs.writeFileSync('CHANGELOG', '[0.1.0]\n* Initial version\n\n', 'utf8');
1500
+ }
1501
+
1484
1502
  console.log();
1485
1503
  console.log('Now edit the CloudronManifest.json');
1486
1504
  console.log();
@@ -1499,8 +1517,9 @@ async function setEnv(app, env, options) {
1499
1517
  console.log('\n');
1500
1518
  }
1501
1519
 
1502
- async function envSet(envVars, options) {
1520
+ async function envSet(envVars, localOptions, cmd) {
1503
1521
  try {
1522
+ const options = cmd.optsWithGlobals();
1504
1523
  const app = await getApp(options);
1505
1524
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1506
1525
 
@@ -1520,8 +1539,9 @@ async function envSet(envVars, options) {
1520
1539
  }
1521
1540
  }
1522
1541
 
1523
- async function envUnset(envNames, options) {
1542
+ async function envUnset(envNames, localOptions, cmd) {
1524
1543
  try {
1544
+ const options = cmd.optsWithGlobals();
1525
1545
  const app = await getApp(options);
1526
1546
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1527
1547
 
@@ -1537,10 +1557,9 @@ async function envUnset(envNames, options) {
1537
1557
  }
1538
1558
  }
1539
1559
 
1540
- async function envList(options) {
1541
- helper.verifyArguments(arguments);
1542
-
1560
+ async function envList(localOptions, cmd) {
1543
1561
  try {
1562
+ const options = cmd.optsWithGlobals();
1544
1563
  const app = await getApp(options);
1545
1564
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1546
1565
 
@@ -1566,8 +1585,9 @@ async function envList(options) {
1566
1585
  }
1567
1586
  }
1568
1587
 
1569
- async function envGet(envName, options) {
1588
+ async function envGet(envName, localOptions, cmd) {
1570
1589
  try {
1590
+ const options = cmd.optsWithGlobals();
1571
1591
  const app = await getApp(options);
1572
1592
  if (!app) return exit(NO_APP_FOUND_ERROR_STRING);
1573
1593
 
@@ -2,30 +2,29 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- var superagent = require('superagent'),
6
- util = require('util'),
7
- path = require('path'),
8
- assert = require('assert'),
5
+ const assert = require('assert'),
6
+ config = require('./config.js'),
9
7
  fs = require('fs'),
8
+ { exit, locateManifest } = require('./helper.js'),
9
+ manifestFormat = require('cloudron-manifestformat'),
10
+ path = require('path'),
11
+ readlineSync = require('readline-sync'),
10
12
  safe = require('safetydance'),
13
+ superagent = require('superagent'),
11
14
  Table = require('easy-table'),
12
- readlineSync = require('readline-sync'),
13
- config = require('./config.js'),
14
- helper = require('./helper.js'),
15
- exit = helper.exit,
16
- manifestFormat = require('cloudron-manifestformat');
15
+ util = require('util');
17
16
 
18
17
  exports = module.exports = {
19
- login: login,
20
- logout: logout,
21
- info: info,
22
- listVersions: listVersions,
23
- submit: submit,
24
- upload: upload,
25
- revoke: revoke,
26
- approve: approve,
27
- unpublish: unpublish,
28
- listPublishedApps: listPublishedApps
18
+ login,
19
+ logout,
20
+ info,
21
+ listVersions,
22
+ submit,
23
+ upload,
24
+ revoke,
25
+ approve,
26
+ unpublish,
27
+ listPublishedApps
29
28
  };
30
29
 
31
30
  const NO_MANIFEST_FOUND_ERROR_STRING = 'No CloudronManifest.json found';
@@ -42,7 +41,7 @@ function getAppstoreId(appstoreId, callback) {
42
41
  return callback(null, parts[0], parts[1]);
43
42
  }
44
43
 
45
- var manifestFilePath = helper.locateManifest();
44
+ var manifestFilePath = locateManifest();
46
45
 
47
46
  if (!manifestFilePath) return callback('No CloudronManifest.json found');
48
47
 
@@ -136,8 +135,6 @@ function info(options) {
136
135
  }
137
136
 
138
137
  function listVersions(options) {
139
- helper.verifyArguments(arguments);
140
-
141
138
  getAppstoreId(options.appstoreId, function (error, id) {
142
139
  if (error) exit(error);
143
140
 
@@ -428,10 +425,8 @@ function submitAppForReview(manifest, callback) {
428
425
  }
429
426
 
430
427
  function upload(options) {
431
- helper.verifyArguments(arguments);
432
-
433
428
  // try to find the manifest of this project
434
- var manifestFilePath = helper.locateManifest();
429
+ var manifestFilePath = locateManifest();
435
430
  if (!manifestFilePath) return exit(NO_MANIFEST_FOUND_ERROR_STRING);
436
431
 
437
432
  var result = manifestFormat.parseFile(manifestFilePath);
@@ -467,10 +462,8 @@ function upload(options) {
467
462
  }
468
463
 
469
464
  function submit() {
470
- helper.verifyArguments(arguments);
471
-
472
465
  // try to find the manifest of this project
473
- var manifestFilePath = helper.locateManifest();
466
+ var manifestFilePath = locateManifest();
474
467
  if (!manifestFilePath) return exit(NO_MANIFEST_FOUND_ERROR_STRING);
475
468
 
476
469
  var result = manifestFormat.parseFile(manifestFilePath);
@@ -482,8 +475,6 @@ function submit() {
482
475
  }
483
476
 
484
477
  function unpublish(options) {
485
- helper.verifyArguments(arguments);
486
-
487
478
  getAppstoreId(options.appstoreId, function (error, id, version) {
488
479
  if (error) exit(error);
489
480
 
@@ -499,8 +490,6 @@ function unpublish(options) {
499
490
  }
500
491
 
501
492
  function revoke(options) {
502
- helper.verifyArguments(arguments);
503
-
504
493
  getAppstoreId(options.appstoreId, function (error, id, version) {
505
494
  if (error) return exit(error);
506
495
 
@@ -525,8 +514,6 @@ function approve(options) {
525
514
 
526
515
  // TODO currently no pagination, only needed once we have users with more than 100 apps
527
516
  function listPublishedApps(options) {
528
- helper.verifyArguments(arguments);
529
-
530
517
  superagentEnd(function () {
531
518
  return superagent.get(createUrl('/api/v1/developers/apps?per_page=100'))
532
519
  .query({ accessToken: config.appStoreToken() })
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  exports = module.exports = {
4
- build: build
4
+ build
5
5
  };
6
6
 
7
7
  const assert = require('assert'),
@@ -292,8 +292,6 @@ function buildRemote(manifest, sourceDir, appConfig, options) {
292
292
  }
293
293
 
294
294
  function build(options) {
295
- helper.verifyArguments(arguments);
296
-
297
295
  // try to find the manifest of this project
298
296
  const manifestFilePath = helper.locateManifest();
299
297
  if (!manifestFilePath) return exit('No CloudronManifest.json found');
package/src/completion.js CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- var helper = require('./helper.js'),
5
+ const helper = require('./helper.js'),
6
6
  util = require('util');
7
7
 
8
- exports = module.exports = function (options) {
8
+ exports = module.exports = function (options, cmd) {
9
9
  var completion = '';
10
10
 
11
- var commands = [];
12
- for (var command in options.parent.commands) {
13
- if (options.parent.commands[command]._name === '*' || options.parent.commands[command]._name === 'completion') continue;
14
- if (options.parent.commands[command]._name) commands.push(options.parent.commands[command]._name);
15
- if (options.parent.commands[command]._alias) commands.push(options.parent.commands[command]._alias);
11
+ const commands = [];
12
+ for (const command of cmd.parent.commands) {
13
+ if (command._name === '*' || command._name === 'completion') continue;
14
+ if (command._name) commands.push(command._name);
15
+ if (command._alias) commands.push(command._alias);
16
16
  }
17
17
  commands.sort();
18
18
 
package/src/helper.js CHANGED
@@ -10,7 +10,6 @@ exports = module.exports = {
10
10
  exit,
11
11
 
12
12
  locateManifest,
13
- verifyArguments
14
13
  };
15
14
 
16
15
  function exit(error) {
@@ -35,11 +34,3 @@ function locateManifest() {
35
34
 
36
35
  return null;
37
36
  }
38
-
39
- function verifyArguments(args) {
40
- if (args.length > 1) {
41
- console.log('Too many arguments');
42
- args[0].parent.help(); // extra args won't have parent() field
43
- process.exit(1);
44
- }
45
- }
@@ -0,0 +1,25 @@
1
+ {
2
+ "id": "",
3
+ "version": "<%- version %>",
4
+ "upstreamVersion": "",
5
+ "minBoxVersion": "7.1.0",
6
+ "title": "",
7
+ "author": "",
8
+ "description": "file://DESCRIPTION.md",
9
+ "tagline": "",
10
+ "website": "",
11
+ "contactEmail": "",
12
+ "icon": "file://logo.png",
13
+ "healthCheckPath": "/",
14
+ "mediaLinks": [],
15
+ "httpPort": <%- httpPort %>,
16
+ "tags": [],
17
+ "changelog": "file://CHANGELOG",
18
+ "postInstallMessage": "file://POSTINSTALL.md",
19
+ "documentationUrl": "",
20
+ "forumUrl": "",
21
+ "addons": {
22
+ "localstorage": {}
23
+ },
24
+ "manifestVersion": 2
25
+ }
@@ -1,3 +1,9 @@
1
1
  FROM cloudron/base:3.2.0@sha256:ba1d566164a67c266782545ea9809dc611c4152e27686fd14060332dd88263ea
2
2
 
3
- EXPOSE <%- httpPort %>
3
+ RUN mkdir -p /app/code
4
+ WORKDIR /app/code
5
+
6
+ COPY start.sh /app/pkg/
7
+
8
+ CMD [ "/app/pkg/start.sh" ]
9
+
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+
@@ -1,65 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- 'use strict';
4
-
5
- const program = require('commander'),
6
- actions = require('../src/actions.js'),
7
- backupTools = require('../src/backup-tools.js');
8
-
9
- program.version(require('../package.json').version);
10
-
11
- program.command('create')
12
- .description('Create new app backup')
13
- .option('--app <id>', 'App id')
14
- .action(actions.backupCreate);
15
-
16
- program.command('list')
17
- .description('List all backups for specified app')
18
- .option('--raw', 'Print raw json output')
19
- .option('--app <id>', 'App id')
20
- .action(actions.backupList);
21
-
22
- program.command('decrypt <file>')
23
- .description('Decrypt an encrypted file')
24
- .option('--password <password>', 'password')
25
- .action(backupTools.decrypt);
26
-
27
- program.command('decrypt-dir <indir> <outdir>')
28
- .description('Decrypt an encrypted directory')
29
- .option('--password <password>', 'password')
30
- .action(backupTools.decryptDir);
31
-
32
- program.command('decrypt-filename <path>')
33
- .description('Encrypt a file name')
34
- .option('--password <password>', 'password')
35
- .action(backupTools.decryptFilename);
36
-
37
- program.command('encrypt <input>')
38
- .description('Encrypt a file')
39
- .option('--password <password>', 'password')
40
- .action(backupTools.encrypt);
41
-
42
- program.command('encrypt-filename <path>')
43
- .description('Encrypt a file name')
44
- .option('--password <password>', 'password')
45
- .action(backupTools.encryptFilename);
46
-
47
- if (!process.argv.slice(2).length) {
48
- program.outputHelp();
49
- } else { // https://github.com/tj/commander.js/issues/338
50
- // deal first with global flags!
51
- program.parse(process.argv);
52
-
53
- if (process.argv[2] === 'help') {
54
- return program.outputHelp();
55
- }
56
-
57
- var knownCommand = program.commands.some(function (command) { return command._name === process.argv[2] || command._alias === process.argv[2]; });
58
- if (!knownCommand) {
59
- console.log('Unknown command: ' + process.argv[2] + '.\nTry ' + 'cloudron backup help');
60
- process.exit(1);
61
- }
62
- return;
63
- }
64
-
65
- program.parse(process.argv);
package/bin/cloudron-env DELETED
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- 'use strict';
4
-
5
- const program = require('commander'),
6
- actions = require('../src/actions.js');
7
-
8
- program.version(require('../package.json').version);
9
-
10
- program.command('get <name>')
11
- .description('Get environment variables')
12
- .option('--app <id>', 'App id')
13
- .action(actions.envGet);
14
-
15
- program.command('list')
16
- .description('List environment variables')
17
- .option('--app <id>', 'App id')
18
- .action(actions.envList);
19
-
20
- program.command('set <KEY=value...>')
21
- .description('Set environment variables')
22
- .option('--app <id>', 'App id')
23
- .action(actions.envSet);
24
-
25
- program.command('unset <KEY...>')
26
- .description('Unset environment variables')
27
- .option('--app <id>', 'App id')
28
- .action(actions.envUnset);
29
-
30
- if (!process.argv.slice(2).length) {
31
- program.outputHelp();
32
- } else { // https://github.com/tj/commander.js/issues/338
33
- // deal first with global flags!
34
- program.parse(process.argv);
35
-
36
- if (process.argv[2] === 'help') {
37
- return program.outputHelp();
38
- }
39
-
40
- var knownCommand = program.commands.some(function (command) { return command._name === process.argv[2] || command._alias === process.argv[2]; });
41
- if (!knownCommand) {
42
- console.log('Unknown command: ' + process.argv[2] + '.\nTry ' + 'cloudron env help');
43
- process.exit(1);
44
- }
45
- return;
46
- }
47
-
48
- program.parse(process.argv);