@catladder/cli 0.0.0-fix-changeset-hint-package-manager-20002c27 → 0.0.0-fix-github-gating-release-push-fbb576dc

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 (35) hide show
  1. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithub.js +31 -9
  2. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithub.js.map +1 -1
  3. package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.d.ts +70 -0
  4. package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.js +71 -0
  5. package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.js.map +1 -0
  6. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupGithub.d.ts +9 -7
  7. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupGithub.js +77 -31
  8. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupGithub.js.map +1 -1
  9. package/dist/apps/cli/src/release/changesetsReleaseJob.js +19 -1
  10. package/dist/apps/cli/src/release/changesetsReleaseJob.js.map +1 -1
  11. package/dist/apps/cli/src/utils/github.d.ts +1 -1
  12. package/dist/apps/cli/src/utils/github.js.map +1 -1
  13. package/dist/bundles/catci/index.js +1 -1
  14. package/dist/bundles/catenv/index.js +200 -8
  15. package/dist/bundles/cli/index.js +386 -48
  16. package/dist/bundles/skills/catladder-deploys/references/deploy-types.md +1 -0
  17. package/dist/bundles/skills/catladder-migrate-ci-backend/SKILL.md +11 -7
  18. package/dist/packages/pipeline/src/deploy/cloudRun/index.js +6 -1
  19. package/dist/packages/pipeline/src/deploy/cloudRun/index.js.map +1 -1
  20. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.d.ts +68 -0
  21. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js +184 -3
  22. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js.map +1 -1
  23. package/dist/packages/pipeline/src/store/index.d.ts +8 -0
  24. package/dist/packages/pipeline/src/store/index.js +9 -3
  25. package/dist/packages/pipeline/src/store/index.js.map +1 -1
  26. package/dist/skills/catladder-deploys/references/deploy-types.md +1 -0
  27. package/dist/skills/catladder-migrate-ci-backend/SKILL.md +11 -7
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +1 -1
  30. package/src/apps/cli/commands/project/__tests__/githubMergeGating.test.ts +112 -0
  31. package/src/apps/cli/commands/project/doctor/checkGithub.ts +44 -8
  32. package/src/apps/cli/commands/project/githubMergeGating.ts +90 -0
  33. package/src/apps/cli/commands/project/setup/setupGithub.ts +105 -26
  34. package/src/release/changesetsReleaseJob.ts +19 -1
  35. package/src/utils/github.ts +1 -1
@@ -1,6 +1,74 @@
1
+ import type { StringOrBashExpression } from "../../../../../bash/src/index.js";
1
2
  import type { DeployConfigCloudRun } from "../..";
2
3
  import type { BuildConfig } from "../../../build/types";
3
4
  import type { ComponentContext } from "../../../types/context";
4
5
  import type { EnvironmentContext } from "../../../types/environmentContext";
