@catladder/cli 5.1.3 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/apps/cli/src/commands/project/commandPortForward.js +1 -1
  2. package/dist/apps/cli/src/commands/project/commandPortForward.js.map +1 -1
  3. package/dist/apps/cli/src/commands/project/secrets/scope.js +25 -2
  4. package/dist/apps/cli/src/commands/project/secrets/scope.js.map +1 -1
  5. package/dist/apps/cli/src/config/getProjectConfig.js +4 -1
  6. package/dist/apps/cli/src/config/getProjectConfig.js.map +1 -1
  7. package/dist/apps/cli/src/secrets/github.js +4 -2
  8. package/dist/apps/cli/src/secrets/github.js.map +1 -1
  9. package/dist/bundles/catenv/index.js +419 -106
  10. package/dist/bundles/cli/index.js +449 -111
  11. package/dist/bundles/skills/catladder-config/SKILL.md +34 -2
  12. package/dist/bundles/skills/catladder-deploys/SKILL.md +5 -0
  13. package/dist/bundles/skills/catladder-pipelines/SKILL.md +42 -1
  14. package/dist/bundles/skills/catladder-secrets/SKILL.md +6 -0
  15. package/dist/packages/pipeline/src/backends/github/GithubBackend.js +23 -3
  16. package/dist/packages/pipeline/src/backends/github/GithubBackend.js.map +1 -1
  17. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js +2 -2
  18. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js.map +1 -1
  19. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js +18 -2
  20. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js.map +1 -1
  21. package/dist/packages/pipeline/src/backends/gitlab/createGitlabJobs.js +2 -2
  22. package/dist/packages/pipeline/src/backends/gitlab/createGitlabJobs.js.map +1 -1
  23. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.d.ts +6 -1
  24. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js +24 -1
  25. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js.map +1 -1
  26. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.d.ts +17 -0
  27. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js +13 -0
  28. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js.map +1 -1
  29. package/dist/packages/pipeline/src/config/configruedEnvs.d.ts +9 -2
  30. package/dist/packages/pipeline/src/config/configruedEnvs.js +54 -5
  31. package/dist/packages/pipeline/src/config/configruedEnvs.js.map +1 -1
  32. package/dist/packages/pipeline/src/context/createAllComponentsContext.d.ts +2 -2
  33. package/dist/packages/pipeline/src/context/createComponentContext.d.ts +2 -2
  34. package/dist/packages/pipeline/src/context/createWorkspaceContext.d.ts +2 -2
  35. package/dist/packages/pipeline/src/context/getEnvConfig.js +35 -19
  36. package/dist/packages/pipeline/src/context/getEnvConfig.js.map +1 -1
  37. package/dist/packages/pipeline/src/context/getEnvInherit.d.ts +20 -0
  38. package/dist/packages/pipeline/src/context/getEnvInherit.js +72 -0
  39. package/dist/packages/pipeline/src/context/getEnvInherit.js.map +1 -0
  40. package/dist/packages/pipeline/src/context/getEnvInstance.d.ts +4 -0
  41. package/dist/packages/pipeline/src/context/{getReviewSlug.js → getEnvInstance.js} +13 -9
  42. package/dist/packages/pipeline/src/context/getEnvInstance.js.map +1 -0
  43. package/dist/packages/pipeline/src/context/getEnvOn.d.ts +13 -0
  44. package/dist/packages/pipeline/src/context/getEnvOn.js +28 -0
  45. package/dist/packages/pipeline/src/context/getEnvOn.js.map +1 -0
  46. package/dist/packages/pipeline/src/context/getEnvType.d.ts +8 -2
  47. package/dist/packages/pipeline/src/context/getEnvType.js +27 -5
  48. package/dist/packages/pipeline/src/context/getEnvType.js.map +1 -1
  49. package/dist/packages/pipeline/src/context/getEnvironment.js +3 -0
  50. package/dist/packages/pipeline/src/context/getEnvironment.js.map +1 -1
  51. package/dist/packages/pipeline/src/context/getEnvironmentContext.js +18 -8
  52. package/dist/packages/pipeline/src/context/getEnvironmentContext.js.map +1 -1
  53. package/dist/packages/pipeline/src/context/getEnvironmentVariables.d.ts +1 -1
  54. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js +4 -4
  55. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js.map +1 -1
  56. package/dist/packages/pipeline/src/context/index.d.ts +1 -0
  57. package/dist/packages/pipeline/src/context/index.js +1 -0
  58. package/dist/packages/pipeline/src/context/index.js.map +1 -1
  59. package/dist/packages/pipeline/src/deploy/base/deploy.js +11 -1
  60. package/dist/packages/pipeline/src/deploy/base/deploy.js.map +1 -1
  61. package/dist/packages/pipeline/src/deploy/cloudRun/artifactsRegistry.js +5 -4
  62. package/dist/packages/pipeline/src/deploy/cloudRun/artifactsRegistry.js.map +1 -1
  63. package/dist/packages/pipeline/src/deploy/cloudRun/cleanup.js +8 -2
  64. package/dist/packages/pipeline/src/deploy/cloudRun/cleanup.js.map +1 -1
  65. package/dist/packages/pipeline/src/deploy/cloudRun/index.js +2 -2
  66. package/dist/packages/pipeline/src/deploy/cloudRun/index.js.map +1 -1
  67. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js +2 -2
  68. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js.map +1 -1
  69. package/dist/packages/pipeline/src/deploy/kubernetes/index.js +3 -3
  70. package/dist/packages/pipeline/src/deploy/kubernetes/index.js.map +1 -1
  71. package/dist/packages/pipeline/src/deploy/pages/deployJob.js +3 -2
  72. package/dist/packages/pipeline/src/deploy/pages/deployJob.js.map +1 -1
  73. package/dist/packages/pipeline/src/deploy/types/googleCloudRun.d.ts +9 -0
  74. package/dist/packages/pipeline/src/types/config.d.ts +126 -9
  75. package/dist/packages/pipeline/src/types/config.js +11 -10
  76. package/dist/packages/pipeline/src/types/config.js.map +1 -1
  77. package/dist/packages/pipeline/src/types/context.d.ts +22 -3
  78. package/dist/packages/pipeline/src/types/context.js.map +1 -1
  79. package/dist/packages/pipeline/src/types/environmentContext.d.ts +29 -0
  80. package/dist/packages/pipeline/src/types/gitlab-types.d.ts +29 -1
  81. package/dist/skills/catladder-config/SKILL.md +34 -2
  82. package/dist/skills/catladder-deploys/SKILL.md +5 -0
  83. package/dist/skills/catladder-pipelines/SKILL.md +42 -1
  84. package/dist/skills/catladder-secrets/SKILL.md +6 -0
  85. package/dist/tsconfig.tsbuildinfo +1 -1
  86. package/package.json +1 -1
  87. package/src/commands/project/commandPortForward.ts +1 -1
  88. package/src/commands/project/secrets/scope.ts +38 -2
  89. package/src/config/getProjectConfig.ts +9 -1
  90. package/src/secrets/github.ts +8 -5
  91. package/dist/packages/pipeline/src/context/getReviewSlug.d.ts +0 -4
  92. package/dist/packages/pipeline/src/context/getReviewSlug.js.map +0 -1
