@caelo-cms/provisioning 0.10.53 → 0.10.55

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 (90) hide show
  1. package/dist/agent-db.js +196 -109
  2. package/dist/audit-job-setup.d.ts +69 -0
  3. package/dist/audit-job-setup.d.ts.map +1 -0
  4. package/dist/audit-job-setup.js +317 -0
  5. package/dist/audit-job-setup.js.map +1 -0
  6. package/dist/caddy.d.ts +9 -0
  7. package/dist/caddy.d.ts.map +1 -1
  8. package/dist/caddy.js +38 -0
  9. package/dist/caddy.js.map +1 -1
  10. package/dist/cli.js +32 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/gcp-names.d.ts +16 -1
  13. package/dist/gcp-names.d.ts.map +1 -1
  14. package/dist/gcp-names.js +23 -0
  15. package/dist/gcp-names.js.map +1 -1
  16. package/dist/infra-contract.d.ts +1 -1
  17. package/dist/infra-contract.d.ts.map +1 -1
  18. package/dist/infra-contract.js +7 -1
  19. package/dist/infra-contract.js.map +1 -1
  20. package/dist/private-file.d.ts +26 -0
  21. package/dist/private-file.d.ts.map +1 -0
  22. package/dist/private-file.js +49 -0
  23. package/dist/private-file.js.map +1 -0
  24. package/dist/release-contract.d.ts.map +1 -1
  25. package/dist/release-contract.js +3 -0
  26. package/dist/release-contract.js.map +1 -1
  27. package/dist/stack-contract.d.ts +8 -0
  28. package/dist/stack-contract.d.ts.map +1 -1
  29. package/dist/stack-contract.js +13 -1
  30. package/dist/stack-contract.js.map +1 -1
  31. package/dist/tar-entries.d.ts.map +1 -1
  32. package/dist/tar-entries.js +84 -9
  33. package/dist/tar-entries.js.map +1 -1
  34. package/dist/upgrade/app-roll.d.ts.map +1 -1
  35. package/dist/upgrade/app-roll.js +35 -0
  36. package/dist/upgrade/app-roll.js.map +1 -1
  37. package/dist/upgrade/audit-job-roll.d.ts +38 -0
  38. package/dist/upgrade/audit-job-roll.d.ts.map +1 -0
  39. package/dist/upgrade/audit-job-roll.js +43 -0
  40. package/dist/upgrade/audit-job-roll.js.map +1 -0
  41. package/dist/upgrade/converge.d.ts.map +1 -1
  42. package/dist/upgrade/converge.js +33 -2
  43. package/dist/upgrade/converge.js.map +1 -1
  44. package/dist/upgrader-setup.d.ts.map +1 -1
  45. package/dist/upgrader-setup.js +9 -3
  46. package/dist/upgrader-setup.js.map +1 -1
  47. package/dist/wizards/gcp-cost.js +3 -3
  48. package/dist/wizards/gcp-cost.js.map +1 -1
  49. package/dist/wizards/gcp-pulumi.d.ts +6 -0
  50. package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
  51. package/dist/wizards/gcp-pulumi.js +7 -0
  52. package/dist/wizards/gcp-pulumi.js.map +1 -1
  53. package/dist/wizards/gcp.d.ts.map +1 -1
  54. package/dist/wizards/gcp.js +31 -0
  55. package/dist/wizards/gcp.js.map +1 -1
  56. package/package.json +4 -4
  57. package/src/agent/run.test.ts +1 -0
  58. package/src/audit-job-setup.test.ts +234 -0
  59. package/src/audit-job-setup.ts +360 -0
  60. package/src/caddy-redirects.docker.test.ts +19 -6
  61. package/src/caddy.test.ts +30 -0
  62. package/src/caddy.ts +43 -0
  63. package/src/cli-init-validation.test.ts +44 -0
  64. package/src/cli.ts +36 -7
  65. package/src/gcp-names.ts +28 -1
  66. package/src/infra-contract.test.ts +3 -3
  67. package/src/infra-contract.ts +7 -1
  68. package/src/private-file.test.ts +80 -0
  69. package/src/private-file.ts +50 -0
  70. package/src/release-contract.ts +3 -0
  71. package/src/stack-contract.test.ts +4 -0
  72. package/src/stack-contract.ts +14 -0
  73. package/src/stack-converge.test.ts +1 -1
  74. package/src/tar-entries.test.ts +149 -7
  75. package/src/tar-entries.ts +90 -9
  76. package/src/upgrade/app-roll.ts +39 -0
  77. package/src/upgrade/audit-job-roll.ts +68 -0
  78. package/src/upgrade/converge.ts +42 -1
  79. package/src/upgrade/upgrade.test.ts +76 -2
  80. package/src/upgrader-assess.test.ts +1 -0
  81. package/src/upgrader-job.test.ts +1 -0
  82. package/src/upgrader-setup.test.ts +1 -0
  83. package/src/upgrader-setup.ts +9 -2
  84. package/src/wizards/gcp-cost.ts +3 -3
  85. package/src/wizards/gcp-pulumi.ts +7 -0
  86. package/src/wizards/gcp.ts +37 -0
  87. package/stacks/aws/index.ts +8 -0
  88. package/stacks/azure/index.ts +9 -0
  89. package/stacks/gcp/index.ts +13 -0
  90. package/stacks/gcp-firebase/index.ts +13 -0