6
+ /**
7
+ * cloud run only serves its deterministic url
8
+ * `https://<service>-<projectNumber>.<region>.run.app` while that first dns
9
+ * label stays within the 63 characters a dns label allows — the label holds
10
+ * the service name, the project number and any traffic tag together.
11
+ *
12
+ * Beyond it cloud run silently switches to a legacy url built from a random
13
+ * identifier that nothing can recompute. The url catladder generates would
14
+ * then be a hostname that can never resolve, and everything consuming
15
+ * ROOT_URL/ROOT_URL_INTERNAL (cloud tasks targets, service-to-service
16
+ * calls) fails with a dns error instead of a helpful one — which is why the
17
+ * component part is shortened to stay under the limit rather than left to
18
+ * overflow.
19
+ *
20
+ * https://cloud.google.com/run/docs/triggering/https-request
21
+ */
22
+ export declare const CLOUD_RUN_DNS_SEGMENT_LIMIT = 63;
23
+ /**
24
+ * review environments resolve their slug at runtime (`mr<iid>` on gitlab,
25
+ * `pr<number>` on github), so its exact length is unknown while generating.
26
+ * Budget for a six digit number so that service names — and with them the
27
+ * urls of already deployed services — do not start changing years later
28
+ * just because a project's merge request numbers grew a digit.
29
+ */
30
+ export declare const REVIEW_SLUG_LENGTH_BUDGET: number;
31
+ export type CloudRunComponentPartInput = {
32
+ customerName: string;
33
+ appName: string;
34
+ env: string;
35
+ /** whether the env resolves a review slug at runtime */
36
+ isReviewEnv: boolean;
37
+ componentName: string;
38
+ projectNumber: string;
39
+ /**
40
+ * the config's other component names, so that a shortened name colliding
41
+ * with a sibling's is caught while generating instead of at deploy time,
42
+ * where two components would fight over one cloud run service
43
+ */
44
+ otherComponentNames: string[];
45
+ };
46
+ export type CloudRunComponentPart = {
47
+ /** the component part of the service name, shortened if it had to be */
48
+ value: string;
49
+ /** characters dropped from the component name — 0 when it already fit */
50
+ dropped: number;
51
+ /** dns label length of the resulting url, review slug budget included */
52
+ dnsSegmentLength: number;
53
+ };
54
+ /**
55
+ * the component-name part of a cloud run service name, shortened just
56
+ * enough to keep the deterministic url resolvable.
57
+ *
58
+ * Only the component part is shortened: the env and review slug parts have
59
+ * to stay intact (they are what makes the name unique per environment), the
60
+ * project number is fixed, and customerName/appName are shared by every
61
+ * service of the project.
62
+ */
63
+ export declare const resolveCloudRunComponentPart: ({ customerName, appName, env, isReviewEnv, componentName, projectNumber, otherComponentNames, }: CloudRunComponentPartInput) => CloudRunComponentPart;
64
+ /**
65
+ * the cloud run service name as it is passed to `gcloud run` — lowercased,
66
+ * and shortened when the deterministic url would otherwise not fit.
67
+ */
5
68
  export declare const getServiceName: (context: ComponentContext) => string | import("../../../../../bash/src/index.js").BashExpression;
6
69
  export declare const getServiceNameForEnvContext: (context: EnvironmentContext<BuildConfig, DeployConfigCloudRun>) => string | import("../../../../../bash/src/index.js").BashExpression;
