@catladder/cli 5.5.0 → 5.7.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 (109) hide show
  1. package/dist/apps/cli/src/catci.js +22 -0
  2. package/dist/apps/cli/src/catci.js.map +1 -1
  3. package/dist/apps/cli/src/commands/index.d.ts +1 -0
  4. package/dist/apps/cli/src/commands/index.js +4 -1
  5. package/dist/apps/cli/src/commands/index.js.map +1 -1
  6. package/dist/apps/cli/src/commands/project/commandMrPin.d.ts +9 -0
  7. package/dist/apps/cli/src/commands/project/commandMrPin.js +11 -13
  8. package/dist/apps/cli/src/commands/project/commandMrPin.js.map +1 -1
  9. package/dist/apps/cli/src/commands/project/commandMrReviewApp.d.ts +21 -0
  10. package/dist/apps/cli/src/commands/project/commandMrReviewApp.js +102 -0
  11. package/dist/apps/cli/src/commands/project/commandMrReviewApp.js.map +1 -0
  12. package/dist/apps/cli/src/reviewApps/github.d.ts +10 -0
  13. package/dist/apps/cli/src/reviewApps/github.js +196 -0
  14. package/dist/apps/cli/src/reviewApps/github.js.map +1 -0
  15. package/dist/bundles/catci/index.js +4 -4
  16. package/dist/bundles/catenv/index.js +1261 -70
  17. package/dist/bundles/catenv/xdg-open +0 -0
  18. package/dist/bundles/cli/clipboard_i686.exe +0 -0
  19. package/dist/bundles/cli/clipboard_x86_64.exe +0 -0
  20. package/dist/bundles/cli/index.js +1387 -86
  21. package/dist/bundles/cli/xdg-open +0 -0
  22. package/dist/bundles/cli/xsel +0 -0
  23. package/dist/bundles/runner-images/changesets/scripts/changesetsRelease +0 -0
  24. package/dist/bundles/runner-images/kubernetes/scripts/kubernetesCreateSecret +0 -0
  25. package/dist/bundles/runner-images/kubernetes/scripts/kubernetesDelete +0 -0
  26. package/dist/bundles/runner-images/kubernetes/scripts/kubernetesDeploy +0 -0
  27. package/dist/bundles/runner-images/kubernetes/scripts/kubernetesRollback +0 -0
  28. package/dist/bundles/skills/catladder-builds/SKILL.md +50 -2
  29. package/dist/bundles/skills/catladder-builds/references/build-types.md +10 -2
  30. package/dist/bundles/skills/catladder-cli/references/commands.md +13 -0
  31. package/dist/bundles/skills/catladder-config/SKILL.md +4 -0
  32. package/dist/bundles/skills/catladder-deploys/SKILL.md +6 -0
  33. package/dist/bundles/skills/catladder-pipelines/SKILL.md +97 -3
  34. package/dist/packages/pipeline/src/backends/github/GithubBackend.js +60 -3
  35. package/dist/packages/pipeline/src/backends/github/GithubBackend.js.map +1 -1
  36. package/dist/packages/pipeline/src/backends/github/aggregateCheckJob.d.ts +1 -1
  37. package/dist/packages/pipeline/src/backends/github/aggregateCheckJob.js +22 -8
  38. package/dist/packages/pipeline/src/backends/github/aggregateCheckJob.js.map +1 -1
  39. package/dist/packages/pipeline/src/backends/github/artifactArchive.d.ts +22 -0
  40. package/dist/packages/pipeline/src/backends/github/artifactArchive.js +84 -0
  41. package/dist/packages/pipeline/src/backends/github/artifactArchive.js.map +1 -0
  42. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js +12 -29
  43. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js.map +1 -1
  44. package/dist/packages/pipeline/src/backends/github/reviewDeployWorkflow.d.ts +128 -0
  45. package/dist/packages/pipeline/src/backends/github/reviewDeployWorkflow.js +510 -0
  46. package/dist/packages/pipeline/src/backends/github/reviewDeployWorkflow.js.map +1 -0
  47. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js +32 -5
  48. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js.map +1 -1
  49. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.d.ts +11 -1
  50. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js +17 -5
  51. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js.map +1 -1
  52. package/dist/packages/pipeline/src/backends/gitlab/getPipelineStages.d.ts +4 -1
  53. package/dist/packages/pipeline/src/backends/gitlab/getPipelineStages.js +11 -2
  54. package/dist/packages/pipeline/src/backends/gitlab/getPipelineStages.js.map +1 -1
  55. package/dist/packages/pipeline/src/backends/gitlab/reviewDeployGate.d.ts +26 -0
  56. package/dist/packages/pipeline/src/backends/gitlab/reviewDeployGate.js +94 -0
  57. package/dist/packages/pipeline/src/backends/gitlab/reviewDeployGate.js.map +1 -0
  58. package/dist/packages/pipeline/src/build/types.d.ts +4 -3
  59. package/dist/packages/pipeline/src/build/types.js.map +1 -1
  60. package/dist/packages/pipeline/src/deploy/base/deploy.js +25 -12
  61. package/dist/packages/pipeline/src/deploy/base/deploy.js.map +1 -1
  62. package/dist/packages/pipeline/src/index.d.ts +2 -0
  63. package/dist/packages/pipeline/src/index.js +2 -0
  64. package/dist/packages/pipeline/src/index.js.map +1 -1
  65. package/dist/packages/pipeline/src/pipeline/createJobsForComponent.js +12 -2
  66. package/dist/packages/pipeline/src/pipeline/createJobsForComponent.js.map +1 -1
  67. package/dist/packages/pipeline/src/pipeline/createJobsForWorkspace.js +7 -2
  68. package/dist/packages/pipeline/src/pipeline/createJobsForWorkspace.js.map +1 -1
  69. package/dist/packages/pipeline/src/pipeline/resolveRequirements.d.ts +2 -0
  70. package/dist/packages/pipeline/src/pipeline/resolveRequirements.js +4 -2
  71. package/dist/packages/pipeline/src/pipeline/resolveRequirements.js.map +1 -1
  72. package/dist/packages/pipeline/src/postBuildTest/createPostBuildTestJobs.d.ts +16 -0
  73. package/dist/packages/pipeline/src/postBuildTest/createPostBuildTestJobs.js +167 -0
  74. package/dist/packages/pipeline/src/postBuildTest/createPostBuildTestJobs.js.map +1 -0
  75. package/dist/packages/pipeline/src/postBuildTest/index.d.ts +2 -0
  76. package/dist/packages/pipeline/src/postBuildTest/index.js +19 -0
  77. package/dist/packages/pipeline/src/postBuildTest/index.js.map +1 -0
  78. package/dist/packages/pipeline/src/postBuildTest/types.d.ts +61 -0
  79. package/dist/packages/pipeline/src/postBuildTest/types.js +3 -0
  80. package/dist/packages/pipeline/src/postBuildTest/types.js.map +1 -0
  81. package/dist/packages/pipeline/src/reviewApps.d.ts +38 -0
  82. package/dist/packages/pipeline/src/reviewApps.js +86 -0
  83. package/dist/packages/pipeline/src/reviewApps.js.map +1 -0
  84. package/dist/packages/pipeline/src/reviewAppsPolicy.d.ts +46 -0
  85. package/dist/packages/pipeline/src/reviewAppsPolicy.js +37 -0
  86. package/dist/packages/pipeline/src/reviewAppsPolicy.js.map +1 -0
  87. package/dist/packages/pipeline/src/types/config.d.ts +55 -0
  88. package/dist/packages/pipeline/src/types/config.js.map +1 -1
  89. package/dist/packages/pipeline/src/types/jobs.d.ts +2 -2
  90. package/dist/packages/pipeline/src/types/jobs.js +1 -0
  91. package/dist/packages/pipeline/src/types/jobs.js.map +1 -1
  92. package/dist/runner-images/changesets/scripts/changesetsRelease +0 -0
  93. package/dist/runner-images/kubernetes/scripts/kubernetesCreateSecret +0 -0
  94. package/dist/runner-images/kubernetes/scripts/kubernetesDelete +0 -0
  95. package/dist/runner-images/kubernetes/scripts/kubernetesDeploy +0 -0
  96. package/dist/runner-images/kubernetes/scripts/kubernetesRollback +0 -0
  97. package/dist/skills/catladder-builds/SKILL.md +50 -2
  98. package/dist/skills/catladder-builds/references/build-types.md +10 -2
  99. package/dist/skills/catladder-cli/references/commands.md +13 -0
  100. package/dist/skills/catladder-config/SKILL.md +4 -0
  101. package/dist/skills/catladder-deploys/SKILL.md +6 -0
  102. package/dist/skills/catladder-pipelines/SKILL.md +97 -3
  103. package/dist/tsconfig.tsbuildinfo +1 -1
  104. package/package.json +1 -1
  105. package/src/catci.ts +29 -0
  106. package/src/commands/index.ts +4 -0
  107. package/src/commands/project/commandMrPin.ts +15 -14
  108. package/src/commands/project/commandMrReviewApp.ts +143 -0
  109. package/src/reviewApps/github.ts +253 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: catladder-builds
