@catladder/pipeline 0.0.0-vars-07a720d0 → 0.0.0-waitfor-deploy-e64a33ba
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/dist/build/base/createBuildJob.js +3 -1
- package/dist/build/docker.d.ts +1 -0
- package/dist/build/docker.js +19 -1
- package/dist/build/index.js +1 -1
- package/dist/build/node/buildJob.js +2 -2
- package/dist/build/node/yarn.d.ts +1 -0
- package/dist/build/node/yarn.js +43 -2
- package/dist/catladder-gitlab.js +8 -6
- package/dist/config/readConfig.d.ts +5 -1
- package/dist/config/readConfig.js +20 -5
- package/dist/constants.js +1 -1
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +14 -8
- package/dist/context/resolveReferences.d.ts +1 -1
- package/dist/context/resolveReferences.js +9 -4
- package/dist/deploy/base.js +29 -10
- package/dist/deploy/custom/deployJob.d.ts +3 -0
- package/dist/deploy/custom/deployJob.js +65 -0
- package/dist/deploy/custom/index.d.ts +1 -0
- package/dist/deploy/custom/index.js +22 -0
- package/dist/deploy/index.js +8 -0
- package/dist/deploy/kubernetes/deployJob.js +3 -1
- package/dist/deploy/kubernetes/processSecretsAsFiles.d.ts +87 -0
- package/dist/deploy/kubernetes/processSecretsAsFiles.js +39 -0
- package/dist/deploy/types.d.ts +58 -21
- package/dist/deploy/utils.d.ts +4 -3
- package/dist/deploy/utils.js +20 -2
- package/dist/pipeline/createJobs.js +59 -15
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/config.d.ts +12 -0
- package/dist/types/context.d.ts +1 -1
- package/dist/types/jobs.d.ts +6 -1
- package/package.json +1 -1
- package/src/build/base/createBuildJob.ts +2 -0
- package/src/build/docker.ts +14 -0
- package/src/build/index.ts +1 -1
- package/src/build/node/buildJob.ts +29 -24
- package/src/build/node/yarn.ts +5 -1
- package/src/catladder-gitlab.ts +1 -1
- package/src/config/readConfig.ts +18 -6
- package/src/context/index.ts +14 -8
- package/src/context/resolveReferences.ts +16 -12
- package/src/deploy/base.ts +36 -11
- package/src/deploy/custom/deployJob.ts +23 -0
- package/src/deploy/custom/index.ts +1 -0
- package/src/deploy/index.ts +5 -0
- package/src/deploy/kubernetes/deployJob.ts +4 -3
- package/src/deploy/kubernetes/processSecretsAsFiles.ts +34 -0
- package/src/deploy/types.ts +53 -4
- package/src/deploy/utils.ts +15 -2
- package/src/pipeline/createJobs.ts +50 -16
- package/src/types/config.ts +12 -0
- package/src/types/context.ts +1 -1
- package/src/types/jobs.ts +7 -1
|
@@ -6,32 +6,36 @@ export const resolveReferences = (
|
|
|
6
6
|
vars: Record<string, string>,
|
|
7
7
|
getOtherVariables?: (
|
|
8
8
|
componentName: string,
|
|
9
|
-
variableName: string
|
|
10
|
-
|
|
9
|
+
variableName: string,
|
|
10
|
+
alreadyVisited: Record<string, Record<string, boolean>>
|
|
11
|
+
) => string | null,
|
|
12
|
+
alreadyVisitedBase: Record<string, Record<string, boolean>> = {}
|
|
11
13
|
) => {
|
|
12
14
|
const replaceSingleValue = (
|
|
13
15
|
value: string,
|
|
14
|
-
alreadyVisited: Record<string, Record<string, boolean>> =
|
|
16
|
+
alreadyVisited: Record<string, Record<string, boolean>> = alreadyVisitedBase
|
|
15
17
|
): string => {
|
|
16
18
|
if (REGEX.test(value)) {
|
|
17
19
|
return value.replace(REGEX, (match, _, componentName, variableName) => {
|
|
18
20
|
if (alreadyVisited[componentName]?.[variableName]) {
|
|
19
21
|
return match; // prevent endless loop
|
|
20
22
|
}
|
|
23
|
+
const newAlreadyVisited = merge({}, alreadyVisited, {
|
|
24
|
+
[componentName]: {
|
|
25
|
+
[variableName]: true,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
21
28
|
const result = componentName
|
|
22
|
-
? getOtherVariables?.(
|
|
29
|
+
? getOtherVariables?.(
|
|
30
|
+
componentName,
|
|
31
|
+
variableName,
|
|
32
|
+
newAlreadyVisited
|
|
33
|
+
) ?? null
|
|
23
34
|
: vars[variableName]; // is self reference
|
|
24
35
|
|
|
25
36
|
const replaced =
|
|
26
37
|
result !== null && result !== undefined
|
|
27
|
-
? replaceSingleValue(
|
|
28
|
-
result,
|
|
29
|
-
merge({}, alreadyVisited, {
|
|
30
|
-
[componentName]: {
|
|
31
|
-
[variableName]: true,
|
|
32
|
-
},
|
|
33
|
-
})
|
|
34
|
-
)
|
|
38
|
+
? replaceSingleValue(result, newAlreadyVisited)
|
|
35
39
|
: match;
|
|
36
40
|
|
|
37
41
|
return replaced;
|
package/src/deploy/base.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { requiresDockerBuild } from "../build/docker";
|
|
1
2
|
import { Context } from "../types";
|
|
2
3
|
import { CatladderJob } from "../types/jobs";
|
|
4
|
+
import { contextIsStoppable } from "./utils";
|
|
3
5
|
|
|
4
6
|
export const DEPLOY_JOB_NAME = "🚀 Deploy";
|
|
5
7
|
export const STOP_JOB_NAME = "🛑 Stop ⚠️";
|
|
@@ -17,36 +19,53 @@ export const getBaseDeploymentJob = (context: Context): JobWithoutScript => {
|
|
|
17
19
|
url: context.environment.url,
|
|
18
20
|
};
|
|
19
21
|
|
|
22
|
+
const hasDocker = requiresDockerBuild(context);
|
|
23
|
+
const isStoppable = contextIsStoppable(context);
|
|
24
|
+
|
|
20
25
|
const autoStop =
|
|
21
26
|
context.environment.envType === "review"
|
|
22
|
-
? "
|
|
27
|
+
? "3 days" // auto stop is not working properly when branch is merged, so we do
|
|
23
28
|
: context.environment.envType === "dev"
|
|
24
29
|
? "3 weeks"
|
|
25
30
|
: undefined;
|
|
26
|
-
//
|
|
27
|
-
const
|
|
31
|
+
// if auto or manual is configured explicitly, use that
|
|
32
|
+
const whenDeployDefined =
|
|
33
|
+
context.componentConfig.deploy && context.componentConfig.deploy.when
|
|
34
|
+
? context.componentConfig.deploy.when
|
|
35
|
+
: undefined;
|
|
36
|
+
// otherwise auto deploy if env is not prod. If its prod, deploy automatically if stage is disabled
|
|
37
|
+
const whenDeployDefault =
|
|
28
38
|
context.environment.envType !== "prod"
|
|
29
|
-
?
|
|
39
|
+
? "auto" // is not stage, auto deploy
|
|
30
40
|
: context.componentConfig.env?.stage === false
|
|
31
|
-
?
|
|
32
|
-
:
|
|
41
|
+
? "auto" // is prod, but no staging, auto deploy
|
|
42
|
+
: "manual"; // manually deploy
|
|
43
|
+
const whenDeploy = whenDeployDefined ? whenDeployDefined : whenDeployDefault;
|
|
44
|
+
|
|
33
45
|
return {
|
|
34
46
|
name: DEPLOY_JOB_NAME,
|
|
35
47
|
envMode: "stagePerEnv", // makes it easier to run manual tasks er env
|
|
48
|
+
|
|
49
|
+
needsOtherComponent: context.componentConfig.deploy
|
|
50
|
+
? context.componentConfig.deploy.waitFor?.map((c) => ({
|
|
51
|
+
componentName: c,
|
|
52
|
+
job: DEPLOY_JOB_NAME,
|
|
53
|
+
artifacts: false,
|
|
54
|
+
})) ?? undefined
|
|
55
|
+
: undefined,
|
|
36
56
|
// we don't want to deploy when there is a broken test
|
|
37
57
|
needsStages: [
|
|
38
58
|
{
|
|
39
59
|
stage: "build",
|
|
40
|
-
artifacts: false,
|
|
60
|
+
artifacts: hasDocker ? false : true, // we asume that no-docker deployments need build artifacts,
|
|
41
61
|
},
|
|
42
62
|
{
|
|
43
63
|
stage: "test",
|
|
44
64
|
artifacts: false,
|
|
45
65
|
},
|
|
46
66
|
], // workaround for https://gitlab.com/gitlab-org/gitlab/-/issues/220758
|
|
47
|
-
|
|
48
67
|
rules: [
|
|
49
|
-
|
|
68
|
+
whenDeploy === "auto"
|
|
50
69
|
? {
|
|
51
70
|
when: "on_success",
|
|
52
71
|
}
|
|
@@ -54,14 +73,20 @@ export const getBaseDeploymentJob = (context: Context): JobWithoutScript => {
|
|
|
54
73
|
when: "manual",
|
|
55
74
|
},
|
|
56
75
|
],
|
|
76
|
+
allow_failure: whenDeploy === "manual" ? true : false,
|
|
57
77
|
stage: "deploy",
|
|
58
78
|
variables: {
|
|
59
79
|
...DEPLOY_RUNNER_VARIABLES,
|
|
80
|
+
...context.environment.envVars,
|
|
60
81
|
},
|
|
61
82
|
environment: {
|
|
62
83
|
...environment,
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
...(isStoppable
|
|
85
|
+
? {
|
|
86
|
+
on_stop: STOP_JOB_NAME,
|
|
87
|
+
auto_stop_in: autoStop,
|
|
88
|
+
}
|
|
89
|
+
: {}),
|
|
65
90
|
},
|
|
66
91
|
};
|
|
67
92
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { merge } from "lodash";
|
|
2
|
+
import { Context } from "../../types/context";
|
|
3
|
+
import { CatladderJob } from "../../types/jobs";
|
|
4
|
+
import { getBaseDeploymentJob } from "../base";
|
|
5
|
+
import { isOfDeployType } from "../types";
|
|
6
|
+
|
|
7
|
+
export const createCustomDeployJobs = (context: Context): CatladderJob[] => {
|
|
8
|
+
const deployConfig = context.componentConfig.deploy;
|
|
9
|
+
if (deployConfig === false) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
if (!isOfDeployType(deployConfig, "custom")) {
|
|
13
|
+
// should not happen
|
|
14
|
+
throw new Error("deploy config is not custom");
|
|
15
|
+
}
|
|
16
|
+
const baseDeploymentJob = getBaseDeploymentJob(context);
|
|
17
|
+
|
|
18
|
+
return [
|
|
19
|
+
merge({}, baseDeploymentJob, {
|
|
20
|
+
script: [`cd ${context.componentConfig.dir}`, ...deployConfig.script],
|
|
21
|
+
}),
|
|
22
|
+
];
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./deployJob";
|
package/src/deploy/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Context } from "../types/context";
|
|
2
2
|
import { CatladderJob } from "../types/jobs";
|
|
3
|
+
import { createCustomDeployJobs } from "./custom";
|
|
3
4
|
import { createKubernetesDeployJobs } from "./kubernetes";
|
|
4
5
|
import { DeployConfig } from "./types";
|
|
5
6
|
export * from "./kubernetes";
|
|
@@ -17,4 +18,8 @@ export const DEPLOY_TYPES: DeployTypes = {
|
|
|
17
18
|
jobs: createKubernetesDeployJobs,
|
|
18
19
|
defaults: () => ({}),
|
|
19
20
|
},
|
|
21
|
+
custom: {
|
|
22
|
+
jobs: createCustomDeployJobs,
|
|
23
|
+
defaults: () => ({}),
|
|
24
|
+
},
|
|
20
25
|
};
|
|
@@ -10,6 +10,7 @@ import { getBaseDeploymentJob, getBaseDeploymentStopJob } from "../base";
|
|
|
10
10
|
import { isOfDeployType } from "../types";
|
|
11
11
|
import { createCloudsqlBaseConfig } from "./cloudsql";
|
|
12
12
|
import { createMongodbBaseConfig } from "./mongodb";
|
|
13
|
+
import { processSecretsAsFiles } from "./processSecretsAsFiles";
|
|
13
14
|
|
|
14
15
|
export const createKubernetesDeployJobs = (
|
|
15
16
|
context: Context
|
|
@@ -74,6 +75,7 @@ export const createKubernetesDeployJobs = (
|
|
|
74
75
|
|
|
75
76
|
const defaultKubeValues = {
|
|
76
77
|
application: defaultAppConfig,
|
|
78
|
+
|
|
77
79
|
env: env,
|
|
78
80
|
...(deployConfig.values?.cloudsql?.enabled
|
|
79
81
|
? createCloudsqlBaseConfig(context)
|
|
@@ -83,9 +85,8 @@ export const createKubernetesDeployJobs = (
|
|
|
83
85
|
: {}),
|
|
84
86
|
};
|
|
85
87
|
|
|
86
|
-
const kubeValues =
|
|
87
|
-
defaultKubeValues,
|
|
88
|
-
deployConfig.values
|
|
88
|
+
const kubeValues = processSecretsAsFiles(
|
|
89
|
+
mergeWithMergingArrays(defaultKubeValues, deployConfig.values)
|
|
89
90
|
);
|
|
90
91
|
|
|
91
92
|
const kubernetesEnvironment = {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { omit } from "lodash";
|
|
2
|
+
import { DeployConfigKubernetesValues } from "../types";
|
|
3
|
+
|
|
4
|
+
export const processSecretsAsFiles = <
|
|
5
|
+
T extends {
|
|
6
|
+
env: {
|
|
7
|
+
secret: Record<string, string>;
|
|
8
|
+
};
|
|
9
|
+
} & DeployConfigKubernetesValues
|
|
10
|
+
>(
|
|
11
|
+
values: T
|
|
12
|
+
): T => {
|
|
13
|
+
if (!values.secretsAsFile || values.secretsAsFile.length === 0) {
|
|
14
|
+
return values;
|
|
15
|
+
}
|
|
16
|
+
// the initial idea was that we "shadow" secrets that we mount as files,
|
|
17
|
+
// but there is a weird kubernetes issue with that (https://github.com/kubernetes/kubernetes/issues/46861),
|
|
18
|
+
// so instead we rename the secrets and append _content, so that inside kuberentes, we can use the original name to contain the path and name+_content as the actual content
|
|
19
|
+
return {
|
|
20
|
+
...values,
|
|
21
|
+
env: {
|
|
22
|
+
...values.env,
|
|
23
|
+
secret: {
|
|
24
|
+
...omit(values.env.secret, values.secretsAsFile),
|
|
25
|
+
...Object.fromEntries(
|
|
26
|
+
values.secretsAsFile.map((key) => [
|
|
27
|
+
key + "_content",
|
|
28
|
+
values.env.secret[key],
|
|
29
|
+
])
|
|
30
|
+
),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
};
|
package/src/deploy/types.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
2
|
-
export type DeployConfigBase = {
|
|
2
|
+
export type DeployConfigBase = {
|
|
3
|
+
/**
|
|
4
|
+
* whether to deploy automatically or manual. If not defined, these rules apply:
|
|
5
|
+
* - prod: manual
|
|
6
|
+
* - all other envs: auto
|
|
7
|
+
*/
|
|
8
|
+
when?: "manual" | "auto";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* EXPERIMENTAL
|
|
12
|
+
* wait for other components to deploy first, before doing this deployment
|
|
13
|
+
*/
|
|
14
|
+
waitFor?: string[];
|
|
15
|
+
};
|
|
3
16
|
type AllowUnknownProps<T extends Record<string, unknown>> = T &
|
|
4
17
|
Record<string, unknown>;
|
|
5
18
|
|
|
@@ -24,6 +37,30 @@ export type DeployConfigKubernetesClusterGCloud = {
|
|
|
24
37
|
};
|
|
25
38
|
export type DeployConfigKubernetesCluster = DeployConfigKubernetesClusterGCloud; // currently only this
|
|
26
39
|
|
|
40
|
+
export type KubernetesAutoscaleMetricResourceTarget =
|
|
41
|
+
| {
|
|
42
|
+
type: "Utilization";
|
|
43
|
+
averageUtilization: number;
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
type: "AverageValue";
|
|
47
|
+
averageValue: number | string;
|
|
48
|
+
};
|
|
49
|
+
export type KubernetesAutoscaleMetric = {
|
|
50
|
+
type: "Resource";
|
|
51
|
+
resource: {
|
|
52
|
+
name: "cpu" | "memory";
|
|
53
|
+
target: KubernetesAutoscaleMetricResourceTarget;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export type KubernetesAutoscale = {
|
|
57
|
+
minReplicas: number;
|
|
58
|
+
maxReplicas: number;
|
|
59
|
+
/**
|
|
60
|
+
* declare a number of metrics (at least one). Usually you would use cpu as a metric
|
|
61
|
+
*/
|
|
62
|
+
metrics: KubernetesAutoscaleMetric[];
|
|
63
|
+
};
|
|
27
64
|
type KubernetesHealthDef = {
|
|
28
65
|
httpGet?: {
|
|
29
66
|
path?: string;
|
|
@@ -94,6 +131,11 @@ export type DeployConfigKubernetesValues = AllowUnknownProps<{
|
|
|
94
131
|
* how many pods will be started
|
|
95
132
|
*/
|
|
96
133
|
replicas?: number;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* autoscale the pods horizontally
|
|
137
|
+
*/
|
|
138
|
+
autoscale?: KubernetesAutoscale;
|
|
97
139
|
/**
|
|
98
140
|
* kubernetes resources
|
|
99
141
|
*/
|
|
@@ -112,6 +154,11 @@ export type DeployConfigKubernetesValues = AllowUnknownProps<{
|
|
|
112
154
|
readinessProbe?: KubernetesHealthDef;
|
|
113
155
|
livenessProbe?: KubernetesHealthDef;
|
|
114
156
|
}>;
|
|
157
|
+
/**
|
|
158
|
+
* Mount secrets as files in the filesystem.
|
|
159
|
+
* These secrets are still available as env vars, but will contain the path of the file instead
|
|
160
|
+
*/
|
|
161
|
+
secretsAsFile?: string[];
|
|
115
162
|
}>;
|
|
116
163
|
|
|
117
164
|
export type DeployConfigKubernetes = {
|
|
@@ -131,12 +178,14 @@ export type DeployConfigKubernetes = {
|
|
|
131
178
|
values?: DeployConfigKubernetesValues;
|
|
132
179
|
} & DeployConfigBase;
|
|
133
180
|
|
|
134
|
-
type
|
|
181
|
+
type DeployConfigCustom = {
|
|
135
182
|
type: "custom";
|
|
183
|
+
requiresDocker: boolean;
|
|
136
184
|
script: string[];
|
|
137
|
-
|
|
185
|
+
stopScript?: string[];
|
|
186
|
+
} & DeployConfigBase;
|
|
138
187
|
|
|
139
|
-
export type DeployConfig = DeployConfigKubernetes;
|
|
188
|
+
export type DeployConfig = DeployConfigKubernetes | DeployConfigCustom;
|
|
140
189
|
|
|
141
190
|
export const isOfDeployType = <T extends Array<DeployConfig["type"]>>(
|
|
142
191
|
t: DeployConfig | false,
|
package/src/deploy/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Config } from "../types";
|
|
2
|
-
import
|
|
1
|
+
import { Config, Context } from "../types";
|
|
2
|
+
import { DeployConfigKubernetesCluster, isOfDeployType } from "./types";
|
|
3
3
|
|
|
4
4
|
export const getFullKubernetesClusterName = (
|
|
5
5
|
cluster: DeployConfigKubernetesCluster
|
|
@@ -15,3 +15,16 @@ export const getKubernetesNamespace = (
|
|
|
15
15
|
) => {
|
|
16
16
|
return `${config.customerName}-${config.appName}-${env}`;
|
|
17
17
|
};
|
|
18
|
+
|
|
19
|
+
export const contextIsStoppable = (context: Context) => {
|
|
20
|
+
const deployConfig = context.componentConfig.deploy;
|
|
21
|
+
if (isOfDeployType(deployConfig, "kubernetes")) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (isOfDeployType(deployConfig, "custom") && deployConfig.stopScript) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return false;
|
|
30
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { exec } from "child-process-promise";
|
|
2
|
-
import { isObject } from "lodash";
|
|
2
|
+
import { isFunction, isObject } from "lodash";
|
|
3
3
|
import { BUILD_TYPES } from "../build";
|
|
4
4
|
import { createContext } from "../context";
|
|
5
5
|
import { DEPLOY_TYPES } from "../deploy";
|
|
@@ -11,6 +11,26 @@ import { notNil } from "../utils";
|
|
|
11
11
|
import { makeGitlabJob } from "./gitlab/makeGitlabJob";
|
|
12
12
|
import { getPackageManagerInfo } from "./packageManager";
|
|
13
13
|
|
|
14
|
+
const injectDefaultVarsInCustomJobs = (
|
|
15
|
+
context: Context,
|
|
16
|
+
jobs: CatladderJob[]
|
|
17
|
+
) =>
|
|
18
|
+
jobs.map(({ variables, ...job }) => ({
|
|
19
|
+
variables: {
|
|
20
|
+
...(context.environment.envVars ?? {}),
|
|
21
|
+
...(variables ?? {}),
|
|
22
|
+
},
|
|
23
|
+
...job,
|
|
24
|
+
}));
|
|
25
|
+
const getCustomJobs = (context: Context) => {
|
|
26
|
+
if (!context.componentConfig.customJobs) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const rawJobs = isFunction(context.componentConfig.customJobs)
|
|
30
|
+
? context.componentConfig.customJobs(context)
|
|
31
|
+
: context.componentConfig.customJobs;
|
|
32
|
+
return injectDefaultVarsInCustomJobs(context, rawJobs);
|
|
33
|
+
};
|
|
14
34
|
const createRawJobs = (context: Context): CatladderJob[] => {
|
|
15
35
|
if (context.componentConfig.deploy === false) {
|
|
16
36
|
return [];
|
|
@@ -20,7 +40,8 @@ const createRawJobs = (context: Context): CatladderJob[] => {
|
|
|
20
40
|
const deployJobs =
|
|
21
41
|
DEPLOY_TYPES[context.componentConfig.deploy.type].jobs(context);
|
|
22
42
|
|
|
23
|
-
|
|
43
|
+
const customJobs = getCustomJobs(context);
|
|
44
|
+
return [...buildJobs, ...deployJobs, ...customJobs];
|
|
24
45
|
};
|
|
25
46
|
const getFullJobName = (
|
|
26
47
|
name: string,
|
|
@@ -56,22 +77,33 @@ const replaceReferences = (
|
|
|
56
77
|
): CatladderJob<string> => {
|
|
57
78
|
const stage =
|
|
58
79
|
job.envMode === "stagePerEnv" ? `${job.stage} ${env}` : job.stage;
|
|
80
|
+
|
|
81
|
+
const needsFromNeeds: CatladderJob["needs"] = job.needs?.map((n) =>
|
|
82
|
+
isObject(n)
|
|
83
|
+
? {
|
|
84
|
+
job: getFullReferencedJobName(n.job, componentName, env, allRawJobs),
|
|
85
|
+
artifacts: n.artifacts,
|
|
86
|
+
}
|
|
87
|
+
: getFullReferencedJobName(n, componentName, env, allRawJobs)
|
|
88
|
+
);
|
|
89
|
+
const needsFromOtherComponents: CatladderJob["needs"] =
|
|
90
|
+
job.needsOtherComponent?.map((other) => ({
|
|
91
|
+
artifacts: other.artifacts,
|
|
92
|
+
job: getFullReferencedJobName(
|
|
93
|
+
other.job,
|
|
94
|
+
other.componentName,
|
|
95
|
+
env,
|
|
96
|
+
allRawJobs
|
|
97
|
+
),
|
|
98
|
+
}));
|
|
99
|
+
const needs = [
|
|
100
|
+
...(needsFromNeeds ?? []),
|
|
101
|
+
...(needsFromOtherComponents ?? []),
|
|
102
|
+
];
|
|
59
103
|
return {
|
|
60
104
|
...job,
|
|
61
105
|
stage,
|
|
62
|
-
needs
|
|
63
|
-
isObject(n)
|
|
64
|
-
? {
|
|
65
|
-
job: getFullReferencedJobName(
|
|
66
|
-
n.job,
|
|
67
|
-
componentName,
|
|
68
|
-
env,
|
|
69
|
-
allRawJobs
|
|
70
|
-
),
|
|
71
|
-
artifacts: n.artifacts,
|
|
72
|
-
}
|
|
73
|
-
: getFullReferencedJobName(n, componentName, env, allRawJobs)
|
|
74
|
-
),
|
|
106
|
+
needs,
|
|
75
107
|
environment: job.environment?.on_stop
|
|
76
108
|
? {
|
|
77
109
|
...job.environment,
|
|
@@ -122,7 +154,9 @@ export const createJobs = async <T extends PipelineType>(
|
|
|
122
154
|
): Promise<Record<string, PipelineJob<T>>> => {
|
|
123
155
|
const commitInfo: CommitInfo = {
|
|
124
156
|
refName: process.env.CI_COMMIT_REF_NAME ?? "unknown",
|
|
125
|
-
|
|
157
|
+
reviewSlug: process.env.CI_MERGE_REQUEST_IID
|
|
158
|
+
? `mr${process.env.CI_MERGE_REQUEST_IID}`
|
|
159
|
+
: "unknown",
|
|
126
160
|
buildTime: new Date().toISOString(),
|
|
127
161
|
buildId: await exec("git describe --tags || git rev-parse HEAD").then((s) =>
|
|
128
162
|
s.stdout.trim()
|
package/src/types/config.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { BuildConfig } from "../build/types";
|
|
2
2
|
import { DeployConfig } from "../deploy/types";
|
|
3
3
|
import { PartialDeep } from "type-fest";
|
|
4
|
+
import { CatladderJob } from "./jobs";
|
|
5
|
+
import { Context } from "./context";
|
|
4
6
|
export type PipelineTrigger = "mainBranch" | "mr" | "taggedRelease";
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -123,6 +125,16 @@ export type ComponentConfig = {
|
|
|
123
125
|
* the directory of the component (e.g. where the package.json or similar is located). You can set "." if you only have one app.
|
|
124
126
|
*/
|
|
125
127
|
dir: string;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* add custom jobs. Can be an array or a function that receives the context and returns an array of jobs.
|
|
131
|
+
* For convenience, all env vars are injectd as variables
|
|
132
|
+
*
|
|
133
|
+
* Please raise an issue on https://git.panter.ch/catladder/catladder
|
|
134
|
+
* to let us know about why you need to use custom jobs.
|
|
135
|
+
* This feedback will help us to generalize use cases
|
|
136
|
+
*/
|
|
137
|
+
customJobs?: CatladderJob[] | ((context: Context) => CatladderJob[]);
|
|
126
138
|
} & DefaultEnvConfig;
|
|
127
139
|
|
|
128
140
|
export type Config = {
|
package/src/types/context.ts
CHANGED
package/src/types/jobs.ts
CHANGED
|
@@ -49,9 +49,15 @@ export type CatladderJob<S = BaseStage> = {
|
|
|
49
49
|
}[];
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* does this require another job?
|
|
52
|
+
* does this require another job (from the same component)?
|
|
53
53
|
*/
|
|
54
54
|
needs?: Array<string | { job: string; artifacts: boolean }>;
|
|
55
|
+
|
|
56
|
+
needsOtherComponent?: Array<{
|
|
57
|
+
componentName: string;
|
|
58
|
+
job: string;
|
|
59
|
+
artifacts: boolean;
|
|
60
|
+
}>;
|
|
55
61
|
/**
|
|
56
62
|
* cache config, we use here the same shape as gitlab itself
|
|
57
63
|
*/
|