70
+ /**
71
+ * the service name part of the deterministic url, still to be lowercased
72
+ * together with the rest of the hostname by the caller.
73
+ */
74
+ export declare const getRawServiceNameForEnvContext: (context: EnvironmentContext<BuildConfig, DeployConfigCloudRun>) => StringOrBashExpression;
@@ -1,8 +1,189 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getServiceNameForEnvContext = exports.getServiceName = void 0;
4
- const getServiceName = (context) => context.environment.fullName.toLowerCase();
3
+ exports.getRawServiceNameForEnvContext = exports.getServiceNameForEnvContext = exports.getServiceName = exports.resolveCloudRunComponentPart = exports.REVIEW_SLUG_LENGTH_BUDGET = exports.CLOUD_RUN_DNS_SEGMENT_LIMIT = void 0;
4
+ const bash_1 = require("../../../../../bash/src/index.js");
5
+ const store_1 = require("../../../store");
6
+ /**
7
+ * cloud run only serves its deterministic url
8
+ * `https://<service>-<projectNumber>.<region>.run.app` while that first dns
9
+ * label stays within the 63 characters a dns label allows — the label holds
10
+ * the service name, the project number and any traffic tag together.
11
+ *
12
+ * Beyond it cloud run silently switches to a legacy url built from a random
13
+ * identifier that nothing can recompute. The url catladder generates would
14
+ * then be a hostname that can never resolve, and everything consuming
15
+ * ROOT_URL/ROOT_URL_INTERNAL (cloud tasks targets, service-to-service
16
+ * calls) fails with a dns error instead of a helpful one — which is why the
17
+ * component part is shortened to stay under the limit rather than left to
18
+ * overflow.
19
+ *
20
+ * https://cloud.google.com/run/docs/triggering/https-request
21
+ */
22
+ exports.CLOUD_RUN_DNS_SEGMENT_LIMIT = 63;
23
+ /**
24
+ * review environments resolve their slug at runtime (`mr<iid>` on gitlab,
25
+ * `pr<number>` on github), so its exact length is unknown while generating.
26
+ * Budget for a six digit number so that service names — and with them the
27
+ * urls of already deployed services — do not start changing years later
28
+ * just because a project's merge request numbers grew a digit.
29
+ */
30
+ exports.REVIEW_SLUG_LENGTH_BUDGET = "mr123456".length;
31
+ /**
32
+ * a service name shortened past this point is no longer recognizable. When
33
+ * even this does not fit, the fixed parts (customer name, app name, env,
34
+ * project number) are what needs to shrink — only the user can do that.
35
+ */
36
+ const MIN_COMPONENT_PART_LENGTH = 6;
37
+ const quote = (name) => `"${name}"`;
38
+ /**
39
+ * a trailing dash would end up in the hostname as `...-foo-.run.app`
40
+ */
41
+ const shortenTo = (name, maxLength) => name.slice(0, maxLength).replace(/-+$/, "");
42
+ /**
43
+ * the component-name part of a cloud run service name, shortened just
44
+ * enough to keep the deterministic url resolvable.
45
+ *
46
+ * Only the component part is shortened: the env and review slug parts have
47
+ * to stay intact (they are what makes the name unique per environment), the
48
+ * project number is fixed, and customerName/appName are shared by every
49
+ * service of the project.
50
+ */
51
+ const resolveCloudRunComponentPart = ({ customerName, appName, env, isReviewEnv, componentName, projectNumber, otherComponentNames, }) => {
52
+ const fixedParts = [
53
+ customerName,
54
+ appName,
55
+ env,
56
+ ...(isReviewEnv ? ["_".repeat(exports.REVIEW_SLUG_LENGTH_BUDGET)] : []),
57
+ projectNumber,
58
+ ];
59
+ // + 1 for the dash that joins the component part to the rest
60
+ const fixedLength = fixedParts.join("-").length + 1;
61
+ const available = exports.CLOUD_RUN_DNS_SEGMENT_LIMIT - fixedLength;
62
+ if (componentName.length <= available) {
63
+ return {
64
+ value: componentName,
65
+ dropped: 0,
66
+ dnsSegmentLength: fixedLength + componentName.length,
67
+ };
68
+ }
69
+ if (available < MIN_COMPONENT_PART_LENGTH) {
70
+ throw new Error(`Cannot build a resolvable cloud run url for component ${quote(componentName)} in env ${quote(env)}.\n` +
71
+ `Everything but the component name already takes ${fixedLength} of the ${exports.CLOUD_RUN_DNS_SEGMENT_LIMIT} characters ` +
72
+ `a dns label allows (${fixedParts.join("-")}-<component>), leaving only ${available} for the component name.\n` +
73
+ `Shorten appName (${quote(appName)}) or customerName (${quote(customerName)}) by at least ` +
74
+ `${MIN_COMPONENT_PART_LENGTH - available} character(s).` +
75
+ (isReviewEnv
76
+ ? `\nReview environments also carry a runtime slug, for which ${exports.REVIEW_SLUG_LENGTH_BUDGET} characters are reserved.`
77
+ : ""));
78
+ }
79
+ const value = shortenTo(componentName, available);
80
+ const colliding = otherComponentNames.filter((other) => shortenTo(other, available) === value);
81
+ if (colliding.length > 0) {
82
+ throw new Error(`Cloud run service names collide in env ${quote(env)}: ${quote(componentName)} and ` +
83
+ `${colliding.map(quote).join(", ")} all shorten to ${quote(value)}, so they would deploy over each other.\n` +
84
+ `The deterministic cloud run url allows ${exports.CLOUD_RUN_DNS_SEGMENT_LIMIT} characters, which leaves ${available} ` +
85
+ `for the component name here — rename the components so their first ${available} characters differ.`);
86
+ }
87
+ return {
88
+ value,
89
+ dropped: componentName.length - value.length,
90
+ dnsSegmentLength: fixedLength + value.length,
91
+ };
92
+ };
93
+ exports.resolveCloudRunComponentPart = resolveCloudRunComponentPart;
94
+ /**
95
+ * one note per shortened service name instead of one per generated job:
96
+ * service names are resolved again for every job that references them
97
+ */
98
+ const reportedTruncations = new Set();
99
+ const reportTruncation = (env, componentName, { value, dnsSegmentLength }) => {
100
+ const key = `${env}/${componentName}`;
101
+ if (reportedTruncations.has(key)) {
102
+ return;
103
+ }
104
+ reportedTruncations.add(key);
105
+ console.warn(`ℹ️ component "${componentName}" deploys to cloud run as "...-${value}" in env "${env}": ` +
106
+ `the full name would push the url past the ${exports.CLOUD_RUN_DNS_SEGMENT_LIMIT} characters cloud run ` +
107
+ `serves deterministically, so the component part is shortened ` +
108
+ `(${dnsSegmentLength} of ${exports.CLOUD_RUN_DNS_SEGMENT_LIMIT} characters used).`);
109
+ };
110
+ /**
111
+ * the cloud run service name before lowercasing, so that callers keep
112
+ * lowercasing the whole expression exactly like they did before.
113
+ *
114
+ * Returns `fullName` unchanged unless the component part had to be
115
+ * shortened — every project whose url already fits generates byte-identical
116
+ * output.
117
+ *
118
+ * Note that the composition below has to match the one `fullName` is built
119
+ * from in getEnvironmentContext; the tests pin that they agree.
120
+ */
121
+ const getRawServiceName = ({ fullName, environmentSlugPrefix, componentName, env, isReviewEnv, deployConfig, config, }) => {
122
+ const projectNumber = deployConfig
123
+ ? (0, store_1.readGcloudProjectNumber)(config, deployConfig.projectId)
124
+ : null;
125
+ if (!projectNumber) {
126
+ // without the project number there is no deterministic url to keep
127
+ // resolvable — generating the url itself fails with a store error that
128
+ // says how to fix it, so don't second-guess the name here
129
+ return fullName;
130
+ }
131
+ const componentPart = (0, exports.resolveCloudRunComponentPart)({
132
+ customerName: config.customerName,
133
+ appName: config.appName,
134
+ env,
135
+ isReviewEnv,
136
+ componentName,
137
+ projectNumber,
138
+ otherComponentNames: Object.keys(config.components).filter((name) => name !== componentName),
139
+ });
140
+ if (componentPart.dropped === 0) {
141
+ return fullName;
142
+ }
143
+ reportTruncation(env, componentName, componentPart);
144
+ return (0, bash_1.joinBashExpressions)([
145
+ config.customerName,
146
+ config.appName,
147
+ environmentSlugPrefix,
148
+ componentPart.value,
149
+ ], "-");
150
+ };
151
+ const fromComponentContext = (context) => {
152
+ var _a;
153
+ const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
154
+ return {
155
+ fullName: context.environment.fullName,
156
+ environmentSlugPrefix: context.environment.slugPrefix,
157
+ componentName: context.name,
158
+ env: context.env,
159
+ isReviewEnv: context.environment.envType === "review",
160
+ deployConfig: deployConfig && deployConfig.type === "google-cloudrun"
161
+ ? deployConfig
162
+ : null,
163
+ config: context.fullConfig,
164
+ };
165
+ };
166
+ const fromEnvironmentContext = (context) => ({
167
+ fullName: context.fullName,
168
+ environmentSlugPrefix: context.environmentSlugPrefix,
169
+ componentName: context.componentName,
170
+ env: context.env,
171
+ isReviewEnv: context.envType === "review",
172
+ deployConfig: context.deployConfigRaw || null,
173
+ config: context.fullConfig,
174
+ });
175
+ /**
176
+ * the cloud run service name as it is passed to `gcloud run` — lowercased,
177
+ * and shortened when the deterministic url would otherwise not fit.
178
+ */
179
+ const getServiceName = (context) => getRawServiceName(fromComponentContext(context)).toLowerCase();
5
180
  exports.getServiceName = getServiceName;