@@ -13,6 +13,24 @@ import type { ProjectImageConfig } from "../customImages/projectImages";
13
13
  import type { VerifyConfig } from "../verify/types";
14
14
  export declare const ALL_PIPELINE_TRIGGERS: readonly ["mainBranch", "mr", "taggedRelease"];
15
15
  export type PipelineTrigger = (typeof ALL_PIPELINE_TRIGGERS)[number];
16
+ /**
17
+ * a pipeline that runs on pushes to one specific branch (in addition to
18
+ * the built-in triggers). Declared by an env via its `on` config.
19
+ */
20
+ export type BranchPipelineTrigger = {
21
+ branch: string;
22
+ };
23
+ /**
24
+ * everything a pipeline (and therefore an env's jobs) can run on
25
+ */
26
+ export type EnvPipelineTrigger = PipelineTrigger | BranchPipelineTrigger;
27
+ /**
28
+ * the `on` config of an env: a trigger, or `false` for an env that is
29
+ * not part of any pipeline (like `local`)
30
+ */
31
+ export type EnvOnConfig = EnvPipelineTrigger | false;
32
+ export declare const isBranchTrigger: (trigger: EnvPipelineTrigger) => trigger is BranchPipelineTrigger;
33
+ export declare const envTriggerEquals: (a: EnvOnConfig | undefined, b: EnvPipelineTrigger) => boolean;
16
34
  /**
17
35
  * all env types with their trigger.
18
36
  * Each env type has a default env with the same name which is always included
@@ -34,16 +52,10 @@ export declare const ENV_TYPES: {
34
52
  readonly triggers: readonly [];
35
53
  };
36
54
  };
37
- /**
38
- *
39
- * @param trigger a trigger
40
- * @returns array of env types for that trigger. this is also the list of default envs
41
- */
42
- export declare const getEnvTypesByTrigger: (trigger: PipelineTrigger) => ("stage" | "prod" | "local" | "dev" | "review")[];
43
55
  export declare const DEFAULT_ENVS: string[];
44
- export declare const DEFAULT_ENV_TYPES: ("stage" | "prod" | "local" | "dev" | "review")[];
56
+ export declare const DEFAULT_ENV_TYPES: ("stage" | "prod" | "local" | "review" | "dev")[];
45
57
  export type EnvType = keyof typeof ENV_TYPES;
