@supacloud/cli 0.88.0 → 0.90.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/README.md +29 -15
- package/dist/index.js +246 -79
- package/dist/shared/tools/app-tools.d.ts +2 -0
- package/dist/shared/tools/application-schemas.d.ts +42 -5
- package/dist/shared/tools/application-tools.d.ts +8 -1
- package/package.json +3 -3
- package/skills/supacloud/references/command-map.md +4 -4
package/README.md
CHANGED
|
@@ -284,9 +284,13 @@ supacloud --env production deploy --confirm-production production-ref
|
|
|
284
284
|
Test and production use the same workflow. Production safety is enforced by the
|
|
285
285
|
CLI confirmation gate; immutable releases, configuration revisions, backups,
|
|
286
286
|
readiness, CAS activation, rollback artifacts and uncertain-result
|
|
287
|
-
reconciliation belong to SupaCloud.
|
|
288
|
-
|
|
289
|
-
|
|
287
|
+
reconciliation belong to SupaCloud. `SUPACLOUD_APPLICATION_CONFIGURATION_PATH`
|
|
288
|
+
and `SUPACLOUD_APPLICATION_CONFIGURATION_ID` are optional pins; when neither
|
|
289
|
+
is supplied, SupaCloud reuses the current configuration head for the selected
|
|
290
|
+
environment. A first deployment still requires a configuration revision to
|
|
291
|
+
exist on the platform. The application layer only supplies its build output and
|
|
292
|
+
optional configuration file; it does not implement backup, activation or
|
|
293
|
+
downgrade orchestration.
|
|
290
294
|
|
|
291
295
|
CLI requests (including `status`) default to `SUPACLOUD_TLS_VERIFY=false`, skipping HTTPS certificate verification so that
|
|
292
296
|
air-gapped installations using private or self-signed certificates work
|
|
@@ -632,41 +636,51 @@ reporting success. Management endpoint projection is checked before and after.
|
|
|
632
636
|
### Release scope rebinding and inspection
|
|
633
637
|
|
|
634
638
|
Release scopes bind deployment manifests (functions, migrations, web assets) to
|
|
635
|
-
an exact 40-character base commit SHA
|
|
636
|
-
|
|
637
|
-
|
|
639
|
+
an exact 40-character base commit SHA and one concrete deployment environment.
|
|
640
|
+
Old scope files without `targetEnvironmentId` are rejected and must be
|
|
641
|
+
regenerated. Test and production scopes may share the same source commit, but
|
|
642
|
+
they are separate environment-bound release identities.
|
|
638
643
|
|
|
639
644
|
```bash
|
|
640
|
-
# Rebind a single scope
|
|
641
|
-
supacloud release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json
|
|
645
|
+
# Rebind a single environment-bound scope to latest git main (or origin/main)
|
|
646
|
+
supacloud release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json --environment_id production
|
|
642
647
|
|
|
643
|
-
# Rebind multiple
|
|
644
|
-
supacloud release scope_rebind --files "20260907-intake-p1-production.json,20260907-intake-p1-
|
|
648
|
+
# Rebind multiple production files together
|
|
649
|
+
supacloud release scope_rebind --files "20260907-intake-p1-production.json,20260907-intake-p1-production-hotfix.json" --environment_id production
|
|
650
|
+
|
|
651
|
+
# Rebind staging separately; a staging scope can never be rebound as production
|
|
652
|
+
supacloud release scope_rebind --task 20260907-intake-p1 --environment_id staging
|
|
645
653
|
|
|
646
654
|
# Rebind all scopes matching a task ID or filename prefix in standard directories
|
|
647
|
-
supacloud release scope_rebind --task 20260907-intake-p1
|
|
655
|
+
supacloud release scope_rebind --task 20260907-intake-p1 --environment_id production
|
|
648
656
|
|
|
649
657
|
# Specify an explicit base commit SHA or git ref
|
|
650
|
-
supacloud release scope_rebind --task 20260907-intake-p1 --base_commit origin/main
|
|
658
|
+
supacloud release scope_rebind --task 20260907-intake-p1 --base_commit origin/main --environment_id production
|
|
651
659
|
|
|
652
660
|
# Preview changes without modifying files
|
|
653
|
-
supacloud release scope_rebind --task 20260907-intake-p1 --dry_run
|
|
661
|
+
supacloud release scope_rebind --task 20260907-intake-p1 --environment_id production --dry_run
|
|
654
662
|
|
|
655
663
|
# Inspect scope status, SHA-256 hash, and alignment with git HEAD / origin/main
|
|
656
|
-
supacloud release scope_inspect --file 20260907-intake-p1-production.json
|
|
664
|
+
supacloud release scope_inspect --file 20260907-intake-p1-production.json --environment_id production
|
|
657
665
|
|
|
658
666
|
# Scaffold a new canonical release scope
|
|
659
667
|
supacloud release scope_create \
|
|
660
668
|
--file supacloud/fa/release-scopes/20260907-intake-p1.json \
|
|
661
669
|
--task FA-INTAKE-P1 \
|
|
662
670
|
--ref abc123 \
|
|
671
|
+
--environment_id production \
|
|
663
672
|
--functions "fa-api,fa-worker" \
|
|
664
673
|
--migrations "20260907120000_init"
|
|
665
674
|
```
|
|
666
675
|
|
|
667
676
|
Scope actions run locally without requiring Management API credentials. Files
|
|
668
677
|
are formatted canonically with 2 spaces and sorted keys, and the authoritative
|
|
669
|
-
`scope_sha256` checksum is recalculated automatically.
|
|
678
|
+
`scope_sha256` checksum is recalculated automatically. `scope_create` resolves
|
|
679
|
+
`origin/main` by default, so a new test or production scope is generated from
|
|
680
|
+
the current main baseline instead of reusing an older scope identity.
|
|
681
|
+
These commands are for release maintenance and audit workflows; customers do
|
|
682
|
+
not need to create or rebind a scope before running the one-command deployment
|
|
683
|
+
flow above.
|
|
670
684
|
|
|
671
685
|
The legacy `.env` fallback is unclassified and therefore does not enable the
|
|
672
686
|
production confirmation gate. Production automation must select a `prod` or
|
package/dist/index.js
CHANGED
|
@@ -9167,7 +9167,7 @@ var ACTION_POLICY = {
|
|
|
9167
9167
|
deploy: { write: ["deploy"] },
|
|
9168
9168
|
applications: {
|
|
9169
9169
|
read: ["list_releases", "get_release", "get_runtime", "get_deployment_evidence", "get_configuration", "get_release_transfer_plan", "get_promotion_plan", "get_promotion_status", "get_rollback_snapshot", "get_history", "get_deploy_plan", "logs", "get_preview_plan", "list_previews", "get_preview"],
|
|
9170
|
-
write: ["upload_release", "put_configuration", "
|
|
9170
|
+
write: ["upload_release", "put_configuration", "deploy_application", "transfer_release", "promote_application", "reconcile_promotion", "create_preview", "reconcile_preview", "cleanup_preview", "activate_release", "deploy_release", "rollback_release", "reconcile_activation", "retire_activation"]
|
|
9171
9171
|
},
|
|
9172
9172
|
project: {
|
|
9173
9173
|
read: ["get", "endpoints", "health", "logs", "api_keys", "settings", "tasks", "task_detail", "task_stats", "dlq", "background_settings"],
|
|
@@ -10493,34 +10493,67 @@ function parseApplicationDeployPlan(value) {
|
|
|
10493
10493
|
throw new Error("Invalid application deploy plan action");
|
|
10494
10494
|
return value;
|
|
10495
10495
|
}
|
|
10496
|
-
// node_modules/@supacloud/delivery/src/application-
|
|
10496
|
+
// node_modules/@supacloud/delivery/src/application-deploy-operation.ts
|
|
10497
10497
|
var closed3 = { additionalProperties: false };
|
|
10498
|
+
var sha256 = ApplicationReleaseIdSchema;
|
|
10498
10499
|
var nullableActivation = Union([ApplicationActivationIdSchema, Null()]);
|
|
10499
|
-
var
|
|
10500
|
-
|
|
10500
|
+
var ApplicationDeployWebRequestSchema = _Object_({
|
|
10501
|
+
deployment_id: String2({ pattern: "^[A-Za-z0-9_-]{1,128}$" }),
|
|
10502
|
+
release_id: sha256,
|
|
10503
|
+
expected_active_release_id: String2({ minLength: 1 }),
|
|
10504
|
+
expected_activation_id: String2({ minLength: 1 }),
|
|
10505
|
+
mutation_id: ApplicationActivationIdSchema
|
|
10506
|
+
}, closed3);
|
|
10507
|
+
var ApplicationDeployRequestSchema = _Object_({
|
|
10508
|
+
mutation_id: ApplicationActivationIdSchema,
|
|
10509
|
+
release_id: sha256,
|
|
10501
10510
|
configuration_id: Optional(ApplicationConfigurationIdSchema),
|
|
10502
10511
|
configuration_write: Optional(ApplicationConfigurationWriteSchema),
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10512
|
+
expected_plan_sha256: Optional(sha256),
|
|
10513
|
+
approved_migration_digest: Optional(sha256),
|
|
10514
|
+
web: Optional(ApplicationDeployWebRequestSchema)
|
|
10506
10515
|
}, closed3);
|
|
10507
|
-
var
|
|
10508
|
-
schema: Literal("supacloud.application-
|
|
10516
|
+
var ApplicationBackendReceiptSchema = _Object_({
|
|
10517
|
+
schema: Literal("supacloud.application-backend-receipt.v1"),
|
|
10509
10518
|
project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
|
|
10510
10519
|
application_id: ApplicationIdSchema,
|
|
10511
10520
|
environment_id: ApplicationIdSchema,
|
|
10512
|
-
release_id:
|
|
10521
|
+
release_id: sha256,
|
|
10522
|
+
manifest_sha256: sha256,
|
|
10523
|
+
mutation_id: ApplicationActivationIdSchema,
|
|
10524
|
+
plan_sha256: sha256,
|
|
10525
|
+
backup_id: Union([String2(), Null()]),
|
|
10526
|
+
before_ledger_digest: sha256,
|
|
10527
|
+
after_ledger_digest: sha256,
|
|
10528
|
+
applied_versions: _Array_(String2({ pattern: "^\\d{1,20}$" }), { uniqueItems: true }),
|
|
10529
|
+
schema_reloaded: Boolean2(),
|
|
10530
|
+
postgrest_healthy: Boolean2()
|
|
10531
|
+
}, closed3);
|
|
10532
|
+
var ApplicationDeployResultSchema = _Object_({
|
|
10533
|
+
schema: Literal("supacloud.application-deploy.v1"),
|
|
10534
|
+
project_ref: String2({ pattern: "^[a-z0-9-]{1,20}$" }),
|
|
10535
|
+
application_id: ApplicationIdSchema,
|
|
10536
|
+
environment_id: ApplicationIdSchema,
|
|
10537
|
+
mutation_id: ApplicationActivationIdSchema,
|
|
10538
|
+
release_id: sha256,
|
|
10513
10539
|
configuration_id: ApplicationConfigurationIdSchema,
|
|
10514
10540
|
activation_id: ApplicationActivationIdSchema,
|
|
10515
10541
|
expected_activation_id: nullableActivation,
|
|
10516
|
-
plan_sha256:
|
|
10542
|
+
plan_sha256: sha256,
|
|
10517
10543
|
action: Union([Literal("activate"), Literal("no-op")]),
|
|
10518
10544
|
replayed: Boolean2(),
|
|
10519
|
-
execution_performed: Boolean2()
|
|
10545
|
+
execution_performed: Boolean2(),
|
|
10546
|
+
backend_receipt: ApplicationBackendReceiptSchema,
|
|
10547
|
+
web: Optional(_Object_({
|
|
10548
|
+
deployment_id: String2({ pattern: "^[A-Za-z0-9_-]{1,128}$" }),
|
|
10549
|
+
release_id: sha256,
|
|
10550
|
+
activation_id: ApplicationActivationIdSchema,
|
|
10551
|
+
replayed: Boolean2()
|
|
10552
|
+
}, closed3))
|
|
10520
10553
|
}, closed3);
|
|
10521
|
-
function
|
|
10522
|
-
if (!Check2(
|
|
10523
|
-
throw new Error("Invalid application
|
|
10554
|
+
function parseApplicationDeployResult(value) {
|
|
10555
|
+
if (!Check2(ApplicationDeployResultSchema, value)) {
|
|
10556
|
+
throw new Error("Invalid application deploy result");
|
|
10524
10557
|
}
|
|
10525
10558
|
return structuredClone(value);
|
|
10526
10559
|
}
|
|
@@ -10721,7 +10754,7 @@ function parseApplicationReleaseTransferResult(candidate) {
|
|
|
10721
10754
|
}
|
|
10722
10755
|
// node_modules/@supacloud/delivery/src/deployment-evidence.ts
|
|
10723
10756
|
var closed5 = { additionalProperties: false };
|
|
10724
|
-
var
|
|
10757
|
+
var sha2562 = String2({ pattern: "^[a-f0-9]{64}$" });
|
|
10725
10758
|
var isoDate = String2({ minLength: 20, maxLength: 40 });
|
|
10726
10759
|
var status = Union([
|
|
10727
10760
|
Literal("confirmed"),
|
|
@@ -10741,7 +10774,7 @@ var DatabaseProviderSchema = _Object_({
|
|
|
10741
10774
|
topology: Union([Literal("single-node"), Literal("distributed")]),
|
|
10742
10775
|
migration: _Object_({
|
|
10743
10776
|
status,
|
|
10744
|
-
inventory_sha256: Union([
|
|
10777
|
+
inventory_sha256: Union([sha2562, Null()]),
|
|
10745
10778
|
compatibility: Union([
|
|
10746
10779
|
Literal("verified"),
|
|
10747
10780
|
Literal("not-proven"),
|
|
@@ -10789,9 +10822,9 @@ var DeploymentEvidenceSchema = _Object_({
|
|
|
10789
10822
|
}, closed5),
|
|
10790
10823
|
source: _Object_({
|
|
10791
10824
|
commit_sha: Union([String2({ pattern: "^[0-9a-f]{7,64}$" }), Null()]),
|
|
10792
|
-
manifest_sha256:
|
|
10825
|
+
manifest_sha256: sha2562,
|
|
10793
10826
|
contract_schema: Union([String2({ minLength: 1, maxLength: 128 }), Null()]),
|
|
10794
|
-
environment_binding_version: Union([
|
|
10827
|
+
environment_binding_version: Union([sha2562, Null()])
|
|
10795
10828
|
}, closed5),
|
|
10796
10829
|
database: DatabaseProviderSchema,
|
|
10797
10830
|
components: _Array_(DeploymentComponentEvidenceSchema, { minItems: 1, maxItems: 32 }),
|
|
@@ -10847,9 +10880,9 @@ function parseDeploymentEvidence(value) {
|
|
|
10847
10880
|
// node_modules/@supacloud/delivery/src/application-promotion.ts
|
|
10848
10881
|
var closed6 = { additionalProperties: false };
|
|
10849
10882
|
var projectRef2 = String2({ pattern: "^[a-z0-9-]{1,20}$" });
|
|
10850
|
-
var
|
|
10883
|
+
var sha2563 = ApplicationReleaseIdSchema;
|
|
10851
10884
|
var nullableActivation2 = Union([ApplicationActivationIdSchema, Null()]);
|
|
10852
|
-
var nullableDigest = Union([
|
|
10885
|
+
var nullableDigest = Union([sha2563, Null()]);
|
|
10853
10886
|
var observation = {
|
|
10854
10887
|
activation_id: nullableActivation2,
|
|
10855
10888
|
receipt_confirmed: Boolean2(),
|
|
@@ -10875,24 +10908,24 @@ var ApplicationPromotionPlanSchema = _Object_({
|
|
|
10875
10908
|
project_ref: projectRef2,
|
|
10876
10909
|
application_id: ApplicationIdSchema,
|
|
10877
10910
|
environment_id: ApplicationIdSchema,
|
|
10878
|
-
manifest_sha256:
|
|
10879
|
-
plan_sha256:
|
|
10911
|
+
manifest_sha256: sha2563,
|
|
10912
|
+
plan_sha256: sha2563,
|
|
10880
10913
|
source: _Object_({
|
|
10881
10914
|
project_ref: projectRef2,
|
|
10882
10915
|
environment_id: ApplicationIdSchema,
|
|
10883
|
-
release_id:
|
|
10916
|
+
release_id: sha2563,
|
|
10884
10917
|
...observation
|
|
10885
10918
|
}, closed6),
|
|
10886
10919
|
target: _Object_({
|
|
10887
|
-
candidate_release_id:
|
|
10888
|
-
current_release_id: Union([
|
|
10920
|
+
candidate_release_id: sha2563,
|
|
10921
|
+
current_release_id: Union([sha2563, Null()]),
|
|
10889
10922
|
artifact_action: Union([Literal("materialize"), Literal("reuse")]),
|
|
10890
10923
|
configuration_id: Union([ApplicationConfigurationIdSchema, Null()]),
|
|
10891
10924
|
configuration: Union([ApplicationConfigurationViewSchema, Null()]),
|
|
10892
10925
|
...observation
|
|
10893
10926
|
}, closed6),
|
|
10894
10927
|
migrations: _Object_({
|
|
10895
|
-
ledger_digest:
|
|
10928
|
+
ledger_digest: sha2563,
|
|
10896
10929
|
ledger_compatible: Boolean2(),
|
|
10897
10930
|
project_migrations_applied: Boolean2(),
|
|
10898
10931
|
pending_versions: _Array_(String2({ pattern: "^\\d{1,20}$" }), { maxItems: 4096, uniqueItems: true }),
|
|
@@ -16857,7 +16890,8 @@ var ApplicationActivationRetirementResultSchema2 = ApplicationActivationRetireme
|
|
|
16857
16890
|
var ApplicationRollbackSnapshotSchema2 = ApplicationRollbackSnapshotSchema;
|
|
16858
16891
|
var ApplicationActivationHistoryCursorSchema2 = ApplicationActivationHistoryCursorSchema;
|
|
16859
16892
|
var ApplicationPromotionPlanSchema2 = ApplicationPromotionPlanSchema;
|
|
16860
|
-
var
|
|
16893
|
+
var ApplicationDeployRequestSchema2 = ApplicationDeployRequestSchema;
|
|
16894
|
+
var ApplicationDeployWebRequestSchema2 = ApplicationDeployWebRequestSchema;
|
|
16861
16895
|
|
|
16862
16896
|
// src/shared/mutation-protocol.ts
|
|
16863
16897
|
import { createHash as createHash6 } from "node:crypto";
|
|
@@ -17645,7 +17679,7 @@ var APPLICATION_TOOL_SCHEMA = {
|
|
|
17645
17679
|
"put_configuration",
|
|
17646
17680
|
"get_deploy_plan",
|
|
17647
17681
|
"deploy_release",
|
|
17648
|
-
"
|
|
17682
|
+
"deploy_application",
|
|
17649
17683
|
"get_release_transfer_plan",
|
|
17650
17684
|
"transfer_release",
|
|
17651
17685
|
"get_promotion_plan",
|
|
@@ -17673,14 +17707,15 @@ var APPLICATION_TOOL_SCHEMA = {
|
|
|
17673
17707
|
activation_id: optional(ApplicationActivationIdSchema2, "[deploy_release/activate_release/reconcile_activation/retire_activation/rollback_release/get_history] Stable activation ID; generated for automatic deployment/rollback if omitted"),
|
|
17674
17708
|
expected_activation_id: optional(Union([ApplicationActivationIdSchema2, Null(), Literal("absent")]), "[deploy_release/activate_release/rollback_release] Current activation ID, or absent for first activation; selected from verified plan for automatic deployment"),
|
|
17675
17709
|
configuration_path: optional(String2(), "[put_configuration] Local configuration write JSON including revision and expected revision"),
|
|
17676
|
-
configuration_write: optional(Unknown(), "[
|
|
17710
|
+
configuration_write: optional(Unknown(), "[deploy_application] Parsed immutable configuration write"),
|
|
17677
17711
|
manifest_path: optional(String2(), "[upload_release] Local delivery.manifest.json"),
|
|
17712
|
+
web: optional(ApplicationDeployWebRequestSchema2, "[deploy_application] Immutable staged Web release binding"),
|
|
17678
17713
|
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"),
|
|
17679
17714
|
source_ref: optional(String2({ pattern: "^[A-Za-z0-9_-]{1,20}$" }), "[get_release_transfer_plan/transfer_release] Source project ref"),
|
|
17680
17715
|
source_release_id: optional(ApplicationReleaseIdSchema2, "[get_release_transfer_plan/transfer_release] Source immutable release ID"),
|
|
17681
17716
|
source_environment_id: optional(ApplicationIdSchema2, "[get_promotion_plan] Source environment"),
|
|
17682
|
-
mutation_id: optional(ApplicationActivationIdSchema2, "[promote_application/get_promotion_status/reconcile_promotion] Stable
|
|
17683
|
-
approved_migration_digest: optional(ApplicationReleaseIdSchema2, "[promote_application] Exact reviewed migration execution-plan digest"),
|
|
17717
|
+
mutation_id: optional(ApplicationActivationIdSchema2, "[deploy_application/promote_application/get_promotion_status/reconcile_promotion] Stable mutation ID"),
|
|
17718
|
+
approved_migration_digest: optional(ApplicationReleaseIdSchema2, "[deploy_application/promote_application] Exact reviewed migration execution-plan digest"),
|
|
17684
17719
|
plan: optional(ApplicationPromotionPlanSchema2, "[promote_application] Immutable plan returned by get_promotion_plan"),
|
|
17685
17720
|
cursor: optional(Union([ApplicationReleaseIdSchema2, ApplicationActivationHistoryCursorSchema2]), "[list_releases/get_history] Returned page cursor"),
|
|
17686
17721
|
limit: optional(Integer({ minimum: 1, maximum: 100 }), "[list_releases/get_history] Page size, default 50/20"),
|
|
@@ -18033,57 +18068,50 @@ function registerApplicationTools(server, http) {
|
|
|
18033
18068
|
const observed = await readDeployPlan(http, args, project);
|
|
18034
18069
|
return observed.failure ?? releaseControlSuccess(operation, { ...observed.identity, plan: observed.plan });
|
|
18035
18070
|
}
|
|
18036
|
-
if (action === "
|
|
18071
|
+
if (action === "deploy_application") {
|
|
18037
18072
|
const environmentId = text(args, "environment_id");
|
|
18038
18073
|
const releaseId = text(args, "release_id");
|
|
18039
|
-
|
|
18040
|
-
|
|
18074
|
+
const mutationId = text(args, "mutation_id");
|
|
18075
|
+
if (!Check2(ApplicationIdSchema2, environmentId) || !Check2(ApplicationReleaseIdSchema2, releaseId) || !Check2(ApplicationActivationIdSchema2, mutationId)) {
|
|
18076
|
+
throw new Error("Invalid application deploy identity");
|
|
18041
18077
|
}
|
|
18042
18078
|
const configurationId = args["configuration_id"];
|
|
18043
|
-
const activationId = args["activation_id"];
|
|
18044
|
-
const expectedActivationId = args["expected_activation_id"];
|
|
18045
18079
|
if (configurationId !== undefined && !Check2(ApplicationConfigurationIdSchema2, configurationId)) {
|
|
18046
18080
|
throw new Error("Invalid application configuration ID");
|
|
18047
18081
|
}
|
|
18048
|
-
if (activationId !== undefined && !Check2(ApplicationActivationIdSchema2, activationId)) {
|
|
18049
|
-
throw new Error("Invalid application activation ID");
|
|
18050
|
-
}
|
|
18051
|
-
if (expectedActivationId !== undefined && expectedActivationId !== null && expectedActivationId !== "absent" && !Check2(ApplicationActivationIdSchema2, expectedActivationId)) {
|
|
18052
|
-
throw new Error("Invalid expected activation ID");
|
|
18053
|
-
}
|
|
18054
18082
|
const configurationWrite = args["configuration_write"];
|
|
18055
18083
|
const parsedConfigurationWrite = configurationWrite === undefined ? undefined : parseApplicationConfigurationWrite(configurationWrite);
|
|
18056
18084
|
const requestedConfigurationId = configurationId ?? parsedConfigurationWrite?.configuration_id;
|
|
18057
18085
|
const body = {
|
|
18086
|
+
mutation_id: mutationId,
|
|
18058
18087
|
release_id: releaseId,
|
|
18059
18088
|
...configurationId === undefined ? {} : { configuration_id: configurationId },
|
|
18060
18089
|
...configurationWrite === undefined ? {} : { configuration_write: configurationWrite },
|
|
18061
|
-
...
|
|
18062
|
-
...
|
|
18063
|
-
expected_activation_id: expectedActivationId === "absent" ? null : expectedActivationId
|
|
18064
|
-
}
|
|
18090
|
+
...typeof args["approved_migration_digest"] === "string" ? { approved_migration_digest: args["approved_migration_digest"] } : {},
|
|
18091
|
+
...args["web"] === undefined ? {} : { web: args["web"] }
|
|
18065
18092
|
};
|
|
18066
|
-
if (!Check2(
|
|
18067
|
-
throw new Error("Invalid application
|
|
18093
|
+
if (!Check2(ApplicationDeployRequestSchema2, body)) {
|
|
18094
|
+
throw new Error("Invalid application deploy request");
|
|
18068
18095
|
}
|
|
18069
|
-
const result = await http.post(`/v1/projects/${project}/applications/${encodeURIComponent(id)}/environments/${encodeURIComponent(environmentId)}/
|
|
18096
|
+
const result = await http.post(`/v1/projects/${project}/applications/${encodeURIComponent(id)}/environments/${encodeURIComponent(environmentId)}/deploy`, body, { timeoutMs: 120000, maxJsonBytes: 131072, responseTimeoutMs: 30000 });
|
|
18070
18097
|
const identity = {
|
|
18071
18098
|
project_ref: ref,
|
|
18072
18099
|
application_id: id,
|
|
18073
18100
|
environment_id: environmentId,
|
|
18074
18101
|
release_id: releaseId,
|
|
18075
|
-
|
|
18076
|
-
...
|
|
18102
|
+
mutation_id: mutationId,
|
|
18103
|
+
...requestedConfigurationId === undefined ? {} : { configuration_id: requestedConfigurationId }
|
|
18077
18104
|
};
|
|
18078
18105
|
if (!result.ok)
|
|
18079
18106
|
return releaseControlMutationFailure(operation, result, identity);
|
|
18080
18107
|
try {
|
|
18081
18108
|
if (result.status !== 200)
|
|
18082
18109
|
throw new Error;
|
|
18083
|
-
const
|
|
18084
|
-
if (
|
|
18110
|
+
const deploy = parseApplicationDeployResult(result.data);
|
|
18111
|
+
if (deploy.project_ref !== ref || deploy.application_id !== id || deploy.environment_id !== environmentId || deploy.release_id !== releaseId || deploy.mutation_id !== mutationId || requestedConfigurationId !== undefined && deploy.configuration_id !== requestedConfigurationId) {
|
|
18085
18112
|
throw new Error;
|
|
18086
|
-
|
|
18113
|
+
}
|
|
18114
|
+
return releaseControlSuccess(operation, deploy);
|
|
18087
18115
|
} catch {
|
|
18088
18116
|
return releaseControlFailure(operation, "OUTCOME_UNKNOWN", result.status, identity);
|
|
18089
18117
|
}
|
|
@@ -24538,6 +24566,7 @@ var REMOTE_APP_DESCRIPTIONS = {
|
|
|
24538
24566
|
source_environment_id: "[promote-plan/diff/promote] Source environment ID",
|
|
24539
24567
|
mutation_id: "[promote-status/promote-reconcile] Stable promotion mutation ID",
|
|
24540
24568
|
approved_migration_digest: "[promote] Exact reviewed migration execution-plan digest",
|
|
24569
|
+
web: "[release] Immutable staged Web release binding prepared by the deploy profile",
|
|
24541
24570
|
branch_ref: "[preview-plan] Preview branch identity",
|
|
24542
24571
|
branch_name: "[preview/preview-create] Human-readable preview branch name",
|
|
24543
24572
|
data_mode: "[preview-plan/preview/preview-create] schema_only or full_clone",
|
|
@@ -25506,9 +25535,6 @@ async function runAppRelease(request, options, delegate) {
|
|
|
25506
25535
|
if (request.configuration_path !== undefined && request.configuration_id !== undefined) {
|
|
25507
25536
|
throw new Error("app release accepts either --configuration_path or --configuration_id, not both");
|
|
25508
25537
|
}
|
|
25509
|
-
if (request.configuration_path === undefined && request.configuration_id === undefined) {
|
|
25510
|
-
throw new Error("app release requires --configuration_path or --configuration_id");
|
|
25511
|
-
}
|
|
25512
25538
|
if (request.activation_id !== undefined || request.expected_activation_id !== undefined) {
|
|
25513
25539
|
throw new Error("app release generates one activation identity; use app deploy for an exact retry");
|
|
25514
25540
|
}
|
|
@@ -25562,25 +25588,36 @@ async function runAppRelease(request, options, delegate) {
|
|
|
25562
25588
|
configurationWrite = parseApplicationConfigurationWrite(await Bun.file(configurationPath).json());
|
|
25563
25589
|
configurationId = configurationWrite.configuration_id;
|
|
25564
25590
|
} catch {
|
|
25565
|
-
return releasePhaseFailure("configure", releaseControlFailure("applications.
|
|
25591
|
+
return releasePhaseFailure("configure", releaseControlFailure("applications.deploy_application", "INVALID_RESPONSE", null, { project_ref: ref, application_id: id, environment_id: environmentId }), completed);
|
|
25566
25592
|
}
|
|
25567
25593
|
completed.configure = { delegated: true, configuration_id: configurationId };
|
|
25568
25594
|
} else {
|
|
25569
|
-
completed.configure = {
|
|
25595
|
+
completed.configure = {
|
|
25596
|
+
delegated: false,
|
|
25597
|
+
resolved_by: "platform",
|
|
25598
|
+
...configurationId === undefined ? {} : { configuration_id: configurationId }
|
|
25599
|
+
};
|
|
25570
25600
|
}
|
|
25571
25601
|
const deployArgs = {
|
|
25572
|
-
action: "
|
|
25602
|
+
action: "deploy_application",
|
|
25573
25603
|
ref,
|
|
25574
25604
|
id,
|
|
25575
25605
|
environment_id: environmentId,
|
|
25576
25606
|
release_id: releaseId,
|
|
25577
|
-
|
|
25578
|
-
|
|
25579
|
-
...configurationWrite === undefined ? {} : { configuration_write: configurationWrite }
|
|
25607
|
+
mutation_id: activationId,
|
|
25608
|
+
...configurationId === undefined ? {} : { configuration_id: configurationId },
|
|
25609
|
+
...configurationWrite === undefined ? {} : { configuration_write: configurationWrite },
|
|
25610
|
+
...request.web === undefined ? {} : { web: request.web }
|
|
25580
25611
|
};
|
|
25581
25612
|
validateToolArguments(APPLICATION_TOOL_SCHEMA, deployArgs);
|
|
25582
25613
|
const deployed = await delegate(deployArgs);
|
|
25583
25614
|
const deployReceipt = resultObject(deployed);
|
|
25615
|
+
const resolvedConfigurationId = typeof deployReceipt?.configuration_id === "string" ? deployReceipt.configuration_id : undefined;
|
|
25616
|
+
if (resolvedConfigurationId !== undefined) {
|
|
25617
|
+
configurationId = resolvedConfigurationId;
|
|
25618
|
+
const configure = completed.configure;
|
|
25619
|
+
completed.configure = configure && typeof configure === "object" && !Array.isArray(configure) ? { ...configure, configuration_id: resolvedConfigurationId } : { delegated: false, resolved_by: "platform", configuration_id: resolvedConfigurationId };
|
|
25620
|
+
}
|
|
25584
25621
|
if (deployed.isError || !deployReceipt?.ok) {
|
|
25585
25622
|
return releasePhaseFailure("deploy", deployed, completed);
|
|
25586
25623
|
}
|
|
@@ -25593,13 +25630,14 @@ async function runAppRelease(request, options, delegate) {
|
|
|
25593
25630
|
application_id: id,
|
|
25594
25631
|
environment_id: environmentId,
|
|
25595
25632
|
release_id: releaseId,
|
|
25596
|
-
configuration_id: configurationId,
|
|
25633
|
+
...configurationId === undefined ? {} : { configuration_id: configurationId },
|
|
25597
25634
|
manifest_path: manifestPath,
|
|
25598
25635
|
build: buildReceipt ? completed.build : { skipped: true, manifest_path: manifestPath },
|
|
25599
25636
|
phases: completed,
|
|
25600
25637
|
no_op: deployReceipt.action === "no-op" || deployReceipt.no_op === true,
|
|
25601
25638
|
execution_performed: deployReceipt.execution_performed === true,
|
|
25602
|
-
...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {}
|
|
25639
|
+
...typeof deployReceipt.activation_id === "string" ? { activation_id: deployReceipt.activation_id } : {},
|
|
25640
|
+
...deployReceipt.web === undefined ? {} : { web: deployReceipt.web }
|
|
25603
25641
|
};
|
|
25604
25642
|
return request.format === "json" || request.json === true ? textResult(JSON.stringify(result, null, 2)) : textResult([
|
|
25605
25643
|
`${ref}/${id}/${environmentId}: ${result.no_op ? "no-op" : "released"}`,
|
|
@@ -27205,6 +27243,7 @@ import { createHash as createHash8 } from "node:crypto";
|
|
|
27205
27243
|
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "node:fs";
|
|
27206
27244
|
import { dirname as dirname10, join as join16, relative as relative9, resolve as resolve21 } from "node:path";
|
|
27207
27245
|
var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
|
|
27246
|
+
var SAFE_ENVIRONMENT_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
27208
27247
|
var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
|
|
27209
27248
|
var SHA256 = /^[a-f0-9]{64}$/;
|
|
27210
27249
|
var SAFE_DATABASE = /^[^\u0000-\u001f\u007f]{1,128}$/;
|
|
@@ -27445,6 +27484,27 @@ function genericScopedReleaseScopeJson(scope) {
|
|
|
27445
27484
|
function genericScopedReleaseScopeSha256(scope) {
|
|
27446
27485
|
return createHash8("sha256").update(genericScopedReleaseScopeJson(scope)).digest("hex");
|
|
27447
27486
|
}
|
|
27487
|
+
function scopeEnvironmentId(scope, action) {
|
|
27488
|
+
const environmentId = isRecord6(scope) ? scope.targetEnvironmentId : undefined;
|
|
27489
|
+
if (typeof environmentId !== "string" || !SAFE_ENVIRONMENT_ID.test(environmentId)) {
|
|
27490
|
+
throw new Error(`Scope for ${action} must be regenerated with a concrete targetEnvironmentId`);
|
|
27491
|
+
}
|
|
27492
|
+
return environmentId;
|
|
27493
|
+
}
|
|
27494
|
+
function requestedScopeEnvironment(args, action) {
|
|
27495
|
+
const environmentId = args.environment_id;
|
|
27496
|
+
if (typeof environmentId !== "string" || !SAFE_ENVIRONMENT_ID.test(environmentId)) {
|
|
27497
|
+
throw new Error(`'environment_id' is required for ${action} and must identify one deployment environment`);
|
|
27498
|
+
}
|
|
27499
|
+
return environmentId;
|
|
27500
|
+
}
|
|
27501
|
+
function assertScopeEnvironment(scope, requestedEnvironmentId, action) {
|
|
27502
|
+
const targetEnvironmentId = scopeEnvironmentId(scope, action);
|
|
27503
|
+
if (targetEnvironmentId !== requestedEnvironmentId) {
|
|
27504
|
+
throw new Error(`Scope target environment '${targetEnvironmentId}' does not match requested environment '${requestedEnvironmentId}'`);
|
|
27505
|
+
}
|
|
27506
|
+
return targetEnvironmentId;
|
|
27507
|
+
}
|
|
27448
27508
|
function runGitRevParse(cwd, args) {
|
|
27449
27509
|
const res = Bun.spawnSync(["git", ...args], { cwd });
|
|
27450
27510
|
if (res.exitCode !== 0 || !res.stdout)
|
|
@@ -27571,10 +27631,10 @@ function collectScopeFiles(cwd, file, files, dir, task) {
|
|
|
27571
27631
|
function handleScopeRebind(args) {
|
|
27572
27632
|
const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
|
|
27573
27633
|
const targetBaseCommit = resolveGitBaseCommit(cwd, args.base_commit);
|
|
27634
|
+
const requestedEnvironmentId = requestedScopeEnvironment(args, "scope_rebind");
|
|
27574
27635
|
const scopeFiles = collectScopeFiles(cwd, args.file, args.files, args.dir, args.task);
|
|
27575
27636
|
const dryRun = args.dry_run === true;
|
|
27576
|
-
const
|
|
27577
|
-
for (const filePath of scopeFiles) {
|
|
27637
|
+
const parsedScopes = scopeFiles.map((filePath) => {
|
|
27578
27638
|
let content;
|
|
27579
27639
|
try {
|
|
27580
27640
|
content = readFileSync10(filePath, "utf8");
|
|
@@ -27587,15 +27647,20 @@ function handleScopeRebind(args) {
|
|
|
27587
27647
|
} catch {
|
|
27588
27648
|
throw new Error(`Scope file '${filePath}' is not valid JSON`);
|
|
27589
27649
|
}
|
|
27590
|
-
|
|
27650
|
+
const targetEnvironmentId = assertScopeEnvironment(parsed, requestedEnvironmentId, "scope_rebind");
|
|
27651
|
+
const previousBaseCommit = parsed.baseCommit;
|
|
27652
|
+
if (typeof previousBaseCommit !== "string" || !/^[0-9a-f]{40}$/.test(previousBaseCommit)) {
|
|
27591
27653
|
throw new Error(`Scope file '${filePath}' has missing or invalid baseCommit`);
|
|
27592
27654
|
}
|
|
27655
|
+
return { filePath, parsed, targetEnvironmentId };
|
|
27656
|
+
});
|
|
27657
|
+
const scopes = [];
|
|
27658
|
+
for (const { filePath, parsed, targetEnvironmentId } of parsedScopes) {
|
|
27593
27659
|
const previousBaseCommit = parsed.baseCommit;
|
|
27594
27660
|
const updated = previousBaseCommit !== targetBaseCommit;
|
|
27595
27661
|
if (updated) {
|
|
27596
27662
|
parsed.baseCommit = targetBaseCommit;
|
|
27597
27663
|
}
|
|
27598
|
-
const canonicalJson = genericScopedReleaseScopeJson(parsed);
|
|
27599
27664
|
const scopeSha256 = genericScopedReleaseScopeSha256(parsed);
|
|
27600
27665
|
if (updated && !dryRun) {
|
|
27601
27666
|
const formatted = JSON.stringify(canonicalValue(parsed), null, 2) + `
|
|
@@ -27608,6 +27673,7 @@ function handleScopeRebind(args) {
|
|
|
27608
27673
|
target_project_ref: typeof parsed.targetProjectRef === "string" ? parsed.targetProjectRef : "",
|
|
27609
27674
|
previous_base_commit: previousBaseCommit,
|
|
27610
27675
|
new_base_commit: targetBaseCommit,
|
|
27676
|
+
target_environment_id: targetEnvironmentId,
|
|
27611
27677
|
scope_sha256: scopeSha256,
|
|
27612
27678
|
updated
|
|
27613
27679
|
});
|
|
@@ -27622,6 +27688,7 @@ function handleScopeRebind(args) {
|
|
|
27622
27688
|
}
|
|
27623
27689
|
function handleScopeInspect(args) {
|
|
27624
27690
|
const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve21(args.cwd.trim()) : process.cwd();
|
|
27691
|
+
const requestedEnvironmentId = requestedScopeEnvironment(args, "scope_inspect");
|
|
27625
27692
|
if (typeof args.file !== "string" || !args.file.trim()) {
|
|
27626
27693
|
throw new Error("'file' is required for scope_inspect");
|
|
27627
27694
|
}
|
|
@@ -27651,7 +27718,11 @@ function handleScopeInspect(args) {
|
|
|
27651
27718
|
} catch {
|
|
27652
27719
|
throw new Error(`Scope file '${args.file}' is not valid JSON`);
|
|
27653
27720
|
}
|
|
27654
|
-
if (!isRecord6(parsed)
|
|
27721
|
+
if (!isRecord6(parsed)) {
|
|
27722
|
+
throw new Error(`Scope file '${args.file}' must be an object and must be regenerated`);
|
|
27723
|
+
}
|
|
27724
|
+
const targetEnvironmentId = assertScopeEnvironment(parsed, requestedEnvironmentId, "scope_inspect");
|
|
27725
|
+
if (typeof parsed.baseCommit !== "string" || !/^[0-9a-f]{40}$/.test(parsed.baseCommit)) {
|
|
27655
27726
|
throw new Error(`Scope file '${args.file}' has missing or invalid baseCommit`);
|
|
27656
27727
|
}
|
|
27657
27728
|
const scopeSha256 = genericScopedReleaseScopeSha256(parsed);
|
|
@@ -27661,6 +27732,7 @@ function handleScopeInspect(args) {
|
|
|
27661
27732
|
scope: {
|
|
27662
27733
|
task_id: parsed.taskId,
|
|
27663
27734
|
target_project_ref: parsed.targetProjectRef,
|
|
27735
|
+
target_environment_id: targetEnvironmentId,
|
|
27664
27736
|
base_commit: parsed.baseCommit,
|
|
27665
27737
|
functions: parsed.functions,
|
|
27666
27738
|
excluded_functions: parsed.excludedFunctions,
|
|
@@ -27698,6 +27770,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
27698
27770
|
if (typeof args.task !== "string" || !args.task.trim()) {
|
|
27699
27771
|
throw new Error("'task' is required for scope_create");
|
|
27700
27772
|
}
|
|
27773
|
+
const targetEnvironmentId = requestedScopeEnvironment(args, "scope_create");
|
|
27701
27774
|
const projectRef = typeof args.ref === "string" && args.ref.trim() || fallbackRef;
|
|
27702
27775
|
if (!projectRef) {
|
|
27703
27776
|
throw new Error("'ref' is required for scope_create");
|
|
@@ -27714,6 +27787,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
27714
27787
|
functions: parseCommaList(args.functions),
|
|
27715
27788
|
migrations: parseCommaList(args.migrations),
|
|
27716
27789
|
targetProjectRef: projectRef,
|
|
27790
|
+
targetEnvironmentId,
|
|
27717
27791
|
taskId: String(args.task).trim(),
|
|
27718
27792
|
web: Boolean(args.web)
|
|
27719
27793
|
};
|
|
@@ -27726,6 +27800,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
27726
27800
|
file: relative9(cwd, filePath),
|
|
27727
27801
|
task_id: scope.taskId,
|
|
27728
27802
|
target_project_ref: projectRef,
|
|
27803
|
+
target_environment_id: targetEnvironmentId,
|
|
27729
27804
|
base_commit: targetBaseCommit,
|
|
27730
27805
|
scope_sha256: scopeSha256,
|
|
27731
27806
|
scope
|
|
@@ -27752,6 +27827,7 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
27752
27827
|
dir: optional(String2(), "[scope_rebind] Directory containing release scope JSON files (defaults to supacloud/fa/release-scopes, release-scopes, or scopes)"),
|
|
27753
27828
|
task: optional(String2(), "[scope_rebind/scope_create] Task ID or prefix filter for scope files"),
|
|
27754
27829
|
base_commit: optional(String2(), "[scope_rebind/scope_create] Target 40-character base commit SHA or git ref (defaults to origin/main -> main -> HEAD^ -> HEAD)"),
|
|
27830
|
+
environment_id: optional(String2(), "[scope_inspect/scope_rebind/scope_create] Exact target deployment environment"),
|
|
27755
27831
|
functions: optional(String2(), "[scope_create] Comma-separated list of functions to deploy"),
|
|
27756
27832
|
excluded_functions: optional(String2(), "[scope_create] Comma-separated list of functions to exclude"),
|
|
27757
27833
|
migrations: optional(String2(), "[scope_create] Comma-separated list of migrations to apply"),
|
|
@@ -28153,6 +28229,33 @@ function selectDeployment(deployments, requestedId, projectName) {
|
|
|
28153
28229
|
const ids = deployments.map((deployment) => deployment.id).sort().join(", ");
|
|
28154
28230
|
throw new Error(deployments.length === 0 ? "No frontend deployments exist for this project" : `Multiple frontend deployments found (${ids}). Set frontend.id in supacloud.json or pass --id`);
|
|
28155
28231
|
}
|
|
28232
|
+
function selectFrontendTarget(configRoot, config) {
|
|
28233
|
+
if (config.targets === undefined && config.frontend === undefined)
|
|
28234
|
+
return null;
|
|
28235
|
+
const targets = config.targets ? Object.entries(config.targets).filter(([, target]) => target.type === "frontend") : [["frontend", {
|
|
28236
|
+
type: "frontend",
|
|
28237
|
+
root: config.frontend?.root,
|
|
28238
|
+
id: config.frontend?.id,
|
|
28239
|
+
buildCommand: config.frontend?.buildCommand,
|
|
28240
|
+
outputDirectory: config.frontend?.outputDirectory,
|
|
28241
|
+
buildEnv: config.frontend?.buildEnv
|
|
28242
|
+
}]];
|
|
28243
|
+
if (targets.length === 0)
|
|
28244
|
+
return null;
|
|
28245
|
+
if (config.defaultTarget) {
|
|
28246
|
+
const selected = targets.find(([name]) => name === config.defaultTarget);
|
|
28247
|
+
if (selected)
|
|
28248
|
+
return { name: selected[0], config: selected[1], root: targetRoot(configRoot, selected[1], selected[0]) };
|
|
28249
|
+
}
|
|
28250
|
+
const namedWeb = targets.find(([name]) => name === "web");
|
|
28251
|
+
if (namedWeb)
|
|
28252
|
+
return { name: namedWeb[0], config: namedWeb[1], root: targetRoot(configRoot, namedWeb[1], namedWeb[0]) };
|
|
28253
|
+
if (targets.length === 1) {
|
|
28254
|
+
const selected = targets[0];
|
|
28255
|
+
return { name: selected[0], config: selected[1], root: targetRoot(configRoot, selected[1], selected[0]) };
|
|
28256
|
+
}
|
|
28257
|
+
throw new Error(`Multiple frontend deploy targets found (${targets.map(([name]) => name).sort().join(", ")}). Set defaultTarget to the Web target`);
|
|
28258
|
+
}
|
|
28156
28259
|
function targetRoot(configRoot, target, name) {
|
|
28157
28260
|
const root = resolve22(configRoot, target.root || (name === "frontend" ? "." : name));
|
|
28158
28261
|
const prefix = `${resolve22(configRoot)}${sep5}`;
|
|
@@ -28325,6 +28428,71 @@ function payload(response) {
|
|
|
28325
28428
|
throw new Error("Deployment operation returned an invalid response");
|
|
28326
28429
|
return parsed;
|
|
28327
28430
|
}
|
|
28431
|
+
async function stageApplicationWebRelease(http, projectRef, configRoot, skipBuild, json, environmentVariables) {
|
|
28432
|
+
const target = selectFrontendTarget(configRoot, await readDeployConfig(configRoot));
|
|
28433
|
+
if (!target)
|
|
28434
|
+
return null;
|
|
28435
|
+
const projectDirectory = target.root;
|
|
28436
|
+
const targetState = await lstat5(projectDirectory).catch(() => null);
|
|
28437
|
+
if (!targetState?.isDirectory() || targetState.isSymbolicLink()) {
|
|
28438
|
+
throw new Error(`Deploy target '${target.name}' root is not a regular directory: ${projectDirectory}`);
|
|
28439
|
+
}
|
|
28440
|
+
const requestedId = target.config.id;
|
|
28441
|
+
const listResponse = await http.get(`/v1/projects/${encodeURIComponent(projectRef)}/frontend/deployments`);
|
|
28442
|
+
if (!listResponse.ok)
|
|
28443
|
+
throw new Error(`Failed to list frontend deployments (HTTP ${listResponse.status})`);
|
|
28444
|
+
const selected = selectDeployment(deploymentList(listResponse.data), requestedId, await packageName(projectDirectory));
|
|
28445
|
+
const configuredBuildCommand = String(target.config.buildCommand || selected.buildCommand || "").trim();
|
|
28446
|
+
const buildCommand = configuredBuildCommand || (skipBuild ? "" : await defaultBuildCommand(projectDirectory, configRoot));
|
|
28447
|
+
const configuredOutput = String(target.config.outputDirectory || "").trim();
|
|
28448
|
+
const remoteOutput = selected.outputDirectory && selected.outputDirectory !== "." ? selected.outputDirectory : "";
|
|
28449
|
+
const outputDirectory = resolveInsideProject(projectDirectory, configuredOutput || remoteOutput || defaultOutputDirectory(selected.framework));
|
|
28450
|
+
const build = createBuildEnvironment(target.config.buildEnv, environmentVariables);
|
|
28451
|
+
const report = phaseReporter(json);
|
|
28452
|
+
report("web", `${target.name} (${selected.id})`);
|
|
28453
|
+
assertBuildEnvironment(build, skipBuild);
|
|
28454
|
+
if (!skipBuild) {
|
|
28455
|
+
report("build", buildCommand);
|
|
28456
|
+
await runBuild(buildCommand, projectDirectory, build.env, json);
|
|
28457
|
+
} else {
|
|
28458
|
+
report("build", "skipped");
|
|
28459
|
+
}
|
|
28460
|
+
report("package", relative10(projectDirectory, outputDirectory) || ".");
|
|
28461
|
+
const archive = await createFrontendArchive(outputDirectory);
|
|
28462
|
+
try {
|
|
28463
|
+
const authority = payload(await readFrontendReleaseAuthority(http, projectRef, selected.id));
|
|
28464
|
+
const activeReleaseId = typeof authority.active_release_id === "string" ? authority.active_release_id : null;
|
|
28465
|
+
const activeActivationId = typeof authority.active_activation_id === "string" ? authority.active_activation_id : null;
|
|
28466
|
+
const current = Array.isArray(authority.releases) ? record4(authority.releases[0]) : null;
|
|
28467
|
+
if (archive.sha256 === activeReleaseId && archive.treeSha256 !== current?.tree_sha256) {
|
|
28468
|
+
throw new Error("Active Web release tree does not match the local archive");
|
|
28469
|
+
}
|
|
28470
|
+
if (archive.treeSha256 === current?.tree_sha256) {
|
|
28471
|
+
report("done", "Web already current");
|
|
28472
|
+
return {};
|
|
28473
|
+
}
|
|
28474
|
+
report("upload", `${archive.fileCount} files`);
|
|
28475
|
+
const uploaded = payload(await uploadFrontendRelease(http, projectRef, selected.id, archive.archivePath));
|
|
28476
|
+
const release = record4(uploaded.release);
|
|
28477
|
+
const releaseId = requiredString(release?.release_id, "release.release_id");
|
|
28478
|
+
if (releaseId !== archive.sha256)
|
|
28479
|
+
throw new Error("Uploaded Web release identity does not match the local archive");
|
|
28480
|
+
if (release?.tree_sha256 !== archive.treeSha256)
|
|
28481
|
+
throw new Error("Uploaded Web release tree does not match the local build");
|
|
28482
|
+
report("done", releaseId.slice(0, 12));
|
|
28483
|
+
return {
|
|
28484
|
+
request: {
|
|
28485
|
+
deployment_id: selected.id,
|
|
28486
|
+
release_id: releaseId,
|
|
28487
|
+
expected_active_release_id: activeReleaseId ?? "absent",
|
|
28488
|
+
expected_activation_id: activeActivationId ?? "absent",
|
|
28489
|
+
mutation_id: crypto.randomUUID()
|
|
28490
|
+
}
|
|
28491
|
+
};
|
|
28492
|
+
} finally {
|
|
28493
|
+
await archive.cleanup();
|
|
28494
|
+
}
|
|
28495
|
+
}
|
|
28328
28496
|
function phaseReporter(json) {
|
|
28329
28497
|
const started = performance.now();
|
|
28330
28498
|
return (phase, detail) => {
|
|
@@ -28373,12 +28541,10 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28373
28541
|
}
|
|
28374
28542
|
const configurationPath = options.application.configurationPath?.trim();
|
|
28375
28543
|
const configurationId = options.application.configurationId?.trim();
|
|
28376
|
-
if (!configurationPath && !configurationId) {
|
|
28377
|
-
throw new Error("Application profile requires SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
|
|
28378
|
-
}
|
|
28379
28544
|
if (configurationPath && configurationId) {
|
|
28380
28545
|
throw new Error("Application profile must set only one of SUPACLOUD_APPLICATION_CONFIGURATION_PATH or SUPACLOUD_APPLICATION_CONFIGURATION_ID");
|
|
28381
28546
|
}
|
|
28547
|
+
const stagedWeb = await stageApplicationWebRelease(http, projectRef, configRoot, args.skip_build === true, args.json === true, options.environmentVariables);
|
|
28382
28548
|
const result = await options.applicationRelease({
|
|
28383
28549
|
action: "release",
|
|
28384
28550
|
ref: projectRef,
|
|
@@ -28389,6 +28555,7 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
28389
28555
|
...configurationId ? { configuration_id: configurationId } : {},
|
|
28390
28556
|
...options.application.manifestPath ? { manifest_path: options.application.manifestPath } : {},
|
|
28391
28557
|
...args.skip_build === true ? { skip_build: true } : {},
|
|
28558
|
+
...stagedWeb?.request === undefined ? {} : { web: stagedWeb.request },
|
|
28392
28559
|
...args.json === true ? { format: "json", json: true } : {}
|
|
28393
28560
|
});
|
|
28394
28561
|
return result;
|
|
@@ -29205,7 +29372,7 @@ function registerDeveloperExperienceTools(server, options = {}) {
|
|
|
29205
29372
|
// package.json
|
|
29206
29373
|
var package_default = {
|
|
29207
29374
|
name: "@supacloud/cli",
|
|
29208
|
-
version: "0.
|
|
29375
|
+
version: "0.90.0",
|
|
29209
29376
|
description: "Project-scoped CLI for SupaCloud users",
|
|
29210
29377
|
type: "module",
|
|
29211
29378
|
main: "./dist/index.js",
|
|
@@ -29258,13 +29425,13 @@ var package_default = {
|
|
|
29258
29425
|
},
|
|
29259
29426
|
dependencies: {
|
|
29260
29427
|
"@supacloud/compiler": "^0.44.1",
|
|
29261
|
-
"@supacloud/delivery": "0.
|
|
29428
|
+
"@supacloud/delivery": "0.15.0",
|
|
29262
29429
|
"@supacloud/db": "0.16.1",
|
|
29263
29430
|
typebox: "1.3.36"
|
|
29264
29431
|
},
|
|
29265
29432
|
overrides: {
|
|
29266
29433
|
"@supacloud/contracts": "0.8.1",
|
|
29267
|
-
"@supacloud/delivery": "0.
|
|
29434
|
+
"@supacloud/delivery": "0.15.0",
|
|
29268
29435
|
"@graphql-tools/utils": "^12.0.3"
|
|
29269
29436
|
},
|
|
29270
29437
|
devDependencies: {
|
|
@@ -29498,8 +29665,8 @@ EXAMPLES
|
|
|
29498
29665
|
${preferredCommand} release postgrest_restart --ref abc123
|
|
29499
29666
|
${preferredCommand} release release_canary_fixture_stage_replay --ref abc123 --subject <uuid> --request_id <uuid>
|
|
29500
29667
|
${preferredCommand} release release_canary_fixture_disable_replay --ref abc123 --fixture_id <uuid> --disable_request_id <uuid> --issuer <issuer-url> --subject <uuid>
|
|
29501
|
-
${preferredCommand} release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json
|
|
29502
|
-
${preferredCommand} release scope_inspect --file supacloud/fa/release-scopes/20260907-intake-p1-production.json
|
|
29668
|
+
${preferredCommand} release scope_rebind --file supacloud/fa/release-scopes/20260907-intake-p1-production.json --environment_id production
|
|
29669
|
+
${preferredCommand} release scope_inspect --file supacloud/fa/release-scopes/20260907-intake-p1-production.json --environment_id production
|
|
29503
29670
|
${preferredCommand} queue stats --queue emails
|
|
29504
29671
|
${preferredCommand} queue dlq --queue emails --limit 20
|
|
29505
29672
|
${preferredCommand} frontend list --ref abc123
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ApplicationDeployRequest } from "@supacloud/delivery";
|
|
1
2
|
import { type ToolServer } from "../tool-server";
|
|
2
3
|
import { type AppManifest } from "./app-tool-export";
|
|
3
4
|
declare const REMOTE_APP_ACTIONS: {
|
|
@@ -53,6 +54,7 @@ export interface AppToolArguments {
|
|
|
53
54
|
source_environment_id?: string;
|
|
54
55
|
mutation_id?: string;
|
|
55
56
|
approved_migration_digest?: string;
|
|
57
|
+
web?: ApplicationDeployRequest["web"];
|
|
56
58
|
branch_ref?: string;
|
|
57
59
|
branch_name?: string;
|
|
58
60
|
data_mode?: "schema_only" | "full_clone";
|
|
@@ -152,7 +152,8 @@ export declare const ApplicationPromotionPlanSchema: import("typebox").TObject<{
|
|
|
152
152
|
execution_performed: import("typebox").TLiteral<false>;
|
|
153
153
|
data_recovery: import("typebox").TLiteral<"separate-required">;
|
|
154
154
|
}>;
|
|
155
|
-
export declare const
|
|
155
|
+
export declare const ApplicationDeployRequestSchema: import("typebox").TObject<{
|
|
156
|
+
mutation_id: import("typebox").TString;
|
|
156
157
|
release_id: import("typebox").TString;
|
|
157
158
|
configuration_id: import("typebox").TOptional<import("typebox").TString>;
|
|
158
159
|
configuration_write: import("typebox").TOptional<import("typebox").TObject<{
|
|
@@ -168,15 +169,22 @@ export declare const ApplicationReleaseRequestSchema: import("typebox").TObject<
|
|
|
168
169
|
}>>;
|
|
169
170
|
}>;
|
|
170
171
|
}>>;
|
|
171
|
-
activation_id: import("typebox").TOptional<import("typebox").TString>;
|
|
172
|
-
expected_activation_id: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TString, import("typebox").TNull]>>;
|
|
173
172
|
expected_plan_sha256: import("typebox").TOptional<import("typebox").TString>;
|
|
173
|
+
approved_migration_digest: import("typebox").TOptional<import("typebox").TString>;
|
|
174
|
+
web: import("typebox").TOptional<import("typebox").TObject<{
|
|
175
|
+
deployment_id: import("typebox").TString;
|
|
176
|
+
release_id: import("typebox").TString;
|
|
177
|
+
expected_active_release_id: import("typebox").TString;
|
|
178
|
+
expected_activation_id: import("typebox").TString;
|
|
179
|
+
mutation_id: import("typebox").TString;
|
|
180
|
+
}>>;
|
|
174
181
|
}>;
|
|
175
|
-
export declare const
|
|
176
|
-
schema: import("typebox").TLiteral<"supacloud.application-
|
|
182
|
+
export declare const ApplicationDeployResultSchema: import("typebox").TObject<{
|
|
183
|
+
schema: import("typebox").TLiteral<"supacloud.application-deploy.v1">;
|
|
177
184
|
project_ref: import("typebox").TString;
|
|
178
185
|
application_id: import("typebox").TString;
|
|
179
186
|
environment_id: import("typebox").TString;
|
|
187
|
+
mutation_id: import("typebox").TString;
|
|
180
188
|
release_id: import("typebox").TString;
|
|
181
189
|
configuration_id: import("typebox").TString;
|
|
182
190
|
activation_id: import("typebox").TString;
|
|
@@ -185,4 +193,33 @@ export declare const ApplicationReleaseResultSchema: import("typebox").TObject<{
|
|
|
185
193
|
action: import("typebox").TUnion<[import("typebox").TLiteral<"activate">, import("typebox").TLiteral<"no-op">]>;
|
|
186
194
|
replayed: import("typebox").TBoolean;
|
|
187
195
|
execution_performed: import("typebox").TBoolean;
|
|
196
|
+
backend_receipt: import("typebox").TObject<{
|
|
197
|
+
schema: import("typebox").TLiteral<"supacloud.application-backend-receipt.v1">;
|
|
198
|
+
project_ref: import("typebox").TString;
|
|
199
|
+
application_id: import("typebox").TString;
|
|
200
|
+
environment_id: import("typebox").TString;
|
|
201
|
+
release_id: import("typebox").TString;
|
|
202
|
+
manifest_sha256: import("typebox").TString;
|
|
203
|
+
mutation_id: import("typebox").TString;
|
|
204
|
+
plan_sha256: import("typebox").TString;
|
|
205
|
+
backup_id: import("typebox").TUnion<[import("typebox").TString, import("typebox").TNull]>;
|
|
206
|
+
before_ledger_digest: import("typebox").TString;
|
|
207
|
+
after_ledger_digest: import("typebox").TString;
|
|
208
|
+
applied_versions: import("typebox").TArray<import("typebox").TString>;
|
|
209
|
+
schema_reloaded: import("typebox").TBoolean;
|
|
210
|
+
postgrest_healthy: import("typebox").TBoolean;
|
|
211
|
+
}>;
|
|
212
|
+
web: import("typebox").TOptional<import("typebox").TObject<{
|
|
213
|
+
deployment_id: import("typebox").TString;
|
|
214
|
+
release_id: import("typebox").TString;
|
|
215
|
+
activation_id: import("typebox").TString;
|
|
216
|
+
replayed: import("typebox").TBoolean;
|
|
217
|
+
}>>;
|
|
218
|
+
}>;
|
|
219
|
+
export declare const ApplicationDeployWebRequestSchema: import("typebox").TObject<{
|
|
220
|
+
deployment_id: import("typebox").TString;
|
|
221
|
+
release_id: import("typebox").TString;
|
|
222
|
+
expected_active_release_id: import("typebox").TString;
|
|
223
|
+
expected_activation_id: import("typebox").TString;
|
|
224
|
+
mutation_id: import("typebox").TString;
|
|
188
225
|
}>;
|
|
@@ -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_release_transfer_plan" | "get_promotion_plan" | "get_promotion_status" | "get_rollback_snapshot" | "get_history" | "get_deploy_plan" | "get_preview_plan" | "list_previews" | "get_preview" | "put_configuration" | "
|
|
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_promotion_status" | "get_rollback_snapshot" | "get_history" | "get_deploy_plan" | "get_preview_plan" | "list_previews" | "get_preview" | "put_configuration" | "deploy_application" | "transfer_release" | "promote_application" | "reconcile_promotion" | "create_preview" | "reconcile_preview" | "cleanup_preview" | "deploy_release" | "rollback_release" | "reconcile_activation" | "retire_activation")[]>;
|
|
13
13
|
ref: Type.TString;
|
|
14
14
|
id: Type.TString;
|
|
15
15
|
environment_id: Type.TOptional<Type.TString>;
|
|
@@ -19,6 +19,13 @@ export declare const APPLICATION_TOOL_SCHEMA: {
|
|
|
19
19
|
configuration_path: Type.TOptional<Type.TString>;
|
|
20
20
|
configuration_write: Type.TOptional<Type.TUnknown>;
|
|
21
21
|
manifest_path: Type.TOptional<Type.TString>;
|
|
22
|
+
web: Type.TOptional<Type.TObject<{
|
|
23
|
+
deployment_id: Type.TString;
|
|
24
|
+
release_id: Type.TString;
|
|
25
|
+
expected_active_release_id: Type.TString;
|
|
26
|
+
expected_activation_id: Type.TString;
|
|
27
|
+
mutation_id: Type.TString;
|
|
28
|
+
}>>;
|
|
22
29
|
release_id: Type.TOptional<Type.TString>;
|
|
23
30
|
source_ref: Type.TOptional<Type.TString>;
|
|
24
31
|
source_release_id: Type.TOptional<Type.TString>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.90.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.
|
|
56
|
+
"@supacloud/delivery": "0.15.0",
|
|
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.
|
|
62
|
+
"@supacloud/delivery": "0.15.0",
|
|
63
63
|
"@graphql-tools/utils": "^12.0.3"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
@@ -23,9 +23,9 @@ Load this reference when selecting a command surface or when a user asks an AI t
|
|
|
23
23
|
| Inspect or back up a remote database | `supabase db_pull`, `migration_list`, `db_dump`, `gen_types` | Requires explicit PostgreSQL DSN; redact it |
|
|
24
24
|
| Replay the release-canary fixture stage receipt | `release release_canary_fixture_stage_replay` | Dual-bind Management project context and that project's service-role application origin; accepts only the exact subject/request UUID pair and a strict idempotent receipt |
|
|
25
25
|
| Disable a staged release-canary fixture | `release release_canary_fixture_disable_replay` | Uses the fixed disable RPC once, accepts idempotent false/true receipts, then queries the existing pending RPC with issuer/subject and requires authoritative JSON `false`; fixture binding comes from the strict disable receipt |
|
|
26
|
-
| Rebind release scope manifest to upstream base commit | `release scope_rebind` | Local action;
|
|
27
|
-
| Inspect release scope manifest and git alignment | `release scope_inspect` | Local action;
|
|
28
|
-
| Scaffold a new canonical release scope manifest | `release scope_create` | Local action; outputs
|
|
26
|
+
| Rebind release scope manifest to upstream base commit | `release scope_rebind` | Local maintenance action; requires the exact `--environment_id`, updates only scopes bound to that environment, supports `--dry_run`/`--task`, and recalculates `scope_sha256` |
|
|
27
|
+
| Inspect release scope manifest and git alignment | `release scope_inspect` | Local maintenance action; requires the exact `--environment_id`, rejects legacy or cross-environment scopes, returns canonical hash, and checks alignment with HEAD / origin/main |
|
|
28
|
+
| Scaffold a new canonical release scope manifest | `release scope_create` | Local maintenance action; requires `--environment_id` and outputs a current-main-bound scope with sorted functions, migrations, and environment identity |
|
|
29
29
|
| Preview/apply migrations remotely | `supabase push` | Always dry-run first; production needs explicit approval |
|
|
30
30
|
| Mark proven-equivalent historical migrations as applied | `database baseline_migrations` | Dry-run, schema-equivalence proof, backup, explicit approval |
|
|
31
31
|
| Inspect auth users or generate a controlled login link | `auth list_users`, `auth get_user`, `auth generate_link` | User reads are bounded; generation supports only `magiclink`, `recovery`, and `invite`, requires production confirmation, and returns only a validated action URL |
|
|
@@ -51,7 +51,7 @@ until a project-scoped context is resolved.
|
|
|
51
51
|
- `secrets`: project secret management; never print values after write.
|
|
52
52
|
- `queue`, `task_events`, `diagnostics`: asynchronous workload operations and bounded diagnostics.
|
|
53
53
|
- `gateway`: project route/config/rebuild operations.
|
|
54
|
-
- `release`: local release scope
|
|
54
|
+
- `release`: local release scope maintenance (`scope_rebind`, `scope_inspect`, `scope_create`), verified backup/PostgREST lifecycle controls, plus production-confirmed, non-retried, strict release-canary fixture stage and disable replay actions. Scope maintenance is not a prerequisite for the normal `supacloud --env <name> deploy` flow.
|
|
55
55
|
- `ai`: inspect or install this packaged Skill.
|
|
56
56
|
|
|
57
57
|
## Safe inspection pattern
|