@caelo-cms/provisioning 0.10.47 → 0.10.49

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 (321) hide show
  1. package/caddy/Caddyfile.production +1 -1
  2. package/caddy/Caddyfile.staging +1 -1
  3. package/dist/agent/apply.d.ts +45 -0
  4. package/dist/agent/apply.d.ts.map +1 -0
  5. package/dist/agent/apply.js +83 -0
  6. package/dist/agent/apply.js.map +1 -0
  7. package/dist/agent/cli-provenance.d.ts +42 -0
  8. package/dist/agent/cli-provenance.d.ts.map +1 -0
  9. package/dist/agent/cli-provenance.js +138 -0
  10. package/dist/agent/cli-provenance.js.map +1 -0
  11. package/dist/agent/command.d.ts +79 -0
  12. package/dist/agent/command.d.ts.map +1 -0
  13. package/dist/agent/command.js +454 -0
  14. package/dist/agent/command.js.map +1 -0
  15. package/dist/agent/config.d.ts +30 -0
  16. package/dist/agent/config.d.ts.map +1 -0
  17. package/dist/agent/config.js +60 -0
  18. package/dist/agent/config.js.map +1 -0
  19. package/dist/agent/lock.d.ts +13 -0
  20. package/dist/agent/lock.d.ts.map +1 -0
  21. package/dist/agent/lock.js +100 -0
  22. package/dist/agent/lock.js.map +1 -0
  23. package/dist/agent/queue.d.ts +35 -0
  24. package/dist/agent/queue.d.ts.map +1 -0
  25. package/dist/agent/queue.js +50 -0
  26. package/dist/agent/queue.js.map +1 -0
  27. package/dist/agent/run.d.ts +97 -0
  28. package/dist/agent/run.d.ts.map +1 -0
  29. package/dist/agent/run.js +198 -0
  30. package/dist/agent/run.js.map +1 -0
  31. package/dist/agent/systemd.d.ts +55 -0
  32. package/dist/agent/systemd.d.ts.map +1 -0
  33. package/dist/agent/systemd.js +171 -0
  34. package/dist/agent/systemd.js.map +1 -0
  35. package/dist/agent/units.d.ts +44 -0
  36. package/dist/agent/units.d.ts.map +1 -0
  37. package/dist/agent/units.js +127 -0
  38. package/dist/agent/units.js.map +1 -0
  39. package/dist/agent-db.js +18402 -0
  40. package/dist/cli.js +119 -16
  41. package/dist/cli.js.map +1 -1
  42. package/dist/cloudsql-iam-user.d.ts +39 -0
  43. package/dist/cloudsql-iam-user.d.ts.map +1 -0
  44. package/dist/cloudsql-iam-user.js +119 -0
  45. package/dist/cloudsql-iam-user.js.map +1 -0
  46. package/dist/compose.d.ts +16 -0
  47. package/dist/compose.d.ts.map +1 -1
  48. package/dist/compose.js +26 -1
  49. package/dist/compose.js.map +1 -1
  50. package/dist/dns/index.d.ts +2 -0
  51. package/dist/dns/index.d.ts.map +1 -1
  52. package/dist/dns/index.js +3 -2
  53. package/dist/dns/index.js.map +1 -1
  54. package/dist/dns/manual.d.ts +5 -0
  55. package/dist/dns/manual.d.ts.map +1 -1
  56. package/dist/dns/manual.js +28 -12
  57. package/dist/dns/manual.js.map +1 -1
  58. package/dist/gcloud.d.ts +2 -0
  59. package/dist/gcloud.d.ts.map +1 -1
  60. package/dist/gcloud.js +1 -1
  61. package/dist/gcloud.js.map +1 -1
  62. package/dist/gcp-names.d.ts +15 -0
  63. package/dist/gcp-names.d.ts.map +1 -1
  64. package/dist/gcp-names.js +23 -0
  65. package/dist/gcp-names.js.map +1 -1
  66. package/dist/index.d.ts +6 -0
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +4 -0
  69. package/dist/index.js.map +1 -1
  70. package/dist/infra-contract.d.ts +1 -1
  71. package/dist/infra-contract.js +7 -1
  72. package/dist/infra-contract.js.map +1 -1
  73. package/dist/install-job-runner.d.ts +13 -0
  74. package/dist/install-job-runner.d.ts.map +1 -0
  75. package/dist/install-job-runner.js +155 -0
  76. package/dist/install-job-runner.js.map +1 -0
  77. package/dist/install-job.d.ts +210 -0
  78. package/dist/install-job.d.ts.map +1 -0
  79. package/dist/install-job.js +449 -0
  80. package/dist/install-job.js.map +1 -0
  81. package/dist/install-state.d.ts +10 -1
  82. package/dist/install-state.d.ts.map +1 -1
  83. package/dist/install-state.js +10 -2
  84. package/dist/install-state.js.map +1 -1
  85. package/dist/lifecycle.d.ts.map +1 -1
  86. package/dist/lifecycle.js +19 -4
  87. package/dist/lifecycle.js.map +1 -1
  88. package/dist/migration-runner.d.ts +5 -0
  89. package/dist/migration-runner.d.ts.map +1 -1
  90. package/dist/migration-runner.js +1 -1
  91. package/dist/migration-runner.js.map +1 -1
  92. package/dist/operator-access.d.ts +14 -8
  93. package/dist/operator-access.d.ts.map +1 -1
  94. package/dist/operator-access.js +16 -102
  95. package/dist/operator-access.js.map +1 -1
  96. package/dist/release-manifest.d.ts +2 -41
  97. package/dist/release-manifest.d.ts.map +1 -1
  98. package/dist/release-manifest.js +18 -121
  99. package/dist/release-manifest.js.map +1 -1
  100. package/dist/run-mode.d.ts +27 -0
  101. package/dist/run-mode.d.ts.map +1 -0
  102. package/dist/run-mode.js +22 -0
  103. package/dist/run-mode.js.map +1 -0
  104. package/dist/runtime-secrets.d.ts +19 -0
  105. package/dist/runtime-secrets.d.ts.map +1 -1
  106. package/dist/runtime-secrets.js +44 -0
  107. package/dist/runtime-secrets.js.map +1 -1
  108. package/dist/self-hosted-roles.d.ts +22 -1
  109. package/dist/self-hosted-roles.d.ts.map +1 -1
  110. package/dist/self-hosted-roles.js +81 -12
  111. package/dist/self-hosted-roles.js.map +1 -1
  112. package/dist/self-hosted-upgrade.d.ts +17 -0
  113. package/dist/self-hosted-upgrade.d.ts.map +1 -1
  114. package/dist/self-hosted-upgrade.js +16 -1
  115. package/dist/self-hosted-upgrade.js.map +1 -1
  116. package/dist/stack-contract.d.ts +6 -0
  117. package/dist/stack-contract.d.ts.map +1 -1
  118. package/dist/stack-contract.js +10 -1
  119. package/dist/stack-contract.js.map +1 -1
  120. package/dist/upgrade/converge.d.ts +6 -0
  121. package/dist/upgrade/converge.d.ts.map +1 -1
  122. package/dist/upgrade/converge.js +59 -2
  123. package/dist/upgrade/converge.js.map +1 -1
  124. package/dist/upgrade/gcp-lookup.d.ts +6 -0
  125. package/dist/upgrade/gcp-lookup.d.ts.map +1 -1
  126. package/dist/upgrade/gcp-lookup.js +25 -0
  127. package/dist/upgrade/gcp-lookup.js.map +1 -1
  128. package/dist/upgrade/release-images.d.ts +26 -2
  129. package/dist/upgrade/release-images.d.ts.map +1 -1
  130. package/dist/upgrade/release-images.js +35 -2
  131. package/dist/upgrade/release-images.js.map +1 -1
  132. package/dist/upgrade/upgrade.d.ts +5 -0
  133. package/dist/upgrade/upgrade.d.ts.map +1 -1
  134. package/dist/upgrade/upgrade.js +28 -1
  135. package/dist/upgrade/upgrade.js.map +1 -1
  136. package/dist/upgrader-assess.d.ts +85 -0
  137. package/dist/upgrader-assess.d.ts.map +1 -0
  138. package/dist/upgrader-assess.js +122 -0
  139. package/dist/upgrader-assess.js.map +1 -0
  140. package/dist/upgrader-gcp.d.ts +17 -0
  141. package/dist/upgrader-gcp.d.ts.map +1 -0
  142. package/dist/upgrader-gcp.js +87 -0
  143. package/dist/upgrader-gcp.js.map +1 -0
  144. package/dist/upgrader-job.d.ts +133 -0
  145. package/dist/upgrader-job.d.ts.map +1 -0
  146. package/dist/upgrader-job.js +259 -0
  147. package/dist/upgrader-job.js.map +1 -0
  148. package/dist/upgrader-reporter.d.ts +35 -0
  149. package/dist/upgrader-reporter.d.ts.map +1 -0
  150. package/dist/upgrader-reporter.js +79 -0
  151. package/dist/upgrader-reporter.js.map +1 -0
  152. package/dist/upgrader-setup.d.ts +59 -0
  153. package/dist/upgrader-setup.d.ts.map +1 -0
  154. package/dist/upgrader-setup.js +308 -0
  155. package/dist/upgrader-setup.js.map +1 -0
  156. package/dist/wizard.d.ts +6 -0
  157. package/dist/wizard.d.ts.map +1 -1
  158. package/dist/wizard.js +32 -4
  159. package/dist/wizard.js.map +1 -1
  160. package/dist/wizards/gcp-firebase.d.ts +3 -0
  161. package/dist/wizards/gcp-firebase.d.ts.map +1 -1
  162. package/dist/wizards/gcp-firebase.js +1 -0
  163. package/dist/wizards/gcp-firebase.js.map +1 -1
  164. package/dist/wizards/gcp-install-job.d.ts +74 -0
  165. package/dist/wizards/gcp-install-job.d.ts.map +1 -0
  166. package/dist/wizards/gcp-install-job.js +398 -0
  167. package/dist/wizards/gcp-install-job.js.map +1 -0
  168. package/dist/wizards/gcp-prompts.d.ts +3 -0
  169. package/dist/wizards/gcp-prompts.d.ts.map +1 -0
  170. package/dist/wizards/gcp-prompts.js +25 -0
  171. package/dist/wizards/gcp-prompts.js.map +1 -0
  172. package/dist/wizards/gcp-pulumi.d.ts +4 -2
  173. package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
  174. package/dist/wizards/gcp-pulumi.js +1 -1
  175. package/dist/wizards/gcp-pulumi.js.map +1 -1
  176. package/dist/wizards/gcp.d.ts +40 -0
  177. package/dist/wizards/gcp.d.ts.map +1 -1
  178. package/dist/wizards/gcp.js +223 -59
  179. package/dist/wizards/gcp.js.map +1 -1
  180. package/package.json +6 -4
  181. package/src/adapter.ts +97 -0
  182. package/src/adc-impersonation.ts +176 -0
  183. package/src/admin-domain.test.ts +254 -0
  184. package/src/admin-domain.ts +245 -0
  185. package/src/admin-memory.test.ts +151 -0
  186. package/src/agent/apply.test.ts +185 -0
  187. package/src/agent/apply.ts +124 -0
  188. package/src/agent/cli-provenance.test.ts +159 -0
  189. package/src/agent/cli-provenance.ts +191 -0
  190. package/src/agent/command.test.ts +77 -0
  191. package/src/agent/command.ts +564 -0
  192. package/src/agent/config.ts +86 -0
  193. package/src/agent/lock-config.test.ts +97 -0
  194. package/src/agent/lock.ts +110 -0
  195. package/src/agent/queue.test.ts +10 -0
  196. package/src/agent/queue.ts +91 -0
  197. package/src/agent/run.test.ts +437 -0
  198. package/src/agent/run.ts +295 -0
  199. package/src/agent/systemd.ts +209 -0
  200. package/src/agent/units.test.ts +223 -0
  201. package/src/agent/units.ts +163 -0
  202. package/src/azure-front-door.test.ts +119 -0
  203. package/src/azure-front-door.ts +56 -0
  204. package/src/bootstrap-token.ts +20 -0
  205. package/src/caddy.ts +112 -0
  206. package/src/cdn-copy.ts +91 -0
  207. package/src/cert-wait.test.ts +194 -0
  208. package/src/cert-wait.ts +163 -0
  209. package/src/checkpoint-secrets.test.ts +380 -0
  210. package/src/checkpoint-secrets.ts +283 -0
  211. package/src/cli-routing.test.ts +45 -0
  212. package/src/cli-routing.ts +43 -0
  213. package/src/cli.ts +1491 -0
  214. package/src/cloudsql-iam-user.ts +147 -0
  215. package/src/compose.ts +334 -0
  216. package/src/default-firewall.test.ts +411 -0
  217. package/src/default-firewall.ts +342 -0
  218. package/src/deployed-release.test.ts +231 -0
  219. package/src/deployed-release.ts +220 -0
  220. package/src/dns/cloudflare.ts +182 -0
  221. package/src/dns/groups.test.ts +76 -0
  222. package/src/dns/groups.ts +86 -0
  223. package/src/dns/index.ts +56 -0
  224. package/src/dns/manual.ts +130 -0
  225. package/src/dns/types.ts +27 -0
  226. package/src/gateway-credentials.test.ts +180 -0
  227. package/src/gateway-credentials.ts +144 -0
  228. package/src/gcloud-components.test.ts +76 -0
  229. package/src/gcloud-components.ts +85 -0
  230. package/src/gcloud-retry.ts +41 -0
  231. package/src/gcloud-runner.test.ts +61 -0
  232. package/src/gcloud.test.ts +99 -0
  233. package/src/gcloud.ts +322 -0
  234. package/src/gcp-names.ts +135 -0
  235. package/src/index.test.ts +332 -0
  236. package/src/index.ts +114 -0
  237. package/src/infra-contract.ts +32 -0
  238. package/src/install-job-runner.ts +166 -0
  239. package/src/install-job.test.ts +566 -0
  240. package/src/install-job.ts +542 -0
  241. package/src/install-state.test.ts +239 -0
  242. package/src/install-state.ts +583 -0
  243. package/src/internal-token.ts +35 -0
  244. package/src/lifecycle.ts +900 -0
  245. package/src/mcp-iap-stacks.test.ts +58 -0
  246. package/src/mcp-iap.test.ts +149 -0
  247. package/src/mcp-iap.ts +131 -0
  248. package/src/media-volume.test.ts +296 -0
  249. package/src/migration-runner.ts +365 -0
  250. package/src/operator-access-stacks.test.ts +55 -0
  251. package/src/operator-access.test.ts +332 -0
  252. package/src/operator-access.ts +554 -0
  253. package/src/ops-backend.test.ts +320 -0
  254. package/src/ops-backend.ts +414 -0
  255. package/src/provisioner-identity.test.ts +482 -0
  256. package/src/provisioner-identity.ts +407 -0
  257. package/src/redirects-emit.ts +175 -0
  258. package/src/region-data.ts +428 -0
  259. package/src/regions.test.ts +277 -0
  260. package/src/regions.ts +370 -0
  261. package/src/release-contract.ts +57 -0
  262. package/src/release-manifest.test.ts +300 -0
  263. package/src/release-manifest.ts +310 -0
  264. package/src/run-mode.ts +40 -0
  265. package/src/runtime-secrets.test.ts +627 -0
  266. package/src/runtime-secrets.ts +754 -0
  267. package/src/self-command.test.ts +14 -0
  268. package/src/self-command.ts +21 -0
  269. package/src/self-hosted-compose.test.ts +177 -0
  270. package/src/self-hosted-release.ts +240 -0
  271. package/src/self-hosted-roles.integration.test.ts +161 -0
  272. package/src/self-hosted-roles.test.ts +124 -0
  273. package/src/self-hosted-roles.ts +203 -0
  274. package/src/self-hosted-upgrade.test.ts +394 -0
  275. package/src/self-hosted-upgrade.ts +548 -0
  276. package/src/stack-contract-parity.test.ts +199 -0
  277. package/src/stack-contract.test.ts +391 -0
  278. package/src/stack-contract.ts +888 -0
  279. package/src/stack-converge-secrets.test.ts +421 -0
  280. package/src/stack-converge.test.ts +833 -0
  281. package/src/stack-converge.ts +909 -0
  282. package/src/staging-admin.test.ts +246 -0
  283. package/src/staging-admin.ts +324 -0
  284. package/src/state-attach.test.ts +211 -0
  285. package/src/state-attach.ts +132 -0
  286. package/src/state-backend.test.ts +145 -0
  287. package/src/state-backend.ts +262 -0
  288. package/src/state-command.ts +293 -0
  289. package/src/state-migrate.test.ts +479 -0
  290. package/src/state-migrate.ts +434 -0
  291. package/src/state-pulumi.ts +289 -0
  292. package/src/state-store.test.ts +212 -0
  293. package/src/state-store.ts +243 -0
  294. package/src/upgrade/app-roll.ts +486 -0
  295. package/src/upgrade/backup.ts +109 -0
  296. package/src/upgrade/converge.ts +437 -0
  297. package/src/upgrade/gcp-lookup.ts +167 -0
  298. package/src/upgrade/health.test.ts +279 -0
  299. package/src/upgrade/health.ts +305 -0
  300. package/src/upgrade/release-images.test.ts +119 -0
  301. package/src/upgrade/release-images.ts +393 -0
  302. package/src/upgrade/reporter.ts +46 -0
  303. package/src/upgrade/upgrade.test.ts +626 -0
  304. package/src/upgrade/upgrade.ts +249 -0
  305. package/src/upgrader-assess.test.ts +306 -0
  306. package/src/upgrader-assess.ts +250 -0
  307. package/src/upgrader-gcp.ts +123 -0
  308. package/src/upgrader-job.test.ts +411 -0
  309. package/src/upgrader-job.ts +502 -0
  310. package/src/upgrader-reporter.test.ts +46 -0
  311. package/src/upgrader-reporter.ts +105 -0
  312. package/src/upgrader-setup.test.ts +169 -0
  313. package/src/upgrader-setup.ts +355 -0
  314. package/src/wizard.ts +425 -0
  315. package/src/wizards/gcp-cost.ts +188 -0
  316. package/src/wizards/gcp-firebase.ts +72 -0
  317. package/src/wizards/gcp-install-job.ts +506 -0
  318. package/src/wizards/gcp-prompts.ts +26 -0
  319. package/src/wizards/gcp-pulumi.test.ts +246 -0
  320. package/src/wizards/gcp-pulumi.ts +516 -0
  321. package/src/wizards/gcp.ts +1935 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-job-runner.js","sourceRoot":"","sources":["../src/install-job-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,210 @@
