@substrat-run/cli 0.38.1 → 0.39.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/dist/cli.js +24 -7
- package/dist/cli.js.map +1 -1
- package/dist/hostnames.d.ts +2 -21
- package/dist/hostnames.d.ts.map +1 -1
- package/dist/hostnames.js +30 -38
- package/dist/hostnames.js.map +1 -1
- package/dist/http.d.ts +8 -19
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +8 -29
- package/dist/http.js.map +1 -1
- package/dist/installs.d.ts.map +1 -1
- package/dist/installs.js +3 -12
- package/dist/installs.js.map +1 -1
- package/dist/listing.d.ts.map +1 -1
- package/dist/listing.js +3 -20
- package/dist/listing.js.map +1 -1
- package/dist/login.d.ts +5 -0
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +9 -12
- package/dist/login.js.map +1 -1
- package/dist/plane.d.ts +50 -0
- package/dist/plane.d.ts.map +1 -0
- package/dist/plane.js +68 -0
- package/dist/plane.js.map +1 -0
- package/dist/preview.d.ts.map +1 -1
- package/dist/preview.js +20 -10
- package/dist/preview.js.map +1 -1
- package/dist/problem.d.ts +7 -2
- package/dist/problem.d.ts.map +1 -1
- package/dist/problem.js +8 -10
- package/dist/problem.js.map +1 -1
- package/dist/promote.d.ts +3 -31
- package/dist/promote.d.ts.map +1 -1
- package/dist/promote.js +25 -27
- package/dist/promote.js.map +1 -1
- package/dist/push.d.ts +73 -0
- package/dist/push.d.ts.map +1 -1
- package/dist/push.js +127 -25
- package/dist/push.js.map +1 -1
- package/dist/schedule-sweeper.d.ts +34 -0
- package/dist/schedule-sweeper.d.ts.map +1 -0
- package/dist/schedule-sweeper.js +223 -0
- package/dist/schedule-sweeper.js.map +1 -0
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +37 -99
- package/dist/scope.js.map +1 -1
- package/dist/versions.d.ts.map +1 -1
- package/dist/versions.js +8 -18
- package/dist/versions.js.map +1 -1
- package/dist/whoami.d.ts +7 -11
- package/dist/whoami.d.ts.map +1 -1
- package/dist/whoami.js +2 -13
- package/dist/whoami.js.map +1 -1
- package/package.json +6 -4
package/dist/promote.js
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* by name, any other tenant only as a count. `--ack-export-break` passes it once that is read.
|
|
15
15
|
*/
|
|
16
16
|
import { diffRegistries, unitemisedRegistryChanges, } from '@substrat-run/contracts';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { failureMessage,
|
|
17
|
+
import { ControlPlaneError, } from '@substrat-run/control-plane-client';
|
|
18
|
+
import { planeFor, viaPlane, walkAll } from './plane.js';
|
|
19
|
+
import { failureMessage, readRefused } from './problem.js';
|
|
20
20
|
/** The listing a refused (or acknowledged) export break carries, one line per affected app. */
|
|
21
21
|
export function exportBreakLines(b) {
|
|
22
22
|
const lines = b.affected.map((r) => ` ${r.vertical} (scope ${r.scopeId}) imports ${r.type} v${r.schemaVersion} — ` +
|
|
@@ -30,23 +30,22 @@ export function exportBreakListing(b) {
|
|
|
30
30
|
return `\n${exportBreakLines(b).join('\n')}\n(re-run with --ack-export-break once read)`;
|
|
31
31
|
}
|
|
32
32
|
export async function promote(opts) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (!res.ok) {
|
|
33
|
+
// `advisory`: a promote is one of the three commands that nudge a stale CLI, off every
|
|
34
|
+
// answer — success or refusal — before it is read.
|
|
35
|
+
const client = planeFor(opts.controlPlaneUrl, opts.header, { advisory: true });
|
|
36
|
+
try {
|
|
37
|
+
// The refusal is passed through as it is: it needs a second round-trip to explain, which a
|
|
38
|
+
// synchronous mapper cannot make.
|
|
39
|
+
return await viaPlane(() => client.promoteChannel(opts.slug, opts.channel, opts.versionId, opts.acknowledge), (e) => e);
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
if (!(e instanceof ControlPlaneError))
|
|
43
|
+
throw e;
|
|
44
|
+
const body = e.body ?? '';
|
|
45
|
+
// A refused promote is exactly where the problem document earns its keep: the two
|
|
46
|
+
// checkpoints answer 4xx with the digests that need acknowledging (#971) — and, since
|
|
47
|
+
// #1677, with the two diffs those digests stand for, so `--ack-*` is an informed answer.
|
|
48
|
+
// An export-break refusal (#1705 PR 3) carries its own listing of the apps it breaks.
|
|
50
49
|
let parsed = {};
|
|
51
50
|
try {
|
|
52
51
|
parsed = JSON.parse(body);
|
|
@@ -59,7 +58,7 @@ export async function promote(opts) {
|
|
|
59
58
|
: parsed.exportBreaksUnavailable
|
|
60
59
|
? `\n (${parsed.exportBreaksUnavailable}; re-run with --ack-export-break to promote anyway)`
|
|
61
60
|
: '';
|
|
62
|
-
const message = failureMessage('promote failed',
|
|
61
|
+
const message = failureMessage('promote failed', e.status, body);
|
|
63
62
|
if (!message.includes(NEEDS_ACK))
|
|
64
63
|
throw new Error(message + listing);
|
|
65
64
|
const lines = await explainRefusal(opts, {
|
|
@@ -68,7 +67,6 @@ export async function promote(opts) {
|
|
|
68
67
|
});
|
|
69
68
|
throw new Error(`${message}${listing}\n\n${lines.join('\n')}`);
|
|
70
69
|
}
|
|
71
|
-
return parseJsonBody(body, url);
|
|
72
70
|
}
|
|
73
71
|
/** What both digest refusals say (`… — acknowledge it explicitly to promote`). Case matters:
|
|
74
72
|
* the export-break refusal says "Acknowledge it explicitly (exportBreak)", and it is answered
|
|
@@ -81,20 +79,20 @@ const NEEDS_ACK = 'acknowledge it explicitly';
|
|
|
81
79
|
* that could not be read is said so in a line rather than put in its place.
|
|
82
80
|
*/
|
|
83
81
|
export async function explainRefusal(opts, refused) {
|
|
84
|
-
const
|
|
85
|
-
const get = (
|
|
82
|
+
const client = planeFor(opts.controlPlaneUrl, opts.header);
|
|
83
|
+
const get = (read) => viaPlane(read, readRefused);
|
|
86
84
|
try {
|
|
87
|
-
const channels = await
|
|
85
|
+
const channels = await walkAll((page) => client.listChannels(opts.slug, page));
|
|
88
86
|
// The channel's `versionId`, as the gate compares — not `servingVersionId`, which only
|
|
89
87
|
// differs after a failed in-place serve (#1661 follows that one for reconcile).
|
|
90
88
|
const serving = channels.find((c) => c.channel === opts.channel)?.versionId;
|
|
91
89
|
if (!serving)
|
|
92
90
|
return ['(nothing serves this channel yet, so there is nothing to diff against)'];
|
|
93
|
-
const registry = (id) => get(
|
|
91
|
+
const registry = (id) => get(() => client.getVersionRegistry(opts.slug, id)).then((r) => r.registry);
|
|
94
92
|
const [from, to, migrations] = await Promise.all([
|
|
95
93
|
registry(serving),
|
|
96
94
|
registry(opts.versionId),
|
|
97
|
-
get(
|
|
95
|
+
get(() => client.getVersionMigrations(opts.slug, opts.versionId, serving)).then((r) => r.migrations),
|
|
98
96
|
]);
|
|
99
97
|
return [
|
|
100
98
|
`${opts.channel} serves ${serving}; promoting ${opts.versionId}.`,
|
package/dist/promote.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promote.js","sourceRoot":"","sources":["../src/promote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,cAAc,EACd,yBAAyB,GAK1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,
|
|
1
|
+
{"version":3,"file":"promote.js","sourceRoot":"","sources":["../src/promote.ts"],"names":[],"mappings":"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"}
|
package/dist/push.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type DeclaredMigration, type AssetEntry, type AssetsNeed, type DeclaredBinding, type DeclaredOperationOutput, type EmittedModel, type EnvVarSpec, type PermissionRegistry, type PermissionsInput, type RuntimeNeeds, type VersionOrigin, type DeployManifest } from '@substrat-run/contracts';
|
|
2
|
+
import { type ScheduleRef } from './schedule-sweeper.js';
|
|
2
3
|
/**
|
|
3
4
|
* Derive the vertical's permission registry (D-39/D-41) from its declared TypeScript surface —
|
|
4
5
|
* the single typed source. `package.json` `substrat.permissions` points at the module that
|
|
@@ -144,6 +145,9 @@ export interface PermissionSurface {
|
|
|
144
145
|
/** The code-side `envSpec` declaration, when the entry exports one (#1206) — already
|
|
145
146
|
* checked against any leftover package.json copy, so a drift threw before this existed. */
|
|
146
147
|
readonly envSpec?: readonly EnvVarSpec[];
|
|
148
|
+
/** Every module's declared schedules (#1232), for `--check`'s sweeper check (#1646). Not
|
|
149
|
+
* part of the `--json` artifact, which names its fields one by one. */
|
|
150
|
+
readonly schedules?: DeployManifest['schedules'];
|
|
147
151
|
}
|
|
148
152
|
/**
|
|
149
153
|
* The push's permission preflight, on its own (#1205).
|
|
@@ -299,6 +303,42 @@ export interface LayerRulesChecked {
|
|
|
299
303
|
* default and reads exactly as before.
|
|
300
304
|
*/
|
|
301
305
|
export declare function assertLayerRules(dir: string, skipLint?: boolean, log?: (message: string) => void): LayerRulesChecked;
|
|
306
|
+
/**
|
|
307
|
+
* Declared schedules, and something to run them (#1646).
|
|
308
|
+
*
|
|
309
|
+
* A vertical's modules — its own, and every engine it composes — may declare recurring work
|
|
310
|
+
* (`manifest.schedules`). On a hosted deploy nothing runs it unless the worker brings its own
|
|
311
|
+
* timer: an exported `defineScopeSweeperDO` class, bound as a Durable Object. The control
|
|
312
|
+
* plane's cron reaches only its own module-less host, and a dispatch script's `triggers.crons`
|
|
313
|
+
* is not honoured, so a vertical without one deploys cleanly, provisions cleanly, and its
|
|
314
|
+
* schedules simply never fire — no error anywhere, because nothing ever tries. This repo's
|
|
315
|
+
* `lint:schedule-sweeper` refuses that for the verticals in it; an external vertical's only
|
|
316
|
+
* check is this one, on the push.
|
|
317
|
+
*
|
|
318
|
+
* The signals, and why each is the one used:
|
|
319
|
+
*
|
|
320
|
+
* - SCHEDULES come from the same `definePermissions(...)` import the manifest's `schedules`
|
|
321
|
+
* field is built from (`deriveDeclaredSurface`), so a schedule an ENGINE declares counts
|
|
322
|
+
* even though the vertical's source never names it — a text search would miss exactly
|
|
323
|
+
* that case.
|
|
324
|
+
* - BINDINGS come from the deploy config the push is about to build with (`cfg`), in either
|
|
325
|
+
* vocabulary — `runtimeNeeds.stores` or a hand-authored `wrangler.jsonc` — so the check
|
|
326
|
+
* reads the bindings that will actually be uploaded.
|
|
327
|
+
* - The EXPORT is read from the worker entry's source (`cfg.main`), following relative
|
|
328
|
+
* re-exports. The built bundle was considered and rejected: its exports are a list of
|
|
329
|
+
* names with no mark saying which class is a sweeper, and it cannot be imported to ask
|
|
330
|
+
* (it imports `cloudflare:workers`) — the source's `defineScopeSweeperDO(...)` call is
|
|
331
|
+
* the only place that fact is legible. Reading source also lets `push --check` make the
|
|
332
|
+
* same check with no build at all.
|
|
333
|
+
*
|
|
334
|
+
* What it cannot see is whether the sweeper's roster is ever filled (`noteScope` from
|
|
335
|
+
* `onProvision`): that is runtime wiring, and the refusal's remedy names it instead.
|
|
336
|
+
*
|
|
337
|
+
* `--allow-unswept-schedules` pushes anyway and says so — for a sweeper wired in a shape this
|
|
338
|
+
* reader does not follow (built by a helper of your own, say), where refusing would be refusing
|
|
339
|
+
* a working deploy.
|
|
340
|
+
*/
|
|
341
|
+
export declare function assertSchedulesAreSwept(dir: string, cfg: Record<string, unknown>, schedules: readonly ScheduleRef[] | undefined, allow?: boolean, log?: (message: string) => void): void;
|
|
302
342
|
export interface PushOptions {
|
|
303
343
|
dir: string;
|
|
304
344
|
slug: string;
|
|
@@ -361,6 +401,12 @@ export interface PushOptions {
|
|
|
361
401
|
* that only shows up as a 404 on the live hostname, so it is refused by default.
|
|
362
402
|
*/
|
|
363
403
|
allowUnservedUi?: boolean;
|
|
404
|
+
/**
|
|
405
|
+
* Push schedules nothing visibly runs (#1646) — the CLI's --allow-unswept-schedules. See
|
|
406
|
+
* `assertSchedulesAreSwept`: declared schedules with no exported, bound sweeper are refused
|
|
407
|
+
* by default, because they deploy cleanly and never fire.
|
|
408
|
+
*/
|
|
409
|
+
allowUnsweptSchedules?: boolean;
|
|
364
410
|
/**
|
|
365
411
|
* Push code the layer rules never saw (#955) — the CLI's --skip-lint. See
|
|
366
412
|
* `assertLayerRules`: the gate refuses on a violation, and this is the deliberate,
|
|
@@ -396,6 +442,13 @@ export declare function resolveWranglerConfig(dir: string): {
|
|
|
396
442
|
cfg: Record<string, unknown>;
|
|
397
443
|
needs: RuntimeNeeds | undefined;
|
|
398
444
|
};
|
|
445
|
+
/**
|
|
446
|
+
* The deploy config a push WOULD build with, without `resolveWranglerConfig`'s notes and
|
|
447
|
+
* refusals — for `push --check` (#1646), which asks what the bindings are and has no business
|
|
448
|
+
* refusing a tree for lacking a deploy config it is not about to build. Same precedence:
|
|
449
|
+
* `substrat.runtimeNeeds` wins over a hand-authored wrangler.jsonc.
|
|
450
|
+
*/
|
|
451
|
+
export declare function deployConfigIfAny(dir: string): Record<string, unknown> | undefined;
|
|
399
452
|
/**
|
|
400
453
|
* A vertical's OWN stores, as the push declares them to the control plane: its DO
|
|
401
454
|
* bindings and D1 databases (e.g. a Better-Auth AUTH_DB), and the DO classes they bind.
|
|
@@ -431,6 +484,26 @@ export declare function push(opts: PushOptions): Promise<{
|
|
|
431
484
|
verticalSlug: string;
|
|
432
485
|
warnings?: string[];
|
|
433
486
|
}>;
|
|
487
|
+
/**
|
|
488
|
+
* POST the assembled bundle to `/verticals/<slug>/deploy` and read the control plane's answer.
|
|
489
|
+
*
|
|
490
|
+
* The multipart upload is the one request the typed client cannot express — its body is a
|
|
491
|
+
* form whose boundary `fetch` writes, so no content type may be set — and it needs the raw
|
|
492
|
+
* exchange: the CLI reads the answer's status, headers and text itself. It still goes through
|
|
493
|
+
* the client, for the addressing and the credential map, with the stale-CLI nudge off the
|
|
494
|
+
* answer (success or refusal) as it always was.
|
|
495
|
+
*/
|
|
496
|
+
export declare function uploadVersion(opts: {
|
|
497
|
+
controlPlaneUrl: string;
|
|
498
|
+
authHeader: Record<string, string>;
|
|
499
|
+
slug: string;
|
|
500
|
+
}, form: FormData, what: string): Promise<{
|
|
501
|
+
id: string;
|
|
502
|
+
admission: string;
|
|
503
|
+
deploymentRef: string;
|
|
504
|
+
verticalSlug: string;
|
|
505
|
+
warnings?: string[];
|
|
506
|
+
}>;
|
|
434
507
|
/** Push defaults read from a vertical's package.json, so `substrat push` needs no flags. */
|
|
435
508
|
export interface VerticalMeta {
|
|
436
509
|
/** Registry slug: an explicit `substrat.slug`, else the package name with scope + a leading `demo-` stripped. */
|
package/dist/push.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"AAOA,OAAO,EAaL,KAAK,iBAAiB,EAItB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"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"}
|
package/dist/push.js
CHANGED
|
@@ -7,9 +7,11 @@ import { webcrypto } from 'node:crypto';
|
|
|
7
7
|
import { build } from 'esbuild';
|
|
8
8
|
import { DECLARED_OUTPUT_FIELDS_MAX, ASSET_PART_PREFIX, assetHash, assetsNeed, buildPermissionRegistry, DECLARED_MIGRATIONS_MAX, DECLARED_MIGRATIONS_SQL_BYTES_MAX, DEPLOY_MANIFEST_BYTES_SAFE, deployManifest, emittedModel, sqlBytes, utf8Length, envVarSpec, runtimeNeeds, RUNTIME_BASELINE, } from '@substrat-run/contracts';
|
|
9
9
|
import { lint, loadConfig as loadBoundaryLintConfig, resolvePackages, declaredEngines, formatViolations, maskSource, } from '@substrat-run/boundary-lint';
|
|
10
|
-
import {
|
|
11
|
-
import { parseJsonBody
|
|
10
|
+
import { walkPages } from '@substrat-run/control-plane-client';
|
|
11
|
+
import { parseJsonBody } from './http.js';
|
|
12
|
+
import { planeFor } from './plane.js';
|
|
12
13
|
import { failureMessage } from './problem.js';
|
|
14
|
+
import { exportedSweeperNamesOf, sweeperOffence } from './schedule-sweeper.js';
|
|
13
15
|
/**
|
|
14
16
|
* Where this push runs: the generated deploy workflow runs THIS SAME CLI inside GitHub
|
|
15
17
|
* Actions, so the runner's env is what tells a git-driven release apart from a person's
|
|
@@ -386,7 +388,7 @@ export async function permissionDigest(registry) {
|
|
|
386
388
|
* turns each into a non-zero exit, which is what makes this usable as a gate.
|
|
387
389
|
*/
|
|
388
390
|
export async function checkPermissionSurface(dir) {
|
|
389
|
-
const { registry, envSpec: derived } = await deriveDeclaredSurface(dir);
|
|
391
|
+
const { registry, envSpec: derived, schedules } = await deriveDeclaredSurface(dir);
|
|
390
392
|
// The envSpec drift check (#1206) runs here too, so `--check` in CI refuses exactly what a
|
|
391
393
|
// push would. The duplicate-copy note is push-time chatter, not part of the check artifact.
|
|
392
394
|
const envSpec = resolveDeclaredEnvSpec(derived, readVerticalMeta(dir).envSpec, () => { });
|
|
@@ -394,6 +396,7 @@ export async function checkPermissionSurface(dir) {
|
|
|
394
396
|
registry,
|
|
395
397
|
digest: await permissionDigest(registry),
|
|
396
398
|
...(derived ? { envSpec: envSpec } : {}),
|
|
399
|
+
...(schedules ? { schedules } : {}),
|
|
397
400
|
};
|
|
398
401
|
}
|
|
399
402
|
/**
|
|
@@ -835,6 +838,87 @@ export function assertLayerRules(dir, skipLint = false, log = console.log) {
|
|
|
835
838
|
' --skip-lint to deploy ungated code deliberately.',
|
|
836
839
|
].join('\n'));
|
|
837
840
|
}
|
|
841
|
+
/**
|
|
842
|
+
* Declared schedules, and something to run them (#1646).
|
|
843
|
+
*
|
|
844
|
+
* A vertical's modules — its own, and every engine it composes — may declare recurring work
|
|
845
|
+
* (`manifest.schedules`). On a hosted deploy nothing runs it unless the worker brings its own
|
|
846
|
+
* timer: an exported `defineScopeSweeperDO` class, bound as a Durable Object. The control
|
|
847
|
+
* plane's cron reaches only its own module-less host, and a dispatch script's `triggers.crons`
|
|
848
|
+
* is not honoured, so a vertical without one deploys cleanly, provisions cleanly, and its
|
|
849
|
+
* schedules simply never fire — no error anywhere, because nothing ever tries. This repo's
|
|
850
|
+
* `lint:schedule-sweeper` refuses that for the verticals in it; an external vertical's only
|
|
851
|
+
* check is this one, on the push.
|
|
852
|
+
*
|
|
853
|
+
* The signals, and why each is the one used:
|
|
854
|
+
*
|
|
855
|
+
* - SCHEDULES come from the same `definePermissions(...)` import the manifest's `schedules`
|
|
856
|
+
* field is built from (`deriveDeclaredSurface`), so a schedule an ENGINE declares counts
|
|
857
|
+
* even though the vertical's source never names it — a text search would miss exactly
|
|
858
|
+
* that case.
|
|
859
|
+
* - BINDINGS come from the deploy config the push is about to build with (`cfg`), in either
|
|
860
|
+
* vocabulary — `runtimeNeeds.stores` or a hand-authored `wrangler.jsonc` — so the check
|
|
861
|
+
* reads the bindings that will actually be uploaded.
|
|
862
|
+
* - The EXPORT is read from the worker entry's source (`cfg.main`), following relative
|
|
863
|
+
* re-exports. The built bundle was considered and rejected: its exports are a list of
|
|
864
|
+
* names with no mark saying which class is a sweeper, and it cannot be imported to ask
|
|
865
|
+
* (it imports `cloudflare:workers`) — the source's `defineScopeSweeperDO(...)` call is
|
|
866
|
+
* the only place that fact is legible. Reading source also lets `push --check` make the
|
|
867
|
+
* same check with no build at all.
|
|
868
|
+
*
|
|
869
|
+
* What it cannot see is whether the sweeper's roster is ever filled (`noteScope` from
|
|
870
|
+
* `onProvision`): that is runtime wiring, and the refusal's remedy names it instead.
|
|
871
|
+
*
|
|
872
|
+
* `--allow-unswept-schedules` pushes anyway and says so — for a sweeper wired in a shape this
|
|
873
|
+
* reader does not follow (built by a helper of your own, say), where refusing would be refusing
|
|
874
|
+
* a working deploy.
|
|
875
|
+
*/
|
|
876
|
+
export function assertSchedulesAreSwept(dir, cfg, schedules, allow = false, log = console.log) {
|
|
877
|
+
if (!schedules || schedules.length === 0)
|
|
878
|
+
return;
|
|
879
|
+
const main = typeof cfg.main === 'string' ? cfg.main : undefined;
|
|
880
|
+
const entryPath = main ? resolve(dir, main) : undefined;
|
|
881
|
+
if (!entryPath || !existsSync(entryPath)) {
|
|
882
|
+
// The build would fail on this anyway; say what this check could not do rather than
|
|
883
|
+
// refusing a sweeper it never got to look for.
|
|
884
|
+
log(`note: the worker entry ${main ? `"${main}" ` : ''}was not found — whether a sweeper runs this ` +
|
|
885
|
+
"vertical's declared schedules was NOT checked");
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const exportedNames = exportedSweeperNamesOf(entryPath);
|
|
889
|
+
const boundClassNames = declaredStoresOf(cfg)
|
|
890
|
+
.bindings.filter((b) => b.type === 'durable_object_namespace')
|
|
891
|
+
.map((b) => b.class_name)
|
|
892
|
+
.filter((c) => Boolean(c));
|
|
893
|
+
const why = sweeperOffence(schedules, { exportedNames, boundClassNames });
|
|
894
|
+
if (!why)
|
|
895
|
+
return;
|
|
896
|
+
if (allow) {
|
|
897
|
+
log(`warning: --allow-unswept-schedules — this vertical ${why}
|
|
898
|
+
` +
|
|
899
|
+
' Pushing anyway, as asked: on a hosted deploy these schedules will not fire unless a sweeper you wired some other way runs them.');
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
throw new Error([
|
|
903
|
+
`this vertical ${why}`,
|
|
904
|
+
'',
|
|
905
|
+
' The wiring, in the worker entry (the `npm create substrat` template does exactly this):',
|
|
906
|
+
'',
|
|
907
|
+
" import { defineScopeSweeperDO, SCOPE_SWEEPER_NAME } from '@substrat-run/adapter-cloudflare';",
|
|
908
|
+
' export const SweeperDO = defineScopeSweeperDO<Env>({ host: hostFor });',
|
|
909
|
+
' // …and in mountPlatformSurface, so provisioned scopes join its roster:',
|
|
910
|
+
' onProvision: async (env, b) => {',
|
|
911
|
+
' await env.SWEEPER.get(env.SWEEPER.idFromName(SCOPE_SWEEPER_NAME)).noteScope(b.tenantId, b.scopeId);',
|
|
912
|
+
' },',
|
|
913
|
+
'',
|
|
914
|
+
' plus its binding — in package.json:',
|
|
915
|
+
'',
|
|
916
|
+
' "runtimeNeeds": { "stores": [ …, { "binding": "SWEEPER", "class": "SweeperDO" } ] }',
|
|
917
|
+
'',
|
|
918
|
+
" (or a durable_objects binding and migration in wrangler.jsonc). If a sweeper you wired",
|
|
919
|
+
' another way already runs these, push with --allow-unswept-schedules to say so.',
|
|
920
|
+
].join('\n'));
|
|
921
|
+
}
|
|
838
922
|
/**
|
|
839
923
|
* Build a vertical and push its bundle to the platform's deploy endpoint
|
|
840
924
|
* (self-serve-deploy.md). The worker is built with `wrangler --dry-run --outdir` — the
|
|
@@ -907,6 +991,19 @@ export function resolveWranglerConfig(dir) {
|
|
|
907
991
|
? { cfg: wranglerConfigFor(needs), needs }
|
|
908
992
|
: { cfg: readJsonc(join(dir, 'wrangler.jsonc')), needs: undefined };
|
|
909
993
|
}
|
|
994
|
+
/**
|
|
995
|
+
* The deploy config a push WOULD build with, without `resolveWranglerConfig`'s notes and
|
|
996
|
+
* refusals — for `push --check` (#1646), which asks what the bindings are and has no business
|
|
997
|
+
* refusing a tree for lacking a deploy config it is not about to build. Same precedence:
|
|
998
|
+
* `substrat.runtimeNeeds` wins over a hand-authored wrangler.jsonc.
|
|
999
|
+
*/
|
|
1000
|
+
export function deployConfigIfAny(dir) {
|
|
1001
|
+
const needs = readRuntimeNeeds(dir);
|
|
1002
|
+
if (needs)
|
|
1003
|
+
return wranglerConfigFor(needs);
|
|
1004
|
+
const path = join(dir, 'wrangler.jsonc');
|
|
1005
|
+
return existsSync(path) ? readJsonc(path) : undefined;
|
|
1006
|
+
}
|
|
910
1007
|
/**
|
|
911
1008
|
* A vertical's OWN stores, as the push declares them to the control plane: its DO
|
|
912
1009
|
* bindings and D1 databases (e.g. a Better-Auth AUTH_DB), and the DO classes they bind.
|
|
@@ -1012,6 +1109,12 @@ export async function push(opts) {
|
|
|
1012
1109
|
// The same import reads the entry's `envSpec` export (#1206); when it exists it is the copy
|
|
1013
1110
|
// that ships, and a drifted package.json duplicate refuses the push.
|
|
1014
1111
|
const { registry, envSpec: derivedEnvSpec, schedules, freshness, declaredEvents, declaredEventsTruncated, migrations } = await deriveDeclaredSurface(opts.dir);
|
|
1112
|
+
// Declared schedules need a sweeper to run them on a hosted deploy (#1646). After the build
|
|
1113
|
+
// rather than before it, because this is where the push has always read the permission
|
|
1114
|
+
// entry, and a vertical's entry may import something its own build produces. Still before
|
|
1115
|
+
// anything is uploaded; a vertical that wants the refusal in a second runs
|
|
1116
|
+
// `substrat push --check`, which makes the same check with no build at all.
|
|
1117
|
+
assertSchedulesAreSwept(opts.dir, cfg, schedules, opts.allowUnsweptSchedules);
|
|
1015
1118
|
if (migrations.omitted) {
|
|
1016
1119
|
console.warn(`⚠ the SQL migrations are not carried in this version's manifest (${migrations.omitted}) — ` +
|
|
1017
1120
|
'a promote of it will say the SQL is not available.');
|
|
@@ -1154,14 +1257,23 @@ export async function push(opts) {
|
|
|
1154
1257
|
const part = `${ASSET_PART_PREFIX}${a.entry.path}`;
|
|
1155
1258
|
form.set(part, new Blob([a.content], { type: a.entry.contentType }), part);
|
|
1156
1259
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1260
|
+
return uploadVersion(opts, form, `${entry} (+${modules.length - 1} modules${assets.length ? `, ${assets.length} assets` : ''})`);
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* POST the assembled bundle to `/verticals/<slug>/deploy` and read the control plane's answer.
|
|
1264
|
+
*
|
|
1265
|
+
* The multipart upload is the one request the typed client cannot express — its body is a
|
|
1266
|
+
* form whose boundary `fetch` writes, so no content type may be set — and it needs the raw
|
|
1267
|
+
* exchange: the CLI reads the answer's status, headers and text itself. It still goes through
|
|
1268
|
+
* the client, for the addressing and the credential map, with the stale-CLI nudge off the
|
|
1269
|
+
* answer (success or refusal) as it always was.
|
|
1270
|
+
*/
|
|
1271
|
+
export async function uploadVersion(opts, form, what) {
|
|
1272
|
+
const plane = planeFor(opts.controlPlaneUrl, opts.authHeader, { advisory: true });
|
|
1273
|
+
const path = `/verticals/${encodeURIComponent(opts.slug)}/deploy`;
|
|
1274
|
+
const url = plane.urlFor(path);
|
|
1275
|
+
console.log(`uploading ${what} → ${url}`);
|
|
1276
|
+
const res = await plane.request(path, { method: 'POST', body: form });
|
|
1165
1277
|
const body = await res.text();
|
|
1166
1278
|
if (!res.ok) {
|
|
1167
1279
|
// Surface what the control plane said — its problem document's `detail`, `code` and
|
|
@@ -1354,16 +1466,11 @@ function isNewer(a, b) {
|
|
|
1354
1466
|
* (they are the same lineage; at most one exists in practice).
|
|
1355
1467
|
*/
|
|
1356
1468
|
export async function nextVersion(controlPlaneUrl, header, slugs, seed) {
|
|
1357
|
-
const
|
|
1469
|
+
const client = planeFor(controlPlaneUrl, header);
|
|
1358
1470
|
let best = null;
|
|
1359
1471
|
for (const slug of slugs) {
|
|
1360
1472
|
// The max semver must see EVERY version, so walk the paged list to the end.
|
|
1361
|
-
const versions = await
|
|
1362
|
-
const r = await fetch(pageUrl, { headers: header });
|
|
1363
|
-
if (!r.ok)
|
|
1364
|
-
throw new Error(String(r.status));
|
|
1365
|
-
return r.json();
|
|
1366
|
-
}).catch(() => []);
|
|
1473
|
+
const versions = await walkPages((page) => client.listVersions(slug, page)).catch(() => []);
|
|
1367
1474
|
for (const v of versions) {
|
|
1368
1475
|
const t = parseSemver(v.version);
|
|
1369
1476
|
if (t && (!best || isNewer(t, best)))
|
|
@@ -1383,15 +1490,10 @@ export async function nextVersion(controlPlaneUrl, header, slugs, seed) {
|
|
|
1383
1490
|
* ask (e)). `<n>` disambiguates successive pushes to the same tag on the same base.
|
|
1384
1491
|
*/
|
|
1385
1492
|
export async function previewVersion(controlPlaneUrl, header, slugs, seed, tag) {
|
|
1386
|
-
const
|
|
1493
|
+
const client = planeFor(controlPlaneUrl, header);
|
|
1387
1494
|
const all = [];
|
|
1388
1495
|
for (const slug of slugs) {
|
|
1389
|
-
const versions = await
|
|
1390
|
-
const r = await fetch(pageUrl, { headers: header });
|
|
1391
|
-
if (!r.ok)
|
|
1392
|
-
throw new Error(String(r.status));
|
|
1393
|
-
return r.json();
|
|
1394
|
-
}).catch(() => []);
|
|
1496
|
+
const versions = await walkPages((page) => client.listVersions(slug, page)).catch(() => []);
|
|
1395
1497
|
for (const v of versions)
|
|
1396
1498
|
all.push(v.version);
|
|
1397
1499
|
}
|