3
- description: Configuring how a component is built in a catladder project — the `build` config in catladder.ts (build types node / rails / meteor / custom, build & start commands, artifacts, caching, lint/test/audit jobs, the Docker image, project-declared job images, and shared workspace builds). Use when adding or changing a component's build, choosing a build type, wiring a Dockerfile/nginx image, declaring a custom CI job image (`images` config), tuning the build/test/lint jobs, or setting up a monorepo workspace build. Triggers on "build config", "buildCommand", "build type", "Dockerfile", "docker image", "job image", "jobImage", "custom image", "project image", "rails build", "monorepo build", "artifacts".
3
+ description: Configuring how a component is built in a catladder project — the `build` config in catladder.ts (build types node / rails / meteor / custom, build & start commands, artifacts, caching, lint/test/audit jobs, the Docker image, project-declared job images, and shared workspace builds). Use when adding or changing a component's build, choosing a build type, wiring a Dockerfile/nginx image, declaring a custom CI job image (`images` config), tuning the build/test/lint jobs, adding e2e tests against the build (`postBuildTests`), or setting up a monorepo workspace build. Triggers on "build config", "postBuildTests", "post-build test", "e2e", "playwright", "buildCommand", "build type", "Dockerfile", "docker image", "job image", "jobImage", "custom image", "project image", "rails build", "monorepo build", "artifacts".
4
4
  ---
