@supacloud/cli 0.80.0 → 0.81.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/index.js +407 -54
- package/dist/shared/tools/app-tools.d.ts +7 -1
- package/dist/shared/tools/application-promotion-help.test.d.ts +1 -0
- package/dist/shared/tools/application-promotion.test.d.ts +1 -0
- package/dist/shared/tools/application-release-transfer.d.ts +8 -0
- package/dist/shared/tools/application-tools.d.ts +4 -1
- package/dist/shared/tools/release-backup-http-status.test.d.ts +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -179093,10 +179093,11 @@ function resolveSupaCloudContext(env = process.env, cwd = process.cwd(), selecti
|
|
|
179093
179093
|
var ACTION_POLICY = {
|
|
179094
179094
|
deploy: { write: ["deploy"] },
|
|
179095
179095
|
applications: {
|
|
179096
|
-
read: ["list_releases", "get_release", "get_runtime", "get_deployment_evidence", "get_configuration", "get_rollback_snapshot", "get_history", "get_deploy_plan", "logs", "get_preview_plan"],
|
|
179096
|
+
read: ["list_releases", "get_release", "get_runtime", "get_deployment_evidence", "get_configuration", "get_release_transfer_plan", "get_promotion_plan", "get_rollback_snapshot", "get_history", "get_deploy_plan", "logs", "get_preview_plan"],
|
|
179097
179097
|
write: [
|
|
179098
179098
|
"upload_release",
|
|
179099
179099
|
"put_configuration",
|
|
179100
|
+
"transfer_release",
|
|
179100
179101
|
"activate_release",
|
|
179101
179102
|
"deploy_release",
|
|
179102
179103
|
"rollback_release",
|
|
@@ -179178,7 +179179,7 @@ var ACTION_POLICY = {
|
|
|
179178
179179
|
scheduled_functions: { read: ["list", "get"], write: ["create", "update", "delete"] },
|
|
179179
179180
|
mutations: { read: ["status"] },
|
|
179180
179181
|
release: {
|
|
179181
|
-
read: ["logical_backup_list", "postgrest_status"],
|
|
179182
|
+
read: ["logical_backup_list", "logical_backup_status", "postgrest_status"],
|
|
179182
179183
|
local: ["scope_inspect", "scope_rebind", "scope_create"],
|
|
179183
179184
|
write: ["logical_backup_create", "logical_backup_restore", "postgrest_restart", "release_canary_fixture_stage_replay", "release_canary_fixture_disable_replay"]
|
|
179184
179185
|
},
|
|
@@ -179201,8 +179202,8 @@ var ACTION_POLICY = {
|
|
|
179201
179202
|
ai: { local: ["show_skill", "install_skill"] },
|
|
179202
179203
|
app: {
|
|
179203
179204
|
local: ["init", "generate", "dev", "watch", "verify-plan", "compile", "check", "graph", "explain", "export-tools", "context", "doctor", "fix", "plan", "build"],
|
|
179204
|
-
read: ["status", "logs", "rollback-plan", "history", "deploy-plan", "diff", "preview-plan"],
|
|
179205
|
-
write: ["upload", "configure", "deploy", "rollback", "reconcile", "retire", "preview", "previews", "preview-status", "preview-cleanup"]
|
|
179205
|
+
read: ["status", "logs", "transfer-plan", "promote-plan", "rollback-plan", "history", "deploy-plan", "diff", "preview-plan"],
|
|
179206
|
+
write: ["upload", "configure", "transfer", "deploy", "rollback", "reconcile", "retire", "preview", "previews", "preview-status", "preview-cleanup"]
|
|
179206
179207
|
},
|
|
179207
179208
|
db: {
|
|
179208
179209
|
local: ["context", "lint", "explain", "diff", "role_sql"],
|
|
@@ -180419,6 +180420,52 @@ function parseApplicationActivationHistory(value) {
|
|
|
180419
180420
|
}
|
|
180420
180421
|
return structuredClone(value);
|
|
180421
180422
|
}
|
|
180423
|
+
// node_modules/@supacloud/delivery/src/application-release-transfer.ts
|
|
180424
|
+
var projectRef = String2({ pattern: "^[A-Za-z0-9_-]{1,20}$" });
|
|
180425
|
+
var source = _Object_({
|
|
180426
|
+
project_ref: projectRef,
|
|
180427
|
+
release_id: ApplicationReleaseIdSchema,
|
|
180428
|
+
manifest_sha256: ApplicationReleaseIdSchema
|
|
180429
|
+
}, { additionalProperties: false });
|
|
180430
|
+
var identity = {
|
|
180431
|
+
project_ref: projectRef,
|
|
180432
|
+
application_id: ApplicationIdSchema,
|
|
180433
|
+
source,
|
|
180434
|
+
candidate_release_id: ApplicationReleaseIdSchema
|
|
180435
|
+
};
|
|
180436
|
+
var ApplicationReleaseTransferPlanSchema = _Object_({
|
|
180437
|
+
schema: Literal("supacloud.application-release-transfer-plan.v1"),
|
|
180438
|
+
...identity,
|
|
180439
|
+
action: Union([Literal("materialize"), Literal("no-op")]),
|
|
180440
|
+
execution_performed: Literal(false)
|
|
180441
|
+
}, { additionalProperties: false });
|
|
180442
|
+
var ApplicationReleaseTransferResultSchema = _Object_({
|
|
180443
|
+
schema: Literal("supacloud.application-release-transfer-result.v1"),
|
|
180444
|
+
...identity,
|
|
180445
|
+
release: ApplicationReleaseRecordSchema,
|
|
180446
|
+
activation_performed: Literal(false)
|
|
180447
|
+
}, { additionalProperties: false });
|
|
180448
|
+
function assertIdentity(value) {
|
|
180449
|
+
if (value.source.release_id !== applicationReleaseId(value.source.project_ref, value.application_id, value.source.manifest_sha256) || value.candidate_release_id !== applicationReleaseId(value.project_ref, value.application_id, value.source.manifest_sha256)) {
|
|
180450
|
+
throw new Error("Invalid application release transfer identity");
|
|
180451
|
+
}
|
|
180452
|
+
}
|
|
180453
|
+
function parseApplicationReleaseTransferPlan(candidate) {
|
|
180454
|
+
if (!Check2(ApplicationReleaseTransferPlanSchema, candidate))
|
|
180455
|
+
throw new Error("Invalid application release transfer plan");
|
|
180456
|
+
assertIdentity(candidate);
|
|
180457
|
+
return candidate;
|
|
180458
|
+
}
|
|
180459
|
+
function parseApplicationReleaseTransferResult(candidate) {
|
|
180460
|
+
if (!Check2(ApplicationReleaseTransferResultSchema, candidate))
|
|
180461
|
+
throw new Error("Invalid application release transfer result");
|
|
180462
|
+
assertIdentity(candidate);
|
|
180463
|
+
const release = parseApplicationReleaseRecord(candidate.release);
|
|
180464
|
+
if (release.project_ref !== candidate.project_ref || release.application_id !== candidate.application_id || release.release_id !== candidate.candidate_release_id || release.manifest_sha256 !== candidate.source.manifest_sha256) {
|
|
180465
|
+
throw new Error("Invalid application release transfer result");
|
|
180466
|
+
}
|
|
180467
|
+
return candidate;
|
|
180468
|
+
}
|
|
180422
180469
|
// node_modules/@supacloud/delivery/src/deployment-evidence.ts
|
|
180423
180470
|
var closed3 = { additionalProperties: false };
|
|
180424
180471
|
var sha256 = String2({ pattern: "^[a-f0-9]{64}$" });
|
|
@@ -180544,6 +180591,116 @@ function parseDeploymentEvidence(value) {
|
|
|
180544
180591
|
}
|
|
180545
180592
|
return structuredClone(value);
|
|
180546
180593
|
}
|
|
180594
|
+
// node_modules/@supacloud/delivery/src/application-promotion.ts
|
|
180595
|
+
var closed4 = { additionalProperties: false };
|
|
180596
|
+
var projectRef2 = String2({ pattern: "^[a-z0-9-]{1,20}$" });
|
|
180597
|
+
var sha2562 = ApplicationReleaseIdSchema;
|
|
180598
|
+
var nullableActivation = Union([ApplicationActivationIdSchema, Null()]);
|
|
180599
|
+
var nullableDigest = Union([sha2562, Null()]);
|
|
180600
|
+
var observation = {
|
|
180601
|
+
activation_id: nullableActivation,
|
|
180602
|
+
receipt_confirmed: Boolean2(),
|
|
180603
|
+
ready: Boolean2(),
|
|
180604
|
+
smoke_verified: Boolean2(),
|
|
180605
|
+
evidence_sha256: nullableDigest,
|
|
180606
|
+
migration_ledger_digest: nullableDigest
|
|
180607
|
+
};
|
|
180608
|
+
var blocker = [
|
|
180609
|
+
"SOURCE_NOT_ACTIVE",
|
|
180610
|
+
"SOURCE_RECEIPT_UNCONFIRMED",
|
|
180611
|
+
"SOURCE_NOT_READY",
|
|
180612
|
+
"SOURCE_SMOKE_UNVERIFIED",
|
|
180613
|
+
"TARGET_CONFIGURATION_MISSING",
|
|
180614
|
+
"TARGET_CONFIGURATION_MISMATCH",
|
|
180615
|
+
"MIGRATION_PENDING",
|
|
180616
|
+
"MIGRATION_CONFLICT",
|
|
180617
|
+
"OPERATOR_PROVISIONING_REQUIRED",
|
|
180618
|
+
"BACKUP_REQUIRED"
|
|
180619
|
+
];
|
|
180620
|
+
var ApplicationPromotionPlanSchema = _Object_({
|
|
180621
|
+
schema: Literal("supacloud.application-promotion-plan.v1"),
|
|
180622
|
+
project_ref: projectRef2,
|
|
180623
|
+
application_id: ApplicationIdSchema,
|
|
180624
|
+
environment_id: ApplicationIdSchema,
|
|
180625
|
+
manifest_sha256: sha2562,
|
|
180626
|
+
plan_sha256: sha2562,
|
|
180627
|
+
source: _Object_({
|
|
180628
|
+
project_ref: projectRef2,
|
|
180629
|
+
environment_id: ApplicationIdSchema,
|
|
180630
|
+
release_id: sha2562,
|
|
180631
|
+
...observation
|
|
180632
|
+
}, closed4),
|
|
180633
|
+
target: _Object_({
|
|
180634
|
+
candidate_release_id: sha2562,
|
|
180635
|
+
current_release_id: Union([sha2562, Null()]),
|
|
180636
|
+
artifact_action: Union([Literal("materialize"), Literal("reuse")]),
|
|
180637
|
+
configuration_id: Union([ApplicationConfigurationIdSchema, Null()]),
|
|
180638
|
+
configuration: Union([ApplicationConfigurationViewSchema, Null()]),
|
|
180639
|
+
...observation
|
|
180640
|
+
}, closed4),
|
|
180641
|
+
migrations: _Object_({
|
|
180642
|
+
ledger_digest: sha2562,
|
|
180643
|
+
ledger_compatible: Boolean2(),
|
|
180644
|
+
project_migrations_applied: Boolean2(),
|
|
180645
|
+
pending_versions: _Array_(String2({ pattern: "^\\d{1,20}$" }), { maxItems: 4096, uniqueItems: true }),
|
|
180646
|
+
operator_provisioning_required: Boolean2()
|
|
180647
|
+
}, closed4),
|
|
180648
|
+
backup: _Object_({
|
|
180649
|
+
required: Boolean2(),
|
|
180650
|
+
confirmed: Literal(false)
|
|
180651
|
+
}, closed4),
|
|
180652
|
+
action: Union([Literal("promote"), Literal("no-op"), Literal("blocked")]),
|
|
180653
|
+
blockers: _Array_(Enum([...blocker]), {
|
|
180654
|
+
maxItems: blocker.length,
|
|
180655
|
+
uniqueItems: true
|
|
180656
|
+
}),
|
|
180657
|
+
steps: _Array_(String2({ minLength: 1, maxLength: 64 }), { maxItems: 8, uniqueItems: true }),
|
|
180658
|
+
execution_performed: Literal(false),
|
|
180659
|
+
data_recovery: Literal("separate-required")
|
|
180660
|
+
}, closed4);
|
|
180661
|
+
function applicationPromotionPlanDigest(value) {
|
|
180662
|
+
return digest(canonical(value));
|
|
180663
|
+
}
|
|
180664
|
+
function applicationPromotionAction(value) {
|
|
180665
|
+
if (value.blockers.length > 0)
|
|
180666
|
+
return "blocked";
|
|
180667
|
+
return value.target.artifact_action === "reuse" && value.target.current_release_id === value.target.candidate_release_id && value.target.configuration !== null && value.target.activation_id !== null && value.target.receipt_confirmed && value.target.ready && value.target.smoke_verified && value.migrations.project_migrations_applied && !value.migrations.operator_provisioning_required && (!value.backup.required || value.backup.confirmed) ? "no-op" : "promote";
|
|
180668
|
+
}
|
|
180669
|
+
function applicationPromotionSteps(value) {
|
|
180670
|
+
if (value.action === "no-op")
|
|
180671
|
+
return [];
|
|
180672
|
+
return [
|
|
180673
|
+
...value.blockers.some((code) => code.startsWith("SOURCE_")) ? ["verify-source"] : [],
|
|
180674
|
+
...value.blockers.some((code) => code.startsWith("TARGET_CONFIGURATION_")) ? ["bind-target-configuration"] : [],
|
|
180675
|
+
...value.blockers.includes("MIGRATION_CONFLICT") ? ["resolve-migration-conflicts"] : [],
|
|
180676
|
+
...!value.backup.required || value.backup.confirmed ? [] : ["backup"],
|
|
180677
|
+
...value.migrations.project_migrations_applied ? [] : ["review-and-apply-migrations"],
|
|
180678
|
+
...value.migrations.operator_provisioning_required ? ["verify-operator-provisioning"] : [],
|
|
180679
|
+
...value.action === "promote" ? [
|
|
180680
|
+
...value.target.artifact_action === "materialize" ? ["transfer"] : [],
|
|
180681
|
+
"activate-with-cas",
|
|
180682
|
+
"verify-runtime-and-smoke"
|
|
180683
|
+
] : []
|
|
180684
|
+
];
|
|
180685
|
+
}
|
|
180686
|
+
function parseApplicationPromotionPlan(value) {
|
|
180687
|
+
if (!Check2(ApplicationPromotionPlanSchema, value))
|
|
180688
|
+
throw new Error("Invalid application promotion plan");
|
|
180689
|
+
const { plan_sha256: _planSha256, ...content } = value;
|
|
180690
|
+
if (value.migrations.project_migrations_applied && (!value.migrations.ledger_compatible || value.migrations.pending_versions.length > 0) || !value.source.activation_id && (value.source.ready || value.source.receipt_confirmed || value.source.smoke_verified) || !value.target.activation_id && (value.target.ready || value.target.receipt_confirmed || value.target.smoke_verified) || !value.blockers.length && (!value.source.ready || !value.source.receipt_confirmed || !value.source.smoke_verified || !value.source.activation_id || !value.target.configuration || !value.migrations.ledger_compatible)) {
|
|
180691
|
+
throw new Error("Invalid application promotion prerequisites");
|
|
180692
|
+
}
|
|
180693
|
+
if (value.backup.required !== !value.migrations.project_migrations_applied || [value.source, value.target].some((observation) => observation.smoke_verified && (observation.evidence_sha256 === null || observation.migration_ledger_digest === null)) || value.target.smoke_verified && value.target.migration_ledger_digest !== value.migrations.ledger_digest) {
|
|
180694
|
+
throw new Error("Invalid application promotion migration binding");
|
|
180695
|
+
}
|
|
180696
|
+
if (value.source.release_id !== applicationReleaseId(value.source.project_ref, value.application_id, value.manifest_sha256) || value.target.candidate_release_id !== applicationReleaseId(value.project_ref, value.application_id, value.manifest_sha256) || value.action !== applicationPromotionAction(content) || canonical(value.steps) !== canonical(applicationPromotionSteps(content)) || value.plan_sha256 !== applicationPromotionPlanDigest(content) || value.target.configuration !== null && (() => {
|
|
180697
|
+
const configuration = parseApplicationConfigurationView(value.target.configuration);
|
|
180698
|
+
return configuration.project_ref !== value.project_ref || configuration.application_id !== value.application_id || configuration.environment_id !== value.environment_id || configuration.configuration_id !== value.target.configuration_id;
|
|
180699
|
+
})()) {
|
|
180700
|
+
throw new Error("Invalid application promotion plan identity");
|
|
180701
|
+
}
|
|
180702
|
+
return structuredClone(value);
|
|
180703
|
+
}
|
|
180547
180704
|
// node_modules/@supacloud/delivery/src/application-runtime-identity.ts
|
|
180548
180705
|
var ApplicationRuntimeIdentitySchema = _Object_({
|
|
180549
180706
|
schema: Literal("supacloud.application-runtime.v1"),
|
|
@@ -181551,7 +181708,7 @@ ALTER TABLE supacloud_commands.execution_receipts
|
|
|
181551
181708
|
DROP COLUMN IF EXISTS recovery_attempts;
|
|
181552
181709
|
`;
|
|
181553
181710
|
var MAX_MIGRATION_VERSION = 9223372036854775807n;
|
|
181554
|
-
function
|
|
181711
|
+
function sha2563(value) {
|
|
181555
181712
|
return createHash22("sha256").update(value).digest("hex");
|
|
181556
181713
|
}
|
|
181557
181714
|
function normalizedSql(value) {
|
|
@@ -181620,7 +181777,7 @@ function planMigrationRebase(migrations, options, baselineSql) {
|
|
|
181620
181777
|
file: migration.file,
|
|
181621
181778
|
version: migration.version,
|
|
181622
181779
|
name: migration.name,
|
|
181623
|
-
sha256:
|
|
181780
|
+
sha256: sha2563(normalizedSql(migration.sql))
|
|
181624
181781
|
}));
|
|
181625
181782
|
return {
|
|
181626
181783
|
version: 1,
|
|
@@ -181630,8 +181787,8 @@ function planMigrationRebase(migrations, options, baselineSql) {
|
|
|
181630
181787
|
baselineMigration,
|
|
181631
181788
|
retainedMigrations: retained.map((migration) => migration.file),
|
|
181632
181789
|
archivedMigrations: archived.map((migration) => migration.file),
|
|
181633
|
-
sourceHistorySha256:
|
|
181634
|
-
baselineSha256:
|
|
181790
|
+
sourceHistorySha256: sha2563(JSON.stringify(sourceHistory)),
|
|
181791
|
+
baselineSha256: sha2563(normalizedBaseline),
|
|
181635
181792
|
warnings: [
|
|
181636
181793
|
"The baseline must come from a schema-only snapshot of the verified target database.",
|
|
181637
181794
|
"Data backfills, reference data and non-replayable side effects need a separate review.",
|
|
@@ -181667,7 +181824,7 @@ async function rebaseMigrations(options) {
|
|
|
181667
181824
|
file: migration.file,
|
|
181668
181825
|
version: migration.version,
|
|
181669
181826
|
name: migration.name,
|
|
181670
|
-
sha256:
|
|
181827
|
+
sha256: sha2563(normalizedSql(migration.sql))
|
|
181671
181828
|
}))
|
|
181672
181829
|
}, null, 2) + `
|
|
181673
181830
|
`);
|
|
@@ -184569,7 +184726,7 @@ function clientId(value) {
|
|
|
184569
184726
|
}
|
|
184570
184727
|
return value;
|
|
184571
184728
|
}
|
|
184572
|
-
function
|
|
184729
|
+
function projectRef3(value) {
|
|
184573
184730
|
if (typeof value !== "string" || !value.trim())
|
|
184574
184731
|
throw new Error("'ref' is required");
|
|
184575
184732
|
return projectRefPathSegment(value.trim(), "OAuth client");
|
|
@@ -184721,7 +184878,7 @@ function registerOAuthClientTools(server2, http) {
|
|
|
184721
184878
|
client_id: optional(String2(), "[get/delete] Exact release-canary public OAuth client ID"),
|
|
184722
184879
|
redirect_uri: optional(String2(), "[create/delete] Exact HTTPS or port-bound RFC 8252 loopback callback")
|
|
184723
184880
|
}, async ({ action, ref, client_id, redirect_uri }) => {
|
|
184724
|
-
const targetRef =
|
|
184881
|
+
const targetRef = projectRef3(ref);
|
|
184725
184882
|
if (action === "list")
|
|
184726
184883
|
return listReleaseCanaryClients(http, targetRef);
|
|
184727
184884
|
if (action === "get")
|
|
@@ -187394,6 +187551,64 @@ var ApplicationActivationRetirementResultSchema2 = ApplicationActivationRetireme
|
|
|
187394
187551
|
var ApplicationRollbackSnapshotSchema2 = ApplicationRollbackSnapshotSchema;
|
|
187395
187552
|
var ApplicationActivationHistoryCursorSchema2 = ApplicationActivationHistoryCursorSchema;
|
|
187396
187553
|
|
|
187554
|
+
// src/shared/tools/application-release-transfer.ts
|
|
187555
|
+
async function applicationReleaseTransfer(http, input, project) {
|
|
187556
|
+
const operation = `applications.${input.action}`;
|
|
187557
|
+
const identity = {
|
|
187558
|
+
project_ref: input.ref,
|
|
187559
|
+
application_id: input.id,
|
|
187560
|
+
source_ref: input.sourceRef,
|
|
187561
|
+
source_release_id: input.sourceReleaseId
|
|
187562
|
+
};
|
|
187563
|
+
const path = `/v1/projects/${project}/applications/${encodeURIComponent(input.id)}`;
|
|
187564
|
+
const query = new URLSearchParams({ source_ref: input.sourceRef, source_release_id: input.sourceReleaseId });
|
|
187565
|
+
const response = await http.get(`${path}/release-transfer-plan?${query}`, { maxJsonBytes: 65536, responseTimeoutMs: 30000 });
|
|
187566
|
+
if (!response.ok)
|
|
187567
|
+
return releaseControlFailure(operation, "HTTP_ERROR", response.transportError ? null : response.status, identity);
|
|
187568
|
+
let plan;
|
|
187569
|
+
try {
|
|
187570
|
+
if (response.status !== 200)
|
|
187571
|
+
throw new Error;
|
|
187572
|
+
plan = parseApplicationReleaseTransferPlan(response.data);
|
|
187573
|
+
if (plan.project_ref !== input.ref || plan.application_id !== input.id || plan.source.project_ref !== input.sourceRef || plan.source.release_id !== input.sourceReleaseId)
|
|
187574
|
+
throw new Error;
|
|
187575
|
+
} catch {
|
|
187576
|
+
return releaseControlFailure(operation, "INVALID_RESPONSE", response.status, identity);
|
|
187577
|
+
}
|
|
187578
|
+
if (input.action === "get_release_transfer_plan")
|
|
187579
|
+
return releaseControlSuccess(operation, { ...identity, plan });
|
|
187580
|
+
const safeIdentity = {
|
|
187581
|
+
...identity,
|
|
187582
|
+
release_id: plan.candidate_release_id,
|
|
187583
|
+
manifest_sha256: plan.source.manifest_sha256
|
|
187584
|
+
};
|
|
187585
|
+
if (plan.action === "no-op")
|
|
187586
|
+
return releaseControlSuccess(operation, {
|
|
187587
|
+
...safeIdentity,
|
|
187588
|
+
no_op: true,
|
|
187589
|
+
execution_performed: false,
|
|
187590
|
+
activation_performed: false,
|
|
187591
|
+
plan
|
|
187592
|
+
});
|
|
187593
|
+
const result = await http.postReleaseMutation(`${path}/release-transfers`, {
|
|
187594
|
+
source_ref: input.sourceRef,
|
|
187595
|
+
source_release_id: input.sourceReleaseId,
|
|
187596
|
+
expected_manifest_sha256: plan.source.manifest_sha256
|
|
187597
|
+
}, { timeoutMs: 120000 });
|
|
187598
|
+
if (!result.ok)
|
|
187599
|
+
return releaseControlMutationFailure(operation, result, safeIdentity);
|
|
187600
|
+
try {
|
|
187601
|
+
if (result.status !== 200)
|
|
187602
|
+
throw new Error;
|
|
187603
|
+
const receipt = parseApplicationReleaseTransferResult(result.data);
|
|
187604
|
+
if (receipt.project_ref !== input.ref || receipt.application_id !== input.id || receipt.source.project_ref !== input.sourceRef || receipt.source.release_id !== input.sourceReleaseId || receipt.candidate_release_id !== plan.candidate_release_id || receipt.source.manifest_sha256 !== plan.source.manifest_sha256)
|
|
187605
|
+
throw new Error;
|
|
187606
|
+
return releaseControlSuccess(operation, { ...safeIdentity, no_op: false, transfer: receipt });
|
|
187607
|
+
} catch {
|
|
187608
|
+
return releaseControlFailure(operation, "OUTCOME_UNKNOWN", result.status, safeIdentity);
|
|
187609
|
+
}
|
|
187610
|
+
}
|
|
187611
|
+
|
|
187397
187612
|
// src/shared/tools/application-preview-tools.ts
|
|
187398
187613
|
var APPLICATION_PREVIEW_ACTIONS = [
|
|
187399
187614
|
"get_preview_plan",
|
|
@@ -187732,6 +187947,9 @@ var APPLICATION_TOOL_SCHEMA = {
|
|
|
187732
187947
|
"put_configuration",
|
|
187733
187948
|
"get_deploy_plan",
|
|
187734
187949
|
"deploy_release",
|
|
187950
|
+
"get_release_transfer_plan",
|
|
187951
|
+
"transfer_release",
|
|
187952
|
+
"get_promotion_plan",
|
|
187735
187953
|
"activate_release",
|
|
187736
187954
|
"rollback_release",
|
|
187737
187955
|
"get_rollback_snapshot",
|
|
@@ -187750,6 +187968,9 @@ var APPLICATION_TOOL_SCHEMA = {
|
|
|
187750
187968
|
configuration_path: optional(String2(), "[put_configuration] Local configuration write JSON including revision and expected revision"),
|
|
187751
187969
|
manifest_path: optional(String2(), "[upload_release] Local delivery.manifest.json"),
|
|
187752
187970
|
release_id: optional(ApplicationReleaseIdSchema2, "[get_release/get_deploy_plan/deploy_release/activate_release/reconcile_activation/rollback_release] Immutable application release ID; platform selects previous for default rollback"),
|
|
187971
|
+
source_ref: optional(String2({ pattern: "^[A-Za-z0-9_-]{1,20}$" }), "[get_release_transfer_plan/transfer_release] Source project ref"),
|
|
187972
|
+
source_release_id: optional(ApplicationReleaseIdSchema2, "[get_release_transfer_plan/transfer_release] Source immutable release ID"),
|
|
187973
|
+
source_environment_id: optional(ApplicationIdSchema2, "[get_promotion_plan] Source environment"),
|
|
187753
187974
|
cursor: optional(Union([ApplicationReleaseIdSchema2, ApplicationActivationHistoryCursorSchema2]), "[list_releases/get_history] Returned page cursor"),
|
|
187754
187975
|
limit: optional(Integer({ minimum: 1, maximum: 100 }), "[list_releases/get_history] Page size, default 50/20"),
|
|
187755
187976
|
offset: optional(Integer({ minimum: 0, maximum: 1e6 }), "[logs] Result offset"),
|
|
@@ -188045,6 +188266,52 @@ function registerApplicationTools(server2, http) {
|
|
|
188045
188266
|
throw new Error("Invalid application ID");
|
|
188046
188267
|
const path = `/v1/projects/${project}/applications/${encodeURIComponent(id)}/releases`;
|
|
188047
188268
|
const operation = `applications.${action}`;
|
|
188269
|
+
if (action === "get_promotion_plan") {
|
|
188270
|
+
const environmentId = text(args, "environment_id");
|
|
188271
|
+
const sourceRef = text(args, "source_ref"), sourceEnvironmentId = text(args, "source_environment_id");
|
|
188272
|
+
const sourceReleaseId = text(args, "source_release_id");
|
|
188273
|
+
if (!/^[a-z0-9-]{1,20}$/.test(sourceRef) || !Check2(ApplicationReleaseIdSchema2, sourceReleaseId) || !Check2(ApplicationIdSchema2, environmentId) || !Check2(ApplicationIdSchema2, sourceEnvironmentId)) {
|
|
188274
|
+
throw new Error("Invalid promotion environment");
|
|
188275
|
+
}
|
|
188276
|
+
const query = new URLSearchParams({
|
|
188277
|
+
source_ref: sourceRef,
|
|
188278
|
+
source_environment_id: sourceEnvironmentId,
|
|
188279
|
+
source_release_id: sourceReleaseId
|
|
188280
|
+
});
|
|
188281
|
+
if (typeof args["configuration_id"] === "string")
|
|
188282
|
+
query.set("configuration_id", args["configuration_id"]);
|
|
188283
|
+
const result = await http.get(`/v1/projects/${project}/applications/${encodeURIComponent(id)}/environments/${encodeURIComponent(environmentId)}/promotion-plan?${query}`, { maxJsonBytes: 262144, responseTimeoutMs: 30000 });
|
|
188284
|
+
const identity = {
|
|
188285
|
+
project_ref: ref,
|
|
188286
|
+
application_id: id,
|
|
188287
|
+
environment_id: environmentId,
|
|
188288
|
+
source_ref: sourceRef,
|
|
188289
|
+
source_environment_id: sourceEnvironmentId,
|
|
188290
|
+
source_release_id: sourceReleaseId
|
|
188291
|
+
};
|
|
188292
|
+
if (!result.ok)
|
|
188293
|
+
return releaseControlFailure(operation, "HTTP_ERROR", result.status, identity);
|
|
188294
|
+
try {
|
|
188295
|
+
if (result.status !== 200)
|
|
188296
|
+
throw new Error;
|
|
188297
|
+
const plan = parseApplicationPromotionPlan(result.data);
|
|
188298
|
+
if (plan.project_ref !== ref || plan.application_id !== id || plan.environment_id !== environmentId || plan.source.project_ref !== sourceRef || plan.source.environment_id !== sourceEnvironmentId || plan.source.release_id !== sourceReleaseId || args["configuration_id"] !== undefined && plan.target.configuration_id !== args["configuration_id"]) {
|
|
188299
|
+
throw new Error;
|
|
188300
|
+
}
|
|
188301
|
+
return releaseControlSuccess(operation, { ...identity, plan });
|
|
188302
|
+
} catch {
|
|
188303
|
+
return releaseControlFailure(operation, "INVALID_RESPONSE", result.status, identity);
|
|
188304
|
+
}
|
|
188305
|
+
}
|
|
188306
|
+
if (action === "get_release_transfer_plan" || action === "transfer_release") {
|
|
188307
|
+
return applicationReleaseTransfer(http, {
|
|
188308
|
+
action,
|
|
188309
|
+
ref,
|
|
188310
|
+
id,
|
|
188311
|
+
sourceRef: text(args, "source_ref"),
|
|
188312
|
+
sourceReleaseId: text(args, "source_release_id")
|
|
188313
|
+
}, project);
|
|
188314
|
+
}
|
|
188048
188315
|
if (action === "get_deploy_plan") {
|
|
188049
188316
|
const observed = await readDeployPlan(http, args, project);
|
|
188050
188317
|
return observed.failure ?? releaseControlSuccess(operation, { ...observed.identity, plan: observed.plan });
|
|
@@ -194299,6 +194566,9 @@ async function applyScaffoldWrites(plan) {
|
|
|
194299
194566
|
// src/shared/tools/app-tools.ts
|
|
194300
194567
|
var REMOTE_APP_ACTIONS = {
|
|
194301
194568
|
upload: "upload_release",
|
|
194569
|
+
"transfer-plan": "get_release_transfer_plan",
|
|
194570
|
+
"promote-plan": "get_promotion_plan",
|
|
194571
|
+
transfer: "transfer_release",
|
|
194302
194572
|
configure: "put_configuration",
|
|
194303
194573
|
deploy: "deploy_release",
|
|
194304
194574
|
"deploy-plan": "get_deploy_plan",
|
|
@@ -194317,15 +194587,18 @@ var REMOTE_APP_ACTIONS = {
|
|
|
194317
194587
|
"preview-cleanup": "cleanup_preview"
|
|
194318
194588
|
};
|
|
194319
194589
|
var REMOTE_APP_DESCRIPTIONS = {
|
|
194320
|
-
ref: "[upload/configure/deploy/deploy-plan/diff/status/rollback/rollback-plan/history/reconcile/retire/preview-plan/preview/previews/preview-status/preview-cleanup] Project ref (defaults to context)",
|
|
194321
|
-
id: "[upload/configure/deploy/deploy-plan/diff/status/rollback/rollback-plan/history/reconcile/retire/preview-plan/preview/previews/preview-status/preview-cleanup] Application ID",
|
|
194322
|
-
environment_id: "[configure/deploy/deploy-plan/diff/status/rollback/rollback-plan/history/reconcile/retire/preview-plan/preview/previews/preview-status/preview-cleanup]
|
|
194323
|
-
configuration_id: "[deploy/deploy-plan/diff/rollback/preview]
|
|
194590
|
+
ref: "[upload/configure/transfer-plan/transfer/promote-plan/deploy/deploy-plan/diff/status/rollback/rollback-plan/history/reconcile/retire/preview-plan/preview/previews/preview-status/preview-cleanup] Project ref (defaults to context)",
|
|
194591
|
+
id: "[upload/configure/transfer-plan/transfer/promote-plan/deploy/deploy-plan/diff/status/rollback/rollback-plan/history/reconcile/retire/preview-plan/preview/previews/preview-status/preview-cleanup] Application ID",
|
|
194592
|
+
environment_id: "[configure/promote-plan/deploy/deploy-plan/diff/status/rollback/rollback-plan/history/reconcile/retire/preview-plan/preview/previews/preview-status/preview-cleanup] Target environment ID",
|
|
194593
|
+
configuration_id: "[promote-plan/deploy/deploy-plan/diff/rollback/preview] Immutable target configuration revision; required for deployment/preview; platform selects for default rollback",
|
|
194324
194594
|
activation_id: "[deploy/rollback/reconcile/retire] Stable activation ID; auto-generated for deploy/rollback if omitted",
|
|
194325
194595
|
expected_activation_id: "[deploy/rollback] Auto-selected from verified state; supply with activation_id for an exact retry",
|
|
194326
194596
|
configuration_path: "[configure] Configuration write JSON including revision and expected revision",
|
|
194327
194597
|
manifest_path: "[upload] Local delivery.manifest.json",
|
|
194328
194598
|
release_id: "[deploy/deploy-plan/diff/rollback/reconcile/preview-plan/preview] Required for deployment/preview; defaults to journal-selected previous for rollback",
|
|
194599
|
+
source_ref: "[transfer-plan/transfer/promote-plan] Source project ref",
|
|
194600
|
+
source_release_id: "[transfer-plan/transfer/promote-plan] Source immutable release ID",
|
|
194601
|
+
source_environment_id: "[promote-plan] Source environment ID",
|
|
194329
194602
|
preview_id: "[preview-status/preview-cleanup] Preview receipt ID",
|
|
194330
194603
|
branch_ref: "[preview-plan] Proposed branch ref; preview assigns its own",
|
|
194331
194604
|
branch_name: "[preview] Branch display name",
|
|
@@ -195233,6 +195506,30 @@ async function runDelivery(args) {
|
|
|
195233
195506
|
const result = await buildDeliveryProject(options, loaded.delivery);
|
|
195234
195507
|
return textResult(JSON.stringify(result, null, 2), !result.ok);
|
|
195235
195508
|
}
|
|
195509
|
+
function formatReleaseTransfer(result) {
|
|
195510
|
+
if (result.isError)
|
|
195511
|
+
return result;
|
|
195512
|
+
try {
|
|
195513
|
+
const payload = JSON.parse(result.content[0]?.text ?? "");
|
|
195514
|
+
if (!payload || typeof payload !== "object" || !("ok" in payload) || payload.ok !== true)
|
|
195515
|
+
return result;
|
|
195516
|
+
const plan = "plan" in payload ? parseApplicationReleaseTransferPlan(payload.plan) : null;
|
|
195517
|
+
const receipt = !plan && "transfer" in payload ? parseApplicationReleaseTransferResult(payload.transfer) : null;
|
|
195518
|
+
const data = plan ?? receipt;
|
|
195519
|
+
if (!data)
|
|
195520
|
+
return result;
|
|
195521
|
+
return textResult([
|
|
195522
|
+
`${data.project_ref}/${data.application_id}: ${plan ? plan.action === "no-op" ? "No changes" : "Transfer required" : "Artifact transferred"}`,
|
|
195523
|
+
`Source: ${data.source.project_ref} / ${data.source.release_id}`,
|
|
195524
|
+
`Target release: ${data.candidate_release_id}`,
|
|
195525
|
+
`Manifest: ${data.source.manifest_sha256}`,
|
|
195526
|
+
"Activation: not performed"
|
|
195527
|
+
].join(`
|
|
195528
|
+
`));
|
|
195529
|
+
} catch {
|
|
195530
|
+
return result;
|
|
195531
|
+
}
|
|
195532
|
+
}
|
|
195236
195533
|
var historyReceiptSchema = _Object_({
|
|
195237
195534
|
schema: Literal(RELEASE_CONTROL_RESPONSE_SCHEMA),
|
|
195238
195535
|
ok: Literal(true),
|
|
@@ -195318,9 +195615,11 @@ async function runAppTool(request, options = {}) {
|
|
|
195318
195615
|
if (request.database_url !== undefined && request.action !== "dev" && request.action !== "watch") {
|
|
195319
195616
|
throw new ScaffoldError("SCAFFOLD_OPTION_INVALID", "--database-url applies only to app dev --profile integration");
|
|
195320
195617
|
}
|
|
195321
|
-
if (request.json !== undefined &&
|
|
195322
|
-
throw new Error("--json applies only to app history
|
|
195618
|
+
if (request.json !== undefined && request.action !== "history" && request.action !== "transfer-plan" && request.action !== "transfer" && request.action !== "promote-plan") {
|
|
195619
|
+
throw new Error("--json applies only to app history, transfer-plan, transfer or promote-plan");
|
|
195323
195620
|
}
|
|
195621
|
+
if (request.json !== undefined && request.format === "text")
|
|
195622
|
+
throw new Error("--json and --format text cannot be combined");
|
|
195324
195623
|
if (Object.hasOwn(REMOTE_APP_ACTIONS, request.action)) {
|
|
195325
195624
|
const delegate = options.getApplications?.();
|
|
195326
195625
|
if (!delegate)
|
|
@@ -195337,7 +195636,24 @@ async function runAppTool(request, options = {}) {
|
|
|
195337
195636
|
if (request.action === "history" && format !== "json" && json !== true) {
|
|
195338
195637
|
return historyText(result, request, args.ref);
|
|
195339
195638
|
}
|
|
195340
|
-
|
|
195639
|
+
if ((request.action === "deploy-plan" || request.action === "diff") && format !== "json") {
|
|
195640
|
+
return formatRemoteDeployPlan(result);
|
|
195641
|
+
}
|
|
195642
|
+
if (request.action === "promote-plan" && !result.isError && format !== "json" && !json) {
|
|
195643
|
+
const payload = JSON.parse(result.content[0]?.text ?? "");
|
|
195644
|
+
if (!payload || typeof payload !== "object" || !("plan" in payload))
|
|
195645
|
+
return result;
|
|
195646
|
+
const plan = parseApplicationPromotionPlan(payload.plan);
|
|
195647
|
+
return textResult([
|
|
195648
|
+
`${plan.project_ref}/${plan.application_id}/${plan.environment_id}: ${plan.action}`,
|
|
195649
|
+
`Source: ${plan.source.project_ref}/${plan.source.environment_id}`,
|
|
195650
|
+
...plan.blockers.length ? [`Blocked: ${plan.blockers.join(", ")}`] : [],
|
|
195651
|
+
...plan.steps.length ? [`Next: ${plan.steps.join(" -> ")}`] : [],
|
|
195652
|
+
"Execution: not performed"
|
|
195653
|
+
].join(`
|
|
195654
|
+
`));
|
|
195655
|
+
}
|
|
195656
|
+
return (request.action === "transfer-plan" || request.action === "transfer") && format !== "json" && !json ? formatReleaseTransfer(result) : result;
|
|
195341
195657
|
}
|
|
195342
195658
|
switch (request.action) {
|
|
195343
195659
|
case "verify-plan": {
|
|
@@ -195411,6 +195727,9 @@ function registerAppTools(server2, options = {}) {
|
|
|
195411
195727
|
"build",
|
|
195412
195728
|
"upload",
|
|
195413
195729
|
"configure",
|
|
195730
|
+
"transfer-plan",
|
|
195731
|
+
"transfer",
|
|
195732
|
+
"promote-plan",
|
|
195414
195733
|
"deploy",
|
|
195415
195734
|
"deploy-plan",
|
|
195416
195735
|
"diff",
|
|
@@ -195426,7 +195745,7 @@ function registerAppTools(server2, options = {}) {
|
|
|
195426
195745
|
"previews",
|
|
195427
195746
|
"preview-status",
|
|
195428
195747
|
"preview-cleanup"
|
|
195429
|
-
]), "App action; local plan is topology-only, deploy-plan/diff observe remote state, deploy skips verified no-op, rollback never downgrades schema"),
|
|
195748
|
+
]), "App action; transfer reuses a verified release without build or activation; local plan is topology-only, deploy-plan/diff observe remote state, deploy skips verified no-op, rollback never downgrades schema"),
|
|
195430
195749
|
kind: optional(stringEnum(["module", "command", "query", "controller", "job", "contract", "resource"]), "[generate] Scaffold kind"),
|
|
195431
195750
|
template: optional(stringEnum(["minimal", "http", "command", "edge"]), "[init] Minimal application by default; explicit http/command/edge recipes"),
|
|
195432
195751
|
name: optional(String2(), "[init/generate] Project or object name"),
|
|
@@ -195441,8 +195760,8 @@ function registerAppTools(server2, options = {}) {
|
|
|
195441
195760
|
include: optional(String2(), "[dev/compile/check] Comma-separated glob patterns for source files"),
|
|
195442
195761
|
out_dir: optional(String2(), "[dev/compile/plan/build/export-tools] Output directory (default: configured outDir)"),
|
|
195443
195762
|
strict: optional(Boolean2(), "[dev/compile/check] Promote warnings to errors"),
|
|
195444
|
-
format: optional(stringEnum(["text", "json"]), "[dev/generate/compile/check/plan/deploy-plan/diff/history/graph/export-tools] Output format (default: text)"),
|
|
195445
|
-
json: optional(
|
|
195763
|
+
format: optional(stringEnum(["text", "json"]), "[dev/generate/compile/check/plan/deploy-plan/diff/history/promote-plan/graph/export-tools] Output format (default: text)"),
|
|
195764
|
+
json: optional(Boolean2(), "[history/transfer-plan/transfer/promote-plan] Preserve or return the full machine-readable receipt"),
|
|
195446
195765
|
profile: optional(stringEnum(["fast", "integration"]), "[dev] Run dev or dev:integration; integration verifies an explicit loopback database"),
|
|
195447
195766
|
once: optional(Boolean2(), "[dev] Validate and report once without watching"),
|
|
195448
195767
|
watch: optional(Boolean2(), "[dev] Watch for changes (default: true; --once disables)"),
|
|
@@ -196883,7 +197202,8 @@ function validProjectRef(ref) {
|
|
|
196883
197202
|
return SAFE_PROJECT_REF.test(ref);
|
|
196884
197203
|
}
|
|
196885
197204
|
function backupBelongsToProject(backupId, projectRef) {
|
|
196886
|
-
|
|
197205
|
+
const prefix = `logical-full_${projectRef}_`;
|
|
197206
|
+
return BACKUP_ID.test(backupId) && backupId.startsWith(prefix) && /^[a-f0-9]{32}$/.test(backupId.slice(prefix.length));
|
|
196887
197207
|
}
|
|
196888
197208
|
function verifiedBackup(value, projectRef) {
|
|
196889
197209
|
if (!isRecord6(value) || typeof value.backup_id !== "string" || !backupBelongsToProject(value.backup_id, projectRef) || value.project_ref !== projectRef || typeof value.database !== "string" || !SAFE_DATABASE.test(value.database) || value.kind !== "logical-full" || !canonicalTimestamp4(value.created_at) || !canonicalTimestamp4(value.completed_at) || new Date(value.completed_at).valueOf() < new Date(value.created_at).valueOf() || typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || typeof value.sha256 !== "string" || !SHA256.test(value.sha256))
|
|
@@ -196922,16 +197242,11 @@ function publicBackup(backup) {
|
|
|
196922
197242
|
function equalBackup(left, right) {
|
|
196923
197243
|
return left.backup_id === right.backup_id && left.project_ref === right.project_ref && left.database === right.database && left.kind === right.kind && left.created_at === right.created_at && left.completed_at === right.completed_at && left.bytes === right.bytes && left.sha256 === right.sha256;
|
|
196924
197244
|
}
|
|
196925
|
-
function
|
|
196926
|
-
|
|
196927
|
-
|
|
196928
|
-
const current = afterById.get(previous.backup_id);
|
|
196929
|
-
if (!current || !equalBackup(previous, current))
|
|
196930
|
-
return null;
|
|
197245
|
+
function requiredBackupId(value, projectRef) {
|
|
197246
|
+
if (typeof value !== "string" || !backupBelongsToProject(value, projectRef)) {
|
|
197247
|
+
throw new Error("'backup_id' must identify a logical-full backup for 'ref'");
|
|
196931
197248
|
}
|
|
196932
|
-
|
|
196933
|
-
const additions = after.filter((backup) => !known.has(backup.backup_id));
|
|
196934
|
-
return additions.length === 1 ? additions[0] : null;
|
|
197249
|
+
return value;
|
|
196935
197250
|
}
|
|
196936
197251
|
function restoreRequest(projectRef, backupId, expectedSha256, restoreConfirmation) {
|
|
196937
197252
|
if (typeof backupId !== "string" || !backupBelongsToProject(backupId, projectRef)) {
|
|
@@ -196957,11 +197272,27 @@ function httpFailure(operation, response) {
|
|
|
196957
197272
|
}
|
|
196958
197273
|
return releaseControlFailure(operation, "HTTP_ERROR", response.transportError ? null : response.status);
|
|
196959
197274
|
}
|
|
196960
|
-
function
|
|
196961
|
-
|
|
196962
|
-
|
|
197275
|
+
function mutationOutcomeUnknown(response) {
|
|
197276
|
+
return Boolean(response.responseReadError || response.transportError || response.status === 408 || response.status >= 500 || response.status >= 200 && response.status < 300 && response.status !== 200);
|
|
197277
|
+
}
|
|
197278
|
+
function mutationFailure(operation, response, safeState = {}) {
|
|
197279
|
+
if (mutationOutcomeUnknown(response)) {
|
|
197280
|
+
return releaseControlFailure(operation, "OUTCOME_UNKNOWN", response.transportError ? null : response.status, safeState);
|
|
196963
197281
|
}
|
|
196964
|
-
return releaseControlFailure(operation, "HTTP_ERROR", response.status);
|
|
197282
|
+
return releaseControlFailure(operation, "HTTP_ERROR", response.status, safeState);
|
|
197283
|
+
}
|
|
197284
|
+
async function readBackup(http, projectRef, backupId) {
|
|
197285
|
+
let response;
|
|
197286
|
+
try {
|
|
197287
|
+
response = await http.get(`${endpoint(projectRef)}/database/backups/logical/${backupId}`, {
|
|
197288
|
+
maxJsonBytes: MUTATION_MAX_BYTES,
|
|
197289
|
+
responseTimeoutMs: RELEASE_READ_RESPONSE_TIMEOUT_MS
|
|
197290
|
+
});
|
|
197291
|
+
} catch {
|
|
197292
|
+
response = { ok: false, status: 0, data: null, transportError: true };
|
|
197293
|
+
}
|
|
197294
|
+
const candidate = response.ok && response.status === 200 && isRecord6(response.data) ? verifiedBackup(response.data.backup, projectRef) : null;
|
|
197295
|
+
return { response, backup: candidate?.backup_id === backupId ? candidate : null };
|
|
196965
197296
|
}
|
|
196966
197297
|
async function readInventory(http, projectRef) {
|
|
196967
197298
|
const response = await http.get(`${endpoint(projectRef)}/database/backups/logical`, {
|
|
@@ -197374,6 +197705,7 @@ function registerReleaseTools(server2, http, options = {}) {
|
|
|
197374
197705
|
const localActions = ["scope_inspect", "scope_rebind", "scope_create"];
|
|
197375
197706
|
const remoteActions = [
|
|
197376
197707
|
"logical_backup_list",
|
|
197708
|
+
"logical_backup_status",
|
|
197377
197709
|
"logical_backup_create",
|
|
197378
197710
|
"logical_backup_restore",
|
|
197379
197711
|
"postgrest_status",
|
|
@@ -197396,7 +197728,7 @@ function registerReleaseTools(server2, http, options = {}) {
|
|
|
197396
197728
|
web: optional(Boolean2(), "[scope_create] Whether web assets are included in release scope"),
|
|
197397
197729
|
dry_run: optional(Boolean2(), "[scope_rebind] Perform calculation and validation without writing files"),
|
|
197398
197730
|
cwd: optional(String2(), "[scope_inspect/scope_rebind/scope_create] Base directory for relative file and git resolution"),
|
|
197399
|
-
backup_id: optional(String2(), "[
|
|
197731
|
+
backup_id: optional(String2(), "[logical_backup_create/status/restore] Stable project-bound backup ID; create generates one when omitted"),
|
|
197400
197732
|
expected_sha256: optional(String2(), "[logical_backup_restore] Exact lowercase SHA-256 from the selected project inventory"),
|
|
197401
197733
|
restore_confirmation: optional(String2(), "[logical_backup_restore] Exact RESTORE_PROJECT:<ref>:<backup_id>:<sha256> confirmation"),
|
|
197402
197734
|
subject: optional(String2(), "[release_canary_fixture_stage_replay/disable_replay] Exact central subject UUID"),
|
|
@@ -197441,28 +197773,47 @@ function registerReleaseTools(server2, http, options = {}) {
|
|
|
197441
197773
|
});
|
|
197442
197774
|
}
|
|
197443
197775
|
if (action === "logical_backup_create") {
|
|
197444
|
-
const
|
|
197445
|
-
const
|
|
197446
|
-
|
|
197447
|
-
|
|
197448
|
-
|
|
197449
|
-
|
|
197450
|
-
|
|
197451
|
-
|
|
197776
|
+
const backupId = requiredBackupId(backup_id ?? `logical-full_${projectRef}_${crypto.randomUUID().replaceAll("-", "")}`, projectRef);
|
|
197777
|
+
const state = { project_ref: projectRef, backup_id: backupId };
|
|
197778
|
+
let mutation;
|
|
197779
|
+
try {
|
|
197780
|
+
mutation = await http.postReleaseMutation(`${endpoint(projectRef)}/database/backups/logical`, { backup_id: backupId }, { timeoutMs: BACKUP_TIMEOUT_MS });
|
|
197781
|
+
} catch {
|
|
197782
|
+
mutation = { ok: false, status: 0, data: null, transportError: true };
|
|
197783
|
+
}
|
|
197452
197784
|
if (!mutation.ok || mutation.status !== 200) {
|
|
197453
|
-
|
|
197785
|
+
if (mutationOutcomeUnknown(mutation)) {
|
|
197786
|
+
const observed = await readBackup(http, projectRef, backupId);
|
|
197787
|
+
if (observed.backup)
|
|
197788
|
+
return releaseControlFailure("release.logical_backup.create", "OUTCOME_UNKNOWN", mutation.transportError ? null : mutation.status, {
|
|
197789
|
+
...state,
|
|
197790
|
+
backup: publicBackup(observed.backup),
|
|
197791
|
+
backup_verified: true,
|
|
197792
|
+
creation_confirmed: false
|
|
197793
|
+
});
|
|
197794
|
+
}
|
|
197795
|
+
return mutationFailure("release.logical_backup.create", mutation, state);
|
|
197454
197796
|
}
|
|
197455
197797
|
const responseBackup = isRecord6(mutation.data) ? verifiedBackup(mutation.data.backup, projectRef) : null;
|
|
197456
|
-
const
|
|
197457
|
-
|
|
197458
|
-
|
|
197459
|
-
return releaseControlFailure("release.logical_backup.create", "OUTCOME_UNKNOWN", mutation.status);
|
|
197798
|
+
const after = await readBackup(http, projectRef, backupId);
|
|
197799
|
+
if (!responseBackup || responseBackup.backup_id !== backupId || !after.backup || !equalBackup(responseBackup, after.backup)) {
|
|
197800
|
+
return releaseControlFailure("release.logical_backup.create", "OUTCOME_UNKNOWN", mutation.status, state);
|
|
197460
197801
|
}
|
|
197461
197802
|
return releaseControlSuccess("release.logical_backup.create", {
|
|
197462
|
-
|
|
197463
|
-
backup: publicBackup(
|
|
197803
|
+
...state,
|
|
197804
|
+
backup: publicBackup(after.backup)
|
|
197464
197805
|
});
|
|
197465
197806
|
}
|
|
197807
|
+
if (action === "logical_backup_status") {
|
|
197808
|
+
const backupId = requiredBackupId(backup_id, projectRef);
|
|
197809
|
+
const read = await readBackup(http, projectRef, backupId);
|
|
197810
|
+
const state = { project_ref: projectRef, backup_id: backupId };
|
|
197811
|
+
if (!read.response.ok || read.response.status !== 200) {
|
|
197812
|
+
const code = read.response.responseReadError || read.response.status >= 200 && read.response.status < 300 ? "INVALID_RESPONSE" : "HTTP_ERROR";
|
|
197813
|
+
return releaseControlFailure("release.logical_backup.status", code, read.response.transportError ? null : read.response.status, state);
|
|
197814
|
+
}
|
|
197815
|
+
return read.backup ? releaseControlSuccess("release.logical_backup.status", { ...state, backup: publicBackup(read.backup) }) : releaseControlFailure("release.logical_backup.status", "INVALID_RESPONSE", read.response.status, state);
|
|
197816
|
+
}
|
|
197466
197817
|
if (action === "logical_backup_restore") {
|
|
197467
197818
|
const request = restoreRequest(projectRef, backup_id, expected_sha256, restore_confirmation);
|
|
197468
197819
|
const before = await readInventory(http, projectRef);
|
|
@@ -198994,7 +199345,7 @@ function registerRemoteDevTools(server2, options = {}) {
|
|
|
198994
199345
|
// package.json
|
|
198995
199346
|
var package_default = {
|
|
198996
199347
|
name: "@supacloud/cli",
|
|
198997
|
-
version: "0.
|
|
199348
|
+
version: "0.81.0",
|
|
198998
199349
|
description: "Project-scoped CLI for SupaCloud users",
|
|
198999
199350
|
type: "module",
|
|
199000
199351
|
main: "./dist/index.js",
|
|
@@ -199046,14 +199397,14 @@ var package_default = {
|
|
|
199046
199397
|
},
|
|
199047
199398
|
dependencies: {
|
|
199048
199399
|
"@supacloud/compiler": "^0.43.0",
|
|
199049
|
-
"@supacloud/delivery": "0.
|
|
199400
|
+
"@supacloud/delivery": "0.11.0",
|
|
199050
199401
|
"@supacloud/db": "0.16.0",
|
|
199051
199402
|
fflate: "^0.8.3",
|
|
199052
199403
|
typebox: "1.3.34"
|
|
199053
199404
|
},
|
|
199054
199405
|
overrides: {
|
|
199055
199406
|
"@supacloud/contracts": "0.8.0",
|
|
199056
|
-
"@supacloud/delivery": "0.
|
|
199407
|
+
"@supacloud/delivery": "0.11.0",
|
|
199057
199408
|
"@graphql-tools/utils": "^12.0.3"
|
|
199058
199409
|
},
|
|
199059
199410
|
devDependencies: {
|
|
@@ -199274,6 +199625,7 @@ EXAMPLES
|
|
|
199274
199625
|
${preferredCommand} project logs --log_type database
|
|
199275
199626
|
${preferredCommand} project task_stats
|
|
199276
199627
|
${preferredCommand} release logical_backup_create --ref abc123
|
|
199628
|
+
${preferredCommand} release logical_backup_status --ref abc123 --backup_id <backup_id>
|
|
199277
199629
|
${preferredCommand} release logical_backup_restore --ref abc123 --backup_id <backup_id> --expected_sha256 <sha256> --restore_confirmation RESTORE_PROJECT:abc123:<backup_id>:<sha256>
|
|
199278
199630
|
${preferredCommand} release postgrest_status --ref abc123
|
|
199279
199631
|
${preferredCommand} release postgrest_restart --ref abc123
|
|
@@ -199318,6 +199670,7 @@ EXAMPLES
|
|
|
199318
199670
|
${preferredCommand} app diff --ref abc123 --id orders --environment_id test --release_id <sha256> --configuration_id <uuid>
|
|
199319
199671
|
${preferredCommand} app deploy --ref abc123 --id orders --environment_id test --release_id <sha256> --configuration_id <uuid>
|
|
199320
199672
|
${preferredCommand} app status --ref abc123 --id orders --environment_id test
|
|
199673
|
+
${preferredCommand} --env production app promote-plan --id orders --environment_id production --source_ref staging --source_environment_id staging --source_release_id <sha256>
|
|
199321
199674
|
${preferredCommand} app preview-plan --ref abc123 --id orders --environment_id test --release_id <sha256> --branch_ref preview-orders
|
|
199322
199675
|
${preferredCommand} app preview --ref abc123 --id orders --environment_id test --release_id <sha256> --configuration_id <uuid> --ttl_seconds 3600 --wait
|
|
199323
199676
|
${preferredCommand} app previews --ref abc123 --id orders --environment_id test
|
|
@@ -2,6 +2,9 @@ import { type ToolServer } from "../tool-server";
|
|
|
2
2
|
import { type AppManifest } from "./app-tool-export";
|
|
3
3
|
declare const REMOTE_APP_ACTIONS: {
|
|
4
4
|
readonly upload: "upload_release";
|
|
5
|
+
readonly "transfer-plan": "get_release_transfer_plan";
|
|
6
|
+
readonly "promote-plan": "get_promotion_plan";
|
|
7
|
+
readonly transfer: "transfer_release";
|
|
5
8
|
readonly configure: "put_configuration";
|
|
6
9
|
readonly deploy: "deploy_release";
|
|
7
10
|
readonly "deploy-plan": "get_deploy_plan";
|
|
@@ -38,6 +41,9 @@ export interface AppToolArguments {
|
|
|
38
41
|
configuration_path?: string;
|
|
39
42
|
manifest_path?: string;
|
|
40
43
|
release_id?: string;
|
|
44
|
+
source_ref?: string;
|
|
45
|
+
source_release_id?: string;
|
|
46
|
+
source_environment_id?: string;
|
|
41
47
|
cursor?: string;
|
|
42
48
|
limit?: number;
|
|
43
49
|
preview_id?: string;
|
|
@@ -62,7 +68,7 @@ export interface AppToolArguments {
|
|
|
62
68
|
out_dir?: string;
|
|
63
69
|
strict?: boolean;
|
|
64
70
|
format?: "text" | "json";
|
|
65
|
-
json?:
|
|
71
|
+
json?: boolean;
|
|
66
72
|
target?: string;
|
|
67
73
|
fix?: string;
|
|
68
74
|
write?: boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HttpTransport } from "../transports/http";
|
|
2
|
+
export declare function applicationReleaseTransfer(http: HttpTransport, input: {
|
|
3
|
+
action: "get_release_transfer_plan" | "transfer_release";
|
|
4
|
+
ref: string;
|
|
5
|
+
id: string;
|
|
6
|
+
sourceRef: string;
|
|
7
|
+
sourceReleaseId: string;
|
|
8
|
+
}, project: string): Promise<import("./release-control-response").ReleaseControlToolResponse>;
|
|
@@ -9,7 +9,7 @@ export declare const APPLICATION_TOOL_SCHEMA: {
|
|
|
9
9
|
ttl_seconds: Type.TOptional<Type.TInteger>;
|
|
10
10
|
wait: Type.TOptional<Type.TBoolean>;
|
|
11
11
|
timeout_seconds: Type.TOptional<Type.TInteger>;
|
|
12
|
-
action: Type.TEnum<("list_releases" | "get_release" | "upload_release" | "activate_release" | "logs" | "get_runtime" | "get_deployment_evidence" | "get_configuration" | "get_rollback_snapshot" | "get_history" | "get_deploy_plan" | "get_preview_plan" | "put_configuration" | "deploy_release" | "rollback_release" | "reconcile_activation" | "retire_activation" | "create_preview" | "list_previews" | "get_preview" | "cleanup_preview")[]>;
|
|
12
|
+
action: Type.TEnum<("list_releases" | "get_release" | "upload_release" | "activate_release" | "logs" | "get_runtime" | "get_deployment_evidence" | "get_configuration" | "get_release_transfer_plan" | "get_promotion_plan" | "get_rollback_snapshot" | "get_history" | "get_deploy_plan" | "get_preview_plan" | "put_configuration" | "transfer_release" | "deploy_release" | "rollback_release" | "reconcile_activation" | "retire_activation" | "create_preview" | "list_previews" | "get_preview" | "cleanup_preview")[]>;
|
|
13
13
|
ref: Type.TString;
|
|
14
14
|
id: Type.TString;
|
|
15
15
|
environment_id: Type.TOptional<Type.TString>;
|
|
@@ -19,6 +19,9 @@ export declare const APPLICATION_TOOL_SCHEMA: {
|
|
|
19
19
|
configuration_path: Type.TOptional<Type.TString>;
|
|
20
20
|
manifest_path: Type.TOptional<Type.TString>;
|
|
21
21
|
release_id: Type.TOptional<Type.TString>;
|
|
22
|
+
source_ref: Type.TOptional<Type.TString>;
|
|
23
|
+
source_release_id: Type.TOptional<Type.TString>;
|
|
24
|
+
source_environment_id: Type.TOptional<Type.TString>;
|
|
22
25
|
cursor: Type.TOptional<Type.TUnion<[Type.TString, Type.TString]>>;
|
|
23
26
|
limit: Type.TOptional<Type.TInteger>;
|
|
24
27
|
offset: Type.TOptional<Type.TInteger>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.0",
|
|
4
4
|
"description": "Project-scoped CLI for SupaCloud users",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -52,14 +52,14 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@supacloud/compiler": "^0.43.0",
|
|
55
|
-
"@supacloud/delivery": "0.
|
|
55
|
+
"@supacloud/delivery": "0.11.0",
|
|
56
56
|
"@supacloud/db": "0.16.0",
|
|
57
57
|
"fflate": "^0.8.3",
|
|
58
58
|
"typebox": "1.3.34"
|
|
59
59
|
},
|
|
60
60
|
"overrides": {
|
|
61
61
|
"@supacloud/contracts": "0.8.0",
|
|
62
|
-
"@supacloud/delivery": "0.
|
|
62
|
+
"@supacloud/delivery": "0.11.0",
|
|
63
63
|
"@graphql-tools/utils": "^12.0.3"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|