@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.
- package/bin/catladder-gitlab.js +1 -1
- package/dist/build/base/createBuildJob.d.ts +2 -2
- package/dist/build/base/getBuildInfo.d.ts +1 -1
- package/dist/build/docker.d.ts +44 -6
- package/dist/build/docker.js +45 -19
- package/dist/build/index.d.ts +6 -5
- package/dist/build/node/buildJob.d.ts +2 -2
- package/dist/build/node/cache.d.ts +2 -2
- package/dist/build/node/index.d.ts +1 -1
- package/dist/build/node/meteor.d.ts +1 -1
- package/dist/build/node/meteor.js +6 -0
- package/dist/build/node/testJob.d.ts +2 -2
- package/dist/build/node/testJob.js +3 -0
- package/dist/build/node/yarn.d.ts +1 -1
- package/dist/build/node/yarn.js +13 -1
- package/dist/build/types.d.ts +10 -0
- package/dist/bundles/catladder-gitlab/index.js +32457 -0
- package/dist/catladder-gitlab.js +1 -2
- package/dist/config/configruedEnvs.d.ts +1 -1
- package/dist/config/readConfig.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/context/getEnvConfig.d.ts +9 -0
- package/dist/context/getEnvConfig.js +26 -0
- package/dist/context/getEnvType.d.ts +4 -0
- package/dist/context/getEnvType.js +18 -0
- package/dist/context/getEnvironment.d.ts +3 -0
- package/dist/context/getEnvironment.js +187 -0
- package/dist/context/getEnvironmentContext.d.ts +4 -0
- package/dist/context/getEnvironmentContext.js +36 -0
- package/dist/context/getEnvironmentVariables.d.ts +10 -0
- package/dist/context/getEnvironmentVariables.js +322 -0
- package/dist/context/getLabels.d.ts +8 -0
- package/dist/context/getLabels.js +49 -0
- package/dist/context/index.d.ts +5 -6
- package/dist/context/index.js +176 -181
- package/dist/context/resolveReferences.d.ts +1 -3
- package/dist/context/resolveReferences.js +275 -26
- package/dist/context/resolveReferences.test.js +332 -81
- package/dist/defaults/index.d.ts +1 -1
- package/dist/deploy/base.d.ts +4 -2
- package/dist/deploy/base.js +37 -17
- package/dist/deploy/cloudRun/deployJob.d.ts +3 -0
- package/dist/deploy/cloudRun/deployJob.js +139 -0
- package/dist/deploy/cloudRun/index.d.ts +9 -0
- package/dist/deploy/cloudRun/index.js +56 -0
- package/dist/deploy/cloudRun/utils/gcloudServiceAccountLoginCommands.d.ts +2 -0
- package/dist/deploy/cloudRun/utils/gcloudServiceAccountLoginCommands.js +14 -0
- package/dist/deploy/cloudSql/index.d.ts +8 -0
- package/dist/deploy/cloudSql/index.js +30 -0
- package/dist/deploy/custom/deployJob.d.ts +2 -2
- package/dist/deploy/custom/deployJob.js +4 -1
- package/dist/deploy/custom/index.d.ts +2 -1
- package/dist/deploy/custom/index.js +15 -18
- package/dist/deploy/dockerTag/deployJob.d.ts +3 -0
- package/dist/deploy/dockerTag/deployJob.js +33 -0
- package/dist/deploy/dockerTag/index.d.ts +2 -0
- package/dist/deploy/dockerTag/index.js +19 -0
- package/dist/deploy/index.d.ts +12 -8
- package/dist/deploy/index.js +27 -14
- package/dist/deploy/kubernetes/additionalSecretKeys.d.ts +4 -0
- package/dist/deploy/kubernetes/additionalSecretKeys.js +33 -0
- package/dist/deploy/kubernetes/cloudsql.d.ts +1 -1
- package/dist/deploy/kubernetes/deployJob.d.ts +2 -2
- package/dist/deploy/kubernetes/deployJob.js +13 -66
- package/dist/deploy/kubernetes/index.d.ts +2 -1
- package/dist/deploy/kubernetes/index.js +45 -16
- package/dist/deploy/kubernetes/kubeEnv.d.ts +10 -0
- package/dist/deploy/kubernetes/kubeEnv.js +90 -0
- package/dist/deploy/kubernetes/kubeValues.d.ts +90 -0
- package/dist/deploy/kubernetes/kubeValues.js +96 -0
- package/dist/deploy/kubernetes/mongodb.d.ts +18 -5
- package/dist/deploy/kubernetes/mongodb.js +118 -8
- package/dist/deploy/kubernetes/processSecretsAsFiles.d.ts +21 -21
- package/dist/deploy/types/base.d.ts +15 -0
- package/dist/deploy/types/base.js +3 -0
- package/dist/deploy/types/custom.d.ts +27 -0
- package/dist/deploy/types/custom.js +3 -0
- package/dist/deploy/types/dockerTag.d.ts +10 -0
- package/dist/deploy/types/dockerTag.js +3 -0
- package/dist/deploy/types/googleCloudRun.d.ts +39 -0
- package/dist/deploy/types/googleCloudRun.js +3 -0
- package/dist/deploy/types/index.d.ts +24 -0
- package/dist/deploy/types/index.js +41 -0
- package/dist/deploy/{types.d.ts → types/kubernetes.d.ts} +128 -36
- package/dist/deploy/types/kubernetes.js +3 -0
- package/dist/deploy/utils.d.ts +2 -2
- package/dist/deploy/utils.js +4 -0
- package/dist/pipeline/createChildPipeline.d.ts +2 -2
- package/dist/pipeline/createChildPipeline.js +3 -3
- package/dist/pipeline/createJobs.d.ts +2 -2
- package/dist/pipeline/createJobs.js +63 -25
- package/dist/pipeline/gitlab/makeGitlabJob.d.ts +3 -2
- package/dist/pipeline/gitlab/makeGitlabJob.js +2 -1
- package/dist/pipeline/packageManager.d.ts +1 -1
- package/dist/rules/index.d.ts +1 -1
- package/dist/runner/index.d.ts +2 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/config.d.ts +7 -6
- package/dist/types/config.js +1 -1
- package/dist/types/context.d.ts +10 -2
- package/dist/types/environmentContext.d.ts +22 -0
- package/dist/types/environmentContext.js +3 -0
- package/dist/types/gitlab-types.d.ts +1 -1
- package/dist/types/jobs.d.ts +8 -3
- package/dist/types/jobs.js +1 -1
- package/dist/types/pipeline.d.ts +1 -1
- package/package.json +15 -11
- package/scripts/bundle +2 -0
- package/src/build/base/createBuildJob.ts +3 -2
- package/src/build/base/getBuildInfo.ts +1 -1
- package/src/build/docker.ts +47 -29
- package/src/build/index.ts +8 -5
- package/src/build/node/buildJob.ts +2 -2
- package/src/build/node/cache.ts +2 -2
- package/src/build/node/index.ts +1 -1
- package/src/build/node/meteor.ts +8 -2
- package/src/build/node/testJob.ts +4 -2
- package/src/build/node/yarn.ts +21 -10
- package/src/build/tests/storybook.test.ts +1 -1
- package/src/build/types.ts +13 -1
- package/src/catladder-gitlab.ts +4 -2
- package/src/config/configruedEnvs.test.ts +2 -2
- package/src/config/configruedEnvs.ts +5 -4
- package/src/config/readConfig.ts +1 -1
- package/src/context/getEnvConfig.ts +21 -0
- package/src/context/getEnvType.ts +17 -0
- package/src/context/getEnvironment.ts +40 -0
- package/src/context/getEnvironmentContext.ts +57 -0
- package/src/context/getEnvironmentVariables.ts +152 -0
- package/src/context/getLabels.ts +20 -0
- package/src/context/index.ts +25 -191
- package/src/context/resolveReferences.test.ts +15 -15
- package/src/context/resolveReferences.ts +37 -30
- package/src/defaults/index.ts +1 -1
- package/src/deploy/base.ts +43 -19
- package/src/deploy/cloudRun/deployJob.ts +104 -0
- package/src/deploy/cloudRun/index.ts +48 -0
- package/src/deploy/cloudRun/utils/gcloudServiceAccountLoginCommands.ts +14 -0
- package/src/deploy/cloudSql/index.ts +16 -0
- package/src/deploy/custom/deployJob.ts +10 -3
- package/src/deploy/custom/index.ts +9 -1
- package/src/deploy/dockerTag/deployJob.ts +31 -0
- package/src/deploy/dockerTag/index.ts +9 -0
- package/src/deploy/index.ts +26 -17
- package/src/deploy/kubernetes/additionalSecretKeys.ts +23 -0
- package/src/deploy/kubernetes/cloudsql.ts +2 -1
- package/src/deploy/kubernetes/deployJob.ts +19 -77
- package/src/deploy/kubernetes/index.ts +42 -1
- package/src/deploy/kubernetes/kubeEnv.ts +48 -0
- package/src/deploy/kubernetes/kubeValues.ts +82 -0
- package/src/deploy/kubernetes/mongodb.ts +104 -7
- package/src/deploy/kubernetes/processSecretsAsFiles.ts +1 -1
- package/src/deploy/types/base.ts +17 -0
- package/src/deploy/types/custom.ts +24 -0
- package/src/deploy/types/dockerTag.ts +12 -0
- package/src/deploy/types/googleCloudRun.ts +79 -0
- package/src/deploy/types/index.ts +28 -0
- package/src/deploy/types/kubernetes.ts +281 -0
- package/src/deploy/utils.ts +6 -2
- package/src/pipeline/createChildPipeline.test.ts +1 -1
- package/src/pipeline/createChildPipeline.ts +4 -4
- package/src/pipeline/createJobs.ts +32 -21
- package/src/pipeline/gitlab/makeGitlabJob.ts +3 -2
- package/src/pipeline/packageManager.ts +1 -1
- package/src/rules/index.ts +1 -1
- package/src/runner/index.ts +2 -1
- package/src/types/config.ts +8 -6
- package/src/types/context.ts +14 -2
- package/src/types/environmentContext.ts +26 -0
- package/src/types/gitlab-types.ts +1 -1
- package/src/types/jobs.ts +9 -2
- package/src/types/pipeline.ts +1 -1
- package/tsconfig.json +2 -0
- package/dist/deploy/types.js +0 -16
- package/src/deploy/types.ts +0 -189
package/src/context/index.ts
CHANGED
|
@@ -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
|
|
6
|
-
import { DeployConfig } from "../deploy/types";
|
|
7
|
-
import { 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
|
-
|
|
17
|
-
translateLegacyFromComponents,
|
|
18
|
-
} from "./resolveReferences";
|
|
8
|
+
import { getEnvironment } from "./getEnvironment";
|
|
9
|
+
import { getEnvironmentContext } from "./getEnvironmentContext";
|
|
19
10
|
|
|
20
|
-
|
|
21
|
-
export
|
|
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
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
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[
|
|
41
|
+
BUILD_TYPES[
|
|
42
|
+
componentConfigWithoutDefaults.build.type as BuildConfigType
|
|
43
|
+
].defaults(envContext),
|
|
212
44
|
deploy:
|
|
213
|
-
DEPLOY_TYPES[
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
const replaced =
|
|
41
|
+
result !== null && result !== undefined
|
|
42
|
+
? await replaceSingleValue(result, newAlreadyVisited)
|
|
43
|
+
: match;
|
|
40
44
|
|
|
41
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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 = (
|
package/src/defaults/index.ts
CHANGED
package/src/deploy/base.ts
CHANGED
|
@@ -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
|
-
? "
|
|
24
|
+
? "2 week"
|
|
28
25
|
: context.environment.envType === "dev"
|
|
29
|
-
? "
|
|
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
|
+
};
|