5
5
 
6
6
  # Component builds with catladder
@@ -66,6 +66,10 @@ type: `build: { from: "web" }` (see workspace builds below).
66
66
  won't run).
67
67
  - `lint` / `test` / `audit` — customize (`{ command, jobImage, … }`) or
68
68
  set to `false` to disable that job.
69
+ - `postBuildTests` — named tests against the **build output** (e.g. an
70
+ e2e suite against the production build with a Postgres service). Each
71
+ entry becomes a job in the `post-build` stage between build and deploy
72
+ and blocks the deploy when it fails (see below).
69
73
  - `artifactsPaths` / `artifactsExcludePaths` — extra build artifacts
70
74
  (`dist` and `.next` are always included).
71
75
  - `cache` — build caching (see the `catladder-pipelines` skill for the
@@ -82,11 +86,55 @@ type: `build: { from: "web" }` (see workspace builds below).
82
86
  (`{ type: "nginx" | "node" | "meteor" }`) or
83
87
  `{ type: "custom" }` (expects a `Dockerfile`).
84
88
 
89
+ ## Post-build tests (e2e against the build)
90
+
91
+ `lint`/`test` run against the sources, in parallel with the build. Tests
92
+ that need the built app go into `postBuildTests`:
93
+
94
+ ```ts
95
+ build: {
96
+ type: "node",
97
+ postBuildTests: {
98
+ e2e: {
99
+ command: "pnpm test:e2e", // runs in the build dir
100
+ jobImage: "mcr.microsoft.com/playwright:v1.56.0-noble",
101
+ services: [{ name: "postgres:17", alias: "postgres",
102
+ variables: { POSTGRES_PASSWORD: "postgres" } }],
103
+ vars: { DATABASE_URL: "postgres://postgres:postgres@postgres:5432/postgres" },
104
+ artifacts: { paths: ["apps/web/playwright-report"] }, // repo-root relative
105
+ },
106
+ },
107
+ }
108
+ ```
109
+
110
+ - The job (`🔬 e2e`, stage `post-build <env>`) downloads the build
111
+ artifacts (`.next`, `dist`, `artifactsPaths`), installs dependencies
112
+ for node builds, then runs `command`. The command starts the app
113
+ itself, e.g. playwright `webServer: { command: "pnpm start" }`.
114
+ Builds without artifacts (`rails`, `buildCommand: false`) just run
115
+ it after the build.
116
+ - It is a quality gate: the env's deploy waits for it; on github it is
117
+ part of the `catladder ✅` required check. `allowFailure: true` makes
118
+ it report-only.
119
+ - It gets the build vars plus `vars`, **not** the deployed env's
120
+ runtime vars/secrets — use services instead of the real database.
121
+ - Artifacts are uploaded also when the job fails.
122
+ - Not run in tagged releases. Disable one per env:
123
+ `env: { dev: { build: { postBuildTests: { e2e: false } } } }`.
124
+ - Names must not clash with other jobs of the component (e.g. `test`,
125
+ `lint`) — generation fails with a clear error.
126
+ - Workspace builds: on `builds.<ws>` they block the deploys of all
127
+ components in the workspace; on `build: { from: "<ws>", postBuildTests }`
128
+ only that component's deploy.
129
+ - To test the **deployed** env instead (smoke tests on dev / review
130
+ apps), use the component's `verify` (post-deploy, does not block the
131
+ deploy). The same suite can serve both, with `BASE_URL` as the switch.
132
+
85
133
  ## Project images (custom CI job images)
