@catladder/pipeline 0.0.2 → 0.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@catladder/pipeline",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "scripts": {
5
5
  "prepack": "yarn tsc"
6
6
  },
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ts-node
2
+ import { writeFileSync } from "fs";
3
+ import { createPipeline } from "./magic/createPipeline";
4
+ import sampleConfig from "./magic/sample-config";
5
+
6
+ createPipeline("taggedRelease", sampleConfig).then((mainPipeline) => {
7
+ writeFileSync(`__pipeline.yml`, JSON.stringify(mainPipeline, null, 2), {
8
+ encoding: "utf-8",
9
+ });
10
+ });
@@ -1,8 +1,9 @@
1
1
  #!/bin/bash
2
- kubectl create secret -n "$NAMESPACE" \
2
+ echo "KUBE_NAMESPACE: '$KUBE_NAMESPACE', IMAGE_PULL_SECRET: '$IMAGE_PULL_SECRET'"
3
+ kubectl create secret -n "$KUBE_NAMESPACE" \
3
4
  docker-registry $IMAGE_PULL_SECRET \
4
5
  --docker-server="$CI_REGISTRY" \
5
6
  --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \
6
7
  --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \
7
8
  --docker-email="$GITLAB_USER_EMAIL" \
8
- -o yaml --dry-run | kubectl replace -n "$NAMESPACE" --force -f -
9
+ -o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f -
@@ -1,4 +1,4 @@
1
1
  #!/bin/bash
2
2
  RELEASE_NAME=${CUSTOMER_NAME}-${APP_NAME}-${CI_ENVIRONMENT_SLUG}
3
3
  echo "Delete $RELEASE_NAME"
4
- helm3 uninstall "$RELEASE_NAME" --namespace "$NAMESPACE"
4
+ helm3 uninstall "$RELEASE_NAME" --namespace "$KUBE_NAMESPACE"
@@ -1,7 +1,6 @@
1
1
  #!/bin/bash
2
2
 
3
3
  echo "Deploy to kubernetes"
4
- RELEASE_NAME=${CUSTOMER_NAME}-${APP_NAME}-${CI_ENVIRONMENT_SLUG}
5
4
  echo "Release: $RELEASE_NAME"
6
5
  echo "URL (canonical): $HOST_CANONICAL"
7
6
  #helm3 init --client-only --stable-repo-url=https://charts.helm.sh/stable
@@ -16,10 +15,9 @@ readarray -t helmArgsArray <<<"$helmArgsEvaluated"
16
15
  #Log the time until selfdestruct in case of review environment
17
16
  if [[ ${ENV_SHORT} == "review" ]]; then echo "NOTE - This deployment will stop itself after 2 weeks"; fi
18
17
 
19
- echo "reading values files from $VALUES_PATH for $ENV_SHORT"
20
-
21
- printAllValues.ts -d $VALUES_PATH -e $ENV_SHORT
22
- printAllValues.ts -d $VALUES_PATH -e $ENV_SHORT >__all_values.yml
18
+ echo "all values"
19
+ echo "$KUBE_VALUES"
20
+ echo $KUBE_VALUES >__all_values.yml
23
21
 
24
22
  echo "doing helm upgrade"
25
23
  helm3 upgrade --install "$RELEASE_NAME" /tmp/$HELM_GITLAB_CHART_NAME \
@@ -50,8 +48,8 @@ helm3 upgrade --install "$RELEASE_NAME" /tmp/$HELM_GITLAB_CHART_NAME \
50
48
  --set gitlab.mergeRequestId="$CI_MERGE_REQUEST_IID" \
51
49
  --set gitlab.projectId="$CI_PROJECT_ID" \
52
50
  --set gitlab.env="$CI_ENVIRONMENT_SLUG" \
53
- --set namespace="$NAMESPACE" \
54
- --namespace="$NAMESPACE" \
51
+ --set namespace="$KUBE_NAMESPACE" \
52
+ --namespace="$KUBE_NAMESPACE" \
55
53
  ${helmArgsArray[@]} \
56
- --valeues __all_values.yml
54
+ --values __all_values.yml
57
55
  echo "Deployment successful 😻"
@@ -1,3 +1,3 @@
1
1
  #!/bin/bash
