@caelo-cms/provisioning 0.10.46 → 0.10.47

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.
Files changed (44) hide show
  1. package/dist/adc-impersonation.d.ts.map +1 -1
  2. package/dist/adc-impersonation.js +23 -1
  3. package/dist/adc-impersonation.js.map +1 -1
  4. package/dist/cli.js +4 -1
  5. package/dist/cli.js.map +1 -1
  6. package/dist/install-state.d.ts +9 -0
  7. package/dist/install-state.d.ts.map +1 -1
  8. package/dist/install-state.js +21 -1
  9. package/dist/install-state.js.map +1 -1
  10. package/dist/lifecycle.d.ts.map +1 -1
  11. package/dist/lifecycle.js +5 -1
  12. package/dist/lifecycle.js.map +1 -1
  13. package/dist/state-attach.d.ts +23 -0
  14. package/dist/state-attach.d.ts.map +1 -0
  15. package/dist/state-attach.js +112 -0
  16. package/dist/state-attach.js.map +1 -0
  17. package/dist/state-backend.d.ts +11 -0
  18. package/dist/state-backend.d.ts.map +1 -1
  19. package/dist/state-backend.js +20 -0
  20. package/dist/state-backend.js.map +1 -1
  21. package/dist/state-command.d.ts +20 -0
  22. package/dist/state-command.d.ts.map +1 -0
  23. package/dist/state-command.js +237 -0
  24. package/dist/state-command.js.map +1 -0
  25. package/dist/state-migrate.d.ts +121 -0
  26. package/dist/state-migrate.d.ts.map +1 -0
  27. package/dist/state-migrate.js +290 -0
  28. package/dist/state-migrate.js.map +1 -0
  29. package/dist/state-pulumi.d.ts +17 -0
  30. package/dist/state-pulumi.d.ts.map +1 -0
  31. package/dist/state-pulumi.js +218 -0
  32. package/dist/state-pulumi.js.map +1 -0
  33. package/dist/state-store.d.ts +27 -0
  34. package/dist/state-store.d.ts.map +1 -1
  35. package/dist/state-store.js +65 -0
  36. package/dist/state-store.js.map +1 -1
  37. package/dist/wizards/gcp-pulumi.d.ts +5 -0
  38. package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
  39. package/dist/wizards/gcp-pulumi.js +7 -5
  40. package/dist/wizards/gcp-pulumi.js.map +1 -1
  41. package/dist/wizards/gcp.d.ts.map +1 -1
  42. package/dist/wizards/gcp.js +69 -25
  43. package/dist/wizards/gcp.js.map +1 -1
  44. package/package.json +2 -2
