@invarn/cibuild 1.3.15 → 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,193 @@
1
+ /**
2
+ * Google Play deploy step implementation
3
+ * Uploads APK/AAB to Google Play using fastlane supply
4
+ */
5
+ import { BaseStepExecutor } from './base.js';
6
+ /**
7
+ * Google Play deploy step executor
8
+ * Maps google-play-deploy to fastlane supply
9
+ */
10
+ export class GooglePlayDeployStepExecutor extends BaseStepExecutor {
11
+ isSkippedInLocalMode() {
12
+ return true;
13
+ }
14
+ getValidationRequirements(inputs, env, _config) {
15
+ const requirements = [];
16
+ // service_account_json_key_path only required when JSON content is not available via secret
17
+ const jsonVarName = inputs.service_account_json_var || 'GOOGLE_PLAY_SERVICE_ACCOUNT_JSON';
18
+ if (!env[jsonVarName]) {
19
+ requirements.push(this.requireInput('service_account_json_key_path', inputs, 'Path to service account JSON key file'));
20
+ }
21
+ requirements.push(this.requireInput('package_name', inputs, 'Android app package name'));
22
+ requirements.push(this.requireCommand('fastlane', 'Fastlane for Google Play deployment', 'Install with: gem install fastlane'));
23
+ return requirements;
24
+ }
25
+ async execute(inputs, env, config) {
26
+ const stepName = 'google-play-deploy';
27
+ const commands = [];
28
+ // In local mode, skip entirely — no Google Play credentials available
29
+ if (config.local) {
30
+ commands.push('echo "⏭️ google-play-deploy: skipped in local mode"');
31
+ commands.push('echo " Google Play upload requires CI credentials."');
32
+ const script = this.createBashScriptFromCommands(commands, stepName);
33
+ return this.createScriptStep(script, stepName);
34
+ }
35
+ const packageName = this.getInput(inputs, 'package_name', '');
36
+ const artifactType = this.getInput(inputs, 'artifact_type', 'apk');
37
+ const isAab = artifactType === 'aab';
38
+ const defaultAppPath = isAab
39
+ ? '$CIBUILD_AAB_PATH\n$CIBUILD_APK_PATH'
40
+ : '$CIBUILD_APK_PATH\n$CIBUILD_AAB_PATH';
41
+ const appPath = this.getInput(inputs, 'app_path', defaultAppPath);
42
+ const track = this.getInput(inputs, 'track', 'alpha');
43
+ const userFraction = this.getInput(inputs, 'user_fraction', '');
44
+ const status = this.getInput(inputs, 'status', '');
45
+ const whatsnewsDir = this.getInput(inputs, 'whatsnews_dir', '');
46
+ const mappingFile = this.getInput(inputs, 'mapping_file', '$CIBUILD_MAPPING_PATH');
47
+ const dryRun = this.getInput(inputs, 'dry_run', 'false');
48
+ const verboseLog = this.getInput(inputs, 'verbose_log', 'false');
49
+ commands.push('# google-play-deploy step → fastlane supply');
50
+ commands.push('echo "🚀 Deploying to Google Play..."');
51
+ commands.push('');
52
+ // Service account JSON: prefer content from secret, fall back to file path
53
+ const jsonVarName = this.getInput(inputs, 'service_account_json_var', 'GOOGLE_PLAY_SERVICE_ACCOUNT_JSON');
54
+ const jsonContent = env[jsonVarName] || '';
55
+ if (jsonContent) {
56
+ const encoded = Buffer.from(jsonContent).toString('base64');
57
+ commands.push('# Write service account JSON from GOOGLE_PLAY_SERVICE_ACCOUNT_JSON secret');
58
+ commands.push('_SA_TEMP_FILE=""');
59
+ commands.push('_cleanup_sa() { [ -n "$_SA_TEMP_FILE" ] && rm -f "$_SA_TEMP_FILE" 2>/dev/null || true; }');
60
+ commands.push('trap _cleanup_sa EXIT');
61
+ commands.push('_SA_TEMP_FILE="$(mktemp /tmp/google-play-sa-XXXXXX.json)"');
62
+ commands.push(`printf '%s' '${encoded}' | base64 -d > "$_SA_TEMP_FILE"`);
63
+ commands.push('SERVICE_ACCOUNT_KEY="$_SA_TEMP_FILE"');
64
+ commands.push('echo "Service account JSON written to temp file"');
65
+ commands.push('');
66
+ }
67
+ else {
68
+ const serviceAccountKey = this.getInput(inputs, 'service_account_json_key_path', '');
69
+ commands.push(`SERVICE_ACCOUNT_KEY="${this.escapeBash(serviceAccountKey)}"`);
70
+ commands.push('');
71
+ }
72
+ commands.push(`PACKAGE_NAME="${this.escapeBash(packageName)}"`);
73
+ commands.push('');
74
+ commands.push('if [ -z "$SERVICE_ACCOUNT_KEY" ]; then');
75
+ commands.push(' echo "❌ Error: service_account_json_key_path is required"');
76
+ commands.push(' echo " Or set GOOGLE_PLAY_SERVICE_ACCOUNT_JSON in your CI secrets"');
77
+ commands.push(' exit 1');
78
+ commands.push('fi');
79
+ commands.push('');
80
+ commands.push('if [ ! -f "$SERVICE_ACCOUNT_KEY" ]; then');
81
+ commands.push(' echo "❌ Error: Service account key file not found: $SERVICE_ACCOUNT_KEY"');
82
+ commands.push(' exit 1');
83
+ commands.push('fi');
84
+ commands.push('');
85
+ commands.push('if [ -z "$PACKAGE_NAME" ]; then');
86
+ commands.push(' echo "❌ Error: package_name is required"');
87
+ commands.push(' exit 1');
88
+ commands.push('fi');
89
+ commands.push('');
90
+ // Resolve app file from app_path (newline-separated list)
91
+ commands.push('# Locate app file (APK or AAB)');
92
+ commands.push('APP_FILE=""');
93
+ commands.push('');
94
+ // Build a list of candidates from app_path
95
+ const appPathLines = appPath.split('\n').map(l => l.trim()).filter(l => l.length > 0);
96
+ if (appPathLines.length > 0) {
97
+ commands.push('# Check configured app_path candidates');
98
+ for (const candidate of appPathLines) {
99
+ commands.push(`if [ -z "$APP_FILE" ] && [ -f "${this.escapeBash(candidate)}" ]; then`);
100
+ commands.push(` APP_FILE="${this.escapeBash(candidate)}"`);
101
+ commands.push('fi');
102
+ }
103
+ commands.push('');
104
+ }
105
+ // Fall back to env-var resolved paths at runtime (order matches artifact_type preference)
106
+ commands.push('# Fall back to environment variable paths if not yet resolved');
107
+ const primaryEnv = isAab ? 'CIBUILD_AAB_PATH' : 'CIBUILD_APK_PATH';
108
+ const fallbackEnv = isAab ? 'CIBUILD_APK_PATH' : 'CIBUILD_AAB_PATH';
109
+ commands.push(`if [ -z "$APP_FILE" ] && [ -n "\${${primaryEnv}:-}" ] && [ -f "$${primaryEnv}" ]; then`);
110
+ commands.push(` APP_FILE="$${primaryEnv}"`);
111
+ commands.push('fi');
112
+ commands.push(`if [ -z "$APP_FILE" ] && [ -n "\${${fallbackEnv}:-}" ] && [ -f "$${fallbackEnv}" ]; then`);
113
+ commands.push(` APP_FILE="$${fallbackEnv}"`);
114
+ commands.push('fi');
115
+ commands.push('');
116
+ commands.push('if [ -z "$APP_FILE" ]; then');
117
+ commands.push(' echo "❌ Error: No APK or AAB file found."');
118
+ commands.push(' echo " Set app_path input or ensure CIBUILD_APK_PATH / CIBUILD_AAB_PATH is set."');
119
+ commands.push(' exit 1');
120
+ commands.push('fi');
121
+ commands.push('');
122
+ commands.push('echo "App file: $APP_FILE"');
123
+ commands.push('');
124
+ // Detect AAB vs APK
125
+ commands.push('# Detect format: AAB or APK');
126
+ commands.push('APP_FLAG=""');
127
+ commands.push('if [[ "$APP_FILE" == *.aab ]]; then');
128
+ commands.push(' APP_FLAG="--aab"');
129
+ commands.push(' echo "Format: AAB (Android App Bundle)"');
130
+ commands.push('else');
131
+ commands.push(' APP_FLAG="--apk"');
132
+ commands.push(' echo "Format: APK"');
133
+ commands.push('fi');
134
+ commands.push('');
135
+ // Resolve mapping file at runtime
136
+ commands.push('# Resolve mapping file');
137
+ commands.push(`MAPPING_FILE="${this.escapeBash(mappingFile)}"`);
138
+ commands.push('MAPPING_FLAG=""');
139
+ commands.push('if [ -n "$MAPPING_FILE" ] && [ -f "$MAPPING_FILE" ]; then');
140
+ commands.push(' MAPPING_FLAG="--mapping \\"$MAPPING_FILE\\""');
141
+ commands.push(' echo "Mapping file: $MAPPING_FILE"');
142
+ commands.push('fi');
143
+ commands.push('');
144
+ // Build fastlane supply command
145
+ commands.push('# Build fastlane supply command');
146
+ commands.push(`TRACK="${this.escapeBash(track)}"`);
147
+ commands.push('');
148
+ commands.push('SUPPLY_CMD="fastlane supply"');
149
+ commands.push('SUPPLY_CMD="$SUPPLY_CMD --json_key \\"$SERVICE_ACCOUNT_KEY\\""');
150
+ commands.push('SUPPLY_CMD="$SUPPLY_CMD --package_name \\"$PACKAGE_NAME\\""');
151
+ commands.push('SUPPLY_CMD="$SUPPLY_CMD $APP_FLAG \\"$APP_FILE\\""');
152
+ commands.push('SUPPLY_CMD="$SUPPLY_CMD --track \\"$TRACK\\""');
153
+ commands.push('');
154
+ if (status) {
155
+ commands.push(`SUPPLY_CMD="$SUPPLY_CMD --release_status \\"${this.escapeBash(status)}\\""`);
156
+ }
157
+ if (userFraction) {
158
+ commands.push(`SUPPLY_CMD="$SUPPLY_CMD --rollout \\"${this.escapeBash(userFraction)}\\""`);
159
+ }
160
+ if (whatsnewsDir) {
161
+ commands.push(`SUPPLY_CMD="$SUPPLY_CMD --whatsnew_paths \\"${this.escapeBash(whatsnewsDir)}\\""`);
162
+ }
163
+ commands.push('if [ -n "$MAPPING_FLAG" ]; then');
164
+ commands.push(' SUPPLY_CMD="$SUPPLY_CMD $MAPPING_FLAG"');
165
+ commands.push('fi');
166
+ commands.push('');
167
+ if (verboseLog === 'true') {
168
+ commands.push('SUPPLY_CMD="$SUPPLY_CMD --verbose"');
169
+ }
170
+ if (dryRun === 'true') {
171
+ commands.push('SUPPLY_CMD="$SUPPLY_CMD --dry_run"');
172
+ }
173
+ commands.push('');
174
+ commands.push('echo "Running: $SUPPLY_CMD"');
175
+ commands.push('echo ""');
176
+ commands.push('');
177
+ commands.push('# Execute fastlane supply');
178
+ commands.push('eval "$SUPPLY_CMD"');
179
+ commands.push('SUPPLY_EXIT=$?');
180
+ commands.push('');
181
+ commands.push('if [ $SUPPLY_EXIT -eq 0 ]; then');
182
+ commands.push(' echo ""');
183
+ commands.push(' echo "✅ Successfully deployed to Google Play (track: $TRACK)"');
184
+ commands.push('else');
185
+ commands.push(' echo ""');
186
+ commands.push(' echo "❌ Failed to deploy to Google Play (exit code: $SUPPLY_EXIT)"');
187
+ commands.push(' exit 1');
188
+ commands.push('fi');
189
+ const script = this.createBashScriptFromCommands(commands, stepName);
190
+ return this.createScriptStep(script, stepName);
191
+ }
192
+ }
193
+ //# sourceMappingURL=google-play-deploy.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Unit tests for Google Play deploy step
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=google-play-deploy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-play-deploy.test.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/google-play-deploy.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Unit tests for Google Play deploy step
3
+ */
4
+ import { describe, test, expect } from '@jest/globals';
5
+ import { GooglePlayDeployStepExecutor } from './google-play-deploy.js';
6
+ describe('GooglePlayDeployStepExecutor', () => {
7
+ const mockConfig = {
8
+ artifactsDir: '/ci/artifacts',
9
+ interpreters: {
10
+ python: '/usr/bin/python3',
11
+ ruby: '/usr/bin/ruby',
12
+ node: '/usr/bin/node',
13
+ bash: '/bin/bash',
14
+ },
15
+ maxConcurrentJobs: 4,
16
+ paths: {
17
+ buildsDir: '/ci/builds',
18
+ cacheDir: '/ci/cache',
19
+ derivedDataDir: '/ci/derived_data',
20
+ },
21
+ };
22
+ const localConfig = { ...mockConfig, local: true };
23
+ describe('Basic functionality', () => {
24
+ test('should create a script step with correct name', async () => {
25
+ const executor = new GooglePlayDeployStepExecutor();
26
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
27
+ expect(result.kind).toBe('script');
28
+ expect(result.name).toBe('google-play-deploy');
29
+ });
30
+ test('should call fastlane supply', async () => {
31
+ const executor = new GooglePlayDeployStepExecutor();
32
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
33
+ expect(result.script).toContain('fastlane supply');
34
+ });
35
+ test('should include step label', async () => {
36
+ const executor = new GooglePlayDeployStepExecutor();
37
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
38
+ expect(result.script).toContain('google-play-deploy step → fastlane supply');
39
+ });
40
+ });
41
+ describe('Local mode', () => {
42
+ test('should skip in local mode', async () => {
43
+ const executor = new GooglePlayDeployStepExecutor();
44
+ const result = await executor.execute({}, {}, localConfig);
45
+ expect(result.script).toContain('skipped in local mode');
46
+ expect(result.script).not.toContain('fastlane supply');
47
+ });
48
+ test('isSkippedInLocalMode should return true', () => {
49
+ const executor = new GooglePlayDeployStepExecutor();
50
+ expect(executor.isSkippedInLocalMode()).toBe(true);
51
+ });
52
+ });
53
+ describe('Required input validation', () => {
54
+ test('should error if service_account_json_key_path is empty', async () => {
55
+ const executor = new GooglePlayDeployStepExecutor();
56
+ const result = await executor.execute({ package_name: 'com.example.app' }, {}, mockConfig);
57
+ expect(result.script).toContain('service_account_json_key_path is required');
58
+ expect(result.script).toContain('exit 1');
59
+ });
60
+ test('should check that service account key file exists on disk', async () => {
61
+ const executor = new GooglePlayDeployStepExecutor();
62
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
63
+ expect(result.script).toContain('Service account key file not found');
64
+ expect(result.script).toContain('if [ ! -f "$SERVICE_ACCOUNT_KEY" ]');
65
+ });
66
+ test('should error if package_name is empty', async () => {
67
+ const executor = new GooglePlayDeployStepExecutor();
68
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json' }, {}, mockConfig);
69
+ expect(result.script).toContain('package_name is required');
70
+ expect(result.script).toContain('exit 1');
71
+ });
72
+ });
73
+ describe('App file handling', () => {
74
+ test('should use provided app_path', async () => {
75
+ const executor = new GooglePlayDeployStepExecutor();
76
+ const result = await executor.execute({
77
+ service_account_json_key_path: '/keys/service.json',
78
+ package_name: 'com.example.app',
79
+ app_path: '/build/app-release.apk',
80
+ }, {}, mockConfig);
81
+ expect(result.script).toContain('/build/app-release.apk');
82
+ });
83
+ test('should fall back to CIBUILD_AAB_PATH env var', async () => {
84
+ const executor = new GooglePlayDeployStepExecutor();
85
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
86
+ expect(result.script).toContain('CIBUILD_AAB_PATH');
87
+ });
88
+ test('should fall back to CIBUILD_APK_PATH env var', async () => {
89
+ const executor = new GooglePlayDeployStepExecutor();
90
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
91
+ expect(result.script).toContain('CIBUILD_APK_PATH');
92
+ });
93
+ test('should error if no app file found', async () => {
94
+ const executor = new GooglePlayDeployStepExecutor();
95
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
96
+ expect(result.script).toContain('No APK or AAB file found');
97
+ expect(result.script).toContain('exit 1');
98
+ });
99
+ });
100
+ describe('AAB vs APK detection', () => {
101
+ test('should use --aab flag for .aab files', async () => {
102
+ const executor = new GooglePlayDeployStepExecutor();
103
+ const result = await executor.execute({
104
+ service_account_json_key_path: '/keys/service.json',
105
+ package_name: 'com.example.app',
106
+ app_path: '/build/app-release.aab',
107
+ }, {}, mockConfig);
108
+ expect(result.script).toContain('APP_FLAG="--aab"');
109
+ expect(result.script).toContain('*.aab');
110
+ });
111
+ test('should use --apk flag for non-.aab files', async () => {
112
+ const executor = new GooglePlayDeployStepExecutor();
113
+ const result = await executor.execute({
114
+ service_account_json_key_path: '/keys/service.json',
115
+ package_name: 'com.example.app',
116
+ app_path: '/build/app-release.apk',
117
+ }, {}, mockConfig);
118
+ expect(result.script).toContain('APP_FLAG="--apk"');
119
+ });
120
+ });
121
+ describe('Track input', () => {
122
+ test('should default to alpha track', async () => {
123
+ const executor = new GooglePlayDeployStepExecutor();
124
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
125
+ expect(result.script).toContain('TRACK="alpha"');
126
+ });
127
+ test('should use provided track value', async () => {
128
+ const executor = new GooglePlayDeployStepExecutor();
129
+ const result = await executor.execute({
130
+ service_account_json_key_path: '/keys/service.json',
131
+ package_name: 'com.example.app',
132
+ track: 'production',
133
+ }, {}, mockConfig);
134
+ expect(result.script).toContain('TRACK="production"');
135
+ });
136
+ test('should pass track to fastlane supply', async () => {
137
+ const executor = new GooglePlayDeployStepExecutor();
138
+ const result = await executor.execute({
139
+ service_account_json_key_path: '/keys/service.json',
140
+ package_name: 'com.example.app',
141
+ track: 'internal',
142
+ }, {}, mockConfig);
143
+ expect(result.script).toContain('--track \\"$TRACK\\"');
144
+ });
145
+ });
146
+ describe('Optional inputs', () => {
147
+ test('should pass user_fraction for staged rollout', async () => {
148
+ const executor = new GooglePlayDeployStepExecutor();
149
+ const result = await executor.execute({
150
+ service_account_json_key_path: '/keys/service.json',
151
+ package_name: 'com.example.app',
152
+ user_fraction: '0.1',
153
+ }, {}, mockConfig);
154
+ expect(result.script).toContain('--rollout \\"0.1\\"');
155
+ });
156
+ test('should pass status when provided', async () => {
157
+ const executor = new GooglePlayDeployStepExecutor();
158
+ const result = await executor.execute({
159
+ service_account_json_key_path: '/keys/service.json',
160
+ package_name: 'com.example.app',
161
+ status: 'draft',
162
+ }, {}, mockConfig);
163
+ expect(result.script).toContain('--release_status \\"draft\\"');
164
+ });
165
+ test('should pass whatsnews_dir when provided', async () => {
166
+ const executor = new GooglePlayDeployStepExecutor();
167
+ const result = await executor.execute({
168
+ service_account_json_key_path: '/keys/service.json',
169
+ package_name: 'com.example.app',
170
+ whatsnews_dir: './changelogs',
171
+ }, {}, mockConfig);
172
+ expect(result.script).toContain('--whatsnew_paths \\"./changelogs\\"');
173
+ });
174
+ test('should attempt to resolve mapping_file', async () => {
175
+ const executor = new GooglePlayDeployStepExecutor();
176
+ const result = await executor.execute({
177
+ service_account_json_key_path: '/keys/service.json',
178
+ package_name: 'com.example.app',
179
+ mapping_file: './mapping.txt',
180
+ }, {}, mockConfig);
181
+ expect(result.script).toContain('MAPPING_FILE="./mapping.txt"');
182
+ expect(result.script).toContain('--mapping');
183
+ });
184
+ test('should not pass user_fraction if not provided', async () => {
185
+ const executor = new GooglePlayDeployStepExecutor();
186
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
187
+ expect(result.script).not.toContain('--rollout');
188
+ });
189
+ });
190
+ describe('Dry run', () => {
191
+ test('should pass --dry_run when dry_run is true', async () => {
192
+ const executor = new GooglePlayDeployStepExecutor();
193
+ const result = await executor.execute({
194
+ service_account_json_key_path: '/keys/service.json',
195
+ package_name: 'com.example.app',
196
+ dry_run: 'true',
197
+ }, {}, mockConfig);
198
+ expect(result.script).toContain('--dry_run');
199
+ });
200
+ test('should not pass --dry_run by default', async () => {
201
+ const executor = new GooglePlayDeployStepExecutor();
202
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
203
+ expect(result.script).not.toContain('--dry_run');
204
+ });
205
+ });
206
+ describe('Verbose logging', () => {
207
+ test('should pass --verbose when verbose_log is true', async () => {
208
+ const executor = new GooglePlayDeployStepExecutor();
209
+ const result = await executor.execute({
210
+ service_account_json_key_path: '/keys/service.json',
211
+ package_name: 'com.example.app',
212
+ verbose_log: 'true',
213
+ }, {}, mockConfig);
214
+ expect(result.script).toContain('--verbose');
215
+ });
216
+ test('should not pass --verbose by default', async () => {
217
+ const executor = new GooglePlayDeployStepExecutor();
218
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
219
+ expect(result.script).not.toContain('--verbose');
220
+ });
221
+ });
222
+ describe('Script structure', () => {
223
+ test('should include bash error handling directives', async () => {
224
+ const executor = new GooglePlayDeployStepExecutor();
225
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
226
+ expect(result.script).toContain('#!/bin/bash');
227
+ expect(result.script).toContain('set -e');
228
+ expect(result.script).toContain('set -o pipefail');
229
+ });
230
+ test('should include success message', async () => {
231
+ const executor = new GooglePlayDeployStepExecutor();
232
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
233
+ expect(result.script).toContain('Successfully deployed to Google Play');
234
+ });
235
+ test('should include failure message and exit 1', async () => {
236
+ const executor = new GooglePlayDeployStepExecutor();
237
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
238
+ expect(result.script).toContain('Failed to deploy to Google Play');
239
+ expect(result.script).toContain('exit 1');
240
+ });
241
+ });
242
+ describe('Validation requirements', () => {
243
+ test('should require service_account_json_key_path input when secret is not set', () => {
244
+ const executor = new GooglePlayDeployStepExecutor();
245
+ const requirements = executor.getValidationRequirements({}, {}, mockConfig);
246
+ const inputReqs = requirements.filter(r => r.category === 'input');
247
+ expect(inputReqs.some(r => r.name === 'service_account_json_key_path')).toBe(true);
248
+ });
249
+ test('should not require service_account_json_key_path when GOOGLE_PLAY_SERVICE_ACCOUNT_JSON is set', () => {
250
+ const executor = new GooglePlayDeployStepExecutor();
251
+ const requirements = executor.getValidationRequirements({}, { GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: '{"type":"service_account"}' }, mockConfig);
252
+ const inputReqs = requirements.filter(r => r.category === 'input');
253
+ expect(inputReqs.some(r => r.name === 'service_account_json_key_path')).toBe(false);
254
+ });
255
+ test('should not require service_account_json_key_path when custom service_account_json_var secret is set', () => {
256
+ const executor = new GooglePlayDeployStepExecutor();
257
+ const requirements = executor.getValidationRequirements({ service_account_json_var: 'MY_CUSTOM_SA_JSON' }, { MY_CUSTOM_SA_JSON: '{"type":"service_account"}' }, mockConfig);
258
+ const inputReqs = requirements.filter(r => r.category === 'input');
259
+ expect(inputReqs.some(r => r.name === 'service_account_json_key_path')).toBe(false);
260
+ });
261
+ test('should require package_name input', () => {
262
+ const executor = new GooglePlayDeployStepExecutor();
263
+ const requirements = executor.getValidationRequirements({}, {}, mockConfig);
264
+ const inputReqs = requirements.filter(r => r.category === 'input');
265
+ expect(inputReqs.some(r => r.name === 'package_name')).toBe(true);
266
+ });
267
+ test('should require fastlane command', () => {
268
+ const executor = new GooglePlayDeployStepExecutor();
269
+ const requirements = executor.getValidationRequirements({}, {}, mockConfig);
270
+ const commandReqs = requirements.filter(r => r.category === 'command');
271
+ expect(commandReqs.some(r => r.name === 'fastlane')).toBe(true);
272
+ });
273
+ });
274
+ describe('GOOGLE_PLAY_SERVICE_ACCOUNT_JSON secret', () => {
275
+ const jsonSecret = '{"type":"service_account","project_id":"my-project"}';
276
+ test('should write secret to temp file when GOOGLE_PLAY_SERVICE_ACCOUNT_JSON is set', async () => {
277
+ const executor = new GooglePlayDeployStepExecutor();
278
+ const result = await executor.execute({ package_name: 'com.example.app' }, { GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: jsonSecret }, mockConfig);
279
+ expect(result.script).toContain('GOOGLE_PLAY_SERVICE_ACCOUNT_JSON secret');
280
+ expect(result.script).toContain('mktemp');
281
+ expect(result.script).toContain('base64 -d');
282
+ expect(result.script).toContain('SERVICE_ACCOUNT_KEY="$_SA_TEMP_FILE"');
283
+ });
284
+ test('should embed base64-encoded JSON content in the script', async () => {
285
+ const executor = new GooglePlayDeployStepExecutor();
286
+ const result = await executor.execute({ package_name: 'com.example.app' }, { GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: jsonSecret }, mockConfig);
287
+ const expected = Buffer.from(jsonSecret).toString('base64');
288
+ expect(result.script).toContain(expected);
289
+ });
290
+ test('should set up EXIT trap to clean up temp file', async () => {
291
+ const executor = new GooglePlayDeployStepExecutor();
292
+ const result = await executor.execute({ package_name: 'com.example.app' }, { GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: jsonSecret }, mockConfig);
293
+ expect(result.script).toContain('trap _cleanup_sa EXIT');
294
+ expect(result.script).toContain('rm -f "$_SA_TEMP_FILE"');
295
+ });
296
+ test('should not require service_account_json_key_path when secret is provided', async () => {
297
+ const executor = new GooglePlayDeployStepExecutor();
298
+ const result = await executor.execute({ package_name: 'com.example.app' }, { GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: jsonSecret }, mockConfig);
299
+ // Should still call fastlane supply successfully (no early exit from missing path)
300
+ expect(result.script).toContain('fastlane supply');
301
+ });
302
+ test('should still use file path when secret is not set', async () => {
303
+ const executor = new GooglePlayDeployStepExecutor();
304
+ const result = await executor.execute({ service_account_json_key_path: '/keys/service.json', package_name: 'com.example.app' }, {}, mockConfig);
305
+ expect(result.script).toContain('SERVICE_ACCOUNT_KEY="/keys/service.json"');
306
+ expect(result.script).not.toContain('mktemp');
307
+ });
308
+ });
309
+ });
310
+ //# sourceMappingURL=google-play-deploy.test.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Step registry initialization
3
+ * Registers all available YAML steps
4
+ */
5
+ /**
6
+ * Initializes the step registry with all available steps
7
+ * This function should be called once at application startup
8
+ */
9
+ export declare function initializeStepRegistry(): void;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+BH;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAm1C7C"}