@catladder/pipeline 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/build/base/createBuildJob.d.ts +3 -2
  2. package/dist/build/base/createBuildJob.js +11 -17
  3. package/dist/build/docker.d.ts +3 -2
  4. package/dist/build/docker.js +24 -27
  5. package/dist/build/index.d.ts +2 -2
  6. package/dist/build/node/buildJob.d.ts +2 -2
  7. package/dist/build/node/index.d.ts +4 -4
  8. package/dist/build/node/meteor.d.ts +2 -2
  9. package/dist/build/node/testJob.d.ts +2 -2
  10. package/dist/build/node/testJob.js +20 -29
  11. package/dist/build/tests/storybook.test.js +10 -23
  12. package/dist/catladder-gitlab.js +33 -2
  13. package/dist/constants.js +1 -1
  14. package/dist/deploy/base.d.ts +3 -4
  15. package/dist/deploy/base.js +27 -33
  16. package/dist/deploy/index.d.ts +2 -2
  17. package/dist/deploy/kubernetes/deployJob.d.ts +2 -2
  18. package/dist/deploy/kubernetes/deployJob.js +32 -38
  19. package/dist/pipeline/createChildPipeline.d.ts +2 -11
  20. package/dist/pipeline/createChildPipeline.js +24 -18
  21. package/dist/pipeline/createChildPipeline.test.js +3 -14
  22. package/dist/pipeline/createJobs.d.ts +2 -2
  23. package/dist/pipeline/createJobs.js +24 -18
  24. package/dist/pipeline/gitlab/makeGitlabJob.d.ts +8 -0
  25. package/dist/pipeline/gitlab/makeGitlabJob.js +45 -0
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/dist/types/gitlab-types.d.ts +10 -22
  28. package/dist/types/gitlab-types.js +1 -3
  29. package/dist/types/index.d.ts +2 -1
  30. package/dist/types/index.js +3 -1
  31. package/dist/types/jobs.d.ts +75 -0
  32. package/dist/types/jobs.js +5 -0
  33. package/dist/types/pipeline.d.ts +4 -0
  34. package/dist/types/pipeline.js +3 -0
  35. package/package.json +1 -1
  36. package/src/build/base/createBuildJob.ts +28 -32
  37. package/src/build/docker.ts +49 -51
  38. package/src/build/index.ts +3 -2
  39. package/src/build/node/buildJob.ts +3 -3
  40. package/src/build/node/index.ts +4 -4
  41. package/src/build/node/meteor.ts +5 -7
  42. package/src/build/node/testJob.ts +31 -40
  43. package/src/build/tests/storybook.test.ts +4 -2
  44. package/src/catladder-gitlab.ts +9 -5
  45. package/src/deploy/base.ts +40 -45
  46. package/src/deploy/index.ts +2 -2
  47. package/src/deploy/kubernetes/deployJob.ts +43 -48
  48. package/src/pipeline/createChildPipeline.test.ts +2 -1
  49. package/src/pipeline/createChildPipeline.ts +23 -21
  50. package/src/pipeline/createJobs.ts +47 -38
  51. package/src/pipeline/gitlab/makeGitlabJob.ts +16 -0
  52. package/src/types/gitlab-types.ts +10 -30
  53. package/src/types/index.ts +1 -0
  54. package/src/types/jobs.ts +88 -0
  55. package/src/types/pipeline.ts +11 -0
@@ -1,5 +1,5 @@
1
- import { GitlabJobs } from "../types/gitlab-types";
2
1
  import { Context } from "../types/context";
2
+ import { CatladderJob } from "../types/jobs";
3
3
  import { createKubernetesDeployJobs } from "./kubernetes";
4
4
  import { DeployConfig } from "./types";
5
5
  export * from "./kubernetes";
@@ -7,7 +7,7 @@ export * from "./types";
7
7
  export * from "./utils";
8
8
  export type DeployTypes = {
9
9
  [type in DeployConfig["type"]]: {
10
- jobs: (context: Context) => GitlabJobs;
10
+ jobs: (context: Context) => CatladderJob[];
11
11
  defaults: () => Partial<Extract<DeployConfig, { type: type }>>;
12
12
  };
13
13
  };
