@catladder/pipeline 0.0.0-node16-ddfae617 → 0.0.0-refactor-env-context-9e652b63

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 (175) hide show
  1. package/bin/catladder-gitlab.js +1 -1
  2. package/dist/build/base/createBuildJob.d.ts +2 -2
  3. package/dist/build/base/getBuildInfo.d.ts +1 -1
  4. package/dist/build/docker.d.ts +44 -6
  5. package/dist/build/docker.js +45 -19
  6. package/dist/build/index.d.ts +6 -5
  7. package/dist/build/node/buildJob.d.ts +2 -2
  8. package/dist/build/node/cache.d.ts +2 -2
  9. package/dist/build/node/index.d.ts +1 -1
  10. package/dist/build/node/meteor.d.ts +1 -1
  11. package/dist/build/node/meteor.js +6 -0
  12. package/dist/build/node/testJob.d.ts +2 -2
  13. package/dist/build/node/testJob.js +3 -0
  14. package/dist/build/node/yarn.d.ts +1 -1
  15. package/dist/build/node/yarn.js +13 -1
  16. package/dist/build/types.d.ts +10 -0
  17. package/dist/bundles/catladder-gitlab/index.js +32457 -0
  18. package/dist/catladder-gitlab.js +1 -2
  19. package/dist/config/configruedEnvs.d.ts +1 -1
  20. package/dist/config/readConfig.d.ts +1 -1
  21. package/dist/constants.js +1 -1
  22. package/dist/context/getEnvConfig.d.ts +9 -0
  23. package/dist/context/getEnvConfig.js +26 -0
  24. package/dist/context/getEnvType.d.ts +4 -0
  25. package/dist/context/getEnvType.js +18 -0
  26. package/dist/context/getEnvironment.d.ts +3 -0
  27. package/dist/context/getEnvironment.js +187 -0
  28. package/dist/context/getEnvironmentContext.d.ts +4 -0
  29. package/dist/context/getEnvironmentContext.js +36 -0
  30. package/dist/context/getEnvironmentVariables.d.ts +10 -0
  31. package/dist/context/getEnvironmentVariables.js +322 -0
  32. package/dist/context/getLabels.d.ts +8 -0
  33. package/dist/context/getLabels.js +49 -0
  34. package/dist/context/index.d.ts +5 -6
  35. package/dist/context/index.js +176 -181
  36. package/dist/context/resolveReferences.d.ts +1 -3
  37. package/dist/context/resolveReferences.js +275 -26
  38. package/dist/context/resolveReferences.test.js +332 -81
  39. package/dist/defaults/index.d.ts +1 -1
  40. package/dist/deploy/base.d.ts +4 -2
  41. package/dist/deploy/base.js +37 -17
  42. package/dist/deploy/cloudRun/deployJob.d.ts +3 -0
  43. package/dist/deploy/cloudRun/deployJob.js +139 -0
  44. package/dist/deploy/cloudRun/index.d.ts +9 -0
  45. package/dist/deploy/cloudRun/index.js +56 -0
  46. package/dist/deploy/cloudRun/utils/gcloudServiceAccountLoginCommands.d.ts +2 -0
  47. package/dist/deploy/cloudRun/utils/gcloudServiceAccountLoginCommands.js +14 -0
  48. package/dist/deploy/cloudSql/index.d.ts +8 -0
  49. package/dist/deploy/cloudSql/index.js +30 -0
  50. package/dist/deploy/custom/deployJob.d.ts +2 -2
  51. package/dist/deploy/custom/deployJob.js +4 -1
  52. package/dist/deploy/custom/index.d.ts +2 -1
  53. package/dist/deploy/custom/index.js +15 -18
  54. package/dist/deploy/dockerTag/deployJob.d.ts +3 -0
  55. package/dist/deploy/dockerTag/deployJob.js +33 -0
  56. package/dist/deploy/dockerTag/index.d.ts +2 -0
  57. package/dist/deploy/dockerTag/index.js +19 -0
  58. package/dist/deploy/index.d.ts +12 -8
  59. package/dist/deploy/index.js +27 -14
  60. package/dist/deploy/kubernetes/additionalSecretKeys.d.ts +4 -0
  61. package/dist/deploy/kubernetes/additionalSecretKeys.js +33 -0
  62. package/dist/deploy/kubernetes/cloudsql.d.ts +1 -1
  63. package/dist/deploy/kubernetes/deployJob.d.ts +2 -2
  64. package/dist/deploy/kubernetes/deployJob.js +13 -66
  65. package/dist/deploy/kubernetes/index.d.ts +2 -1
  66. package/dist/deploy/kubernetes/index.js +45 -16
  67. package/dist/deploy/kubernetes/kubeEnv.d.ts +10 -0
  68. package/dist/deploy/kubernetes/kubeEnv.js +90 -0
  69. package/dist/deploy/kubernetes/kubeValues.d.ts +90 -0
  70. package/dist/deploy/kubernetes/kubeValues.js +96 -0
  71. package/dist/deploy/kubernetes/mongodb.d.ts +18 -5
  72. package/dist/deploy/kubernetes/mongodb.js +118 -8
  73. package/dist/deploy/kubernetes/processSecretsAsFiles.d.ts +21 -21
  74. package/dist/deploy/types/base.d.ts +15 -0
  75. package/dist/deploy/types/base.js +3 -0
  76. package/dist/deploy/types/custom.d.ts +27 -0
  77. package/dist/deploy/types/custom.js +3 -0
  78. package/dist/deploy/types/dockerTag.d.ts +10 -0
  79. package/dist/deploy/types/dockerTag.js +3 -0
  80. package/dist/deploy/types/googleCloudRun.d.ts +39 -0
  81. package/dist/deploy/types/googleCloudRun.js +3 -0
  82. package/dist/deploy/types/index.d.ts +24 -0
  83. package/dist/deploy/types/index.js +41 -0
  84. package/dist/deploy/{types.d.ts → types/kubernetes.d.ts} +128 -36
  85. package/dist/deploy/types/kubernetes.js +3 -0
  86. package/dist/deploy/utils.d.ts +2 -2
  87. package/dist/deploy/utils.js +4 -0
  88. package/dist/pipeline/createChildPipeline.d.ts +2 -2
  89. package/dist/pipeline/createChildPipeline.js +3 -3
  90. package/dist/pipeline/createJobs.d.ts +2 -2
  91. package/dist/pipeline/createJobs.js +63 -25
  92. package/dist/pipeline/gitlab/makeGitlabJob.d.ts +3 -2
  93. package/dist/pipeline/gitlab/makeGitlabJob.js +2 -1
  94. package/dist/pipeline/packageManager.d.ts +1 -1
  95. package/dist/rules/index.d.ts +1 -1
  96. package/dist/runner/index.d.ts +2 -3
  97. package/dist/tsconfig.tsbuildinfo +1 -1
  98. package/dist/types/config.d.ts +7 -6
  99. package/dist/types/config.js +1 -1
  100. package/dist/types/context.d.ts +10 -2
  101. package/dist/types/environmentContext.d.ts +22 -0
  102. package/dist/types/environmentContext.js +3 -0
  103. package/dist/types/gitlab-types.d.ts +1 -1
  104. package/dist/types/jobs.d.ts +8 -3
  105. package/dist/types/jobs.js +1 -1
  106. package/dist/types/pipeline.d.ts +1 -1
  107. package/package.json +15 -11
  108. package/scripts/bundle +2 -0
  109. package/src/build/base/createBuildJob.ts +3 -2
  110. package/src/build/base/getBuildInfo.ts +1 -1
  111. package/src/build/docker.ts +47 -29
  112. package/src/build/index.ts +8 -5
  113. package/src/build/node/buildJob.ts +2 -2
  114. package/src/build/node/cache.ts +2 -2
  115. package/src/build/node/index.ts +1 -1
  116. package/src/build/node/meteor.ts +8 -2
  117. package/src/build/node/testJob.ts +4 -2
  118. package/src/build/node/yarn.ts +21 -10
  119. package/src/build/tests/storybook.test.ts +1 -1
  120. package/src/build/types.ts +13 -1
  121. package/src/catladder-gitlab.ts +4 -2
  122. package/src/config/configruedEnvs.test.ts +2 -2
  123. package/src/config/configruedEnvs.ts +5 -4
  124. package/src/config/readConfig.ts +1 -1
  125. package/src/context/getEnvConfig.ts +21 -0
  126. package/src/context/getEnvType.ts +17 -0
  127. package/src/context/getEnvironment.ts +40 -0
  128. package/src/context/getEnvironmentContext.ts +57 -0
  129. package/src/context/getEnvironmentVariables.ts +152 -0
  130. package/src/context/getLabels.ts +20 -0
  131. package/src/context/index.ts +25 -191
  132. package/src/context/resolveReferences.test.ts +15 -15
  133. package/src/context/resolveReferences.ts +37 -30
  134. package/src/defaults/index.ts +1 -1
  135. package/src/deploy/base.ts +43 -19
  136. package/src/deploy/cloudRun/deployJob.ts +104 -0
  137. package/src/deploy/cloudRun/index.ts +48 -0
  138. package/src/deploy/cloudRun/utils/gcloudServiceAccountLoginCommands.ts +14 -0
  139. package/src/deploy/cloudSql/index.ts +16 -0
  140. package/src/deploy/custom/deployJob.ts +10 -3
  141. package/src/deploy/custom/index.ts +9 -1
  142. package/src/deploy/dockerTag/deployJob.ts +31 -0
  143. package/src/deploy/dockerTag/index.ts +9 -0
  144. package/src/deploy/index.ts +26 -17
  145. package/src/deploy/kubernetes/additionalSecretKeys.ts +23 -0
  146. package/src/deploy/kubernetes/cloudsql.ts +2 -1
  147. package/src/deploy/kubernetes/deployJob.ts +19 -77
  148. package/src/deploy/kubernetes/index.ts +42 -1
  149. package/src/deploy/kubernetes/kubeEnv.ts +48 -0
  150. package/src/deploy/kubernetes/kubeValues.ts +82 -0
  151. package/src/deploy/kubernetes/mongodb.ts +104 -7
  152. package/src/deploy/kubernetes/processSecretsAsFiles.ts +1 -1
  153. package/src/deploy/types/base.ts +17 -0
  154. package/src/deploy/types/custom.ts +24 -0
  155. package/src/deploy/types/dockerTag.ts +12 -0
  156. package/src/deploy/types/googleCloudRun.ts +79 -0
  157. package/src/deploy/types/index.ts +28 -0
  158. package/src/deploy/types/kubernetes.ts +281 -0
  159. package/src/deploy/utils.ts +6 -2
  160. package/src/pipeline/createChildPipeline.test.ts +1 -1
  161. package/src/pipeline/createChildPipeline.ts +4 -4
  162. package/src/pipeline/createJobs.ts +32 -21
  163. package/src/pipeline/gitlab/makeGitlabJob.ts +3 -2
  164. package/src/pipeline/packageManager.ts +1 -1
  165. package/src/rules/index.ts +1 -1
  166. package/src/runner/index.ts +2 -1
  167. package/src/types/config.ts +8 -6
  168. package/src/types/context.ts +14 -2
  169. package/src/types/environmentContext.ts +26 -0
  170. package/src/types/gitlab-types.ts +1 -1
  171. package/src/types/jobs.ts +9 -2
  172. package/src/types/pipeline.ts +1 -1
  173. package/tsconfig.json +2 -0
  174. package/dist/deploy/types.js +0 -16
  175. package/src/deploy/types.ts +0 -189
