@catladder/pipeline 0.0.7 → 1.0.2
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/babel-inline.json +11 -0
- package/bin/catladder-gitlab.js +3 -0
- package/dist/build/base/constants.d.ts +7 -0
- package/dist/build/base/constants.js +11 -0
- package/dist/build/base/createBuildJob.d.ts +5 -0
- package/dist/build/base/createBuildJob.js +99 -0
- package/dist/build/docker.d.ts +5 -0
- package/dist/build/docker.js +95 -0
- package/dist/build/index.d.ts +12 -0
- package/dist/build/index.js +63 -0
- package/dist/build/node/buildJob.d.ts +3 -0
- package/dist/build/node/buildJob.js +97 -0
- package/dist/build/node/cache.d.ts +4 -0
- package/dist/build/node/cache.js +67 -0
- package/dist/build/node/constants.d.ts +6 -0
- package/dist/build/node/constants.js +10 -0
- package/dist/build/node/index.d.ts +5 -0
- package/dist/build/node/index.js +63 -0
- package/dist/build/node/meteor.d.ts +3 -0
- package/dist/build/node/meteor.js +103 -0
- package/dist/build/node/testJob.d.ts +3 -0
- package/dist/build/node/testJob.js +112 -0
- package/dist/build/node/yarn.d.ts +2 -0
- package/dist/build/node/yarn.js +12 -0
- package/dist/build/tests/storybook.test.d.ts +1 -0
- package/dist/build/tests/storybook.test.js +221 -0
- package/dist/build/types.d.ts +63 -0
- package/dist/build/types.js +16 -0
- package/dist/catladder-gitlab.d.ts +1 -0
- package/dist/catladder-gitlab.js +44 -0
- package/dist/config/configruedEnvs.d.ts +4 -0
- package/dist/config/configruedEnvs.js +86 -0
- package/dist/config/configruedEnvs.test.d.ts +1 -0
- package/dist/config/configruedEnvs.test.js +103 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.js +24 -0
- package/dist/config/readConfig.d.ts +2 -0
- package/dist/config/readConfig.js +47 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +6 -0
- package/dist/context/index.d.ts +5 -0
- package/dist/context/index.js +211 -0
- package/dist/defaults/index.d.ts +2 -0
- package/dist/defaults/index.js +8 -0
- package/dist/deploy/base.d.ts +9 -0
- package/dist/deploy/base.js +94 -0
- package/dist/deploy/index.d.ts +11 -0
- package/dist/deploy/index.js +34 -0
- package/dist/deploy/kubernetes/cloudsql.d.ts +6 -0
- package/dist/deploy/kubernetes/cloudsql.js +16 -0
- package/dist/deploy/kubernetes/deployJob.d.ts +3 -0
- package/dist/deploy/kubernetes/deployJob.js +148 -0
- package/dist/deploy/kubernetes/index.d.ts +1 -0
- package/dist/deploy/kubernetes/index.js +22 -0
- package/dist/deploy/kubernetes/mongodb.d.ts +15 -0
- package/dist/deploy/kubernetes/mongodb.js +23 -0
- package/dist/deploy/types.d.ts +92 -0
- package/dist/deploy/types.js +16 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +36 -0
- package/dist/pipeline/createChildPipeline.d.ts +9 -0
- package/dist/pipeline/createChildPipeline.js +266 -0
- package/dist/pipeline/createChildPipeline.test.d.ts +1 -0
- package/dist/pipeline/createChildPipeline.test.js +199 -0
- package/dist/pipeline/createJobs.d.ts +3 -0
- package/dist/pipeline/createJobs.js +331 -0
- package/dist/pipeline/index.d.ts +2 -0
- package/dist/pipeline/index.js +24 -0
- package/dist/pipeline/yarnInfo.d.ts +2 -0
- package/dist/pipeline/yarnInfo.js +296 -0
- package/dist/rules/index.d.ts +3 -0
- package/dist/rules/index.js +27 -0
- package/dist/runner/index.d.ts +3 -0
- package/dist/runner/index.js +12 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/config.d.ts +73 -0
- package/dist/types/config.js +80 -0
- package/dist/types/context.d.ts +36 -0
- package/dist/types/context.js +3 -0
- package/dist/types/gitlab-types.d.ts +86 -0
- package/dist/types/gitlab-types.js +5 -0
- package/dist/{scripts/magic/types → types}/index.d.ts +1 -1
- package/dist/types/index.js +26 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +18 -0
- package/package.json +30 -9
- package/src/build/base/constants.ts +7 -0
- package/src/build/base/createBuildJob.ts +46 -0
- package/{scripts/magic → src}/build/docker.ts +15 -2
- package/src/build/index.ts +42 -0
- package/src/build/node/buildJob.ts +54 -0
- package/src/build/node/cache.ts +36 -0
- package/src/build/node/constants.ts +6 -0
- package/src/build/node/index.ts +17 -0
- package/src/build/node/meteor.ts +75 -0
- package/src/build/node/testJob.ts +80 -0
- package/src/build/node/yarn.ts +8 -0
- package/src/build/tests/storybook.test.ts +46 -0
- package/src/build/types.ts +75 -0
- package/src/catladder-gitlab.ts +52 -0
- package/src/config/configruedEnvs.test.ts +97 -0
- package/src/config/configruedEnvs.ts +56 -0
- package/src/config/index.ts +2 -0
- package/src/config/readConfig.ts +36 -0
- package/src/constants.ts +2 -0
- package/src/context/index.ts +194 -0
- package/src/defaults/index.ts +6 -0
- package/src/deploy/base.ts +101 -0
- package/src/deploy/index.ts +19 -0
- package/src/deploy/kubernetes/cloudsql.ts +13 -0
- package/src/deploy/kubernetes/deployJob.ts +116 -0
- package/src/deploy/kubernetes/index.ts +1 -0
- package/src/deploy/kubernetes/mongodb.ts +20 -0
- package/src/deploy/types.ts +60 -0
- package/src/index.ts +8 -0
- package/{scripts/magic/tests/createPipeline.test.ts → src/pipeline/createChildPipeline.test.ts} +5 -4
- package/src/pipeline/createChildPipeline.ts +51 -0
- package/src/pipeline/createJobs.ts +158 -0
- package/src/pipeline/index.ts +2 -0
- package/src/pipeline/yarnInfo.ts +54 -0
- package/src/rules/index.ts +37 -0
- package/src/runner/index.ts +10 -0
- package/src/types/config.ts +88 -0
- package/src/types/context.ts +36 -0
- package/src/types/gitlab-types.ts +94 -0
- package/{scripts/magic → src}/types/index.ts +0 -0
- package/src/utils/index.ts +10 -0
- package/tsconfig.json +4 -2
- package/Dockerfile +0 -84
- package/dist/scripts/createSamplePipeline.d.ts +0 -2
- package/dist/scripts/createSamplePipeline.js +0 -14
- package/dist/scripts/magic/build/docker.d.ts +0 -2
- package/dist/scripts/magic/build/docker.js +0 -68
- package/dist/scripts/magic/build/index.d.ts +0 -9
- package/dist/scripts/magic/build/index.js +0 -12
- package/dist/scripts/magic/build/node.d.ts +0 -6
- package/dist/scripts/magic/build/node.js +0 -150
- package/dist/scripts/magic/build/types.d.ts +0 -18
- package/dist/scripts/magic/build/types.js +0 -7
- package/dist/scripts/magic/context/index.d.ts +0 -3
- package/dist/scripts/magic/context/index.js +0 -122
- package/dist/scripts/magic/createChildPipeline/index.d.ts +0 -3
- package/dist/scripts/magic/createChildPipeline/index.js +0 -68
- package/dist/scripts/magic/createPipeline.d.ts +0 -14
- package/dist/scripts/magic/createPipeline.js +0 -80
- package/dist/scripts/magic/deploy/index.d.ts +0 -9
- package/dist/scripts/magic/deploy/index.js +0 -12
- package/dist/scripts/magic/deploy/kubernetes.d.ts +0 -5
- package/dist/scripts/magic/deploy/kubernetes.js +0 -62
- package/dist/scripts/magic/deploy/types.d.ts +0 -41
- package/dist/scripts/magic/deploy/types.js +0 -7
- package/dist/scripts/magic/index.d.ts +0 -2
- package/dist/scripts/magic/index.js +0 -16
- package/dist/scripts/magic/sample-config.d.ts +0 -3
- package/dist/scripts/magic/sample-config.js +0 -77
- package/dist/scripts/magic/tests/createPipeline.test.d.ts +0 -1
- package/dist/scripts/magic/tests/createPipeline.test.js +0 -89
- package/dist/scripts/magic/types/config.d.ts +0 -49
- package/dist/scripts/magic/types/config.js +0 -17
- package/dist/scripts/magic/types/context.d.ts +0 -19
- package/dist/scripts/magic/types/context.js +0 -2
- package/dist/scripts/magic/types/gitlab-types.d.ts +0 -57
- package/dist/scripts/magic/types/gitlab-types.js +0 -2
- package/dist/scripts/magic/types/index.js +0 -15
- package/dist/scripts/magic.d.ts +0 -2
- package/dist/scripts/magic.js +0 -43
- package/dist/scripts/monorepoGenerate.d.ts +0 -2
- package/dist/scripts/monorepoGenerate.js +0 -164
- package/dist/scripts/printAllValues.d.ts +0 -2
- package/dist/scripts/printAllValues.js +0 -58
- package/dist/scripts/printBuildEnv.d.ts +0 -2
- package/dist/scripts/printBuildEnv.js +0 -77
- package/dist/scripts/utils/extractAllValues.d.ts +0 -1
- package/dist/scripts/utils/extractAllValues.js +0 -78
- package/dist/scripts/utils/extractBuildEnv.d.ts +0 -5
- package/dist/scripts/utils/extractBuildEnv.js +0 -152
- package/dist/scripts/utils/extractValuesFromEnvVars.d.ts +0 -2
- package/dist/scripts/utils/extractValuesFromEnvVars.js +0 -63
- package/dist/scripts/utils/extractValuesFromMr.d.ts +0 -2
- package/dist/scripts/utils/extractValuesFromMr.js +0 -86
- package/dist/tests/extractValuesFromMr.test.d.ts +0 -1
- package/dist/tests/extractValuesFromMr.test.js +0 -68
- package/scripts/createSamplePipeline.ts +0 -10
- package/scripts/getCommitInfo +0 -11
- package/scripts/kubernetesCreateSecret +0 -9
- package/scripts/kubernetesDelete +0 -4
- package/scripts/kubernetesDeploy +0 -55
- package/scripts/kubernetesEnsureNamespace +0 -3
- package/scripts/magic/build/index.ts +0 -18
- package/scripts/magic/build/node.ts +0 -143
- package/scripts/magic/build/types.ts +0 -21
- package/scripts/magic/context/index.ts +0 -127
- package/scripts/magic/createChildPipeline/index.ts +0 -60
- package/scripts/magic/createPipeline.ts +0 -52
- package/scripts/magic/deploy/index.ts +0 -19
- package/scripts/magic/deploy/kubernetes.ts +0 -65
- package/scripts/magic/deploy/types.ts +0 -29
- package/scripts/magic/index.ts +0 -2
- package/scripts/magic/sample-config.ts +0 -78
- package/scripts/magic/types/config.ts +0 -54
- package/scripts/magic/types/context.ts +0 -21
- package/scripts/magic/types/gitlab-types.ts +0 -69
- package/scripts/magic.ts +0 -51
- package/scripts/monorepoGenerate.ts +0 -104
- package/scripts/printAllValues.ts +0 -17
- package/scripts/printBuildEnv.ts +0 -22
- package/scripts/utils/extractAllValues.ts +0 -32
- package/scripts/utils/extractBuildEnv.ts +0 -94
- package/scripts/utils/extractValuesFromEnvVars.ts +0 -21
- package/scripts/utils/extractValuesFromMr.ts +0 -39
- package/tests/extractValuesFromMr.test.ts +0 -156
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { GitlabJobs } from "../../types/gitlab-types";
|
|
2
|
+
import { Context } from "../../types/context";
|
|
3
|
+
import { isOfDeployType } from "../types";
|
|
4
|
+
import { getRunnerImage } from "../../runner";
|
|
5
|
+
import { getBaseDeploymentJob, getBaseDeploymentStopJob } from "../base";
|
|
6
|
+
import { merge } from "lodash";
|
|
7
|
+
import { dump } from "js-yaml";
|
|
8
|
+
import { createMongodbBaseConfig } from "./mongodb";
|
|
9
|
+
import { createCloudsqlBaseConfig } from "./cloudsql";
|
|
10
|
+
|
|
11
|
+
export const createKubernetesDeployJobs = (context: Context): GitlabJobs => {
|
|
12
|
+
const deployConfig = context.componentConfig.deploy;
|
|
13
|
+
if (deployConfig === false) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
if (!isOfDeployType(deployConfig, "kubernetes")) {
|
|
17
|
+
// should not happen
|
|
18
|
+
throw new Error("deploy config is not kubernetes");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const allEnvVars = context.environment.envVars;
|
|
22
|
+
/**
|
|
23
|
+
* separate by secrets and public.
|
|
24
|
+
* we evalulate the actual values later, but want to store the secrets in kubernetes secrets
|
|
25
|
+
*/
|
|
26
|
+
const env = Object.entries(allEnvVars).reduce<{
|
|
27
|
+
secret: Record<string, string>;
|
|
28
|
+
public: Record<string, string>;
|
|
29
|
+
}>(
|
|
30
|
+
(acc, [key, value]) => {
|
|
31
|
+
if (value?.startsWith("$CL_")) {
|
|
32
|
+
acc.secret = {
|
|
33
|
+
...acc.secret,
|
|
34
|
+
[key]: value,
|
|
35
|
+
};
|
|
36
|
+
return acc;
|
|
37
|
+
}
|
|
38
|
+
acc.public = {
|
|
39
|
+
...acc.public,
|
|
40
|
+
[key]: value,
|
|
41
|
+
};
|
|
42
|
+
return acc;
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
secret: {},
|
|
46
|
+
public: {},
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const defaultKubeValues = {
|
|
51
|
+
application: {
|
|
52
|
+
hostname: context.environment.hostname,
|
|
53
|
+
command: context.componentConfig.build.startCommand,
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
env: env,
|
|
57
|
+
...(deployConfig.values?.cloudsql?.enabled
|
|
58
|
+
? createCloudsqlBaseConfig(context)
|
|
59
|
+
: {}),
|
|
60
|
+
...(deployConfig.values?.mongodb?.enabled
|
|
61
|
+
? createMongodbBaseConfig(context)
|
|
62
|
+
: {}),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const kubeValues = merge({}, defaultKubeValues, deployConfig.values);
|
|
66
|
+
|
|
67
|
+
const kubernetesEnvironment = {
|
|
68
|
+
namespace: context.environment.envVars.KUBE_NAMESPACE,
|
|
69
|
+
};
|
|
70
|
+
const shared = {
|
|
71
|
+
job: {
|
|
72
|
+
image: getRunnerImage("kubernetes"),
|
|
73
|
+
variables: {
|
|
74
|
+
...context.environment.envVars,
|
|
75
|
+
HELM_EXPERIMENTAL_OCI: "1",
|
|
76
|
+
IMAGE_PULL_SECRET: `gitlab-registry-${context.componentName}`,
|
|
77
|
+
KUBE_VALUES: dump(kubeValues, {
|
|
78
|
+
lineWidth: -1,
|
|
79
|
+
quotingType: "'",
|
|
80
|
+
forceQuotes: true,
|
|
81
|
+
}),
|
|
82
|
+
HELM_GITLAB_CHART_NAME: "the-panter-chart",
|
|
83
|
+
HELM_ARGS: deployConfig.additionalHelmArgs,
|
|
84
|
+
COMPONENT_NAME: context.componentName,
|
|
85
|
+
// TODO: unify with docker build stage
|
|
86
|
+
IMAGE_TAG: "$CI_COMMIT_SHA",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const baseDeploymentJob = getBaseDeploymentJob(context);
|
|
92
|
+
const baseStopJob = getBaseDeploymentStopJob(context);
|
|
93
|
+
|
|
94
|
+
return [
|
|
95
|
+
merge({}, baseDeploymentJob, shared, {
|
|
96
|
+
job: {
|
|
97
|
+
script: [
|
|
98
|
+
"kubernetesEnsureNamespace",
|
|
99
|
+
"kubernetesCreateSecret",
|
|
100
|
+
"kubernetesDeploy",
|
|
101
|
+
],
|
|
102
|
+
environment: {
|
|
103
|
+
kubernetes: kubernetesEnvironment,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
}),
|
|
107
|
+
merge({}, baseStopJob, shared, {
|
|
108
|
+
job: {
|
|
109
|
+
script: ["kubernetesDelete"],
|
|
110
|
+
environment: {
|
|
111
|
+
kubernetes: kubernetesEnvironment,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
}),
|
|
115
|
+
];
|
|
116
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./deployJob";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Context } from "../..";
|
|
2
|
+
|
|
3
|
+
export const createMongodbBaseConfig = (context: Context) => {
|
|
4
|
+
return {
|
|
5
|
+
mongodb: {
|
|
6
|
+
enabled: true,
|
|
7
|
+
backup: {
|
|
8
|
+
enabled: ["prod", "stage"].includes(context.environment.envType),
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
"mongodb-replicaset": {
|
|
12
|
+
replicas: 1,
|
|
13
|
+
persistentVolume: {
|
|
14
|
+
storageClass: ["prod", "stage"].includes(context.environment.envType)
|
|
15
|
+
? "fast"
|
|
16
|
+
: "standard",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
2
|
+
export type DeployConfigBase = {};
|
|
3
|
+
type AllowUnknownProps<T extends Record<string, unknown>> = T &
|
|
4
|
+
Record<string, unknown>;
|
|
5
|
+
export type DeployConfigKubernetes = {
|
|
6
|
+
type: "kubernetes";
|
|
7
|
+
cluster?: string;
|
|
8
|
+
additionalHelmArgs?: string[];
|
|
9
|
+
values?: AllowUnknownProps<{
|
|
10
|
+
cloudsql?: {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
instanceId?: string;
|
|
13
|
+
projectId?: string;
|
|
14
|
+
region?: string;
|
|
15
|
+
};
|
|
16
|
+
mongodb?: AllowUnknownProps<{
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
mailhog?: {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
};
|
|
22
|
+
jobs?: Record<string, AllowUnknownProps<{ command: string }>>;
|
|
23
|
+
cronjobs?: Record<
|
|
24
|
+
string,
|
|
25
|
+
AllowUnknownProps<{
|
|
26
|
+
schedule: string;
|
|
27
|
+
command: string;
|
|
28
|
+
concurrencyPolicy?: "Forbid" | "Allow" | "Replace";
|
|
29
|
+
}>
|
|
30
|
+
>;
|
|
31
|
+
application?: AllowUnknownProps<{
|
|
32
|
+
redirects?: AllowUnknownProps<{ host: string }>[];
|
|
33
|
+
replicas?: number;
|
|
34
|
+
resources?: {
|
|
35
|
+
limits?: {
|
|
36
|
+
cpu?: string;
|
|
37
|
+
memory?: string;
|
|
38
|
+
};
|
|
39
|
+
requests?: {
|
|
40
|
+
cpu?: string;
|
|
41
|
+
memory?: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
}>;
|
|
45
|
+
}>;
|
|
46
|
+
} & DeployConfigBase;
|
|
47
|
+
|
|
48
|
+
type CustomDeployConfig = {
|
|
49
|
+
type: "custom";
|
|
50
|
+
script: string[];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type DeployConfig = DeployConfigKubernetes;
|
|
54
|
+
|
|
55
|
+
export const isOfDeployType = <T extends Array<DeployConfig["type"]>>(
|
|
56
|
+
t: DeployConfig | false,
|
|
57
|
+
...types: T
|
|
58
|
+
): t is Extract<DeployConfig, { type: T[number] }> => {
|
|
59
|
+
return t && types.includes(t.type);
|
|
60
|
+
};
|
package/src/index.ts
ADDED
package/{scripts/magic/tests/createPipeline.test.ts → src/pipeline/createChildPipeline.test.ts}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Config } from "../types";
|
|
2
|
-
import {
|
|
2
|
+
import { createChildPipeline } from "./createChildPipeline";
|
|
3
3
|
|
|
4
|
-
describe("
|
|
4
|
+
describe("createChildPipeline", () => {
|
|
5
5
|
describe("node-app to kuberntes", () => {
|
|
6
6
|
const config: Config = {
|
|
7
7
|
appName: "test",
|
|
@@ -20,12 +20,12 @@ describe("createPipeline", () => {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
it("creates a pipeline for a single app on the main branch", async () => {
|
|
23
|
-
const { image, stages, workflow, ...jobs } = await
|
|
23
|
+
const { image, stages, workflow, ...jobs } = await createChildPipeline(
|
|
24
24
|
"mainBranch",
|
|
25
25
|
config
|
|
26
26
|
);
|
|
27
27
|
expect(image).toEqual(
|
|
28
|
-
"git.panter.ch:5001/catladder/
|
|
28
|
+
"git.panter.ch:5001/catladder/catladder/base-pipeline:"
|
|
29
29
|
);
|
|
30
30
|
|
|
31
31
|
expect(Object.keys(jobs)).toEqual([
|
|
@@ -35,6 +35,7 @@ describe("createPipeline", () => {
|
|
|
35
35
|
"dev my-app app-build",
|
|
36
36
|
"dev my-app docker-build",
|
|
37
37
|
"dev my-app deploy-to-kubernetes",
|
|
38
|
+
"dev my-app kubernetes-stop",
|
|
38
39
|
]);
|
|
39
40
|
});
|
|
40
41
|
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { getAllEnvsByTrigger, getAllEnvsInAllComponents } from "../config";
|
|
2
|
+
import { createJobs } from "./createJobs";
|
|
3
|
+
import { RULES_ALWAYS } from "../rules";
|
|
4
|
+
import { getRunnerImage } from "../runner";
|
|
5
|
+
import { Config, PipelineTrigger } from "../types/config";
|
|
6
|
+
import { GitlabJobDef } from "../types/gitlab-types";
|
|
7
|
+
const baseStages = ["setup", "test", "build", "deploy", "verify", "stop"];
|
|
8
|
+
export const createChildPipeline = async (
|
|
9
|
+
trigger: PipelineTrigger,
|
|
10
|
+
config: Config
|
|
11
|
+
) => {
|
|
12
|
+
const components = Object.keys(config.components);
|
|
13
|
+
|
|
14
|
+
// 2. write the triggering pipeline
|
|
15
|
+
|
|
16
|
+
const jobs = await components.reduce<Promise<Record<string, GitlabJobDef>>>(
|
|
17
|
+
async (acc, componentName) => {
|
|
18
|
+
const envs = getAllEnvsByTrigger(config, componentName, trigger);
|
|
19
|
+
return {
|
|
20
|
+
...(await acc),
|
|
21
|
+
...(await createJobs(envs, config, componentName, trigger)),
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
Promise.resolve({})
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// while technically not required, we group different envs in its own stage
|
|
28
|
+
// each job from `createJobs` that is defined as `envMode: "stagePerEnv"` will have `deploy dev`, etc. instead of just `deploy`
|
|
29
|
+
// this is just so that it looks nicer in gitlab and makes running mutliple manual tasks more easy to use
|
|
30
|
+
|
|
31
|
+
const allEnvs = getAllEnvsInAllComponents(config);
|
|
32
|
+
const stages = baseStages.reduce<string[]>(
|
|
33
|
+
(acc, baseStage) => [
|
|
34
|
+
...acc,
|
|
35
|
+
baseStage,
|
|
36
|
+
...allEnvs.map((e) => `${baseStage} ${e}`),
|
|
37
|
+
],
|
|
38
|
+
[]
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const childPipeline = {
|
|
42
|
+
image: getRunnerImage("jobs"), // default image
|
|
43
|
+
workflow: {
|
|
44
|
+
rules: RULES_ALWAYS,
|
|
45
|
+
},
|
|
46
|
+
stages,
|
|
47
|
+
...jobs,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return childPipeline as typeof childPipeline & Record<string, GitlabJobDef>;
|
|
51
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { isObject } from "lodash";
|
|
2
|
+
import { BUILD_TYPES } from "../build";
|
|
3
|
+
import { createContext } from "../context";
|
|
4
|
+
import { DEPLOY_TYPES } from "../deploy";
|
|
5
|
+
import { Config, PipelineTrigger } from "../types/config";
|
|
6
|
+
import { Context, CommitInfo } from "../types/context";
|
|
7
|
+
import { GitlabJob, GitlabJobDef, GitlabJobs } from "../types/gitlab-types";
|
|
8
|
+
import { notNil } from "../utils";
|
|
9
|
+
import { getYarnInfo } from "./yarnInfo";
|
|
10
|
+
|
|
11
|
+
const createRawJobs = (context: Context): GitlabJobs => {
|
|
12
|
+
if (context.componentConfig.deploy === false) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const buildJobs =
|
|
16
|
+
BUILD_TYPES[context.componentConfig.build.type].jobs(context);
|
|
17
|
+
const deployJobs =
|
|
18
|
+
DEPLOY_TYPES[context.componentConfig.deploy.type].jobs(context);
|
|
19
|
+
|
|
20
|
+
return [...buildJobs, ...deployJobs];
|
|
21
|
+
};
|
|
22
|
+
const getFullJobName = (
|
|
23
|
+
name: string,
|
|
24
|
+
componentName: string,
|
|
25
|
+
env?: string | null
|
|
26
|
+
) => {
|
|
27
|
+
if (env) {
|
|
28
|
+
return `${componentName} ${name} | ${env} `;
|
|
29
|
+
}
|
|
30
|
+
return `${componentName} ${name}`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getFullReferencedJobName = (
|
|
34
|
+
referencedJobName: string,
|
|
35
|
+
componentName: string,
|
|
36
|
+
env: string,
|
|
37
|
+
allRawJobs: GitlabJobs
|
|
38
|
+
) => {
|
|
39
|
+
const referencedJob = allRawJobs.find((j) => j.name === referencedJobName);
|
|
40
|
+
if (!referencedJob) {
|
|
41
|
+
throw new Error("unknown job referenced: " + referencedJobName);
|
|
42
|
+
}
|
|
43
|
+
const envToSet = referencedJob.envMode !== "none" ? env : null;
|
|
44
|
+
return getFullJobName(referencedJobName, componentName, envToSet);
|
|
45
|
+
};
|
|
46
|
+
// replaces references to other jobs with the full name
|
|
47
|
+
// the full name contains the componentname and the env name (if any)
|
|
48
|
+
const replaceReferences = (
|
|
49
|
+
job: GitlabJob,
|
|
50
|
+
componentName: string,
|
|
51
|
+
env: string,
|
|
52
|
+
allRawJobs: GitlabJobs
|
|
53
|
+
): GitlabJobDef => {
|
|
54
|
+
const def = job.job;
|
|
55
|
+
const stage =
|
|
56
|
+
job.envMode === "stagePerEnv" ? `${def.stage} ${env}` : def.stage;
|
|
57
|
+
return {
|
|
58
|
+
...def,
|
|
59
|
+
stage,
|
|
60
|
+
needs: def.needs?.map((n) =>
|
|
61
|
+
isObject(n)
|
|
62
|
+
? {
|
|
63
|
+
job: getFullReferencedJobName(
|
|
64
|
+
n.job,
|
|
65
|
+
componentName,
|
|
66
|
+
env,
|
|
67
|
+
allRawJobs
|
|
68
|
+
),
|
|
69
|
+
artifacts: n.artifacts,
|
|
70
|
+
}
|
|
71
|
+
: getFullReferencedJobName(n, componentName, env, allRawJobs)
|
|
72
|
+
),
|
|
73
|
+
environment: def.environment?.on_stop
|
|
74
|
+
? {
|
|
75
|
+
...def.environment,
|
|
76
|
+
on_stop: getFullReferencedJobName(
|
|
77
|
+
def.environment.on_stop,
|
|
78
|
+
componentName,
|
|
79
|
+
env,
|
|
80
|
+
allRawJobs
|
|
81
|
+
),
|
|
82
|
+
}
|
|
83
|
+
: def.environment,
|
|
84
|
+
dependencies: def.dependencies?.map((n) =>
|
|
85
|
+
getFullReferencedJobName(n, componentName, env, allRawJobs)
|
|
86
|
+
),
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// this can be removed once https://gitlab.com/gitlab-org/gitlab/-/issues/220758 is resolved
|
|
91
|
+
const addStageNeeds = (jobs: GitlabJobs): GitlabJobs => {
|
|
92
|
+
// when a job defines needsStages, we add these as needs
|
|
93
|
+
return jobs.map((job) => {
|
|
94
|
+
if (!job.needsStages || job.needsStages.length === 0) {
|
|
95
|
+
return job;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const neededJobs = jobs
|
|
99
|
+
.map((j) => {
|
|
100
|
+
const neededStage = job.needsStages?.find(
|
|
101
|
+
(s) => j.job.stage === s.stage
|
|
102
|
+
);
|
|
103
|
+
if (neededStage) {
|
|
104
|
+
return {
|
|
105
|
+
job: j.name,
|
|
106
|
+
artifacts: neededStage.artifacts ?? false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
.filter(notNil);
|
|
111
|
+
return {
|
|
112
|
+
...job,
|
|
113
|
+
job: {
|
|
114
|
+
...job.job,
|
|
115
|
+
needs: [...(job.job.needs ?? []), ...neededJobs],
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
export const createJobs = async (
|
|
121
|
+
envs: string[],
|
|
122
|
+
config: Config,
|
|
123
|
+
componentName: string,
|
|
124
|
+
trigger: PipelineTrigger
|
|
125
|
+
): Promise<Record<string, GitlabJobDef>> => {
|
|
126
|
+
const commitInfo: CommitInfo = {
|
|
127
|
+
refName: process.env.CI_COMMIT_REF_NAME ?? "unknown",
|
|
128
|
+
refSlug: process.env.CI_COMMIT_REF_SLUG ?? "unknown",
|
|
129
|
+
trigger,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const yarnInfo = await getYarnInfo(config, componentName);
|
|
133
|
+
|
|
134
|
+
return envs.reduce((acc, env) => {
|
|
135
|
+
const context = createContext(
|
|
136
|
+
config,
|
|
137
|
+
componentName,
|
|
138
|
+
env,
|
|
139
|
+
commitInfo,
|
|
140
|
+
yarnInfo
|
|
141
|
+
);
|
|
142
|
+
const jobs = addStageNeeds(createRawJobs(context));
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
...acc,
|
|
146
|
+
...jobs.reduce((acc, job) => {
|
|
147
|
+
return {
|
|
148
|
+
...acc,
|
|
149
|
+
[getFullJobName(
|
|
150
|
+
job.name,
|
|
151
|
+
componentName,
|
|
152
|
+
job.envMode !== "none" ? env : undefined
|
|
153
|
+
)]: replaceReferences(job, componentName, env, jobs),
|
|
154
|
+
};
|
|
155
|
+
}, {}),
|
|
156
|
+
};
|
|
157
|
+
}, {});
|
|
158
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { exec } from "child-process-promise";
|
|
2
|
+
import { Config, YarnInfo } from "../types";
|
|
3
|
+
import { pathEqual } from "path-equal";
|
|
4
|
+
import memoizee from "memoizee";
|
|
5
|
+
|
|
6
|
+
const execOrFail = async (cmd: string, onFail: string): Promise<string> => {
|
|
7
|
+
try {
|
|
8
|
+
return await exec(cmd).then((r) => r.stdout);
|
|
9
|
+
} catch (e) {
|
|
10
|
+
return onFail ?? null;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const getYarnVersion = memoizee(
|
|
15
|
+
async () => {
|
|
16
|
+
return await execOrFail("yarn --version", "");
|
|
17
|
+
},
|
|
18
|
+
{ promise: true }
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const getWorkspaces = memoizee(
|
|
22
|
+
async (isClassic: boolean): Promise<YarnInfo["workspaces"]> => {
|
|
23
|
+
return isClassic
|
|
24
|
+
? Object.values(
|
|
25
|
+
JSON.parse(
|
|
26
|
+
JSON.parse(await execOrFail("yarn workspaces --json info", "{}"))
|
|
27
|
+
?.data ?? "{}"
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
: JSON.parse(`[${await execOrFail("yarn workspaces list --json", "")}]`);
|
|
31
|
+
},
|
|
32
|
+
{ promise: true }
|
|
33
|
+
);
|
|
34
|
+
export const getYarnInfo = async (
|
|
35
|
+
config: Config,
|
|
36
|
+
componentName: string
|
|
37
|
+
): Promise<YarnInfo> => {
|
|
38
|
+
const version = await getYarnVersion();
|
|
39
|
+
if (!version) throw new Error("could not get yarn version");
|
|
40
|
+
const isClassic = version.startsWith("1");
|
|
41
|
+
|
|
42
|
+
const component = config.components[componentName];
|
|
43
|
+
const workspaces = await getWorkspaces(isClassic);
|
|
44
|
+
const componentIsInWorkspace = workspaces.some((w) =>
|
|
45
|
+
pathEqual(component.dir, w.location)
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
workspaces,
|
|
50
|
+
version,
|
|
51
|
+
isClassic,
|
|
52
|
+
componentIsInWorkspace,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { GitlabRule } from "../types";
|
|
2
|
+
|
|
3
|
+
const NEVER_ON_RELEASE_COMMIT: GitlabRule = {
|
|
4
|
+
if: "$CI_COMMIT_MESSAGE =~ /^chore\\(release\\).*/",
|
|
5
|
+
when: "never",
|
|
6
|
+
};
|
|
7
|
+
export const RULES_ALWAYS: GitlabRule[] = [
|
|
8
|
+
{
|
|
9
|
+
if: "$CI_COMMIT_TAG",
|
|
10
|
+
},
|
|
11
|
+
NEVER_ON_RELEASE_COMMIT,
|
|
12
|
+
{
|
|
13
|
+
if: "$CI_COMMIT_BRANCH =~ /^[0-9]+.([0-9]+|x).x$/",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
if: "$CI_MERGE_REQUEST_ID",
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export const RULES_RELEASE: GitlabRule[] = [
|
|
24
|
+
NEVER_ON_RELEASE_COMMIT,
|
|
25
|
+
{
|
|
26
|
+
if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $AUTO_RELEASE == "true"',
|
|
27
|
+
when: "on_success",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH",
|
|
31
|
+
when: "manual",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
if: "$CI_COMMIT_BRANCH =~ /^[0-9]+.([0-9]+|x).x$/", // hotfix branches
|
|
35
|
+
when: "manual",
|
|
36
|
+
},
|
|
37
|
+
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PIPELINE_IMAGE_TAG, DOCKER_REGISTRY } from "../constants";
|
|
2
|
+
|
|
3
|
+
type RunnerImageName =
|
|
4
|
+
| "jobs"
|
|
5
|
+
| "kubernetes"
|
|
6
|
+
| "base-pipeline"
|
|
7
|
+
| "docker-build"
|
|
8
|
+
| "semantic-release";
|
|
9
|
+
export const getRunnerImage = (imageName: RunnerImageName) =>
|
|
10
|
+
DOCKER_REGISTRY + "/" + imageName + ":" + PIPELINE_IMAGE_TAG;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { BuildConfig } from "../build/types";
|
|
2
|
+
import { DeployConfig } from "../deploy/types";
|
|
3
|
+
import { PartialDeep } from "type-fest";
|
|
4
|
+
export type PipelineTrigger = "mainBranch" | "mr" | "taggedRelease";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* all env types with their trigger.
|
|
8
|
+
* Each env type has a default env with the same name which is always included
|
|
9
|
+
*/
|
|
10
|
+
export const ENV_TYPES = {
|
|
11
|
+
dev: {
|
|
12
|
+
triggers: ["mainBranch", "taggedRelease"], // we also trigger dev on tagged release, so that the versions match
|
|
13
|
+
},
|
|
14
|
+
review: {
|
|
15
|
+
triggers: ["mr"],
|
|
16
|
+
},
|
|
17
|
+
stage: {
|
|
18
|
+
triggers: ["taggedRelease"],
|
|
19
|
+
},
|
|
20
|
+
prod: {
|
|
21
|
+
triggers: ["taggedRelease"],
|
|
22
|
+
},
|
|
23
|
+
local: {
|
|
24
|
+
triggers: [],
|
|
25
|
+
},
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param trigger a trigger
|
|
31
|
+
* @returns array of env types for that trigger. this is also the list of default envs
|
|
32
|
+
*/
|
|
33
|
+
export const getEnvTypesByTrigger = (trigger: PipelineTrigger) =>
|
|
34
|
+
Object.entries(ENV_TYPES)
|
|
35
|
+
.filter(([, e]) =>
|
|
36
|
+
(e.triggers as readonly PipelineTrigger[]).includes(trigger)
|
|
37
|
+
)
|
|
38
|
+
.map(([e]) => e as EnvType);
|
|
39
|
+
|
|
40
|
+
export const DEFAULT_ENVS = Object.keys(ENV_TYPES);
|
|
41
|
+
export const DEFAULT_ENV_TYPES = DEFAULT_ENVS as EnvType[];
|
|
42
|
+
export type EnvType = keyof typeof ENV_TYPES;
|
|
43
|
+
|
|
44
|
+
export const isKnowEnvType = (env: string): env is EnvType => {
|
|
45
|
+
return env in ENV_TYPES;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type EnvVars = {
|
|
49
|
+
public?: Record<string, any>;
|
|
50
|
+
secret?: string[];
|
|
51
|
+
fromComponents?: {
|
|
52
|
+
[otherApp: string]: Record<string, string>;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type DefaultEnvConfig = {
|
|
57
|
+
deploy: DeployConfig | false;
|
|
58
|
+
build: BuildConfig;
|
|
59
|
+
vars?: EnvVars;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type DevLocalEnvConfig = {
|
|
63
|
+
vars?: EnvVars;
|
|
64
|
+
port?: number;
|
|
65
|
+
};
|
|
66
|
+
export type EnvConfig<E extends EnvType = EnvType> = {
|
|
67
|
+
type?: E;
|
|
68
|
+
hostname?: string;
|
|
69
|
+
} & PartialDeep<DefaultEnvConfig>;
|
|
70
|
+
|
|
71
|
+
export type Env = {
|
|
72
|
+
local?: DevLocalEnvConfig;
|
|
73
|
+
dev?: EnvConfig<"dev"> | false;
|
|
74
|
+
stage?: EnvConfig<"stage"> | false;
|
|
75
|
+
review?: EnvConfig<"review"> | false;
|
|
76
|
+
prod?: EnvConfig<"prod"> | false;
|
|
77
|
+
} & Record<string, EnvConfig | false>;
|
|
78
|
+
|
|
79
|
+
export type ComponentConfig = {
|
|
80
|
+
env?: Env;
|
|
81
|
+
dir: string;
|
|
82
|
+
} & DefaultEnvConfig;
|
|
83
|
+
|
|
84
|
+
export type Config = {
|
|
85
|
+
customerName: string;
|
|
86
|
+
appName: string;
|
|
87
|
+
components: Record<string, ComponentConfig>;
|
|
88
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PipelineTrigger, ComponentConfig, Config, EnvType } from "./config";
|
|
2
|
+
|
|
3
|
+
export type Environment = {
|
|
4
|
+
hostname: string;
|
|
5
|
+
fullName: string;
|
|
6
|
+
shortName: string;
|
|
7
|
+
slug: string;
|
|
8
|
+
url: string;
|
|
9
|
+
/**
|
|
10
|
+
* env vars contain all build-time env vars. secrets have to be resolved (they are stored in gitlab)
|
|
11
|
+
*/
|
|
12
|
+
envVars: Record<string, string>;
|
|
13
|
+
envType: EnvType;
|
|
14
|
+
secretEnvVarKeys: string[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type CommitInfo = {
|
|
18
|
+
refName: string;
|
|
19
|
+
refSlug: string;
|
|
20
|
+
trigger: PipelineTrigger;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type YarnInfo = {
|
|
24
|
+
version: string;
|
|
25
|
+
workspaces: { location: string }[];
|
|
26
|
+
isClassic: boolean;
|
|
27
|
+
componentIsInWorkspace: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type Context = {
|
|
30
|
+
componentName: string;
|
|
31
|
+
componentConfig: ComponentConfig;
|
|
32
|
+
fullConfig: Config;
|
|
33
|
+
environment: Environment;
|
|
34
|
+
commitInfo?: CommitInfo;
|
|
35
|
+
yarnInfo?: YarnInfo;
|
|
36
|
+
};
|