@catladder/pipeline 1.170.0 → 2.0.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 (194) hide show
  1. package/dist/bash/BashExpression.d.ts +2 -6
  2. package/dist/bash/BashExpression.js +5 -15
  3. package/dist/bash/bashEscape.d.ts +34 -0
  4. package/dist/bash/bashEscape.js +114 -0
  5. package/dist/bash/bashYaml.js +25 -2
  6. package/dist/bash/getInjectVarsScript.js +4 -2
  7. package/dist/bash/index.d.ts +2 -0
  8. package/dist/bash/index.js +26 -0
  9. package/dist/build/base/createAppBuildJob.js +3 -3
  10. package/dist/build/base/writeDotEnv.js +6 -4
  11. package/dist/build/custom/testJob.js +12 -12
  12. package/dist/build/docker.d.ts +3 -3
  13. package/dist/build/node/buildJob.js +1 -1
  14. package/dist/build/node/cache.d.ts +2 -4
  15. package/dist/build/node/cache.js +3 -24
  16. package/dist/build/node/testJob.js +11 -11
  17. package/dist/build/rails/build.js +1 -1
  18. package/dist/build/rails/test.js +8 -8
  19. package/dist/build/types.d.ts +0 -10
  20. package/dist/constants.js +1 -1
  21. package/dist/context/createComponentContext.js +0 -1
  22. package/dist/context/getEnvConfig.js +2 -1
  23. package/dist/context/getEnvironment.js +1 -2
  24. package/dist/context/getEnvironmentVariables.d.ts +5 -6
  25. package/dist/context/getEnvironmentVariables.js +50 -38
  26. package/dist/deploy/base/deploy.js +3 -3
  27. package/dist/deploy/cloudRun/createJobs/getCloudRunDeployScripts.js +2 -2
  28. package/dist/deploy/cloudRun/index.js +2 -2
  29. package/dist/deploy/cloudRun/utils/getServiceName.d.ts +1 -1
  30. package/dist/deploy/kubernetes/cloudSql/index.d.ts +2 -2
  31. package/dist/deploy/kubernetes/cloudSql/index.js +3 -14
  32. package/dist/deploy/kubernetes/deployJob.js +1 -3
  33. package/dist/deploy/kubernetes/index.js +2 -2
  34. package/dist/deploy/kubernetes/kubeEnv.d.ts +3 -3
  35. package/dist/deploy/kubernetes/kubeValues.d.ts +3 -4
  36. package/dist/deploy/kubernetes/kubeValues.js +2 -3
  37. package/dist/deploy/types/base.d.ts +0 -6
  38. package/dist/deploy/types/kubernetes.d.ts +1 -34
  39. package/dist/globalScriptFunctions/index.d.ts +14 -0
  40. package/dist/globalScriptFunctions/index.js +37 -0
  41. package/dist/index.d.ts +3 -1
  42. package/dist/index.js +3 -1
  43. package/dist/pipeline/gitlab/createGitlabJobs.js +3 -5
  44. package/dist/pipeline/gitlab/createGitlabPipeline.d.ts +1 -0
  45. package/dist/pipeline/gitlab/createGitlabPipeline.js +38 -2
  46. package/dist/pipeline/packageManager.js +1 -1
  47. package/dist/runner/index.d.ts +1 -1
  48. package/dist/tsconfig.tsbuildinfo +1 -1
  49. package/dist/types/config.d.ts +6 -9
  50. package/dist/types/context.d.ts +2 -9
  51. package/dist/types/gitlab-types.d.ts +1 -0
  52. package/dist/types/jobs.d.ts +0 -8
  53. package/dist/utils/gitlab.js +4 -1
  54. package/dist/utils/writeFiles.js +1 -7
  55. package/dist/variables/VariableValue.d.ts +3 -0
  56. package/dist/variables/VariableValue.js +5 -0
  57. package/dist/variables/VariableValueContainingReferences.d.ts +24 -0
  58. package/dist/variables/VariableValueContainingReferences.js +97 -0
  59. package/dist/variables/__tests__/resolveAllReferences.test.js +219 -0
  60. package/dist/variables/__tests__/resolveAllReferencesOnce.test.d.ts +1 -0
  61. package/dist/variables/__tests__/resolveAllReferencesOnce.test.js +171 -0
  62. package/dist/variables/__tests__/resolveReferencesOnce.test.d.ts +1 -0
  63. package/dist/variables/__tests__/resolveReferencesOnce.test.js +202 -0
  64. package/dist/variables/__tests__/variableValue.test.d.ts +1 -0
  65. package/dist/variables/__tests__/variableValue.test.js +36 -0
  66. package/dist/variables/resolveAllReferences.d.ts +3 -0
  67. package/dist/{bash/replaceAsync.js → variables/resolveAllReferences.js} +60 -41
  68. package/dist/variables/resolveAllReferencesOnce.d.ts +5 -0
  69. package/dist/variables/resolveAllReferencesOnce.js +191 -0
  70. package/dist/variables/resolveReferencesOnce.d.ts +8 -0
  71. package/dist/variables/resolveReferencesOnce.js +22 -0
  72. package/examples/__snapshots__/cloud-run-http2.test.ts.snap +312 -238
  73. package/examples/__snapshots__/cloud-run-memory-limit.test.ts.snap +312 -238
  74. package/examples/__snapshots__/cloud-run-meteor-with-worker.test.ts.snap +312 -222
  75. package/examples/__snapshots__/cloud-run-nextjs.test.ts.snap +1436 -0
  76. package/examples/__snapshots__/cloud-run-no-cpu-throttling.test.ts.snap +312 -238
  77. package/examples/__snapshots__/cloud-run-no-service.test.ts.snap +316 -238
  78. package/examples/__snapshots__/cloud-run-non-public.test.ts.snap +312 -238
  79. package/examples/__snapshots__/cloud-run-post-stop-job.test.ts.snap +313 -238
  80. package/examples/__snapshots__/cloud-run-service-custom-vpc-connector.test.ts.snap +312 -238
  81. package/examples/__snapshots__/cloud-run-service-custom-vpc.test.ts.snap +312 -238
  82. package/examples/__snapshots__/cloud-run-service-gen2.test.ts.snap +312 -238
  83. package/examples/__snapshots__/cloud-run-service-increase-timout.test.ts.snap +312 -238
  84. package/examples/__snapshots__/cloud-run-service-with-volumes.test.ts.snap +316 -238
  85. package/examples/__snapshots__/cloud-run-storybook.test.ts.snap +294 -220
  86. package/examples/__snapshots__/cloud-run-with-ngnix.test.ts.snap +312 -238
  87. package/examples/__snapshots__/cloud-run-with-sql-reuse-db.test.ts.snap +652 -486
  88. package/examples/__snapshots__/cloud-run-with-sql.test.ts.snap +282 -288
  89. package/examples/__snapshots__/cloud-run-with-worker.test.ts.snap +312 -238
  90. package/examples/__snapshots__/custom-build-job-with-tests.test.ts.snap +284 -194
  91. package/examples/__snapshots__/custom-build-job.test.ts.snap +278 -188
  92. package/examples/__snapshots__/custom-deploy.test.ts.snap +220 -154
  93. package/examples/__snapshots__/custom-envs.test.ts.snap +216 -126
  94. package/examples/__snapshots__/custom-sbom-java.test.ts.snap +278 -188
  95. package/examples/__snapshots__/git-submodule.test.ts.snap +312 -238
  96. package/examples/__snapshots__/kubernetes-application-customization.test.ts.snap +231 -253
  97. package/examples/__snapshots__/kubernetes-with-cloud-sql.test.ts.snap +240 -262
  98. package/examples/__snapshots__/kubernetes-with-jobs.test.ts.snap +504 -506
  99. package/examples/__snapshots__/kubernetes-with-mongodb.test.ts.snap +239 -261
  100. package/examples/__snapshots__/local-dot-env.test.ts.snap +236 -238
  101. package/examples/__snapshots__/meteor-kubernetes.test.ts.snap +236 -242
  102. package/examples/__snapshots__/multiline-var.test.ts.snap +1355 -973
  103. package/examples/__snapshots__/native-app.test.ts.snap +438 -392
  104. package/examples/__snapshots__/node-build-with-custom-image.test.ts.snap +312 -238
  105. package/examples/__snapshots__/node-build-with-docker-additions.test.ts.snap +312 -238
  106. package/examples/__snapshots__/rails-k8s-with-worker-dockerfile.test.ts.snap +186 -188
  107. package/examples/__snapshots__/rails-k8s-with-worker.test.ts.snap +162 -164
  108. package/examples/__snapshots__/referencing-other-vars.test.ts.snap +4741 -0
  109. package/examples/__snapshots__/wait-for-other-deploy.test.ts.snap +330 -228
  110. package/examples/__snapshots__/{workspace-api-www-custom-cache.test.ts.snap → workspace-api-www-turbo-cache.test.ts.snap} +457 -499
  111. package/examples/__snapshots__/workspace-api-www.test.ts.snap +452 -482
  112. package/examples/{workspace-api-www-custom-cache.test.ts → cloud-run-nextjs.test.ts} +2 -2
  113. package/examples/cloud-run-nextjs.ts +28 -0
  114. package/examples/cloud-run-with-sql.ts +0 -1
  115. package/examples/kubernetes-application-customization.ts +1 -0
  116. package/examples/kubernetes-with-cloud-sql.ts +1 -0
  117. package/examples/kubernetes-with-jobs.ts +1 -0
  118. package/examples/kubernetes-with-mongodb.ts +1 -0
  119. package/examples/meteor-kubernetes.ts +1 -1
  120. package/examples/native-app.ts +10 -7
  121. package/examples/rails-k8s-with-worker.ts +7 -1
  122. package/examples/{kubernetes-with-cloud-sql-legacy.test.ts → referencing-other-vars.test.ts} +2 -2
  123. package/examples/referencing-other-vars.ts +83 -0
  124. package/examples/workspace-api-www-turbo-cache.test.ts +11 -0
  125. package/examples/{workspace-api-www-custom-cache.ts → workspace-api-www-turbo-cache.ts} +4 -3
  126. package/examples/workspace-api-www.ts +3 -2
  127. package/package.json +2 -6
  128. package/src/bash/BashExpression.ts +10 -13
  129. package/src/bash/bashEscape.ts +158 -0
  130. package/src/bash/bashYaml.ts +36 -2
  131. package/src/bash/getInjectVarsScript.ts +11 -2
  132. package/src/bash/index.ts +2 -0
  133. package/src/build/base/createAppBuildJob.ts +0 -1
  134. package/src/build/base/writeDotEnv.ts +6 -6
  135. package/src/build/custom/testJob.ts +0 -1
  136. package/src/build/node/buildJob.ts +2 -2
  137. package/src/build/node/cache.ts +0 -29
  138. package/src/build/node/testJob.ts +0 -1
  139. package/src/build/rails/build.ts +0 -1
  140. package/src/build/rails/test.ts +0 -1
  141. package/src/build/types.ts +0 -13
  142. package/src/context/createComponentContext.ts +0 -1
  143. package/src/context/getEnvConfig.ts +2 -2
  144. package/src/context/getEnvironment.ts +1 -1
  145. package/src/context/getEnvironmentContext.ts +1 -1
  146. package/src/context/getEnvironmentVariables.ts +44 -51
  147. package/src/deploy/base/deploy.ts +1 -1
  148. package/src/deploy/cloudRun/createJobs/getCloudRunDeployScripts.ts +4 -12
  149. package/src/deploy/cloudRun/index.ts +2 -2
  150. package/src/deploy/kubernetes/cloudSql/index.ts +3 -16
  151. package/src/deploy/kubernetes/deployJob.ts +0 -2
  152. package/src/deploy/kubernetes/index.ts +2 -2
  153. package/src/deploy/kubernetes/kubeEnv.ts +3 -3
  154. package/src/deploy/kubernetes/kubeValues.ts +5 -8
  155. package/src/deploy/types/base.ts +0 -6
  156. package/src/deploy/types/kubernetes.ts +1 -36
  157. package/src/globalScriptFunctions/index.ts +30 -0
  158. package/src/index.ts +2 -0
  159. package/src/pipeline/gitlab/createGitlabJobs.ts +1 -4
  160. package/src/pipeline/gitlab/createGitlabPipeline.ts +8 -1
  161. package/src/pipeline/packageManager.ts +7 -5
  162. package/src/runner/index.ts +0 -1
  163. package/src/types/config.ts +6 -9
  164. package/src/types/context.ts +3 -9
  165. package/src/types/gitlab-types.ts +1 -0
  166. package/src/types/jobs.ts +0 -8
  167. package/src/utils/gitlab.ts +19 -2
  168. package/src/utils/writeFiles.ts +1 -2
  169. package/src/variables/VariableValue.ts +6 -0
  170. package/src/variables/VariableValueContainingReferences.ts +89 -0
  171. package/src/variables/__tests__/resolveAllReferences.test.ts +110 -0
  172. package/src/variables/__tests__/resolveAllReferencesOnce.test.ts +64 -0
  173. package/src/variables/__tests__/resolveReferencesOnce.test.ts +117 -0
  174. package/src/variables/__tests__/variableValue.test.ts +73 -0
  175. package/src/variables/resolveAllReferences.ts +46 -0
  176. package/src/variables/resolveAllReferencesOnce.ts +44 -0
  177. package/src/variables/resolveReferencesOnce.ts +29 -0
  178. package/bin/catladder-gitlab-dev.js +0 -3
  179. package/bin/catladder-gitlab.js +0 -3
  180. package/dist/bash/replaceAsync.d.ts +0 -2
  181. package/dist/bundles/catladder-gitlab/index.js +0 -15
  182. package/dist/context/__tests__/resolveReferences.test.js +0 -368
  183. package/dist/context/resolveReferences.d.ts +0 -6
  184. package/dist/context/resolveReferences.js +0 -286
  185. package/dist/deploy/kubernetes/processSecretsAsFiles.d.ts +0 -85
  186. package/dist/deploy/kubernetes/processSecretsAsFiles.js +0 -33
  187. package/examples/__snapshots__/kubernetes-with-cloud-sql-legacy.test.ts.snap +0 -1795
  188. package/examples/kubernetes-with-cloud-sql-legacy.ts +0 -35
  189. package/scripts/bundle +0 -2
  190. package/src/bash/replaceAsync.ts +0 -54
  191. package/src/context/__tests__/resolveReferences.test.ts +0 -148
  192. package/src/context/resolveReferences.ts +0 -93
  193. package/src/deploy/kubernetes/processSecretsAsFiles.ts +0 -35
  194. /package/dist/{context/__tests__/resolveReferences.test.d.ts → variables/__tests__/resolveAllReferences.test.d.ts} +0 -0
