@catladder/pipeline 0.0.1
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/Dockerfile +84 -0
- package/dist/scripts/magic/createChildPipeline/index.d.ts +5 -0
- package/dist/scripts/magic/createChildPipeline/index.js +141 -0
- package/dist/scripts/magic/createPipeline.d.ts +14 -0
- package/dist/scripts/magic/createPipeline.js +80 -0
- package/dist/scripts/magic/index.d.ts +2 -0
- package/dist/scripts/magic/index.js +16 -0
- package/dist/scripts/magic/sample-config.d.ts +3 -0
- package/dist/scripts/magic/sample-config.js +77 -0
- package/dist/scripts/magic/types/build/docker.d.ts +2 -0
- package/dist/scripts/magic/types/build/docker.js +63 -0
- package/dist/scripts/magic/types/build/index.d.ts +6 -0
- package/dist/scripts/magic/types/build/index.js +8 -0
- package/dist/scripts/magic/types/build/node.d.ts +3 -0
- package/dist/scripts/magic/types/build/node.js +118 -0
- package/dist/scripts/magic/types/build/types.d.ts +7 -0
- package/dist/scripts/magic/types/build/types.js +2 -0
- package/dist/scripts/magic/types/config.d.ts +49 -0
- package/dist/scripts/magic/types/config.js +17 -0
- package/dist/scripts/magic/types/context.d.ts +12 -0
- package/dist/scripts/magic/types/context.js +2 -0
- package/dist/scripts/magic/types/deploy/index.d.ts +6 -0
- package/dist/scripts/magic/types/deploy/index.js +8 -0
- package/dist/scripts/magic/types/deploy/kubernetes.d.ts +3 -0
- package/dist/scripts/magic/types/deploy/kubernetes.js +46 -0
- package/dist/scripts/magic/types/deploy/types.d.ts +41 -0
- package/dist/scripts/magic/types/deploy/types.js +7 -0
- package/dist/scripts/magic/types/gitlab-types.d.ts +63 -0
- package/dist/scripts/magic/types/gitlab-types.js +2 -0
- package/dist/scripts/magic/types/index.d.ts +3 -0
- package/dist/scripts/magic/types/index.js +15 -0
- package/dist/scripts/magic.d.ts +2 -0
- package/dist/scripts/magic.js +43 -0
- package/dist/scripts/monorepoGenerate.d.ts +2 -0
- package/dist/scripts/monorepoGenerate.js +164 -0
- package/dist/scripts/printAllValues.d.ts +2 -0
- package/dist/scripts/printAllValues.js +58 -0
- package/dist/scripts/printBuildEnv.d.ts +2 -0
- package/dist/scripts/printBuildEnv.js +77 -0
- package/dist/scripts/utils/extractAllValues.d.ts +1 -0
- package/dist/scripts/utils/extractAllValues.js +78 -0
- package/dist/scripts/utils/extractBuildEnv.d.ts +5 -0
- package/dist/scripts/utils/extractBuildEnv.js +152 -0
- package/dist/scripts/utils/extractValuesFromEnvVars.d.ts +2 -0
- package/dist/scripts/utils/extractValuesFromEnvVars.js +63 -0
- package/dist/scripts/utils/extractValuesFromMr.d.ts +2 -0
- package/dist/scripts/utils/extractValuesFromMr.js +86 -0
- package/dist/tests/extractValuesFromMr.test.d.ts +1 -0
- package/dist/tests/extractValuesFromMr.test.js +68 -0
- package/package.json +21 -0
- package/scripts/getCommitInfo +11 -0
- package/scripts/kubernetesCreateSecret +8 -0
- package/scripts/kubernetesDelete +4 -0
- package/scripts/kubernetesDeploy +57 -0
- package/scripts/kubernetesEnsureNamespace +3 -0
- package/scripts/magic/createChildPipeline/index.ts +163 -0
- package/scripts/magic/createPipeline.ts +48 -0
- package/scripts/magic/index.ts +2 -0
- package/scripts/magic/sample-config.ts +78 -0
- package/scripts/magic/types/build/docker.ts +36 -0
- package/scripts/magic/types/build/index.ts +11 -0
- package/scripts/magic/types/build/node.ts +107 -0
- package/scripts/magic/types/build/types.ts +9 -0
- package/scripts/magic/types/config.ts +54 -0
- package/scripts/magic/types/context.ts +13 -0
- package/scripts/magic/types/deploy/index.ts +11 -0
- package/scripts/magic/types/deploy/kubernetes.ts +38 -0
- package/scripts/magic/types/deploy/types.ts +29 -0
- package/scripts/magic/types/gitlab-types.ts +77 -0
- package/scripts/magic/types/index.ts +3 -0
- package/scripts/magic.ts +51 -0
- package/scripts/monorepoGenerate.ts +104 -0
- package/scripts/printAllValues.ts +17 -0
- package/scripts/printBuildEnv.ts +22 -0
- package/scripts/utils/extractAllValues.ts +32 -0
- package/scripts/utils/extractBuildEnv.ts +92 -0
- package/scripts/utils/extractValuesFromEnvVars.ts +21 -0
- package/scripts/utils/extractValuesFromMr.ts +39 -0
- package/tests/extractValuesFromMr.test.ts +156 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { merge } from "lodash";
|
|
2
|
+
import slugify from "slugify";
|
|
3
|
+
import { GitlabJobDef, GitlabJobs } from "../types/gitlab-types";
|
|
4
|
+
import { buildJobCreators } from "../types/build";
|
|
5
|
+
import { Config } from "../types/config";
|
|
6
|
+
import { Context, Environment } from "../types/context";
|
|
7
|
+
import { deployJobCreators } from "../types/deploy";
|
|
8
|
+
|
|
9
|
+
const createRawJobs = (context: Context): GitlabJobs => {
|
|
10
|
+
const buildJobs =
|
|
11
|
+
buildJobCreators[context.componentConfig.build.type](context);
|
|
12
|
+
const deployJobs =
|
|
13
|
+
deployJobCreators[context.componentConfig.deploy.type](context);
|
|
14
|
+
|
|
15
|
+
return [...buildJobs, ...deployJobs];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getEnvironment = (
|
|
19
|
+
config: Config,
|
|
20
|
+
componentName: string,
|
|
21
|
+
env: string
|
|
22
|
+
): Environment => {
|
|
23
|
+
const componentConfig = config.components[componentName];
|
|
24
|
+
if (!componentConfig) {
|
|
25
|
+
throw new Error("unknown component " + componentName);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const defaultConfig = componentConfig;
|
|
29
|
+
const envConfig = componentConfig.env?.[env] ?? {};
|
|
30
|
+
|
|
31
|
+
const mergedConfig = merge({}, defaultConfig, envConfig);
|
|
32
|
+
|
|
33
|
+
const publicEnvVars = mergedConfig.vars?.public ?? {};
|
|
34
|
+
const secretEnvVarsRaw = mergedConfig.vars?.secret ?? {}; // TODO, map to gl secrets?
|
|
35
|
+
const secretEnvVars = {}; // TODO
|
|
36
|
+
const referencedRaw = mergedConfig.vars?.fromComponents ?? {};
|
|
37
|
+
|
|
38
|
+
const referenced = Object.entries(referencedRaw).reduce(
|
|
39
|
+
(acc, [otherApp, mapping]) => {
|
|
40
|
+
// TODO: prevent infinit looop
|
|
41
|
+
const { variables } = getEnvironment(config, otherApp, env);
|
|
42
|
+
|
|
43
|
+
return Object.fromEntries(
|
|
44
|
+
Object.entries(mapping).map(([ourKey, otherKey]) => [
|
|
45
|
+
ourKey,
|
|
46
|
+
variables[otherKey],
|
|
47
|
+
])
|
|
48
|
+
);
|
|
49
|
+
},
|
|
50
|
+
{}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const envType = componentConfig.env?.[env]?.type ?? env;
|
|
54
|
+
const environmentName =
|
|
55
|
+
envType === "review"
|
|
56
|
+
? `${env}-${componentName}/$CI_COMMIT_REF_NAME`
|
|
57
|
+
: `${env}-${componentName}`;
|
|
58
|
+
|
|
59
|
+
const KUBE_APP_NAME =
|
|
60
|
+
envType === "review"
|
|
61
|
+
? `${componentName}-$CI_COMMIT_REF_SLUG`
|
|
62
|
+
: componentName;
|
|
63
|
+
|
|
64
|
+
const KUBE_NAMESPACE = `${config.customerName}-${config.appName}-${env}`;
|
|
65
|
+
|
|
66
|
+
const APP_SLUG = slugify(KUBE_APP_NAME);
|
|
67
|
+
|
|
68
|
+
const CANONICAL_HOST = `${config.appName}-${APP_SLUG}.${config.customerName}.panter.cloud`;
|
|
69
|
+
|
|
70
|
+
const hostname = mergedConfig.hostname ?? CANONICAL_HOST;
|
|
71
|
+
const url = `https://${hostname}`;
|
|
72
|
+
|
|
73
|
+
const predefinedVariables = {
|
|
74
|
+
CANONICAL_URL: url,
|
|
75
|
+
ROOT_URL: url,
|
|
76
|
+
KUBE_NAMESPACE,
|
|
77
|
+
};
|
|
78
|
+
const variables = {
|
|
79
|
+
...predefinedVariables,
|
|
80
|
+
...publicEnvVars,
|
|
81
|
+
...secretEnvVars,
|
|
82
|
+
...referenced,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
fullName: environmentName,
|
|
87
|
+
shortName: env,
|
|
88
|
+
url: url,
|
|
89
|
+
variables,
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const createContext = (
|
|
94
|
+
componentName: string,
|
|
95
|
+
config: Config
|
|
96
|
+
): Context => {
|
|
97
|
+
const componentConfig = config.components[componentName];
|
|
98
|
+
if (!componentConfig) {
|
|
99
|
+
throw new Error("unknown component " + componentName);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
fullConfig: config,
|
|
103
|
+
componentConfig,
|
|
104
|
+
componentName,
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const getFullJobName = (name: string, componentName: string, env?: string) => {
|
|
109
|
+
if (env) {
|
|
110
|
+
return `${env} ${componentName} ${name}`;
|
|
111
|
+
}
|
|
112
|
+
return `${componentName} ${name}`;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const replaceReferences = (
|
|
116
|
+
def: GitlabJobDef,
|
|
117
|
+
componentName: string,
|
|
118
|
+
env?: string
|
|
119
|
+
): GitlabJobDef => {
|
|
120
|
+
return {
|
|
121
|
+
...def,
|
|
122
|
+
needs: def.needs?.map((n) => getFullJobName(n, componentName, env)),
|
|
123
|
+
dependencies: def.dependencies?.map((n) =>
|
|
124
|
+
getFullJobName(n, componentName, env)
|
|
125
|
+
),
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const createJobs = (
|
|
130
|
+
envs: string[],
|
|
131
|
+
config: Config,
|
|
132
|
+
componentName: string
|
|
133
|
+
): Record<string, GitlabJobDef> => {
|
|
134
|
+
const context = createContext(componentName, config);
|
|
135
|
+
const jobs = createRawJobs(context);
|
|
136
|
+
const environments = envs.map((e) =>
|
|
137
|
+
getEnvironment(config, componentName, e)
|
|
138
|
+
);
|
|
139
|
+
return jobs.reduce((acc, job) => {
|
|
140
|
+
if (!job.env) {
|
|
141
|
+
const def = job.job();
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
...acc,
|
|
145
|
+
[getFullJobName(job.name, componentName)]: replaceReferences(
|
|
146
|
+
def,
|
|
147
|
+
componentName
|
|
148
|
+
),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
...acc,
|
|
153
|
+
...environments.reduce((envacc, environment) => {
|
|
154
|
+
const def = job.job(environment);
|
|
155
|
+
return {
|
|
156
|
+
...acc,
|
|
157
|
+
[getFullJobName(job.name, componentName, environment.shortName)]:
|
|
158
|
+
replaceReferences(def, componentName, environment.shortName),
|
|
159
|
+
};
|
|
160
|
+
}, {}),
|
|
161
|
+
};
|
|
162
|
+
}, {});
|
|
163
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createJobs } from "./createChildPipeline";
|
|
2
|
+
import { Config, ENV_TYPES, PipelineTrigger } from "./types/config";
|
|
3
|
+
|
|
4
|
+
export const createPipeline = async (
|
|
5
|
+
trigger: PipelineTrigger,
|
|
6
|
+
config: Config
|
|
7
|
+
) => {
|
|
8
|
+
const envs = Object.keys(ENV_TYPES).filter(
|
|
9
|
+
(e) => ENV_TYPES[e as keyof typeof ENV_TYPES].trigger === trigger
|
|
10
|
+
);
|
|
11
|
+
const components = Object.keys(config.components);
|
|
12
|
+
|
|
13
|
+
// 2. write the triggering pipeline
|
|
14
|
+
|
|
15
|
+
const jobs = components.reduce((acc, componentName) => {
|
|
16
|
+
return {
|
|
17
|
+
...acc,
|
|
18
|
+
...createJobs(envs, config, componentName),
|
|
19
|
+
};
|
|
20
|
+
}, {});
|
|
21
|
+
|
|
22
|
+
const rules = [
|
|
23
|
+
// same as rules "always", but with `changes` to only trigger changed branches
|
|
24
|
+
{ if: "$CI_COMMIT_TAG" },
|
|
25
|
+
{
|
|
26
|
+
if: "$CI_COMMIT_MESSAGE =~ /^chore(release).*/",
|
|
27
|
+
when: "never",
|
|
28
|
+
},
|
|
29
|
+
{ if: "$CI_COMMIT_BRANCH =~ /^[0-9]+.([0-9]+|x).x$/" },
|
|
30
|
+
{
|
|
31
|
+
if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH",
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
{ if: "$CI_MERGE_REQUEST_ID" },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const mainPipeline = {
|
|
38
|
+
image:
|
|
39
|
+
"git.panter.ch:5001/catladder/gitlab-ci/pipeline:$PIPELINE_IMAGE_TAG",
|
|
40
|
+
workflow: {
|
|
41
|
+
rules,
|
|
42
|
+
},
|
|
43
|
+
stages: ["setup", "test", "build", "deploy", "verify", "actions"],
|
|
44
|
+
...jobs,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return mainPipeline;
|
|
48
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Config } from "./types/config";
|
|
2
|
+
|
|
3
|
+
const sampleConfig: Config = {
|
|
4
|
+
customerName: "pan",
|
|
5
|
+
appName: "fonsi",
|
|
6
|
+
components: {
|
|
7
|
+
web: {
|
|
8
|
+
dir: "web",
|
|
9
|
+
build: {
|
|
10
|
+
type: "node-static",
|
|
11
|
+
},
|
|
12
|
+
deploy: {
|
|
13
|
+
type: "kubernetes",
|
|
14
|
+
values: {
|
|
15
|
+
application: {
|
|
16
|
+
resources: {
|
|
17
|
+
limits: {
|
|
18
|
+
cpu: "100m",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
vars: {
|
|
25
|
+
public: {
|
|
26
|
+
GOOGLE_TAG_MANAGER_ID: "1234",
|
|
27
|
+
},
|
|
28
|
+
fromComponents: {
|
|
29
|
+
api: {
|
|
30
|
+
API_URL: "ROOT_URL",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
env: {
|
|
35
|
+
prod: {
|
|
36
|
+
hostname: "fudilo.ch",
|
|
37
|
+
vars: {},
|
|
38
|
+
deploy: {
|
|
39
|
+
type: "kubernetes",
|
|
40
|
+
values: {
|
|
41
|
+
application: {
|
|
42
|
+
resources: {
|
|
43
|
+
limits: {
|
|
44
|
+
cpu: "1000m",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
myCustomProd: {
|
|
52
|
+
type: "prod",
|
|
53
|
+
hostname: "fudilo2.ch",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
api: {
|
|
58
|
+
env: {
|
|
59
|
+
prod: {
|
|
60
|
+
hostname: "api.fudilo.ch",
|
|
61
|
+
},
|
|
62
|
+
myCustomProd: {
|
|
63
|
+
type: "prod",
|
|
64
|
+
hostname: "api.fudilo2.ch",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
build: {
|
|
68
|
+
type: "node",
|
|
69
|
+
},
|
|
70
|
+
deploy: {
|
|
71
|
+
type: "kubernetes",
|
|
72
|
+
},
|
|
73
|
+
dir: "api",
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default sampleConfig;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { GitlabJobDef } from "../gitlab-types";
|
|
2
|
+
|
|
3
|
+
export const createDockerBuildJob = ({
|
|
4
|
+
script,
|
|
5
|
+
}: Pick<GitlabJobDef, "script">): GitlabJobDef => {
|
|
6
|
+
const base: Omit<GitlabJobDef, "script"> = {
|
|
7
|
+
stage: "build",
|
|
8
|
+
image:
|
|
9
|
+
"git.panter.ch:5001/catladder/gitlab-ci/docker-build:$PIPELINE_IMAGE_TAG",
|
|
10
|
+
interruptible: true,
|
|
11
|
+
services: [
|
|
12
|
+
{
|
|
13
|
+
name: "docker:20-dind", // see see https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27300#note_466755332
|
|
14
|
+
command: ["--tls=false"],
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
variables: {
|
|
18
|
+
DOCKER_DRIVER: "overlay2",
|
|
19
|
+
IMAGE_NAME: "$CI_REGISTRY_IMAGE/$COMPONENT_NAME",
|
|
20
|
+
CACHE_IMAGE: "$CI_REGISTRY_IMAGE/$COMPONENT_NAME/cache:cache",
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
...base,
|
|
26
|
+
script: [
|
|
27
|
+
...script,
|
|
28
|
+
"docker login --username gitlab-ci-token --password $CI_JOB_TOKEN $CI_REGISTRY",
|
|
29
|
+
"docker pull $CACHE_IMAGE || true",
|
|
30
|
+
"docker build --network host --cache-from $CACHE_IMAGE --tag $IMAGE_NAME:$IMAGE_TAG $DOCKER_DIR",
|
|
31
|
+
"docker push $IMAGE_NAME:$IMAGE_TAG",
|
|
32
|
+
"docker tag $IMAGE_NAME:$IMAGE_TAG $CACHE_IMAGE",
|
|
33
|
+
"docker push $CACHE_IMAGE",
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GitlabJobs } from "../gitlab-types";
|
|
2
|
+
import { Context } from "../context";
|
|
3
|
+
import { createNodeJobs } from "./node";
|
|
4
|
+
import { BuildConfig } from "./types";
|
|
5
|
+
|
|
6
|
+
export const buildJobCreators: {
|
|
7
|
+
[type in BuildConfig["type"]]: (context: Context) => GitlabJobs;
|
|
8
|
+
} = {
|
|
9
|
+
node: createNodeJobs,
|
|
10
|
+
"node-static": createNodeJobs, // TODO
|
|
11
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GitlabJobDef,
|
|
3
|
+
GitlabJobCache,
|
|
4
|
+
GitlabJobs,
|
|
5
|
+
Retry,
|
|
6
|
+
} from "../gitlab-types";
|
|
7
|
+
import { Context } from "../context";
|
|
8
|
+
import { createDockerBuildJob } from "./docker";
|
|
9
|
+
|
|
10
|
+
const yarnInstall = [
|
|
11
|
+
"cd $APP_PATH",
|
|
12
|
+
"if [ -f ./.nvmrc ]; then source /root/.nvm/nvm.sh && nvm install <<< .nvmrc; fi",
|
|
13
|
+
"yarn install --frozen-lockfile",
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const baseRetry: Retry = {
|
|
17
|
+
max: 2,
|
|
18
|
+
when: ["runner_system_failure", "stuck_or_timeout_failure"],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const createNodeTestJobs = (context: Context): GitlabJobs => {
|
|
22
|
+
const cache: GitlabJobCache[] = [
|
|
23
|
+
{
|
|
24
|
+
key: "node-modules",
|
|
25
|
+
policy: "pull-push",
|
|
26
|
+
paths: ["node_modules", "**/node_modules/"],
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
const base: Omit<GitlabJobDef, "script"> = {
|
|
30
|
+
variables: {
|
|
31
|
+
APP_PATH: context.componentConfig.dir,
|
|
32
|
+
},
|
|
33
|
+
cache,
|
|
34
|
+
stage: "test",
|
|
35
|
+
interruptible: true,
|
|
36
|
+
needs: [],
|
|
37
|
+
retry: baseRetry,
|
|
38
|
+
};
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
name: "test",
|
|
42
|
+
env: false,
|
|
43
|
+
job: () => ({
|
|
44
|
+
...base,
|
|
45
|
+
script: [...yarnInstall, "yarn test"],
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "lint",
|
|
50
|
+
env: false,
|
|
51
|
+
job: () => ({
|
|
52
|
+
...base,
|
|
53
|
+
script: [...yarnInstall, "yarn lint"],
|
|
54
|
+
}),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "audit",
|
|
58
|
+
env: false,
|
|
59
|
+
job: () => ({
|
|
60
|
+
...base,
|
|
61
|
+
script: [...yarnInstall, "yarn audit"],
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const createNodeBuildJobs = (context: Context): GitlabJobs => {
|
|
68
|
+
const buildInfo = [
|
|
69
|
+
". getCommitInfo", // TODO: inline
|
|
70
|
+
`echo '{"id":"'$BUILD_ID'","commit":"'$BUILD_COMMIT'","tag":"'$BUILD_TAG'","time":"'$BUILD_TIME'"}' > $APP_DIR/__build_info.json`,
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
name: "app-build",
|
|
76
|
+
env: true,
|
|
77
|
+
job: (environment) => ({
|
|
78
|
+
variables: environment.variables,
|
|
79
|
+
retry: baseRetry,
|
|
80
|
+
interruptible: true,
|
|
81
|
+
stage: "build",
|
|
82
|
+
script: [...buildInfo, ...yarnInstall, "yarn build"],
|
|
83
|
+
artifacts: {
|
|
84
|
+
paths: [
|
|
85
|
+
context.componentConfig.dir + "/__build_info.json",
|
|
86
|
+
context.componentConfig.dir + "/dist",
|
|
87
|
+
context.componentConfig.dir + "/.next",
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "docker-build",
|
|
94
|
+
env: true,
|
|
95
|
+
job: () => ({
|
|
96
|
+
...createDockerBuildJob({
|
|
97
|
+
script: ["ensureNodeDockerfile"], // TOOD: inline
|
|
98
|
+
}),
|
|
99
|
+
needs: ["app-build"],
|
|
100
|
+
}),
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const createNodeJobs = (context: Context): GitlabJobs => {
|
|
106
|
+
return [...createNodeBuildJobs(context), ...createNodeTestJobs(context)];
|
|
107
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BuildConfig } from "./build/types";
|
|
2
|
+
import { DeployConfig } from "./deploy/types";
|
|
3
|
+
|
|
4
|
+
export type PipelineTrigger = "mainBranch" | "mr" | "taggedRelease";
|
|
5
|
+
|
|
6
|
+
export const ENV_TYPES = {
|
|
7
|
+
dev: {
|
|
8
|
+
trigger: "mainBranch",
|
|
9
|
+
},
|
|
10
|
+
review: {
|
|
11
|
+
trigger: "mr",
|
|
12
|
+
},
|
|
13
|
+
prod: {
|
|
14
|
+
trigger: "taggedRelease",
|
|
15
|
+
},
|
|
16
|
+
stage: {
|
|
17
|
+
trigger: "taggedRelease",
|
|
18
|
+
},
|
|
19
|
+
} as const;
|
|
20
|
+
type EnvType = keyof typeof ENV_TYPES;
|
|
21
|
+
|
|
22
|
+
type DefaultEnvConfig = {
|
|
23
|
+
deploy: DeployConfig;
|
|
24
|
+
build: BuildConfig;
|
|
25
|
+
vars?: {
|
|
26
|
+
public?: Record<string, any>;
|
|
27
|
+
secret?: Record<string, string>;
|
|
28
|
+
fromComponents?: {
|
|
29
|
+
[otherApp: string]: Record<string, string>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
type EnvConfig<E extends EnvType = EnvType> = {
|
|
34
|
+
type?: E;
|
|
35
|
+
hostname?: string;
|
|
36
|
+
} & Partial<DefaultEnvConfig>;
|
|
37
|
+
|
|
38
|
+
export type Env = {
|
|
39
|
+
dev?: EnvConfig<"dev">;
|
|
40
|
+
stage?: EnvConfig<"stage">;
|
|
41
|
+
review?: EnvConfig<"review">;
|
|
42
|
+
prod?: EnvConfig<"prod">;
|
|
43
|
+
} & Record<string, EnvConfig>;
|
|
44
|
+
|
|
45
|
+
export type ComponentConfig = {
|
|
46
|
+
env?: Env;
|
|
47
|
+
dir: string;
|
|
48
|
+
} & DefaultEnvConfig;
|
|
49
|
+
|
|
50
|
+
export type Config = {
|
|
51
|
+
customerName: string;
|
|
52
|
+
appName: string;
|
|
53
|
+
components: Record<string, ComponentConfig>;
|
|
54
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ComponentConfig, Config, PipelineTrigger } from "./config";
|
|
2
|
+
|
|
3
|
+
export type Environment = {
|
|
4
|
+
fullName: string;
|
|
5
|
+
shortName: string;
|
|
6
|
+
url: string;
|
|
7
|
+
variables: Record<string, string>;
|
|
8
|
+
};
|
|
9
|
+
export type Context = {
|
|
10
|
+
componentConfig: ComponentConfig;
|
|
11
|
+
componentName: string;
|
|
12
|
+
fullConfig: Config;
|
|
13
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GitlabJobs } from "../gitlab-types";
|
|
2
|
+
import { ComponentConfig } from "../config";
|
|
3
|
+
import { Context } from "../context";
|
|
4
|
+
import { createKubernetesDeployJobs } from "./kubernetes";
|
|
5
|
+
|
|
6
|
+
export const deployJobCreators: {
|
|
7
|
+
[type in ComponentConfig["deploy"]["type"]]: (context: Context) => GitlabJobs;
|
|
8
|
+
} = {
|
|
9
|
+
kubernetes: createKubernetesDeployJobs,
|
|
10
|
+
serverless: () => [],
|
|
11
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { GitlabJobs } from "../gitlab-types";
|
|
2
|
+
import { Context } from "../context";
|
|
3
|
+
import { isOfType } from "./types";
|
|
4
|
+
|
|
5
|
+
export const createKubernetesDeployJobs = (context: Context): GitlabJobs => {
|
|
6
|
+
const deployConfig = context.componentConfig.deploy;
|
|
7
|
+
if (!isOfType(deployConfig, "kubernetes")) {
|
|
8
|
+
// should not happen
|
|
9
|
+
throw new Error("deploy config is not kubernetes");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
name: "deploy-to-kubernetes",
|
|
15
|
+
env: true,
|
|
16
|
+
job: (environment) => ({
|
|
17
|
+
variables: {
|
|
18
|
+
...environment.variables,
|
|
19
|
+
RELEASE_NAME: "${CUSTOMER_NAME}-${APP_NAME}-${CI_ENVIRONMENT_SLUG}",
|
|
20
|
+
},
|
|
21
|
+
stage: "deploy",
|
|
22
|
+
dependencies: [],
|
|
23
|
+
script: [
|
|
24
|
+
"kubernetesEnsureNamespace",
|
|
25
|
+
"kubernetesCreateSecret",
|
|
26
|
+
"kubernetesDeploy",
|
|
27
|
+
],
|
|
28
|
+
environment: {
|
|
29
|
+
name: environment.fullName,
|
|
30
|
+
url: environment.url,
|
|
31
|
+
kubernetes: {
|
|
32
|
+
namespace: environment.variables.KUBE_NAMESPACE,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type DeployConfigBase = {};
|
|
2
|
+
export type DeployConfigKubernetes = {
|
|
3
|
+
type: "kubernetes";
|
|
4
|
+
additionalHelmArgs?: string[];
|
|
5
|
+
values?: {
|
|
6
|
+
application?: {
|
|
7
|
+
resources?: {
|
|
8
|
+
limits?: {
|
|
9
|
+
cpu?: any;
|
|
10
|
+
};
|
|
11
|
+
requests?: {
|
|
12
|
+
cpu?: any;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
} & Record<string, any>;
|
|
16
|
+
} & Record<string, any>;
|
|
17
|
+
} & DeployConfigBase;
|
|
18
|
+
|
|
19
|
+
export type DeployConfigServerless = {
|
|
20
|
+
type: "serverless";
|
|
21
|
+
};
|
|
22
|
+
export type DeployConfig = DeployConfigKubernetes | DeployConfigServerless;
|
|
23
|
+
|
|
24
|
+
export const isOfType = <T extends DeployConfig["type"]>(
|
|
25
|
+
t: DeployConfig,
|
|
26
|
+
type: T
|
|
27
|
+
): t is Extract<DeployConfig, { type: T }> => {
|
|
28
|
+
return t.type === type;
|
|
29
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Environment } from "./context";
|
|
2
|
+
|
|
3
|
+
export type GitlabStage =
|
|
4
|
+
| "setup"
|
|
5
|
+
| "test"
|
|
6
|
+
| "build"
|
|
7
|
+
| "deploy"
|
|
8
|
+
| "verify"
|
|
9
|
+
| "actions";
|
|
10
|
+
|
|
11
|
+
export type Artifacts = {
|
|
12
|
+
paths: string[];
|
|
13
|
+
};
|
|
14
|
+
export type GitlabJobCache = {
|
|
15
|
+
key: string;
|
|
16
|
+
policy?: string;
|
|
17
|
+
paths: string[];
|
|
18
|
+
};
|
|
19
|
+
export type GitlabRule = {
|
|
20
|
+
if: string;
|
|
21
|
+
when?: "always" | "on_success" | "manual" | "never"; // todo
|
|
22
|
+
};
|
|
23
|
+
export type Retry = {
|
|
24
|
+
max: number;
|
|
25
|
+
when: string[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type Service = {
|
|
29
|
+
name: string;
|
|
30
|
+
command: string[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type GitlabEnvironment = {
|
|
34
|
+
url: string;
|
|
35
|
+
name: string;
|
|
36
|
+
kubernetes?: {
|
|
37
|
+
namespace?: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export type GitlabJobDef = {
|
|
41
|
+
stage: GitlabStage;
|
|
42
|
+
before_script?: string[];
|
|
43
|
+
script: string[];
|
|
44
|
+
interruptible?: boolean;
|
|
45
|
+
needs?: string[];
|
|
46
|
+
rules?: GitlabRule[];
|
|
47
|
+
cache?: GitlabJobCache | GitlabJobCache[];
|
|
48
|
+
artifacts?: Artifacts;
|
|
49
|
+
retry?: Retry;
|
|
50
|
+
services?: Service[];
|
|
51
|
+
image?: string;
|
|
52
|
+
variables?: GitlabVariables;
|
|
53
|
+
dependencies?: string[];
|
|
54
|
+
environment?: GitlabEnvironment;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type GitlabVariables = Record<string, string | undefined>;
|
|
58
|
+
|
|
59
|
+
export type JobEnvDependent = {
|
|
60
|
+
name: string;
|
|
61
|
+
env: true;
|
|
62
|
+
job: (environment: Environment) => GitlabJobDef;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type JobNonEnv = {
|
|
66
|
+
name: string;
|
|
67
|
+
env: false;
|
|
68
|
+
job: () => GitlabJobDef;
|
|
69
|
+
};
|
|
70
|
+
export type GitlabJob = JobEnvDependent | JobNonEnv;
|
|
71
|
+
export type GitlabJobs = GitlabJob[];
|
|
72
|
+
|
|
73
|
+
export type GitlabPipeline = {
|
|
74
|
+
variables: GitlabVariables;
|
|
75
|
+
stages: GitlabStage[];
|
|
76
|
+
jobs: GitlabJobs;
|
|
77
|
+
};
|