@@ -0,0 +1,112 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * `cms-provision attach --project <id>` (#696): make an install whose state
4
+ * lives in its ops bucket manageable from this machine (a new laptop, Cloud
5
+ * Shell). It reads `gs://<project>-caelo-ops/install/install.json`, checks
6
+ * it, and mirrors install.json + progress.json into `~/.caelo-<id>/`. No
7
+ * secret is copied, because none exists outside the operator's Google
8
+ * login: Pulumi reaches the state and its KMS key with that login.
9
+ *
10
+ * Refuses rather than overwrites: an install directory here that holds this
11
+ * install's local state (it was never migrated) or another backend.
12
+ */
13
+ import { existsSync, mkdirSync } from "node:fs";
14
+ import { selfCommand } from "./self-command.js";
15
+ import { opsBucketName, parseStateBackend } from "./state-backend.js";
16
+ import { GcsStateStore, LocalStateStore } from "./state-store.js";
17
+ /** Install ids are `<provider>-<slug>` (install-state.ts deriveInstallId). */
18
+ const INSTALL_ID = /^[a-z0-9][a-z0-9-]{0,80}$/;
19
+ /**
20
+ * Attach the install in `projectId` to this machine.
21
+ *
22
+ * @param deps.run gcloud runner (tests pass a fake bucket)
23
+ * @param deps.rootFor `~/.caelo-<id>` for an install id
24
+ * @param deps.hasLocalState whether a dir holds a local Pulumi stack
25
+ */
26
+ export function attachInstall(projectId, deps) {
27
+ const bucket = opsBucketName(projectId);
28
+ let text;
29
+ try {
30
+ // Peek: the directory to mirror into isn't known (or checked) yet.
31
+ text = new GcsStateStore(bucket, "/nonexistent", deps.run).peek("install.json");
32
+ }
33
+ catch (e) {
34
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
35
+ }
36
+ if (text === null) {
37
+ return {
38
+ ok: false,
39
+ error: `gs://${bucket} holds no install.json. If this install's state is still on the machine that installed it, run \`${selfCommand("state", "migrate")}\` there first.`,
40
+ };
41
+ }
42
+ let remote;
43
+ try {
44
+ remote = JSON.parse(text);
45
+ }
46
+ catch {
47
+ return { ok: false, error: `gs://${bucket}/install/install.json is not valid JSON.` };
48
+ }
49
+ const installId = typeof remote.installId === "string" ? remote.installId : "";
50
+ if (!INSTALL_ID.test(installId) || remote.projectId !== projectId) {
51
+ return {
52
+ ok: false,
53
+ error: `gs://${bucket}/install/install.json does not describe an install in project ${projectId}; refusing to attach.`,
54
+ };
55
+ }
56
+ let backend;
57
+ try {
58
+ backend = parseStateBackend(remote.stateBackend);
59
+ }
60
+ catch (e) {
61
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
62
+ }
63
+ if (backend?.kind !== "gcs") {
64
+ return {
65
+ ok: false,
66
+ error: `${installId}'s state was moved back to a machine (state migrate --to local); there is nothing in gs://${bucket} to attach to. Run \`state migrate --to remote\` on that machine first.`,
67
+ };
68
+ }
69
+ if (backend.bucket !== bucket) {
70
+ return {
71
+ ok: false,
72
+ error: `install.json in gs://${bucket} names another bucket (${backend.bucket}); refusing to attach.`,
73
+ };
74
+ }
75
+ const root = deps.rootFor(installId);
76
+ const localText = new LocalStateStore(root).read("install.json");
77
+ // Local Pulumi state blocks the attach whether or not an install.json
78
+ // sits next to it: attaching would hide it.
79
+ if (deps.hasLocalState(root)) {
80
+ return {
81
+ ok: false,
82
+ error: `${root} holds a local Pulumi state of this install. Attaching would hide it; run \`${selfCommand("state", "migrate", "--install", installId)}\` instead, or move ${root} aside if that state is stale.`,
83
+ };
84
+ }
85
+ let status = "attached";
86
+ if (localText !== null) {
87
+ const local = JSON.parse(localText);
88
+ const localBackend = parseStateBackend(local.stateBackend);
89
+ if (localBackend?.kind === "gcs" && localBackend.bucket === bucket) {
90
+ status = "refreshed";
91
+ }
92
+ else {
93
+ return {
94
+ ok: false,
95
+ error: `${root}/install.json exists and names another backend. Move ${root} aside, then re-run attach.`,
96
+ };
97
+ }
98
+ }
99
+ if (!existsSync(root))
100
+ mkdirSync(root, { recursive: true, mode: 0o700 });
101
+ // Mirror exactly the install.json validated above (a re-read could fetch
102
+ // a different one written meanwhile); progress.json is bookkeeping.
103
+ new LocalStateStore(root).write("install.json", JSON.parse(text));
104
+ new GcsStateStore(bucket, root, deps.run).read("progress.json");
105
+ return {
106
+ ok: true,
107
+ installId,
108
+ domain: typeof remote.domain === "string" ? remote.domain : installId,
109
+ status,
110
+ };
111
+ }
112
+ //# sourceMappingURL=state-attach.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-attach.js","sourceRoot":"","sources":["../src/state-attach.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAEnC;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAmB,MAAM,kBAAkB,CAAC;AAWnF,8EAA8E;AAC9E,MAAM,UAAU,GAAG,2BAA2B,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,SAAiB,EACjB,IAIC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,IAAmB,CAAC;IACxB,IAAI,CAAC;QACH,mEAAmE;QACnE,IAAI,GAAG,IAAI,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAClF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,CAAC;IACD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,QAAQ,MAAM,oGAAoG,WAAW,CAAC,OAAO,EAAE,SAAS,CAAC,iBAAiB;SAC1K,CAAC;IACJ,CAAC;IACD,IAAI,MAKH,CAAC;IACF,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,MAAM,0CAA0C,EAAE,CAAC;IACxF,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/E,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAClE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,QAAQ,MAAM,iEAAiE,SAAS,uBAAuB;SACvH,CAAC;IACJ,CAAC;IACD,IAAI,OAA6C,CAAC;IAClD,IAAI,CAAC;QACH,OAAO,GAAG,iBAAiB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,CAAC;IACD,IAAI,OAAO,EAAE,IAAI,KAAK,KAAK,EAAE,CAAC;QAC5B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,GAAG,SAAS,6FAA6F,MAAM,yEAAyE;SAChM,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC9B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,wBAAwB,MAAM,0BAA0B,OAAO,CAAC,MAAM,wBAAwB;SACtG,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACjE,sEAAsE;IACtE,4CAA4C;IAC5C,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,GAAG,IAAI,+EAA+E,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,CAAC,uBAAuB,IAAI,gCAAgC;SAChN,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,GAA6B,UAAU,CAAC;IAClD,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAA+B,CAAC;QAClE,MAAM,YAAY,GAAG,iBAAiB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC3D,IAAI,YAAY,EAAE,IAAI,KAAK,KAAK,IAAI,YAAY,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACnE,MAAM,GAAG,WAAW,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,GAAG,IAAI,wDAAwD,IAAI,6BAA6B;aACxG,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,yEAAyE;IACzE,oEAAoE;IACpE,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,IAAI,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAChE,OAAO;QACL,EAAE,EAAE,IAAI;QACR,SAAS;QACT,MAAM,EAAE,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACrE,MAAM;KACP,CAAC;AACJ,CAAC"}
@@ -100,4 +100,15 @@ export declare function chooseInstallBackend(meta: {
100
100
  readonly provider: string;
101
101
  readonly stateBackend?: StateBackendRecord;
102
102
  }, localStateExists: boolean, deployed?: boolean): StateBackendRecord["kind"];
103
+ /**
104
+ * What `upgrade` tells the operator about an install whose Pulumi state is
105
+ * still on this machine (#696), or null when it lives in the install's own
106
+ * cloud. `upgrade` never moves state itself: the move has its own
107
+ * preflight, backup and verification (`state migrate`).
108
+ */
109
+ export declare function localStateNotice(meta: {
110
+ readonly installId: string;
111
+ readonly provider: string;
112
+ readonly stateBackend?: StateBackendRecord;
113
+ }): string | null;
103
114
  //# sourceMappingURL=state-backend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-backend.d.ts","sourceRoot":"","sources":["../src/state-backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state-backend.d.ts","sourceRoot":"","sources":["../src/state-backend.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,7 @@
24
24
  */
25
25
  import { existsSync, readdirSync } from "node:fs";
26
26
  import { join } from "node:path";
27
+ import { selfCommand } from "./self-command.js";
27
28
  /** Name of the Cloud KMS key ring holding Caelo's keys in the install project. */
28
29
  export const OPS_KEY_RING = "caelo";
29
30
  /** Name of the Cloud KMS key Pulumi encrypts the stack's secrets with. */
@@ -177,4 +178,23 @@ export function chooseInstallBackend(meta, localStateExists, deployed = false) {
177
178
  }
178
179
  return "gcs";
179
180
  }