86
134
 
87
135
  When a job needs a toolchain catladder doesn't ship (Java, Playwright,
88
136
  …), declare a Docker image at the top level under `images` and reference
89
- it in any `jobImage` field (build, `test.jobImage`, custom/pages deploy,
137
+ it in any `jobImage` field (build, `test.jobImage`, `postBuildTests`, custom/pages deploy,
90
138
  verify):
91
139
 
92
140
  ```ts
@@ -12,6 +12,7 @@ generated pipeline files.
12
12
  | `startCommand` | `string` | runtime start command |
13
13
  | `postInstall` | `string \| string[]` | runs after the package-manager install (node family) |
14
14
  | `lint` / `test` / `audit` | `false \| TestJobCustom` | customize or disable the job |
15
+ | `postBuildTests` | `Record<string, PostBuildTestConfig \| false>` | named tests against the build output in the `post-build` stage; block the deploy |
15
16
  | `artifactsPaths` | `string[]` | extra artifacts (`dist`, `.next` always included) |
16
17
  | `artifactsExcludePaths` | `string[]` | artifact excludes |
17
18
  | `artifactsReports` | `{ junit?: string[] }` | CI test report paths |
@@ -27,6 +28,13 @@ generated pipeline files.
27
28
  `artifactsReports`, `artifacts`, `runnerVariables`, `allowFailure`
28
29
  (`allowFailure` defaults to `true` for `audit`).
29
30
 
31
+ `PostBuildTestConfig` (for `postBuildTests` entries): `TestJobCustom` with
32
+ a required `command`, plus `services?: Services` (gitlab shape, reachable
33
+ by alias) and `vars?: Record<string,string>`. The job gets the build
34
+ artifacts, the build vars and `vars` (not the deployed env's runtime
35
+ vars), installs dependencies for node builds, and uploads `artifacts`
36
+ also on failure.
37
+
30
38
  `audit` additionally takes `level`: `"info" | "low" | "moderate" | "high" |
31
39
  "critical"` (default `"critical"`) — the lowest severity that fails the job,
32
40
  translated to the package manager's own flag (`pnpm audit --audit-level`,
@@ -81,5 +89,5 @@ defaulted `buildCommand: "yarn build-storybook --quiet -o ./dist"`.
81
89
  Top-level `builds: Record<string, WorkspaceBuildConfig>`. Only
82
90
  `type: "node"` is supported.
83
91
 
84
- - Node workspace build: `dir?`, `buildCommand?` (default `<pm> build`), `dockerDefaults?: { yarnRebuildEnabled }`, plus shared `lint?` (default `{ command: "<pm> lint" }`), `test?` (default `{ command: "<pm> test" }`), `audit?`, `runnerVariables?`, `artifactsReports?`, `jobImage?`, `jobTags?`, `cache?` (`<pm>` = `yarn`/`pnpm` per detection).
85
- - A component references it via `build: { from: "<name>", docker?, startCommand?, artifactsPaths?, artifactsExcludePaths?, cache? }` — the per-component overrides layer on top of the shared build.
92
+ - Node workspace build: `dir?`, `buildCommand?` (default `<pm> build`), `dockerDefaults?: { yarnRebuildEnabled }`, plus shared `lint?` (default `{ command: "<pm> lint" }`), `test?` (default `{ command: "<pm> test" }`), `audit?`, `postBuildTests?` (block the deploys of all its components), `runnerVariables?`, `artifactsReports?`, `jobImage?`, `jobTags?`, `cache?` (`<pm>` = `yarn`/`pnpm` per detection).
93
+ - A component references it via `build: { from: "<name>", docker?, startCommand?, artifactsPaths?, artifactsExcludePaths?, cache?, postBuildTests? }` — the per-component overrides layer on top of the shared build.
@@ -86,6 +86,19 @@ pin the review apps of a merge request (disable their auto-stop) by adding the p
86
86
  - `mr` (positional): merge request IID (defaults to the open MR of the current branch)
87
87
  - `--pipeline`: trigger a merge-request pipeline so the pin takes effect immediately
88
88
 
89
+ ## `catladder mr review-app-off [mr]`
90
+
91
+ stop deploying the review apps of a merge request: switches the reviewApps label off (opt-in: removes it, opt-out: adds the skip label). Running review apps stay until the MR closes or they auto-stop
92
+
93
+ - `mr` (positional): merge request IID (defaults to the open MR of the current branch)
94
+
95
+ ## `catladder mr review-app-on [mr]`
96
+
97
+ deploy the review apps of a merge request: switches the reviewApps label on (opt-in: adds it, opt-out: removes the skip label) and triggers a pipeline, since gitlab doesn't start one for label changes
98
+
99
+ - `mr` (positional): merge request IID (defaults to the open MR of the current branch)
100
+ - `--pipeline`: trigger a merge-request pipeline so the review apps deploy now
101
+
89
102
  ## `catladder mr unpin [mr]`
90
103
 
91
104
  unpin the review apps of a merge request — the auto-stop timer re-arms with the next deploy
@@ -104,6 +104,10 @@ Key concepts:
104
104
  affects both). The object form separates the axes:
105
105
  `inherit: { config: "dev" }` inherits config only,
106
106
  `inherit: { config: "dev", secrets: "dev" }` both.
107
+ - **Review apps**: top-level `reviewApps: { deploy, label, skipLabel,
108
+ drafts }` decides whether merge/pull requests deploy their review
109
+ apps — every MR/PR (default), opt-in or opt-out by label, or on
110
+ request; and what drafts run. See the `catladder-pipelines` skill.
107
111
  - **Pipelines**: `pipelines: { gitlab: true, github: true }` selects
108
112
  which CI systems get generated files. Options objects instead of
109
113
  `true` allow per-pipeline settings (e.g. `runnerVariables`,
@@ -46,6 +46,12 @@ is `auto`.** Set per component or per environment (`env.<name>.deploy`).
46
46
  `waitFor: ["otherComponent"]` (experimental) makes a deploy wait for
47
47
  another component to deploy first.
48
48
 
49
+ Whether merge/pull requests deploy review apps at all (opt-in/opt-out
50
+ by label, drafts, deploy on request for all components at once) is the
51
+ project-wide top-level `reviewApps` config — see the
52
+ `catladder-pipelines` skill. With it set, per-component `when` of the
53
+ review env is ignored.
54
+
49
55
  ## Environment lifetimes
50
56
 
51
57
  Review apps auto-stop after 1 week, dev environments after 4 weeks
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: catladder-pipelines
3
- description: Understanding and debugging the CI/CD pipelines catladder generates (GitLab CI and GitHub Actions) — pipeline triggers, stages, environments, caching, job images, releases, and infrastructure drift. Use when investigating failing CI jobs, when asked why/when a job runs, when changing caching or job behavior, when deployments misbehave, or when a review app stopped/expired or should be kept alive (pinning). Triggers on "pipeline", "CI job", "gitlab-ci", "workflow", "deploy failed", "cache", "release", "auto-stop", "review app stopped", "pin review app", "keep review app".
3
+ description: Understanding and debugging the CI/CD pipelines catladder generates (GitLab CI and GitHub Actions) — pipeline triggers, stages, environments, caching, job images, releases, and infrastructure drift. Use when investigating failing CI jobs, when asked why/when a job runs, when changing caching or job behavior, when deployments misbehave, when a review app should (not) be deployed for a merge/pull request (opt-in/opt-out labels, drafts, deploy on request), or when a review app stopped/expired or should be kept alive (pinning). Triggers on "pipeline", "CI job", "gitlab-ci", "workflow", "deploy failed", "cache", "release", "auto-stop", "review app stopped", "pin review app", "keep review app", "deploy review app", "review app label", "skip review app", "draft PR", "reviewApps".
4
4
  ---
5
5
 
6
6
  # How catladder pipelines work
@@ -46,8 +46,9 @@ review app`); the same workflow can be dispatched with a PR number to
46
46
  tear one down while the pull request is still open (gitlab runs these as manual jobs inside the pipeline
47
47
  instead).