2
- echo "Ensure Namespace $NAMESPACE"
3
- kubectl describe namespace "$NAMESPACE" || kubectl create namespace "$NAMESPACE"
2
+ echo "Ensure Namespace $KUBE_NAMESPACE"
3
+ kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
@@ -1,8 +1,9 @@
1
- import { GitlabJobDef } from "../gitlab-types";
1
+ import { GitlabJobDef, Context } from "../types";
2
2
 
3
- export const createDockerBuildJob = ({
4
- script,
5
- }: Pick<GitlabJobDef, "script">): GitlabJobDef => {
3
+ export const createDockerBuildJob = (
4
+ context: Context,
5
+ { script }: Pick<GitlabJobDef, "script">
6
+ ): GitlabJobDef => {
6
7
  const base: Omit<GitlabJobDef, "script"> = {
7
8
  stage: "build",
8
9
  image:
@@ -15,9 +16,15 @@ export const createDockerBuildJob = ({
15
16
  },
16
17
  ],
17
18
  variables: {
19
+ APP_DIR: context.componentConfig.dir,
20
+ DOCKER_HOST: "tcp://0.0.0.0:2375",
21
+ DOCKER_TLS_CERTDIR: "",
22
+ DOCKER_DIR: ".",
23
+ IMAGE_TAG: "$CI_COMMIT_SHA",
18
24
  DOCKER_DRIVER: "overlay2",
19
- IMAGE_NAME: "$CI_REGISTRY_IMAGE/$COMPONENT_NAME",
20
- CACHE_IMAGE: "$CI_REGISTRY_IMAGE/$COMPONENT_NAME/cache:cache",
25
+ IMAGE_NAME: "$CI_REGISTRY_IMAGE/" + context.componentName,
26
+ CACHE_IMAGE:
27
+ "$CI_REGISTRY_IMAGE/" + context.componentName + "/cache:cache",
21
28
  },
22
29
  };
23
30
 
@@ -0,0 +1,16 @@
1
+ import { GitlabJobs } from "../types/gitlab-types";
2
+ import { Context } from "../types/context";
3
+ import { createNodeJobs, getNodeDefaults } 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
+ };
11
+
12
+ export const getBuildDefaults: {
13
+ [type in BuildConfig["type"]]: () => Partial<BuildConfig>;
14
+ } = {
15
+ node: getNodeDefaults,
16
+ };
@@ -3,21 +3,21 @@ import {
3
3
  GitlabJobCache,
4
4
  GitlabJobs,
5
5
  Retry,
6
- } from "../gitlab-types";
7
- import { Context } from "../context";
6
+ } from "../types/gitlab-types";
7
+ import { Context } from "../types/context";
8
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
- ];
9
+ import { BuildConfigNode, isOfType } from "./types";
15
10
 
16
11
  const baseRetry: Retry = {
17
12
  max: 2,
18
13
  when: ["runner_system_failure", "stuck_or_timeout_failure"],
19
14
  };
20
15
 