1
+ /**
2
+ * The install job (epic #696, Phase 3): the long half of a GCP install runs
3
+ * as a Cloud Run job in the install's own project instead of in the
4
+ * operator's terminal, so closing the Cloud Shell tab (or its 40-minute idle
5
+ * timeout) never breaks an install.
6
+ *
7
+ * Split of work:
8
+ * - The operator's terminal (Cloud Shell, or a laptop with
9
+ * `--install-job`) does everything that needs the operator's own Google
10
+ * identity: project, billing, APIs, the keyless provisioner SA, the ops
11
+ * bucket + KMS key, the cost table and its y/N. Then it starts the job
12
+ * and tails it.
13
+ * - The job — `caelo-ops install-job --project <id>` from the signed ops
14
+ * image, pinned by the digest the signed release manifest names, running
15
+ * AS `caelo-provisioner` (its runtime service account: no key, no
16
+ * impersonation) — runs `pulumi up`, the DNS + certificate waits,
17
+ * migrations, the operator-access job and the Owner setup link.
18
+ *
19
+ * Progress lives in the install's ops bucket next to install.json:
20
+ * `install/install-job.json` ({@link InstallJobStatus}) says which execution
21
+ * runs the install and how far it got; progress.json keeps the per-step
22
+ * checkpoints, so a restarted execution resumes where the last one stopped.
23
+ * Re-running the launcher reads the status and attaches to a running
24
+ * execution, shows a finished one, or starts a new one ({@link decideInstallJob}).
25
+ *
26
+ * The Owner setup link is a secret (#656): it never reaches the bucket or
27
+ * the job's logs. The job stores it in Secret Manager
28
+ * ({@link INSTALL_SETUP_LINK_SECRET}); the operator's terminal reads it there.
29
+ */
30
+ import { z } from "zod";
31
+ import type { GcloudRunner } from "./gcloud-retry.js";
32
+ import type { SyncGcloud } from "./state-store.js";
33
+ /** Name of the Cloud Run job that runs the install. */
34
+ export declare const INSTALL_JOB_NAME = "caelo-install";
35
+ /** Secret Manager secret the job stores the Owner setup link in. */
36
+ export declare const INSTALL_SETUP_LINK_SECRET = "caelo-install-setup-link";
37
+ /**
38
+ * Secret Manager secret holding the operator's `CLOUDFLARE_API_TOKEN` for
39
+ * the job's automatic DNS (dns/cloudflare.ts). Written by the launcher only
40
+ * when the operator's shell has the token; the job reads it as an env var
41
+ * mounted from the secret, so it never appears in the job definition.
42
+ */
43
+ export declare const INSTALL_CLOUDFLARE_TOKEN_SECRET = "caelo-install-cloudflare-token";
44
+ /**
45
+ * Repository of the `caelo-ops` image on the public Artifact Registry
46
+ * mirror. Cloud Run pulls from Artifact Registry, not from GHCR; the mirror
47
+ * carries the same digests and signatures (release-images.yml, `oras copy -r`).
48
+ */
49
+ export declare const OPS_IMAGE_REPOSITORY = "europe-west1-docker.pkg.dev/caelo-website/caelo-cms-images/ops";
50
+ /**
51
+ * Roles the provisioner needs on top of {@link PROVISIONER_ROLE_LIST} when it
52
+ * runs the install job instead of the operator: the operator-access sync job
53
+ * creates a project custom role and a Cloud Scheduler job (operator-access.ts),
54
+ * and the job's own output has to reach Cloud Logging so the terminal can
55
+ * tail it. Granted only to installs that use the job. Not an escalation: the
56
+ * provisioner already holds `resourcemanager.projectIamAdmin`.
57
+ */
58
+ export declare const INSTALL_JOB_ROLES: readonly string[];
59
+ /** The ops image pinned by digest. */
60
+ export declare function opsImageRef(digest: string): string;
61
+ /** What `install/install-job.json` records. */
62
+ export declare const InstallJobStatusSchema: z.ZodObject<{
63
+ statusVersion: z.ZodLiteral<1>;
64
+ installId: z.ZodString;
65
+ execution: z.ZodString;
66
+ state: z.ZodEnum<{
67
+ failed: "failed";
68
+ succeeded: "succeeded";
69
+ running: "running";
70
+ }>;
71
+ phase: z.ZodString;
72
+ detail: z.ZodOptional<z.ZodString>;
73
+ caeloVersion: z.ZodString;
74
+ startedAt: z.ZodString;
75
+ updatedAt: z.ZodString;
76
+ }, z.core.$strip>;
77
+ export type InstallJobStatus = z.infer<typeof InstallJobStatusSchema>;
78
+ /** `gs://<bucket>/install/install-job.json`. */
79
+ export declare function installJobStatusUrl(bucket: string): string;
80
+ /**
81
+ * The install job's status as the ops bucket holds it now (never cached:
82
+ * another process — the job — writes it), or null before the first start.
83
+ * Throws when the bucket can't be read or the document is malformed.
84
+ */
85
+ export declare function readInstallJobStatus(bucket: string, run: SyncGcloud): InstallJobStatus | null;
86
+ /** Replace the status document. Refuses secret material (#656). */
87
+ export declare function writeInstallJobStatus(bucket: string, status: InstallJobStatus, run: SyncGcloud): void;
88
+ /**
89
+ * Write `status` only if the document doesn't name its execution yet and
90
+ * nobody wrote it since this call read it (`--if-generation-match`). The
91
+ * launcher records a just-started execution this way: the execution itself
92
+ * may already have written a newer phase — or its failure — which must win.
93
+ *
94
+ * @returns whether this call's write landed
95
+ */
96
+ export declare function claimInstallJobStatus(bucket: string, status: InstallJobStatus, run: SyncGcloud): boolean;
97
+ /** `gs://<bucket>/install/install-job.lock` — held while a launcher decides and starts. */
98
+ export declare function installJobLockUrl(bucket: string): string;
99
+ /**
100
+ * Take the launch lock (created only if absent). A lock left by a crashed
101
+ * launcher is reported with the command to remove it, never broken
102
+ * automatically — like the install's state lock (state-store.ts).
103
+ *
104
+ * @returns the release function
105
+ */
106
+ export declare function acquireLaunchLock(bucket: string, run: SyncGcloud): () => void;
107
+ /** Where a Cloud Run execution stands. */
108
+ export type ExecutionState = "running" | "succeeded" | "failed" | "missing";
109
+ /** Read one execution of the install job. */
110
+ export declare function describeExecution(run: GcloudRunner, target: {
111
+ projectId: string;
112
+ region: string;
113
+ execution: string;
114
+ }): Promise<ExecutionState>;
115
+ /** What the launcher does with an install that may already have a job. */
116
+ export type InstallJobDecision = {
117
+ readonly kind: "start";
118
+ readonly previous: InstallJobStatus | null;
119
+ readonly why: string;
120
+ } | {
121
+ readonly kind: "attach";
122
+ readonly status: InstallJobStatus;
123
+ } | {
124
+ readonly kind: "done";
125
+ readonly status: InstallJobStatus;
126
+ };
127
+ /**
128
+ * Decide from the bucket's status and the execution's live state. Only a
129
+ * status the job itself finished as `succeeded` counts as done; a running
130
+ * status whose execution has ended (crash, out of memory, cancelled, task
131
+ * timeout) is started again — the steps are checkpointed, so a new
132
+ * execution resumes where the last one stopped.
133
+ */
134
+ export declare function decideInstallJob(status: InstallJobStatus | null, live: ExecutionState | null): InstallJobDecision;
135
+ /** Everything the job definition depends on. */
136
+ export interface InstallJobSpec {
137
+ readonly projectId: string;
138
+ readonly region: string;
139
+ /** `caelo-provisioner@<project>` — the job's runtime identity. */
140
+ readonly serviceAccount: string;
141
+ /** {@link opsImageRef} of the release's signed ops image. */
142
+ readonly image: string;
143
+ /**
144
+ * Mount {@link INSTALL_CLOUDFLARE_TOKEN_SECRET} as `CLOUDFLARE_API_TOKEN`
145
+ * (the launcher stored the operator's token there); without it the job's
146
+ * DNS step prints the records and polls.
147
+ */
148
+ readonly cloudflareToken: boolean;
149
+ }
150
+ /**
151
+ * The `gcloud run jobs deploy` argv: creates the job or replaces its
152
+ * definition (image, args, identity) in place. The only argument the job
153
+ * gets is the project; everything else it reads from the ops bucket.
154
+ */
155
+ export declare function installJobDeployArgs(spec: InstallJobSpec): string[];
156
+ /** Create or update the job, then start one execution of it. */
157
+ export declare function startInstallJob(run: GcloudRunner, spec: InstallJobSpec): Promise<{
158
+ ok: true;
159
+ execution: string;
160
+ } | {
161
+ ok: false;
162
+ error: string;
163
+ }>;
164
+ /**
165
+ * The execution's log lines not printed yet, oldest first, looking back
166
+ * {@link LOG_LOOKBACK_MS} before `since` (the newest timestamp seen so far,
167
+ * or null for all). `seen` holds the insert ids already printed — the
168
+ * overlap and entries sharing a timestamp would otherwise repeat.
169
+ */
170
+ export declare function readExecutionLogs(run: GcloudRunner, target: {
171
+ projectId: string;
172
+ execution: string;
173
+ }, since: string | null, seen: Set<string>): Promise<{
174
+ lines: string[];
175
+ cursor: string | null;
176
+ }>;
177
+ /**
178
+ * Store `value` as the newest version of the install secret `secretId`
179
+ * (created with the install's secret replication if missing) and disable
180
+ * the versions before it, so exactly one value is readable. The value goes
181
+ * to gcloud on stdin, never on a command line.
182
+ */
183
+ export declare function storeInstallSecretValue(run: GcloudRunner, target: {
184
+ projectId: string;
185
+ region: string;
186
+ }, secretId: string, value: string): Promise<{
187
+ ok: true;
188
+ } | {
189
+ ok: false;
190
+ error: string;
191
+ }>;
192
+ /** Store the Owner setup link in {@link INSTALL_SETUP_LINK_SECRET}. */
193
+ export declare function storeSetupLink(run: GcloudRunner, target: {
194
+ projectId: string;
195
+ region: string;
196
+ }, link: string): Promise<{
197
+ ok: true;
198
+ } | {
199
+ ok: false;
200
+ error: string;
201
+ }>;
202
+ /**
203
+ * The Owner setup link the job stored, or null when there is none (the
204
+ * secret or an enabled version is missing).
205
+ *
206
+ * @throws when it can't be read for any other reason (no permission), with
207
+ * what the operator's account needs
208
+ */
209
+ export declare function readSetupLink(run: GcloudRunner, projectId: string): Promise<string | null>;
210
+ //# sourceMappingURL=install-job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-job.d.ts","sourceRoot":"","sources":["../src/install-job.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,449 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * The install job (epic #696, Phase 3): the long half of a GCP install runs
4
+ * as a Cloud Run job in the install's own project instead of in the
5
+ * operator's terminal, so closing the Cloud Shell tab (or its 40-minute idle
6
+ * timeout) never breaks an install.
7
+ *
8
+ * Split of work:
9
+ * - The operator's terminal (Cloud Shell, or a laptop with
10
+ * `--install-job`) does everything that needs the operator's own Google
11
+ * identity: project, billing, APIs, the keyless provisioner SA, the ops
12
+ * bucket + KMS key, the cost table and its y/N. Then it starts the job
13
+ * and tails it.
14
+ * - The job — `caelo-ops install-job --project <id>` from the signed ops
15
+ * image, pinned by the digest the signed release manifest names, running
16
+ * AS `caelo-provisioner` (its runtime service account: no key, no
17
+ * impersonation) — runs `pulumi up`, the DNS + certificate waits,
18
+ * migrations, the operator-access job and the Owner setup link.
19
+ *
20
+ * Progress lives in the install's ops bucket next to install.json:
21
+ * `install/install-job.json` ({@link InstallJobStatus}) says which execution
22
+ * runs the install and how far it got; progress.json keeps the per-step
23
+ * checkpoints, so a restarted execution resumes where the last one stopped.
24
+ * Re-running the launcher reads the status and attaches to a running
25
+ * execution, shows a finished one, or starts a new one ({@link decideInstallJob}).
26
+ *
27
+ * The Owner setup link is a secret (#656): it never reaches the bucket or
28
+ * the job's logs. The job stores it in Secret Manager
29
+ * ({@link INSTALL_SETUP_LINK_SECRET}); the operator's terminal reads it there.
30
+ */
31
+ import { z } from "zod";
32
+ import { assertNoSecrets } from "./checkpoint-secrets.js";
33
+ import { GCP_STACK_ENV } from "./gcp-names.js";
34
+ import { createInstallSecret } from "./runtime-secrets.js";
35
+ import { OPS_INSTALL_PREFIX } from "./state-backend.js";
36
+ import { IMAGE_DIGEST } from "./upgrade/release-images.js";
37
+ /** Name of the Cloud Run job that runs the install. */
38
+ export const INSTALL_JOB_NAME = "caelo-install";
39
+ /** Secret Manager secret the job stores the Owner setup link in. */
40
+ export const INSTALL_SETUP_LINK_SECRET = "caelo-install-setup-link";
41
+ /**
42
+ * Secret Manager secret holding the operator's `CLOUDFLARE_API_TOKEN` for
43
+ * the job's automatic DNS (dns/cloudflare.ts). Written by the launcher only
44
+ * when the operator's shell has the token; the job reads it as an env var
45
+ * mounted from the secret, so it never appears in the job definition.
46
+ */
47
+ export const INSTALL_CLOUDFLARE_TOKEN_SECRET = "caelo-install-cloudflare-token";
48
+ /**
49
+ * Repository of the `caelo-ops` image on the public Artifact Registry
50
+ * mirror. Cloud Run pulls from Artifact Registry, not from GHCR; the mirror
51
+ * carries the same digests and signatures (release-images.yml, `oras copy -r`).
52
+ */
53
+ export const OPS_IMAGE_REPOSITORY = "europe-west1-docker.pkg.dev/caelo-website/caelo-cms-images/ops";
54
+ /**
55
+ * Roles the provisioner needs on top of {@link PROVISIONER_ROLE_LIST} when it
56
+ * runs the install job instead of the operator: the operator-access sync job
57
+ * creates a project custom role and a Cloud Scheduler job (operator-access.ts),
58
+ * and the job's own output has to reach Cloud Logging so the terminal can
59
+ * tail it. Granted only to installs that use the job. Not an escalation: the
60
+ * provisioner already holds `resourcemanager.projectIamAdmin`.
61
+ */
62
+ export const INSTALL_JOB_ROLES = [
63
+ "roles/iam.roleAdmin",
64
+ "roles/cloudscheduler.admin",
65
+ "roles/logging.logWriter",
66
+ ];
67
+ /** Resources the job runs with: Pulumi + the Node language host want memory. */
68
+ const JOB_CPU = "2";
69
+ const JOB_MEMORY = "4Gi";
70
+ /** DNS (operator at the registrar) + certificate waits can each take an hour. */
71
+ const JOB_TASK_TIMEOUT = "6h";
72
+ /** The ops image pinned by digest. */
73
+ export function opsImageRef(digest) {
74
+ if (!IMAGE_DIGEST.test(digest))
75
+ throw new Error(`not an image digest: ${digest}`);
76
+ return `${OPS_IMAGE_REPOSITORY}@${digest}`;
77
+ }
78
+ /** What `install/install-job.json` records. */
79
+ export const InstallJobStatusSchema = z.object({
80
+ statusVersion: z.literal(1),
81
+ installId: z.string().min(1),
82
+ /** The Cloud Run execution running (or last running) the install. */
83
+ execution: z.string().min(1),
84
+ state: z.enum(["running", "succeeded", "failed"]),
85
+ /** Human-readable step the job is on, e.g. "Waiting for DNS". */
86
+ phase: z.string(),
87
+ /** Extra context for the phase (the DNS records to set, a failure reason). */
88
+ detail: z.string().optional(),
89
+ /** Caelo release the job's image belongs to. */
90
+ caeloVersion: z.string(),
91
+ startedAt: z.string(),
92
+ updatedAt: z.string(),
93
+ });
94
+ /** `gs://<bucket>/install/install-job.json`. */
95
+ export function installJobStatusUrl(bucket) {
96
+ return `gs://${bucket}/${OPS_INSTALL_PREFIX}/install-job.json`;
97
+ }
98
+ const NOT_FOUND = /matched no objects|No URLs matched|NotFound|NOT_FOUND|404|does not exist/i;
99
+ /**
100
+ * The install job's status as the ops bucket holds it now (never cached:
101
+ * another process — the job — writes it), or null before the first start.
102
+ * Throws when the bucket can't be read or the document is malformed.
103
+ */
104
+ export function readInstallJobStatus(bucket, run) {
105
+ const url = installJobStatusUrl(bucket);
106
+ const r = run(["storage", "cat", url]);
107
+ if (!r.ok) {
108
+ if (NOT_FOUND.test(r.stderr))
109
+ return null;
110
+ throw new Error(`Could not read ${url}: ${r.stderr.trim() || `gcloud exited ${r.exitCode}`}`);
111
+ }
112
+ let json;
113
+ try {
114
+ json = JSON.parse(r.stdout);
115
+ }
116
+ catch {
117
+ throw new Error(`${url} is not valid JSON; remove it and re-run to start the install again.`);
118
+ }
119
+ const parsed = InstallJobStatusSchema.safeParse(json);
120
+ if (!parsed.success) {
121
+ throw new Error(`${url} is not an install-job status this CLI understands (${parsed.error.message}); remove it and re-run.`);
122
+ }
123
+ return parsed.data;
124
+ }
125
+ /** Replace the status document. Refuses secret material (#656). */
126
+ export function writeInstallJobStatus(bucket, status, run) {
127
+ const parsed = InstallJobStatusSchema.parse(status);
128
+ assertNoSecrets(parsed, "install-job.json");
129
+ const url = installJobStatusUrl(bucket);
130
+ const r = run(["storage", "cp", "-", url, "--content-type=application/json"], `${JSON.stringify(parsed, null, 2)}\n`);
131
+ if (!r.ok) {
132
+ throw new Error(`Could not write ${url}: ${r.stderr.trim() || `gcloud exited ${r.exitCode}`}`);
133
+ }
134
+ }
135
+ /**
136
+ * Write `status` only if the document doesn't name its execution yet and
137
+ * nobody wrote it since this call read it (`--if-generation-match`). The
138
+ * launcher records a just-started execution this way: the execution itself
139
+ * may already have written a newer phase — or its failure — which must win.
140
+ *
141
+ * @returns whether this call's write landed
142
+ */
143
+ export function claimInstallJobStatus(bucket, status, run) {
144
+ const url = installJobStatusUrl(bucket);
145
+ const described = run(["storage", "objects", "describe", url, "--format=value(generation)"]);
146
+ let generation;
147
+ if (described.ok)
148
+ generation = described.stdout.trim();
149
+ else if (NOT_FOUND.test(described.stderr))
150
+ generation = "0";
151
+ else
152
+ throw new Error(`Could not read ${url}: ${described.stderr.trim()}`);
153
+ if (!/^\d+$/.test(generation))
154
+ throw new Error(`${url}: unexpected generation "${generation}"`);
155
+ if (readInstallJobStatus(bucket, run)?.execution === status.execution)
156
+ return false;
157
+ const parsed = InstallJobStatusSchema.parse(status);
158
+ assertNoSecrets(parsed, "install-job.json");
159
+ const r = run([
160
+ "storage",
161
+ "cp",
162
+ "-",
163
+ url,
164
+ "--content-type=application/json",
165
+ `--if-generation-match=${generation}`,
166
+ ], `${JSON.stringify(parsed, null, 2)}\n`);
167
+ if (r.ok)
168
+ return true;
169
+ if (/PreconditionFailed|412|pre-conditions/i.test(r.stderr))
170
+ return false;
171
+ throw new Error(`Could not write ${url}: ${r.stderr.trim() || `gcloud exited ${r.exitCode}`}`);
172
+ }
173
+ /** `gs://<bucket>/install/install-job.lock` — held while a launcher decides and starts. */
174
+ export function installJobLockUrl(bucket) {
175
+ return `gs://${bucket}/${OPS_INSTALL_PREFIX}/install-job.lock`;
176
+ }
177
+ /**
178
+ * Take the launch lock (created only if absent). A lock left by a crashed
179
+ * launcher is reported with the command to remove it, never broken
180
+ * automatically — like the install's state lock (state-store.ts).
181
+ *
182
+ * @returns the release function
183
+ */
184
+ export function acquireLaunchLock(bucket, run) {
185
+ const url = installJobLockUrl(bucket);
186
+ const body = JSON.stringify({ purpose: "start the install job", at: new Date().toISOString() });
187
+ const r = run(["storage", "cp", "-", url, "--if-generation-match=0"], body);
188
+ if (!r.ok) {
189
+ if (/PreconditionFailed|412|pre-conditions/i.test(r.stderr)) {
190
+ throw new Error(`Another terminal is starting this install's job right now. Re-run in a minute to follow it; if no other run is starting it, remove the stale lock: gcloud storage rm ${url}`);
191
+ }
192
+ throw new Error(`Could not take the launch lock ${url}: ${r.stderr.trim()}`);
193
+ }
194
+ return () => {
195
+ const rm = run(["storage", "rm", url]);
196
+ if (!rm.ok)
197
+ throw new Error(`Could not release ${url}; remove it: gcloud storage rm ${url}`);
198
+ };
199
+ }
200
+ /** Read one execution of the install job. */
201
+ export async function describeExecution(run, target) {
202
+ const r = await run([
203
+ "run",
204
+ "jobs",
205
+ "executions",
206
+ "describe",
207
+ target.execution,
208
+ `--region=${target.region}`,
209
+ `--project=${target.projectId}`,
210
+ "--format=json",
211
+ ]);
212
+ if (!r.ok) {
213
+ if (NOT_FOUND.test(r.stderr))
214
+ return "missing";
215
+ throw new Error(`Could not read execution ${target.execution}: ${r.stderr.trim() || `gcloud exited ${r.exitCode}`}`);
216
+ }
217
+ const d = JSON.parse(r.stdout || "{}");
218
+ const s = d.status ?? {};
219
+ if (!s.completionTime)
220
+ return "running";
221
+ return (s.succeededCount ?? 0) >= 1 && !s.failedCount && !s.cancelledCount
222
+ ? "succeeded"
223
+ : "failed";
224
+ }
225
+ /**
226
+ * Decide from the bucket's status and the execution's live state. Only a
227
+ * status the job itself finished as `succeeded` counts as done; a running
228
+ * status whose execution has ended (crash, out of memory, cancelled, task
229
+ * timeout) is started again — the steps are checkpointed, so a new
230
+ * execution resumes where the last one stopped.
231
+ */
232
+ export function decideInstallJob(status, live) {
233
+ if (!status)
234
+ return { kind: "start", previous: null, why: "no install job has run yet" };
235
+ if (status.state === "succeeded")
236
+ return { kind: "done", status };
237
+ if (status.state === "running" && live === "running")
238
+ return { kind: "attach", status };
239
+ if (status.state === "failed") {
240
+ return {
241
+ kind: "start",
242
+ previous: status,
243
+ why: `the last run stopped at "${status.phase}"${status.detail ? `: ${status.detail}` : ""}`,
244
+ };
245
+ }
246
+ return {
247
+ kind: "start",
248
+ previous: status,
249
+ why: `execution ${status.execution} ended (${live ?? "unknown"}) while at "${status.phase}"`,
250
+ };
251
+ }
252
+ /**
253
+ * The `gcloud run jobs deploy` argv: creates the job or replaces its
254
+ * definition (image, args, identity) in place. The only argument the job
255
+ * gets is the project; everything else it reads from the ops bucket.
256
+ */
257
+ export function installJobDeployArgs(spec) {
258
+ return [
259
+ "run",
260
+ "jobs",
261
+ "deploy",
262
+ INSTALL_JOB_NAME,
263
+ `--image=${spec.image}`,
264
+ `--region=${spec.region}`,
265
+ `--project=${spec.projectId}`,
266
+ `--service-account=${spec.serviceAccount}`,
267
+ `--args=install-job,--project,${spec.projectId}`,
268
+ "--tasks=1",
269
+ "--max-retries=0",
270
+ `--task-timeout=${JOB_TASK_TIMEOUT}`,
271
+ `--cpu=${JOB_CPU}`,
272
+ `--memory=${JOB_MEMORY}`,
273
+ // Plain log lines: no colour codes, no spinner frames.
274
+ "--set-env-vars=NO_COLOR=1,CI=true",
275
+ spec.cloudflareToken
276
+ ? `--set-secrets=CLOUDFLARE_API_TOKEN=${INSTALL_CLOUDFLARE_TOKEN_SECRET}:latest`
277
+ : "--clear-secrets",
278
+ "--labels=caelo-role=install",
279
+ "--quiet",
280
+ ];
281
+ }
282
+ /** Create or update the job, then start one execution of it. */
283
+ export async function startInstallJob(run, spec) {
284
+ const deploy = await run(installJobDeployArgs(spec));
285
+ if (!deploy.ok) {
286
+ return { ok: false, error: `create the install job: ${deploy.stderr.trim()}` };
287
+ }
288
+ const exec = await run([
289
+ "run",
290
+ "jobs",
291
+ "execute",
292
+ INSTALL_JOB_NAME,
293
+ `--region=${spec.region}`,
294
+ `--project=${spec.projectId}`,
295
+ "--async",
296
+ "--format=value(metadata.name)",
297
+ ]);
298
+ if (!exec.ok)
299
+ return { ok: false, error: `start the install job: ${exec.stderr.trim()}` };
300
+ // gcloud prints the resource with --format on stdout; older releases only
301
+ // announce "Execution [name] has successfully started" on stderr.
302
+ const execution = exec.stdout.trim().split(/\s+/)[0] || /Execution \[([^\]]+)\]/.exec(exec.stderr)?.[1] || "";
303
+ if (!/^[a-z0-9-]+$/.test(execution)) {
304
+ return {
305
+ ok: false,
306
+ error: `the install job started but gcloud did not name the execution (${exec.stderr.trim()})`,
307
+ };
308
+ }
309
+ return { ok: true, execution };
310
+ }
311
+ /** `since` minus the lookback window, as an RFC 3339 timestamp. */
312
+ function lookback(since) {
313
+ const t = Date.parse(since);
314
+ if (Number.isNaN(t))
315
+ throw new Error(`not a log timestamp: ${since}`);
316
+ return new Date(t - LOG_LOOKBACK_MS).toISOString();
317
+ }
318
+ /**
319
+ * How far before the newest line seen each poll looks again. Cloud Logging
320
+ * can make an entry queryable after a later-timestamped one; the overlap
321
+ * (deduplicated by insert id) lets such a late entry still reach the
322
+ * terminal.
323
+ */
324
+ const LOG_LOOKBACK_MS = 2 * 60 * 1000;
325
+ /** Entries per `gcloud logging read` page, and pages per poll. */
326
+ const LOG_PAGE_SIZE = 1000;
327
+ const LOG_MAX_PAGES = 20;
328
+ /**
329
+ * The execution's log lines not printed yet, oldest first, looking back
330
+ * {@link LOG_LOOKBACK_MS} before `since` (the newest timestamp seen so far,
331
+ * or null for all). `seen` holds the insert ids already printed — the
332
+ * overlap and entries sharing a timestamp would otherwise repeat.
333
+ */
334
+ export async function readExecutionLogs(run, target, since, seen) {
335
+ const lines = [];
336
+ let cursor = since;
337
+ // A page that comes back full may hide newer entries behind it (a chatty
338
+ // two-minute window): read on from its last timestamp, page by page.
339
+ let from = since ? lookback(since) : null;
340
+ for (let page = 0; page < LOG_MAX_PAGES; page++) {
341
+ const filter = [
342
+ 'resource.type="cloud_run_job"',
343
+ `resource.labels.job_name="${INSTALL_JOB_NAME}"`,
344
+ `labels."run.googleapis.com/execution_name"="${target.execution}"`,
345
+ ...(from ? [`timestamp>="${from}"`] : []),
346
+ ].join(" AND ");
347
+ const r = await run([
348
+ "logging",
349
+ "read",
350
+ filter,
351
+ `--project=${target.projectId}`,
352
+ "--order=asc",
353
+ `--limit=${LOG_PAGE_SIZE}`,
354
+ "--freshness=1d",
355
+ "--format=json",
356
+ ]);
357
+ if (!r.ok)
358
+ break;
359
+ const entries = JSON.parse(r.stdout || "[]");
360
+ for (const e of entries) {
361
+ const id = e.insertId ?? `${e.timestamp}:${e.textPayload}`;
362
+ if (seen.has(id))
363
+ continue;
364
+ seen.add(id);
365
+ if (e.timestamp && (cursor === null || Date.parse(e.timestamp) > Date.parse(cursor))) {
366
+ cursor = e.timestamp;
367
+ }
368
+ if (e.textPayload !== undefined)
369
+ lines.push(e.textPayload.replace(/\n$/, ""));
370
+ }
371
+ const last = entries.at(-1)?.timestamp ?? null;
372
+ // Not full: nothing newer is hidden. Full but one timestamp throughout:
373
+ // the next page would be the same one.
374
+ if (entries.length < LOG_PAGE_SIZE || last === null || last === from)
375
+ break;
376
+ from = last;
377
+ }
378
+ return { lines, cursor };
379
+ }
380
+ /**
381
+ * Store `value` as the newest version of the install secret `secretId`
382
+ * (created with the install's secret replication if missing) and disable
383
+ * the versions before it, so exactly one value is readable. The value goes
384
+ * to gcloud on stdin, never on a command line.
385
+ */
386
+ export async function storeInstallSecretValue(run, target, secretId, value) {
387
+ const project = `--project=${target.projectId}`;
388
+ const exists = await run(["secrets", "describe", secretId, project, "--format=value(name)"]);
389
+ if (!exists.ok) {
390
+ const created = await createInstallSecret({ ...target, env: GCP_STACK_ENV, secretId }, { run });
391
+ if (!created.ok)
392
+ return created;
393
+ }
394
+ const add = await run(["secrets", "versions", "add", secretId, project, "--data-file=-", "--format=value(name)"], { stdin: value });
395
+ if (!add.ok)
396
+ return { ok: false, error: `store ${secretId}: ${add.stderr.trim()}` };
397
+ const newest = add.stdout.trim().split("/").pop() ?? "";
398
+ const enabled = await run([
399
+ "secrets",
400
+ "versions",
401
+ "list",
402
+ secretId,
403
+ project,
404
+ "--filter=state:enabled",
405
+ "--format=value(name)",
406
+ ]);
407
+ for (const name of enabled.ok ? enabled.stdout.split("\n") : []) {
408
+ const version = name.trim().split("/").pop() ?? "";
409
+ if (version === "" || version === newest)
410
+ continue;
411
+ await run([
412
+ "secrets",
413
+ "versions",
414
+ "disable",
415
+ version,
416
+ `--secret=${secretId}`,
417
+ project,
418
+ "--quiet",
419
+ ]);
420
+ }
421
+ return { ok: true };
422
+ }
423
+ /** Store the Owner setup link in {@link INSTALL_SETUP_LINK_SECRET}. */
424
+ export function storeSetupLink(run, target, link) {
425
+ return storeInstallSecretValue(run, target, INSTALL_SETUP_LINK_SECRET, link);
426
+ }
427
+ /**
428
+ * The Owner setup link the job stored, or null when there is none (the
429
+ * secret or an enabled version is missing).
430
+ *
431
+ * @throws when it can't be read for any other reason (no permission), with
432
+ * what the operator's account needs
433
+ */
434
+ export async function readSetupLink(run, projectId) {
435
+ const r = await run([
436
+ "secrets",
437
+ "versions",
438
+ "access",
439
+ "latest",
440
+ `--secret=${INSTALL_SETUP_LINK_SECRET}`,
441
+ `--project=${projectId}`,
442
+ ]);
443
+ if (r.ok)
444
+ return r.stdout.trim() === "" ? null : r.stdout.trim();
445
+ if (/NOT_FOUND|not found|DISABLED|DESTROYED|FAILED_PRECONDITION/i.test(r.stderr))
446
+ return null;
447
+ throw new Error(`Could not read the Owner setup link (secret ${INSTALL_SETUP_LINK_SECRET} in ${projectId}): ${r.stderr.trim()}. Your gcloud account needs secretmanager.versions.access on the project (e.g. roles/secretmanager.secretAccessor or project Owner).`);
448
+ }
449
+ //# sourceMappingURL=install-job.js.map