cloudron 4.15.1 → 5.0.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/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # The Cloudron CLI tool
2
2
 
3
- The [Cloudron](https://cloudron.io) CLI tool allows you to install, configure and test apps on your Cloudron.
3
+ The [Cloudron](https://cloudron.io) [CLI tool](https://docs.cloudron.io/packaging/cli/) allows you to install, configure and test apps on your Cloudron.
4
4
  It is also used to submit your app to the Cloudron Store. The `machine` subcommand can be used for
5
5
  various maintenance tasks on a selfhosted Cloudron.
6
6
 
7
- Read the Cloudron.io [documentation](https://cloudron.io/documentation.html) for in-depth information.
7
+ Read the Cloudron.io [documentation](https://docs.cloudron.io/) for in-depth information.
8
8
 
9
9
  ## Installation
10
10
 
package/bin/cloudron CHANGED
@@ -257,7 +257,7 @@ program.command('update')
257
257
 
258
258
  const knownCommand = program.commands.some(function (command) { return command._name === process.argv[2] || command._alias === process.argv[2]; });
259
259
  if (!knownCommand) {
260
- console.log('Unknown command: ' + process.argv[2].bold + '.\nTry ' + 'cloudron help'.yellow);
260
+ console.log('Unknown command: ' + process.argv[2] + '.\nTry ' + 'cloudron help');
261
261
  process.exit(1);
262
262
  }
263
263
  })();
@@ -71,7 +71,7 @@ if (!process.argv.slice(2).length) {
71
71
 
72
72
  var knownCommand = program.commands.some(function (command) { return command._name === process.argv[2] || command._alias === process.argv[2]; });
73
73
  if (!knownCommand) {
74
- console.log('Unknown command: ' + process.argv[2].bold + '.\nTry ' + 'cloudron appstore help'.yellow);
74
+ console.log('Unknown command: ' + process.argv[2] + '.\nTry ' + 'cloudron appstore help');
75
75
  process.exit(1);
76
76
  }
77
77
  return;
@@ -20,12 +20,12 @@ program.command('list')
20
20
  .action(actions.backupList);
21
21
 
22
22
  program.command('decrypt <file>')
23
- .description('Decrypt a directory')
23
+ .description('Decrypt an encrypted file')
24
24
  .option('--password <password>', 'password')
25
25
  .action(backupTools.decrypt);
26
26
 
27
27
  program.command('decrypt-dir <indir> <outdir>')
28
- .description('Decrypt a directory')
28
+ .description('Decrypt an encrypted directory')
29
29
  .option('--password <password>', 'password')
30
30
  .action(backupTools.decryptDir);
31
31
 
@@ -56,7 +56,7 @@ if (!process.argv.slice(2).length) {
56
56
 
57
57
  var knownCommand = program.commands.some(function (command) { return command._name === process.argv[2] || command._alias === process.argv[2]; });
58
58
  if (!knownCommand) {
59
- console.log('Unknown command: ' + process.argv[2].bold + '.\nTry ' + 'cloudron backup help'.yellow);
59
+ console.log('Unknown command: ' + process.argv[2] + '.\nTry ' + 'cloudron backup help');
60
60
  process.exit(1);
61
61
  }
62
62
  return;
package/bin/cloudron-env CHANGED
@@ -39,7 +39,7 @@ if (!process.argv.slice(2).length) {
39
39
 
40
40
  var knownCommand = program.commands.some(function (command) { return command._name === process.argv[2] || command._alias === process.argv[2]; });
41
41
  if (!knownCommand) {
42
- console.log('Unknown command: ' + process.argv[2].bold + '.\nTry ' + 'cloudron env help'.yellow);
42
+ console.log('Unknown command: ' + process.argv[2] + '.\nTry ' + 'cloudron env help');
43
43
  process.exit(1);
44
44
  }
45
45
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudron",
3
- "version": "4.15.1",
3
+ "version": "5.0.0",
4
4
  "license": "MIT",
5
5
  "description": "Cloudron Commandline Tool",
6
6
  "main": "main.js",
@@ -20,12 +20,12 @@
20
20
  "async": "^3.2.3",
21
21
  "cloudron-manifestformat": "^5.15.2",
22
22
  "commander": "^6.1.0",
23
- "debug": "^4.3.3",
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": "^7.1.3",
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.3"
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
@@ -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,
@@ -1309,19 +1308,30 @@ async function exec(cmd, options) {
1309
1308
 
1310
1309
  if (tty && !stdin.isTTY) exit('stdin is not tty');
1311
1310
 
1311
+ const request = createRequest('POST', `/api/v1/apps/${app.id}/exec`, options);
1312
+ const response = await request.send({ cmd, tty });
1313
+ if (response.statusCode !== 200) return exit(`Failed to create exec: ${requestError(response)}`);
1314
+ const execId = response.body.id;
1315
+
1316
+ async function exitWithCode() {
1317
+ const response2 = await createRequest('GET', `/api/v1/apps/${app.id}/exec/${execId}`, options);
1318
+ if (response2.statusCode !== 200) return exit(`Failed to get exec code: ${requestError(response2)}`);
1319
+
1320
+ process.exit(response2.body.exitCode);
1321
+ }
1322
+
1312
1323
  const { adminFqdn, token, rejectUnauthorized } = requestOptions(options);
1313
1324
 
1314
- const query = {
1315
- rows: stdout.rows,
1316
- columns: stdout.columns,
1325
+ const searchParams = new URLSearchParams({
1326
+ rows: stdout.rows || 24,
1327
+ columns: stdout.columns || 80,
1317
1328
  access_token: token,
1318
- cmd: JSON.stringify(cmd),
1319
- tty: tty
1320
- };
1329
+ tty
1330
+ });
1321
1331
 
1322
1332
  const req = https.request({
1323
1333
  hostname: adminFqdn,
1324
- path: `/api/v1/apps/${app.id}/exec?` + querystring.stringify(query),
1334
+ path: `/api/v1/apps/${app.id}/exec/${execId}/start?${searchParams.toString()}`,
1325
1335
  method: 'GET',
1326
1336
  headers: {
1327
1337
  'Connection': 'Upgrade',
@@ -1345,8 +1355,8 @@ async function exec(cmd, options) {
1345
1355
  stdin.setRawMode(true);
1346
1356
  stdin.pipe(socket, { end: false }); // the remote will close the connection
1347
1357
  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
1358
+ socket.on('end', exitWithCode); // server closed the socket
1359
+ } else { // create stdin process on demand
1350
1360
  if (typeof stdin === 'function') stdin = stdin();
1351
1361
 
1352
1362
  stdin.on('data', function (d) {
@@ -1361,7 +1371,7 @@ async function exec(cmd, options) {
1361
1371
  socket.write(buf);
1362
1372
  });
1363
1373
 
1364
- stdout.on('close', () => process.exit()); // this is only emitted when stdout is a file and not the terminal
1374
+ stdout.on('close', exitWithCode); // this is only emitted when stdout is a file and not the terminal
1365
1375
 
1366
1376
  demuxStream(socket, stdout, process.stderr); // can get separate streams in non-tty mode
1367
1377
  socket.on('end', function () { // server closed the socket
@@ -1371,7 +1381,7 @@ async function exec(cmd, options) {
1371
1381
  socket.end();
1372
1382
 
1373
1383
  // process._getActiveHandles(); process._getActiveRequests();
1374
- if (stdout === process.stdout) setImmediate(() => process.exit()); // otherwise, we rely on the 'close' event above
1384
+ if (stdout === process.stdout) setImmediate(exitWithCode); // otherwise, we rely on the 'close' event above
1375
1385
  });
1376
1386
  }
1377
1387
  });
@@ -1457,6 +1467,7 @@ function init() {
1457
1467
  const manifestTemplate = fs.readFileSync(path.join(__dirname, 'templates/', 'CloudronManifest.json.ejs'), 'utf8');
1458
1468
  const dockerfileTemplate = fs.readFileSync(path.join(__dirname, 'templates/', 'Dockerfile.ejs'), 'utf8');
1459
1469
  const dockerignoreTemplate = fs.readFileSync(path.join(__dirname, 'templates/', 'dockerignore.ejs'), 'utf8');
1470
+ const startShTemplate = fs.readFileSync(path.join(__dirname, 'templates/', 'start.sh.ejs'), 'utf8');
1460
1471
 
1461
1472
  const data = {
1462
1473
  version: '0.1.0',
@@ -1481,6 +1492,15 @@ function init() {
1481
1492
  fs.writeFileSync('.dockerignore', dockerignore, 'utf8');
1482
1493
  }
1483
1494
 
1495
+ if (fs.existsSync('start.sh')) {
1496
+ console.log('start.sh already exists, skipping');
1497
+ } else {
1498
+ const dockerignore = ejs.render(startShTemplate, {});
1499
+ fs.writeFileSync('start.sh', dockerignore, 'utf8');
1500
+ fs.chmodSync('start.sh', 0o0775);
1501
+ }
1502
+
1503
+
1484
1504
  console.log();
1485
1505
  console.log('Now edit the CloudronManifest.json');
1486
1506
  console.log();
@@ -23,7 +23,7 @@ function encryptFilePath(filePath, encryption) {
23
23
  assert.strictEqual(typeof filePath, 'string');
24
24
  assert.strictEqual(typeof encryption, 'object');
25
25
 
26
- var encryptedParts = filePath.split('/').map(function (part) {
26
+ const encryptedParts = filePath.split('/').map(function (part) {
27
27
  let hmac = crypto.createHmac('sha256', Buffer.from(encryption.filenameHmacKey, 'hex'));
28
28
  const iv = hmac.update(part).digest().slice(0, 16); // iv has to be deterministic, for our sync (copy) logic to work
29
29
  const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(encryption.filenameKey, 'hex'), iv);
@@ -42,7 +42,7 @@ function decryptFilePath(filePath, encryption) {
42
42
  assert.strictEqual(typeof filePath, 'string');
43
43
  assert.strictEqual(typeof encryption, 'object');
44
44
 
45
- let decryptedParts = [];
45
+ const decryptedParts = [];
46
46
  for (let part of filePath.split('/')) {
47
47
  part = part + Array(part.length % 4).join('='); // add back = padding
48
48
  part = part.replace(/-/g, '/'); // replace with '/'
@@ -197,9 +197,9 @@ function encrypt(input, options) {
197
197
 
198
198
  const encryption = aesKeysFromPassword(options.password);
199
199
 
200
- let inStream = fs.createReadStream(input);
201
- let outStream = process.stdout;
202
- let encryptStream = new EncryptStream(encryption);
200
+ const inStream = fs.createReadStream(input);
201
+ const outStream = process.stdout;
202
+ const encryptStream = new EncryptStream(encryption);
203
203
 
204
204
  inStream.on('error', exit);
205
205
  encryptStream.on('error', exit);
@@ -227,9 +227,9 @@ function decrypt(input, options) {
227
227
 
228
228
  const encryption = aesKeysFromPassword(options.password);
229
229
 
230
- let inStream = fs.createReadStream(input);
231
- let outStream = process.stdout;
232
- let decryptStream = new DecryptStream(encryption);
230
+ const inStream = fs.createReadStream(input);
231
+ const outStream = process.stdout;
232
+ const decryptStream = new DecryptStream(encryption);
233
233
 
234
234
  inStream.on('error', exit);
235
235
  decryptStream.on('error', exit);
@@ -1,3 +1,9 @@
1
- FROM cloudron/base:3.1.0@sha256:eb2ab9c7d361acda2f3ef2d8388154bc48f1651b5013fe6de4beea003018e427
1
+ FROM cloudron/base:3.2.0@sha256:ba1d566164a67c266782545ea9809dc611c4152e27686fd14060332dd88263ea
2
+
3
+ RUN mkdir -p /app/code
4
+ WORKDIR /app/code
5
+
6
+ COPY start.sh /app/pkg/
7
+
8
+ CMD [ "/app/pkg/start.sh" ]
2
9
 
3
- EXPOSE <%- httpPort %>
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+