cloudron 8.1.2 → 8.2.1

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/CHANGELOG.md CHANGED
@@ -94,3 +94,34 @@
94
94
  * Add `cloudron sync push` and `cloudron sync pull` subcommands
95
95
  * Merge all binaries into single `cloudron` command
96
96
 
97
+ [7.1.1]
98
+ * Prettier login success page
99
+ * oidc: explicitly listen on ipv4 which is always available
100
+
101
+ [7.1.2]
102
+ * versions: implement revoke --version
103
+ * Make the login page slightly more like the dashboard style and add dark mode
104
+
105
+ [8.0.0]
106
+ * Implement device authorization grant flow
107
+
108
+ [8.0.1]
109
+ * device codes only needs to be confirmed not entered
110
+ * Add fallback in case the overloaded cloudron build --help is called
111
+
112
+ [8.0.2]
113
+ * install/update: -f and --build-arg
114
+ * wait even in debug mode, but just not for healthy
115
+ * add cloudron init --template <template>
116
+ * Fix cloudron pull as newer nodejs versions do not have resume() on stdout anymore
117
+
118
+ [8.1.0]
119
+ * Continue cloudron build even if run outside a git repository
120
+ * Remove old legacy appstore login/logout
121
+
122
+ [8.1.1]
123
+ * token is not required. verify-manifest does not need it
124
+
125
+ [8.1.2]
126
+ * add appstore login/logout and also forum post logs
127
+
package/bin/cloudron CHANGED
@@ -437,4 +437,8 @@ versionsCommand.command('update')
437
437
  .option('--state <state>', 'Publish state (published or testing)')
438
438
  .action(versionsActions.addOrUpdate);
439
439
 
440
+ versionsCommand.command('verify')
441
+ .description('Verify if versions file is valid for publishing')
442
+ .action(versionsActions.verify);
443
+
440
444
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudron",
3
- "version": "8.1.2",
3
+ "version": "8.2.1",
4
4
  "license": "MIT",
5
5
  "description": "Cloudron Commandline Tool",
6
6
  "type": "module",
package/src/actions.js CHANGED
@@ -102,6 +102,11 @@ async function stopActiveTask(app, options) {
102
102
  const request = createRequest('POST', `/api/v1/tasks/${app.taskId}/stop`, options);
103
103
  const response = await request.send({});
104
104
  if (response.status !== 204) throw `Failed to stop active task: ${requestError(response)}`;
105
+
106
+ if (!options.wait) return;
107
+
108
+ const result = await waitForTask(app.taskId, options);
109
+ if (result.error) throw new Error(`Stop active task failed: ${result.error.message}`);
105
110
  }
106
111
 