@@ -1,216 +1,50 @@
1
- import { isObject, merge } from "lodash";
2
- import slugify from "slugify";
3
1
  import { BUILD_TYPES } from "../build";
4
- import { BuildConfig } from "../build/types";
5
- import { DEPLOY_TYPES, getKubernetesNamespace } from "../deploy";
6
- import { DeployConfig } from "../deploy/types";
7
- import { Config, isKnowEnvType, DevLocalEnvConfig } from "../types/config";
8
- import {
9
- CommitInfo,
10
- Context,
11
- Environment,
12
- PackageManagerInfo,
13
- } from "../types/context";
2
+ import type { BuildConfig, BuildConfigType } from "../build/types";
3
+ import { DEPLOY_TYPES } from "../deploy";
4
+ import type { DeployConfig, DeployConfigType } from "../deploy/types";
5
+ import type { Config } from "../types/config";
6
+ import type { CommitInfo, Context, PackageManagerInfo } from "../types/context";
14
7
  import { mergeWithMergingArrays } from "../utils";
15
- import {
16
- resolveReferences,
17
- translateLegacyFromComponents,
18
- } from "./resolveReferences";
8
+ import { getEnvironment } from "./getEnvironment";
9
+ import { getEnvironmentContext } from "./getEnvironmentContext";
19
10
 