@@ -1,18 +1,19 @@
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
1
  import { dump } from "js-yaml";
8
- import { createMongodbBaseConfig } from "./mongodb";
9
- import { createCloudsqlBaseConfig } from "./cloudsql";
2
+ import { merge } from "lodash";
3
+ import { DeployConfigKubernetesValues } from "..";
10
4
  import { getSecretVarNameForContext } from "../..";
5
+ import { getRunnerImage } from "../../runner";
6
+ import { Context } from "../../types/context";
7
+ import { CatladderJob } from "../../types/jobs";
11
8
  import { mergeWithMergingArrays } from "../../utils";
12
- import { DeployConfigKubernetesValues } from "..";
13
- import { PartialDeep } from "type-fest";
9
+ import { getBaseDeploymentJob, getBaseDeploymentStopJob } from "../base";
10
+ import { isOfDeployType } from "../types";
11
+ import { createCloudsqlBaseConfig } from "./cloudsql";
12
+ import { createMongodbBaseConfig } from "./mongodb";
14
13
 
15
- export const createKubernetesDeployJobs = (context: Context): GitlabJobs => {
14
+ export const createKubernetesDeployJobs = (
15
+ context: Context
16
+ ): CatladderJob[] => {
16
17
  const deployConfig = context.componentConfig.deploy;
17
18
  if (deployConfig === false) {
18
19
  return [];
@@ -91,28 +92,26 @@ export const createKubernetesDeployJobs = (context: Context): GitlabJobs => {
91
92
  namespace: context.environment.envVars.KUBE_NAMESPACE,
92
93
  };
93
94
  const shared = {
94
- job: {
95
- image: getRunnerImage("kubernetes"),
96
- variables: {
97
- ...context.environment.envVars,
98
- HELM_EXPERIMENTAL_OCI: "1",
99
- IMAGE_PULL_SECRET: `gitlab-registry-${context.componentName}`,
100
- KUBE_VALUES: dump(kubeValues, {
101
- lineWidth: -1,
102
- quotingType: "'",
103
- forceQuotes: true,
104
- }),
105
- HELM_GITLAB_CHART_NAME: "the-panter-chart",
106
- HELM_ARGS: [
107
- ...(deployConfig.debug ? ["--debug"] : []),
108
- ...(deployConfig.additionalHelmArgs ?? []),
109
- ].join(" "),
110
- COMPONENT_NAME: context.componentName,
111
- BUILD_ID: context.commitInfo?.buildId,
112
- // TODO: unify with docker build stage
113
- IMAGE_NAME: context.environment.shortName + "/" + context.componentName,
114
- IMAGE_TAG: "$CI_COMMIT_SHA",
115
- },
95
+ image: getRunnerImage("kubernetes"),
96
+ variables: {
97
+ ...context.environment.envVars,
98
+ HELM_EXPERIMENTAL_OCI: "1",
99
+ IMAGE_PULL_SECRET: `gitlab-registry-${context.componentName}`,
100
+ KUBE_VALUES: dump(kubeValues, {
101
+ lineWidth: -1,
102
+ quotingType: "'",
103
+ forceQuotes: true,
104
+ }),
105
+ HELM_GITLAB_CHART_NAME: "the-panter-chart",
106
+ HELM_ARGS: [
107
+ ...(deployConfig.debug ? ["--debug"] : []),
108
+ ...(deployConfig.additionalHelmArgs ?? []),
109
+ ].join(" "),
110
+ COMPONENT_NAME: context.componentName,
111
+ BUILD_ID: context.commitInfo?.buildId,
112
+ // TODO: unify with docker build stage
113
+ IMAGE_NAME: context.environment.shortName + "/" + context.componentName,
114
+ IMAGE_TAG: "$CI_COMMIT_SHA",
116
115
  },
117
116
  };
118
117
 
@@ -136,24 +135,20 @@ export const createKubernetesDeployJobs = (context: Context): GitlabJobs => {
136
135
  ];
137
136
  return [
138
137
  merge({}, baseDeploymentJob, shared, {
139
- job: {
140
- script: [
141
- ...connectContext,
142
- "kubernetesCreateSecret",
143
- "kubernetesDeploy",
144
- "echo deployment successful 😻",
145
- ],
146
- environment: {
147
- kubernetes: kubernetesEnvironment,
148
- },
138
+ script: [
139
+ ...connectContext,
140
+ "kubernetesCreateSecret",
141
+ "kubernetesDeploy",
142
+ "echo deployment successful 😻",
143
+ ],
144
+ environment: {
145
+ kubernetes: kubernetesEnvironment,
149
146
  },
150
147
  }),
151
148
  merge({}, baseStopJob, shared, {
152
- job: {
153
- script: [...connectContext, "kubernetesDelete"],
154
- environment: {
155
- kubernetes: kubernetesEnvironment,
156
- },
149
+ script: [...connectContext, "kubernetesDelete"],
150
+ environment: {
151
+ kubernetes: kubernetesEnvironment,
157
152
  },
158
153
  }),
159
154
  ];
@@ -26,7 +26,8 @@ describe("createChildPipeline", () => {
26
26
  };
27
27
 
28
28
  it("creates a pipeline for a single app on the main branch", async () => {
29
- const { image, stages, workflow, ...jobs } = await createChildPipeline(
29
+ const { image, jobs } = await createChildPipeline(
30
+ "gitlab",
30
31
  "mainBranch",
31
32
  config
32
33
  );
@@ -1,24 +1,25 @@
1
1
  import { getAllEnvsByTrigger, getAllEnvsInAllComponents } from "../config";
2
- import { createJobs } from "./createJobs";
3
2
  import { RULES_ALWAYS } from "../rules";
4
3
  import { getRunnerImage } from "../runner";
4
+ import { GitlabPipeline, Pipeline, PipelineJob, PipelineType } from "../types";
5
5
  import { Config, PipelineTrigger } from "../types/config";
6
- import { GitlabJobDef } from "../types/gitlab-types";
6
+ import { createJobs } from "./createJobs";
7
+
7
8
  const baseStages = ["setup", "test", "build", "deploy", "verify", "stop"];
8
- export const createChildPipeline = async (
9
+ export const createChildPipeline = async <T extends PipelineType>(
10
+ type: T,
9
11
  trigger: PipelineTrigger,
10
12
  config: Config
11
- ) => {
13
+ ): Promise<Pipeline<T>> => {
12
14
  const components = Object.keys(config.components);
13
15
 
14
16
  // 2. write the triggering pipeline
15
-
16
- const jobs = await components.reduce<Promise<Record<string, GitlabJobDef>>>(
17
+ const jobs = await components.reduce<Promise<Record<string, PipelineJob<T>>>>(
17
18
  async (acc, componentName) => {
18
19
  const envs = getAllEnvsByTrigger(config, componentName, trigger);
19
20
  return {
20
21
  ...(await acc),
21
- ...(await createJobs(envs, config, componentName, trigger)),
22
+ ...(await createJobs(type, envs, config, componentName, trigger)),
22
23
  };
23
24
  },
24
25
  Promise.resolve({})
@@ -37,18 +38,19 @@ export const createChildPipeline = async (
37
38
  ],
38
39
  []
39
40
  );
40
-
41
- const childPipeline = {
42
- image: getRunnerImage("jobs-default"), // default image
43
- variables: {
44
- FF_USE_FASTZIP: "true",
45
- },
46
- workflow: {
47
- rules: RULES_ALWAYS,
48
- },
49
- stages,
50
- ...jobs,
51
- };
52
-
53
- return childPipeline as typeof childPipeline & Record<string, GitlabJobDef>;
41
+ if (type === "gitlab") {
42
+ const pipeline: GitlabPipeline = {
43
+ image: getRunnerImage("jobs-default"), // default image
44
+ variables: {
45
+ FF_USE_FASTZIP: "true",
46
+ },
47
+ workflow: {
48
+ rules: RULES_ALWAYS,
49
+ },
50
+ stages,
51
+ jobs,
52
+ };
53
+ return pipeline as Pipeline<T>;
54
+ }
55
+ throw new Error(`${type} is not supported`);
54
56
  };
@@ -3,13 +3,15 @@ import { isObject } from "lodash";
3
3
  import { BUILD_TYPES } from "../build";
4
4
  import { createContext } from "../context";
5
5
  import { DEPLOY_TYPES } from "../deploy";
6
+ import { PipelineJob, PipelineType } from "../types";
6
7
  import { Config, PipelineTrigger } from "../types/config";
7
- import { Context, CommitInfo } from "../types/context";
8
- import { GitlabJob, GitlabJobDef, GitlabJobs } from "../types/gitlab-types";
8
+ import { CommitInfo, Context } from "../types/context";
9
+ import { CatladderJob } from "../types/jobs";
9
10
  import { notNil } from "../utils";
11
+ import { makeGitlabJob } from "./gitlab/makeGitlabJob";
10
12
  import { getPackageManagerInfo } from "./packageManager";
11
13
 
12
- const createRawJobs = (context: Context): GitlabJobs => {
14
+ const createRawJobs = (context: Context): CatladderJob[] => {
13
15
  if (context.componentConfig.deploy === false) {
14
16
  return [];
15
17
  }
@@ -35,7 +37,7 @@ const getFullReferencedJobName = (
35
37
  referencedJobName: string,
36
38
  componentName: string,
37
39
  env: string,
38
- allRawJobs: GitlabJobs
40
+ allRawJobs: CatladderJob[]
39
41
  ) => {
40
42
  const referencedJob = allRawJobs.find((j) => j.name === referencedJobName);
41
43
  if (!referencedJob) {
@@ -47,18 +49,17 @@ const getFullReferencedJobName = (
47
49
  // replaces references to other jobs with the full name
48
50
  // the full name contains the componentname and the env name (if any)
49
51
  const replaceReferences = (
50
- job: GitlabJob,
52
+ job: CatladderJob,
51
53
  componentName: string,
52
54
  env: string,
53
- allRawJobs: GitlabJobs
54
- ): GitlabJobDef => {
55
- const def = job.job;
55
+ allRawJobs: CatladderJob[]
56
+ ): CatladderJob<string> => {
56
57
  const stage =
57
- job.envMode === "stagePerEnv" ? `${def.stage} ${env}` : def.stage;
58
+ job.envMode === "stagePerEnv" ? `${job.stage} ${env}` : job.stage;
58
59
  return {
59
- ...def,
60
+ ...job,
60
61
  stage,
61
- needs: def.needs?.map((n) =>
62
+ needs: job.needs?.map((n) =>
62
63
  isObject(n)
63
64
  ? {
64
65
  job: getFullReferencedJobName(
@@ -71,25 +72,22 @@ const replaceReferences = (
71
72
  }
72
73
  : getFullReferencedJobName(n, componentName, env, allRawJobs)
73
74
  ),
74
- environment: def.environment?.on_stop
75
+ environment: job.environment?.on_stop
75
76
  ? {
76
- ...def.environment,
77
+ ...job.environment,
77
78
  on_stop: getFullReferencedJobName(
78
- def.environment.on_stop,
79
+ job.environment.on_stop,
79
80
  componentName,
80
81
  env,
81
82
  allRawJobs
82
83
  ),
83
84
  }
84
- : def.environment,
85
- dependencies: def.dependencies?.map((n) =>
86
- getFullReferencedJobName(n, componentName, env, allRawJobs)
87
- ),
85
+ : job.environment,
88
86
  };
89
87
  };
90
88
 
91
89
  // this can be removed once https://gitlab.com/gitlab-org/gitlab/-/issues/220758 is resolved
92
- const addStageNeeds = (jobs: GitlabJobs): GitlabJobs => {
90
+ const addStageNeeds = (jobs: CatladderJob[]): CatladderJob[] => {
93
91
  // when a job defines needsStages, we add these as needs
94
92
  return jobs.map((job) => {
95
93
  if (!job.needsStages || job.needsStages.length === 0) {
@@ -98,9 +96,7 @@ const addStageNeeds = (jobs: GitlabJobs): GitlabJobs => {
98
96
 
99
97
  const neededJobs = jobs
100
98
  .map((j) => {
101
- const neededStage = job.needsStages?.find(
102
- (s) => j.job.stage === s.stage
103
- );
99
+ const neededStage = job.needsStages?.find((s) => j.stage === s.stage);
104
100
  if (neededStage) {
105
101
  return {
106
102
  job: j.name,
@@ -111,19 +107,19 @@ const addStageNeeds = (jobs: GitlabJobs): GitlabJobs => {
111
107
  .filter(notNil);
112
108
  return {
113
109
  ...job,
114
- job: {
115
- ...job.job,
116
- needs: [...(job.job.needs ?? []), ...neededJobs],
117
- },
110
+
111
+ needs: [...(job.needs ?? []), ...neededJobs],
118
112
  };
119
113
  });
120
114
  };
121
- export const createJobs = async (
115
+
116
+ export const createJobs = async <T extends PipelineType>(
117
+ type: T,
122
118
  envs: string[],
123
119
  config: Config,
124
120
  componentName: string,
125
121
  trigger: PipelineTrigger
126
- ): Promise<Record<string, GitlabJobDef>> => {
122
+ ): Promise<Record<string, PipelineJob<T>>> => {
127
123
  const commitInfo: CommitInfo = {
128
124
  refName: process.env.CI_COMMIT_REF_NAME ?? "unknown",
129
125
  refSlug: process.env.CI_COMMIT_REF_SLUG ?? "unknown",
@@ -146,18 +142,31 @@ export const createJobs = async (
146
142
  );
147
143
  const jobs = addStageNeeds(createRawJobs(context));
148
144
 
149
- return {
145
+ const result = {
150
146
  ...acc,
151
- ...jobs.reduce((acc, job) => {
152
- return {
153
- ...acc,
154
- [getFullJobName(
155
- job.name,
156
- componentName,
157
- job.envMode !== "none" ? env : undefined
158
- )]: replaceReferences(job, componentName, env, jobs),
159
- };
147
+ ...jobs.reduce<Record<string, PipelineJob<T>>>((acc, job) => {
148
+ const jobWithResolvedReferences = replaceReferences(
149
+ job,
150
+ componentName,
151
+ env,
152
+ jobs
153
+ );
154
+ const jobName = getFullJobName(
155
+ job.name,
156
+ componentName,
157
+ job.envMode !== "none" ? env : undefined
158
+ );
159
+ if (type === "gitlab") {
160
+ return {
161
+ ...acc,
162
+ [jobName]: makeGitlabJob(
163
+ jobWithResolvedReferences
164
+ ) as PipelineJob<T>,
165
+ };
166
+ }
167
+ throw new Error("not supported");
160
168
  }, {}),
161
169
  };
170
+ return result;
162
171
  }, {});
163
172
  };
@@ -0,0 +1,16 @@
1
+ import { BASE_RETRY } from "../../defaults";
2
+ import { GitlabJobDef } from "../../types";
3
+ import { CatladderJob } from "../../types/jobs";
4
+
5
+ export const makeGitlabJob = ({
6
+ envMode,
7
+ needsStages,
8
+ name,
9
+ ...rest
10
+ }: CatladderJob<string>): GitlabJobDef => {
11
+ return {
12
+ ...rest,
13
+ retry: BASE_RETRY,
14
+ interruptible: true,
15
+ };
16
+ };
@@ -56,39 +56,19 @@ export type GitlabJobDef = {
56
56
  };
57
57
  };
58
58
 
59
- export const GITLAB_BASE_STAGES = [
60
- "setup",
61
- "test",
62
- "build",
63
- "deploy",
64
- "verify",
65
- "stop",
66
- ] as const;
67
- export type GitlabBaseStage = typeof GITLAB_BASE_STAGES[number];
68
59
  export type GitlabVariables = Record<string, string | undefined>;
69
60
 
70
- export type GitlabJob = {
71
- /**
72
- * the name of the job (without any env or app prefix and suffix)
73
- */
74
- name: string;
75
- /**
76
- * envMode sets the behavior of the job regarding multiple envs:
77
- * - none: the job does not run per env, but once for all envs
78
- * - jobPerEnv: the job runs once per env
79
- * - stagePerEnv: the job runs once per env and is organized in its own stage. This mproves usability in gitlab, but works the same as `jobPerEnv`
80
- */
81
- envMode: "jobPerEnv" | "stagePerEnv" | "none";
82
- job: GitlabJobDef;
83
- needsStages?: {
84
- stage: GitlabBaseStage;
85
- artifacts?: boolean;
86
- }[];
87
- };
88
- export type GitlabJobs = GitlabJob[];
89
-
61
+ /**
62
+ * this is not precicily the type of a gitlab-pipeline
63
+ * the jobs nee to be merge into the object.
64
+ * Problem is, that this type cannot be represented properly with typescript, see https://github.com/microsoft/TypeScript/issues/17867
65
+ */
90
66
  export type GitlabPipeline = {
67
+ image: string;
91
68
  variables: GitlabVariables;
69
+ workflow?: {
70
+ rules: GitlabRule[];
71
+ };
92
72
  stages: string[];
93
- jobs: GitlabJobs;
73
+ jobs: Record<string, GitlabJobDef>;
94
74
  };
@@ -1,3 +1,4 @@
1
1
  export * from "./config";
2
2
  export * from "./gitlab-types";
3
3
  export * from "./context";
4
+ export * from "./pipeline";
@@ -0,0 +1,88 @@
1
+ import {
2
+ Artifacts,
3
+ GitlabEnvironment,
4
+ GitlabJobCache,
5
+ GitlabRule,
6
+ GitlabVariables,
7
+ Service,
8
+ } from "./gitlab-types";
9
+
10
+ export const BASE_STAGES = [
11
+ "setup",
12
+ "test",
13
+ "build",
14
+ "deploy",
15
+ "verify",
16
+ "stop",
17
+ ] as const;
18
+ export type BaseStage = typeof BASE_STAGES[number];
19
+
20
+ export type CatladderJob<S = BaseStage> = {
21
+ /**
22
+ * the name of the job (without any env or app prefix and suffix)
23
+ */
24
+ name: string;
25
+ /**
26
+ * envMode sets the behavior of the job regarding multiple envs:
27
+ * - none: the job does not run per env, but once for all envs
28
+ * - jobPerEnv: the job runs once per env
29
+ * - stagePerEnv: the job runs once per env and is organized in its own stage. This mproves usability in gitlab, but works the same as `jobPerEnv`
30
+ */
31
+ envMode: "jobPerEnv" | "stagePerEnv" | "none";
32
+
33
+ /**
34
+ * the stage of the job
35
+ */
36
+ stage: S;
37
+ /**
38
+ * does this require another stage?
39
+ */
40
+
41
+ /**
42
+ * script to run
43
+ */
44
+ script: (string | undefined)[];
45
+
46
+ needsStages?: {
47
+ stage: S;
48
+ artifacts?: boolean;
49
+ }[];
50
+
51
+ /**
52
+ * does this require another job?
53
+ */
54
+ needs?: Array<string | { job: string; artifacts: boolean }>;
55
+ /**
56
+ * cache config, we use here the same shape as gitlab itself
57
+ */
58
+ cache?: GitlabJobCache | GitlabJobCache[];
59
+
60
+ /**
61
+ * job artifacts, we also use gitlab shape here
62
+ */
63
+ artifacts?: Artifacts;
64
+
65
+ /**
66
+ * additional services, mainly used for docker
67
+ */
68
+ services?: Service[];
69
+
70
+ /**
71
+ * image to use
72
+ */
73
+ image?: string;
74
+
75
+ /**
76
+ * variables to pass
77
+ */
78
+ variables: GitlabVariables;
79
+
80
+ /**
81
+ * whether failures are allowed
82
+ */
83
+ allow_failure?: boolean;
84
+
85
+ environment?: GitlabEnvironment;
86
+
87
+ rules?: GitlabRule[];
88
+ };
@@ -0,0 +1,11 @@
1
+ import { GitlabJobDef, GitlabPipeline } from "./gitlab-types";
2
+
3
+ export type PipelineType = "gitlab";
4
+
5
+ export type PipelineJob<T extends PipelineType> = T extends "gitlab"
6
+ ? GitlabJobDef
7
+ : never;
8
+
9
+ export type Pipeline<T extends PipelineType> = T extends "gitlab"
10
+ ? GitlabPipeline
11
+ : never;