48
48
 
49
- Stages: setup → test → build → deploy → verify (post-deploy checks),
50
- plus stop jobs for review-app teardown.
49
+ Stages: setup → test → build → post-build (`postBuildTests` against
50
+ the build output, block the deploy) → deploy → verify (post-deploy
51
+ checks), plus stop jobs for review-app teardown.
51
52
 
52
53
  ## Superseded pipelines are cancelled
53
54
 
@@ -81,6 +82,84 @@ started keeps _itself_ alive — under gitlab's own default
81
82
  (`conservative`, which catladder overrides) it would shield the whole
82
83
  pipeline from cancellation.
83
84
 
85
+ ## When review apps deploy (`reviewApps`)
86
+
87
+ By default every merge/pull request deploys its review apps. Projects
88
+ with many MRs/PRs (e.g. opened by agents) can put the review delivery
89
+ chains — docker image → deploy → verify, for all components — behind
90
+ ONE switch per MR/PR with the top-level `reviewApps` config. Tests,
91
+ lint and audit always run; only the delivery waits.
92
+
93
+ ```ts
94
+ reviewApps: {
95
+ deploy: "optIn", // auto (default) | manual | optIn | optOut
96
+ // label: "catladder:review-app", // optIn label (default)
97
+ // skipLabel: "catladder:no-review-app", // optOut label (default)
98
+ drafts: "checksOnly", // likeReady (default) | checksOnly (no review apps) | skip (no pipeline)
99
+ },
100
+ ```
101
+
102
+ - `optIn`: deploys only while the MR/PR carries `label`.
103
+ - `optOut`: deploys unless it carries `skipLabel`.
104
+ - `manual`: never on its own — only on request.
105
+ - `drafts: "checksOnly"`: drafts run tests, lint and audit only, the review apps deploy once the
106
+ MR/PR is marked ready; `"skip"`: drafts run nothing.
107
+ - Per-component `deploy.when` of the review env is ignored then.
108
+
109
+ **GitHub** — like gitlab, the review workflow decides at its start
110
+ from the PR's labels and draft state: when the policy says deploy, the
111
+ chains run in the same run (one app build). Whatever changes later goes
112
+ through the `▶️ catladder deploy review` workflow
113
+ (`catladder-deploy-review.yml`, which rebuilds the app):
114
+
115
+ - when the review workflow is green but didn't deploy, its last job
116
+ (`▶️ trigger review deploy`) reads the PR's labels/draft state again
117
+ and dispatches the deploy if the policy now says so — a label set
118
+ while CI ran counts;
119
+ - switching the label on (opt-in: add `label`, opt-out: remove
120
+ `skipLabel`) deploys right away if the PR's `catladder ✅` is green;
121
+ switching it off stops the review apps (`🛑 catladder stop review
122
+ app`), and so does closing the PR — the stop first waits for a
123
+ review run of the PR that may still be deploying (on close it
124
+ cancels it), so a late deploy can't outlive the teardown;
125
+ - on request, any time (agents included):
126
+ `gh workflow run catladder-deploy-review.yml --ref <pr-branch> -f pr=<number>`
127
+ — explicit requests always deploy, but need the PR open and its CI
128
+ green (the run reuses the green CI instead of re-running tests);
129
+ - merge gates wait for **`catladder review app ✅`** (stable name,
130
+ independent of the components), the last job of whichever run
131
+ deploys. It also sets the commit status `catladder review app` on
132
+ the PR head (`success`/`failure`; the deploy workflow marks it
133
+ `pending` first) — dispatched runs are attached to the commit but not
134
+ listed on the pull request, the commit status is. A run that doesn't
135
+ deploy, or is cancelled (superseded, stopped), reports nothing — a
136
+ gate must treat a missing or `pending` status on the current head as
137
+ "wait". In the review run a skipped chain doesn't
138
+ fail `catladder ✅`, a failed one does;
139
+ - `gh pr edit <number> --add-label catladder:review-app` is the
140
+ agent-friendly opt-in. Labels set with the workflow's own
141
+ `GITHUB_TOKEN` (from inside a workflow) trigger nothing — use a user
142
+ or app token;
143
+ - the dispatch only works once the workflow exists on the default
144
+ branch (github rule) — the PR introducing `reviewApps` can't dispatch
145
+ yet.
146
+
147
+ A single component with `env.review.deploy.when: "manual"` (and no
148
+ `reviewApps`) uses the same workflow for just that component, without
149
+ the automatic trigger.
150
+
151
+ **GitLab** — a `🚀 deploy review` job in the MR pipeline is the
152
+ switch: the docker and deploy jobs of all components `need` it. The
153
+ policy decides whether it starts on its own (label set / opt-out not
154
+ set) or waits as a manual job; playing it any time deploys (early
155
+ clicks wait for builds and tests). It has `allow_failure: true`, so an
156
+ unplayed switch leaves the pipeline green. Label changes don't start
157
+ a pipeline on gitlab — use `yarn catladder mr review-app-on` (sets the
158
+ label and triggers a pipeline) / `mr review-app-off`. Drafts are
159
+ detected by their title prefix (`Draft:`, `[Draft]`, `(Draft)`); with
160
+ `drafts: "skip"` marking an MR ready starts no pipeline, the next push
161
+ does.
162
+
84
163
  ## Review-app auto-stop and pinning (GitLab)