16
+ const getYarnInstall = (context: Context) => [
17
+ `cd ${context.componentConfig.dir}`,
18
+ "if [ -f ./.nvmrc ]; then source /root/.nvm/nvm.sh && nvm install <<< .nvmrc; fi",
19
+ "yarn install --frozen-lockfile",
20
+ ];
21
21
  const createNodeTestJobs = (context: Context): GitlabJobs => {
22
22
  const cache: GitlabJobCache[] = [
23
23
  {
@@ -26,6 +26,7 @@ const createNodeTestJobs = (context: Context): GitlabJobs => {
26
26
  paths: ["node_modules", "**/node_modules/"],
27
27
  },
28
28
  ];
29
+
29
30
  const base: Omit<GitlabJobDef, "script"> = {
30
31
  variables: {
31
32
  APP_PATH: context.componentConfig.dir,
@@ -36,50 +37,60 @@ const createNodeTestJobs = (context: Context): GitlabJobs => {
36
37
  needs: [],
37
38
  retry: baseRetry,
38
39
  };
40
+ const yarnInstall = getYarnInstall(context);
39
41
  return [
40
42
  {
41
- name: "test",
42
- env: false,
43
- job: () => ({
43
+ name: "audit",
44
+ perEnv: false,
45
+ job: {
44
46
  ...base,
45
- script: [...yarnInstall, "yarn test"],
46
- }),
47
+ script: [...yarnInstall, "yarn audit"],
48
+ allow_failure: true,
49
+ },
47
50
  },
48
51
  {
49
52
  name: "lint",
50
- env: false,
51
- job: () => ({
53
+ perEnv: false,
54
+ job: {
52
55
  ...base,
53
56
  script: [...yarnInstall, "yarn lint"],
54
- }),
57
+ },
55
58
  },
56
59
  {
57
- name: "audit",
58
- env: false,
59
- job: () => ({
60
+ name: "test",
61
+ perEnv: false,
62
+ job: {
60
63
  ...base,
61
- script: [...yarnInstall, "yarn audit"],
62
- }),
64
+ script: [...yarnInstall, "yarn test"],
65
+ },
63
66
  },
64
67
  ];
65
68
  };
66
69
 
67
70
  const createNodeBuildJobs = (context: Context): GitlabJobs => {
71
+ const buildConfig = context.componentConfig.build;
72
+ if (!isOfType(buildConfig, "node")) {
73
+ // should not happen
74
+ throw new Error("deploy config is not kubernetes");
75
+ }
76
+
68
77
  const buildInfo = [
69
78
  ". getCommitInfo", // TODO: inline
70
- `echo '{"id":"'$BUILD_ID'","commit":"'$BUILD_COMMIT'","tag":"'$BUILD_TAG'","time":"'$BUILD_TIME'"}' > $APP_DIR/__build_info.json`,
79
+ `echo '{"id":"'$BUILD_ID'","commit":"'$BUILD_COMMIT'","tag":"'$BUILD_TAG'","time":"'$BUILD_TIME'"}' > ${context.componentConfig.dir}/__build_info.json`,
71
80
  ];
72
81
 
82
+ const yarnInstall = getYarnInstall(context);
83
+
73
84
  return [
74
85
  {
75
86
  name: "app-build",
76
- env: true,
77
- job: (environment) => ({
78
- variables: environment.variables,
87
+ job: {
88
+ needs: [],
89
+ variables: context.environment.variables,
79
90
  retry: baseRetry,
80
91
  interruptible: true,
81
92
  stage: "build",
82
- script: [...buildInfo, ...yarnInstall, "yarn build"],
93
+ script: [...buildInfo, ...yarnInstall, buildConfig.buildCommand!],
83
94
  artifacts: {
84
95
  paths: [
85
96
  context.componentConfig.dir + "/__build_info.json",
@@ -87,21 +98,33 @@ const createNodeBuildJobs = (context: Context): GitlabJobs => {
87
98
  context.componentConfig.dir + "/.next",
88
99
  ],
89
100
  },
90
- }),
101
+ },
91
102
  },
92
103
  {
93
104
  name: "docker-build",
94
- env: true,
95
- job: () => ({
96
- ...createDockerBuildJob({
97
- script: ["ensureNodeDockerfile"], // TOOD: inline
105
+
106
+ job: {
107
+ ...createDockerBuildJob(context, {
108
+ script: [
109
+ buildConfig.runtime === "static"
110
+ ? "ensureNginxDockerfile"
111
+ : "ensureNodeDockerfile",
112
+ ], // TOOD: inline
98
113
  }),
99
114
  needs: ["app-build"],
100
- }),
115
+ },
101
116
  },
102
117
  ];
103
118
  };
104
119
 
105
120
  export const createNodeJobs = (context: Context): GitlabJobs => {
106
- return [...createNodeBuildJobs(context), ...createNodeTestJobs(context)];
121
+ return [...createNodeTestJobs(context), ...createNodeBuildJobs(context)];
122
+ };
123
+
124
+ export const getNodeDefaults = (): Partial<BuildConfigNode> => {
125
+ return {
126
+ startCommand: "yarn start",
127
+ runtime: "dynamic",
128
+ buildCommand: "yarn build",
129
+ };
107
130
  };
@@ -0,0 +1,17 @@
1
+ export type BuildConfigBase = {
2
+ startCommand?: string;
3
+ };
4
+ export type BuildConfigNode = {
5
+ type: "node";
6
+ runtime?: "dynamic" | "static"; // defaults to dynamic
7
+ buildCommand?: string;
8
+ } & BuildConfigBase;
9
+
10
+ export type BuildConfig = BuildConfigNode;
11
+
12
+ export const isOfType = <T extends BuildConfig["type"]>(
13
+ t: BuildConfig,
14
+ type: T
15
+ ): t is Extract<BuildConfig, { type: T }> => {
16
+ return t.type === type;
17
+ };
@@ -1,10 +1,13 @@
1
1
  import { merge } from "lodash";
2
2
  import slugify from "slugify";
3
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";
4
+
5
+ import { Config, DefaultEnvConfig } from "../types/config";
6
+ import { CommitInfo, Context, Environment } from "../types/context";
7
+ import { deployJobCreators, getDeployDefaults } from "../deploy";
8
+ import { buildJobCreators, getBuildDefaults } from "../build";
9
+ import { BuildConfig } from "../build/types";
10
+ import { DeployConfig } from "../deploy/types";
8
11
 
9
12
  const createRawJobs = (context: Context): GitlabJobs => {
10
13
  const buildJobs =
@@ -18,7 +21,8 @@ const createRawJobs = (context: Context): GitlabJobs => {
18
21
  const getEnvironment = (
19
22
  config: Config,
20
23
  componentName: string,
21
- env: string
24
+ env: string,
25
+ commitInfo: CommitInfo
22
26
  ): Environment => {
23
27
  const componentConfig = config.components[componentName];
24
28
  if (!componentConfig) {
@@ -38,7 +42,7 @@ const getEnvironment = (
38
42
  const referenced = Object.entries(referencedRaw).reduce(
39
43
  (acc, [otherApp, mapping]) => {
40
44
  // TODO: prevent infinit looop
41
- const { variables } = getEnvironment(config, otherApp, env);
45
+ const { variables } = getEnvironment(config, otherApp, env, commitInfo);
42
46
 
43
47
  return Object.fromEntries(
44
48
  Object.entries(mapping).map(([ourKey, otherKey]) => [
@@ -53,27 +57,29 @@ const getEnvironment = (
53
57
  const envType = componentConfig.env?.[env]?.type ?? env;
54
58
  const environmentName =
55
59
  envType === "review"
56
- ? `${env}-${componentName}/$CI_COMMIT_REF_NAME`
60
+ ? `${env}-${componentName}/${commitInfo.refName}`
57
61
  : `${env}-${componentName}`;
58
62
 
59
63
  const KUBE_APP_NAME =
60
64
  envType === "review"
61
- ? `${componentName}-$CI_COMMIT_REF_SLUG`
65
+ ? `${componentName}-${commitInfo.refSlug}`
62
66
  : componentName;
63
67
 
64
68
  const KUBE_NAMESPACE = `${config.customerName}-${config.appName}-${env}`;
65
69
 
66
70
  const APP_SLUG = slugify(KUBE_APP_NAME);
67
71
 
68
- const CANONICAL_HOST = `${config.appName}-${APP_SLUG}.${config.customerName}.panter.cloud`;
72
+ const HOST_CANONICAL = `${config.appName}-${APP_SLUG}.${config.customerName}.panter.cloud`;
69
73
 
70
- const hostname = mergedConfig.hostname ?? CANONICAL_HOST;
74
+ const hostname = mergedConfig.hostname ?? HOST_CANONICAL;
71
75
  const url = `https://${hostname}`;
72
76
 
73
77
  const predefinedVariables = {
74
- CANONICAL_URL: url,
78
+ HOST_CANONICAL,
75
79
  ROOT_URL: url,
76
80
  KUBE_NAMESPACE,
81
+ KUBE_APP_NAME,
82
+ ENV_SHORT: env,
77
83
  };
78
84
  const variables = {
79
85
  ...predefinedVariables,
@@ -83,6 +89,7 @@ const getEnvironment = (
83
89
  };
84
90
 
85
91
  return {
92
+ hostname,
86
93
  fullName: environmentName,
87
94
  shortName: env,
88
95
  url: url,
@@ -92,16 +99,35 @@ const getEnvironment = (
92
99
 
93
100
  export const createContext = (
94
101
  componentName: string,
95
- config: Config
102
+ config: Config,
103
+ env: string
96
104
  ): Context => {
97
- const componentConfig = config.components[componentName];
98
- if (!componentConfig) {
105
+ const rawConfig = config.components[componentName];
106
+ if (!rawConfig) {
99
107
  throw new Error("unknown component " + componentName);
100
108
  }
109
+ // envs can override the config
110
+ const envConfig = rawConfig.env?.[env] ?? {};
111
+ const componentConfigWithoutDefaults = merge({}, rawConfig, envConfig);
112
+ // fill in defaults of build and deploy
113
+ const defaults: {
114
+ build: Partial<BuildConfig>;
115
+ deploy: Partial<DeployConfig>;
116
+ } = {
117
+ build: getBuildDefaults[componentConfigWithoutDefaults.build.type](),
118
+ deploy: getDeployDefaults[componentConfigWithoutDefaults.deploy.type](),
119
+ };
120
+ const componentConfig = merge({}, componentConfigWithoutDefaults, defaults);
121
+ const commit = {
122
+ refName: process.env.CI_COMMIT_REF_NAME ?? "unknown",
123
+ refSlug: process.env.CI_COMMIT_REF_SLUG ?? "unknown",
124
+ };
101
125
  return {
102
126
  fullConfig: config,
103
127
  componentConfig,
104
128
  componentName,
129
+ environment: getEnvironment(config, componentName, env, commit),
130
+ commit: commit,
105
131
  };
106
132
  };
107
133
 
@@ -131,31 +157,18 @@ export const createJobs = (
131
157
  config: Config,
132
158
  componentName: string
133
159
  ): 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
- }
160
+ return envs.reduce((acc, env) => {
161
+ const context = createContext(componentName, config, env);
162
+ const jobs = createRawJobs(context);
151
163
  return {
152
164
  ...acc,
153
- ...environments.reduce((envacc, environment) => {
154
- const def = job.job(environment);
165
+ ...jobs.reduce((acc, { name, job, perEnv = true }) => {
166
+ const def = job;
167
+
155
168
  return {
156
169
  ...acc,
157
- [getFullJobName(job.name, componentName, environment.shortName)]:
158
- replaceReferences(def, componentName, environment.shortName),
170
+ [getFullJobName(name, componentName, perEnv ? env : undefined)]:
171
+ replaceReferences(def, componentName, perEnv ? env : undefined),
159
172
  };
160
173
  }, {}),
161
174
  };
@@ -1,5 +1,6 @@
1
1
  import { createJobs } from "./createChildPipeline";
2
2
  import { Config, ENV_TYPES, PipelineTrigger } from "./types/config";
3
+ import { GitlabJobDef } from "./types/gitlab-types";
3
4
 
4
5
  export const createPipeline = async (
5
6
  trigger: PipelineTrigger,
@@ -12,12 +13,15 @@ export const createPipeline = async (
12
13
 
13
14
  // 2. write the triggering pipeline
14
15
 
15
- const jobs = components.reduce((acc, componentName) => {
16
- return {
17
- ...acc,
18
- ...createJobs(envs, config, componentName),
19
- };
20
- }, {});
16
+ const jobs = components.reduce<Record<string, GitlabJobDef>>(
17
+ (acc, componentName) => {
18
+ return {
19
+ ...acc,
20
+ ...createJobs(envs, config, componentName),
21
+ };
22
+ },
23
+ {}
24
+ );
21
25
 
22
26
  const rules = [
23
27
  // same as rules "always", but with `changes` to only trigger changed branches
@@ -0,0 +1,19 @@
1
+ import { GitlabJobs } from "../types/gitlab-types";
2
+ import { ComponentConfig } from "../types/config";
3
+ import { Context } from "../types/context";
4
+ import { createKubernetesDeployJobs, getKubernetesDefault } from "./kubernetes";
5
+ import { DeployConfig } from "./types";
6
+
7
+ export const deployJobCreators: {
8
+ [type in DeployConfig["type"]]: (context: Context) => GitlabJobs;
9
+ } = {
10
+ kubernetes: createKubernetesDeployJobs,
11
+ serverless: () => [],
12
+ };
13
+
14
+ export const getDeployDefaults: {
15
+ [type in DeployConfig["type"]]: () => Partial<DeployConfig>;
16
+ } = {
17
+ kubernetes: getKubernetesDefault,
18
+ serverless: () => ({}),
19
+ };
@@ -0,0 +1,65 @@
1
+ import { GitlabJobs } from "../types/gitlab-types";
2
+ import { Context } from "../types/context";
3
+ import { DeployConfigKubernetes, 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
+ const defaltKubeValues = {
13
+ application: {
14
+ hostname: context.environment.hostname,
15
+ command: context.componentConfig.build.startCommand,
16
+ },
17
+ };
18
+ const kubeValues = {
19
+ ...defaltKubeValues,
20
+ ...deployConfig.values,
21
+ }; // TODO: merge with some defaults
22
+ return [
23
+ {
24
+ name: "deploy-to-kubernetes",
25
+
26
+ job: {
27
+ variables: {
28
+ ...context.environment.variables,
29
+
30
+ // TODO: refactor and unify with other stages
31
+ HELM_EXPERIMENTAL_OCI: "1",
32
+ IMAGE_PULL_SECRET: `gitlab-registry-${context.componentName}`,
33
+ KUBE_VALUES: JSON.stringify(kubeValues),
34
+ HELM_GITLAB_CHART_PATH: "catladder/helm-charts",
35
+ HELM_GITLAB_CHART_NAME: "the-panter-chart",
36
+ COMPONENT_NAME: context.componentName,
37
+ // TODO: unify with docker build stage
38
+ IMAGE_TAG: "$CI_COMMIT_SHA",
39
+ HELM_GITLAB_CHART_VERSION: "3.2.0", // TODO, we could actually just ship the chart directly here
40
+ RELEASE_NAME: `${context.fullConfig.customerName}-${context.fullConfig.appName}-$CI_ENVIRONMENT_SLUG`,
41
+ },
42
+ stage: "deploy",
43
+ dependencies: [],
44
+ script: [
45
+ "kubernetesEnsureNamespace",
46
+ "kubernetesCreateSecret",
47
+ "kubernetesDeploy",
48
+ ],
49
+ environment: {
50
+ name: context.environment.fullName,
51
+ url: context.environment.url,
52
+ kubernetes: {
53
+ namespace: context.environment.variables.KUBE_NAMESPACE,
54
+ },
55
+ },
56
+ },
57
+ },
58
+ ];
59
+ };
60
+
61
+ export const getKubernetesDefault = (): Partial<DeployConfigKubernetes> => {
62
+ return {
63
+ values: {},
64
+ };
65
+ };
File without changes
@@ -7,7 +7,7 @@ const sampleConfig: Config = {
7
7
  web: {
8
8
  dir: "web",
9
9
  build: {
10
- type: "node-static",
10
+ type: "node",
11
11
  },
12
12
  deploy: {
13
13
  type: "kubernetes",
@@ -0,0 +1,41 @@
1
+ import { Config } from "../types";
2
+ import { createPipeline } from "../createPipeline";
3
+
4
+ describe("createPipeline", () => {
5
+ describe("node-app to kuberntes", () => {
6
+ const config: Config = {
7
+ appName: "test",
8
+ customerName: "pan",
9
+ components: {
10
+ myApp: {
11
+ dir: "myapp",
12
+ build: {
13
+ type: "node",
14
+ },
15
+ deploy: {
16
+ type: "kubernetes",
17
+ },
18
+ },
19
+ },
20
+ };
21
+
22
+ it("creates a pipeline for a single app on the main branch", async () => {
23
+ const { image, stages, workflow, ...jobs } = await createPipeline(
24
+ "mainBranch",
25
+ config
26
+ );
27
+ expect(image).toEqual(
28
+ "git.panter.ch:5001/catladder/gitlab-ci/pipeline:$PIPELINE_IMAGE_TAG"
29
+ );
30
+
31
+ expect(Object.keys(jobs)).toEqual([
32
+ "myApp audit",
33
+ "myApp lint",
34
+ "myApp test",
35
+ "dev myApp app-build",
36
+ "dev myApp docker-build",
37
+ "dev myApp deploy-to-kubernetes",
38
+ ]);
39
+ });
40
+ });
41
+ });
@@ -1,5 +1,5 @@
1
- import { BuildConfig } from "./build/types";
2
- import { DeployConfig } from "./deploy/types";
1
+ import { BuildConfig } from "../build/types";
2
+ import { DeployConfig } from "../deploy/types";
3
3
 
4
4
  export type PipelineTrigger = "mainBranch" | "mr" | "taggedRelease";
5
5
 
@@ -19,7 +19,7 @@ export const ENV_TYPES = {
19
19
  } as const;
20
20
  type EnvType = keyof typeof ENV_TYPES;
21
21
 
22
- type DefaultEnvConfig = {
22
+ export type DefaultEnvConfig = {
23
23
  deploy: DeployConfig;
24
24
  build: BuildConfig;
25
25
  vars?: {
@@ -1,13 +1,21 @@
1
- import { ComponentConfig, Config, PipelineTrigger } from "./config";
1
+ import { ComponentConfig, Config } from "./config";
2
2
 
3
3
  export type Environment = {
4
+ hostname: string;
4
5
  fullName: string;
5
6
  shortName: string;
6
7
  url: string;
7
8
  variables: Record<string, string>;
8
9
  };
10
+
11
+ export type CommitInfo = {
12
+ refName: string;
13
+ refSlug: string;
14
+ };
9
15
  export type Context = {
10
16
  componentConfig: ComponentConfig;
11
17
  componentName: string;
12
18
  fullConfig: Config;
19
+ environment: Environment;
20
+ commit: CommitInfo;
13
21
  };
@@ -1,5 +1,3 @@
1
- import { Environment } from "./context";
2
-
3
1
  export type GitlabStage =
4
2
  | "setup"
5
3
  | "test"
@@ -52,22 +50,16 @@ export type GitlabJobDef = {
52
50
  variables?: GitlabVariables;
53
51
  dependencies?: string[];
54
52
  environment?: GitlabEnvironment;
53
+ allow_failure?: boolean;
55
54
  };
56
55
 
57
56
  export type GitlabVariables = Record<string, string | undefined>;
58
57
 
59
- export type JobEnvDependent = {
60
- name: string;
61
- env: true;
62
- job: (environment: Environment) => GitlabJobDef;
63
- };
64
-
65
- export type JobNonEnv = {
58
+ export type GitlabJob = {
66
59
  name: string;
67
- env: false;
68
- job: () => GitlabJobDef;
60
+ perEnv?: boolean;
61
+ job: GitlabJobDef;
69
62
  };
70
- export type GitlabJob = JobEnvDependent | JobNonEnv;
71
63
  export type GitlabJobs = GitlabJob[];
72
64
 
73
65
  export type GitlabPipeline = {
package/scripts/magic.ts CHANGED
@@ -17,16 +17,16 @@ const isHotfixBranch = false; // TODO: $CI_COMMIT_BRANCH =~ /^[0-9]+\.([0-9]+|x
17
17
  const isMergeRequest = Boolean(CI_MERGE_REQUEST_ID);
18
18
  const isTaggedRelease = Boolean(CI_COMMIT_TAG);
19
19
 
20
- const fullName = (ext: string) => "catladder." + ext;
20
+ const fullPath = (ext: string) => process.cwd() + "/catladder." + ext;
21
21
  const readConfig = () => {
22
22
  const found = ["ts", "js", "yml", "yaml"].find((extension) =>
23
- existsSync(fullName(extension))
23
+ existsSync(fullPath(extension))
24
24
  );
25
25
  if (found) {
26
26
  if (found === "ts" || found === "js") {
27
- return require(fullName(found));
27
+ return require(fullPath(found)).default;
28
28
  } else {
29
- return parse(fullName(found));
29
+ return parse(fullPath(found));
30
30
  }
31
31
  }
32
32
  };
@@ -1,6 +1,8 @@
1
1
  import { extractAllValues } from "./extractAllValues";
2
2
  import slugify from "slugify";
3
3
 
4
+ // deprecated
5
+
4
6
  type EnvVars = Record<string, string>;
5
7
  export const extractBuildEnv = async (
6
8
  componentName: string,
@@ -1,11 +0,0 @@
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
- };
@@ -1,9 +0,0 @@
1
- export type BuildConfigNode = {
2
- type: "node";
3
- };
4
-
5
- export type BuildConfigNodeStatic = {
6
- type: "node-static";
7
- };
8
-
9
- export type BuildConfig = BuildConfigNode | BuildConfigNodeStatic;
@@ -1,11 +0,0 @@
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
- };
@@ -1,38 +0,0 @@
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
- };