20
- const sanitizeForEnVar = (s: string) => s.replace(/-/g, "_");
21
- export const getSecretVarName = (
22
- env: string,
23
- componentName: string,
24
- key: string
25
- ) => `CL_${sanitizeForEnVar(env)}_${sanitizeForEnVar(componentName)}_${key}`; // remove dash from component name
26
-
27
- export const getSecretVarNameForContext = (context: Context, key: string) =>
28
- getSecretVarName(context.environment.shortName, context.componentName, key);
29
- export const getEnvironment = (
30
- config: Config,
31
- componentName: string,
32
- env: string,
33
- commitInfo?: CommitInfo,
34
- alreadyVisited: Record<string, Record<string, boolean>> = {} // to prevent endless loop
35
- ): Environment => {
36
- const componentConfig = config.components[componentName];
37
- if (!componentConfig) {
38
- throw new Error("unknown component " + componentName);
39
- }
40
-
41
- const defaultConfig = componentConfig;
42
-
43
- const envConfig = componentConfig.env?.[env] ?? {};
44
- if (envConfig === false) {
45
- throw new Error("env is disabled: " + env);
46
- }
47
-
48
- const mergedConfig = mergeWithMergingArrays(defaultConfig, envConfig);
49
-
50
- // env type: if its set manually, use that, otherwise use the known env types
51
-
52
- const envType = envConfig?.type ?? (isKnowEnvType(env) ? env : null);
53
-
54
- if (!envType) {
55
- throw new Error(
56
- "Missing type in environment " + env + " in component " + componentName
57
- );
58
- }
59
-
60
- const basePredefinedVariables = {
61
- ENV_SHORT: env,
62
- APP_DIR: componentConfig.dir,
63
- ENV_TYPE: envType,
64
- };
65
-
66
- const environmentName =
67
- envType === "review" && commitInfo
68
- ? `${env}/${commitInfo.refName}/${componentName}`
69
- : `${env}/${componentName}`;
70
-
71
- const environmentSlug =
72
- envType === "review" && commitInfo
73
- ? `${env}-${commitInfo.reviewSlug}-${componentName}`
74
- : `${env}-${componentName}`;
11
+ export * from "./getEnvironment";
12
+ export * from "./getEnvironmentVariables";
75
13
 
76
- let predefinedVariables: Record<string, string>;
77
- let host: string;
78
- let url: string;
79
- if (envType === "local") {
80
- const devLocalConfig: DevLocalEnvConfig = mergedConfig;
81
- const port = devLocalConfig.port ?? 3000;
82
- host = "localhost:" + port;
83
- url = "http://" + host;
84
- predefinedVariables = {
85
- ENV_SHORT: "local",
86
- ROOT_URL: url,
87
- PORT: port.toString(),
88
- };
89
- } else {
90
- const RELEASE_NAME = `${config.customerName}-${config.appName}-${environmentSlug}`;
91
-
92
- const componentSlug = slugify(componentName);
93
- const envInUrl =
94
- envType === "review" && commitInfo ? `${commitInfo.reviewSlug}.${env}` : env;
95
-
96
- const domainCanonical =
97
- (mergedConfig?.deploy &&
98
- mergedConfig?.deploy.type === "kubernetes" &&
99
- mergedConfig.deploy.cluster?.domainCanonical) || // for convenience, we allow clusters to define a canonical domain, because a cluster has a fixed ip and you will usually have a domain pointing to that cluster
100
- config.domainCanonical ||
101
- "panter.cloud";
102
-
103
- const HOST_CANONICAL = `${componentSlug}.${envInUrl}.${config.appName}.${config.customerName}.${domainCanonical}`;
104
- host = mergedConfig?.host ?? HOST_CANONICAL;
105
- const url = `https://${host}`;
106
-
107
- // FIXME: move to kube specific jobs
108
- const KUBE_APP_NAME =
109
- envType === "review" && commitInfo
110
- ? `${commitInfo.reviewSlug}-${componentName}`
111
- : componentName;
112
- const KUBE_NAMESPACE = getKubernetesNamespace(config, env);
113
-
114
- predefinedVariables = {
115
- ...basePredefinedVariables,
116
- HOST_CANONICAL,
117
- ROOT_URL: url,
118
- KUBE_NAMESPACE,
119
- KUBE_APP_NAME,
120
- RELEASE_NAME,
121
- };
122
- }
123
- const publicEnvVarsRaw = mergedConfig.vars?.public ?? {};
124
- const secretEnvVarKeys = mergedConfig.vars?.secret ?? [];
125
- const secretEnvVars = Object.fromEntries(
126
- secretEnvVarKeys.map((key) => [
127
- key,
128
- `$${getSecretVarName(env, componentName, key)}`,
129
- ])
130
- );
131
-
132
- // this is deprecated, we now support: $componentname:FOO
133
- const legacyFromComponents = mergedConfig.vars?.fromComponents ?? {};
134
- const publicEnvVarsRawWithLegasyFromComponents = merge(
135
- {},
136
- translateLegacyFromComponents(legacyFromComponents),
137
- publicEnvVarsRaw
138
- );
139
-
140
- const publicEnvVarsRawSanitized = Object.fromEntries(
141
- Object.entries(publicEnvVarsRawWithLegasyFromComponents).map(
142
- ([key, value]) => [
143
- key,
144
- isObject(value) ? JSON.stringify(value) : `${value}`,
145
- ]
146
- )
147
- );
148
-
149
- const envVarsRaw = {
150
- ...predefinedVariables,
151
- ...secretEnvVars,
152
- ...publicEnvVarsRawSanitized,
153
- };
154
-
155
- const envVars = resolveReferences(
156
- envVarsRaw,
157
- (componentName, variableName, alreadyVisited) => {
158
- const { envVars } = getEnvironment(
159
- config,
160
- componentName,
161
- env,
162
- commitInfo,
163
- alreadyVisited
164
- );
165
- return envVars[variableName];
166
- },
167
- alreadyVisited
168
- );
169
- return {
170
- envType,
171
- host,
172
- fullName: environmentName,
173
- slug: environmentSlug,
174
- shortName: env,
175
- url: predefinedVariables.ROOT_URL,
176
- envVars,
177
- secretEnvVarKeys,
178
- };
179
- };
180
-
181
- export const createContext = (
14
+ export const createContext = async (
182
15
  config: Config,
183
16
  componentName: string,
184
17
  env: string,
185
18
  commitInfo?: CommitInfo,
186
19
  packageManagerInfo?: PackageManagerInfo
187
- ): Context => {
20
+ ): Promise<Context> => {
188
21
  if (!/^[a-z0-9-]+$/.test(componentName)) {
189
22
  throw new Error(
190
23
  "componentName may only contain lower case letters, numbers and -"
191
24
  );
192
25
  }
193
- const rawConfig = config.components[componentName];
194
- if (!rawConfig) {
195
- throw new Error("unknown component " + componentName);
196
- }
197
- // envs can override the config
198
- const envConfig = rawConfig.env?.[env] ?? {};
199
- const componentConfigWithoutDefaults = mergeWithMergingArrays(
200
- rawConfig,
201
- envConfig
26
+
27
+ const envContext = getEnvironmentContext(
28
+ config,
29
+ env,
30
+ componentName,
31
+ commitInfo
202
32
  );
203
33
 
204
- // fill in defaults of build and deploy
34
+ const componentConfigWithoutDefaults = envContext.envConfigRaw;
205
35
  const defaults: {
206
36
  build: Partial<BuildConfig>;
207
37
  deploy: Partial<DeployConfig>;
208
38
  } = componentConfigWithoutDefaults.deploy
209
39
  ? {
210
40
  build:
211
- BUILD_TYPES[componentConfigWithoutDefaults.build.type].defaults(),
41
+ BUILD_TYPES[
42
+ componentConfigWithoutDefaults.build.type as BuildConfigType
43
+ ].defaults(envContext),
212
44
  deploy:
213
- DEPLOY_TYPES[componentConfigWithoutDefaults.deploy.type].defaults(),
45
+ DEPLOY_TYPES[
46
+ componentConfigWithoutDefaults.deploy.type as DeployConfigType
47
+ ].defaults(envContext),
214
48
  }
215
49
  : {
216
50
  build: {},
@@ -225,7 +59,7 @@ export const createContext = (
225
59
  fullConfig: config,
226
60
  componentConfig,
227
61
  componentName,
228
- environment: getEnvironment(config, componentName, env, commitInfo),
62
+ environment: await getEnvironment(config, componentName, env, commitInfo),
229
63
  commitInfo,
230
64
  packageManagerInfo,
231
65
  };
@@ -4,7 +4,7 @@ import {
4
4
  } from "./resolveReferences";
5
5
 
6
6
  describe("resolveReferences", () => {
7
- it("replaces occurences of ${componentName:VARIABLE_NAME}", () => {
7
+ it("replaces occurences of ${componentName:VARIABLE_NAME}", async () => {
8
8
  const variables = {
9
9
  a: "hello world",
10
10
  b: "a replaced value looks like this: '${api:FOO}', nice!",
@@ -17,9 +17,9 @@ describe("resolveReferences", () => {
17
17
  FOO: "foo from frontend",
18
18
  },
19
19
  };
20
- const result = resolveReferences(
20
+ const result = await resolveReferences(
21
21
  variables,
22
- (componentName, variableName) => {
22
+ async (componentName, variableName) => {
23
23
  return otherVariables[componentName]?.[variableName];
24
24
  }
25
25
  );
@@ -43,7 +43,7 @@ describe("resolveReferences", () => {
43
43
  });
44
44
  });
45
45
 
46
- it("replaces self references mixed with other vars", () => {
46
+ it("replaces self references mixed with other vars", async () => {
47
47
  const variables = {
48
48
  FOO: "hello from ${api:FOO}",
49
49
  BAR: "this: ${FOO}!",
@@ -53,9 +53,9 @@ describe("resolveReferences", () => {
53
53
  FOO: "foo from api",
54
54
  },
55
55
  };
56
- const result = resolveReferences(
56
+ const result = await resolveReferences(
57
57
  variables,
58
- (componentName, variableName) => {
58
+ async (componentName, variableName) => {
59
59
  return otherVariables[componentName]?.[variableName];
60
60
  }
61
61
  );
@@ -66,15 +66,15 @@ describe("resolveReferences", () => {
66
66
  });
67
67
  });
68
68
 
69
- it("keeps variables as is if not found (when null is returned)", () => {
69
+ it("keeps variables as is if not found (when null is returned)", async () => {
70
70
  const variables = {
71
71
  a: "hello world",
72
72
  b: "a not found value looks like this: '${api:FOO}'",
73
73
  };
74
74
  const otherVariables: Record<string, Record<string, string>> = {};
75
- const result = resolveReferences(
75
+ const result = await resolveReferences(
76
76
  variables,
77
- (componentName, variableName) => {
77
+ async (componentName, variableName) => {
78
78
  return otherVariables[componentName]?.[variableName];
79
79
  }
80
80
  );
@@ -85,7 +85,7 @@ describe("resolveReferences", () => {
85
85
  });
86
86
  });
87
87
 
88
- it("replaces mulitple levels deep", () => {
88
+ it("replaces mulitple levels deep", async () => {
89
89
  const variables = {
90
90
  a: "value is ${frontend:FOO}!",
91
91
  };
@@ -97,9 +97,9 @@ describe("resolveReferences", () => {
97
97
  FOO: "hi, ${api:FOO}",
98
98
  },
99
99
  };
100
- const result = resolveReferences(
100
+ const result = await resolveReferences(
101
101
  variables,
102
- (componentName, variableName) => {
102
+ async (componentName, variableName) => {
103
103
  return otherVariables[componentName]?.[variableName] ?? "";
104
104
  }
105
105
  );
@@ -109,7 +109,7 @@ describe("resolveReferences", () => {
109
109
  });
110
110
  });
111
111
 
112
- it("prevents endless loops", () => {
112
+ it("prevents endless loops", async () => {
113
113
  const variables = {
114
114
  a: "value is ${frontend:FOO}!",
115
115
  };
@@ -121,9 +121,9 @@ describe("resolveReferences", () => {
121
121
  FOO: "frontend ${api:FOO}",
122
122
  },
123
123
  };
124
- const result = resolveReferences(
124
+ const result = await resolveReferences(
125
125
  variables,
126
- (componentName, variableName) => {
126
+ async (componentName, variableName) => {
127
127
  return otherVariables[componentName]?.[variableName] ?? "";
128
128
  }
129
129
  );
@@ -1,55 +1,62 @@
1
1
  import { merge } from "lodash";
2
+ import replaceAsync from "string-replace-async";
2
3
 
3
4
  const REGEX = /\$\{(([^:]+):)?([^}]+)}/gm;
4
5
 
5
- export const resolveReferences = (
6
+ export const resolveReferences = async (
6
7
  vars: Record<string, string>,
7
8
  getOtherVariables?: (
8
9
  componentName: string,
9
10
  variableName: string,
10
11
  alreadyVisited: Record<string, Record<string, boolean>>
11
- ) => string | null,
12
+ ) => Promise<string | null>,
12
13
  alreadyVisitedBase: Record<string, Record<string, boolean>> = {}
13
14
  ) => {
14
- const replaceSingleValue = (
15
+ const replaceSingleValue = async (
15
16
  value: string,
16
17
  alreadyVisited: Record<string, Record<string, boolean>> = alreadyVisitedBase
17
- ): string => {
18
+ ): Promise<string> => {
18
19
  if (REGEX.test(value)) {
19
- return value.replace(REGEX, (match, _, componentName, variableName) => {
20
- if (alreadyVisited[componentName]?.[variableName]) {
21
- return match; // prevent endless loop
22
- }
23
- const newAlreadyVisited = merge({}, alreadyVisited, {
24
- [componentName]: {
25
- [variableName]: true,
26
- },
27
- });
28
- const result = componentName
29
- ? getOtherVariables?.(
30
- componentName,
31
- variableName,
32
- newAlreadyVisited
33
- ) ?? null
34
- : vars[variableName]; // is self reference
20
+ return await replaceAsync(
21
+ value,
22
+ REGEX,
23
+ async (match, _, componentName, variableName) => {
24
+ if (alreadyVisited[componentName]?.[variableName]) {
25
+ return match; // prevent endless loop
26
+ }
27
+ const newAlreadyVisited = merge({}, alreadyVisited, {
28
+ [componentName]: {
29
+ [variableName]: true,
30
+ },
31
+ });
32
+ const result = componentName
33
+ ? (await getOtherVariables?.(
34
+ componentName,
35
+ variableName,
36
+ newAlreadyVisited
37
+ )) ?? null
38
+ : vars[variableName]; // is self reference
35
39
 
36
- const replaced =
37
- result !== null && result !== undefined
38
- ? replaceSingleValue(result, newAlreadyVisited)
39
- : match;
40
+ const replaced =
41
+ result !== null && result !== undefined
42
+ ? await replaceSingleValue(result, newAlreadyVisited)
43
+ : match;
40
44
 
41
- return replaced;
42
- });
45
+ return replaced;
46
+ }
47
+ );
43
48
  } else {
44
49
  return value;
45
50
  }
46
51
  };
47
52
 
48
53
  return Object.fromEntries(
49
- Object.entries(vars).map(([key, value]) => {
50
- return [key, replaceSingleValue(value)];
51
- })
52
- );
54
+ await Promise.all(
55
+ Object.entries(vars).map(async ([key, value]) => {
56
+ return [key, await replaceSingleValue(value)];
57
+ })
58
+ )
59
+ ) as Record<string, string>;
53
60
  };
54
61
 
55
62
  export const translateLegacyFromComponents = (
@@ -1,4 +1,4 @@
1
- import { Retry } from "../types";
1
+ import type { Retry } from "../types";
2
2
 
3
3
  export const BASE_RETRY: Retry = {
4
4
  max: 2,
@@ -1,10 +1,12 @@
1
- import { requiresDockerBuild } from "../build/docker";
2
- import { Context } from "../types";
3
- import { CatladderJob } from "../types/jobs";
1
+ import { getDockerImageVariables, requiresDockerBuild } from "../build/docker";
2
+ import type { Context } from "../types";
3
+ import type { CatladderJob } from "../types/jobs";
4
+ import { isOfDeployType } from "./types";
4
5
  import { contextIsStoppable } from "./utils";
5
6
 
6
7
  export const DEPLOY_JOB_NAME = "🚀 Deploy";
7
8
  export const STOP_JOB_NAME = "🛑 Stop ⚠️";
9
+ export const ROLLBACK_JOB_NAME = "↩️ Rollback ⚠️";
8
10
 
9
11
  const DEPLOY_RUNNER_VARIABLES = {
10
12
  KUBERNETES_CPU_REQUEST: "0.5",
@@ -14,20 +16,16 @@ const DEPLOY_RUNNER_VARIABLES = {
14
16
  };
15
17
  type JobWithoutScript = Omit<CatladderJob, "script">;
16
18
  export const getBaseDeploymentJob = (context: Context): JobWithoutScript => {
17
- const environment = {
18
- name: context.environment.fullName,
19
- url: context.environment.url,
20
- };
21
-
22
19
  const hasDocker = requiresDockerBuild(context);
23
20
  const isStoppable = contextIsStoppable(context);
24
21
 
25
22
  const autoStop =
26
23
  context.environment.envType === "review"
27
- ? "3 days" // auto stop is not working properly when branch is merged, so we do
24
+ ? "2 week"
28
25
  : context.environment.envType === "dev"
29
- ? "3 weeks"
26
+ ? "4 weeks"
30
27
  : undefined;
28
+
31
29
  // if auto or manual is configured explicitly, use that
32
30
  const whenDeployDefined =
33
31
  context.componentConfig.deploy && context.componentConfig.deploy.when
@@ -45,6 +43,14 @@ export const getBaseDeploymentJob = (context: Context): JobWithoutScript => {
45
43
  return {
46
44
  name: DEPLOY_JOB_NAME,
47
45
  envMode: "stagePerEnv", // makes it easier to run manual tasks er env
46
+
47
+ needsOtherComponent: context.componentConfig.deploy
48
+ ? context.componentConfig.deploy.waitFor?.map((c) => ({
49
+ componentName: c,
50
+ job: DEPLOY_JOB_NAME,
51
+ artifacts: false,
52
+ })) ?? undefined
53
+ : undefined,
48
54
  // we don't want to deploy when there is a broken test
49
55
  needsStages: [
50
56
  {
@@ -56,7 +62,6 @@ export const getBaseDeploymentJob = (context: Context): JobWithoutScript => {
56
62
  artifacts: false,
57
63
  },
58
64
  ], // workaround for https://gitlab.com/gitlab-org/gitlab/-/issues/220758
59
-
60
65
  rules: [
61
66
  whenDeploy === "auto"
62
67
  ? {
@@ -71,9 +76,10 @@ export const getBaseDeploymentJob = (context: Context): JobWithoutScript => {
71
76
  variables: {
72
77
  ...DEPLOY_RUNNER_VARIABLES,
73
78
  ...context.environment.envVars,
79
+ ...(hasDocker ? getDockerImageVariables(context) : {}),
74
80
  },
75
81
  environment: {
76
- ...environment,
82
+ ...context.environment.gitlabEnvironment,
77
83
  ...(isStoppable
78
84
  ? {
79
85
  on_stop: STOP_JOB_NAME,
@@ -87,15 +93,10 @@ export const getBaseDeploymentJob = (context: Context): JobWithoutScript => {
87
93
  export const getBaseDeploymentStopJob = (
88
94
  context: Context
89
95
  ): JobWithoutScript => {
90
- const environment = {
91
- name: context.environment.fullName,
92
- url: context.environment.url,
93
- };
94
96
  return {
95
97
  name: STOP_JOB_NAME,
96
98
  envMode: "stagePerEnv", // makes it easier to run manual tasks er env
97
-
98
- needs: [DEPLOY_JOB_NAME],
99
+ needs: [], // can be executed even if the deploy job failed
99
100
  rules: [
100
101
  {
101
102
  if: "$CI_COMMIT_BRANCH =~ /^[0-9]+\\.([0-9]+|x)\\.x$/", // automatic on hotfix branches
@@ -113,8 +114,31 @@ export const getBaseDeploymentStopJob = (
113
114
  },
114
115
  stage: "stop",
115
116
  environment: {
116
- ...environment,
117
+ ...context.environment.gitlabEnvironment,
117
118
  action: "stop",
118
119
  },
119
120
  };
120
121
  };
122
+
123
+ export const getBaseRollbackJob = (context: Context): JobWithoutScript => {
124
+ return {
125
+ name: ROLLBACK_JOB_NAME,
126
+ envMode: "stagePerEnv", // makes it easier to run manual tasks er env
127
+ needs: [], // can be executed even if the deploy job failed
128
+ rules: [
129
+ {
130
+ when: "manual",
131
+ allow_failure: true,
132
+ },
133
+ ],
134
+ variables: {
135
+ ...DEPLOY_RUNNER_VARIABLES,
136
+ GIT_STRATEGY: "none",
137
+ },
138
+ stage: "rollback",
139
+ environment: {
140
+ ...context.environment.gitlabEnvironment,
141
+ action: "access",
142
+ },
143
+ };
144
+ };
@@ -0,0 +1,104 @@
1
+ import { dump } from "js-yaml";
2
+ import { merge, omit } from "lodash";
3
+ import { GCLOUD_DEPLOY_CREDENTIALS_KEY } from ".";
4
+ import { getDockerJobBaseProps, gitlabDockerLogin } from "../../build/docker";
5
+ import { getLabels } from "../../context/getLabels";
6
+ import { getRunnerImage } from "../../runner";
7
+ import type { Context } from "../../types/context";
8
+ import type { CatladderJob } from "../../types/jobs";
9
+ import { getBaseDeploymentJob, getBaseDeploymentStopJob } from "../base";
10
+ import { isOfDeployType } from "../types";
11
+ import { gcloudServiceAccountLoginCommands } from "./utils/gcloudServiceAccountLoginCommands";
12
+
13
+ export const createGoogleCloudRunDeployJobs = (
14
+ context: Context
15
+ ): CatladderJob[] => {
16
+ const deployConfig = context.componentConfig.deploy;
17
+ if (deployConfig === false) {
18
+ return [];
19
+ }
20
+ if (!isOfDeployType(deployConfig, "google-cloudrun", "google-cloudrun-job")) {
21
+ // should not happen
22
+ throw new Error("deploy config is wrong");
23
+ }
24
+ const baseDeploymentJob = getBaseDeploymentJob(context);
25
+
26
+ const fullAppName = `${context.fullConfig.customerName}-${context.fullConfig.appName}`;
27
+ const dockerUrl = `${deployConfig.region}-docker.pkg.dev/${deployConfig.projectId}/catladder-deploy/${fullAppName}`;
28
+ const gcloudImageName = `${dockerUrl}/$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG`;
29
+
30
+ const pushImageToArtifactsRegistry = [
31
+ gitlabDockerLogin,
32
+ ...gcloudServiceAccountLoginCommands(context),
33
+ `gcloud auth configure-docker ${deployConfig.region}-docker.pkg.dev`,
34
+ `docker pull $DOCKER_IMAGE:$DOCKER_IMAGE_TAG`,
35
+ `docker tag $DOCKER_IMAGE:$DOCKER_IMAGE_TAG ${gcloudImageName}`,
36
+ `docker push ${gcloudImageName}`,
37
+ ];
38
+
39
+ const allEnvVars = omit(
40
+ context.environment.envVars,
41
+ GCLOUD_DEPLOY_CREDENTIALS_KEY
42
+ );
43
+
44
+ const labelsString = Object.entries(getLabels(context))
45
+ .map(([key, value]) => `${key}=${value}`)
46
+ .join(",");
47
+
48
+ const commonArgs = `--project ${deployConfig.projectId} --region=${deployConfig.region}`;
49
+ const getDeployCommand = () => {
50
+ const name = context.environment.fullName.toLowerCase();
51
+ const command =
52
+ deployConfig.command ?? context.componentConfig.build.startCommand;
53
+ const commandArg = command
54
+ ? `--command="${command.split(" ").join(",")}"`
55
+ : "";
56
+
57
+ const commonDeployArgs = `${commandArg} --image ${gcloudImageName} ${commonArgs} --env-vars-file=____envvars.yaml --labels ${labelsString}`;
58
+ if (deployConfig.type === "google-cloudrun") {
59
+ return `gcloud run deploy ${name} ${commonDeployArgs} --allow-unauthenticated`;
60
+ }
61
+ if (deployConfig.type === "google-cloudrun-job") {
62
+ return `gcloud beta run jobs create ${name} ${commonDeployArgs}`;
63
+ }
64
+ };
65
+
66
+ const getStopCommand = () => {
67
+ const name = context.environment.fullName.toLowerCase();
68
+ const commonStopArgs = `${commonArgs} --quiet`;
69
+ if (deployConfig.type === "google-cloudrun") {
70
+ return `gcloud run services delete ${name} ${commonStopArgs}`;
71
+ }
72
+ if (deployConfig.type === "google-cloudrun-job") {
73
+ return `gcloud beta run jobs delete ${name} ${commonStopArgs}`;
74
+ }
75
+ };
76
+
77
+ const cloudRunDeploy = [
78
+ `echo "$ENV_VARS" > ____envvars.yaml`, // TODO: split secrets out
79
+ getDeployCommand(),
80
+ `docker image rm ${gcloudImageName}`,
81
+ ];
82
+
83
+ const baseStopJob = getBaseDeploymentStopJob(context);
84
+
85
+ return [
86
+ merge({}, baseDeploymentJob, getDockerJobBaseProps(context), {
87
+ artifacts: { paths: ["____envvars.yaml"] },
88
+ variables: {
89
+ ENV_VARS: dump(allEnvVars, {
90
+ lineWidth: -1,
91
+ quotingType: "'",
92
+ forceQuotes: true,
93
+ }),
94
+ },
95
+ image: getRunnerImage("gcloud"),
96
+ script: [...pushImageToArtifactsRegistry, ...cloudRunDeploy],
97
+ }),
98
+
99
+ merge({}, baseStopJob, {
100
+ image: getRunnerImage("gcloud"),
101
+ script: [...gcloudServiceAccountLoginCommands(context), getStopCommand()],
102
+ }),
103
+ ];
104
+ };