@homespunapps/cli 1.6.76 → 1.6.77
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 +51 -0
- package/package.json +2 -2
package/dist/commands/deploy.js
CHANGED
|
@@ -274,6 +274,44 @@ function inlineExplicitAsset(asset) {
|
|
|
274
274
|
...(mime !== undefined ? { mime } : {}),
|
|
275
275
|
};
|
|
276
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Report connections the manifest references that the app does not have.
|
|
279
|
+
*
|
|
280
|
+
* Deploying a manifest DECLARES a connection name; it never creates the
|
|
281
|
+
* connection, because its allowed host is the exfiltration defence and only an
|
|
282
|
+
* owner may bind one. Without this the gap is invisible until someone uses the
|
|
283
|
+
* feature and gets "no connection named X on this app", which reads like a bug
|
|
284
|
+
* in the app rather than setup that was never done.
|
|
285
|
+
*
|
|
286
|
+
* STDERR, never stdout: stdout carries the JSON a caller pipes into `jq`.
|
|
287
|
+
*
|
|
288
|
+
* The printed command is a SUGGESTION built from the host the relay read out
|
|
289
|
+
* of the manifest. It is shown with its provenance so the reader checks it
|
|
290
|
+
* rather than pasting blind, and `--header-value -` makes the CLI prompt for
|
|
291
|
+
* the credential instead of putting it in shell history.
|
|
292
|
+
*/
|
|
293
|
+
function reportMissingConnections(missing, appRef) {
|
|
294
|
+
if (!missing || missing.length === 0)
|
|
295
|
+
return;
|
|
296
|
+
for (const m of missing) {
|
|
297
|
+
warn(`connection '${m.name}' is declared in the manifest but does not exist on this app; every call through it will fail`);
|
|
298
|
+
if (m.suggested_host) {
|
|
299
|
+
process.stderr.write(` the manifest points it at ${m.suggested_host}. If that is right:\n` +
|
|
300
|
+
` homespun connections create --app ${appRef} --name ${m.name} \\\n` +
|
|
301
|
+
` --allowed-host ${m.suggested_host} \\\n` +
|
|
302
|
+
` --kind static --header-name Authorization --header-value -\n`);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
// Rules disagreeing on a host, or resolving it from a setting at
|
|
306
|
+
// delivery time. Printing one of several hosts would suggest a binding
|
|
307
|
+
// that silently fails for the others, so name the host yourself.
|
|
308
|
+
process.stderr.write(` its rules do not agree on a single host, so choose the destination yourself:\n` +
|
|
309
|
+
` homespun connections create --app ${appRef} --name ${m.name} \\\n` +
|
|
310
|
+
` --allowed-host <host> \\\n` +
|
|
311
|
+
` --kind static --header-name Authorization --header-value -\n`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
277
315
|
/** Split explicit assets by the presign threshold. A pure size check, no I/O. */
|
|
278
316
|
function partitionExplicitAssets(assets) {
|
|
279
317
|
const small = [];
|
|
@@ -472,6 +510,15 @@ export async function runDeploy(args) {
|
|
|
472
510
|
...(force ? { force } : {}),
|
|
473
511
|
});
|
|
474
512
|
printJson(result);
|
|
513
|
+
reportMissingConnections(result.missing_connections, appId ?? "<app>");
|
|
514
|
+
// A check exists to be a gate, so it must not report success on a
|
|
515
|
+
// manifest whose connections do not exist: every call through them
|
|
516
|
+
// fails at runtime. Gated on the STRUCTURED field, never on the warning
|
|
517
|
+
// prose, because a grep that stops matching fails open and a gate that
|
|
518
|
+
// fails open is worse than no gate.
|
|
519
|
+
if ((result.missing_connections?.length ?? 0) > 0) {
|
|
520
|
+
process.exitCode = 1;
|
|
521
|
+
}
|
|
475
522
|
}
|
|
476
523
|
catch (e) {
|
|
477
524
|
failFromError(e);
|
|
@@ -529,6 +576,9 @@ export async function runDeploy(args) {
|
|
|
529
576
|
...(redeployed.breaks ? { breaks: redeployed.breaks } : {}),
|
|
530
577
|
...(redeployed.warnings ? { warnings: redeployed.warnings } : {}),
|
|
531
578
|
});
|
|
579
|
+
// A brand-new app has no connections at all, so this names every one the
|
|
580
|
+
// manifest declares. That is the moment the advice is most useful.
|
|
581
|
+
reportMissingConnections(redeployed.missing_connections, app.slug);
|
|
532
582
|
}
|
|
533
583
|
catch (e) {
|
|
534
584
|
failFromError(e);
|
|
@@ -577,6 +627,7 @@ export async function runDeploy(args) {
|
|
|
577
627
|
...(redeployed.breaks ? { breaks: redeployed.breaks } : {}),
|
|
578
628
|
...(redeployed.warnings ? { warnings: redeployed.warnings } : {}),
|
|
579
629
|
});
|
|
630
|
+
reportMissingConnections(redeployed.missing_connections, app.slug);
|
|
580
631
|
}
|
|
581
632
|
catch (e) {
|
|
582
633
|
failFromError(e);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homespunapps/cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.77",
|
|
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.77",
|
|
40
40
|
"qrcode-terminal": "^0.12.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|