@clubedge/create-clubedge-app 0.42.1 → 0.43.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 +19 -0
- package/README.md +3 -2
- package/dist/core/customize.js +6 -3
- package/dist/core/railway.js +19 -3
- package/dist/core/update.js +21 -0
- package/dist/modify.js +20 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `create-clubedge-app` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.43.1
|
|
6
|
+
|
|
7
|
+
- **`try` shows Railway's reason when Railway refuses a VM** (#144):
|
|
8
|
+
- Before, it said Railway may have changed its free VM and told you to update the CLI, which no update fixes.
|
|
9
|
+
- It now prints Railway's own explanation, then Railway's limits, the usual cause: 3 free VMs per network per day, and no anonymous VMs while Railway is busy.
|
|
10
|
+
- Still on Starter `v0.27.1`.
|
|
11
|
+
|
|
12
|
+
## 0.43.0
|
|
13
|
+
|
|
14
|
+
- **Upgrades keep each app's own version** (#134, by @ahmed-husssain):
|
|
15
|
+
- An app whose version the project changed in `apps/*/package.json` keeps it, without a conflict on that line.
|
|
16
|
+
- An app whose version the project never touched takes the new Starter's.
|
|
17
|
+
- This completes 0.41.0's version ownership, which covered the root `package.json`.
|
|
18
|
+
- **`add`, `remove`, and `upgrade` that change nothing exit before downloading the Starter** (#133, by @ahmed-husssain; issue #132):
|
|
19
|
+
- The cases: `upgrade` on the pinned Starter's commit or with `--ref` set to the recorded commit, `add` with options the project already uses, and `remove` of modules recorded as `none`.
|
|
20
|
+
- These commands now work offline. They no longer refuse a project with uncommitted changes, since they change nothing.
|
|
21
|
+
- `upgrade --ref <branch>` still resolves the branch, which may have moved. `remove` with an unknown module still fails with `Unknown module`.
|
|
22
|
+
- Still on Starter `v0.27.1`.
|
|
23
|
+
|
|
5
24
|
## 0.42.1
|
|
6
25
|
|
|
7
26
|
- Default to Starter `v0.27.1` (#140): new projects carry the Clubedge Starter brand, and their mobile app shows it.
|
package/README.md
CHANGED
|
@@ -381,7 +381,7 @@ Projects created with `--infra neon` also have `.github/workflows/preview.yml`,
|
|
|
381
381
|
Each CLI release is tested against one specific Starter release:
|
|
382
382
|
|
|
383
383
|
```text
|
|
384
|
-
create-clubedge-app v0.
|
|
384
|
+
create-clubedge-app v0.43.1
|
|
385
385
|
|
|
|
386
386
|
v
|
|
387
387
|
Clubedge Starter v0.27.1
|
|
@@ -392,7 +392,7 @@ The current relationship is:
|
|
|
392
392
|
|
|
393
393
|
| CLI release | Default Starter release | Meaning |
|
|
394
394
|
| --- | --- | --- |
|
|
395
|
-
| `create-clubedge-app@0.
|
|
395
|
+
| `create-clubedge-app@0.43.1` | `clubedge-starter@v0.27.1` | The CLI's required, tested default |
|
|
396
396
|
|
|
397
397
|
This means the same CLI version always scaffolds the same Starter revision by default. The CLI does not silently follow Starter `main`.
|
|
398
398
|
|
|
@@ -509,6 +509,7 @@ Each phase ends with a Starter release and a CLI release pinned to it. The full
|
|
|
509
509
|
| 0.40.0 | v0.26.0 | Starter v0.26.0: every module on React + Vite with FastAPI (pg-boss excepted) |
|
|
510
510
|
| 0.41.0 | v0.26.0 | A conflicted package.json no longer breaks later commands (`--detailed-exit-code`); doctor finds conflict markers; projects own their version and README |
|
|
511
511
|
| 0.42.0 | v0.27.0 | Starter v0.27.0: mobile apps with Capacitor (`--shell capacitor`, template schema 5); client addresses per platform; Docker Compose behind Caddy, with images on GHCR |
|
|
512
|
+
| 0.43.0 | v0.27.1 | Upgrades keep each app's own version; add, remove, and upgrade that change nothing skip the Starter download |
|
|
512
513
|
|
|
513
514
|
## Community
|
|
514
515
|
|
package/dist/core/customize.js
CHANGED
|
@@ -88,10 +88,13 @@ export function customizeLegacySource(source, { displayName }) {
|
|
|
88
88
|
.replaceAll(">Clubedge<", `>${displayName}<`)
|
|
89
89
|
.replaceAll("Starter workspace", "Application workspace");
|
|
90
90
|
}
|
|
91
|
-
/** Gives a selected stack's app the package name generated projects use. */
|
|
92
|
-
export function customizeAppManifest(source, appPackage) {
|
|
91
|
+
/** Gives a selected stack's app the package name generated projects use and preserves its version. */
|
|
92
|
+
export function customizeAppManifest(source, appPackage, version) {
|
|
93
93
|
const manifest = JSON.parse(source);
|
|
94
|
-
|
|
94
|
+
if (appPackage)
|
|
95
|
+
manifest.name = appPackage;
|
|
96
|
+
if (version !== undefined)
|
|
97
|
+
manifest.version = version;
|
|
95
98
|
return json(manifest);
|
|
96
99
|
}
|
|
97
100
|
/**
|
package/dist/core/railway.js
CHANGED
|
@@ -31,11 +31,12 @@ export function hostKeyFingerprint(key) {
|
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Finds Railway's JSON status line in a command's stderr. Returns null when there is none, and
|
|
34
|
-
* throws when there is one without the fields this CLI needs
|
|
34
|
+
* throws when there is one without the fields this CLI needs, with Railway's reason when it
|
|
35
|
+
* refused the VM.
|
|
35
36
|
*/
|
|
36
37
|
export function parseRailwayStatus(stderr) {
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
const lines = stderr.split(/\r?\n/).map((line) => line.trim());
|
|
39
|
+
for (const trimmed of lines) {
|
|
39
40
|
if (!trimmed.startsWith("{"))
|
|
40
41
|
continue;
|
|
41
42
|
let value;
|
|
@@ -47,6 +48,8 @@ export function parseRailwayStatus(stderr) {
|
|
|
47
48
|
}
|
|
48
49
|
if (typeof value.status !== "string")
|
|
49
50
|
continue;
|
|
51
|
+
if (value.status === "error")
|
|
52
|
+
throw new Error(railwayRefusal(value, lines));
|
|
50
53
|
const previewUrl = value.preview_url;
|
|
51
54
|
const claimUrl = value.human_claim_url;
|
|
52
55
|
const expiresAt = new Date(String(value.build_expires_at));
|
|
@@ -57,6 +60,19 @@ export function parseRailwayStatus(stderr) {
|
|
|
57
60
|
}
|
|
58
61
|
return null;
|
|
59
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Why Railway refused a VM, in its own words: a message in its status line, else the text it
|
|
65
|
+
* wrote around that line, else the line itself. Its limits come after, since they are the usual
|
|
66
|
+
* cause.
|
|
67
|
+
*/
|
|
68
|
+
function railwayRefusal(value, lines) {
|
|
69
|
+
const said = ["message", "error", "reason", "description", "detail"]
|
|
70
|
+
.map((key) => value[key])
|
|
71
|
+
.find((text) => typeof text === "string" && text.trim() !== "");
|
|
72
|
+
const around = lines.filter((line) => line && !line.startsWith("{")).join(" ");
|
|
73
|
+
const reason = (said?.trim() || around || `its answer was ${JSON.stringify(value)}`).slice(0, 500).replace(/\.$/, "");
|
|
74
|
+
return `Railway did not start a VM: ${reason}. Railway allows 3 free VMs per network per day and turns anonymous VMs off while it is busy, so try again later or from another network; see ${railwayFreeVmUrl}.`;
|
|
75
|
+
}
|
|
60
76
|
/** Where the CLI keeps its own SSH key for Railway, apart from the user's ~/.ssh. */
|
|
61
77
|
export function railwayKeyPath(env = process.env, platform = process.platform) {
|
|
62
78
|
const configHome = env.XDG_CONFIG_HOME || (platform === "win32" && env.APPDATA ? env.APPDATA : join(homedir(), ".config"));
|
package/dist/core/update.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// through a three-way merge, see merge.ts.
|
|
4
4
|
import { mkdir, readdir, rm, rmdir, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
|
+
import { customizeAppManifest } from "./customize.js";
|
|
6
7
|
import { updateEnvFile } from "./env.js";
|
|
7
8
|
import { INCOMING_DIR, planMerge, sameContent } from "./merge.js";
|
|
8
9
|
import { readProjectFiles, renderProject } from "./project.js";
|
|
@@ -35,6 +36,26 @@ export async function planUpdate(project, base, target) {
|
|
|
35
36
|
baseFiles.delete("README.md");
|
|
36
37
|
targetFiles.delete("README.md");
|
|
37
38
|
}
|
|
39
|
+
// Workspace apps take the new Starter's version unless the project customized their version.
|
|
40
|
+
for (const [path, targetContent] of targetFiles) {
|
|
41
|
+
if (!path.startsWith("apps/") || !path.endsWith("/package.json"))
|
|
42
|
+
continue;
|
|
43
|
+
const baseContent = baseFiles.get(path);
|
|
44
|
+
const currentContent = current.get(path);
|
|
45
|
+
if (!baseContent || !currentContent)
|
|
46
|
+
continue;
|
|
47
|
+
try {
|
|
48
|
+
const basePkg = JSON.parse(baseContent.toString("utf8"));
|
|
49
|
+
const currentPkg = JSON.parse(currentContent.toString("utf8"));
|
|
50
|
+
if (typeof currentPkg.version === "string" && currentPkg.version !== basePkg.version) {
|
|
51
|
+
baseFiles.set(path, Buffer.from(customizeAppManifest(baseContent.toString("utf8"), undefined, currentPkg.version)));
|
|
52
|
+
targetFiles.set(path, Buffer.from(customizeAppManifest(targetContent.toString("utf8"), undefined, currentPkg.version)));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Leave unparseable manifests untouched.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
38
59
|
const actions = planMerge(baseFiles, targetFiles, current);
|
|
39
60
|
if (customReadme && targetReadme && !sameContent(currentReadme, targetReadme)) {
|
|
40
61
|
actions.push({
|
package/dist/modify.js
CHANGED
|
@@ -366,8 +366,27 @@ export async function changeProject(args) {
|
|
|
366
366
|
throw new Error("Not a project change.");
|
|
367
367
|
const interactive = Boolean(process.stdin.isTTY) && !args.yes;
|
|
368
368
|
const project = await readProject(process.cwd());
|
|
369
|
-
await assertSafeToChange(project, args, interactive);
|
|
370
369
|
const upgrading = command.name === "upgrade";
|
|
370
|
+
if (upgrading &&
|
|
371
|
+
!args.templateDir &&
|
|
372
|
+
project.starterCommit &&
|
|
373
|
+
(args.ref ? args.ref === project.starterCommit : project.starterCommit === starterPin.starterCommit)) {
|
|
374
|
+
outro(`The project is already on Starter ${project.starterRef}.`);
|
|
375
|
+
return "unchanged";
|
|
376
|
+
}
|
|
377
|
+
if (command.name === "add" &&
|
|
378
|
+
command.changes.length > 0 &&
|
|
379
|
+
command.changes.every((c) => c.option && project.modules[c.module] === c.option)) {
|
|
380
|
+
outro("The project already uses that selection; nothing to change.");
|
|
381
|
+
return "unchanged";
|
|
382
|
+
}
|
|
383
|
+
if (command.name === "remove" &&
|
|
384
|
+
command.modules.length > 0 &&
|
|
385
|
+
command.modules.every((m) => project.modules[m] === "none")) {
|
|
386
|
+
outro("The project already uses that selection; nothing to change.");
|
|
387
|
+
return "unchanged";
|
|
388
|
+
}
|
|
389
|
+
await assertSafeToChange(project, args, interactive);
|
|
371
390
|
const progress = spinner();
|
|
372
391
|
progress.start(upgrading ? "Preparing both Starter revisions" : `Preparing Starter ${project.starterRef}`);
|
|
373
392
|
const sources = [];
|