@supacloud/cli 0.90.2 → 0.92.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
CHANGED
|
@@ -10553,10 +10553,29 @@ var ApplicationDeployResultSchema = _Object_({
|
|
|
10553
10553
|
replayed: Boolean2()
|
|
10554
10554
|
}, closed3))
|
|
10555
10555
|
}, closed3);
|
|
10556
|
-
function parseApplicationDeployResult(value) {
|
|
10556
|
+
function parseApplicationDeployResult(value, expected) {
|
|
10557
10557
|
if (!Check2(ApplicationDeployResultSchema, value)) {
|
|
10558
10558
|
throw new Error("Invalid application deploy result");
|
|
10559
10559
|
}
|
|
10560
|
+
const backend = value.backend_receipt;
|
|
10561
|
+
const bindingKeys = [
|
|
10562
|
+
"project_ref",
|
|
10563
|
+
"application_id",
|
|
10564
|
+
"environment_id",
|
|
10565
|
+
"release_id",
|
|
10566
|
+
"mutation_id",
|
|
10567
|
+
"plan_sha256"
|
|
10568
|
+
];
|
|
10569
|
+
if (bindingKeys.some((key) => backend[key] !== value[key]) || !backend.postgrest_healthy || backend.applied_versions.length > 0 && (!backend.schema_reloaded || !backend.backup_id)) {
|
|
10570
|
+
throw new Error("Invalid application deploy receipt binding");
|
|
10571
|
+
}
|
|
10572
|
+
if (expected) {
|
|
10573
|
+
const { request } = expected;
|
|
10574
|
+
const configurationId = request.configuration_id ?? request.configuration_write?.configuration_id;
|
|
10575
|
+
if (value.project_ref !== expected.project_ref || value.application_id !== expected.application_id || value.environment_id !== expected.environment_id || value.release_id !== request.release_id || value.mutation_id !== request.mutation_id || configurationId !== undefined && value.configuration_id !== configurationId || request.configuration_write !== undefined && value.configuration_id !== request.configuration_write.configuration_id || request.expected_plan_sha256 !== undefined && value.plan_sha256 !== request.expected_plan_sha256 || Boolean(value.web) !== Boolean(request.web) || request.web !== undefined && (value.web?.deployment_id !== request.web.deployment_id || value.web.release_id !== request.web.release_id)) {
|
|
10576
|
+
throw new Error("Application deploy receipt does not match request");
|
|
10577
|
+
}
|
|
10578
|
+
}
|
|
10560
10579
|
return structuredClone(value);
|
|
10561
10580
|
}
|
|
10562
10581
|
// node_modules/@supacloud/delivery/src/application-history.ts
|
|
@@ -25509,6 +25528,8 @@ function releasePhaseFailure(phase, result, completed) {
|
|
|
25509
25528
|
};
|
|
25510
25529
|
if (phaseResult?.activation_id !== undefined)
|
|
25511
25530
|
payload.activation_id = phaseResult.activation_id;
|
|
25531
|
+
if (typeof phaseResult?.deployment_id === "string")
|
|
25532
|
+
payload.deployment_id = phaseResult.deployment_id;
|
|
25512
25533
|
if (phaseResult?.expected_activation_id !== undefined) {
|
|
25513
25534
|
payload.expected_activation_id = phaseResult.expected_activation_id;
|
|
25514
25535
|
}
|
|
@@ -25612,7 +25633,7 @@ async function runAppRelease(request, options, delegate) {
|
|
|
25612
25633
|
...request.web === undefined ? {} : { web: request.web }
|
|
25613
25634
|
};
|
|
25614
25635
|
validateToolArguments(APPLICATION_TOOL_SCHEMA, deployArgs);
|
|
25615
|
-
const deployed = await delegate(deployArgs);
|
|
25636
|
+
const deployed = options.projectDeployment ? await options.projectDeployment(deployArgs) : await delegate(deployArgs);
|
|
25616
25637
|
const deployReceipt = resultObject(deployed);
|
|
25617
25638
|
const resolvedConfigurationId = typeof deployReceipt?.configuration_id === "string" ? deployReceipt.configuration_id : undefined;
|
|
25618
25639
|
if (resolvedConfigurationId !== undefined) {
|
|
@@ -25632,6 +25653,7 @@ async function runAppRelease(request, options, delegate) {
|
|
|
25632
25653
|
application_id: id,
|
|
25633
25654
|
environment_id: environmentId,
|
|
25634
25655
|
release_id: releaseId,
|
|
25656
|
+
...typeof deployReceipt.deployment_id === "string" ? { deployment_id: deployReceipt.deployment_id } : {},
|
|
25635
25657
|
...configurationId === undefined ? {} : { configuration_id: configurationId },
|
|
25636
25658
|
manifest_path: manifestPath,
|
|
25637
25659
|
build: buildReceipt ? completed.build : { skipped: true, manifest_path: manifestPath },
|
|
@@ -25646,6 +25668,7 @@ async function runAppRelease(request, options, delegate) {
|
|
|
25646
25668
|
`Release: ${releaseId}`,
|
|
25647
25669
|
`Configuration: ${configurationId}`,
|
|
25648
25670
|
`Activation: ${result.activation_id ?? "not performed"}`,
|
|
25671
|
+
...result.deployment_id === undefined ? [] : [`Deployment: ${result.deployment_id}`],
|
|
25649
25672
|
`Build: ${request.skip_build ? "skipped" : "completed"}`
|
|
25650
25673
|
].join(`
|
|
25651
25674
|
`));
|
|
@@ -28163,12 +28186,35 @@ async function readDeployConfig(configRoot) {
|
|
|
28163
28186
|
return {};
|
|
28164
28187
|
const config = record4(candidate);
|
|
28165
28188
|
const frontend = record4(config?.frontend);
|
|
28189
|
+
const application = record4(config?.application);
|
|
28166
28190
|
const targetValues = record4(config?.targets);
|
|
28167
|
-
if (!config || config.frontend !== undefined && !frontend || config.targets !== undefined && !targetValues) {
|
|
28168
|
-
throw new Error("supacloud.json must contain object-valued 'frontend' or 'targets' configuration");
|
|
28191
|
+
if (!config || config.frontend !== undefined && !frontend || config.application !== undefined && !application || config.targets !== undefined && !targetValues) {
|
|
28192
|
+
throw new Error("supacloud.json must contain object-valued 'application', 'frontend' or 'targets' configuration");
|
|
28193
|
+
}
|
|
28194
|
+
let parsedApplication;
|
|
28195
|
+
if (application) {
|
|
28196
|
+
const id = optionalText(application.id, "application.id");
|
|
28197
|
+
const environmentId = optionalText(application.environmentId, "application.environmentId");
|
|
28198
|
+
if (!id || !Check2(ApplicationIdSchema, id) || environmentId !== undefined && !Check2(ApplicationIdSchema, environmentId)) {
|
|
28199
|
+
throw new Error("supacloud.json requires a valid application.id and optional application.environmentId");
|
|
28200
|
+
}
|
|
28201
|
+
const configurationPath = optionalText(application.configurationPath, "application.configurationPath");
|
|
28202
|
+
const configurationId = optionalText(application.configurationId, "application.configurationId");
|
|
28203
|
+
if (configurationPath && configurationId) {
|
|
28204
|
+
throw new Error("supacloud.json application must set only one of application.configurationPath or application.configurationId");
|
|
28205
|
+
}
|
|
28206
|
+
const manifestPath = optionalText(application.manifestPath, "application.manifestPath");
|
|
28207
|
+
parsedApplication = {
|
|
28208
|
+
id,
|
|
28209
|
+
...environmentId ? { environmentId } : {},
|
|
28210
|
+
...configurationPath ? { configurationPath } : {},
|
|
28211
|
+
...configurationId ? { configurationId } : {},
|
|
28212
|
+
...manifestPath ? { manifestPath } : {}
|
|
28213
|
+
};
|
|
28169
28214
|
}
|
|
28170
28215
|
return {
|
|
28171
28216
|
defaultTarget: optionalText(config.defaultTarget, "defaultTarget"),
|
|
28217
|
+
...parsedApplication === undefined ? {} : { application: parsedApplication },
|
|
28172
28218
|
targets: targetValues ? Object.fromEntries(Object.entries(targetValues).map(([name, value]) => [name, deployTargetConfig(value, name)])) : undefined,
|
|
28173
28219
|
frontend: frontend ? {
|
|
28174
28220
|
id: optionalText(frontend.id, "frontend.id"),
|
|
@@ -28186,6 +28232,25 @@ async function packageName(projectDirectory) {
|
|
|
28186
28232
|
async function pathExists(path) {
|
|
28187
28233
|
return access(path).then(() => true, () => false);
|
|
28188
28234
|
}
|
|
28235
|
+
async function hasProjectReleaseManifest(root) {
|
|
28236
|
+
if (await pathExists(join17(root, "deploy-manifest.json")))
|
|
28237
|
+
return true;
|
|
28238
|
+
const directory = await opendir(join17(root, "supacloud")).catch((error) => {
|
|
28239
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
28240
|
+
return null;
|
|
28241
|
+
throw error;
|
|
28242
|
+
});
|
|
28243
|
+
if (!directory)
|
|
28244
|
+
return false;
|
|
28245
|
+
let entries = 0;
|
|
28246
|
+
for await (const entry of directory) {
|
|
28247
|
+
if (++entries > 128)
|
|
28248
|
+
throw new Error("Too many SupaCloud project roots to resolve a deployment");
|
|
28249
|
+
if ((entry.isDirectory() || entry.isSymbolicLink()) && await pathExists(join17(root, "supacloud", entry.name, "deploy-manifest.json")))
|
|
28250
|
+
return true;
|
|
28251
|
+
}
|
|
28252
|
+
return false;
|
|
28253
|
+
}
|
|
28189
28254
|
async function defaultBuildCommand(projectDirectory, configRoot) {
|
|
28190
28255
|
const packageJson = record4(await readJson(join17(projectDirectory, "package.json")));
|
|
28191
28256
|
const scripts = record4(packageJson?.scripts);
|
|
@@ -28457,16 +28522,10 @@ async function stageApplicationWebRelease(http, projectRef, configRoot, skipBuil
|
|
|
28457
28522
|
const remoteOutput = selected.outputDirectory && selected.outputDirectory !== "." ? selected.outputDirectory : "";
|
|
28458
28523
|
const outputDirectory = resolveInsideProject(projectDirectory, configuredOutput || remoteOutput || defaultOutputDirectory(selected.framework));
|
|
28459
28524
|
const build = createBuildEnvironment(target.config.buildEnv, environmentVariables);
|
|
28460
|
-
const report = phaseReporter(json);
|
|
28461
|
-
report("web", `${target.name} (${selected.id})`);
|
|
28462
28525
|
assertBuildEnvironment(build, skipBuild);
|
|
28463
28526
|
if (!skipBuild) {
|
|
28464
|
-
report("build", buildCommand);
|
|
28465
28527
|
await runBuild(buildCommand, projectDirectory, build.env, json);
|
|
28466
|
-
} else {
|
|
28467
|
-
report("build", "skipped");
|
|
28468
28528
|
}
|
|
28469
|
-
report("package", relative10(projectDirectory, outputDirectory) || ".");
|
|
28470
28529
|
const archive = await createFrontendArchive(outputDirectory);
|
|
28471
28530
|
try {
|
|
28472
28531
|
const authority = payload(await readFrontendReleaseAuthority(http, projectRef, selected.id));
|
|
@@ -28477,10 +28536,8 @@ async function stageApplicationWebRelease(http, projectRef, configRoot, skipBuil
|
|
|
28477
28536
|
throw new Error("Active Web release tree does not match the local archive");
|
|
28478
28537
|
}
|
|
28479
28538
|
if (archive.treeSha256 === current?.tree_sha256) {
|
|
28480
|
-
report("done", "Web already current");
|
|
28481
28539
|
return {};
|
|
28482
28540
|
}
|
|
28483
|
-
report("upload", `${archive.fileCount} files`);
|
|
28484
28541
|
const uploaded = payload(await uploadFrontendRelease(http, projectRef, selected.id, archive.archivePath));
|
|
28485
28542
|
const release = record4(uploaded.release);
|
|
28486
28543
|
const releaseId = requiredString(release?.release_id, "release.release_id");
|
|
@@ -28488,7 +28545,6 @@ async function stageApplicationWebRelease(http, projectRef, configRoot, skipBuil
|
|
|
28488
28545
|
throw new Error("Uploaded Web release identity does not match the local archive");
|
|
28489
28546
|
if (release?.tree_sha256 !== archive.treeSha256)
|
|
28490
28547
|
throw new Error("Uploaded Web release tree does not match the local build");
|
|
28491
|
-
report("done", releaseId.slice(0, 12));
|
|
28492
28548
|
return {
|
|
28493
28549
|
request: {
|
|
28494
28550
|
deployment_id: selected.id,
|
|
@@ -28502,14 +28558,6 @@ async function stageApplicationWebRelease(http, projectRef, configRoot, skipBuil
|
|
|
28502
28558
|
await archive.cleanup();
|
|
28503
28559
|
}
|
|
28504
28560
|
}
|
|
28505
|
-
function phaseReporter(json) {
|
|
28506
|
-
const started = performance.now();
|
|
28507
|
-
return (phase, detail) => {
|
|
28508
|
-
if (!json)
|
|
28509
|
-
console.error(` ${phase.padEnd(10)} ${detail}`);
|
|
28510
|
-
return Math.round(performance.now() - started);
|
|
28511
|
-
};
|
|
28512
|
-
}
|
|
28513
28561
|
function deployResponse(result, json) {
|
|
28514
28562
|
if (json)
|
|
28515
28563
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -28518,14 +28566,13 @@ function deployResponse(result, json) {
|
|
|
28518
28566
|
result.url ? `URL: ${String(result.url)}` : null,
|
|
28519
28567
|
result.release_id ? `Release: ${String(result.release_id)}` : null,
|
|
28520
28568
|
result.active_version ? `Active version: ${String(result.active_version)}` : null,
|
|
28521
|
-
result.file_count ? `Files: ${String(result.file_count)}` : null
|
|
28522
|
-
`Duration: ${(Number(result.duration_ms) / 1000).toFixed(1)}s`
|
|
28569
|
+
result.file_count ? `Files: ${String(result.file_count)}` : null
|
|
28523
28570
|
].filter((line) => Boolean(line));
|
|
28524
28571
|
return { content: [{ type: "text", text: lines.join(`
|
|
28525
28572
|
`) }] };
|
|
28526
28573
|
}
|
|
28527
28574
|
function registerDeployTools(server, http, options = {}) {
|
|
28528
|
-
registerTool(server, "deploy", "Build and publish the linked
|
|
28575
|
+
registerTool(server, "deploy", "Build and publish the linked project with one command", deployToolSchema, async (args) => {
|
|
28529
28576
|
if (args.dry_run === true && (args.plan === true || args.diff === true)) {
|
|
28530
28577
|
throw new Error("Use dry_run for resolution only, or plan/diff for a built release comparison, not both");
|
|
28531
28578
|
}
|
|
@@ -28533,8 +28580,19 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28533
28580
|
const projectRef = String(args.ref || options.projectRef || "").trim();
|
|
28534
28581
|
if (!projectRef)
|
|
28535
28582
|
throw new Error("A linked project ref is required. Set SUPACLOUD_PROJECT_REF or pass --ref");
|
|
28536
|
-
const configRoot = await findDeployConfigRoot(invocationDirectory);
|
|
28537
|
-
|
|
28583
|
+
const configRoot = await findDeployConfigRoot(invocationDirectory, options.environment ?? undefined);
|
|
28584
|
+
const config = await readDeployConfig(configRoot);
|
|
28585
|
+
const application = options.application?.id ? options.application : config.application ? {
|
|
28586
|
+
...config.application,
|
|
28587
|
+
environmentId: options.environment || config.application.environmentId
|
|
28588
|
+
} : undefined;
|
|
28589
|
+
if (!args.target && await hasProjectReleaseManifest(configRoot)) {
|
|
28590
|
+
throw new Error("PROJECT_DEPLOYMENT_UNSUPPORTED: This project declares a release manifest that the current deployment orchestrator cannot execute. No Web-only fallback or remote mutation was performed.");
|
|
28591
|
+
}
|
|
28592
|
+
if (application?.id) {
|
|
28593
|
+
if (Object.values(config.targets ?? {}).some((target) => target.type === "edge_function")) {
|
|
28594
|
+
throw new Error("PROJECT_DEPLOYMENT_UNSUPPORTED: Application deployment cannot yet include declared Edge Function targets. No partial deployment was performed.");
|
|
28595
|
+
}
|
|
28538
28596
|
if (!options.applicationRelease) {
|
|
28539
28597
|
throw new Error("Application release support is unavailable in this CLI context");
|
|
28540
28598
|
}
|
|
@@ -28544,12 +28602,12 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28544
28602
|
if (args.dry_run === true || args.plan === true || args.diff === true) {
|
|
28545
28603
|
throw new Error("Application profile deploy does not support dry_run, plan, or diff; use app deploy-plan for a read-only plan");
|
|
28546
28604
|
}
|
|
28547
|
-
const environmentId =
|
|
28548
|
-
if (!environmentId) {
|
|
28605
|
+
const environmentId = application.environmentId?.trim();
|
|
28606
|
+
if (!environmentId || !Check2(ApplicationIdSchema, environmentId) || !Check2(ApplicationIdSchema, application.id)) {
|
|
28549
28607
|
throw new Error("Application profile requires SUPACLOUD_APPLICATION_ENVIRONMENT_ID or SUPACLOUD_ENV");
|
|
28550
28608
|
}
|
|
28551
|
-
const configurationPath =
|
|
28552
|
-
const configurationId =
|
|
28609
|
+
const configurationPath = application.configurationPath?.trim();
|
|
28610
|
+
const configurationId = application.configurationId?.trim();
|
|
28553
28611
|
if (configurationPath && configurationId) {
|
|
28554
28612
|
throw new Error("Application profile must set only one of SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
|
|
28555
28613
|
}
|
|
@@ -28557,20 +28615,22 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28557
28615
|
const result = await options.applicationRelease({
|
|
28558
28616
|
action: "release",
|
|
28559
28617
|
ref: projectRef,
|
|
28560
|
-
id:
|
|
28618
|
+
id: application.id,
|
|
28561
28619
|
environment_id: environmentId,
|
|
28562
28620
|
root: configRoot,
|
|
28563
28621
|
...configurationPath ? { configuration_path: configurationPath } : {},
|
|
28564
28622
|
...configurationId ? { configuration_id: configurationId } : {},
|
|
28565
|
-
...
|
|
28623
|
+
...application.manifestPath ? { manifest_path: application.manifestPath } : {},
|
|
28566
28624
|
...args.skip_build === true ? { skip_build: true } : {},
|
|
28567
28625
|
...stagedWeb?.request === undefined ? {} : { web: stagedWeb.request },
|
|
28568
28626
|
...args.json === true ? { format: "json", json: true } : {}
|
|
28569
28627
|
});
|
|
28570
28628
|
return result;
|
|
28571
28629
|
}
|
|
28572
|
-
const config = await readDeployConfig(configRoot);
|
|
28573
28630
|
const target = selectTarget(configRoot, invocationDirectory, config, String(args.target || "").trim() || undefined);
|
|
28631
|
+
if (!args.target && target.config.type === "frontend" && (Object.values(config.targets ?? {}).some((candidate) => candidate.type === "edge_function") || await pathExists(join17(configRoot, "supacloud.config.ts")) || await pathExists(join17(configRoot, "supabase", "migrations")) || await pathExists(join17(configRoot, "supabase", "functions")))) {
|
|
28632
|
+
throw new Error("PROJECT_DEPLOYMENT_UNSUPPORTED: Backend declarations were detected but are not covered by this deployment. No Web-only fallback or remote mutation was performed.");
|
|
28633
|
+
}
|
|
28574
28634
|
const projectDirectory = target.root;
|
|
28575
28635
|
const targetState = await lstat5(projectDirectory).catch(() => null);
|
|
28576
28636
|
if (!targetState?.isDirectory() || targetState.isSymbolicLink()) {
|
|
@@ -28578,8 +28638,6 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28578
28638
|
}
|
|
28579
28639
|
const requestedId = String(args.id || target.config.id || "").trim() || undefined;
|
|
28580
28640
|
const build = createBuildEnvironment(target.config.buildEnv, options.environmentVariables);
|
|
28581
|
-
const report = phaseReporter(args.json === true);
|
|
28582
|
-
report("inspect", `${target.name} (${projectRef})`);
|
|
28583
28641
|
if (target.config.type === "edge_function") {
|
|
28584
28642
|
if (args.diff === true)
|
|
28585
28643
|
throw new Error("Function release diffs require an immutable function release manifest");
|
|
@@ -28624,12 +28682,8 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28624
28682
|
}
|
|
28625
28683
|
assertBuildEnvironment(build, args.skip_build === true);
|
|
28626
28684
|
if (args.skip_build !== true) {
|
|
28627
|
-
report("build", buildCommand);
|
|
28628
28685
|
await runBuild(buildCommand, projectDirectory, build.env, args.json === true);
|
|
28629
|
-
} else {
|
|
28630
|
-
report("build", "skipped");
|
|
28631
28686
|
}
|
|
28632
|
-
report("deploy", `edge function ${slug}`);
|
|
28633
28687
|
const deployed = await options.edgeFunctionDeploy({
|
|
28634
28688
|
action: "deploy_bundle",
|
|
28635
28689
|
ref: projectRef,
|
|
@@ -28646,7 +28700,6 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28646
28700
|
return deployed;
|
|
28647
28701
|
const deployedText = deployed.content.find((chunk) => chunk.type === "text")?.text;
|
|
28648
28702
|
const deployedPayload = deployedText ? record4(JSON.parse(deployedText)) : null;
|
|
28649
|
-
const durationMs = report("done", slug);
|
|
28650
28703
|
return deployResponse({
|
|
28651
28704
|
ok: true,
|
|
28652
28705
|
unchanged: false,
|
|
@@ -28655,8 +28708,7 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28655
28708
|
project_ref: projectRef,
|
|
28656
28709
|
slug,
|
|
28657
28710
|
active_version: deployedPayload?.active_version,
|
|
28658
|
-
activation_id: deployedPayload?.activation_id
|
|
28659
|
-
duration_ms: durationMs
|
|
28711
|
+
activation_id: deployedPayload?.activation_id
|
|
28660
28712
|
}, args.json === true);
|
|
28661
28713
|
}
|
|
28662
28714
|
const listResponse = await http.get(`/v1/projects/${encodeURIComponent(projectRef)}/frontend/deployments`);
|
|
@@ -28682,12 +28734,8 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28682
28734
|
}
|
|
28683
28735
|
assertBuildEnvironment(build, args.skip_build === true);
|
|
28684
28736
|
if (args.skip_build !== true) {
|
|
28685
|
-
report("build", buildCommand);
|
|
28686
28737
|
await runBuild(buildCommand, projectDirectory, build.env, args.json === true);
|
|
28687
|
-
} else {
|
|
28688
|
-
report("build", "skipped");
|
|
28689
28738
|
}
|
|
28690
|
-
report("package", relative10(projectDirectory, outputDirectory) || ".");
|
|
28691
28739
|
const archive = await createFrontendArchive(outputDirectory);
|
|
28692
28740
|
try {
|
|
28693
28741
|
const inventory = payload(await readFrontendReleaseAuthority(http, projectRef, selected.id));
|
|
@@ -28699,7 +28747,6 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28699
28747
|
}
|
|
28700
28748
|
const unchanged = archive.treeSha256 === current?.tree_sha256;
|
|
28701
28749
|
if (args.plan === true || args.diff === true) {
|
|
28702
|
-
const durationMs = report("done", unchanged ? "unchanged plan" : "release diff ready");
|
|
28703
28750
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
28704
28751
|
schema: "supacloud.frontend-deploy-plan.v1",
|
|
28705
28752
|
ok: true,
|
|
@@ -28722,12 +28769,10 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28722
28769
|
actions: unchanged ? [] : ["upload_release", "activate_release"],
|
|
28723
28770
|
file_count: archive.fileCount,
|
|
28724
28771
|
source_bytes: archive.sourceBytes,
|
|
28725
|
-
archive_bytes: archive.archiveBytes
|
|
28726
|
-
duration_ms: durationMs
|
|
28772
|
+
archive_bytes: archive.archiveBytes
|
|
28727
28773
|
}, null, 2) }] };
|
|
28728
28774
|
}
|
|
28729
28775
|
if (unchanged) {
|
|
28730
|
-
const durationMs = report("done", "already current");
|
|
28731
28776
|
return deployResponse({
|
|
28732
28777
|
ok: true,
|
|
28733
28778
|
unchanged: true,
|
|
@@ -28737,11 +28782,9 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28737
28782
|
tree_sha256: archive.treeSha256,
|
|
28738
28783
|
url: selected.deploymentUrl,
|
|
28739
28784
|
file_count: archive.fileCount,
|
|
28740
|
-
source_bytes: archive.sourceBytes
|
|
28741
|
-
duration_ms: durationMs
|
|
28785
|
+
source_bytes: archive.sourceBytes
|
|
28742
28786
|
}, args.json === true);
|
|
28743
28787
|
}
|
|
28744
|
-
report("upload", `${archive.fileCount} files`);
|
|
28745
28788
|
const uploaded = payload(await uploadFrontendRelease(http, projectRef, selected.id, archive.archivePath));
|
|
28746
28789
|
const release = record4(uploaded.release);
|
|
28747
28790
|
const releaseId = requiredString(release?.release_id, "release.release_id");
|
|
@@ -28749,7 +28792,6 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28749
28792
|
throw new Error("Uploaded release identity does not match the local archive");
|
|
28750
28793
|
if (release?.tree_sha256 !== archive.treeSha256)
|
|
28751
28794
|
throw new Error("Uploaded release tree does not match the local build");
|
|
28752
|
-
report("activate", releaseId.slice(0, 12));
|
|
28753
28795
|
const mutationId = crypto.randomUUID();
|
|
28754
28796
|
const activationResponse = await activateFrontendRelease(http, {
|
|
28755
28797
|
projectRef,
|
|
@@ -28778,7 +28820,6 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28778
28820
|
const activated = payload(activationResponse);
|
|
28779
28821
|
const finalResponse = await http.get(`/v1/projects/${encodeURIComponent(projectRef)}/frontend/deployments/${encodeURIComponent(selected.id)}`);
|
|
28780
28822
|
const finalDeployment = finalResponse.ok ? frontendDeployment(finalResponse.data) : selected;
|
|
28781
|
-
const durationMs = report("done", finalDeployment.deploymentUrl || selected.id);
|
|
28782
28823
|
return deployResponse({
|
|
28783
28824
|
ok: true,
|
|
28784
28825
|
unchanged: false,
|
|
@@ -28790,8 +28831,7 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28790
28831
|
previous_activation_id: activeActivationId,
|
|
28791
28832
|
url: finalDeployment.deploymentUrl,
|
|
28792
28833
|
file_count: archive.fileCount,
|
|
28793
|
-
source_bytes: archive.sourceBytes
|
|
28794
|
-
duration_ms: durationMs
|
|
28834
|
+
source_bytes: archive.sourceBytes
|
|
28795
28835
|
}, args.json === true);
|
|
28796
28836
|
} finally {
|
|
28797
28837
|
await archive.cleanup();
|
|
@@ -29378,10 +29418,109 @@ function registerDeveloperExperienceTools(server, options = {}) {
|
|
|
29378
29418
|
registerTool(server, "init", "Create SupaCloud deployment configuration and non-secret environment examples without overwriting project files.", initToolSchema, (request) => initializeProject(request, options));
|
|
29379
29419
|
registerTool(server, "env", "List or locally validate named SupaCloud environment profiles without contacting the platform or printing secrets.", environmentToolSchema, (request) => inspectEnvironment(request, options));
|
|
29380
29420
|
}
|
|
29421
|
+
|
|
29422
|
+
// src/shared/tools/project-deployment.ts
|
|
29423
|
+
var operation = "applications.project_deploy";
|
|
29424
|
+
var scopeSchema = _Object_({
|
|
29425
|
+
project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
|
|
29426
|
+
application_id: ApplicationIdSchema,
|
|
29427
|
+
environment_id: ApplicationIdSchema
|
|
29428
|
+
});
|
|
29429
|
+
var statusSchema = _Object_({
|
|
29430
|
+
schema: Literal("supacloud.project-deployment-status.v1"),
|
|
29431
|
+
deployment_id: ApplicationDeployRequestSchema.properties.mutation_id,
|
|
29432
|
+
status: Union([
|
|
29433
|
+
Literal("pending"),
|
|
29434
|
+
Literal("running"),
|
|
29435
|
+
Literal("succeeded"),
|
|
29436
|
+
Literal("failed_retryable"),
|
|
29437
|
+
Literal("failed_terminal"),
|
|
29438
|
+
Literal("outcome_unknown")
|
|
29439
|
+
]),
|
|
29440
|
+
phase: Union([
|
|
29441
|
+
Literal("queued"),
|
|
29442
|
+
Literal("prepared"),
|
|
29443
|
+
Literal("migrating"),
|
|
29444
|
+
Literal("activating"),
|
|
29445
|
+
Literal("binding-web"),
|
|
29446
|
+
Literal("verifying")
|
|
29447
|
+
]),
|
|
29448
|
+
operation: Literal("application.release.deploy"),
|
|
29449
|
+
...scopeSchema.properties,
|
|
29450
|
+
receipt: Optional(Unknown()),
|
|
29451
|
+
failure_code: Optional(String2({ pattern: "^[A-Z][A-Z0-9_]{0,63}$" }))
|
|
29452
|
+
}, { additionalProperties: false });
|
|
29453
|
+
function responseReceipt(data, expected) {
|
|
29454
|
+
if (!data || typeof data !== "object" || Array.isArray(data) || !("ok" in data) || data.ok !== true) {
|
|
29455
|
+
throw new Error("Invalid project deployment response");
|
|
29456
|
+
}
|
|
29457
|
+
const { ok: _ok, ...receipt } = data;
|
|
29458
|
+
return parseApplicationDeployResult(receipt, expected);
|
|
29459
|
+
}
|
|
29460
|
+
async function runProjectDeployment(http, args) {
|
|
29461
|
+
const scope = { project_ref: args.ref, application_id: args.id, environment_id: args.environment_id };
|
|
29462
|
+
const request = {
|
|
29463
|
+
mutation_id: args.mutation_id,
|
|
29464
|
+
release_id: args.release_id,
|
|
29465
|
+
...args.configuration_id === undefined ? {} : { configuration_id: args.configuration_id },
|
|
29466
|
+
...args.configuration_write === undefined ? {} : { configuration_write: args.configuration_write },
|
|
29467
|
+
...args.expected_plan_sha256 === undefined ? {} : { expected_plan_sha256: args.expected_plan_sha256 },
|
|
29468
|
+
...args.approved_migration_digest === undefined ? {} : { approved_migration_digest: args.approved_migration_digest },
|
|
29469
|
+
...args.web === undefined ? {} : { web: args.web }
|
|
29470
|
+
};
|
|
29471
|
+
if (!Check2(scopeSchema, scope) || !Check2(ApplicationDeployRequestSchema, request) || request.configuration_id !== undefined && request.configuration_write !== undefined && request.configuration_id !== request.configuration_write.configuration_id) {
|
|
29472
|
+
return releaseControlFailure(operation, "INVALID_RESPONSE", null);
|
|
29473
|
+
}
|
|
29474
|
+
const expected = { ...scope, request };
|
|
29475
|
+
const identity = {
|
|
29476
|
+
...scope,
|
|
29477
|
+
deployment_id: request.mutation_id,
|
|
29478
|
+
mutation_id: request.mutation_id,
|
|
29479
|
+
release_id: request.release_id
|
|
29480
|
+
};
|
|
29481
|
+
const endpoint = `/v1/projects/${encodeURIComponent(scope.project_ref)}/environments/${encodeURIComponent(scope.environment_id)}/deployments`;
|
|
29482
|
+
const { mutation_id, ...candidate } = request;
|
|
29483
|
+
const result = await http.post(endpoint, {
|
|
29484
|
+
...candidate,
|
|
29485
|
+
deployment_id: mutation_id,
|
|
29486
|
+
application_id: scope.application_id
|
|
29487
|
+
}, { timeoutMs: 120000, maxJsonBytes: 131072, responseTimeoutMs: 30000 });
|
|
29488
|
+
if (result.ok && result.status === 200) {
|
|
29489
|
+
try {
|
|
29490
|
+
return releaseControlSuccess(operation, {
|
|
29491
|
+
...responseReceipt(result.data, expected),
|
|
29492
|
+
deployment_id: mutation_id
|
|
29493
|
+
});
|
|
29494
|
+
} catch {}
|
|
29495
|
+
} else if (!result.ok && !result.transportError && !result.responseReadError && result.status < 500 && result.status !== 408) {
|
|
29496
|
+
return releaseControlMutationFailure(operation, result, identity);
|
|
29497
|
+
}
|
|
29498
|
+
const observed = await http.get(`${endpoint}/${encodeURIComponent(mutation_id)}`, {
|
|
29499
|
+
retry: false,
|
|
29500
|
+
totalTimeoutMs: 30000,
|
|
29501
|
+
maxJsonBytes: 131072
|
|
29502
|
+
});
|
|
29503
|
+
try {
|
|
29504
|
+
if (!observed.ok || observed.status !== 200 || !Check2(statusSchema, observed.data))
|
|
29505
|
+
throw new Error;
|
|
29506
|
+
const state = observed.data;
|
|
29507
|
+
if (state.deployment_id !== mutation_id || state.project_ref !== scope.project_ref || state.application_id !== scope.application_id || state.environment_id !== scope.environment_id || state.status !== "succeeded" || state.phase !== "verifying")
|
|
29508
|
+
throw new Error;
|
|
29509
|
+
const receipt = parseApplicationDeployResult(state.receipt, expected);
|
|
29510
|
+
return releaseControlSuccess(operation, {
|
|
29511
|
+
...receipt,
|
|
29512
|
+
deployment_id: mutation_id,
|
|
29513
|
+
replayed: true,
|
|
29514
|
+
execution_performed: false
|
|
29515
|
+
});
|
|
29516
|
+
} catch {
|
|
29517
|
+
return releaseControlFailure(operation, "OUTCOME_UNKNOWN", result.transportError ? null : result.status, identity);
|
|
29518
|
+
}
|
|
29519
|
+
}
|
|
29381
29520
|
// package.json
|
|
29382
29521
|
var package_default = {
|
|
29383
29522
|
name: "@supacloud/cli",
|
|
29384
|
-
version: "0.
|
|
29523
|
+
version: "0.92.0",
|
|
29385
29524
|
description: "Project-scoped CLI for SupaCloud users",
|
|
29386
29525
|
type: "module",
|
|
29387
29526
|
main: "./dist/index.js",
|
|
@@ -29434,13 +29573,13 @@ var package_default = {
|
|
|
29434
29573
|
},
|
|
29435
29574
|
dependencies: {
|
|
29436
29575
|
"@supacloud/compiler": "^0.44.1",
|
|
29437
|
-
"@supacloud/delivery": "0.15.
|
|
29576
|
+
"@supacloud/delivery": "0.15.1",
|
|
29438
29577
|
"@supacloud/db": "0.16.1",
|
|
29439
29578
|
typebox: "1.3.36"
|
|
29440
29579
|
},
|
|
29441
29580
|
overrides: {
|
|
29442
29581
|
"@supacloud/contracts": "0.8.1",
|
|
29443
|
-
"@supacloud/delivery": "0.15.
|
|
29582
|
+
"@supacloud/delivery": "0.15.1",
|
|
29444
29583
|
"@graphql-tools/utils": "^12.0.3"
|
|
29445
29584
|
},
|
|
29446
29585
|
devDependencies: {
|
|
@@ -29996,7 +30135,8 @@ function createCliTools(context, confirmProduction) {
|
|
|
29996
30135
|
Object.assign(tools, captureTools((server) => registerAppTools(server, {
|
|
29997
30136
|
getApplications: () => tools.applications?.callback,
|
|
29998
30137
|
onDevProgress: writeDevProgress,
|
|
29999
|
-
projectRef: context.projectRef || undefined
|
|
30138
|
+
projectRef: context.projectRef || undefined,
|
|
30139
|
+
projectDeployment: (args) => runProjectDeployment(http, args)
|
|
30000
30140
|
})));
|
|
30001
30141
|
assign(captureTools((server) => registerDeployTools(server, http, {
|
|
30002
30142
|
environment: context.environment,
|
|
@@ -30,6 +30,7 @@ declare const REMOTE_APP_ACTIONS: {
|
|
|
30
30
|
};
|
|
31
31
|
export interface AppToolOptions {
|
|
32
32
|
getApplications?: () => ((args: Record<string, unknown>) => Promise<ToolResult>) | undefined;
|
|
33
|
+
projectDeployment?: (args: Record<string, unknown>) => Promise<ToolResult>;
|
|
33
34
|
projectRef?: string;
|
|
34
35
|
/** Abort signal for long-running local actions (`app dev` watch), used for cancellation and tests. */
|
|
35
36
|
signal?: AbortSignal;
|
|
@@ -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.92.0",
|
|
4
4
|
"description": "Project-scoped CLI for SupaCloud users",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@supacloud/compiler": "^0.44.1",
|
|
56
|
-
"@supacloud/delivery": "0.15.
|
|
56
|
+
"@supacloud/delivery": "0.15.1",
|
|
57
57
|
"@supacloud/db": "0.16.1",
|
|
58
58
|
"typebox": "1.3.36"
|
|
59
59
|
},
|
|
60
60
|
"overrides": {
|
|
61
61
|
"@supacloud/contracts": "0.8.1",
|
|
62
|
-
"@supacloud/delivery": "0.15.
|
|
62
|
+
"@supacloud/delivery": "0.15.1",
|
|
63
63
|
"@graphql-tools/utils": "^12.0.3"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|