@@ -5,7 +5,6 @@ export type RunnerImageName =
5
5
  | "jobs-meteor"
6
6
  | "jobs-testing-chrome"
7
7
  | "kubernetes"
8
- | "base-pipeline"
9
8
  | "docker-build"
10
9
  | "gcloud"
11
10
  | "semantic-release";
@@ -73,14 +73,6 @@ export type EnvVars = {
73
73
  * secret env vars. These vars can be managed with catladder/cli
74
74
  */
75
75
  secret?: string[];
76
- /**
77
- * @deprecated, use ${componentName:variableName} instead
78
- *
79
- * With fromComponents you can inject env vars from other components.
80
- */
81
- fromComponents?: {
82
- [otherApp: string]: Record<string, string>;
83
- };
84
76
  };
85
77
 
86
78
  export type DefaultEnvConfig = {
@@ -169,12 +161,14 @@ export type ComponentConfig<C extends ConfigProps = never> = {
169
161
  * Use this in combination with @dotenvx/dotenvx or node 20 to start apps locally with .env files
170
162
  * During build and runtime, it relies on the env vars set in the environment
171
163
  *
164
+ * @defaultValue true
165
+ *
172
166
  */
173
167
  dotEnv?: boolean | "local";
174
168
  /**
175
169
  * whether to create a env.d.ts localy and during build jobs
176
170
  *
177
- * Be careful, this will overwrite any existing env.d.ts file!
171
+ * @defaultValue true
178
172
  */
179
173
  envDTs?: boolean;
180
174
  } & DefaultEnvConfig;
@@ -184,6 +178,9 @@ export type ConfigProps = {
184
178
  };
185
179
 
186
180
  export type Config<C extends ConfigProps = never> = {
181
+ /**
182
+ * the pipeline type to generate, defaults to gitlab
183
+ */
187
184
  pipelineType?: PipelineType;
188
185
 
189
186
  /**
@@ -6,6 +6,7 @@ import type {
6
6
  } from "../build";
7
7
  import type { PredefinedVariables, SecretEnvVar } from "../context";
8
8
  import type { DeployConfig } from "../deploy";
9
+ import type { VariableValue } from "../variables/VariableValue";
9
10
  import type {
10
11
  ComponentConfig,
11
12
  Config,
@@ -17,7 +18,7 @@ import type { PipelineType } from "./pipeline";
17
18
 
18
19
  export type UnspecifiedEnvVars = Record<
19
20
  string,
20
- StringOrBashExpression | undefined | null
21
+ VariableValue | undefined | null
21
22
  >;
22
23
 
23
24
  export type EnvironmentEnvVars<
@@ -46,10 +47,7 @@ export type Environment = {
46
47
  * the full name of the app. We use this as RELEASE_NAME in kubernetes and the service name in google cloud run
47
48
  */
48
49
  fullName: StringOrBashExpression;
49
- /**
50
- * @deprecated this is the same as context.env, use that instead
51
- */
52
- shortName: string;
50
+
53
51
  /**
54
52
  * the environment slug without component name.
55
53
  */
@@ -158,10 +156,6 @@ export type ComponentContext<
158
156
  * the name of the component
159
157
  */
160
158
  name: string;
161
- /**
162
- * @deprecated use name instead
163
- */
164
- componentName: string;
165
159
 
166
160
  /**
167
161
  * the merged component config.
@@ -66,4 +66,5 @@ export interface GitlabPipeline extends Pick<JobTemplate, "variables"> {
66
66
  };
67
67
  stages: string[];
68
68
  jobs: Record<string, GitlabJobDef>;
69
+ before_script?: string[];
69
70
  }
package/src/types/jobs.ts CHANGED
@@ -70,14 +70,6 @@ export type CatladderJob<S = BaseStage> = {
70
70
  */
71
71
  needs?: Array<CatladderJobNeed>;
72
72
 
73
- /**
74
- * @deprecated set `componentName` to `needs` if you want to wait for a job from another component
75
- */
76
- needsOtherComponent?: Array<{
77
- componentName: string;
78
- job: string;
79
- artifacts: boolean;
80
- }>;
81
73
  /**
82
74
  * cache config, we use here the same shape as gitlab itself
83
75
  */
@@ -1,3 +1,5 @@
1
+ import { registerGlobalScriptFunction } from "../globalScriptFunctions";
2
+
1
3
  export const allowFailureInScripts = (script: string[]): string[] => [
2
4
  "set +e", // disable fail job on error
3
5
  ...script,
@@ -22,12 +24,27 @@ export const repeatOnFailure = (
22
24
  `;
23
25
  };
24
26
 
27
+ const start = registerGlobalScriptFunction(
28
+ "collapseable_section_start",
29
+ `local section_title="\${1}"
30
+ local section_description="\${2:-$section_title}"
31
+ echo -e "section_start:\`date +%s\`:\${section_title}[collapsed=true]\\r\\e[0K$\{section_description}"
32
+ `,
33
+ );
34
+
35
+ const end = registerGlobalScriptFunction(
36
+ "collapseable_section_end",
37
+ `local section_title="\${1}"
38
+ echo -e "section_end:\`date +%s\`:\${section_title}\\r\\e[0K"
39
+ `,
40
+ );
41
+
25
42
  export const collapseableSection =
26
43
  (name: string, header: string) =>
27
44
  (commands: string[]): string[] => {
28
45
  return [
29
- `echo -e "\\e[0Ksection_start:$(date +%s):${name}[collapsed=true]\\r\\e[0K${header}"`,
46
+ start.invoke(`"${name}"`, `"${header}"`),
30
47
  ...commands,
31
- `echo -e "\\e[0Ksection_end:$(date +%s):${name}\\r\\e[0K"`,
48
+ end.invoke(`"${name}"`),
32
49
  ];
33
50
  };
@@ -1,11 +1,10 @@
1
1
  import { writeFile } from "fs/promises";
2
2
  import { stringify } from "yaml";
3
- import packageInfos from "../packageInfos";
4
3
 
5
4
  export const getAutoGeneratedHeader = (commentChar: string) => {
6
5
  return [
7
6
  "-------------------------------------------------",
8
- `🐱 🔨 This file is generated by catladder v${packageInfos.version}`,
7
+ `🐱 🔨 This file is generated by catladder`,
9
8
  `🚨 Do not edit this file manually 🚨`,
10
9
  "-------------------------------------------------",
11
10
  ]
@@ -0,0 +1,6 @@
1
+ import type { BashExpression } from "../bash/BashExpression";
2
+ import type { VariableValueContainingReferences } from "./VariableValueContainingReferences";
3
+ export type VariableValue =
4
+ | VariableValueContainingReferences
5
+ | BashExpression
6
+ | string;
@@ -0,0 +1,89 @@
1
+ import { BashExpression } from "../bash/BashExpression";
2
+ import type { EscapeOptions } from "../bash/bashEscape";
3
+ import { escapeBashExpression, escapeString } from "../bash/bashEscape";
4
+
5
+ export class UnresolvableReference {
6
+ constructor(public reference: VariableReference) {}
7
+ public toString() {
8
+ return `Unresolvable reference: ${this.reference.toString()}`;
9
+ }
10
+ }
11
+ export class VariableReference {
12
+ public componentName: string;
13
+ public variableName: string;
14
+
15
+ constructor(componentName: string, variableName: string) {
16
+ this.componentName = componentName;
17
+ this.variableName = variableName;
18
+ }
19
+
20
+ public toString() {
21
+ return `\${${this.componentName}:${this.variableName}}`;
22
+ }
23
+ }
24
+
25
+ type VariableValuePart =
26
+ | string
27
+ | BashExpression
28
+ | VariableReference
29
+ | UnresolvableReference;
30
+
31
+ type MaybeArray<T> = T | T[];
32
+ export class VariableValueContainingReferences {
33
+ public parts: VariableValuePart[];
34
+ constructor(
35
+ parts: MaybeArray<VariableValuePart | VariableValueContainingReferences>,
36
+ ) {
37
+ this.parts = (Array.isArray(parts) ? parts : [parts]).flatMap((part) =>
38
+ part instanceof VariableValueContainingReferences
39
+ ? part.parts
40
+ : part === ""
41
+ ? []
42
+ : [part],
43
+ );
44
+ }
45
+
46
+ public toString(
47
+ options: EscapeOptions = {
48
+ quotes: false,
49
+ },
50
+ ) {
51
+ return this.parts
52
+ .map((part) => {
53
+ if (typeof part === "string") {
54
+ return escapeString(part, options);
55
+ } else if (part instanceof BashExpression) {
56
+ return escapeBashExpression(part, options);
57
+ } else {
58
+ return part.toString();
59
+ }
60
+ })
61
+ .join("");
62
+ }
63
+ }
64
+
65
+ // regex to resolve references in catladder variables
66
+ // those expressions have the pattern ${componentName:variableName}
67
+ const REGEX = /\$\{(([^:}]+):)?([^}]+)}/gm;
68
+ export const createVariableValueContainingReferencesFromString = (
69
+ value: string,
70
+ options: { componentName: string },
71
+ ) => {
72
+ const parts: Array<VariableValuePart> = [];
73
+ let match: any;
74
+ let lastIndex = 0;
75
+ while ((match = REGEX.exec(value)) !== null) {
76
+ const [fullMatch, _, otherComponentName, variableName] = match;
77
+
78
+ parts.push(value.slice(lastIndex, match.index));
79
+ parts.push(
80
+ new VariableReference(
81
+ otherComponentName || options.componentName,
82
+ variableName,
83
+ ),
84
+ );
85
+ lastIndex = REGEX.lastIndex;
86
+ }
87
+ parts.push(value.slice(lastIndex));
88
+ return new VariableValueContainingReferences(parts);
89
+ };
@@ -0,0 +1,110 @@
1
+ import {
2
+ VariableValueContainingReferences,
3
+ createVariableValueContainingReferencesFromString,
4
+ } from "../VariableValueContainingReferences";
5
+ import { resolveAllReferences } from "../resolveAllReferences";
6
+
7
+ describe("replaceAllReferences", () => {
8
+ it("should replace all references recursivly", async () => {
9
+ const values = {
10
+ myVar: createVariableValueContainingReferencesFromString(
11
+ "the 2nd component has ${component2:variable1} and self reference ${variable2}",
12
+ {
13
+ componentName: "component1",
14
+ },
15
+ ),
16
+ myOtherVar: createVariableValueContainingReferencesFromString(
17
+ "the third component has ${component3:variable1}, isn't that cool?",
18
+ {
19
+ componentName: "component1",
20
+ },
21
+ ),
22
+ myThirdVar: createVariableValueContainingReferencesFromString(
23
+ "value from component3: ${component3:variable3}",
24
+ {
25
+ componentName: "component1",
26
+ },
27
+ ),
28
+ };
29
+
30
+ const getEnvVars = async (componentName: string) => {
31
+ return {
32
+ variable1: createVariableValueContainingReferencesFromString(
33
+ `i am variable1 from ${componentName}`,
34
+ { componentName },
35
+ ),
36
+ variable2: createVariableValueContainingReferencesFromString(
37
+ `foo from ${componentName}`,
38
+ {
39
+ componentName,
40
+ },
41
+ ),
42
+ variable3: createVariableValueContainingReferencesFromString(
43
+ `i am referencing \${component1:variable1}`,
44
+ { componentName },
45
+ ),
46
+ };
47
+ };
48
+
49
+ const result = await resolveAllReferences(values, getEnvVars);
50
+ expect(result).toEqual({
51
+ myVar: new VariableValueContainingReferences([
52
+ "the 2nd component has ",
53
+ "i am variable1 from component2",
54
+ " and self reference ",
55
+ "foo from component1",
56
+ ]),
57
+ myOtherVar: new VariableValueContainingReferences([
58
+ "the third component has ",
59
+ "i am variable1 from component3",
60
+ ", isn't that cool?",
61
+ ]),
62
+ myThirdVar: new VariableValueContainingReferences([
63
+ "value from component3: ",
64
+ "i am referencing ",
65
+ "i am variable1 from component1",
66
+ ]),
67
+ });
68
+ });
69
+
70
+ it("detects infinte loop", async () => {
71
+ const values = {
72
+ myVar: createVariableValueContainingReferencesFromString(
73
+ "i am referencing ${component2:variable1}",
74
+ {
75
+ componentName: "component1",
76
+ },
77
+ ),
78
+ };
79
+
80
+ const getEnvVars = async (componentName: string) => {
81
+ if (componentName === "component2") {
82
+ return {
83
+ variable1: createVariableValueContainingReferencesFromString(
84
+ `i am referencing \${component3:variable1}`,
85
+ { componentName },
86
+ ),
87
+ };
88
+ } else if (componentName === "component3") {
89
+ return {
90
+ variable1: createVariableValueContainingReferencesFromString(
91
+ `i am referencing \${component1:variable1}`,
92
+ { componentName },
93
+ ),
94
+ };
95
+ } else {
96
+ return {
97
+ variable1: createVariableValueContainingReferencesFromString(
98
+ `i am referencing \${component2:variable1}`,
99
+ { componentName },
100
+ ),
101
+ };
102
+ }
103
+ };
104
+
105
+ // expect to throw an error
106
+ await expect(resolveAllReferences(values, getEnvVars)).rejects.toThrow(
107
+ "Infinite loop detected in these variables: myVar (last reference: ${component1:variable1})",
108
+ );
109
+ });
110
+ });
@@ -0,0 +1,64 @@
1
+ import {
2
+ VariableReference,
3
+ VariableValueContainingReferences,
4
+ createVariableValueContainingReferencesFromString,
5
+ } from "../VariableValueContainingReferences";
6
+ import { resolveAllReferencesOnce } from "../resolveAllReferencesOnce";
7
+
8
+ describe("resolveAllReferencesOnce", () => {
9
+ it("should replace all references", async () => {
10
+ const values = {
11
+ myVar: createVariableValueContainingReferencesFromString(
12
+ "the 2nd component has ${component2:variable1} and self reference ${variable2}",
13
+ { componentName: "component1" },
14
+ ),
15
+ myOtherVar: createVariableValueContainingReferencesFromString(
16
+ "the third component has ${component3:variable1}, isn't that cool?",
17
+ { componentName: "component1" },
18
+ ),
19
+ myThirdVar: createVariableValueContainingReferencesFromString(
20
+ "value from component3: ${component3:variable3}",
21
+ { componentName: "component1" },
22
+ ),
23
+ };
24
+
25
+ const getEnvVars = async (componentName: string) => {
26
+ return {
27
+ variable1: createVariableValueContainingReferencesFromString(
28
+ `i am variable1 from ${componentName}`,
29
+ { componentName },
30
+ ),
31
+ variable2: createVariableValueContainingReferencesFromString(
32
+ `foo from ${componentName}`,
33
+ {
34
+ componentName,
35
+ },
36
+ ),
37
+ variable3: createVariableValueContainingReferencesFromString(
38
+ `i am referencing \${component1:variable1}`,
39
+ { componentName },
40
+ ),
41
+ };
42
+ };
43
+
44
+ const result = await resolveAllReferencesOnce(values, getEnvVars);
45
+ expect(result).toEqual({
46
+ myVar: new VariableValueContainingReferences([
47
+ "the 2nd component has ",
48
+ "i am variable1 from component2",
49
+ " and self reference ",
50
+ "foo from component1",
51
+ ]),
52
+ myOtherVar: new VariableValueContainingReferences([
53
+ "the third component has ",
54
+ "i am variable1 from component3",
55
+ ", isn't that cool?",
56
+ ]),
57
+ myThirdVar: new VariableValueContainingReferences([
58
+ "value from component3: ",
59
+ "i am referencing ",
60
+ new VariableReference("component1", "variable1"),
61
+ ]),
62
+ });
63
+ });
64
+ });
@@ -0,0 +1,117 @@
1
+ import {
2
+ UnresolvableReference,
3
+ VariableReference,
4
+ createVariableValueContainingReferencesFromString,
5
+ } from "../VariableValueContainingReferences";
6
+ import { resolveReferencesOnce } from "../resolveReferencesOnce";
7
+
8
+ describe("resolveReferencesOnce", () => {
9
+ it("should replace a reference to another component", async () => {
10
+ const value = createVariableValueContainingReferencesFromString(
11
+ "the other component has ${otherComponent:variableName}, isn't that cool?",
12
+ {
13
+ componentName: "myComponent",
14
+ },
15
+ );
16
+
17
+ const result = resolveReferencesOnce(
18
+ value,
19
+ ({ componentName, variableName }) => {
20
+ return createVariableValueContainingReferencesFromString(
21
+ `replaced ${componentName}:${variableName}`,
22
+ { componentName },
23
+ );
24
+ },
25
+ );
26
+
27
+ expect(result.parts).toEqual([
28
+ "the other component has ",
29
+ "replaced otherComponent:variableName",
30
+ ", isn't that cool?",
31
+ ]);
32
+ });
33
+ it("should keep references in replacement", async () => {
34
+ const value = createVariableValueContainingReferencesFromString(
35
+ "the other component has ${otherComponent:variableName}, isn't that cool?",
36
+ {
37
+ componentName: "myComponent",
38
+ },
39
+ );
40
+
41
+ const result = resolveReferencesOnce(
42
+ value,
43
+ ({ componentName, variableName }) => {
44
+ return createVariableValueContainingReferencesFromString(
45
+ `replaced ${componentName}:${variableName}, contains reference to \${thirdComponent:x}`,
46
+ { componentName },
47
+ );
48
+ },
49
+ );
50
+
51
+ expect(result.parts).toEqual([
52
+ "the other component has ",
53
+
54
+ "replaced otherComponent:variableName, contains reference to ",
55
+ new VariableReference("thirdComponent", "x"),
56
+ ", isn't that cool?",
57
+ ]);
58
+ });
59
+
60
+ it("can be run multiple times", async () => {
61
+ const value = createVariableValueContainingReferencesFromString(
62
+ "the other component has ${otherComponent:variableName}, isn't that cool?",
63
+ {
64
+ componentName: "myComponent",
65
+ },
66
+ );
67
+ const replacer = ({
68
+ componentName,
69
+ variableName,
70
+ }: {
71
+ componentName: string;
72
+ variableName: string;
73
+ }) => {
74
+ return createVariableValueContainingReferencesFromString(
75
+ componentName === "thirdComponent"
76
+ ? "value from third component"
77
+ : `replaced ${componentName}:${variableName}, contains reference to \${thirdComponent:x}`,
78
+ { componentName },
79
+ );
80
+ };
81
+ const result = resolveReferencesOnce(
82
+ resolveReferencesOnce(value, replacer),
83
+ replacer,
84
+ );
85
+
86
+ expect(result.parts).toEqual([
87
+ "the other component has ",
88
+ "replaced otherComponent:variableName, contains reference to ",
89
+ "value from third component",
90
+ ", isn't that cool?",
91
+ ]);
92
+ });
93
+
94
+ it("flags unresolveable", async () => {
95
+ const value = createVariableValueContainingReferencesFromString(
96
+ "the other component has ${otherComponent:variableName}, isn't that cool?",
97
+ {
98
+ componentName: "myComponent",
99
+ },
100
+ );
101
+
102
+ const result = resolveReferencesOnce(
103
+ value,
104
+ ({ componentName, variableName }) => {
105
+ return null;
106
+ },
107
+ );
108
+
109
+ expect(result.parts).toEqual([
110
+ "the other component has ",
111
+ new UnresolvableReference(
112
+ new VariableReference("otherComponent", "variableName"),
113
+ ),
114
+ ", isn't that cool?",
115
+ ]);
116
+ });
117
+ });
@@ -0,0 +1,73 @@
1
+ import {
2
+ VariableReference,
3
+ createVariableValueContainingReferencesFromString,
4
+ } from "../VariableValueContainingReferences";
5
+
6
+ describe("createVarialeValueFromString", () => {
7
+ it("should create a VariableValue from a simple string", () => {
8
+ const stringValue = "hello world";
9
+
10
+ const result = createVariableValueContainingReferencesFromString(
11
+ stringValue,
12
+ {
13
+ componentName: "myComponent",
14
+ },
15
+ );
16
+
17
+ expect(result.parts).toEqual([stringValue]);
18
+ });
19
+
20
+ it("extracts references to other components", () => {
21
+ const stringValue =
22
+ "the other component has ${otherComponent:variableName}, isn't that cool?";
23
+
24
+ const result = createVariableValueContainingReferencesFromString(
25
+ stringValue,
26
+ {
27
+ componentName: "myComponent",
28
+ },
29
+ );
30
+
31
+ expect(result.parts).toEqual([
32
+ "the other component has ",
33
+ new VariableReference("otherComponent", "variableName"),
34
+ ", isn't that cool?",
35
+ ]);
36
+ });
37
+
38
+ it("extracts self references", () => {
39
+ const stringValue = "my component has ${variableName}!";
40
+
41
+ const result = createVariableValueContainingReferencesFromString(
42
+ stringValue,
43
+ {
44
+ componentName: "myComponent",
45
+ },
46
+ );
47
+
48
+ expect(result.parts).toEqual([
49
+ "my component has ",
50
+ new VariableReference("myComponent", "variableName"),
51
+ "!",
52
+ ]);
53
+ });
54
+ it("extracts multiple references", () => {
55
+ const stringValue =
56
+ "my component has ${variableName} and the other component has ${otherComponent:variableName}!";
57
+
58
+ const result = createVariableValueContainingReferencesFromString(
59
+ stringValue,
60
+ {
61
+ componentName: "myComponent",
62
+ },
63
+ );
64
+
65
+ expect(result.parts).toEqual([
66
+ "my component has ",
67
+ new VariableReference("myComponent", "variableName"),
68
+ " and the other component has ",
69
+ new VariableReference("otherComponent", "variableName"),
70
+ "!",
71
+ ]);
72
+ });
73
+ });
@@ -0,0 +1,46 @@
1
+ import type { VariableValue } from "./VariableValue";
2
+ import type { VariableValueContainingReferences } from "./VariableValueContainingReferences";
3
+ import { VariableReference } from "./VariableValueContainingReferences";
4
+ import { resolveAllReferencesOnce as resolveAllReferencesOnce } from "./resolveAllReferencesOnce";
5
+
6
+ export const resolveAllReferences = async (
7
+ values: Record<string, VariableValueContainingReferences>,
8
+ getEnvVars: (
9
+ componentName: string,
10
+ ) => Promise<Record<string, VariableValue | null | undefined>>,
11
+ ) => {
12
+ // replace until there aren't any references left
13
+ let result = values;
14
+
15
+ let i = 0;
16
+
17
+ while (
18
+ Object.values(result).some((value) =>
19
+ value.parts.some((part) => part instanceof VariableReference),
20
+ )
21
+ ) {
22
+ const replaced = await resolveAllReferencesOnce(result, getEnvVars);
23
+
24
+ result = replaced;
25
+ i++;
26
+ if (i > 1000) {
27
+ const unresolved = Object.entries(result).filter(([key, value]) =>
28
+ value.parts.some((part) => part instanceof VariableReference),
29
+ );
30
+
31
+ throw new Error(
32
+ "Infinite loop detected in these variables: " +
33
+ unresolved
34
+ .map(
35
+ ([key, value]) =>
36
+ `${key} (last reference: ${value.parts.find(
37
+ (part) => part instanceof VariableReference,
38
+ )})`,
39
+ )
40
+ .join(", "),
41
+ );
42
+ }
43
+ }
44
+
45
+ return result;
46
+ };