85
164
 
86
165
  GitLab review environments stop automatically after 1 week, dev
@@ -151,6 +230,21 @@ pipelines: { github: { repository: "AcmeCorp/Nautilus" } },
151
230
  `yarn catladder project doctor` reports a mixed-case repository whose
152
231
  generated workflows still carry the expression.
153
232
 
233
+ ### Artifacts between jobs (GitHub)
234
+
235
+ A job's `artifacts.paths` are handed to the jobs that need them as one
236
+ tar archive (`catladder-artifacts-<job-id>.tar`): an "Archive
237
+ artifacts" step tars them after the job, `upload-artifact` uploads the
238
+ archive under the job id, and the consumers get a matching "Extract
239
+ artifacts" step after `download-artifact`. The tar keeps symlinks, file
240
+ modes and repo-relative paths, as GitLab artifacts do. Plain
241
+ `upload-artifact` loses them, which breaks Next.js/Turbopack builds in
242
+ pnpm workspaces: `.next/node_modules/<pkg>-<hash>` are symlinks into
243
+ the pnpm store, and as copied directories they fail at runtime with
244
+ `ERR_MODULE_NOT_FOUND`. If a deployed app shows that error, check that
245
+ the committed workflows contain the archive steps and regenerate if
246
+ they don't.
247
+
154
248
  ## Caching
155
249
 
156
250
  Cache configuration is generated per build type. **yarn** node builds