6
- const getServiceNameForEnvContext = (context) => context.fullName.toLowerCase();
181
+ const getServiceNameForEnvContext = (context) => getRawServiceName(fromEnvironmentContext(context)).toLowerCase();
7
182
  exports.getServiceNameForEnvContext = getServiceNameForEnvContext;
183
+ /**
184
+ * the service name part of the deterministic url, still to be lowercased
185
+ * together with the rest of the hostname by the caller.
186
+ */
187
+ const getRawServiceNameForEnvContext = (context) => getRawServiceName(fromEnvironmentContext(context));
188
+ exports.getRawServiceNameForEnvContext = getRawServiceNameForEnvContext;
8
189
  //# sourceMappingURL=getServiceName.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getServiceName.js","sourceRoot":"","sources":["../../../../../../../../../packages/pipeline/src/deploy/cloudRun/utils/getServiceName.ts"],"names":[],"mappings":";;;AAIO,MAAM,cAAc,GAAG,CAAC,OAAyB,EAAE,EAAE,CAC1D,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;AADhC,QAAA,cAAc,kBACkB;AAEtC,MAAM,2BAA2B,GAAG,CACzC,OAA8D,EAC9D,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;AAFvB,QAAA,2BAA2B,+BAEJ"}
1
+ {"version":3,"file":"getServiceName.js","sourceRoot":"","sources":["../../../../../../../../../packages/pipeline/src/deploy/cloudRun/utils/getServiceName.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,14 @@ export declare const getCatladderStorePath: (directory: string) => string;
27
27
  * remedy is always rerunning project setup — not hand-editing the file.
28
28
  */
29
29
  export declare const readCatladderStore: (directory?: string) => CatladderStore;
30
+ /**
31
+ * gcloud project number from the store, or null when it has not been
32
+ * fetched yet. Use this where a missing number is not itself the problem —
33
+ * `getGcloudProjectNumber` is the one that reports it.
34
+ */
35
+ export declare const readGcloudProjectNumber: (config: {
36
+ store?: CatladderStore;
37
+ }, projectId: string) => string | null;
30
38
  /**
31
39
  * gcloud project number from the store — needed to compute deterministic
32
40
  * cloud run urls (`<service>-<projectNumber>.<region>.run.app`) at
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getGcloudProjectNumber = exports.readCatladderStore = exports.getCatladderStorePath = exports.catladderStoreSchema = exports.CATLADDER_STORE_FILE = exports.CATLADDER_STORE_FILE_NAME = exports.CATLADDER_STORE_DIR = void 0;
3
+ exports.getGcloudProjectNumber = exports.readGcloudProjectNumber = exports.readCatladderStore = exports.getCatladderStorePath = exports.catladderStoreSchema = exports.CATLADDER_STORE_FILE = exports.CATLADDER_STORE_FILE_NAME = exports.CATLADDER_STORE_DIR = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const path_1 = require("path");
6
6
  const yaml_1 = require("yaml");
@@ -52,6 +52,13 @@ const readCatladderStore = (directory = process.cwd()) => {
52
52
  }
53
53
  };
54
54
  exports.readCatladderStore = readCatladderStore;
55
+ /**
56
+ * gcloud project number from the store, or null when it has not been
57
+ * fetched yet. Use this where a missing number is not itself the problem —
58
+ * `getGcloudProjectNumber` is the one that reports it.
59
+ */
60
+ const readGcloudProjectNumber = (config, projectId) => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = config.store) === null || _a === void 0 ? void 0 : _a.gcloudProjects) === null || _b === void 0 ? void 0 : _b[projectId]) === null || _c === void 0 ? void 0 : _c.projectNumber) !== null && _d !== void 0 ? _d : null; };
61
+ exports.readGcloudProjectNumber = readGcloudProjectNumber;
55
62
  /**
56
63
  * gcloud project number from the store — needed to compute deterministic
57
64
  * cloud run urls (`<service>-<projectNumber>.<region>.run.app`) at
@@ -61,8 +68,7 @@ exports.readCatladderStore = readCatladderStore;
61
68
  * catenv fail until `catladder project setup` has stored it.
62
69
  */