46
- export declare const isKnowEnvType: (env: string) => env is "stage" | "prod" | "local" | "dev" | "review";
58
+ export declare const isKnowEnvType: (env: string) => env is "stage" | "prod" | "local" | "review" | "dev";
47
59
  export type EnvVars = {
48
60
  /**
49
61
  * public env vars (means: they are checked in the repo).
@@ -120,7 +132,100 @@ export type EnvConfig<E extends EnvType = EnvType> = {
120
132
  * host that is used. If not set, a "canonical" url is created
121
133
  */
122
134
  host?: string;
135
+ /**
136
+ * per-component override of the environment's autoStop (see
137
+ * {@link EnvironmentConfig.autoStop} — each component has its own
138
+ * gitlab environment, so the duration may differ per component)
139
+ */
140
+ autoStop?: string | false;
123
141
  } & PartialDeep<DefaultEnvConfig>;
142
+ /**
143
+ * project-wide declaration and tuning of an environment (the top-level
144
+ * `environments` config). Environments are a project-wide concept —
145
+ * every component deploys to a declared env unless it opts out with
146
+ * `env.<name>: false`. Anything that must be consistent across
147
+ * components (which envs exist, when they deploy) lives here; the
148
+ * per-component `env.<name>` entries only override component-local
149
+ * things (vars, deploy settings, host, autoStop).
150
+ */
151
+ export type EnvironmentConfig = {
152
+ /**
153
+ * the type of env this behaves like (dev, review, stage, prod) —
154
+ * think of it as a preset: it supplies all defaults (when the env
155
+ * deploys, autoStop, deploy policy like rollback history and manual
156
+ * prod deploys) and the env's identity for tooling (the `ENV_TYPE`
157
+ * env var / `env-type` label). Everything it implies can be
158
+ * overridden explicitly. Required for envs whose name is not itself
159
+ * an env type.
160
+ */
161
+ type?: EnvType;
162
+ /**
163
+ * when this env deploys. Defaults from the env type:
164
+ * - dev: "mainBranch" (pushes to the default branch)
165
+ * - review: "mr" (merge requests / pull requests; the env is created
166
+ * per MR and torn down when it closes)
167
+ * - stage / prod: "taggedRelease" (release tags)
168
+ * - local: false (not part of any pipeline)
169
+ *
170
+ * `{ branch: "next" }` deploys the env on pushes to that branch —
171
+ * one stable environment tracking a long-lived branch.
172
+ *
173
+ * `false` removes the env from all pipelines.
174
+ */
175
+ on?: EnvOnConfig;
176
+ /**
177
+ * how long the environment stays up after its last deployment before
178
+ * gitlab stops it automatically (gitlab `auto_stop_in`, e.g. "3 days").
179
+ * `false` disables auto-stopping. Defaults from the env type, tunable
180
+ * project-wide via the top-level `autoStop` durations: review
181
+ * "1 week", dev "4 weeks", everything else never.
182
+ *
183
+ * Only applies to stoppable deployments, and only on gitlab (github
184
+ * review apps stop when their pull request closes). Components can
185
+ * override it per env (`env.<name>.autoStop`).
186
+ *
187
+ * NOTE: an explicit value here is a literal — setting it on a review
188
+ * env opts that env out of the pin-label mechanism (the top-level
189
+ * `autoStop.pinLabel`), which only steers the default review lifetime.
190
+ */
191
+ autoStop?: string | false;
192
+ /**
193
+ * inherit from another environment (see {@link EnvironmentInheritConfig}):
194
+ *
195
+ * `inherit: "dev"` makes this env use dev's per-component config
196
+ * overrides AND dev's secret values. The object form controls the two
197
+ * axes separately.
198
+ */
199
+ inherit?: EnvironmentInheritConfig;
200
+ };
201
+ /**
202
+ * what an environment inherits from another environment.
203
+ *
204
+ * The string shorthand `inherit: "dev"` means
205
+ * `{ config: "dev", secrets: "dev" }`.
206
+ *
207
+ * - `config`: the named env's per-component overrides (`env.<name>` in
208
+ * each component — vars, deploy/build settings, autoStop) are merged
209
+ * in below this env's own overrides: component base → inherited env's
210
+ * overrides → own overrides. `host` and `type` are never inherited
211
+ * (a host must stay unique per env). The env's `type` defaults to the
212
+ * inherited env's type.
213
+ * - `secrets`: this env uses the named env's secret VALUES — all or
214
+ * nothing: its jobs reference the other env's secret variables
215
+ * (`CL_<OTHERENV>_...`) and it has no secret store of its own, so the
216
+ * secrets CLI manages them via the other env and rotating them
217
+ * affects both envs. Set to `false` (or omit in the object form) for
218
+ * own secrets.
219
+ *
220
+ * Inheriting from `local` is not possible, chains are followed, cycles
221
+ * are an error.
222
+ */
223
+ export type EnvironmentInheritConfig = string | {
224
+ /** env whose per-component config overrides are inherited */
225
+ config?: string;
226
+ /** env whose secret values are used (all-or-nothing); false = own secrets */
227
+ secrets?: string | false;
228
+ };
124
229
  export type EnvConfigWithComponent = EnvConfig<EnvType> & ComponentConfig;