107
112
  function saveCwdAppId(appId, manifestFilePath) {
@@ -31,51 +31,6 @@ async function writeVersions(versionsFilePath, versionsRoot) {
31
31
  if (error) return exit(`Unable to write to ${path.relative(process.cwd(), versionsFilePath)}: ${error.message}`);
32
32
  }
33
33
 
34
- async function init(/*localOptions, cmd*/) {
35
- const manifestFilePath = locateManifest();
36
- if (!manifestFilePath) return exit(`${NO_MANIFEST_FOUND_ERROR_STRING}. You must run this command in the package dir.`);
37
-
38
- const baseDir = path.dirname(manifestFilePath);
39
- const versionsFilePath = `${baseDir}/CloudronVersions.json`;
40
-
41
- if (fs.existsSync(versionsFilePath)) return exit(`${path.relative(process.cwd(), versionsFilePath)} already exists.`);
42
-
43
- await writeVersions(versionsFilePath, { stable: true, versions: {} });
44
- console.log(`Created ${path.relative(process.cwd(), versionsFilePath)}.`);
45
-
46
- const result = manifestFormat.parseFile(manifestFilePath);
47
- if (!result.error) {
48
- ensurePublishFields(result.manifest, manifestFilePath);
49
- }
50
-
51
- console.log('\nUse "cloudron versions add" to add a version.');
52
- }
53
-
54
- async function resolveManifest(manifest, baseDir) {
55
- if (manifest.description.slice(0, 7) === 'file://') {
56
- let descriptionFilePath = manifest.description.slice(7);
57
- descriptionFilePath = path.isAbsolute(descriptionFilePath) ? descriptionFilePath : path.join(baseDir, descriptionFilePath);
58
- manifest.description = safe.fs.readFileSync(descriptionFilePath, 'utf8');
59
- if (!manifest.description && safe.error) throw(new Error('Could not read/parse description ' + safe.error.message));
60
- if (!manifest.description) throw new Error('Description cannot be empty');
61
- }
62
-
63
- if (manifest.postInstallMessage && manifest.postInstallMessage.slice(0, 7) === 'file://') {
64
- let postInstallFilePath = manifest.postInstallMessage.slice(7);
65
- postInstallFilePath = path.isAbsolute(postInstallFilePath) ? postInstallFilePath : path.join(baseDir, postInstallFilePath);
66
- manifest.postInstallMessage = safe.fs.readFileSync(postInstallFilePath, 'utf8');
67
- if (!manifest.postInstallMessage && safe.error) throw(new Error('Could not read/parse postInstall ' + safe.error.message));
68
- if (!manifest.postInstallMessage) throw new Error('PostInstall file specified but it is empty');
69
- }
70
-
71
- if (manifest.changelog.slice(0, 7) === 'file://') {
72
- let changelogPath = manifest.changelog.slice(7);
73
- changelogPath = path.isAbsolute(changelogPath) ? changelogPath : path.join(baseDir, changelogPath);
74
- manifest.changelog = parseChangelog(changelogPath, manifest.version);
75
- if (!manifest.changelog) throw new Error('Bad changelog format or missing changelog for this version');
76
- }
77
- }
78
-
79
34
  function createStubFile(filePath, content) {
80
35
  if (fs.existsSync(filePath)) return false;
81
36
  fs.writeFileSync(filePath, content, 'utf8');
@@ -162,6 +117,55 @@ function ensurePublishFields(manifest, manifestFilePath) {
162
117
  console.log(' tags, mediaLinks, DESCRIPTION.md, CHANGELOG, POSTINSTALL.md');
163
118
  }
164
119
 
120
+ async function init(/*localOptions, cmd*/) {
121
+ const manifestFilePath = locateManifest();
122
+ if (!manifestFilePath) return exit(`${NO_MANIFEST_FOUND_ERROR_STRING}. You must run this command in the package dir.`);
123
+
124
+ const baseDir = path.dirname(manifestFilePath);
125
+ const versionsFilePath = `${baseDir}/CloudronVersions.json`;
126
+
127
+ if (fs.existsSync(versionsFilePath)) return exit(`${path.relative(process.cwd(), versionsFilePath)} already exists.`);
128
+
129
+ await writeVersions(versionsFilePath, { stable: true, versions: {} });
130
+ console.log(`Created ${path.relative(process.cwd(), versionsFilePath)}.`);
131
+
132
+ const result = manifestFormat.parseFile(manifestFilePath);
133
+ if (!result.error) {
134
+ ensurePublishFields(result.manifest, manifestFilePath);
135
+ }
136
+
137
+ console.log('\nUse "cloudron versions add" to add a version.');
138
+ }
139
+
140
+ async function resolveManifest(manifest, baseDir) {
141
+ const resolved = structuredClone(manifest);
142
+
143
+ if (resolved.description.slice(0, 7) === 'file://') {
144
+ let descriptionFilePath = resolved.description.slice(7);
145
+ descriptionFilePath = path.isAbsolute(descriptionFilePath) ? descriptionFilePath : path.join(baseDir, descriptionFilePath);
146
+ resolved.description = safe.fs.readFileSync(descriptionFilePath, 'utf8');
147
+ if (!resolved.description && safe.error) throw(new Error('Could not read/parse description ' + safe.error.message));
148
+ if (!resolved.description) throw new Error('Description cannot be empty');
149
+ }
150
+
151
+ if (resolved.postInstallMessage && resolved.postInstallMessage.slice(0, 7) === 'file://') {
152
+ let postInstallFilePath = resolved.postInstallMessage.slice(7);
153
+ postInstallFilePath = path.isAbsolute(postInstallFilePath) ? postInstallFilePath : path.join(baseDir, postInstallFilePath);
154
+ resolved.postInstallMessage = safe.fs.readFileSync(postInstallFilePath, 'utf8');
155
+ if (!resolved.postInstallMessage && safe.error) throw(new Error('Could not read/parse postInstall ' + safe.error.message));
156
+ if (!resolved.postInstallMessage) throw new Error('PostInstall file specified but it is empty');
157
+ }
158
+
159
+ if (resolved.changelog.slice(0, 7) === 'file://') {
160
+ let changelogPath = resolved.changelog.slice(7);
161
+ changelogPath = path.isAbsolute(changelogPath) ? changelogPath : path.join(baseDir, changelogPath);
162
+ resolved.changelog = parseChangelog(changelogPath, resolved.version);
163
+ if (!resolved.changelog) throw new Error('Bad changelog format or missing changelog for this version');
164
+ }
165
+
166
+ return resolved;
167
+ }
168
+
165
169
  async function addOrUpdate(localOptions, cmd) {
166
170
  const isUpdate = cmd.parent.args[0] === 'update';
167
171
  const versionsFilePath = await locateVersions();
@@ -194,7 +198,7 @@ async function addOrUpdate(localOptions, cmd) {
194
198
  const versionsRoot = await readVersions(versionsFilePath);
195
199
  const versions = versionsRoot.versions;
196
200
 
197
- await resolveManifest(manifest, path.dirname(manifestFilePath));
201
+ const resolved = await resolveManifest(manifest, path.dirname(manifestFilePath));
198
202
 
199
203
  if (options.state && options.state !== PUBLISH_STATE_PUBLISHED && options.state !== PUBLISH_STATE_TESTING) {
200
204
  return exit(`Invalid state "${options.state}". Must be "published" or "testing".`);
@@ -204,13 +208,13 @@ async function addOrUpdate(localOptions, cmd) {
204
208
  const targetVersion = options.version || manifest.version;
205
209
  if (!(targetVersion in versions)) exit(`${targetVersion} does not exist in ${path.relative(process.cwd(), versionsFilePath)}.`);
206
210
 
207
- versions[targetVersion].manifest = manifest;
211
+ versions[targetVersion].manifest = resolved;
208
212
  versions[targetVersion].ts = (new Date()).toUTCString();
209
213
  if (options.state) versions[targetVersion].publishState = options.state;
210
214
  } else {
211
215
  if (manifest.version in versions) exit(`${manifest.version} already exists in ${path.relative(process.cwd(), versionsFilePath)}.`);
212
216
  versions[manifest.version] = {
213
- manifest,
217
+ manifest: resolved,
214
218
  creationDate: (new Date()).toUTCString(),
215
219
  ts: (new Date()).toUTCString(),
216
220
  publishState: options.state || PUBLISH_STATE_PUBLISHED
@@ -278,9 +282,38 @@ async function revoke(localOptions, cmd) {
278
282
  console.log(`Marked ${targetVersion} as revoked in ${path.relative(process.cwd(), versionsFilePath)}`);
279
283
  }
280
284
 
285
+ async function verify(/* localOptions, cmd */) {
286
+ const versionsFilePath = await locateVersions();
287
+ if (!versionsFilePath) return exit(NO_VERSIONS_FOUND_ERROR_STRING);
288
+
289
+ const versionsRoot = await readVersions(versionsFilePath);
290
+ const versions = versionsRoot.versions;
291
+
292
+ const sortedVersions = Object.keys(versions).sort(manifestFormat.packageVersionCompare);
293
+ if (sortedVersions.length === 0) return exit('No versions found in versions file');
294
+
295
+ for (const version of sortedVersions) {
296
+ const manifest = versions[version].manifest;
297
+
298
+ if (!manifest.dockerImage) return exit(`Version ${version}: missing dockerImage`);
299
+
300
+ const error = manifestFormat.checkVersionsRequirements(manifest);
301
+ if (error) return exit(`Version ${version}: ${error}`);
302
+
303
+ try {
304
+ await resolveManifest(manifest, path.dirname(versionsFilePath));
305
+ } catch (e) {
306
+ return exit(`Version ${version}: ${e.message}`);
307
+ }
308
+ }
309
+
310
+ console.log(`${path.relative(process.cwd(), versionsFilePath)} is valid (${sortedVersions.length} version(s))`);
311
+ }
312
+
281
313
  export default {
282
314
  init,
283
315
  addOrUpdate,
284
316
  list,
285
317
  revoke,
318
+ verify,
286
319
  };