@homespunapps/cli 1.6.35 → 1.6.36
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/dist/commands/deploy.js +30 -11
- package/dist/help-catalog.js +3 -2
- package/package.json +2 -2
package/dist/commands/deploy.js
CHANGED
|
@@ -9,7 +9,18 @@ import { specFor } from "../help-catalog.js";
|
|
|
9
9
|
import { fail, failFromError, printJson } from "../output.js";
|
|
10
10
|
import { resolveJson } from "../input.js";
|
|
11
11
|
import { resolveAppId } from "../resolve-app.js";
|
|
12
|
-
function readBundle(source, manifestFlag) {
|
|
12
|
+
function readBundle(source, manifestFlag, isRedeploy) {
|
|
13
|
+
// No source at all: only a redeploy can do this, and only to change the
|
|
14
|
+
// manifest alone (the live document is inherited).
|
|
15
|
+
if (source === undefined) {
|
|
16
|
+
if (!isRedeploy) {
|
|
17
|
+
fail("usage: homespun deploy <dir|file> [--app <id>] ...", "invalid_args");
|
|
18
|
+
}
|
|
19
|
+
if (manifestFlag === undefined) {
|
|
20
|
+
fail("nothing to deploy: pass a directory or file, or --manifest <path|json> for a manifest-only redeploy", "invalid_args");
|
|
21
|
+
}
|
|
22
|
+
return { manifest: resolveJson(manifestFlag, "--manifest") };
|
|
23
|
+
}
|
|
13
24
|
if (!existsSync(source)) {
|
|
14
25
|
fail(`no such file or directory: ${source}`, "invalid_args");
|
|
15
26
|
}
|
|
@@ -33,9 +44,13 @@ function readBundle(source, manifestFlag) {
|
|
|
33
44
|
manifest: JSON.parse(readFileSync(manifestPath, "utf8")),
|
|
34
45
|
};
|
|
35
46
|
}
|
|
36
|
-
// Single-file escape hatch.
|
|
47
|
+
// Single-file escape hatch. A create must pair it with --manifest; a
|
|
48
|
+
// redeploy may omit --manifest, which keeps the live manifest.
|
|
37
49
|
if (manifestFlag === undefined) {
|
|
38
|
-
|
|
50
|
+
if (!isRedeploy) {
|
|
51
|
+
fail("single-file deploy requires --manifest <path|json>", "invalid_args");
|
|
52
|
+
}
|
|
53
|
+
return { html: readFileSync(source, "utf8") };
|
|
39
54
|
}
|
|
40
55
|
return {
|
|
41
56
|
html: readFileSync(source, "utf8"),
|
|
@@ -45,9 +60,6 @@ function readBundle(source, manifestFlag) {
|
|
|
45
60
|
export async function runDeploy(args) {
|
|
46
61
|
assertKnownFlags(args, ...specFor("deploy"));
|
|
47
62
|
const source = args.positionals[0];
|
|
48
|
-
if (!source) {
|
|
49
|
-
fail("usage: homespun deploy <dir|file> [--app <id>] ...", "invalid_args");
|
|
50
|
-
}
|
|
51
63
|
const appId = args.flags.get("app");
|
|
52
64
|
const slug = args.flags.get("slug");
|
|
53
65
|
const visibility = args.flags.get("visibility");
|
|
@@ -57,7 +69,7 @@ export async function runDeploy(args) {
|
|
|
57
69
|
}
|
|
58
70
|
const force = args.bools.has("force");
|
|
59
71
|
const check = args.bools.has("check");
|
|
60
|
-
const bundle = readBundle(source, args.flags.get("manifest"));
|
|
72
|
+
const bundle = readBundle(source, args.flags.get("manifest"), appId !== undefined);
|
|
61
73
|
const client = makeClient(args);
|
|
62
74
|
// Dry run (--check): validate + report what a real deploy would do, persist
|
|
63
75
|
// NOTHING. Runs for both create (no --app) and redeploy (--app), the latter
|
|
@@ -67,8 +79,8 @@ export async function runDeploy(args) {
|
|
|
67
79
|
const id = appId !== undefined ? await resolveAppId(client, appId) : undefined;
|
|
68
80
|
const result = await client.checkDeploy({
|
|
69
81
|
...(id !== undefined ? { app_id: id } : {}),
|
|
70
|
-
html: bundle.html,
|
|
71
|
-
manifest: bundle.manifest,
|
|
82
|
+
...(bundle.html !== undefined ? { html: bundle.html } : {}),
|
|
83
|
+
...(bundle.manifest !== undefined ? { manifest: bundle.manifest } : {}),
|
|
72
84
|
...(force ? { force } : {}),
|
|
73
85
|
});
|
|
74
86
|
printJson(result);
|
|
@@ -85,6 +97,9 @@ export async function runDeploy(args) {
|
|
|
85
97
|
fail("a caller-supplied --slug is not allowed with visibility 'link' (link slugs are always server-generated); drop --visibility link, or omit --slug", "invalid_args");
|
|
86
98
|
}
|
|
87
99
|
try {
|
|
100
|
+
// readBundle guarantees both halves on the create path (a create can
|
|
101
|
+
// inherit nothing), so the non-null assertions are the type system
|
|
102
|
+
// catching up with a check that already ran.
|
|
88
103
|
const out = await client.deployApp({
|
|
89
104
|
html: bundle.html,
|
|
90
105
|
manifest: bundle.manifest,
|
|
@@ -107,9 +122,13 @@ export async function runDeploy(args) {
|
|
|
107
122
|
}
|
|
108
123
|
const id = await resolveAppId(client, appId);
|
|
109
124
|
try {
|
|
125
|
+
// Only what this invocation actually read is sent: an omitted html or
|
|
126
|
+
// manifest keeps what is live, so `homespun deploy ./index.html --app <id>`
|
|
127
|
+
// ships the document alone and `--manifest` with no file ships the
|
|
128
|
+
// manifest alone.
|
|
110
129
|
const redeployed = await client.redeployApp(id, {
|
|
111
|
-
html: bundle.html,
|
|
112
|
-
manifest: bundle.manifest,
|
|
130
|
+
...(bundle.html !== undefined ? { html: bundle.html } : {}),
|
|
131
|
+
...(bundle.manifest !== undefined ? { manifest: bundle.manifest } : {}),
|
|
113
132
|
force,
|
|
114
133
|
});
|
|
115
134
|
const app = await client.getApp(id);
|
package/dist/help-catalog.js
CHANGED
|
@@ -777,7 +777,7 @@ const DEPLOY = {
|
|
|
777
777
|
verbs: [
|
|
778
778
|
{
|
|
779
779
|
verb: "",
|
|
780
|
-
positionals: "
|
|
780
|
+
positionals: "[dir|file]",
|
|
781
781
|
summary: "Creates a new app, or redeploys an existing one when --app is given.",
|
|
782
782
|
flags: [
|
|
783
783
|
{
|
|
@@ -813,7 +813,8 @@ const DEPLOY = {
|
|
|
813
813
|
notes: [
|
|
814
814
|
"Packaging has one canonical shape and one escape hatch. A directory deploy (homespun deploy ./my-app) reads ./my-app/index.html and ./my-app/manifest.json: fixed filenames, no discovery heuristics, and both files are required. The single-file escape hatch (homespun deploy ./index.html --manifest ./manifest.json) takes the manifest from --manifest, which accepts a file path or inline JSON.",
|
|
815
815
|
"Create versus redeploy is decided by the presence of --app, not by two verbs. With no --app this creates an app (POST /v1/apps); new apps default to private (owner plus invited members, sign-in gated), --slug is accepted with private or public visibility including the default, and an explicit --visibility link always gets a server-generated slug and rejects --slug. With --app <id> this redeploys (POST /v1/apps/:id/versions), where --slug and --visibility are rejected because the slug is immutable and visibility changes go through 'homespun apps update'.",
|
|
816
|
-
"
|
|
816
|
+
"On redeploy, what you do not send is kept. 'homespun deploy ./index.html --app <id>' ships the document alone and keeps the live manifest; 'homespun deploy --app <id> --manifest ./manifest.json' ships the manifest alone and keeps the live document, with no file argument at all; a directory deploy still ships both. The live asset set is carried forward either way. A create can inherit nothing, so it still needs both halves.",
|
|
817
|
+
"--check is a dry run. It runs the full manifest and asset validation (shape and MIME), the redeploy compat gate (with --app), and the schedule-timezone advisory, then prints { ok, warnings, compat, breaks } without creating a version or mutating anything. An invalid manifest fails the same way a real deploy would, and a redeploy the compat gate would refuse reports the break instead of applying it. It resolves omitted fields exactly as a real redeploy would, so it reports on the deploy that would actually run.",
|
|
817
818
|
],
|
|
818
819
|
outputNote: 'Output is JSON: { app_id, slug, url, version, visibility, created, share_url, compat, breaks, warnings }. share_url is present only when creating a link-visibility app: it carries the app share token in its #k= fragment and is shown ONCE, it is not recoverable later, and it can be rotated with \'homespun apps share-link rotate <app>\'. warnings flags non-fatal issues, for example an app that declares schedules with no timezone set (reminders fire at 08:00 UTC until one is set). Errors go to stderr as {"error":{"code","message"}} with a non-zero exit.',
|
|
819
820
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homespunapps/cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.36",
|
|
4
4
|
"description": "Command-line client for the Homespun relay: deploy a real multi-user web app from your agent, then keep reading and writing its data.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"test:unit": "vitest run"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@homespunapps/core": "^1.6.
|
|
39
|
+
"@homespunapps/core": "^1.6.36",
|
|
40
40
|
"qrcode-terminal": "^0.12.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|