125
230
  export type CustomEnv = EnvConfigWithOverride & {
126
231
  type: EnvType;
@@ -134,7 +239,7 @@ export type Env<C extends ConfigProps = never> = {
134
239
  stage?: EnvConfigWithOverride<"stage"> | false;
135
240
  review?: EnvConfigWithOverride<"review"> | false;
136
241
  prod?: EnvConfigWithOverride<"prod"> | false;
137
- } & Record<C["CustomEnvs"], CustomEnv> & Record<string, any>;
242
+ } & Partial<Record<C["CustomEnvs"], EnvConfigWithOverride | false>> & Record<string, any>;
138
243
  export type ComponentConfig<C extends ConfigProps = never> = {
139
244
  /**
140
245
  * specify environment configurations
@@ -315,6 +420,18 @@ export type Config<C extends ConfigProps = never> = {
315
420
  */
316
421
  images?: Record<string, ProjectImageConfig>;
317
422
  builds?: Record<string, WorkspaceBuildConfig>;
423
+ /**
424
+ * project-wide environment declarations and tuning. Environments are
425
+ * shared across all components, so everything that must be consistent
426
+ * between them (which envs exist, their type, when they deploy) is
427
+ * declared here — components only carry per-component overrides.
428
+ *
429
+ * - tune a default env: `environments: { review: { autoStop: "3 days" } }`
430
+ * - declare an extra env (every component deploys to it unless it
431
+ * opts out with `env.<name>: false`):
432
+ * `environments: { next: { type: "dev", on: { branch: "next" } } }`
433
+ */
434
+ environments?: Record<string, EnvironmentConfig>;
318
435
  /**
319
436
  * components (sub apps)
320
437
  */
@@ -1,11 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isKnowEnvType = exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS = exports.getEnvTypesByTrigger = exports.ENV_TYPES = exports.ALL_PIPELINE_TRIGGERS = void 0;
3
+ exports.isKnowEnvType = exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS = exports.ENV_TYPES = exports.envTriggerEquals = exports.isBranchTrigger = exports.ALL_PIPELINE_TRIGGERS = void 0;
4
4
  exports.ALL_PIPELINE_TRIGGERS = [
5
5
  "mainBranch",
6
6
  "mr",
7
7
  "taggedRelease",
8
8
  ];
9
+ const isBranchTrigger = (trigger) => typeof trigger === "object" && trigger !== null && "branch" in trigger;
10
+ exports.isBranchTrigger = isBranchTrigger;
11
+ const envTriggerEquals = (a, b) => {
12
+ if (a === undefined || a === false)
13
+ return false;
14
+ if ((0, exports.isBranchTrigger)(b))
15
+ return (0, exports.isBranchTrigger)(a) && a.branch === b.branch;
16
+ return a === b;
17
+ };
18
+ exports.envTriggerEquals = envTriggerEquals;
9
19
  /**
10
20
  * all env types with their trigger.
11
21
  * Each env type has a default env with the same name which is always included
@@ -27,15 +37,6 @@ exports.ENV_TYPES = {
27
37
  triggers: [],
28
38
  },
29
39
  };
30
- /**
31
- *
32
- * @param trigger a trigger
33
- * @returns array of env types for that trigger. this is also the list of default envs
34
- */
35
- const getEnvTypesByTrigger = (trigger) => Object.entries(exports.ENV_TYPES)
36
- .filter(([, e]) => e.triggers.includes(trigger))
37
- .map(([e]) => e);
38
- exports.getEnvTypesByTrigger = getEnvTypesByTrigger;
39
40
  exports.DEFAULT_ENVS = Object.keys(exports.ENV_TYPES);
40
41
  exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS;
41
42
  const isKnowEnvType = (env) => {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../../../packages/pipeline/src/types/config.ts"],"names":[],"mappings":";;;AAea,QAAA,qBAAqB,GAAG;IACnC,YAAY;IACZ,IAAI;IACJ,eAAe;CACP,CAAC;AAGX;;;GAGG;AACU,QAAA,SAAS,GAAG;IACvB,GAAG,EAAE;QACH,QAAQ,EAAE,CAAC,YAAY,CAAC;KACzB;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,IAAI,CAAC;KACjB;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,CAAC,eAAe,CAAC;KAC5B;IACD,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,eAAe,CAAC;KAC5B;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,EAAE;KACb;CACO,CAAC;AAEX;;;;GAIG;AACI,MAAM,oBAAoB,GAAG,CAAC,OAAwB,EAAE,EAAE,CAC/D,MAAM,CAAC,OAAO,CAAC,iBAAS,CAAC;KACtB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CACf,CAAC,CAAC,QAAuC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC7D;KACA,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAY,CAAC,CAAC;AALnB,QAAA,oBAAoB,wBAKD;AAEnB,QAAA,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAS,CAAC,CAAC;AACtC,QAAA,iBAAiB,GAAG,oBAAyB,CAAC;AAGpD,MAAM,aAAa,GAAG,CAAC,GAAW,EAAkB,EAAE;IAC3D,OAAO,GAAG,IAAI,iBAAS,CAAC;AAC1B,CAAC,CAAC;AAFW,QAAA,aAAa,iBAExB"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../../../packages/pipeline/src/types/config.ts"],"names":[],"mappings":";;;AAea,QAAA,qBAAqB,GAAG;IACnC,YAAY;IACZ,IAAI;IACJ,eAAe;CACP,CAAC;AAoBJ,MAAM,eAAe,GAAG,CAC7B,OAA2B,EACO,EAAE,CACpC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,QAAQ,IAAI,OAAO,CAAC;AAH5D,QAAA,eAAe,mBAG6C;AAElE,MAAM,gBAAgB,GAAG,CAC9B,CAA0B,EAC1B,CAAqB,EACZ,EAAE;IACX,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,IAAA,uBAAe,EAAC,CAAC,CAAC;QAAE,OAAO,IAAA,uBAAe,EAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC;IAC3E,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC,CAAC;AAPW,QAAA,gBAAgB,oBAO3B;AAEF;;;GAGG;AACU,QAAA,SAAS,GAAG;IACvB,GAAG,EAAE;QACH,QAAQ,EAAE,CAAC,YAAY,CAAC;KACzB;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,IAAI,CAAC;KACjB;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,CAAC,eAAe,CAAC;KAC5B;IACD,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,eAAe,CAAC;KAC5B;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,EAAE;KACb;CACO,CAAC;AAEE,QAAA,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAS,CAAC,CAAC;AACtC,QAAA,iBAAiB,GAAG,oBAAyB,CAAC;AAGpD,MAAM,aAAa,GAAG,CAAC,GAAW,EAAkB,EAAE;IAC3D,OAAO,GAAG,IAAI,iBAAS,CAAC;AAC1B,CAAC,CAAC;AAFW,QAAA,aAAa,iBAExB"}
