@invarn/cibuild 1.3.16 → 1.3.18

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 (242) hide show
  1. package/dist/cli.cjs +1 -1
  2. package/dist/src/cli.d.ts +3 -0
  3. package/dist/src/cli.d.ts.map +1 -0
  4. package/dist/src/cli.js +987 -0
  5. package/dist/src/commands/android-scanner.d.ts +32 -0
  6. package/dist/src/commands/android-scanner.d.ts.map +1 -0
  7. package/dist/src/commands/android-scanner.js +667 -0
  8. package/dist/src/commands/build.d.ts +5 -0
  9. package/dist/src/commands/build.d.ts.map +1 -0
  10. package/dist/src/commands/build.js +1096 -0
  11. package/dist/src/commands/edit.d.ts +3 -0
  12. package/dist/src/commands/edit.d.ts.map +1 -0
  13. package/dist/src/commands/edit.js +651 -0
  14. package/dist/src/commands/file-secret-collector.d.ts +37 -0
  15. package/dist/src/commands/file-secret-collector.d.ts.map +1 -0
  16. package/dist/src/commands/file-secret-collector.js +199 -0
  17. package/dist/src/commands/github-workflow.d.ts +5 -0
  18. package/dist/src/commands/github-workflow.d.ts.map +1 -0
  19. package/dist/src/commands/github-workflow.js +45 -0
  20. package/dist/src/commands/ios-scanner.d.ts +27 -0
  21. package/dist/src/commands/ios-scanner.d.ts.map +1 -0
  22. package/dist/src/commands/ios-scanner.js +337 -0
  23. package/dist/src/commands/reset.d.ts +7 -0
  24. package/dist/src/commands/reset.d.ts.map +1 -0
  25. package/dist/src/commands/reset.js +81 -0
  26. package/dist/src/commands/secrets-sync-workflow.d.ts +15 -0
  27. package/dist/src/commands/secrets-sync-workflow.d.ts.map +1 -0
  28. package/dist/src/commands/secrets-sync-workflow.js +255 -0
  29. package/dist/src/commands/secrets-upload.d.ts +21 -0
  30. package/dist/src/commands/secrets-upload.d.ts.map +1 -0
  31. package/dist/src/commands/secrets-upload.js +177 -0
  32. package/dist/src/commands/secrets-upload.test.d.ts +5 -0
  33. package/dist/src/commands/secrets-upload.test.d.ts.map +1 -0
  34. package/dist/src/commands/secrets-upload.test.js +60 -0
  35. package/dist/src/config.d.ts +3 -0
  36. package/dist/src/config.d.ts.map +1 -0
  37. package/dist/src/config.js +47 -0
  38. package/dist/src/envman/cli.d.ts +21 -0
  39. package/dist/src/envman/cli.d.ts.map +1 -0
  40. package/dist/src/envman/cli.js +240 -0
  41. package/dist/src/envman/envman.d.ts +83 -0
  42. package/dist/src/envman/envman.d.ts.map +1 -0
  43. package/dist/src/envman/envman.js +361 -0
  44. package/dist/src/envman/envman.test.d.ts +5 -0
  45. package/dist/src/envman/envman.test.d.ts.map +1 -0
  46. package/dist/src/envman/envman.test.js +236 -0
  47. package/dist/src/envman/index.d.ts +23 -0
  48. package/dist/src/envman/index.d.ts.map +1 -0
  49. package/dist/src/envman/index.js +23 -0
  50. package/dist/src/envman/types.d.ts +55 -0
  51. package/dist/src/envman/types.d.ts.map +1 -0
  52. package/dist/src/envman/types.js +12 -0
  53. package/dist/src/lib.d.ts +27 -0
  54. package/dist/src/lib.d.ts.map +1 -0
  55. package/dist/src/lib.js +32 -0
  56. package/dist/src/pipeline.d.ts +3 -0
  57. package/dist/src/pipeline.d.ts.map +1 -0
  58. package/dist/src/pipeline.js +57 -0
  59. package/dist/src/runner.d.ts +17 -0
  60. package/dist/src/runner.d.ts.map +1 -0
  61. package/dist/src/runner.js +234 -0
  62. package/dist/src/types.d.ts +58 -0
  63. package/dist/src/types.d.ts.map +1 -0
  64. package/dist/src/types.js +2 -0
  65. package/dist/src/yaml/bitrise-compat.d.ts +65 -0
  66. package/dist/src/yaml/bitrise-compat.d.ts.map +1 -0
  67. package/dist/src/yaml/bitrise-compat.js +206 -0
  68. package/dist/src/yaml/bitrise-compat.test.d.ts +5 -0
  69. package/dist/src/yaml/bitrise-compat.test.d.ts.map +1 -0
  70. package/dist/src/yaml/bitrise-compat.test.js +347 -0
  71. package/dist/src/yaml/converter.d.ts +33 -0
  72. package/dist/src/yaml/converter.d.ts.map +1 -0
  73. package/dist/src/yaml/converter.js +222 -0
  74. package/dist/src/yaml/converter.test.d.ts +5 -0
  75. package/dist/src/yaml/converter.test.d.ts.map +1 -0
  76. package/dist/src/yaml/converter.test.js +348 -0
  77. package/dist/src/yaml/e2e.test.d.ts +6 -0
  78. package/dist/src/yaml/e2e.test.d.ts.map +1 -0
  79. package/dist/src/yaml/e2e.test.js +446 -0
  80. package/dist/src/yaml/env-resolver.d.ts +120 -0
  81. package/dist/src/yaml/env-resolver.d.ts.map +1 -0
  82. package/dist/src/yaml/env-resolver.js +405 -0
  83. package/dist/src/yaml/env-resolver.test.d.ts +5 -0
  84. package/dist/src/yaml/env-resolver.test.d.ts.map +1 -0
  85. package/dist/src/yaml/env-resolver.test.js +502 -0
  86. package/dist/src/yaml/interactive-prompts.d.ts +71 -0
  87. package/dist/src/yaml/interactive-prompts.d.ts.map +1 -0
  88. package/dist/src/yaml/interactive-prompts.js +258 -0
  89. package/dist/src/yaml/missing-env-handler.d.ts +45 -0
  90. package/dist/src/yaml/missing-env-handler.d.ts.map +1 -0
  91. package/dist/src/yaml/missing-env-handler.js +64 -0
  92. package/dist/src/yaml/parser.d.ts +33 -0
  93. package/dist/src/yaml/parser.d.ts.map +1 -0
  94. package/dist/src/yaml/parser.js +145 -0
  95. package/dist/src/yaml/pipeline-with-secrets.d.ts +25 -0
  96. package/dist/src/yaml/pipeline-with-secrets.d.ts.map +1 -0
  97. package/dist/src/yaml/pipeline-with-secrets.js +76 -0
  98. package/dist/src/yaml/platform-detector.d.ts +83 -0
  99. package/dist/src/yaml/platform-detector.d.ts.map +1 -0
  100. package/dist/src/yaml/platform-detector.js +188 -0
  101. package/dist/src/yaml/platform-detector.test.d.ts +5 -0
  102. package/dist/src/yaml/platform-detector.test.d.ts.map +1 -0
  103. package/dist/src/yaml/platform-detector.test.js +414 -0
  104. package/dist/src/yaml/preflight-validation.d.ts +40 -0
  105. package/dist/src/yaml/preflight-validation.d.ts.map +1 -0
  106. package/dist/src/yaml/preflight-validation.js +152 -0
  107. package/dist/src/yaml/secrets-manager.d.ts +77 -0
  108. package/dist/src/yaml/secrets-manager.d.ts.map +1 -0
  109. package/dist/src/yaml/secrets-manager.js +219 -0
  110. package/dist/src/yaml/step-validator.d.ts +54 -0
  111. package/dist/src/yaml/step-validator.d.ts.map +1 -0
  112. package/dist/src/yaml/step-validator.js +403 -0
  113. package/dist/src/yaml/steps/android-sign.d.ts +35 -0
  114. package/dist/src/yaml/steps/android-sign.d.ts.map +1 -0
  115. package/dist/src/yaml/steps/android-sign.js +147 -0
  116. package/dist/src/yaml/steps/android-version.d.ts +26 -0
  117. package/dist/src/yaml/steps/android-version.d.ts.map +1 -0
  118. package/dist/src/yaml/steps/android-version.js +128 -0
  119. package/dist/src/yaml/steps/android-version.test.d.ts +5 -0
  120. package/dist/src/yaml/steps/android-version.test.d.ts.map +1 -0
  121. package/dist/src/yaml/steps/android-version.test.js +196 -0
  122. package/dist/src/yaml/steps/android.d.ts +95 -0
  123. package/dist/src/yaml/steps/android.d.ts.map +1 -0
  124. package/dist/src/yaml/steps/android.js +916 -0
  125. package/dist/src/yaml/steps/app-store-deploy.d.ts +48 -0
  126. package/dist/src/yaml/steps/app-store-deploy.d.ts.map +1 -0
  127. package/dist/src/yaml/steps/app-store-deploy.js +162 -0
  128. package/dist/src/yaml/steps/base.d.ts +238 -0
  129. package/dist/src/yaml/steps/base.d.ts.map +1 -0
  130. package/dist/src/yaml/steps/base.js +345 -0
  131. package/dist/src/yaml/steps/bitrise-android-tools.d.ts +26 -0
  132. package/dist/src/yaml/steps/bitrise-android-tools.d.ts.map +1 -0
  133. package/dist/src/yaml/steps/bitrise-android-tools.js +198 -0
  134. package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts +5 -0
  135. package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts.map +1 -0
  136. package/dist/src/yaml/steps/bitrise-android-tools.test.js +280 -0
  137. package/dist/src/yaml/steps/bitrise-apk-info.d.ts +22 -0
  138. package/dist/src/yaml/steps/bitrise-apk-info.d.ts.map +1 -0
  139. package/dist/src/yaml/steps/bitrise-apk-info.js +144 -0
  140. package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts +5 -0
  141. package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts.map +1 -0
  142. package/dist/src/yaml/steps/bitrise-apk-info.test.js +331 -0
  143. package/dist/src/yaml/steps/bitrise-slack.d.ts +49 -0
  144. package/dist/src/yaml/steps/bitrise-slack.d.ts.map +1 -0
  145. package/dist/src/yaml/steps/bitrise-slack.js +280 -0
  146. package/dist/src/yaml/steps/bitrise-slack.test.d.ts +5 -0
  147. package/dist/src/yaml/steps/bitrise-slack.test.d.ts.map +1 -0
  148. package/dist/src/yaml/steps/bitrise-slack.test.js +484 -0
  149. package/dist/src/yaml/steps/bitrise-ssh.d.ts +27 -0
  150. package/dist/src/yaml/steps/bitrise-ssh.d.ts.map +1 -0
  151. package/dist/src/yaml/steps/bitrise-ssh.js +134 -0
  152. package/dist/src/yaml/steps/bitrise-ssh.test.d.ts +5 -0
  153. package/dist/src/yaml/steps/bitrise-ssh.test.d.ts.map +1 -0
  154. package/dist/src/yaml/steps/bitrise-ssh.test.js +205 -0
  155. package/dist/src/yaml/steps/cache.d.ts +52 -0
  156. package/dist/src/yaml/steps/cache.d.ts.map +1 -0
  157. package/dist/src/yaml/steps/cache.js +352 -0
  158. package/dist/src/yaml/steps/fastlane.d.ts +27 -0
  159. package/dist/src/yaml/steps/fastlane.d.ts.map +1 -0
  160. package/dist/src/yaml/steps/fastlane.js +79 -0
  161. package/dist/src/yaml/steps/file.d.ts +27 -0
  162. package/dist/src/yaml/steps/file.d.ts.map +1 -0
  163. package/dist/src/yaml/steps/file.js +35 -0
  164. package/dist/src/yaml/steps/flutter.d.ts +63 -0
  165. package/dist/src/yaml/steps/flutter.d.ts.map +1 -0
  166. package/dist/src/yaml/steps/flutter.js +215 -0
  167. package/dist/src/yaml/steps/git-clone.d.ts +26 -0
  168. package/dist/src/yaml/steps/git-clone.d.ts.map +1 -0
  169. package/dist/src/yaml/steps/git-clone.js +111 -0
  170. package/dist/src/yaml/steps/google-play-deploy.d.ts +37 -0
  171. package/dist/src/yaml/steps/google-play-deploy.d.ts.map +1 -0
  172. package/dist/src/yaml/steps/google-play-deploy.js +193 -0
  173. package/dist/src/yaml/steps/google-play-deploy.test.d.ts +5 -0
  174. package/dist/src/yaml/steps/google-play-deploy.test.d.ts.map +1 -0
  175. package/dist/src/yaml/steps/google-play-deploy.test.js +310 -0
  176. package/dist/src/yaml/steps/index.d.ts +10 -0
  177. package/dist/src/yaml/steps/index.d.ts.map +1 -0
  178. package/dist/src/yaml/steps/index.js +1361 -0
  179. package/dist/src/yaml/steps/ios-deps.d.ts +43 -0
  180. package/dist/src/yaml/steps/ios-deps.d.ts.map +1 -0
  181. package/dist/src/yaml/steps/ios-deps.js +141 -0
  182. package/dist/src/yaml/steps/ios-deps.test.d.ts +5 -0
  183. package/dist/src/yaml/steps/ios-deps.test.d.ts.map +1 -0
  184. package/dist/src/yaml/steps/ios-deps.test.js +90 -0
  185. package/dist/src/yaml/steps/ios-signing.d.ts +31 -0
  186. package/dist/src/yaml/steps/ios-signing.d.ts.map +1 -0
  187. package/dist/src/yaml/steps/ios-signing.js +144 -0
  188. package/dist/src/yaml/steps/ios-version.d.ts +47 -0
  189. package/dist/src/yaml/steps/ios-version.d.ts.map +1 -0
  190. package/dist/src/yaml/steps/ios-version.js +151 -0
  191. package/dist/src/yaml/steps/linting.d.ts +47 -0
  192. package/dist/src/yaml/steps/linting.d.ts.map +1 -0
  193. package/dist/src/yaml/steps/linting.js +148 -0
  194. package/dist/src/yaml/steps/phase2.test.d.ts +6 -0
  195. package/dist/src/yaml/steps/phase2.test.d.ts.map +1 -0
  196. package/dist/src/yaml/steps/phase2.test.js +197 -0
  197. package/dist/src/yaml/steps/phase3.test.d.ts +5 -0
  198. package/dist/src/yaml/steps/phase3.test.d.ts.map +1 -0
  199. package/dist/src/yaml/steps/phase3.test.js +144 -0
  200. package/dist/src/yaml/steps/phase4.test.d.ts +5 -0
  201. package/dist/src/yaml/steps/phase4.test.d.ts.map +1 -0
  202. package/dist/src/yaml/steps/phase4.test.js +166 -0
  203. package/dist/src/yaml/steps/phase5.test.d.ts +6 -0
  204. package/dist/src/yaml/steps/phase5.test.d.ts.map +1 -0
  205. package/dist/src/yaml/steps/phase5.test.js +263 -0
  206. package/dist/src/yaml/steps/registry.d.ts +88 -0
  207. package/dist/src/yaml/steps/registry.d.ts.map +1 -0
  208. package/dist/src/yaml/steps/registry.js +125 -0
  209. package/dist/src/yaml/steps/registry.test.d.ts +5 -0
  210. package/dist/src/yaml/steps/registry.test.d.ts.map +1 -0
  211. package/dist/src/yaml/steps/registry.test.js +235 -0
  212. package/dist/src/yaml/steps/release.d.ts +50 -0
  213. package/dist/src/yaml/steps/release.d.ts.map +1 -0
  214. package/dist/src/yaml/steps/release.js +154 -0
  215. package/dist/src/yaml/steps/script.d.ts +23 -0
  216. package/dist/src/yaml/steps/script.d.ts.map +1 -0
  217. package/dist/src/yaml/steps/script.js +63 -0
  218. package/dist/src/yaml/steps/spec-validation.test.d.ts +6 -0
  219. package/dist/src/yaml/steps/spec-validation.test.d.ts.map +1 -0
  220. package/dist/src/yaml/steps/spec-validation.test.js +130 -0
  221. package/dist/src/yaml/steps/steps.test.d.ts +6 -0
  222. package/dist/src/yaml/steps/steps.test.d.ts.map +1 -0
  223. package/dist/src/yaml/steps/steps.test.js +505 -0
  224. package/dist/src/yaml/steps/test-config.d.ts +3 -0
  225. package/dist/src/yaml/steps/test-config.d.ts.map +1 -0
  226. package/dist/src/yaml/steps/test-config.js +17 -0
  227. package/dist/src/yaml/steps/xcode-new.test.d.ts +5 -0
  228. package/dist/src/yaml/steps/xcode-new.test.d.ts.map +1 -0
  229. package/dist/src/yaml/steps/xcode-new.test.js +211 -0
  230. package/dist/src/yaml/steps/xcode.d.ts +222 -0
  231. package/dist/src/yaml/steps/xcode.d.ts.map +1 -0
  232. package/dist/src/yaml/steps/xcode.js +999 -0
  233. package/dist/src/yaml/types.d.ts +68 -0
  234. package/dist/src/yaml/types.d.ts.map +1 -0
  235. package/dist/src/yaml/types.js +5 -0
  236. package/dist/src/yaml/validation-types.d.ts +96 -0
  237. package/dist/src/yaml/validation-types.d.ts.map +1 -0
  238. package/dist/src/yaml/validation-types.js +8 -0
  239. package/dist/src/yaml/yaml-updater.d.ts +24 -0
  240. package/dist/src/yaml/yaml-updater.d.ts.map +1 -0
  241. package/dist/src/yaml/yaml-updater.js +128 -0
  242. package/package.json +16 -4
