run402 2.17.0 → 2.19.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/lib/deploy-v2.mjs +265 -3
- package/lib/deploy.mjs +1 -0
- package/package.json +1 -1
- package/sdk/dist/namespaces/deploy.d.ts +30 -1
- package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
- package/sdk/dist/namespaces/deploy.js +70 -0
- package/sdk/dist/namespaces/deploy.js.map +1 -1
- package/sdk/dist/namespaces/deploy.types.d.ts +61 -0
- package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/deploy.types.js.map +1 -1
- package/sdk/dist/node/deploy-manifest.d.ts.map +1 -1
- package/sdk/dist/node/deploy-manifest.js +15 -2
- package/sdk/dist/node/deploy-manifest.js.map +1 -1
- package/sdk/dist/scoped.d.ts +7 -1
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +1 -0
- package/sdk/dist/scoped.js.map +1 -1
package/lib/deploy-v2.mjs
CHANGED
|
@@ -40,6 +40,7 @@ const APPLY_HELP = `run402 deploy apply — Unified deploy primitive (v1.34+)
|
|
|
40
40
|
Usage:
|
|
41
41
|
run402 deploy apply --manifest <path> [--project <id>] [--quiet|--final-only] [--allow-warning <code>] [--allow-warnings]
|
|
42
42
|
run402 deploy apply --spec '<json>' [--project <id>] [--quiet|--final-only] [--allow-warning <code>] [--allow-warnings]
|
|
43
|
+
run402 deploy apply --dir <build-output> [--manifest <path>] [--project <id>]
|
|
43
44
|
cat spec.json | run402 deploy apply [--project <id>]
|
|
44
45
|
|
|
45
46
|
Manifest format mirrors the MCP \`deploy\` tool's ReleaseSpec:
|
|
@@ -79,6 +80,12 @@ Complete static site + function + route manifest:
|
|
|
79
80
|
Options:
|
|
80
81
|
--manifest <path> Read the spec from this JSON file
|
|
81
82
|
--spec '<json>' Inline JSON spec (single-quote in shell)
|
|
83
|
+
--dir <path> Read \`dist/run402/adapter.json\` from this directory and
|
|
84
|
+
merge the Astro release slice (site + functions + routes)
|
|
85
|
+
into the spec. Combine with --manifest to declare
|
|
86
|
+
database/secrets/subdomains/i18n in the manifest while
|
|
87
|
+
the slice carries the build output. Requires
|
|
88
|
+
@run402/astro installed in the project.
|
|
82
89
|
--project <id> Override project_id from the manifest
|
|
83
90
|
--quiet Suppress per-event JSON-line stderr (final result still on stdout)
|
|
84
91
|
--final-only Alias for --quiet; final success/error envelope is still preserved
|
|
@@ -260,6 +267,7 @@ exit 0; inspect would_serve and diagnostic_status in the result payload.
|
|
|
260
267
|
|
|
261
268
|
export async function runDeployV2(sub, args) {
|
|
262
269
|
if (sub === "apply") return await applyCmd(args);
|
|
270
|
+
if (sub === "promote") return await promoteCmd(args);
|
|
263
271
|
if (sub === "resume") return await resumeCmd(args);
|
|
264
272
|
if (sub === "list") return await listCmd(args);
|
|
265
273
|
if (sub === "events") return await eventsCmd(args);
|
|
@@ -273,6 +281,189 @@ export async function runDeployV2(sub, args) {
|
|
|
273
281
|
});
|
|
274
282
|
}
|
|
275
283
|
|
|
284
|
+
const PROMOTE_HELP = `run402 deploy promote — Operator pointer-swap recovery (v1.58+)
|
|
285
|
+
|
|
286
|
+
Usage:
|
|
287
|
+
run402 deploy promote <release-id> [--project <id>] [--allow-warning <code>] [--allow-warnings] [--quiet]
|
|
288
|
+
|
|
289
|
+
Re-points the project's live release at an existing release row without
|
|
290
|
+
re-running the apply pipeline. Designed for "oops on a real project ID"
|
|
291
|
+
recovery — when an apply shipped content the operator regrets, promote
|
|
292
|
+
back to the prior release in seconds instead of re-deploying.
|
|
293
|
+
|
|
294
|
+
Promotable statuses: ready, active, superseded. Releases with status
|
|
295
|
+
'failed' or 'staging' are rejected (they never fully landed).
|
|
296
|
+
|
|
297
|
+
Surfaces structured warnings:
|
|
298
|
+
|
|
299
|
+
MIGRATIONS_NOT_REVERSIBLE (requires_confirmation: true)
|
|
300
|
+
The target release predates migrations applied since. Those
|
|
301
|
+
migrations remain applied — the post-promote release runs against
|
|
302
|
+
the current schema. Ack with --allow-warning MIGRATIONS_NOT_REVERSIBLE.
|
|
303
|
+
|
|
304
|
+
FUNCTION_VERSION_MISMATCH (informational, no ack needed)
|
|
305
|
+
Overlapping function names have different code_hashes. The Lambda
|
|
306
|
+
code is whatever's currently $LATEST.
|
|
307
|
+
|
|
308
|
+
Worked example: recover from a destructive apply
|
|
309
|
+
|
|
310
|
+
# rel_old (good) → rel_new (bad, destructive) → promote back
|
|
311
|
+
run402 deploy promote rel_old_abc123 --project prj_xyz \\
|
|
312
|
+
--allow-warning MIGRATIONS_NOT_REVERSIBLE
|
|
313
|
+
|
|
314
|
+
Options:
|
|
315
|
+
<release-id> Required positional. The release to promote to.
|
|
316
|
+
Format: rel_*
|
|
317
|
+
--project <id> Project id. Falls back to active project, then
|
|
318
|
+
RUN402_PROJECT_ID env var.
|
|
319
|
+
--allow-warning <code> Acknowledge a specific blocking warning
|
|
320
|
+
(repeatable).
|
|
321
|
+
--allow-warnings Acknowledge ALL blocking promote warnings.
|
|
322
|
+
Use this for full recovery mode when you've
|
|
323
|
+
already inspected the diff.
|
|
324
|
+
--quiet | --final-only Suppress per-event stderr; only print the
|
|
325
|
+
final JSON envelope on stdout.
|
|
326
|
+
|
|
327
|
+
Output:
|
|
328
|
+
stdout: {
|
|
329
|
+
"status": "ok",
|
|
330
|
+
"release_id": "rel_old_abc123",
|
|
331
|
+
"operation_id": "op_...",
|
|
332
|
+
"previous_release_id": "rel_new_xxx",
|
|
333
|
+
"diff": { "functions": {...}, "migrations": {...}, "site_paths": {...} },
|
|
334
|
+
"warnings": [...]
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
Errors map to structured envelopes with codes:
|
|
338
|
+
PROMOTE_TARGET_NOT_FOUND 404 — release id doesn't exist
|
|
339
|
+
PROMOTE_PROJECT_MISMATCH 400 — release belongs to another project
|
|
340
|
+
PROMOTE_RELEASE_NOT_READY 409 — release status not promotable
|
|
341
|
+
PROMOTE_NO_OP 409 — target = current live (use
|
|
342
|
+
cache.invalidateAll instead)
|
|
343
|
+
PROMOTE_WARNING_REQUIRES_ACK 409 — at least one blocking warning
|
|
344
|
+
unacked; details list codes`;
|
|
345
|
+
|
|
346
|
+
function parsePromoteArgs(args) {
|
|
347
|
+
const opts = {
|
|
348
|
+
releaseId: null,
|
|
349
|
+
project: null,
|
|
350
|
+
allowWarnings: false,
|
|
351
|
+
allowWarningCodes: [],
|
|
352
|
+
quiet: false,
|
|
353
|
+
};
|
|
354
|
+
const allowedFlags = [
|
|
355
|
+
"--project",
|
|
356
|
+
"--allow-warning",
|
|
357
|
+
"--allow-warnings",
|
|
358
|
+
"--quiet",
|
|
359
|
+
"--final-only",
|
|
360
|
+
"--help",
|
|
361
|
+
"-h",
|
|
362
|
+
];
|
|
363
|
+
|
|
364
|
+
for (let i = 0; i < args.length; i++) {
|
|
365
|
+
const arg = args[i];
|
|
366
|
+
if (arg === "--help" || arg === "-h") {
|
|
367
|
+
console.log(PROMOTE_HELP);
|
|
368
|
+
process.exit(0);
|
|
369
|
+
}
|
|
370
|
+
if (arg === "--project" || arg === "--allow-warning") {
|
|
371
|
+
const value = args[i + 1];
|
|
372
|
+
if (value === undefined || (typeof value === "string" && value.startsWith("--"))) {
|
|
373
|
+
fail({
|
|
374
|
+
code: "BAD_USAGE",
|
|
375
|
+
message: `${arg} requires a value`,
|
|
376
|
+
details: { flag: arg },
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
if (arg === "--project") {
|
|
380
|
+
opts.project = value;
|
|
381
|
+
} else {
|
|
382
|
+
opts.allowWarningCodes.push(value);
|
|
383
|
+
}
|
|
384
|
+
i += 1;
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
if (arg === "--quiet" || arg === "--final-only") {
|
|
388
|
+
opts.quiet = true;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (arg === "--allow-warnings") {
|
|
392
|
+
opts.allowWarnings = true;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (typeof arg === "string" && arg.startsWith("-")) {
|
|
396
|
+
fail({
|
|
397
|
+
code: "BAD_USAGE",
|
|
398
|
+
message: `Unknown flag for deploy promote: ${arg}`,
|
|
399
|
+
details: { flag: arg, allowed_flags: allowedFlags },
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
// Positional: the release id
|
|
403
|
+
if (opts.releaseId !== null) {
|
|
404
|
+
fail({
|
|
405
|
+
code: "BAD_USAGE",
|
|
406
|
+
message: `Unexpected positional argument for deploy promote: ${arg}`,
|
|
407
|
+
details: { argument: arg, already_have: opts.releaseId },
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
opts.releaseId = arg;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (opts.releaseId === null) {
|
|
414
|
+
fail({
|
|
415
|
+
code: "BAD_USAGE",
|
|
416
|
+
message: "deploy promote requires a release id (positional argument)",
|
|
417
|
+
details: { example: "run402 deploy promote rel_abc123" },
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
if (typeof opts.releaseId !== "string" || !opts.releaseId.startsWith("rel_")) {
|
|
421
|
+
fail({
|
|
422
|
+
code: "BAD_USAGE",
|
|
423
|
+
message: `Invalid release id: '${opts.releaseId}' (expected rel_*)`,
|
|
424
|
+
details: { release_id: opts.releaseId },
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return opts;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async function promoteCmd(args) {
|
|
432
|
+
const opts = parsePromoteArgs(args);
|
|
433
|
+
const projectId = opts.project ?? resolveProjectId(null);
|
|
434
|
+
|
|
435
|
+
// Preserve the aggressive early-exit when no allowance is configured
|
|
436
|
+
// — same as apply.
|
|
437
|
+
allowanceAuthHeaders("/apply/v1/releases");
|
|
438
|
+
|
|
439
|
+
try {
|
|
440
|
+
// Call the engine directly (matches the pattern used by apply / resume
|
|
441
|
+
// in this file). The `r.project(id).apply.promote` hero exists for
|
|
442
|
+
// direct-SDK consumers; the CLI's `getSdk()` returns the unwrapped
|
|
443
|
+
// Run402 instance whose `project()` method is async, so going through
|
|
444
|
+
// the hero here would require an extra `await`.
|
|
445
|
+
const result = await getSdk()._applyEngine.promote(projectId, opts.releaseId, {
|
|
446
|
+
allowWarnings: opts.allowWarnings,
|
|
447
|
+
allowWarningCodes: opts.allowWarningCodes,
|
|
448
|
+
});
|
|
449
|
+
if (!opts.quiet) {
|
|
450
|
+
// Emit a single structured stderr event so observers can pick it up
|
|
451
|
+
// alongside the regular deploy event stream. Promote is a one-shot
|
|
452
|
+
// operation; there are no intermediate phase events.
|
|
453
|
+
console.error(JSON.stringify({
|
|
454
|
+
type: "promote.committed",
|
|
455
|
+
release_id: result.release_id,
|
|
456
|
+
previous_release_id: result.previous_release_id,
|
|
457
|
+
operation_id: result.operation_id,
|
|
458
|
+
warnings: result.warnings,
|
|
459
|
+
}));
|
|
460
|
+
}
|
|
461
|
+
console.log(JSON.stringify(result, null, 2));
|
|
462
|
+
} catch (err) {
|
|
463
|
+
reportSdkError(err);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
276
467
|
async function readStdin() {
|
|
277
468
|
const chunks = [];
|
|
278
469
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
@@ -296,8 +487,8 @@ function makeStderrEventWriter(quiet) {
|
|
|
296
487
|
}
|
|
297
488
|
|
|
298
489
|
function parseApplyArgs(args) {
|
|
299
|
-
const opts = { manifest: null, spec: null, project: null, quiet: false, allowWarnings: false, allowWarningCodes: [] };
|
|
300
|
-
const allowedFlags = ["--manifest", "--spec", "--project", "--quiet", "--final-only", "--allow-warning", "--allow-warnings", "--help", "-h"];
|
|
490
|
+
const opts = { manifest: null, spec: null, dir: null, project: null, quiet: false, allowWarnings: false, allowWarningCodes: [] };
|
|
491
|
+
const allowedFlags = ["--manifest", "--spec", "--dir", "--project", "--quiet", "--final-only", "--allow-warning", "--allow-warnings", "--help", "-h"];
|
|
301
492
|
|
|
302
493
|
for (let i = 0; i < args.length; i++) {
|
|
303
494
|
const arg = args[i];
|
|
@@ -305,7 +496,7 @@ function parseApplyArgs(args) {
|
|
|
305
496
|
console.log(APPLY_HELP);
|
|
306
497
|
process.exit(0);
|
|
307
498
|
}
|
|
308
|
-
if (arg === "--manifest" || arg === "--spec" || arg === "--project" || arg === "--allow-warning") {
|
|
499
|
+
if (arg === "--manifest" || arg === "--spec" || arg === "--dir" || arg === "--project" || arg === "--allow-warning") {
|
|
309
500
|
const value = args[i + 1];
|
|
310
501
|
if (value === undefined || (typeof value === "string" && value.startsWith("--"))) {
|
|
311
502
|
fail({
|
|
@@ -332,6 +523,15 @@ function parseApplyArgs(args) {
|
|
|
332
523
|
});
|
|
333
524
|
}
|
|
334
525
|
opts.spec = value;
|
|
526
|
+
} else if (arg === "--dir") {
|
|
527
|
+
if (opts.dir !== null) {
|
|
528
|
+
fail({
|
|
529
|
+
code: "BAD_USAGE",
|
|
530
|
+
message: "--dir may only be provided once",
|
|
531
|
+
details: { flag: "--dir" },
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
opts.dir = value;
|
|
335
535
|
} else if (arg === "--project") {
|
|
336
536
|
opts.project = value;
|
|
337
537
|
} else {
|
|
@@ -362,9 +562,63 @@ function parseApplyArgs(args) {
|
|
|
362
562
|
function applySourceField(opts) {
|
|
363
563
|
if (opts.manifest !== null) return "manifest";
|
|
364
564
|
if (opts.spec !== null) return "spec";
|
|
565
|
+
if (opts.dir !== null && !hasStdinSource()) return "dir";
|
|
365
566
|
return "stdin";
|
|
366
567
|
}
|
|
367
568
|
|
|
569
|
+
async function mergeAstroReleaseSlice(spec, dirArg) {
|
|
570
|
+
let buildAstroReleaseSlice;
|
|
571
|
+
try {
|
|
572
|
+
({ buildAstroReleaseSlice } = await import("@run402/astro/release-slice"));
|
|
573
|
+
} catch (err) {
|
|
574
|
+
fail({
|
|
575
|
+
code: "BAD_USAGE",
|
|
576
|
+
message:
|
|
577
|
+
"--dir requires @run402/astro to be installed in this project. Add it as a dependency (e.g., `npm install -D @run402/astro`).",
|
|
578
|
+
details: { flag: "--dir", import_error: err?.message ?? String(err) },
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const distDirAbs = isAbsolute(dirArg) ? dirArg : resolve(process.cwd(), dirArg);
|
|
583
|
+
|
|
584
|
+
let slice;
|
|
585
|
+
try {
|
|
586
|
+
slice = await buildAstroReleaseSlice(distDirAbs);
|
|
587
|
+
} catch (err) {
|
|
588
|
+
if (err && typeof err === "object" && typeof err.code === "string" &&
|
|
589
|
+
err.code.startsWith("R402_ASTRO_ADAPTER_MANIFEST_")) {
|
|
590
|
+
fail({
|
|
591
|
+
code: err.code,
|
|
592
|
+
message: err.message,
|
|
593
|
+
hint: err.suggestedFix,
|
|
594
|
+
docs: err.docs,
|
|
595
|
+
details: {
|
|
596
|
+
flag: "--dir",
|
|
597
|
+
dir: distDirAbs,
|
|
598
|
+
file: err.file,
|
|
599
|
+
...(err.observedVersion ? { observed_version: err.observedVersion } : {}),
|
|
600
|
+
},
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
throw err;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Slice owns site/functions/routes. The caller's manifest can declare
|
|
607
|
+
// cross-cutting slices (database, secrets, i18n, subdomains) that the
|
|
608
|
+
// slice doesn't touch. On collision in `functions.replace`, the slice
|
|
609
|
+
// wins for its own function name; the caller's other functions are
|
|
610
|
+
// preserved. `site` and `routes` are whole-resource replacements — slice
|
|
611
|
+
// wins entirely on those.
|
|
612
|
+
spec.site = slice.site;
|
|
613
|
+
spec.routes = slice.routes;
|
|
614
|
+
const sliceFns = slice.functions?.replace ?? {};
|
|
615
|
+
const existingFns =
|
|
616
|
+
spec.functions && typeof spec.functions === "object" && spec.functions.replace
|
|
617
|
+
? spec.functions.replace
|
|
618
|
+
: {};
|
|
619
|
+
spec.functions = { replace: { ...existingFns, ...sliceFns } };
|
|
620
|
+
}
|
|
621
|
+
|
|
368
622
|
function validateApplySources(opts) {
|
|
369
623
|
const sources = [];
|
|
370
624
|
if (opts.manifest !== null) sources.push("--manifest");
|
|
@@ -398,6 +652,10 @@ async function applyCmd(args) {
|
|
|
398
652
|
details: { flag: "--manifest", path: opts.manifest },
|
|
399
653
|
});
|
|
400
654
|
}
|
|
655
|
+
} else if (opts.dir !== null && !hasStdinSource()) {
|
|
656
|
+
// --dir without any other source: start from an empty spec and let the
|
|
657
|
+
// Astro release-slice fill in site/functions/routes below.
|
|
658
|
+
raw = "{}";
|
|
401
659
|
} else {
|
|
402
660
|
raw = await readStdin();
|
|
403
661
|
}
|
|
@@ -417,6 +675,10 @@ async function applyCmd(args) {
|
|
|
417
675
|
...(manifestPath ? { path: manifestPath } : {}),
|
|
418
676
|
});
|
|
419
677
|
|
|
678
|
+
if (opts.dir !== null) {
|
|
679
|
+
await mergeAstroReleaseSlice(spec, opts.dir);
|
|
680
|
+
}
|
|
681
|
+
|
|
420
682
|
// GH-232: Reject empty specs client-side. Without this guard,
|
|
421
683
|
// `run402 deploy apply --spec '{}'` (and `--manifest <empty>`) would silently
|
|
422
684
|
// send an empty ReleaseSpec to /apply/v1/plans with no signal that nothing
|
package/lib/deploy.mjs
CHANGED
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* behavior; this file is the implementation.
|
|
20
20
|
*/
|
|
21
21
|
import type { Client } from "../kernel.js";
|
|
22
|
-
import type { ApplyOptions, ActiveReleaseInventory, DeployEvent, DeployEventsResponse, DeployListOptions, DeployListResponse, DeployOperation, DeployResult, DeployResolveOptions, DeployResolveResponse, OperationSnapshot, PlanResponse, ReleaseDiffOptions, ReleaseInventory, ReleaseInventoryByIdOptions, ReleaseInventoryOptions, ReleaseSpec, ReleaseToReleaseDiff, StartOptions } from "./deploy.types.js";
|
|
22
|
+
import type { ApplyOptions, ActiveReleaseInventory, DeployEvent, DeployEventsResponse, DeployListOptions, DeployListResponse, DeployOperation, DeployResult, DeployResolveOptions, DeployResolveResponse, OperationSnapshot, PlanResponse, PromoteOptions, PromoteResult, ReleaseDiffOptions, ReleaseInventory, ReleaseInventoryByIdOptions, ReleaseInventoryOptions, ReleaseSpec, ReleaseToReleaseDiff, StartOptions } from "./deploy.types.js";
|
|
23
23
|
export declare class Deploy {
|
|
24
24
|
private readonly client;
|
|
25
25
|
constructor(client: Client);
|
|
@@ -82,6 +82,35 @@ export declare class Deploy {
|
|
|
82
82
|
onEvent?: (event: DeployEvent) => void;
|
|
83
83
|
project?: string;
|
|
84
84
|
}): Promise<DeployResult>;
|
|
85
|
+
/**
|
|
86
|
+
* Promote an existing release to be the project's current live release —
|
|
87
|
+
* a pointer swap on `internal.projects.live_release_id` without re-running
|
|
88
|
+
* the apply pipeline. Designed for operator recovery from a destructive
|
|
89
|
+
* apply ("oops on a real project ID"). The prior release's bytes,
|
|
90
|
+
* functions, and migrations remain persisted; this just routes traffic
|
|
91
|
+
* back to them.
|
|
92
|
+
*
|
|
93
|
+
* Surfaces structured warnings via the result envelope:
|
|
94
|
+
*
|
|
95
|
+
* - `MIGRATIONS_NOT_REVERSIBLE` (requires_confirmation: true) when the
|
|
96
|
+
* target release predates migrations applied since. The migrations
|
|
97
|
+
* remain applied; the new live release runs against the current
|
|
98
|
+
* schema. Ack via `opts.allowWarningCodes`.
|
|
99
|
+
*
|
|
100
|
+
* - `FUNCTION_VERSION_MISMATCH` (informational) when overlapping
|
|
101
|
+
* function names have different code_hashes. The Lambda code is
|
|
102
|
+
* whatever's currently $LATEST.
|
|
103
|
+
*
|
|
104
|
+
* Rejected cases:
|
|
105
|
+
* - `PROMOTE_TARGET_NOT_FOUND` — releaseId doesn't exist
|
|
106
|
+
* - `PROMOTE_PROJECT_MISMATCH` — releaseId belongs to a different project
|
|
107
|
+
* - `PROMOTE_RELEASE_NOT_READY` — release status isn't promotable
|
|
108
|
+
* - `PROMOTE_NO_OP` — releaseId IS already the project's current live
|
|
109
|
+
* - `PROMOTE_WARNING_REQUIRES_ACK` — at least one blocking warning unacked
|
|
110
|
+
*
|
|
111
|
+
* Capability: unified-deploy (v1.58+, release-promote).
|
|
112
|
+
*/
|
|
113
|
+
promote(project: string, releaseId: string, opts?: PromoteOptions): Promise<PromoteResult>;
|
|
85
114
|
/**
|
|
86
115
|
* Snapshot a deploy operation. The endpoint requires `apikey` auth, so
|
|
87
116
|
* pass the project that owns the operation. (When omitted, the request
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/namespaces/deploy.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/namespaces/deploy.ts"],"names":[],"mappings":"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"}
|
|
@@ -197,6 +197,73 @@ export class Deploy {
|
|
|
197
197
|
}
|
|
198
198
|
return await pollSnapshotUntilReady(this.client, snapshot, {}, [], emit, opts.project);
|
|
199
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Promote an existing release to be the project's current live release —
|
|
202
|
+
* a pointer swap on `internal.projects.live_release_id` without re-running
|
|
203
|
+
* the apply pipeline. Designed for operator recovery from a destructive
|
|
204
|
+
* apply ("oops on a real project ID"). The prior release's bytes,
|
|
205
|
+
* functions, and migrations remain persisted; this just routes traffic
|
|
206
|
+
* back to them.
|
|
207
|
+
*
|
|
208
|
+
* Surfaces structured warnings via the result envelope:
|
|
209
|
+
*
|
|
210
|
+
* - `MIGRATIONS_NOT_REVERSIBLE` (requires_confirmation: true) when the
|
|
211
|
+
* target release predates migrations applied since. The migrations
|
|
212
|
+
* remain applied; the new live release runs against the current
|
|
213
|
+
* schema. Ack via `opts.allowWarningCodes`.
|
|
214
|
+
*
|
|
215
|
+
* - `FUNCTION_VERSION_MISMATCH` (informational) when overlapping
|
|
216
|
+
* function names have different code_hashes. The Lambda code is
|
|
217
|
+
* whatever's currently $LATEST.
|
|
218
|
+
*
|
|
219
|
+
* Rejected cases:
|
|
220
|
+
* - `PROMOTE_TARGET_NOT_FOUND` — releaseId doesn't exist
|
|
221
|
+
* - `PROMOTE_PROJECT_MISMATCH` — releaseId belongs to a different project
|
|
222
|
+
* - `PROMOTE_RELEASE_NOT_READY` — release status isn't promotable
|
|
223
|
+
* - `PROMOTE_NO_OP` — releaseId IS already the project's current live
|
|
224
|
+
* - `PROMOTE_WARNING_REQUIRES_ACK` — at least one blocking warning unacked
|
|
225
|
+
*
|
|
226
|
+
* Capability: unified-deploy (v1.58+, release-promote).
|
|
227
|
+
*/
|
|
228
|
+
async promote(project, releaseId, opts = {}) {
|
|
229
|
+
if (!project || typeof project !== "string") {
|
|
230
|
+
throw new Run402DeployError(`Invalid project id: "${String(project)}"`, {
|
|
231
|
+
code: "BAD_REQUEST",
|
|
232
|
+
retryable: false,
|
|
233
|
+
context: "promoting release",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (!releaseId || !releaseId.startsWith("rel_")) {
|
|
237
|
+
throw new Run402DeployError(`Invalid release id: "${releaseId}"`, {
|
|
238
|
+
code: "BAD_REQUEST",
|
|
239
|
+
retryable: false,
|
|
240
|
+
context: "promoting release",
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
// Note: `allowWarnings: true` is implemented client-side by enumerating
|
|
244
|
+
// every known blocking warning code, since the gateway expects a precise
|
|
245
|
+
// list per warning code (no wildcard accept). v1.58 has exactly one
|
|
246
|
+
// blocking promote warning (MIGRATIONS_NOT_REVERSIBLE); if more land,
|
|
247
|
+
// expand this list.
|
|
248
|
+
const ALL_BLOCKING_PROMOTE_WARNINGS = ["MIGRATIONS_NOT_REVERSIBLE"];
|
|
249
|
+
const allowCodes = opts.allowWarnings === true
|
|
250
|
+
? ALL_BLOCKING_PROMOTE_WARNINGS
|
|
251
|
+
: (opts.allowWarningCodes ?? []);
|
|
252
|
+
try {
|
|
253
|
+
return await this.client.request(`/apply/v1/releases/${encodeURIComponent(releaseId)}/promote`, {
|
|
254
|
+
method: "POST",
|
|
255
|
+
context: "promoting release",
|
|
256
|
+
headers: { "content-type": "application/json" },
|
|
257
|
+
body: {
|
|
258
|
+
project,
|
|
259
|
+
allow_warning_codes: allowCodes,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
throw translateDeployError(err, "promote", null, releaseId);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
200
267
|
/**
|
|
201
268
|
* Snapshot a deploy operation. The endpoint requires `apikey` auth, so
|
|
202
269
|
* pass the project that owns the operation. (When omitted, the request
|
|
@@ -1484,6 +1551,7 @@ const FUNCTION_SPEC_FIELDS = new Set([
|
|
|
1484
1551
|
"schedule",
|
|
1485
1552
|
"requireAuth",
|
|
1486
1553
|
"requireRole",
|
|
1554
|
+
"class",
|
|
1487
1555
|
]);
|
|
1488
1556
|
const FUNCTION_CONFIG_FIELDS = new Set(["timeoutSeconds", "memoryMb"]);
|
|
1489
1557
|
const SITE_SPEC_FIELDS = new Set(["replace", "patch", "public_paths"]);
|
|
@@ -2382,6 +2450,8 @@ async function normalizeFunction(fn, remember) {
|
|
|
2382
2450
|
out.requireAuth = fn.requireAuth;
|
|
2383
2451
|
if (fn.requireRole !== undefined)
|
|
2384
2452
|
out.requireRole = fn.requireRole;
|
|
2453
|
+
if (fn.class !== undefined)
|
|
2454
|
+
out.class = fn.class;
|
|
2385
2455
|
if (fn.source !== undefined) {
|
|
2386
2456
|
const resolved = await resolveContent(fn.source, "function source");
|
|
2387
2457
|
out.source = remember(resolved);
|