@@ -3,7 +3,8 @@ import type { BuildConfigFromWorkspace, BuildConfigStandalone, WorkspaceBuildCon
3
3
  import type { PredefinedVariables, SecretEnvVar } from "../context";
4
4
  import type { DeployConfig } from "../deploy";
5
5
  import type { VariableValue } from "../../../bash/src/index.js";
6
- import type { ComponentConfig, Config, EnvType, PipelineTrigger } from "./config";
6
+ import type { ComponentConfig, Config, EnvPipelineTrigger, EnvType } from "./config";
7
+ import type { EnvironmentInstance } from "./environmentContext";
7
8
  import type { BaseStage, CatladderJob, CatladderJobSpec } from "./jobs";
8
9
  import type { PipelineType } from "./pipeline";
9
10
  export type UnspecifiedEnvVars = Record<string, VariableValue | undefined | null>;
@@ -29,8 +30,14 @@ export type Environment = {
29
30
  * the environment slug without component name.
30
31
  */
31
32
  slugPrefix: StringOrBashExpression;
33
+ /**
34
+ * how the env is instantiated — `instance.type === "review"` is the
35
+ * way to find out whether this is a review app
36
+ */
37
+ instance: EnvironmentInstance;
32
38
  /**
33
39
  * the review slug, if it is a review app, null otherwise
40
+ * @deprecated use `instance` — the review variant carries the slug
34
41
  */
35
42
  reviewSlug: StringOrBashExpression | null;
36
43
  /**
@@ -38,6 +45,18 @@ export type Environment = {
38
45
  */
39
46
  slug: StringOrBashExpression;
40
47
  envType: EnvType;
48
+ /**
49
+ * the resolved `autoStop` config (component override, then the
50
+ * project-wide environment config — see {@link EnvironmentConfig.autoStop});
51
+ * undefined means the env type's default applies
52
+ */
53
+ autoStop?: string | false;
54
+ /**
55
+ * the env whose secret values this env uses — the env itself unless
56
+ * it shares another env's secrets via `inherit`. All secret variable
57
+ * names are built with this env.
58
+ */
59
+ secretsEnv: string;
41
60
  } & EnvironmentEnvVarPart;
42
61
  export type YarnWorkspace = {
43
62
  name: string;
@@ -136,7 +155,7 @@ export type ComponentContext<BC extends BuildContextComponent = BuildContextComp
136
155
  deploy?: DeployContext | null;
137
156
  fullConfig: Config;
138
157
  environment: Environment;
139
- trigger?: PipelineTrigger;
158
+ trigger?: EnvPipelineTrigger;
140
159
  pipelineType?: PipelineType;
141
160
  packageManagerInfo: Promise<PackageManagerInfoComponent>;
142
161
  customJobs?: CatladderJobSpec[];
@@ -159,7 +178,7 @@ export type WorkspaceContext = {
159
178
  packageManagerInfo: Promise<PackageManagerInfoBase>;
160
179
  components: Array<ComponentContext>;
161
180
  build: BuildContextWorkspace;
162
- trigger: PipelineTrigger;
181
+ trigger: EnvPipelineTrigger;
163
182
  pipelineType: PipelineType;
164
183
  env: string;
165
184
  };
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../../../../../packages/pipeline/src/types/context.ts"],"names":[],"mappings":";;;AAiNO,MAAM,iCAAiC,GAAG,CAC/C,OAAgD,EACK,EAAE;IACvD,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC;AAC7C,CAAC,CAAC;AAJW,QAAA,iCAAiC,qCAI5C;AAEK,MAAM,iCAAiC,GAAG,CAC/C,OAAgD,EACQ,EAAE;IAC1D,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe,CAAC;AAChD,CAAC,CAAC;AAJW,QAAA,iCAAiC,qCAI5C"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../../../../../packages/pipeline/src/types/context.ts"],"names":[],"mappings":";;;AAsOO,MAAM,iCAAiC,GAAG,CAC/C,OAAgD,EACK,EAAE;IACvD,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC;AAC7C,CAAC,CAAC;AAJW,QAAA,iCAAiC,qCAI5C;AAEK,MAAM,iCAAiC,GAAG,CAC/C,OAAgD,EACQ,EAAE;IAC1D,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe,CAAC;AAChD,CAAC,CAAC;AAJW,QAAA,iCAAiC,qCAI5C"}
@@ -3,20 +3,49 @@ import type { BuildConfig } from "../build";
3
3
  import type { DeployConfig } from "../deploy";