@@ -0,0 +1,502 @@
1
+ /**
2
+ * Unit tests for environment variable resolution
3
+ */
4
+ import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
5
+ import { EnvResolver, MissingEnvironmentVariableError, createEnvResolver } from './env-resolver.js';
6
+ import * as fs from 'fs';
7
+ describe('EnvResolver', () => {
8
+ let originalEnv;
9
+ beforeEach(() => {
10
+ // Save original environment
11
+ originalEnv = { ...process.env };
12
+ });
13
+ afterEach(() => {
14
+ // Restore original environment
15
+ process.env = originalEnv;
16
+ });
17
+ describe('Built-in environment variables', () => {
18
+ test('should add common built-in variables', () => {
19
+ const pipeline = {
20
+ format_version: '4',
21
+ workflows: { primary: { steps: [] } },
22
+ };
23
+ const workflow = pipeline.workflows.primary;
24
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
25
+ expect(resolver.get('CIBUILD_SOURCE_DIR')).toBe(process.cwd());
26
+ expect(resolver.get('CIBUILD_BUILD_NUMBER')).toBe('1');
27
+ expect(resolver.get('CIBUILD_GIT_BRANCH')).toBeDefined();
28
+ expect(resolver.get('CIBUILD_GIT_COMMIT')).toBeDefined();
29
+ expect(resolver.get('CIBUILD_TRIGGERED_WORKFLOW_ID')).toBe('primary');
30
+ });
31
+ test('should add macOS platform-specific variables', () => {
32
+ const pipeline = {
33
+ format_version: '4',
34
+ workflows: { primary: { steps: [] } },
35
+ };
36
+ const workflow = pipeline.workflows.primary;
37
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'macos', 'macos-ventura-xcode-15.1');
38
+ expect(resolver.get('CIBUILD_STACK')).toBe('macos-ventura-xcode-15.1');
39
+ expect(resolver.get('CIBUILD_XCODE_VERSION')).toBe('15.1');
40
+ expect(resolver.get('CIBUILD_SCHEME')).toBeDefined();
41
+ });
42
+ test('should add Linux platform-specific variables', () => {
43
+ const pipeline = {
44
+ format_version: '4',
45
+ workflows: { primary: { steps: [] } },
46
+ };
47
+ const workflow = pipeline.workflows.primary;
48
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux', 'linux-docker-android-22.04');
49
+ expect(resolver.get('CIBUILD_STACK')).toBe('linux-docker-android-22.04');
50
+ expect(resolver.get('CIBUILD_GRADLE_VERSION')).toBeDefined();
51
+ expect(resolver.get('JAVA_HOME')).toBeDefined();
52
+ });
53
+ test('should extract Xcode version from stack', () => {
54
+ const pipeline = {
55
+ format_version: '4',
56
+ workflows: { primary: { steps: [] } },
57
+ };
58
+ const workflow = pipeline.workflows.primary;
59
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'macos', 'macos-sonoma-xcode-15.0');
60
+ expect(resolver.get('CIBUILD_XCODE_VERSION')).toBe('15.0');
61
+ });
62
+ test('should use process.env values when available', () => {
63
+ process.env.BUILD_NUMBER = '42';
64
+ process.env.GIT_BRANCH = 'feature/test';
65
+ process.env.GIT_COMMIT = 'abc123';
66
+ const pipeline = {
67
+ format_version: '4',
68
+ workflows: { primary: { steps: [] } },
69
+ };
70
+ const workflow = pipeline.workflows.primary;
71
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
72
+ expect(resolver.get('CIBUILD_BUILD_NUMBER')).toBe('42');
73
+ expect(resolver.get('CIBUILD_GIT_BRANCH')).toBe('feature/test');
74
+ expect(resolver.get('CIBUILD_GIT_COMMIT')).toBe('abc123');
75
+ });
76
+ });
77
+ describe('Environment variable merging', () => {
78
+ test('should merge global environment variables', () => {
79
+ const pipeline = {
80
+ format_version: '4',
81
+ app: {
82
+ envs: [
83
+ { GLOBAL_VAR1: 'value1' },
84
+ { GLOBAL_VAR2: 'value2' },
85
+ ],
86
+ },
87
+ workflows: { primary: { steps: [] } },
88
+ };
89
+ const workflow = pipeline.workflows.primary;
90
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
91
+ expect(resolver.get('GLOBAL_VAR1')).toBe('value1');
92
+ expect(resolver.get('GLOBAL_VAR2')).toBe('value2');
93
+ });
94
+ test('should merge workflow-level environment variables', () => {
95
+ const pipeline = {
96
+ format_version: '4',
97
+ workflows: {
98
+ primary: {
99
+ envs: [
100
+ { WORKFLOW_VAR1: 'wf_value1' },
101
+ { WORKFLOW_VAR2: 'wf_value2' },
102
+ ],
103
+ steps: [],
104
+ },
105
+ },
106
+ };
107
+ const workflow = pipeline.workflows.primary;
108
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
109
+ expect(resolver.get('WORKFLOW_VAR1')).toBe('wf_value1');
110
+ expect(resolver.get('WORKFLOW_VAR2')).toBe('wf_value2');
111
+ });
112
+ test('should override global variables with workflow variables', () => {
113
+ const pipeline = {
114
+ format_version: '4',
115
+ app: {
116
+ envs: [{ BUILD_TYPE: 'debug' }],
117
+ },
118
+ workflows: {
119
+ primary: {
120
+ envs: [{ BUILD_TYPE: 'release' }],
121
+ steps: [],
122
+ },
123
+ },
124
+ };
125
+ const workflow = pipeline.workflows.primary;
126
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
127
+ expect(resolver.get('BUILD_TYPE')).toBe('release');
128
+ });
129
+ test('should handle empty env arrays', () => {
130
+ const pipeline = {
131
+ format_version: '4',
132
+ app: { envs: [] },
133
+ workflows: {
134
+ primary: {
135
+ envs: [],
136
+ steps: [],
137
+ },
138
+ },
139
+ };
140
+ const workflow = pipeline.workflows.primary;
141
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
142
+ // Should still have built-in variables
143
+ expect(resolver.get('CIBUILD_SOURCE_DIR')).toBeDefined();
144
+ });
145
+ });
146
+ describe('Variable interpolation', () => {
147
+ test('should interpolate $VAR format', () => {
148
+ const pipeline = {
149
+ format_version: '4',
150
+ app: {
151
+ envs: [{ MY_VAR: 'hello' }],
152
+ },
153
+ workflows: { primary: { steps: [] } },
154
+ };
155
+ const workflow = pipeline.workflows.primary;
156
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
157
+ const result = resolver.interpolate('Value is $MY_VAR world');
158
+ expect(result).toBe('Value is hello world');
159
+ });
160
+ test('should interpolate ${VAR} format', () => {
161
+ const pipeline = {
162
+ format_version: '4',
163
+ app: {
164
+ envs: [{ MY_VAR: 'hello' }],
165
+ },
166
+ workflows: { primary: { steps: [] } },
167
+ };
168
+ const workflow = pipeline.workflows.primary;
169
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
170
+ const result = resolver.interpolate('Value is ${MY_VAR} world');
171
+ expect(result).toBe('Value is hello world');
172
+ });
173
+ test('should interpolate {{getenv "VAR"}} format', () => {
174
+ const pipeline = {
175
+ format_version: '4',
176
+ app: {
177
+ envs: [{ MY_VAR: 'hello' }],
178
+ },
179
+ workflows: { primary: { steps: [] } },
180
+ };
181
+ const workflow = pipeline.workflows.primary;
182
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
183
+ const result = resolver.interpolate('Value is {{getenv "MY_VAR"}} world');
184
+ expect(result).toBe('Value is hello world');
185
+ });
186
+ test('should interpolate {{checksum "file"}} format', () => {
187
+ // Create a test file
188
+ const testFile = 'test-checksum-file.txt';
189
+ fs.writeFileSync(testFile, 'test content');
190
+ try {
191
+ const pipeline = {
192
+ format_version: '4',
193
+ workflows: { primary: { steps: [] } },
194
+ };
195
+ const workflow = pipeline.workflows.primary;
196
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
197
+ const result = resolver.interpolate(`checksum-{{checksum "${testFile}"}}`);
198
+ // MD5 hash of "test content" is 9473fdd0d880a43c21b7778d34872157
199
+ expect(result).toBe('checksum-9473fdd0d880a43c21b7778d34872157');
200
+ }
201
+ finally {
202
+ // Cleanup
203
+ if (fs.existsSync(testFile)) {
204
+ fs.unlinkSync(testFile);
205
+ }
206
+ }
207
+ });
208
+ test('should throw error for non-existent file in checksum', () => {
209
+ const pipeline = {
210
+ format_version: '4',
211
+ workflows: { primary: { steps: [] } },
212
+ };
213
+ const workflow = pipeline.workflows.primary;
214
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
215
+ expect(() => {
216
+ resolver.interpolate('{{checksum "non-existent-file.txt"}}');
217
+ }).toThrow('File not found: non-existent-file.txt');
218
+ });
219
+ test('should interpolate multiple variables in one string', () => {
220
+ const pipeline = {
221
+ format_version: '4',
222
+ app: {
223
+ envs: [
224
+ { VAR1: 'hello' },
225
+ { VAR2: 'world' },
226
+ ],
227
+ },
228
+ workflows: { primary: { steps: [] } },
229
+ };
230
+ const workflow = pipeline.workflows.primary;
231
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
232
+ const result = resolver.interpolate('$VAR1 ${VAR2} {{getenv "VAR1"}}');
233
+ expect(result).toBe('hello world hello');
234
+ });
235
+ test('should interpolate variables in nested objects', () => {
236
+ const pipeline = {
237
+ format_version: '4',
238
+ app: {
239
+ envs: [{ PROJECT_PATH: './MyApp' }],
240
+ },
241
+ workflows: { primary: { steps: [] } },
242
+ };
243
+ const workflow = pipeline.workflows.primary;
244
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
245
+ const obj = {
246
+ path: '$PROJECT_PATH',
247
+ nested: {
248
+ value: '${PROJECT_PATH}/sub',
249
+ },
250
+ };
251
+ const result = resolver.interpolateObject(obj);
252
+ expect(result.path).toBe('./MyApp');
253
+ expect(result.nested.value).toBe('./MyApp/sub');
254
+ });
255
+ test('should interpolate variables in arrays', () => {
256
+ const pipeline = {
257
+ format_version: '4',
258
+ app: {
259
+ envs: [{ DIR: '/path' }],
260
+ },
261
+ workflows: { primary: { steps: [] } },
262
+ };
263
+ const workflow = pipeline.workflows.primary;
264
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
265
+ const arr = ['$DIR/file1', '${DIR}/file2'];
266
+ const result = resolver.interpolateObject(arr);
267
+ expect(result[0]).toBe('/path/file1');
268
+ expect(result[1]).toBe('/path/file2');
269
+ });
270
+ test('should support env vars referencing other env vars', () => {
271
+ const pipeline = {
272
+ format_version: '4',
273
+ app: {
274
+ envs: [
275
+ { BASE_DIR: '/home/user' },
276
+ { PROJECT_DIR: '$BASE_DIR/project' },
277
+ ],
278
+ },
279
+ workflows: { primary: { steps: [] } },
280
+ };
281
+ const workflow = pipeline.workflows.primary;
282
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
283
+ expect(resolver.get('PROJECT_DIR')).toContain('/home/user/project');
284
+ });
285
+ });
286
+ describe('Missing variable detection', () => {
287
+ test('should throw error for missing variable in interpolation', () => {
288
+ const pipeline = {
289
+ format_version: '4',
290
+ workflows: { primary: { steps: [] } },
291
+ };
292
+ const workflow = pipeline.workflows.primary;
293
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
294
+ expect(() => resolver.interpolate('Value is $MISSING_VAR')).toThrow(MissingEnvironmentVariableError);
295
+ });
296
+ test('should include variable name in error message', () => {
297
+ const pipeline = {
298
+ format_version: '4',
299
+ workflows: { primary: { steps: [] } },
300
+ };
301
+ const workflow = pipeline.workflows.primary;
302
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
303
+ expect(() => resolver.interpolate('${MISSING_VAR}')).toThrow(/MISSING_VAR/);
304
+ });
305
+ test('should include step name in error message when provided', () => {
306
+ const pipeline = {
307
+ format_version: '4',
308
+ workflows: { primary: { steps: [] } },
309
+ };
310
+ const workflow = pipeline.workflows.primary;
311
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
312
+ expect(() => resolver.interpolate('${MISSING_VAR}', 'my-step')).toThrow(/my-step/);
313
+ });
314
+ test('should detect missing variables in nested objects', () => {
315
+ const pipeline = {
316
+ format_version: '4',
317
+ workflows: { primary: { steps: [] } },
318
+ };
319
+ const workflow = pipeline.workflows.primary;
320
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
321
+ const obj = {
322
+ nested: {
323
+ value: '$MISSING_VAR',
324
+ },
325
+ };
326
+ expect(() => resolver.interpolateObject(obj)).toThrow(MissingEnvironmentVariableError);
327
+ });
328
+ });
329
+ describe('Helper methods', () => {
330
+ test('has() should check variable existence', () => {
331
+ const pipeline = {
332
+ format_version: '4',
333
+ app: {
334
+ envs: [{ MY_VAR: 'value' }],
335
+ },
336
+ workflows: { primary: { steps: [] } },
337
+ };
338
+ const workflow = pipeline.workflows.primary;
339
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
340
+ expect(resolver.has('MY_VAR')).toBe(true);
341
+ expect(resolver.has('NONEXISTENT_VAR')).toBe(false);
342
+ });
343
+ test('getAll() should return all variables', () => {
344
+ const pipeline = {
345
+ format_version: '4',
346
+ app: {
347
+ envs: [{ VAR1: 'value1' }, { VAR2: 'value2' }],
348
+ },
349
+ workflows: { primary: { steps: [] } },
350
+ };
351
+ const workflow = pipeline.workflows.primary;
352
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
353
+ const allVars = resolver.getAll();
354
+ expect(allVars.VAR1).toBe('value1');
355
+ expect(allVars.VAR2).toBe('value2');
356
+ expect(allVars.CIBUILD_SOURCE_DIR).toBeDefined();
357
+ });
358
+ });
359
+ describe('createEnvResolver helper', () => {
360
+ test('should create resolver for valid workflow', () => {
361
+ const pipeline = {
362
+ format_version: '4',
363
+ workflows: { primary: { steps: [] } },
364
+ };
365
+ const resolver = createEnvResolver(pipeline, 'primary', 'linux');
366
+ expect(resolver).toBeInstanceOf(EnvResolver);
367
+ });
368
+ test('should throw error for non-existent workflow', () => {
369
+ const pipeline = {
370
+ format_version: '4',
371
+ workflows: { primary: { steps: [] } },
372
+ };
373
+ expect(() => createEnvResolver(pipeline, 'nonexistent', 'linux')).toThrow(/not found/);
374
+ });
375
+ test('should pass stack to resolver', () => {
376
+ const pipeline = {
377
+ format_version: '4',
378
+ workflows: { primary: { steps: [] } },
379
+ };
380
+ const resolver = createEnvResolver(pipeline, 'primary', 'macos', 'macos-ventura-xcode-15.1');
381
+ expect(resolver.get('CIBUILD_STACK')).toBe('macos-ventura-xcode-15.1');
382
+ });
383
+ });
384
+ describe('Bitrise variable mapping (FR-9.2)', () => {
385
+ test('should add CIBUILD_BUILD_NUMBER built-in variable', () => {
386
+ const pipeline = {
387
+ format_version: '4',
388
+ workflows: { primary: { steps: [] } },
389
+ };
390
+ const workflow = pipeline.workflows.primary;
391
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
392
+ expect(resolver.get('CIBUILD_BUILD_NUMBER')).toBe('1');
393
+ });
394
+ test('should add CIBUILD_BUILD_URL built-in variable', () => {
395
+ const pipeline = {
396
+ format_version: '4',
397
+ workflows: { primary: { steps: [] } },
398
+ };
399
+ const workflow = pipeline.workflows.primary;
400
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
401
+ expect(resolver.get('CIBUILD_BUILD_URL')).toBe('https://cibuild.io/builds/1');
402
+ });
403
+ test('should add CIBUILD_APP_TITLE built-in variable', () => {
404
+ const pipeline = {
405
+ format_version: '4',
406
+ workflows: { primary: { steps: [] } },
407
+ };
408
+ const workflow = pipeline.workflows.primary;
409
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
410
+ expect(resolver.get('CIBUILD_APP_TITLE')).toBe('CI Build Build');
411
+ });
412
+ test('should use process.env.BUILD_NUMBER for CIBUILD_BUILD_NUMBER', () => {
413
+ process.env.BUILD_NUMBER = '123';
414
+ const pipeline = {
415
+ format_version: '4',
416
+ workflows: { primary: { steps: [] } },
417
+ };
418
+ const workflow = pipeline.workflows.primary;
419
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
420
+ expect(resolver.get('CIBUILD_BUILD_NUMBER')).toBe('123');
421
+ });
422
+ test('should use process.env.BUILD_URL for CIBUILD_BUILD_URL', () => {
423
+ process.env.BUILD_URL = 'https://my-ci.com/build/456';
424
+ const pipeline = {
425
+ format_version: '4',
426
+ workflows: { primary: { steps: [] } },
427
+ };
428
+ const workflow = pipeline.workflows.primary;
429
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
430
+ expect(resolver.get('CIBUILD_BUILD_URL')).toBe('https://my-ci.com/build/456');
431
+ });
432
+ test('should use process.env.APP_TITLE for CIBUILD_APP_TITLE', () => {
433
+ process.env.APP_TITLE = 'My Awesome App';
434
+ const pipeline = {
435
+ format_version: '4',
436
+ workflows: { primary: { steps: [] } },
437
+ };
438
+ const workflow = pipeline.workflows.primary;
439
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
440
+ expect(resolver.get('CIBUILD_APP_TITLE')).toBe('My Awesome App');
441
+ });
442
+ test('should interpolate CIBUILD_BUILD_NUMBER in strings', () => {
443
+ process.env.BUILD_NUMBER = '999';
444
+ const pipeline = {
445
+ format_version: '4',
446
+ workflows: { primary: { steps: [] } },
447
+ };
448
+ const workflow = pipeline.workflows.primary;
449
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
450
+ const result = resolver.interpolate('Build number: $CIBUILD_BUILD_NUMBER');
451
+ expect(result).toBe('Build number: 999');
452
+ });
453
+ test('should support CIBUILD_GIT_REPOSITORY_URL built-in variable', () => {
454
+ process.env.GIT_REPOSITORY_URL = 'https://github.com/user/repo.git';
455
+ const pipeline = {
456
+ format_version: '4',
457
+ workflows: { primary: { steps: [] } },
458
+ };
459
+ const workflow = pipeline.workflows.primary;
460
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
461
+ expect(resolver.get('CIBUILD_GIT_REPOSITORY_URL')).toBe('https://github.com/user/repo.git');
462
+ });
463
+ test('should allow YAML to reference CIBUILD variables naturally', () => {
464
+ process.env.BUILD_NUMBER = '1001';
465
+ const pipeline = {
466
+ format_version: '4',
467
+ workflows: {
468
+ primary: {
469
+ envs: [
470
+ { MESSAGE: 'Build $CIBUILD_BUILD_NUMBER completed at $CIBUILD_BUILD_URL' },
471
+ ],
472
+ steps: [],
473
+ },
474
+ },
475
+ };
476
+ const workflow = pipeline.workflows.primary;
477
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
478
+ expect(resolver.get('MESSAGE')).toContain('Build 1001 completed at');
479
+ expect(resolver.get('MESSAGE')).toContain('https://cibuild.io/builds/1');
480
+ });
481
+ test('should handle CIBUILD variables in complex objects', () => {
482
+ process.env.BUILD_NUMBER = '2020';
483
+ process.env.APP_TITLE = 'Test App';
484
+ const pipeline = {
485
+ format_version: '4',
486
+ workflows: { primary: { steps: [] } },
487
+ };
488
+ const workflow = pipeline.workflows.primary;
489
+ const resolver = new EnvResolver(pipeline, workflow, 'primary', 'linux');
490
+ const obj = {
491
+ cibuild: {
492
+ build: '$CIBUILD_BUILD_NUMBER',
493
+ app: '$CIBUILD_APP_TITLE',
494
+ },
495
+ };
496
+ const result = resolver.interpolateObject(obj);
497
+ expect(result.cibuild.build).toBe('2020');
498
+ expect(result.cibuild.app).toBe('Test App');
499
+ });
500
+ });
501
+ });
502
+ //# sourceMappingURL=env-resolver.test.js.map
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Interactive prompts for missing environment variables
3
+ * Allows users to fill in missing values or cancel the build
4
+ */
5
+ import { SecretsManager } from './secrets-manager.js';
6
+ /**
7
+ * Result of prompting for a missing environment variable
8
+ */
9
+ export interface PromptResult {
10
+ value: string;
11
+ cancelled: boolean;
12
+ copiedToAI?: boolean;
13
+ }
14
+ /**
15
+ * Interactive prompt manager for collecting missing environment variables
16
+ */
17
+ export declare class InteractivePrompts {
18
+ private secretsManager;
19
+ private workflow?;
20
+ constructor(secretsManager: SecretsManager, workflow?: string);
21
+ /**
22
+ * Prompt user for a missing environment variable
23
+ * @param varName Variable name
24
+ * @param stepName Optional step name that requires this variable
25
+ * @param hint Optional hint describing what the variable is for and expected format
26
+ * @returns Promise with the entered value or cancellation
27
+ */
28
+ promptForVariable(varName: string, stepName?: string, hint?: string): Promise<PromptResult>;
29
+ /**
30
+ * Determine if a variable name suggests it's sensitive
31
+ * @param varName Variable name
32
+ * @returns True if variable appears to be sensitive
33
+ */
34
+ private isSensitiveVariable;
35
+ /**
36
+ * Prompt for yes/no confirmation
37
+ * @param message Prompt message
38
+ * @returns Promise with true for yes, false for no
39
+ */
40
+ private promptYesNo;
41
+ /**
42
+ * Process hint text to preserve formatting while wrapping plain text
43
+ * @param hint Hint text (may contain newlines and formatted sections)
44
+ * @param maxWidth Maximum width for wrapping plain text lines
45
+ * @returns Array of processed lines ready for display
46
+ */
47
+ private processHintText;
48
+ /**
49
+ * Wrap text to fit within a specified width
50
+ * @param text Text to wrap
51
+ * @param maxWidth Maximum width per line
52
+ * @returns Array of wrapped lines
53
+ */
54
+ private wrapText;
55
+ /**
56
+ * Generate AI prompt with hint for getting guidance
57
+ * @param hint Hint text from the step
58
+ * @returns Formatted AI prompt
59
+ */
60
+ private generateAIPrompt;
61
+ /**
62
+ * Copy text to system clipboard
63
+ * @param text Text to copy
64
+ */
65
+ private copyToClipboard;
66
+ /**
67
+ * Close the prompts interface (no-op with prompts library)
68
+ */
69
+ close(): void;
70
+ }
71
+ //# sourceMappingURL=interactive-prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-prompts.d.ts","sourceRoot":"","sources":["../../../src/yaml/interactive-prompts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,CAAS;gBAEd,cAAc,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,MAAM;IAK7D;;;;;;OAMG;IACG,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,YAAY,CAAC;IA4FxB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;;;OAIG;YACW,WAAW;IAWzB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IA0BvB;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;IAyBhB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;;OAGG;YACW,eAAe;IAuC7B;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}