181
+ /**
182
+ * What `upgrade` tells the operator about an install whose Pulumi state is
183
+ * still on this machine (#696), or null when it lives in the install's own
184
+ * cloud. `upgrade` never moves state itself: the move has its own
185
+ * preflight, backup and verification (`state migrate`).
186
+ */
187
+ export function localStateNotice(meta) {
188
+ if (installBackend(meta).kind !== "local")
189
+ return null;
190
+ if (meta.provider !== "gcp" && meta.provider !== "gcp-firebase")
191
+ return null;
192
+ return [
193
+ `This install's Pulumi state and passphrase live only on this machine (~/.caelo-${meta.installId}/).`,
194
+ "Lose this machine and the install's infrastructure can no longer be converged from anywhere.",
195
+ "Move them into the install's own GCP project (versioned bucket + Cloud KMS key, < $0.10/mo):",
196
+ ` ${selfCommand("state", "migrate", "--install", meta.installId, "--dry-run")}`,
197
+ ` ${selfCommand("state", "migrate", "--install", meta.installId)}`,
198
+ ].join("\n");
199
+ }
180
200
  //# sourceMappingURL=state-backend.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-backend.js","sourceRoot":"","sources":["../src/state-backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state-backend.js","sourceRoot":"","sources":["../src/state-backend.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAEnC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,kFAAkF;AAClF,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AACpC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,aAAa,GAAG,cAAc,CAAC;AAC5C,mEAAmE;AACnE,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAC1C,4EAA4E;AAC5E,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,CAAC;AA2B5C,0FAA0F;AAC1F,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,OAAO,GAAG,SAAS,YAAY,CAAC;AAClC,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,MAAc;IAC7D,OAAO,YAAY,SAAS,cAAc,MAAM,aAAa,YAAY,eAAe,aAAa,EAAE,CAAC;AAC1G,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,gBAAgB,CAAC,SAAiB,EAAE,MAAc;IAChE,OAAO;QACL,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC;QAChC,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,cAAc,CAAC,IAAoD;IACjF,OAAO,IAAI,CAAC,YAAY,IAAK,EAAE,IAAI,EAAE,OAAO,EAAY,CAAC;AAC3D,CAAC;AAED,MAAM,MAAM,GAAG,qCAAqC,CAAC;AACrD,MAAM,OAAO,GACX,yHAAyH,CAAC;AAE5H;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,CAAC,GAAG,KAAuC,CAAC;IAClD,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACtD,IACE,CAAC;QACD,CAAC,CAAC,IAAI,KAAK,KAAK;QAChB,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;QACrB,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,EACtB,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7D,CAAC;IACD,MAAM,IAAI,KAAK,CACb,2CAA2C,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,gHAAgH,aAAa,KAAK,CACnM,CAAC;AACJ,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,aAAa,CAAC,WAAmB;IAC/C,OAAO,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAgF,EAChF,IAAoF;IAEpF,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,QAAQ,MAAM,CAAC,MAAM,IAAI,iBAAiB,EAAE,CAAC;QACzD,OAAO;YACL,IAAI,EAAE,KAAK;YACX,GAAG;YACH,eAAe,EAAE,YAAY,MAAM,CAAC,MAAM,EAAE;YAC5C,OAAO,EAAE;gBACP,kBAAkB,EAAE,GAAG;gBACvB,wCAAwC,EAAE,MAAM;aACjD;SACF,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IACzC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,YAAY,IAAI,CAAC,SAAS,0IAA0I,IAAI,CAAC,SAAS,IAAI,CACvL,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,UAAU,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;IACxD,OAAO;QACL,IAAI,EAAE,OAAO;QACb,GAAG;QACH,eAAe,EAAE,YAAY;QAC7B,OAAO,EAAE;YACP,kBAAkB,EAAE,GAAG;YACvB,wBAAwB,EAAE,UAAU;YACpC,wCAAwC,EAAE,MAAM;SACjD;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAoD,EACpD,WAAmB;IAEnB,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO,2BAA2B,MAAM,CAAC,MAAM,IAAI,iBAAiB,gDAAgD,CAAC;IACvH,CAAC;IACD,OAAO,6BAA6B,aAAa,CAAC,WAAW,CAAC,oCAAoC,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,mBAAmB,CAAC,kDAAkD,CAAC;AACxM,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,eAAe,CAC7B,IAAoD,EACpD,WAAmB;IAEnB,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,MAAM,CAAC,IAAI,KAAK,KAAK;QAC1B,CAAC,CAAC,QAAQ,MAAM,CAAC,MAAM,wBAAwB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG;QAC9F,CAAC,CAAC,GAAG,aAAa,CAAC,WAAW,CAAC,6CAA6C,CAAC;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACrE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,sEAAsE;IACtE,iCAAiC;IACjC,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAIC,EACD,gBAAyB,EACzB,QAAQ,GAAG,KAAK;IAEhB,IAAI,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;IACrD,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,IAAI,CAAC,QAAQ,KAAK,cAAc;QAAE,OAAO,OAAO,CAAC;IAChF,IAAI,gBAAgB;QAAE,OAAO,OAAO,CAAC;IACrC,IAAI,QAAQ,EAAE,CAAC;QACb,qEAAqE;QACrE,8CAA8C;QAC9C,MAAM,IAAI,KAAK,CACb,WAAW,IAAI,CAAC,SAAS,IAAI,EAAE,2FAA2F,IAAI,CAAC,SAAS,IAAI,cAAc,6DAA6D,CACxN,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAIhC;IACC,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,IAAI,CAAC,QAAQ,KAAK,cAAc;QAAE,OAAO,IAAI,CAAC;IAC7E,OAAO;QACL,kFAAkF,IAAI,CAAC,SAAS,KAAK;QACrG,8FAA8F;QAC9F,8FAA8F;QAC9F,KAAK,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE;QAChF,KAAK,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE;KACpE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,20 @@