4
4
  import type { Config, EnvConfigWithComponent, EnvType } from "./config";
5
5
  import type { PipelineType } from "./pipeline";
6
+ /**
7
+ * how an environment is instantiated:
8
+ * - "stable": one long-lived instance (dev, stage, prod, branch envs)
9
+ * - "review": one instance per merge/pull request (`on: "mr"`),
10
+ * addressed by the runtime review slug (mr<iid> / pr<number>)
11
+ */
12
+ export type EnvironmentInstance = {
13
+ type: "stable";
14
+ } | {
15
+ type: "review";
16
+ reviewSlug: StringOrBashExpression;
17
+ };
6
18
  export type EnvironmentContext<B extends BuildConfig = BuildConfig, D extends DeployConfig = DeployConfig> = {
7
19
  envConfigRaw: EnvConfigWithComponent;
8
20
  buildConfigRaw: false | B;
9
21
  deployConfigRaw: false | D;
10
22
  env: string;
11
23
  envType: EnvType;
24
+ /**
25
+ * the resolved autoStop config (component override, then project-wide
26
+ * environment config); undefined means the env type's default applies
27
+ */
28
+ autoStop?: string | false;
29
+ /**
30
+ * the env whose secret values this env uses — the env itself unless
31
+ * it shares another env's secrets via `inherit`. All secret variable
32
+ * names are built with this env.
33
+ */
34
+ secretsEnv: string;
12
35
  componentName: string;
13
36
  fullName: StringOrBashExpression;
14
37
  /**
15
38
  * the environment slug without component name.
16
39
  */
17
40
  environmentSlugPrefix: StringOrBashExpression;
41
+ /**
42
+ * how the env is instantiated — `instance.type === "review"` is the
43
+ * way to find out whether this is a review app
44
+ */
45
+ instance: EnvironmentInstance;
18
46
  /**
19
47
  * the review slug, if it is a review app, null otherwise
48
+ * @deprecated use `instance` — the review variant carries the slug
20
49
  */
21
50
  reviewSlug: StringOrBashExpression | null;
22
51
  /**
@@ -4,6 +4,33 @@ export { Retry, Image as GitlabJobImage } from "./gitlab-ci-yml";
4
4
  export type Artifacts = GitlabCiArtifacts;
5
5
  export type { GitlabJobCache };
6
6
  export type GitlabRule = Exclude<Exclude<Rules, null>[number], string | string[]>;
7
+ /**
8
+ * gitlab's auto-cancel policy for redundant pipelines. Tunes the
9
+ * project setting "Auto-cancel redundant pipelines" (Settings > CI/CD >
10
+ * General pipelines) — it does NOT enable it: with the setting off,
11
+ * nothing is ever cancelled regardless of this.
12
+ *
13
+ * needs gitlab >= 16.8 (top-level) / >= 16.10 (per workflow rule).
14
+ */
15
+ export interface GitlabAutoCancel {
16
+ /**
17
+ * - `conservative` (gitlab's default): cancel the whole pipeline, but
18
+ * only while no `interruptible: false` job has *started* — a single
19
+ * such job then shields every other job from cancellation
20
+ * - `interruptible`: cancel every `interruptible: true` job, with no
21
+ * such veto
22
+ * - `none`: never auto-cancel
23
+ */
24
+ on_new_commit?: "conservative" | "interruptible" | "none";
25
+ on_job_failure?: "all" | "none";
26
+ }
27
+ /**
28
+ * a workflow rule may override {@link GitlabAutoCancel} per pipeline
29
+ * kind — job rules may not.
30
+ */
31
+ export type GitlabWorkflowRule = GitlabRule & {
32
+ auto_cancel?: GitlabAutoCancel;
33
+ };
7
34
  export type GitlabEnvironment = Omit<Exclude<JobTemplate["environment"], undefined | string>, "deployment_tier">;
8
35
  export interface GitlabJobDef extends Pick<JobTemplate, "after_script" | "allow_failure" | "artifacts" | "before_script" | "coverage" | "dependencies" | "environment" | "except" | "hooks" | "image" | "interruptible" | "needs" | "only" | "parallel" | "release" | "resource_group" | "retry" | "rules" | "script" | "services" | "stage" | "tags" | "trigger" | "variables"> {
9
36
  stage: JobTemplate["stage"];
@@ -23,8 +50,9 @@ export interface GitlabJobDef extends Pick<JobTemplate, "after_script" | "allow_
23
50
  export interface GitlabPipeline extends Pick<JobTemplate, "variables"> {
24
51
  image: string;
25
52
  workflow?: {
26
- rules: GitlabRule[];
27
53
  name?: string;
54
+ auto_cancel?: GitlabAutoCancel;
55
+ rules: GitlabWorkflowRule[];
28
56
  variables?: Record<string, string>;
29
57
  };
30
58
  stages: string[];
@@ -33,6 +33,10 @@ const config = {
33
33
  appName: "my-app",
34
34
  customerName: "pan",
35
35
  pipelines: { gitlab: true, github: true }, // which CI systems to generate
36
+ environments: { // optional: project-wide env declarations/tuning
37
+ review: { autoStop: "3 days" },
38
+ next: { type: "dev", on: { branch: "next" } }, // extra branch-tracking env
39
+ },
36
40
  components: {
37
41
  www: {
38
42
  dir: "frontend", // working directory of this component
@@ -59,8 +63,36 @@ Key concepts:
59
63
  - **Environments**: `dev` (deployed on push to the main branch),
60
64
  `review` (per merge/pull request), `stage` and `prod` (deployed on
61
65
  tagged releases), `local` (only for local development via catenv).
62
- Per-env overrides live under `env.<name>` and can override vars,
63
- deploy settings, and more.
66
+ Environments are project-wide: the top-level `environments` config
67
+ declares and tunes them consistently for all components, while
68
+ per-component `env.<name>` entries only carry component-local
69
+ overrides (vars, deploy settings, host, autoStop) or `false` to opt
70
+ the component out. In `environments`:
71
+ - `on` — when the env deploys: `"mainBranch"`, `"mr"`,
72
+ `"taggedRelease"`, `{ branch: "next" }` (a stable env deploying on
73
+ pushes to that branch), or `false` (in no pipeline). `on: "mr"`
74
+ makes an env a review app (one instance per MR/PR, torn down on
75
+ close). Defaults from the env type.
76
+ - `autoStop` — how long a stoppable environment stays up after its
77
+ last deploy before gitlab stops it (e.g. `"3 days"`; `false`
78
+ disables). Defaults: review `"1 week"`, dev `"4 weeks"`, others
79
+ never. GitLab only — github review apps stop when their PR closes.
80
+ Components can override it per env (`env.<name>.autoStop`).
81
+ - Extra envs are declared with any name plus a `type` — the env type
82
+ acts as a preset: it supplies all defaults (trigger, autoStop,
83
+ deploy policy) and the env's identity for tooling (`ENV_TYPE`),
84
+ everything overridable. Every component deploys to a declared env
85
+ unless it opts out:
86
+ `environments: { next: { type: "dev", on: { branch: "next" } } }`.
87
+ - `inherit` — make an env inherit from another env:
88
+ `inherit: "dev"` gives it dev's per-component config overrides
89
+ (merged below its own; `host` and `type` never inherit, the env
90
+ type is implied from the inherited env) AND dev's secret values —
91
+ all-or-nothing: its jobs reference dev's `CL_DEV_*` variables and
92
+ it has no secret store of its own (manage secrets via dev; rotating
93
+ affects both). The object form separates the axes:
94
+ `inherit: { config: "dev" }` inherits config only,
95
+ `inherit: { config: "dev", secrets: "dev" }` both.
64
96
  - **Pipelines**: `pipelines: { gitlab: true, github: true }` selects
65
97
  which CI systems get generated files. Options objects instead of
66
98
  `true` allow per-pipeline settings (e.g. `runnerVariables`,
@@ -78,6 +78,11 @@ Everything app-level lives under `values`:
78
78
  completion jobs, and always-on background worker pools.
79
79
  - `cloudSql` — attach an (unmanaged) CloudSQL instance; choose the
80
80
  connection-string format (`prisma` default, `rails`, `jdbc`).
81
+ `deleteDatabaseOnStop` controls whether the database is dropped when
82
+ the environment stops (default: true for review envs, false else).
83
+ - `revisionsToKeep` — how many inactive revisions (the rollback
84
+ history) the post-deploy cleanup keeps; older revisions and their
85
+ images are deleted. Default: 5 on prod envs, 0 everywhere else.
81
86
  - `execute` — run a script/job/HTTP call at a deploy lifecycle point
82
87
  (`preDeploy`/`postDeploy`/`preStop`/`postStop`) or on a `schedule`.
83
88
  Prefer this over the deprecated `when`/`schedule` fields on `jobs`.
@@ -11,11 +11,20 @@ generated YAML.
11
11
 
12
12
  ## Triggers and environments
13
13
 
14
- | Trigger | Runs on | Deploys to |
14
+ | Trigger | Runs on | Deploys to (default) |
15
15
  |---|---|---|
16
16
  | `mainBranch` | push to the main branch | `dev` |
17
17
  | `mr` | merge/pull requests | `review` (one app per MR/PR) |
18
18
  | `taggedRelease` | git tags | `stage`, `prod` |
19
+ | `{ branch: "<name>" }` | push to that branch | envs declaring it via `on` |
20
+
21
+ The trigger of each env is its `on` in the top-level `environments`
22
+ config (project-wide — components cannot diverge), defaulting from the
23
+ env type as in the table. An env with `on: { branch: "next" }` gets its
24
+ own pipeline (gitlab: rules on `$CI_COMMIT_BRANCH`; github: a
25
+ `catladder-branch-<name>.yml` workflow) and deploys as one stable,
26
+ branch-tracking environment — unlike review apps, it is not
27
+ per-instance and is stopped via the manual stop job/workflow.
19
28
 
20
29
  Generated layout:
21
30
 
@@ -37,6 +46,38 @@ Generated layout:
37
46
  Stages: setup → test → build → deploy → verify (post-deploy checks),
38
47
  plus stop jobs for review-app teardown.
39
48
 
49
+ ## Superseded pipelines are cancelled
50
+
51
+ Pushing a new commit cancels the pipeline of the previous one, so the
52
+ runners are not busy with results nobody will read.
53
+
54
+ | Pipeline | On a new commit |
55
+ |---|---|
56
+ | `mr` | cancelled |
57
+ | `mainBranch` (dev) | cancelled, except a release already running |
58
+ | `taggedRelease` | **runs through** — never cancelled |
59
+ | agent runs (`trigger`) | **runs through** — never cancelled |
60
+
61
+ - **GitHub**: the generated MR workflow sets `concurrency` with
62
+ `cancel-in-progress: true`.
63
+ - **GitLab**: generated `workflow:auto_cancel:on_new_commit:
64
+ interruptible`, with per-rule `none` for tags and agent runs. Every
65
+ job carries an explicit `interruptible`, jobs that must finish
66
+ (release jobs, agent jobs) carry `interruptible: false`.
67
+
68
+ **GitLab requires the project setting too.** The generated YAML only
69
+ tunes *how* redundant pipelines are cancelled — it does not switch the
70
+ feature on. If old pipelines keep running, check **Settings > CI/CD >
71
+ General pipelines > Auto-cancel redundant pipelines**; with that
72
+ checkbox off nothing is ever cancelled. (`catladder project-doctor`
73
+ does not check this.)
74
+
75
+ **Adding a hand-written GitLab job?** Set `interruptible` explicitly.
76
+ GitLab defaults it to `false`, and a non-interruptible job that has
77
+ started keeps *itself* alive — under gitlab's own default
78
+ (`conservative`, which catladder overrides) it would shield the whole
79
+ pipeline from cancellation.
80
+
40
81
  ## Review-app auto-stop and pinning (GitLab)
41
82
 
42
83
  GitLab review environments stop automatically after 1 week, dev
@@ -38,6 +38,12 @@ files in the repo. All secrets commands accept a **scope**:
38
38
  `dev:web` (one env of one component), `dev:` (one env, all
39
39
  components), `:web` (all envs of one component), or nothing (everything).
40
40
 
41
+ An env that shares another env's secrets (top-level
42
+ `environments.<name>.inherit`, e.g. a `next` branch env inheriting from
43
+ dev) has **no secret store of its own** — its jobs reference the source
44
+ env's variables directly, so it never appears in secrets scopes: manage
45
+ (and rotate) the values via the source env, which affects both.
46
+
41
47
  Check what is declared and what is still unset (never prints values
42
48
  unless `--reveal` is passed):
43
49