63
70
  const getGcloudProjectNumber = (config, projectId) => {
64
- var _a, _b, _c;
65
- const projectNumber = (_c = (_b = (_a = config.store) === null || _a === void 0 ? void 0 : _a.gcloudProjects) === null || _b === void 0 ? void 0 : _b[projectId]) === null || _c === void 0 ? void 0 : _c.projectNumber;
71
+ const projectNumber = (0, exports.readGcloudProjectNumber)(config, projectId);
66
72
  if (!projectNumber) {
67
73
  throw new Error(`Missing gcloud project number for project "${projectId}" in ${exports.CATLADDER_STORE_FILE}.\n` +
68
74
  `Run "catladder project setup" to fetch and store it.`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/pipeline/src/store/index.ts"],"names":[],"mappings":";;;AAAA,2BAA8C;AAC9C,+BAA4B;AAC5B,+BAA6B;AAC7B,6BAAwB;AAEX,QAAA,mBAAmB,GAAG,kBAAkB,CAAC;AACzC,QAAA,yBAAyB,GAAG,WAAW,CAAC;AACxC,QAAA,oBAAoB,GAAG,GAAG,2BAAmB,IAAI,iCAAyB,EAAE,CAAC;AAE1F;;;;;;;;GAQG;AACU,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C;;OAEG;IACH,cAAc,EAAE,OAAC;SACd,MAAM,CACL,OAAC,CAAC,MAAM,EAAE,EACV,OAAC,CAAC,MAAM,CAAC;QACP,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;KACzC,CAAC,CACH;SACA,QAAQ,EAAE;CACd,CAAC,CAAC;AAII,MAAM,qBAAqB,GAAG,CAAC,SAAiB,EAAE,EAAE,CACzD,IAAA,WAAI,EAAC,SAAS,EAAE,2BAAmB,EAAE,iCAAyB,CAAC,CAAC;AADrD,QAAA,qBAAqB,yBACgC;AAElE;;;;;;;GAOG;AACI,MAAM,kBAAkB,GAAG,CAChC,YAAoB,OAAO,CAAC,GAAG,EAAE,EACjB,EAAE;;IAClB,MAAM,IAAI,GAAG,IAAA,6BAAqB,EAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAA,eAAU,EAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,4BAAoB,CAAC,KAAK,CAC/B,MAAA,IAAA,YAAK,EAAC,IAAA,iBAAY,EAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,mCAAI,EAAE,CACvD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CACV,uBAAuB,4BAAoB,wDACzC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAC3C,EAAE,CACH,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC;AAnBW,QAAA,kBAAkB,sBAmB7B;AAEF;;;;;;;GAOG;AACI,MAAM,sBAAsB,GAAG,CACpC,MAAkC,EAClC,SAAiB,EACT,EAAE;;IACV,MAAM,aAAa,GACjB,MAAA,MAAA,MAAA,MAAM,CAAC,KAAK,0CAAE,cAAc,0CAAG,SAAS,CAAC,0CAAE,aAAa,CAAC;IAC3D,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,8CAA8C,SAAS,QAAQ,4BAAoB,KAAK;YACtF,sDAAsD,CACzD,CAAC;IACJ,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC;AAbW,QAAA,sBAAsB,0BAajC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/pipeline/src/store/index.ts"],"names":[],"mappings":";;;AAAA,2BAA8C;AAC9C,+BAA4B;AAC5B,+BAA6B;AAC7B,6BAAwB;AAEX,QAAA,mBAAmB,GAAG,kBAAkB,CAAC;AACzC,QAAA,yBAAyB,GAAG,WAAW,CAAC;AACxC,QAAA,oBAAoB,GAAG,GAAG,2BAAmB,IAAI,iCAAyB,EAAE,CAAC;AAE1F;;;;;;;;GAQG;AACU,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C;;OAEG;IACH,cAAc,EAAE,OAAC;SACd,MAAM,CACL,OAAC,CAAC,MAAM,EAAE,EACV,OAAC,CAAC,MAAM,CAAC;QACP,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;KACzC,CAAC,CACH;SACA,QAAQ,EAAE;CACd,CAAC,CAAC;AAII,MAAM,qBAAqB,GAAG,CAAC,SAAiB,EAAE,EAAE,CACzD,IAAA,WAAI,EAAC,SAAS,EAAE,2BAAmB,EAAE,iCAAyB,CAAC,CAAC;AADrD,QAAA,qBAAqB,yBACgC;AAElE;;;;;;;GAOG;AACI,MAAM,kBAAkB,GAAG,CAChC,YAAoB,OAAO,CAAC,GAAG,EAAE,EACjB,EAAE;;IAClB,MAAM,IAAI,GAAG,IAAA,6BAAqB,EAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAA,eAAU,EAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,4BAAoB,CAAC,KAAK,CAC/B,MAAA,IAAA,YAAK,EAAC,IAAA,iBAAY,EAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,mCAAI,EAAE,CACvD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CACV,uBAAuB,4BAAoB,wDACzC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAC3C,EAAE,CACH,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC;AAnBW,QAAA,kBAAkB,sBAmB7B;AAEF;;;;GAIG;AACI,MAAM,uBAAuB,GAAG,CACrC,MAAkC,EAClC,SAAiB,EACF,EAAE,uBACjB,OAAA,MAAA,MAAA,MAAA,MAAA,MAAM,CAAC,KAAK,0CAAE,cAAc,0CAAG,SAAS,CAAC,0CAAE,aAAa,mCAAI,IAAI,CAAA,EAAA,CAAC;AAJtD,QAAA,uBAAuB,2BAI+B;AAEnE;;;;;;;GAOG;AACI,MAAM,sBAAsB,GAAG,CACpC,MAAkC,EAClC,SAAiB,EACT,EAAE;IACV,MAAM,aAAa,GAAG,IAAA,+BAAuB,EAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjE,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,8CAA8C,SAAS,QAAQ,4BAAoB,KAAK;YACtF,sDAAsD,CACzD,CAAC;IACJ,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC;AAZW,QAAA,sBAAsB,0BAYjC"}
@@ -46,6 +46,7 @@ resources with `yarn catladder project setup`; detect drift with
46
46
  - `cloudSql?: DeployConfigCloudRunCloudSql | false` — `{ type: "unmanaged", instanceConnectionName, dbUser?, dbNamePrefix?, dbBaseName?, deleteDatabaseOnStop?, dbConnectionStringFormat? ("prisma" default | "rails" | "jdbc"), dbConnectionStringVariablesMode? ("embedded" default | "legacy"), dbAdditionalQueryParams? }`. Injects `DB_*` vars plus `DATABASE_URL`/`DATABASE_JDBC_URL`; in the default `"embedded"` mode the connection strings contain the component's final values (referenceable from other components, `vars.public` overrides of `DB_*` flow in). To share another component's database: `dbBaseName: "<owner>"` + `DB_PASSWORD: "${<owner>:DB_PASSWORD}"` in `vars.public`.
47
47
  - `execute?: Record<string, DeployConfigCloudRunExecute | null>` — run a script / job / HTTP call at a lifecycle point (`when`: `preDeploy` | `postDeploy` | `preStop` | `postStop`) or on a `schedule`.
48
48
  - `debug?: boolean`.
49
+ - Service name and url: the service deploys as `<customerName>-<appName>-<env>[-<reviewSlug>]-<componentName>`, and `ROOT_URL`/`ROOT_URL_INTERNAL` are cloud run's deterministic url `https://<service>-<projectNumber>.<region>.run.app`. That url only exists while its first dns label stays within 63 characters, so catladder **shortens the component part** of the service name when it would not fit — otherwise cloud run would serve a legacy url with a random identifier and the generated hostname would never resolve. Review environments carry a runtime slug on top, for which 8 characters are reserved, so a name can fit `dev`/`prod` and still be shortened for `review`. Generation fails instead of shortening when two components would end up with the same name, or when customerName/appName/env/project number already leave no room.
49
50
 
50
51
  ## `npmPackage`
51
52
 
@@ -260,13 +260,17 @@ now-wrong `gitRemote`, regenerate once more, and finish with
260
260
  PR is mergeable the moment it opens, checks still running, and the
261
261
  auto-merge button does not even appear. `yarn catladder project setup`
262
262
  configures it: auto-merge + delete-merged-branches on the repo, and a
263
- branch protection rule on the default branch requiring the generated
264
- **`catladder ✅`** aggregate check (the one stable context — never
265
- require individual job names, they change with every component
266
- add/rename and a stale required name blocks its own PR forever).
267
- `project doctor` verifies it. Two things stay human choices: "Do not
268
- allow bypassing" (leave it off — generated CI means a generation bug
269
- can only be fixed via the admin bypass) and required reviews.
263
+ repository **ruleset** (`catladder merge gating`) on the default
264
+ branch requiring the generated **`catladder ✅`** aggregate check
265
+ (the one stable context — never require individual job names, they
266
+ change with every component add/rename and a stale required name
267
+ blocks its own PR forever). The ruleset carries a bypass for the
268
+ GitHub Actions app: the release job pushes the release commit and
269
+ tag straight to the default branch with the workflow token, which a
270
+ required check would otherwise reject (GH006 — classic branch
271
+ protection has no bypass list, which is why setup uses a ruleset and
272
+ migrates old classic-protection gating away). `project doctor`
273
+ verifies all of it. Required reviews stay a human choice.
270
274
  - Tell the team where the pipeline lives now and how manual actions
271
275
  work there (on GitHub, manual jobs are dispatch workflows in the
272
276
  Actions sidebar — `🚀 catladder create release`, `▶️ catladder deploy`,