@invarn/cibuild 1.3.16 → 1.3.17

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 +46 -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 +57 -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 +351 -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 +474 -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 +16 -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,414 @@
1
+ /**
2
+ * Unit tests for platform detection
3
+ */
4
+ import { describe, test, expect } from '@jest/globals';
5
+ import { PlatformDetector, detectPlatform, detectPlatformInfo, PlatformDetectionError } from './platform-detector.js';
6
+ describe('PlatformDetector', () => {
7
+ describe('Priority 1: Extract from meta.cibuild.io.stack', () => {
8
+ test('should detect macOS from stack', () => {
9
+ const pipeline = {
10
+ format_version: '4',
11
+ meta: {
12
+ 'cibuild.io': {
13
+ stack: 'macos-ventura-xcode-15.1',
14
+ machine_type: 'performance',
15
+ },
16
+ },
17
+ workflows: {
18
+ primary: {
19
+ steps: [{ 'script@1.0.0': { inputs: { content: 'echo "test"' } } }],
20
+ },
21
+ },
22
+ };
23
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
24
+ expect(detector.getPlatform()).toBe('macos');
25
+ expect(detector.getStack()).toBe('macos-ventura-xcode-15.1');
26
+ expect(detector.getMachineType()).toBe('performance');
27
+ });
28
+ test('should detect linux from stack', () => {
29
+ const pipeline = {
30
+ format_version: '4',
31
+ meta: {
32
+ 'cibuild.io': {
33
+ stack: 'linux-docker-android-22.04',
34
+ machine_type: 'standard',
35
+ },
36
+ },
37
+ workflows: {
38
+ primary: {
39
+ steps: [{ 'script@1.0.0': { inputs: { content: 'echo "test"' } } }],
40
+ },
41
+ },
42
+ };
43
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
44
+ expect(detector.getPlatform()).toBe('linux');
45
+ expect(detector.getStack()).toBe('linux-docker-android-22.04');
46
+ expect(detector.getMachineType()).toBe('standard');
47
+ });
48
+ test('should detect windows from stack', () => {
49
+ const pipeline = {
50
+ format_version: '4',
51
+ meta: {
52
+ 'cibuild.io': {
53
+ stack: 'windows-server-2022',
54
+ },
55
+ },
56
+ workflows: {
57
+ primary: {
58
+ steps: [{ 'script@1.0.0': { inputs: { content: 'echo "test"' } } }],
59
+ },
60
+ },
61
+ };
62
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
63
+ expect(detector.getPlatform()).toBe('windows');
64
+ expect(detector.getStack()).toBe('windows-server-2022');
65
+ expect(detector.getMachineType()).toBeNull();
66
+ });
67
+ });
68
+ describe('Priority 2: Use meta.platform field', () => {
69
+ test('should use meta.platform when stack not specified', () => {
70
+ const pipeline = {
71
+ format_version: '4',
72
+ meta: {
73
+ platform: 'macos',
74
+ },
75
+ workflows: {
76
+ primary: {
77
+ steps: [{ 'script@1.0.0': { inputs: { content: 'echo "test"' } } }],
78
+ },
79
+ },
80
+ };
81
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
82
+ expect(detector.getPlatform()).toBe('macos');
83
+ expect(detector.getStack()).toBeNull();
84
+ expect(detector.getMachineType()).toBeNull();
85
+ });
86
+ test('should use meta.platform: linux', () => {
87
+ const pipeline = {
88
+ format_version: '4',
89
+ meta: {
90
+ platform: 'linux',
91
+ },
92
+ workflows: {
93
+ primary: {
94
+ steps: [{ 'script@1.0.0': { inputs: { content: 'echo "test"' } } }],
95
+ },
96
+ },
97
+ };
98
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
99
+ expect(detector.getPlatform()).toBe('linux');
100
+ });
101
+ });
102
+ describe('Priority 3: Auto-detect from workflow steps', () => {
103
+ test('should detect macOS from xcodebuild step', () => {
104
+ const pipeline = {
105
+ format_version: '4',
106
+ workflows: {
107
+ primary: {
108
+ steps: [
109
+ { 'git-clone@4.0.17': { inputs: {} } },
110
+ { 'xcodebuild@5.0.0': { inputs: { project_path: './MyApp.xcodeproj' } } },
111
+ ],
112
+ },
113
+ },
114
+ };
115
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
116
+ expect(detector.getPlatform()).toBe('macos');
117
+ });
118
+ test('should detect macOS from xcode-test step', () => {
119
+ const pipeline = {
120
+ format_version: '4',
121
+ workflows: {
122
+ primary: {
123
+ steps: [
124
+ { 'xcode-test@2.0.0': { inputs: { project_path: './MyApp.xcodeproj' } } },
125
+ ],
126
+ },
127
+ },
128
+ };
129
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
130
+ expect(detector.getPlatform()).toBe('macos');
131
+ });
132
+ test('should detect linux from gradle-build step', () => {
133
+ const pipeline = {
134
+ format_version: '4',
135
+ workflows: {
136
+ primary: {
137
+ steps: [
138
+ { 'git-clone@4.0.17': { inputs: {} } },
139
+ { 'gradle-build@1.0.0': { inputs: { project_location: './android' } } },
140
+ ],
141
+ },
142
+ },
143
+ };
144
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
145
+ expect(detector.getPlatform()).toBe('linux');
146
+ });
147
+ test('should detect linux from android-lint step', () => {
148
+ const pipeline = {
149
+ format_version: '4',
150
+ workflows: {
151
+ primary: {
152
+ steps: [
153
+ { 'android-lint@1.0.0': { inputs: { project_location: './android' } } },
154
+ ],
155
+ },
156
+ },
157
+ };
158
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
159
+ expect(detector.getPlatform()).toBe('linux');
160
+ });
161
+ test('should detect linux from android-unit-test step', () => {
162
+ const pipeline = {
163
+ format_version: '4',
164
+ workflows: {
165
+ primary: {
166
+ steps: [
167
+ { 'android-unit-test@1.0.0': { inputs: { project_location: './android' } } },
168
+ ],
169
+ },
170
+ },
171
+ };
172
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
173
+ expect(detector.getPlatform()).toBe('linux');
174
+ });
175
+ test('should detect linux from set-java-version step', () => {
176
+ const pipeline = {
177
+ format_version: '4',
178
+ workflows: {
179
+ primary: {
180
+ steps: [
181
+ { 'set-java-version@1.0.0': { inputs: { java_version: '17' } } },
182
+ ],
183
+ },
184
+ },
185
+ };
186
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
187
+ expect(detector.getPlatform()).toBe('linux');
188
+ });
189
+ test('should default to linux for platform-agnostic steps', () => {
190
+ const pipeline = {
191
+ format_version: '4',
192
+ workflows: {
193
+ primary: {
194
+ steps: [
195
+ { 'git-clone@4.0.17': { inputs: {} } },
196
+ { 'script@1.0.0': { inputs: { content: 'npm install' } } },
197
+ { 'cache-push@2.0.0': { inputs: { cache_paths: 'node_modules' } } },
198
+ ],
199
+ },
200
+ },
201
+ };
202
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
203
+ expect(detector.getPlatform()).toBe('linux');
204
+ });
205
+ });
206
+ describe('Mixed platform detection (FR-37)', () => {
207
+ test('should throw error for mixed macOS and Linux steps', () => {
208
+ const pipeline = {
209
+ format_version: '4',
210
+ workflows: {
211
+ primary: {
212
+ steps: [
213
+ { 'xcodebuild@5.0.0': { inputs: { project_path: './ios' } } },
214
+ { 'gradle-build@1.0.0': { inputs: { project_location: './android' } } },
215
+ ],
216
+ },
217
+ },
218
+ };
219
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
220
+ expect(() => detector.getPlatform()).toThrow(PlatformDetectionError);
221
+ expect(() => detector.getPlatform()).toThrow(/contains steps for multiple platforms/);
222
+ expect(() => detector.getPlatform()).toThrow(/xcodebuild/);
223
+ expect(() => detector.getPlatform()).toThrow(/gradle-build/);
224
+ });
225
+ test('should throw error listing all mixed platform steps', () => {
226
+ const pipeline = {
227
+ format_version: '4',
228
+ workflows: {
229
+ primary: {
230
+ steps: [
231
+ { 'xcodebuild@5.0.0': { inputs: {} } },
232
+ { 'xcode-test@2.0.0': { inputs: {} } },
233
+ { 'gradle-build@1.0.0': { inputs: {} } },
234
+ { 'android-lint@1.0.0': { inputs: {} } },
235
+ ],
236
+ },
237
+ },
238
+ };
239
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
240
+ expect(() => detector.getPlatform()).toThrow(/xcodebuild.*xcode-test/);
241
+ expect(() => detector.getPlatform()).toThrow(/gradle-build.*android-lint/);
242
+ });
243
+ });
244
+ describe('Step name parsing', () => {
245
+ test('should parse step name with version', () => {
246
+ const pipeline = {
247
+ format_version: '4',
248
+ workflows: {
249
+ primary: {
250
+ steps: [
251
+ { 'xcodebuild@5.0.0': { inputs: {} } },
252
+ ],
253
+ },
254
+ },
255
+ };
256
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
257
+ expect(detector.getPlatform()).toBe('macos');
258
+ });
259
+ test('should parse step name without version', () => {
260
+ const pipeline = {
261
+ format_version: '4',
262
+ workflows: {
263
+ primary: {
264
+ steps: [
265
+ { xcodebuild: { inputs: {} } },
266
+ ],
267
+ },
268
+ },
269
+ };
270
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
271
+ expect(detector.getPlatform()).toBe('macos');
272
+ });
273
+ });
274
+ describe('getPlatformInfo()', () => {
275
+ test('should return complete platform info with all fields', () => {
276
+ const pipeline = {
277
+ format_version: '4',
278
+ meta: {
279
+ 'cibuild.io': {
280
+ stack: 'macos-ventura-xcode-15.1',
281
+ machine_type: 'performance',
282
+ },
283
+ },
284
+ workflows: {
285
+ primary: {
286
+ steps: [{ 'script@1.0.0': { inputs: {} } }],
287
+ },
288
+ },
289
+ };
290
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
291
+ const info = detector.getPlatformInfo();
292
+ expect(info.platform).toBe('macos');
293
+ expect(info.stack).toBe('macos-ventura-xcode-15.1');
294
+ expect(info.machineType).toBe('performance');
295
+ });
296
+ test('should return platform info with only platform field', () => {
297
+ const pipeline = {
298
+ format_version: '4',
299
+ workflows: {
300
+ primary: {
301
+ steps: [{ 'xcodebuild@5.0.0': { inputs: {} } }],
302
+ },
303
+ },
304
+ };
305
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
306
+ const info = detector.getPlatformInfo();
307
+ expect(info.platform).toBe('macos');
308
+ expect(info.stack).toBeUndefined();
309
+ expect(info.machineType).toBeUndefined();
310
+ });
311
+ });
312
+ describe('Helper functions', () => {
313
+ test('detectPlatform() should work', () => {
314
+ const pipeline = {
315
+ format_version: '4',
316
+ workflows: {
317
+ primary: {
318
+ steps: [{ 'xcodebuild@5.0.0': { inputs: {} } }],
319
+ },
320
+ },
321
+ };
322
+ const platform = detectPlatform(pipeline, 'primary');
323
+ expect(platform).toBe('macos');
324
+ });
325
+ test('detectPlatform() should throw for non-existent workflow', () => {
326
+ const pipeline = {
327
+ format_version: '4',
328
+ workflows: {
329
+ primary: {
330
+ steps: [{ 'xcodebuild@5.0.0': { inputs: {} } }],
331
+ },
332
+ },
333
+ };
334
+ expect(() => detectPlatform(pipeline, 'nonexistent')).toThrow(/not found/);
335
+ });
336
+ test('detectPlatformInfo() should work', () => {
337
+ const pipeline = {
338
+ format_version: '4',
339
+ meta: {
340
+ 'cibuild.io': {
341
+ stack: 'linux-docker-android-22.04',
342
+ },
343
+ },
344
+ workflows: {
345
+ build: {
346
+ steps: [{ 'gradle-build@1.0.0': { inputs: {} } }],
347
+ },
348
+ },
349
+ };
350
+ const info = detectPlatformInfo(pipeline, 'build');
351
+ expect(info.platform).toBe('linux');
352
+ expect(info.stack).toBe('linux-docker-android-22.04');
353
+ });
354
+ test('detectPlatformInfo() should throw for non-existent workflow', () => {
355
+ const pipeline = {
356
+ format_version: '4',
357
+ workflows: {
358
+ primary: {
359
+ steps: [{ 'script@1.0.0': { inputs: {} } }],
360
+ },
361
+ },
362
+ };
363
+ expect(() => detectPlatformInfo(pipeline, 'missing')).toThrow(/not found/);
364
+ });
365
+ });
366
+ describe('Edge cases', () => {
367
+ test('should handle empty steps array (defaults to linux)', () => {
368
+ const pipeline = {
369
+ format_version: '4',
370
+ workflows: {
371
+ primary: {
372
+ steps: [],
373
+ },
374
+ },
375
+ };
376
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
377
+ expect(detector.getPlatform()).toBe('linux');
378
+ });
379
+ test('should handle stack with priority over platform field', () => {
380
+ const pipeline = {
381
+ format_version: '4',
382
+ meta: {
383
+ 'cibuild.io': {
384
+ stack: 'macos-ventura-xcode-15.1',
385
+ },
386
+ platform: 'linux', // This should be ignored
387
+ },
388
+ workflows: {
389
+ primary: {
390
+ steps: [{ 'script@1.0.0': { inputs: {} } }],
391
+ },
392
+ },
393
+ };
394
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
395
+ expect(detector.getPlatform()).toBe('macos'); // Stack takes priority
396
+ });
397
+ test('should handle platform field with priority over step detection', () => {
398
+ const pipeline = {
399
+ format_version: '4',
400
+ meta: {
401
+ platform: 'linux',
402
+ },
403
+ workflows: {
404
+ primary: {
405
+ steps: [{ 'xcodebuild@5.0.0': { inputs: {} } }], // Would auto-detect as macOS
406
+ },
407
+ },
408
+ };
409
+ const detector = new PlatformDetector(pipeline, pipeline.workflows.primary, 'primary');
410
+ expect(detector.getPlatform()).toBe('linux'); // Explicit platform takes priority
411
+ });
412
+ });
413
+ });
414
+ //# sourceMappingURL=platform-detector.test.js.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Pre-flight validation for mandatory workflow requirements
3
+ * Validates repository access before workflow execution begins
4
+ */
5
+ import { MissingEnvironmentVariableError } from './env-resolver.js';
6
+ import type { SecretsManager } from './secrets-manager.js';
7
+ import type { YAMLWorkflow } from './types.js';
8
+ /**
9
+ * Pre-flight validation results
10
+ */
11
+ export interface PreFlightResult {
12
+ success: boolean;
13
+ error?: MissingEnvironmentVariableError;
14
+ }
15
+ /**
16
+ * Validates that required environment variables exist and repository is accessible
17
+ * This runs before any workflow steps execute
18
+ */
19
+ export declare class PreFlightValidator {
20
+ /**
21
+ * Check if workflow has activate-ssh-key step before git-clone
22
+ * @param workflow The workflow to check
23
+ * @returns True if SSH key setup is in the workflow
24
+ */
25
+ private hasSSHKeySetup;
26
+ /**
27
+ * Validates CIBUILD_GIT_REPOSITORY_URL exists and is accessible
28
+ * @param env Environment variables
29
+ * @param secretsManager Secrets manager to check for stored secrets
30
+ * @param workflow The workflow to check for SSH key setup
31
+ * @returns Validation result
32
+ */
33
+ validateRepositoryAccess(env: Record<string, string>, secretsManager?: SecretsManager, workflow?: YAMLWorkflow): Promise<PreFlightResult>;
34
+ /**
35
+ * Run all pre-flight validations
36
+ * git-clone now uses the local checkout — no remote URL or connectivity check needed.
37
+ */
38
+ validate(_env: Record<string, string>, _secretsManager?: SecretsManager, _workflow?: YAMLWorkflow): Promise<PreFlightResult>;
39
+ }
40
+ //# sourceMappingURL=preflight-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight-validation.d.ts","sourceRoot":"","sources":["../../../src/yaml/preflight-validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,+BAA+B,CAAC;CACzC;AAED;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B;;;;OAIG;IACH,OAAO,CAAC,cAAc;IA0BtB;;;;;;OAMG;IACG,wBAAwB,CAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,cAAc,CAAC,EAAE,cAAc,EAC/B,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,eAAe,CAAC;IA2H3B;;;OAGG;IACG,QAAQ,CACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,eAAe,CAAC,EAAE,cAAc,EAChC,SAAS,CAAC,EAAE,YAAY,GACvB,OAAO,CAAC,eAAe,CAAC;CAG5B"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Pre-flight validation for mandatory workflow requirements
3
+ * Validates repository access before workflow execution begins
4
+ */
5
+ import { execSync } from 'child_process';
6
+ import { MissingEnvironmentVariableError } from './env-resolver.js';
7
+ /**
8
+ * Validates that required environment variables exist and repository is accessible
9
+ * This runs before any workflow steps execute
10
+ */
11
+ export class PreFlightValidator {
12
+ /**
13
+ * Check if workflow has activate-ssh-key step before git-clone
14
+ * @param workflow The workflow to check
15
+ * @returns True if SSH key setup is in the workflow
16
+ */
17
+ hasSSHKeySetup(workflow) {
18
+ if (!workflow.steps || workflow.steps.length === 0) {
19
+ return false;
20
+ }
21
+ let foundSSHKeyStep = false;
22
+ for (const step of workflow.steps) {
23
+ // Step can be an object with step name as key
24
+ const stepName = typeof step === 'string' ? step : Object.keys(step)[0];
25
+ // Check for git-clone step first - if we hit it before finding SSH key, return false
26
+ if (stepName.startsWith('git-clone')) {
27
+ return foundSSHKeyStep; // Return true only if we found SSH key before this
28
+ }
29
+ // Check for activate-ssh-key step
30
+ if (stepName.startsWith('activate-ssh-key')) {
31
+ foundSSHKeyStep = true;
32
+ }
33
+ }
34
+ // If we got here, no git-clone step was found, so return if SSH key exists
35
+ return foundSSHKeyStep;
36
+ }
37
+ /**
38
+ * Validates CIBUILD_GIT_REPOSITORY_URL exists and is accessible
39
+ * @param env Environment variables
40
+ * @param secretsManager Secrets manager to check for stored secrets
41
+ * @param workflow The workflow to check for SSH key setup
42
+ * @returns Validation result
43
+ */
44
+ async validateRepositoryAccess(env, secretsManager, workflow) {
45
+ // Check environment variables first, then secrets manager
46
+ let repoUrl = env.CIBUILD_GIT_REPOSITORY_URL || env.GIT_REPOSITORY_URL || '';
47
+ // If not in env, check secrets manager
48
+ if ((!repoUrl || repoUrl.trim() === '') && secretsManager) {
49
+ const secretId = secretsManager.getSecretIdByName('CIBUILD_GIT_REPOSITORY_URL');
50
+ if (secretId) {
51
+ repoUrl = secretsManager.getSecret(secretId) || '';
52
+ }
53
+ }
54
+ // Check if repository URL is provided
55
+ if (!repoUrl || repoUrl.trim() === '') {
56
+ return {
57
+ success: false,
58
+ error: new MissingEnvironmentVariableError('CIBUILD_GIT_REPOSITORY_URL', 'preflight-validation', 'Git repository URL for cloning source code in CI/CD builds.\n\n' +
59
+ 'Expected format:\n' +
60
+ ' ┌────────────────────────────────────────────────────────┐\n' +
61
+ ' │ https://github.com/username/repository.git │\n' +
62
+ ' │ git@github.com:username/repository.git │\n' +
63
+ ' │ https://gitlab.com/username/repository.git │\n' +
64
+ ' └────────────────────────────────────────────────────────┘\n\n' +
65
+ 'How to set it:\n' +
66
+ ' • Environment variable: GIT_REPOSITORY_URL=<your-repo-url>\n' +
67
+ ' • In YAML inputs: repository: <your-repo-url>\n' +
68
+ ' • CI platform automatically provides this in most cases'),
69
+ };
70
+ }
71
+ // If the workflow uses activate-ssh-key, skip connectivity test entirely.
72
+ // SSH authentication isn't available pre-execution, so any network test would fail
73
+ // for private repos regardless of whether the URL is SSH or HTTPS format.
74
+ const hasSSHKeyInWorkflow = workflow ? this.hasSSHKeySetup(workflow) : false;
75
+ if (hasSSHKeyInWorkflow) {
76
+ console.log('🔍 Pre-flight: Validating repository URL...');
77
+ console.log(` Repository: ${repoUrl}`);
78
+ console.log(' SSH authentication will be configured by activate-ssh-key step');
79
+ console.log('✅ Pre-flight: Repository URL validated (connectivity test skipped)\n');
80
+ return { success: true };
81
+ }
82
+ // Test repository connectivity using git ls-remote
83
+ try {
84
+ console.log('🔍 Pre-flight: Validating repository access...');
85
+ console.log(` Repository: ${repoUrl}`);
86
+ // Execute git ls-remote to check if repository is accessible
87
+ // This command lists remote references without cloning
88
+ // Timeout after 10 seconds to prevent hanging
89
+ execSync(`git ls-remote "${repoUrl}" HEAD`, {
90
+ stdio: 'pipe',
91
+ timeout: 10000,
92
+ encoding: 'utf-8',
93
+ });
94
+ console.log('✅ Pre-flight: Repository is accessible\n');
95
+ return { success: true };
96
+ }
97
+ catch (error) {
98
+ // Repository is not accessible - analyze the error
99
+ const errorMessage = error.message || String(error);
100
+ console.log(`❌ Pre-flight: Repository access failed`);
101
+ console.log(` Error: ${errorMessage}\n`);
102
+ // Check if this is an SSH authentication error
103
+ const isSSHAuthError = errorMessage.includes('Permission denied') &&
104
+ errorMessage.includes('publickey');
105
+ const isSSHUrl = repoUrl.startsWith('git@') || repoUrl.includes('ssh://');
106
+ if (isSSHAuthError || (isSSHUrl && errorMessage.includes('Could not read from remote'))) {
107
+ // SSH authentication is the issue, not the repository URL
108
+ console.log('⚠️ Repository URL is valid, but SSH authentication is required.\n');
109
+ console.log('Please ensure:');
110
+ console.log(' 1. SSH key is configured in your workflow (activate-ssh-key step)');
111
+ console.log(' 2. SSH key has access to this repository');
112
+ console.log(' 3. SSH key is added to your GitHub/GitLab account\n');
113
+ throw new Error('Pre-flight validation failed: SSH authentication required.\n\n' +
114
+ `Repository: ${repoUrl}\n\n` +
115
+ 'The repository URL is valid but requires SSH authentication.\n' +
116
+ 'Make sure your workflow includes an "activate-ssh-key" step before "git-clone".\n\n' +
117
+ 'Example workflow:\n' +
118
+ ' steps:\n' +
119
+ ' - activate-ssh-key@4: # Add SSH key first\n' +
120
+ ' inputs:\n' +
121
+ ' ssh_key_save_path: $HOME/.ssh/bitrise_rsa\n' +
122
+ ' - git-clone@6: # Then clone\n\n' +
123
+ `Error details: ${errorMessage}`);
124
+ }
125
+ // For other errors (invalid URL, network issues, etc.), return error
126
+ return {
127
+ success: false,
128
+ error: new MissingEnvironmentVariableError('CIBUILD_GIT_REPOSITORY_URL', 'preflight-validation', 'Git repository URL validation failed. The repository is not accessible.\n\n' +
129
+ 'Expected format:\n' +
130
+ ' ┌────────────────────────────────────────────────────────┐\n' +
131
+ ' │ https://github.com/username/repository.git │\n' +
132
+ ' │ git@github.com:username/repository.git │\n' +
133
+ ' │ https://gitlab.com/username/repository.git │\n' +
134
+ ' └────────────────────────────────────────────────────────┘\n\n' +
135
+ 'Common issues:\n' +
136
+ ' • Invalid repository URL\n' +
137
+ ' • Network connectivity issues\n' +
138
+ ' • Repository does not exist or is private\n' +
139
+ ' • Wrong URL format (check for typos)\n\n' +
140
+ `Error details: ${errorMessage}`),
141
+ };
142
+ }
143
+ }
144
+ /**
145
+ * Run all pre-flight validations
146
+ * git-clone now uses the local checkout — no remote URL or connectivity check needed.
147
+ */
148
+ async validate(_env, _secretsManager, _workflow) {
149
+ return { success: true };
150
+ }
151
+ }
152
+ //# sourceMappingURL=preflight-validation.js.map