@@ -21,8 +21,9 @@
21
21
  * move traffic, recreate the migration job, update its own job image.
22
22
  * It includes no `*.setIamPolicy`.
23
23
  * - `roles/iam.serviceAccountUser` on exactly the admin's and the
24
- * gateway's runtime service accounts (a revision runs as them) and on
25
- * itself (its own job) — not on the project.
24
+ * gateway's runtime service accounts (a revision runs as them), on
25
+ * itself (its own job) and on the quality-audit job's account (the
26
+ * app-roll moves that job's image, #875) — not on the project.
26
27
  * - the custom role `caeloUpgraderSqlBackup`: create/read Cloud SQL
27
28
  * backups and read the instance — no restore, no instance changes.
28
29
  * - `roles/cloudsql.instanceUser` + a Cloud SQL IAM database user that
@@ -51,6 +52,7 @@ import { ensureCloudSqlIamUser } from "./cloudsql-iam-user.js";
51
52
  import { gcloud as defaultGcloud, type GcloudResult } from "./gcloud.js";
52
53
  import { type GcloudRunner, realSleep, runWithRetry, type Sleep } from "./gcloud-retry.js";
53
54
  import {
55
+ auditServiceAccountEmail,
54
56
  gatewayServiceAccountEmail,
55
57
  mcpIapServiceAccountEmail,
56
58
  runServiceAccountEmail,
@@ -256,6 +258,11 @@ export async function ensureUpgraderJob(
256
258
  why: "gateway revisions run as it",
257
259
  },
258
260
  { account: sa, role: "roles/iam.serviceAccountUser", why: "the job updates its own image" },
261
+ {
262
+ account: auditServiceAccountEmail(projectId, env),
263
+ role: "roles/iam.serviceAccountUser",
264
+ why: "the app-roll moves the quality-audit job, which runs as it, onto the new image",
265
+ },
259
266
  {
260
267
  account: mcpIapServiceAccountEmail(projectId),
261
268
  role: "roles/iam.serviceAccountTokenCreator",
@@ -101,10 +101,10 @@ export function estimateGcpCost(inputs: CostEstimateInputs): {
101
101
  : `${inputs.adminMinInstances} min-instance${inputs.adminMinInstances > 1 ? "s" : ""} (no cold start)`,
102
102
  },
103
103
  {
104
- name: "Quality checks (Lighthouse on the admin)",
105
- monthlyUsd: 1,
104
+ name: "Quality checks (Cloud Run audit job)",
105
+ monthlyUsd: 2,
106
106
  notes:
107
- "~2-4 min of admin CPU per audited Stage (up to 5 pages x 5 runs); ~$1/mo at a few Stages a day",
107
+ "one container per Lighthouse run, all in parallel (up to 5 pages x 3 runs, ~90 s at 2 vCPU each): ~$0.05 per audited Stage, partly in the free tier; results bucket ~$0",
108
108
  },
109
109
  {
110
110
  name: "Cloud Run gateway",
@@ -13,6 +13,7 @@
13
13
 
14
14
  import { resolve as resolvePath } from "node:path";
15
15
  import * as pulumi from "@pulumi/pulumi/automation";
16
+ import { AUDIT_PARALLELISM_DEFAULT } from "../audit-job-setup.js";
16
17
  import { checkpointableOutputs } from "../checkpoint-secrets.js";
17
18
  import { GCP_STACK_ENV } from "../gcp-names.js";
18
19
  import type { ImageDigests } from "../install-state.js";
@@ -36,6 +37,12 @@ export const GCP_STACK_KNOB_DEFAULTS = {
36
37
  adminMemory: ADMIN_MEMORY_DEFAULT,
37
38
  gatewayMinInstances: 0,
38
39
  wafAdaptiveProtection: false,
40
+ /**
41
+ * #875 — audit tasks at once. Not stack config: the audit job is
42
+ * CLI-owned (audit-job-setup.ts), so the wizard passes it to the job and
43
+ * `upgrade` keeps whatever the job has.
44
+ */
45
+ auditParallelism: AUDIT_PARALLELISM_DEFAULT,
39
46
  } as const;
40
47
 
41
48
  export interface PulumiUpInputs {
@@ -33,6 +33,7 @@ import { join } from "node:path";
33
33
  import { cancel, confirm, isCancel, log, note, select, spinner } from "@clack/prompts";
34
34
  import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
35
35
  import { checkAdcImpersonation } from "../adc-impersonation.js";
36
+ import { ensureAuditJob, resolveAdminImage } from "../audit-job-setup.js";
36
37
  import { CERT_WAIT_TIMEOUT_MS, lbCertProbe, waitForCert } from "../cert-wait.js";
37
38
  import { redactCredentials } from "../checkpoint-secrets.js";
38
39
  import { removeDefaultFirewallRules } from "../default-firewall.js";
@@ -454,6 +455,10 @@ export async function runGcpConverge(opts: GcpConvergeOpts): Promise<void> {
454
455
 
455
456
  phase("Finishing: operator access, welcome page");
456
457
 
458
+ // === 11.3. The quality-audit job: parallel Lighthouse runs (#875) ===
459
+ // Before the upgrader, which gets actAs on the job's service account.
460
+ await stepAuditJob(projectId, region, provider);
461
+
457
462
  // === 11.4. The upgrader job: Settings → Updates → Install update (#696) ===
458
463
  // Before operator access, which keeps the upgrader's caelo-mcp grant.
459
464
  const releaseVersion = await releaseVersionForDigest(gcloud, "admin", imageDigests.admin);
@@ -976,6 +981,38 @@ async function stepUpgraderJob(
976
981
  return true;
977
982
  }
978
983
 
984
+ /**
985
+ * Set up the quality-audit job (audit-job-setup.ts) on the deployed admin's
986
+ * image. A failure stops the wizard: without the job every Stage audit
987
+ * fails. Idempotent, so it runs on every wizard pass.
988
+ */
989
+ async function stepAuditJob(
990
+ projectId: string,
991
+ region: string,
992
+ provider: "gcp" | "gcp-firebase",
993
+ ): Promise<void> {
994
+ const s = spinner();
995
+ s.start("Setting up the quality-audit job (parallel Lighthouse runs)...");
996
+ const image = await resolveAdminImage({ projectId, region, env: GCP_STACK_ENV });
997
+ const r = image.ok
998
+ ? await ensureAuditJob({
999
+ provider,
1000
+ projectId,
1001
+ region,
1002
+ env: GCP_STACK_ENV,
1003
+ adminImageRef: image.imageRef,
1004
+ parallelism: GCP_STACK_KNOB_DEFAULTS.auditParallelism,
1005
+ })
1006
+ : { ok: false as const, done: [], error: image.error };
1007
+ if (!r.ok) {
1008
+ s.stop(red(`Failed: ${r.error}`));
1009
+ log.error("Re-run the wizard once the cause is fixed (this step is idempotent).");
1010
+ cancel("Aborted.");
1011
+ process.exit(1);
1012
+ }
1013
+ s.stop(green(`Quality-audit job ready (${r.done.length} steps)`));
1014
+ }
1015
+
979
1016
  /**
980
1017
  * Whether an upgrader set up by an earlier run exists, so a failed setup
981
1018
  * this time still keeps its caelo-mcp signing grant (the operator-access
@@ -349,6 +349,14 @@ new aws.cloudwatch.LogDelivery(
349
349
  //
350
350
  // Each Caelo service runs as its own Fargate service; sharing a cluster
351
351
  // keeps the per-month Fargate-cluster fee at zero (clusters are free).
352
+ //
353
+ // FIXME(issue #749): no quality-audit task definition yet. On GCP every
354
+ // Lighthouse run of a Stage audit runs in its own container (#875,
355
+ // src/audit-job-setup.ts); the AWS equivalent is a Fargate task per run
356
+ // (admin-core quality/parallel-runner.ts `AuditTaskLauncher`). Until it
357
+ // exists the admin records every Lighthouse audit as failed with that gap
358
+ // (quality/lighthouse-runner-select.ts) instead of measuring on its own
359
+ // shared CPU. Nothing is staged on AWS before #749 either.
352
360
  const cluster = new aws.ecs.Cluster(`${namePrefix}-ecs`, {
353
361
  settings: [{ name: "containerInsights", value: "enabled" }],
354
362
  });
@@ -330,6 +330,15 @@ const logWorkspace = new azure.operationalinsights.Workspace(`${namePrefix}-logs
330
330
  });
331
331
 
332
332
  // === 6. Container Apps environment + apps ===
333
+ //
334
+ // FIXME(issue #750): no quality-audit Container Apps job yet. On GCP every
335
+ // Lighthouse run of a Stage audit runs in its own container (#875,
336
+ // src/audit-job-setup.ts); the Azure equivalent is a Container Apps job
337
+ // with one replica per run (admin-core quality/parallel-runner.ts
338
+ // `AuditTaskLauncher`). Until it exists the admin records every Lighthouse
339
+ // audit as failed with that gap (quality/lighthouse-runner-select.ts)
340
+ // instead of measuring on its own shared CPU. Nothing is staged on Azure
341
+ // before #750 either.
333
342
  const cappEnv = new azure.app.ManagedEnvironment(`${namePrefix}-capp-env`, {
334
343
  resourceGroupName: rg.name,
335
344
  environmentName: `${namePrefix}-capp-env`,
@@ -984,6 +984,19 @@ new gcp.iap.WebBackendServiceIamMember(
984
984
  opts,
985
985
  );
986
986
 
987
+ // =========================================================================
988
+ // Quality-audit job — deliberately NOT here
989
+ // =========================================================================
990
+ //
991
+ // #875 — every Stage audit's Lighthouse runs execute in parallel in a Cloud
992
+ // Run job, one task per run, under its own service account (results bucket
993
+ // create-only, staged build read-only, no secrets, no VPC). The CLI owns the
994
+ // job, its account, the results bucket and the grants
995
+ // (packages/provisioning/src/audit-job-setup.ts, run by the wizard after
996
+ // `pulumi up` and by `upgrade`), because `upgrade` never runs Pulumi: a
997
+ // bucket or job both this program and `upgrade` created would collide on
998
+ // the next wizard run. The app-roll moves the job's image with the admin's.
999
+
987
1000
  // =========================================================================
988
1001
  // Operator access — deliberately NOT here
989
1002
  // =========================================================================
@@ -900,6 +900,19 @@ new gcp.iap.WebCloudRunServiceIamMember(
900
900
  opts,
901
901
  );
902
902
 
903
+ // =========================================================================
904
+ // Quality-audit job — deliberately NOT here
905
+ // =========================================================================
906
+ //
907
+ // #875 — every Stage audit's Lighthouse runs execute in parallel in a Cloud
908
+ // Run job, one task per run, under its own service account (results bucket
909
+ // create-only, staged build read-only, no secrets, no VPC). The CLI owns the
910
+ // job, its account, the results bucket and the grants
911
+ // (packages/provisioning/src/audit-job-setup.ts, run by the wizard after
912
+ // `pulumi up` and by `upgrade`), because `upgrade` never runs Pulumi: a
913
+ // bucket or job both this program and `upgrade` created would collide on
914
+ // the next wizard run. The app-roll moves the job's image with the admin's.
915
+
903
916
  // =========================================================================
904
917
  // Operator access — deliberately NOT here
905
918
  // =========================================================================