1
+ /** Where install-dir archives go: `~/.caelo-backups/` (mode 700). */
2
+ export declare function backupsDir(home?: string): string;
3
+ /**
4
+ * Archive `~/.caelo-<id>/` (secrets and state included) to
5
+ * `~/.caelo-backups/<id>-<time>.tar.zst`, or `.tar.gz` when `zstd` is not
6
+ * installed. The archive and its directory are private (600 / 700): it
7
+ * holds the passphrase. Throws when the archive can't be made.
8
+ *
9
+ * @returns the archive's path
10
+ */
11
+ export declare function backupInstallDir(installId: string, opts?: {
12
+ home?: string;
13
+ now?: Date;
14
+ hasZstd?: boolean;
15
+ }): string;
16
+ /** `cms-provision state migrate …`. */
17
+ export declare function stateCommand(argv: readonly string[]): Promise<void>;
18
+ /** `cms-provision attach --project <id>`. */
19
+ export declare function attachCommand(argv: readonly string[]): Promise<void>;
20
+ //# sourceMappingURL=state-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-command.d.ts","sourceRoot":"","sources":["../src/state-command.ts"],"names":[],"mappings":"AAiDA,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAE3D;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1D,MAAM,CAiCR;AAeD,uCAAuC;AACvC,wBAAsB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuJzE;AAED,6CAA6C;AAC7C,wBAAsB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB1E"}
@@ -0,0 +1,237 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * CLI wiring for `cms-provision state migrate` and `cms-provision attach`
4
+ * (#696): parses the flags, picks the install, and hands the engines
5
+ * (state-migrate.ts, state-attach.ts) the real gcloud, Pulumi and file
6
+ * system.
7
+ */
8
+ import { spawnSync } from "node:child_process";
9
+ import { randomBytes } from "node:crypto";
10
+ import { chmodSync, existsSync, mkdirSync, renameSync, rmSync } from "node:fs";
11
+ import { homedir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { confirm, isCancel, log, note } from "@clack/prompts";
14
+ import { bold, red } from "kleur/colors";
15
+ import { activeAccount } from "./gcloud.js";
16
+ import { adoptStateBackend, currentMetadata, ensureInstallDir, installFlag, installRoot, installStagingHost, listInstalls, readSecret, recordedImageDigests, selectInstall, writeSecret, } from "./install-state.js";
17
+ import { installIapAllowlist } from "./operator-access.js";
18
+ import { ensureOpsBackend } from "./ops-backend.js";
19
+ import { provisionerServiceAccountEmail } from "./provisioner-identity.js";
20
+ import { selfCommand } from "./self-command.js";
21
+ import { attachInstall } from "./state-attach.js";
22
+ import { gcsBackendRecord, hasLocalPulumiState, installBackend, localStateDir, parseStateBackend, resolveBackend, } from "./state-backend.js";
23
+ import { migrateState } from "./state-migrate.js";
24
+ import { realStatePulumiOps } from "./state-pulumi.js";
25
+ import { acquireStateLock, GcsStateStore, realSyncGcloud } from "./state-store.js";
26
+ import { clackReporter } from "./upgrade/reporter.js";
27
+ import { GCP_STACK_KNOB_DEFAULTS } from "./wizards/gcp-pulumi.js";
28
+ /** Where install-dir archives go: `~/.caelo-backups/` (mode 700). */
29
+ export function backupsDir(home = homedir()) {
30
+ return join(home, ".caelo-backups");
31
+ }
32
+ /**
33
+ * Archive `~/.caelo-<id>/` (secrets and state included) to
34
+ * `~/.caelo-backups/<id>-<time>.tar.zst`, or `.tar.gz` when `zstd` is not
35
+ * installed. The archive and its directory are private (600 / 700): it
36
+ * holds the passphrase. Throws when the archive can't be made.
37
+ *
38
+ * @returns the archive's path
39
+ */
40
+ export function backupInstallDir(installId, opts = {}) {
41
+ const home = opts.home ?? homedir();
42
+ const dir = backupsDir(home);
43
+ if (!existsSync(dir))
44
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
45
+ chmodSync(dir, 0o700);
46
+ const stamp = (opts.now ?? new Date()).toISOString().replace(/[:.]/g, "-");
47
+ const base = join(dir, `${installId}-${stamp}`);
48
+ const zstd = opts.hasZstd ?? spawnSync("zstd", ["--version"], { stdio: "ignore" }).status === 0;
49
+ const source = ["-C", home, `.caelo-${installId}`];
50
+ const run = (cmd, args) => {
51
+ const r = spawnSync(cmd, args, { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
52
+ if (r.status !== 0) {
53
+ throw new Error(`${cmd} failed while archiving ~/.caelo-${installId}: ${(r.stderr ?? "").trim()}`);
54
+ }
55
+ };
56
+ if (zstd) {
57
+ const tar = `${base}.tar`;
58
+ try {
59
+ run("tar", ["-cf", tar, ...source]);
60
+ chmodSync(tar, 0o600);
61
+ run("zstd", ["-q", "--rm", "-o", `${tar}.zst`, tar]);
62
+ }
63
+ finally {
64
+ rmSync(tar, { force: true });
65
+ }
66
+ chmodSync(`${tar}.zst`, 0o600);
67
+ return `${tar}.zst`;
68
+ }
69
+ const out = `${base}.tar.gz`;
70
+ run("tar", ["-czf", out, ...source]);
71
+ chmodSync(out, 0o600);
72
+ return out;
73
+ }
74
+ /** The value of `--<name> <v>` / `--<name>=<v>` on argv. */
75
+ function flagValue(argv, name) {
76
+ for (let i = 0; i < argv.length; i++) {
77
+ const a = argv[i] ?? "";
78
+ if (a === `--${name}`)
79
+ return argv[i + 1];
80
+ if (a.startsWith(`--${name}=`))
81
+ return a.slice(name.length + 3);
82
+ }
83
+ return undefined;
84
+ }
85
+ const USAGE = "Usage: cms-provision state migrate [--install <install-id or domain>] [--to remote|local] [--dry-run] [--yes]";
86
+ /** `cms-provision state migrate …`. */
87
+ export async function stateCommand(argv) {
88
+ if (argv[3] !== "migrate") {
89
+ console.log(USAGE);
90
+ process.exit(2);
91
+ }
92
+ const to = flagValue(argv, "to") ?? "remote";
93
+ if (to !== "remote" && to !== "local") {
94
+ console.error(`--to must be remote or local.\n${USAGE}`);
95
+ process.exit(2);
96
+ }
97
+ const dryRun = argv.includes("--dry-run");
98
+ const selected = selectInstall(listInstalls(), installFlag(argv));
99
+ if (!selected.ok) {
100
+ console.error(selected.message);
101
+ process.exit(1);
102
+ }
103
+ const meta = currentMetadata(selected.meta);
104
+ const { installId } = meta;
105
+ const root = installRoot(installId);
106
+ if ((meta.provider !== "gcp" && meta.provider !== "gcp-firebase") ||
107
+ !meta.projectId ||
108
+ !meta.region) {
109
+ log.error(red(`${installId} (${meta.provider}) keeps its state on the host or has no project/region recorded; state migrate is for gcp and gcp-firebase installs.`));
110
+ process.exit(1);
111
+ }
112
+ const { projectId, region, provider } = meta;
113
+ const imageDigests = recordedImageDigests(meta);
114
+ if (!imageDigests) {
115
+ log.error(red(`install.json of ${installId} records no image digests. Run ${bold(selfCommand("upgrade", "--install", installId))} first, then migrate.`));
116
+ process.exit(1);
117
+ }
118
+ const operator = await activeAccount();
119
+ if (!operator) {
120
+ log.error(red("No active gcloud account. Run `gcloud auth login`, then re-run."));
121
+ process.exit(1);
122
+ }
123
+ const provisioner = provisionerServiceAccountEmail(projectId);
124
+ note([
125
+ `Install: ${bold(meta.domain)} (${installId}, project ${projectId}, ${region})`,
126
+ `Direction: ${to === "remote" ? `this machine → gs://${gcsBackendRecord(projectId, region).bucket} + Cloud KMS` : "the install's bucket → this machine"}`,
127
+ dryRun
128
+ ? "Dry run: nothing will be changed"
129
+ : "The source stays in use until the copy is verified.",
130
+ ].join("\n"), "state migrate");
131
+ const result = await migrateState(meta, { to, dryRun }, {
132
+ pulumi: realStatePulumiOps({
133
+ provider,
134
+ installRoot: root,
135
+ impersonateServiceAccount: provisioner,
136
+ stackConfig: {
137
+ ...GCP_STACK_KNOB_DEFAULTS,
138
+ provider,
139
+ projectId,
140
+ domain: meta.domain,
141
+ stagingDomain: installStagingHost(meta),
142
+ ownerEmail: meta.ownerEmail,
143
+ region,
144
+ iapAllowlist: installIapAllowlist(meta.ownerEmail),
145
+ imageDigests,
146
+ },
147
+ }),
148
+ report: clackReporter(),
149
+ resolve: (record) => resolveBackend({ installId, stateBackend: record }, { installRoot: root, readPassphrase: () => readSecret(installId, "pulumi-passphrase") }),
150
+ confirmCost: async (costNote) => {
151
+ log.info(costNote);
152
+ if (argv.includes("--yes"))
153
+ return true;
154
+ if (!process.stdin.isTTY) {
155
+ log.error(red("Not a terminal: pass --yes to accept the cost above."));
156
+ return false;
157
+ }
158
+ const yes = await confirm({ message: "Create them and continue?", initialValue: true });
159
+ return !isCancel(yes) && yes === true;
160
+ },
161
+ ensureRemote: (dry) => ensureOpsBackend({ projectId, region, operatorAccount: operator, provisionerEmail: provisioner }, { dryRun: dry }),
162
+ backup: async () => backupInstallDir(installId),
163
+ localStateEmpty: () => !hasLocalPulumiState(root),
164
+ prepareLocalTarget: () => {
165
+ mkdirSync(localStateDir(root), { recursive: true, mode: 0o700 });
166
+ if (!readSecret(installId, "pulumi-passphrase")) {
167
+ writeSecret(installId, "pulumi-passphrase", randomBytes(32).toString("hex"));
168
+ }
169
+ },
170
+ lock: async () => {
171
+ // The source's lock (local file or bucket object) keeps installer
172
+ // runs out; the bucket's also keeps out a migration started on
173
+ // another machine when the bucket is the target.
174
+ const source = meta.stateBackend ?? { kind: "local" };
175
+ const releases = [acquireStateLock(source, root, "state migrate")];
176
+ if (source.kind === "local" && to === "remote") {
177
+ try {
178
+ releases.push(acquireStateLock(gcsBackendRecord(projectId, region), root, "state migrate"));
179
+ }
180
+ catch (e) {
181
+ for (const r of releases)
182
+ r();
183
+ throw e;
184
+ }
185
+ }
186
+ return async () => {
187
+ for (const r of releases.reverse())
188
+ r();
189
+ };
190
+ },
191
+ remoteRecordedBackend: () => {
192
+ const text = new GcsStateStore(gcsBackendRecord(projectId, region).bucket, root).peek("install.json");
193
+ if (text === null)
194
+ return null;
195
+ return installBackend({ stateBackend: parseStateBackend(JSON.parse(text).stateBackend) })
196
+ .kind;
197
+ },
198
+ adopt: (record) => {
199
+ adoptStateBackend(installId, record);
200
+ },
201
+ retireLocalState: () => {
202
+ const dir = localStateDir(root);
203
+ if (!existsSync(dir))
204
+ return null;
205
+ const moved = `${dir}.migrated-${new Date().toISOString().replace(/[:.]/g, "-")}`;
206
+ renameSync(dir, moved);
207
+ return moved;
208
+ },
209
+ });
210
+ if (!result.ok) {
211
+ log.error(red(result.error));
212
+ process.exit(1);
213
+ }
214
+ }
215
+ /** `cms-provision attach --project <id>`. */
216
+ export async function attachCommand(argv) {
217
+ const projectId = flagValue(argv, "project");
218
+ if (!projectId || projectId.startsWith("-")) {
219
+ console.log("Usage: cms-provision attach --project <gcp-project-id>");
220
+ process.exit(2);
221
+ }
222
+ const r = attachInstall(projectId, {
223
+ run: realSyncGcloud,
224
+ rootFor: installRoot,
225
+ hasLocalState: hasLocalPulumiState,
226
+ });
227
+ if (!r.ok) {
228
+ log.error(red(r.error));
229
+ process.exit(1);
230
+ }
231
+ ensureInstallDir(r.installId);
232
+ log.success(r.status === "refreshed"
233
+ ? `${r.domain} was already attached; its local copy of install.json is up to date.`
234
+ : `Attached ${bold(r.domain)} (${r.installId}). status, upgrade, backup and re-running the installer now work from this machine.`);
235
+ log.info("Infrastructure runs need Application Default Credentials once on this machine: gcloud auth application-default login");
236
+ }
237
+ //# sourceMappingURL=state-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-command.js","sourceRoot":"","sources":["../src/state-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * `cms-provision state migrate` (#696): move an existing install's Pulumi
3
+ * state, secrets provider and install metadata between this machine
4
+ * (`~/.caelo-<id>/state` + passphrase) and the install's own GCP project
5
+ * (`gs://<project>-caelo-ops` + Cloud KMS), in either direction.
6
+ *
7
+ * The source stays authoritative until the very end:
8
+ *
9
+ * 1. preflight — Pulumi CLI version (`pulumi stack migrate` from
10
+ * {@link STACK_MIGRATE_MIN_VERSION}, else export/import +
11
+ * change-secrets-provider), the source stack exists, has no pending
12
+ * operations, and a preview of it shows no changes (an unconverged
13
+ * stack can't prove the copy is identical, so it is refused);
14
+ * 2. a mode-600 archive of `~/.caelo-<id>/` (tar.zst, else tar.gz);
15
+ * 3. the target: bucket + KMS key ensured (remote) or an empty local
16
+ * state dir + passphrase (local); a leftover copy from an interrupted
17
+ * run is removed — it was never authoritative;
18
+ * 4. copy, re-encrypting the secrets for the target's provider;
19
+ * 5. verify — a preview of the target shows no changes and its outputs
20
+ * (secrets included) hash identically to the source's; on any
21
+ * mismatch the target copy is removed and nothing else changes;
22
+ * 6. flip — install.json records the new backend (locally and in the
23
+ * bucket); the old local state dir is renamed, never deleted.
24
+ *
25
+ * `--dry-run` runs the read-only preflight and reports what it would do.
26
+ * The engine takes its side effects as {@link MigrateDeps} so the whole
27
+ * sequence is tested against fakes (state-migrate.test.ts); state-command.ts
28
+ * wires the real Pulumi CLI, gcloud and file system.
29
+ */
30
+ import type { OpsBackendResult } from "./ops-backend.js";
31
+ import { type ResolvedBackend, type StateBackendRecord } from "./state-backend.js";
32
+ import type { UpgradeReporter } from "./upgrade/reporter.js";
33
+ /** First Pulumi CLI release with `pulumi stack migrate` between DIY backends. */
34
+ export declare const STACK_MIGRATE_MIN_VERSION = "3.254.0";
35
+ /** What the ops bucket + KMS key cost (wizards/gcp-cost.ts carries the same line). */
36
+ export declare const OPS_BACKEND_COST_NOTE = "Creates (if missing) a versioned Cloud Storage bucket + one Cloud KMS key in the install's project: < $0.10/mo (KMS key version $0.06/mo, a few MB of state).";
37
+ /** The Pulumi operations a migration needs, against one backend at a time. */
38
+ export interface StatePulumiOps {
39
+ /** `pulumi version` without the `v`, or null when the CLI is missing. */
40
+ cliVersion(): Promise<string | null>;
41
+ stackExists(backend: ResolvedBackend): Promise<boolean>;
42
+ /** Pending operations recorded in the stack's state (an interrupted run). */
43
+ pendingOperations(backend: ResolvedBackend): Promise<number>;
44
+ /** Steps a preview would take other than "same". */
45
+ previewChanges(backend: ResolvedBackend): Promise<number>;
46
+ /** sha256 of the stack outputs, secrets included. Never the values. */
47
+ outputsDigest(backend: ResolvedBackend): Promise<string>;
48
+ /** Whether a Pulumi operation holds the stack's lock in `backend` right now. */
49
+ locked(backend: ResolvedBackend): Promise<boolean>;
50
+ /** sha256 of the stack's exported checkpoint (ciphertext; never decrypted). */
51
+ stateDigest(backend: ResolvedBackend): Promise<string>;
52
+ /** `pulumi stack migrate <from> --secrets-provider <to's>` into `to`. */
53
+ stackMigrate(from: ResolvedBackend, to: ResolvedBackend): Promise<void>;
54
+ /** export → init → import → change-secrets-provider (older CLIs). */
55
+ exportImport(from: ResolvedBackend, to: ResolvedBackend): Promise<void>;
56
+ /** Remove the stack's state from `backend` (cloud resources untouched). */
57
+ removeStack(backend: ResolvedBackend): Promise<void>;
58
+ }
59
+ /** Everything the migration touches outside Pulumi. */
60
+ export interface MigrateDeps {
61
+ readonly pulumi: StatePulumiOps;
62
+ readonly report: UpgradeReporter;
63
+ /** The install's backend resolved for Pulumi. */
64
+ resolve(record: StateBackendRecord): ResolvedBackend;
65
+ /** Ask the operator to accept the ops backend's cost; false = cancel. */
66
+ confirmCost(note: string): Promise<boolean>;
67
+ /** Ensure (or, dry, inspect) the ops bucket + KMS key. */
68
+ ensureRemote(dryRun: boolean): Promise<OpsBackendResult>;
69
+ /** Archive `~/.caelo-<id>/`; returns the archive's path. */
70
+ backup(): Promise<string>;
71
+ /** Whether the local state dir is absent or holds no stack. */
72
+ localStateEmpty(): boolean;
73
+ /** Create the local state dir (mode 700) and `secrets/pulumi-passphrase` if missing. */
74
+ prepareLocalTarget(): void;
75
+ /**
76
+ * The backend the bucket's install.json names (`null` when the bucket or
77
+ * the document doesn't exist). `gcs` there means the bucket copy is
78
+ * authoritative — some machine finished a migration — and must never be
79
+ * replaced from this machine's (then stale) local state.
80
+ */
81
+ remoteRecordedBackend(): StateBackendRecord["kind"] | null;
82
+ /** Record the new backend (install-state.ts adoptStateBackend). */
83
+ adopt(record: StateBackendRecord): void;
84
+ /**
85
+ * Take the install's state lock (state-store.ts acquireStateLock: the
86
+ * source's, plus the bucket's when it is the target), which installer
87
+ * runs hold through `pulumi up` too; throws when another run holds it.
88
+ * Returns the release function.
89
+ */
90
+ lock(): Promise<() => Promise<void>>;
91
+ /** Rename the local state dir out of the way; returns where it went. */
92
+ retireLocalState(): string | null;
93
+ }
94
+ export interface MigrateOptions {
95
+ readonly to: "remote" | "local";
96
+ readonly dryRun: boolean;
97
+ }
98
+ export type MigrateResult = {
99
+ readonly ok: true;
100
+ readonly changed: boolean;
101
+ } | {
102
+ readonly ok: false;
103
+ readonly error: string;
104
+ };
105
+ /** The subset of install metadata a migration reads. */
106
+ export interface MigrateInstall {
107
+ readonly installId: string;
108
+ readonly provider: string;
109
+ readonly projectId: string | null;
110
+ readonly region: string | null;
111
+ readonly stateBackend?: StateBackendRecord;
112
+ }
113
+ /** a >= b for dotted numeric versions (`3.254.0`). */
114
+ export declare function versionAtLeast(a: string, b: string): boolean;
115
+ /**
116
+ * Run the migration. Never throws for an expected failure: the result says
117
+ * what failed, and at every failure point the source backend is still the
118
+ * one install.json names.
119
+ */
120
+ export declare function migrateState(meta: MigrateInstall, opts: MigrateOptions, deps: MigrateDeps): Promise<MigrateResult>;
121
+ //# sourceMappingURL=state-migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-migrate.d.ts","sourceRoot":"","sources":["../src/state-migrate.ts"],"names":[],"mappings":"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"}