@daemux/store-automator 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +6 -24
  3. package/bin/cli.mjs +7 -66
  4. package/package.json +1 -2
  5. package/plugins/store-automator/.claude-plugin/plugin.json +1 -1
  6. package/plugins/store-automator/agents/architect.md +1 -1
  7. package/plugins/store-automator/agents/devops.md +28 -75
  8. package/plugins/store-automator/agents/product-manager.md +5 -5
  9. package/scripts/check_changed.sh +1 -1
  10. package/src/ci-config.mjs +0 -10
  11. package/src/install-paths.mjs +1 -75
  12. package/src/install.mjs +8 -19
  13. package/src/mcp-setup.mjs +1 -35
  14. package/src/prompt.mjs +1 -19
  15. package/src/templates.mjs +0 -1
  16. package/src/uninstall.mjs +0 -4
  17. package/src/utils.mjs +0 -9
  18. package/templates/CLAUDE.md.template +18 -18
  19. package/templates/ci.config.yaml.template +0 -11
  20. package/templates/fastlane/android/Fastfile.template +2 -2
  21. package/templates/fastlane/android/Pluginfile.template +1 -1
  22. package/templates/fastlane/ios/Fastfile.template +2 -2
  23. package/templates/fastlane/ios/Pluginfile.template +1 -1
  24. package/templates/fastlane/ios/Snapfile.template +1 -1
  25. package/templates/github/workflows/android-release.yml +0 -4
  26. package/templates/github/workflows/ios-release.yml +0 -4
  27. package/templates/scripts/check_changed.sh +1 -1
  28. package/templates/scripts/ci/android/manage-version.sh +16 -17
  29. package/templates/scripts/ci/android/sync-iap.sh +4 -4
  30. package/templates/scripts/ci/android/update-data-safety.sh +21 -8
  31. package/templates/scripts/ci/android/upload-metadata.sh +7 -5
  32. package/templates/scripts/ci/common/install-fastlane.sh +14 -0
  33. package/templates/scripts/ci/common/link-fastlane.sh +1 -1
  34. package/templates/scripts/ci/common/read-config.sh +1 -5
  35. package/templates/scripts/ci/ios/build.sh +4 -10
  36. package/templates/scripts/ci/ios/set-build-number.sh +20 -17
  37. package/templates/scripts/ci/ios/setup-signing.sh +0 -9
  38. package/templates/scripts/ci/ios/sync-iap.sh +4 -4
  39. package/templates/scripts/update_data_safety.py +14 -10
  40. package/scripts/codemagic-setup.mjs +0 -44
  41. package/scripts/generate.sh +0 -107
  42. package/src/codemagic-api.mjs +0 -75
  43. package/src/codemagic-setup.mjs +0 -190
  44. package/src/github-setup.mjs +0 -43
  45. package/templates/codemagic.template.yaml +0 -551
  46. package/templates/github/workflows/codemagic-trigger.yml +0 -78
  47. package/templates/scripts/generate.sh +0 -107
@@ -1,551 +0,0 @@
1
- workflows:
2
- ios-release:
3
- name: iOS Release
4
- max_build_duration: 90
5
- instance_type: mac_mini_m4
6
- environment:
7
- flutter: stable
8
- xcode: latest
9
- vars:
10
- BUNDLE_ID: "${BUNDLE_ID}"
11
- APP_NAME: "${APP_NAME}"
12
- SKU: "${SKU}"
13
- APPLE_ID: "${APPLE_ID}"
14
- P8_KEY_PATH: "${P8_KEY_PATH}"
15
- APPLE_KEY_ID: "${APPLE_KEY_ID}"
16
- APPLE_ISSUER_ID: "${APPLE_ISSUER_ID}"
17
- PRIMARY_CATEGORY: "${PRIMARY_CATEGORY}"
18
- SECONDARY_CATEGORY: "${SECONDARY_CATEGORY}"
19
- PRICE_TIER: "${PRICE_TIER}"
20
- SUBMIT_FOR_REVIEW: "${SUBMIT_FOR_REVIEW}"
21
- AUTOMATIC_RELEASE: "${AUTOMATIC_RELEASE}"
22
- FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS: "1"
23
- APP_ROOT: "${APP_ROOT}"
24
- cache:
25
- cache_paths:
26
- - $HOME/.gem
27
- - ${APP_ROOT}/ios/vendor/bundle
28
- triggering:
29
- events:
30
- - push
31
- branch_patterns:
32
- - pattern: main
33
- include: true
34
- scripts:
35
- - name: Link Fastlane directories
36
- script: |
37
- set -euo pipefail
38
- ln -sfn "$CM_BUILD_DIR/fastlane/ios" "$CM_BUILD_DIR/$APP_ROOT/ios/fastlane"
39
- ln -sfn "$CM_BUILD_DIR/fastlane" "$CM_BUILD_DIR/$APP_ROOT/fastlane"
40
-
41
- - name: Ensure CERTIFICATE_PRIVATE_KEY
42
- script: |
43
- set -euo pipefail
44
- KEY_FILE="$CM_BUILD_DIR/creds/ios_dist_private_key"
45
- if [ -f "$KEY_FILE" ]; then
46
- echo "Using CERTIFICATE_PRIVATE_KEY from repo creds/"
47
- else
48
- echo "ERROR: creds/ios_dist_private_key not found in repo." >&2
49
- echo "Generate it with: ssh-keygen -t rsa -b 2048 -m PEM -f creds/ios_dist_private_key -q -N ''" >&2
50
- exit 1
51
- fi
52
- echo "CERTIFICATE_PRIVATE_KEY<<DELIMITER" >> $CM_ENV
53
- cat "$KEY_FILE" >> $CM_ENV
54
- echo "" >> $CM_ENV
55
- echo "DELIMITER" >> $CM_ENV
56
-
57
- - name: Set up App Store Connect API key
58
- script: |
59
- set -euo pipefail
60
- echo "APP_STORE_CONNECT_KEY_IDENTIFIER=$APPLE_KEY_ID" >> $CM_ENV
61
- echo "APP_STORE_CONNECT_ISSUER_ID=$APPLE_ISSUER_ID" >> $CM_ENV
62
- echo "APP_STORE_CONNECT_PRIVATE_KEY<<KEYDELIMITER" >> $CM_ENV
63
- cat "$CM_BUILD_DIR/$P8_KEY_PATH" >> $CM_ENV
64
- echo "" >> $CM_ENV
65
- echo "KEYDELIMITER" >> $CM_ENV
66
- # Write P8 key to temp file once for all Fastlane steps
67
- P8_TMP="/tmp/fastlane_api_key.p8"
68
- cat "$CM_BUILD_DIR/$P8_KEY_PATH" > "$P8_TMP"
69
- echo "FASTLANE_API_KEY_PATH=$P8_TMP" >> $CM_ENV
70
-
71
- - name: Install Fastlane
72
- script: |
73
- set -euo pipefail
74
- cd $CM_BUILD_DIR/$APP_ROOT/ios
75
- gem install bundler
76
- bundle install
77
-
78
- - name: Ensure app record exists
79
- script: |
80
- set -euo pipefail
81
- echo "=== Checking if $BUNDLE_ID exists in App Store Connect ==="
82
-
83
- # First try: list apps filtering by bundle ID
84
- echo "--- Running: app-store-connect apps list --bundle-id-identifier $BUNDLE_ID ---"
85
- if ! APP_JSON=$(app-store-connect apps list \
86
- --bundle-id-identifier "$BUNDLE_ID" \
87
- --strict-match-identifier \
88
- --json 2>/tmp/asc_apps_err.log); then
89
- echo "ERROR: Failed to query App Store Connect" >&2
90
- cat /tmp/asc_apps_err.log >&2 2>/dev/null || true
91
- exit 1
92
- fi
93
-
94
- echo "Apps list stdout (first 500 chars):"
95
- echo "$APP_JSON" | head -c 500
96
-
97
- EXISTING=$(echo "$APP_JSON" | python3 -c "
98
- import sys, json
99
- data = json.load(sys.stdin)
100
- apps = data if isinstance(data, list) else []
101
- print('yes' if len(apps) > 0 else 'no')
102
- ")
103
-
104
- echo "App exists result: $EXISTING"
105
-
106
- if [ "$EXISTING" = "yes" ]; then
107
- echo "App record found for $BUNDLE_ID"
108
- else
109
- echo "ERROR: App record not found for $BUNDLE_ID" >&2
110
- echo "If you already created the app in App Store Connect," >&2
111
- echo "this may be an API propagation delay or permission issue." >&2
112
- echo "" >&2
113
- echo "Listing all visible apps for debugging:" >&2
114
- app-store-connect apps list --json 2>&1 | python3 -c "
115
- import sys, json
116
- data = json.load(sys.stdin)
117
- apps = data if isinstance(data, list) else []
118
- print(f'Total apps: {len(apps)}')
119
- for a in apps[:10]:
120
- attrs = a.get('attributes', {})
121
- name = attrs.get('name', '?')
122
- bid = attrs.get('bundleId', '?')
123
- print(f' {name} | {bid} | {a.get(\"id\", \"?\")}')
124
- " 2>&1 || true
125
- exit 1
126
- fi
127
-
128
- - name: Upload iOS metadata and screenshots
129
- script: |
130
- set -euo pipefail
131
- cd $CM_BUILD_DIR/$APP_ROOT/ios
132
- bundle exec fastlane upload_metadata_ios
133
-
134
- - name: Sync IAP and subscriptions
135
- script: |
136
- set -euo pipefail
137
- FORCE_FLAG=""
138
- if [ ! -f "$CM_BUILD_DIR/.codemagic/ios_iap_synced" ]; then
139
- FORCE_FLAG="--force"
140
- echo "First IAP sync detected - forcing upload"
141
- fi
142
- if ./scripts/check_changed.sh $FORCE_FLAG fastlane/iap_config.json; then
143
- cd $CM_BUILD_DIR/$APP_ROOT/ios
144
- bundle exec fastlane sync_iap
145
- # Create marker after successful sync
146
- if [ -n "$FORCE_FLAG" ]; then
147
- mkdir -p "$CM_BUILD_DIR/.codemagic"
148
- touch "$CM_BUILD_DIR/.codemagic/ios_iap_synced"
149
- cd "$CM_BUILD_DIR"
150
- git add .codemagic/ios_iap_synced
151
- git commit -m "chore: mark iOS IAP sync complete [skip ci]" || true
152
- git push origin HEAD || true
153
- echo "iOS IAP sync marker committed"
154
- fi
155
- else
156
- echo "IAP config unchanged - skipping"
157
- fi
158
-
159
- - name: Set up iOS code signing
160
- script: |
161
- set -euo pipefail
162
- keychain initialize
163
-
164
- # Attempt to fetch or create signing files.
165
- if app-store-connect fetch-signing-files "$BUNDLE_ID" \
166
- --type IOS_APP_STORE \
167
- --certificate-key=@env:CERTIFICATE_PRIVATE_KEY \
168
- --create 2>/tmp/signing_err.log; then
169
- echo "Signing files fetched successfully"
170
- else
171
- cat /tmp/signing_err.log
172
- echo ""
173
- echo "Signing failed. Deleting ALL distribution certs..."
174
-
175
- for CERT_TYPE in IOS_DISTRIBUTION DISTRIBUTION; do
176
- CERT_IDS=$(app-store-connect certificates list \
177
- --type "$CERT_TYPE" --json 2>/dev/null \
178
- | python3 -c "
179
- import sys, json
180
- certs = json.load(sys.stdin)
181
- for c in certs:
182
- print(c.get('id', ''))
183
- " 2>/dev/null || true)
184
-
185
- for CID in $CERT_IDS; do
186
- if [ -n "$CID" ]; then
187
- echo "Deleting $CERT_TYPE cert: $CID"
188
- app-store-connect certificates delete "$CID" || true
189
- fi
190
- done
191
- done
192
-
193
- echo "Waiting 15s for Apple API propagation..."
194
- sleep 15
195
-
196
- # Verify certs are gone
197
- REMAINING=$(app-store-connect certificates list \
198
- --type DISTRIBUTION --json 2>/dev/null \
199
- | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null || echo "?")
200
- echo "Remaining DISTRIBUTION certs: $REMAINING"
201
-
202
- echo "Retrying fetch-signing-files..."
203
- app-store-connect fetch-signing-files "$BUNDLE_ID" \
204
- --type IOS_APP_STORE \
205
- --certificate-key=@env:CERTIFICATE_PRIVATE_KEY \
206
- --create
207
- fi
208
-
209
- # Verify signing artifacts exist
210
- P12_COUNT=$(ls /Users/builder/Library/MobileDevice/Certificates/*.p12 2>/dev/null | wc -l)
211
- if [ "$P12_COUNT" -eq 0 ]; then
212
- echo "ERROR: No .p12 certificates found after signing setup" >&2
213
- exit 1
214
- fi
215
-
216
- keychain add-certificates
217
- xcode-project use-profiles --project $APP_ROOT/ios/Runner.xcodeproj
218
-
219
- - name: Manage iOS version
220
- script: |
221
- set -euo pipefail
222
- pip3 install --break-system-packages PyJWT cryptography requests
223
- VERSION_JSON=$(python3 scripts/manage_version_ios.py)
224
- eval "$(echo "$VERSION_JSON" | python3 -c "
225
- import sys, json
226
- d = json.load(sys.stdin)
227
- v = d['version']
228
- vid = d.get('version_id', '')
229
- st = d.get('state', 'NEW')
230
- print(f\"APP_VERSION='{v}'\")
231
- print(f\"APP_VERSION_ID='{vid}'\")
232
- print(f\"APP_STATUS='{st}'\")
233
- ")"
234
- echo "APP_VERSION=$APP_VERSION" >> $CM_ENV
235
- echo "APP_VERSION_ID=$APP_VERSION_ID" >> $CM_ENV
236
- echo "APP_STATUS=$APP_STATUS" >> $CM_ENV
237
- echo "iOS version: $APP_VERSION (state: $APP_STATUS)"
238
-
239
- - name: Set Flutter version
240
- script: |
241
- set -euo pipefail
242
- BUILD_NUMBER=$(($(app-store-connect get-latest-app-store-build-number "$BUNDLE_ID") + 1))
243
- if [ -z "$APP_VERSION" ]; then
244
- APP_VERSION="1.0.0"
245
- fi
246
- # Normalize to semver (1.0 -> 1.0.0, 1 -> 1.0.0)
247
- PARTS=$(echo "$APP_VERSION" | tr '.' '\n' | wc -l | tr -d ' ')
248
- if [ "$PARTS" -eq 1 ]; then APP_VERSION="${APP_VERSION}.0.0"; fi
249
- if [ "$PARTS" -eq 2 ]; then APP_VERSION="${APP_VERSION}.0"; fi
250
- sed -i '' "s/^version:.*/version: ${APP_VERSION}+${BUILD_NUMBER}/" $APP_ROOT/pubspec.yaml
251
- echo "Building: $APP_VERSION+$BUILD_NUMBER"
252
-
253
- - name: Flutter packages
254
- script: |
255
- set -euo pipefail
256
- cd $CM_BUILD_DIR/$APP_ROOT && flutter pub get
257
-
258
- - name: Build iOS
259
- script: |
260
- set -euo pipefail
261
- cd $CM_BUILD_DIR/$APP_ROOT
262
- flutter build ipa \
263
- --release \
264
- --export-options-plist=/Users/builder/export_options.plist
265
-
266
- - name: Upload IPA to App Store
267
- script: |
268
- set -euo pipefail
269
- cd $CM_BUILD_DIR/$APP_ROOT/ios
270
- bundle exec fastlane upload_binary_ios
271
-
272
- artifacts:
273
- - ${APP_ROOT}/build/ios/ipa/*.ipa
274
- - /tmp/xcodebuild_logs/*.log
275
-
276
- android-release:
277
- name: Android Release
278
- max_build_duration: 60
279
- instance_type: mac_mini_m4
280
- environment:
281
- flutter: stable
282
- groups:
283
- - google_play_credentials
284
- - android_keystore
285
- vars:
286
- PACKAGE_NAME: "${PACKAGE_NAME}"
287
- APP_NAME: "${APP_NAME}"
288
- BUNDLE_ID: "${BUNDLE_ID}"
289
- GOOGLE_SA_JSON_PATH: "${GOOGLE_SA_JSON_PATH}"
290
- KEYSTORE_PASSWORD: "${KEYSTORE_PASSWORD}"
291
- TRACK: "${TRACK}"
292
- ROLLOUT_FRACTION: "${ROLLOUT_FRACTION}"
293
- IN_APP_UPDATE_PRIORITY: "${IN_APP_UPDATE_PRIORITY}"
294
- APPLE_KEY_ID: "${APPLE_KEY_ID}"
295
- APPLE_ISSUER_ID: "${APPLE_ISSUER_ID}"
296
- P8_KEY_PATH: "${P8_KEY_PATH}"
297
- APP_ROOT: "${APP_ROOT}"
298
- cache:
299
- cache_paths:
300
- - $HOME/.gem
301
- - $HOME/.gradle/caches
302
- - ${APP_ROOT}/android/vendor/bundle
303
- triggering:
304
- events:
305
- - push
306
- branch_patterns:
307
- - pattern: main
308
- include: true
309
- scripts:
310
- - name: Link Fastlane directories
311
- script: |
312
- set -euo pipefail
313
- ln -sfn "$CM_BUILD_DIR/fastlane/android" "$CM_BUILD_DIR/$APP_ROOT/android/fastlane"
314
- ln -sfn "$CM_BUILD_DIR/fastlane" "$CM_BUILD_DIR/$APP_ROOT/fastlane"
315
-
316
- - name: Ensure Android upload keystore
317
- script: |
318
- set -euo pipefail
319
- KEYSTORE_PATH="$CM_BUILD_DIR/$APP_ROOT/android/upload.keystore"
320
- CREDS_PATH="$CM_BUILD_DIR/creds/android_upload.keystore"
321
- GENERATED=false
322
- if [ -f "$KEYSTORE_PATH" ]; then
323
- echo "Using existing upload keystore from repo"
324
- elif [ -f "$CREDS_PATH" ]; then
325
- echo "Restoring upload keystore from creds/"
326
- cp "$CREDS_PATH" "$KEYSTORE_PATH"
327
- else
328
- echo "Generating new upload keystore..."
329
- keytool -genkey -v \
330
- -keystore "$KEYSTORE_PATH" \
331
- -storetype JKS \
332
- -keyalg RSA \
333
- -keysize 2048 \
334
- -validity 10000 \
335
- -alias upload \
336
- -storepass "$KEYSTORE_PASSWORD" \
337
- -keypass "$KEYSTORE_PASSWORD" \
338
- -dname "CN=Upload Key, O=Developer, C=US"
339
- GENERATED=true
340
- fi
341
- # Always ensure creds/ backup exists
342
- mkdir -p "$CM_BUILD_DIR/creds"
343
- if [ ! -f "$CREDS_PATH" ] || ! cmp -s "$KEYSTORE_PATH" "$CREDS_PATH"; then
344
- cp "$KEYSTORE_PATH" "$CREDS_PATH"
345
- echo "Keystore backed up to creds/android_upload.keystore"
346
- fi
347
- # Commit keystore files if newly generated
348
- if [ "$GENERATED" = "true" ]; then
349
- cd "$CM_BUILD_DIR"
350
- git add --force $APP_ROOT/android/upload.keystore creds/android_upload.keystore
351
- git commit -m "chore: add Android upload keystore [skip ci]"
352
- git push origin HEAD
353
- echo "Upload keystore generated and committed to repo"
354
- fi
355
- echo "CM_KEYSTORE_PATH=$KEYSTORE_PATH" >> $CM_ENV
356
- echo "CM_KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD" >> $CM_ENV
357
- echo "CM_KEY_ALIAS=upload" >> $CM_ENV
358
- echo "CM_KEY_PASSWORD=$KEYSTORE_PASSWORD" >> $CM_ENV
359
-
360
- - name: Check Google Play readiness
361
- script: |
362
- set -euo pipefail
363
- echo "Checking Google Play setup status..."
364
- pip3 install --break-system-packages PyJWT cryptography requests
365
- export SA_JSON="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
366
- export PACKAGE_NAME="$PACKAGE_NAME"
367
- RESULT=$(python3 scripts/check_google_play.py)
368
- READY=$(echo "$RESULT" | python3 -c "
369
- import sys, json
370
- print(str(json.load(sys.stdin).get('ready', False)).lower())
371
- ")
372
- if [ "$READY" != "true" ]; then
373
- echo "GOOGLE_PLAY_READY=false" >> $CM_ENV
374
- cat > "$CM_BUILD_DIR/HOW_TO_GOOGLE_PLAY.md" <<'GUIDE'
375
- # Google Play Setup - Manual Steps Required
376
-
377
- Your Android AAB is available in the build artifacts above.
378
-
379
- ## Steps to complete
380
-
381
- 1. **Go to Google Play Console** - https://play.google.com/console
382
- 2. **Create your app** (if not already created)
383
- 3. **Upload the AAB** from build artifacts to an internal testing track
384
- 4. **Complete the Store Listing** - title, descriptions, screenshots, icon
385
- 5. **Complete the Content Rating** questionnaire
386
- 6. **Set up Pricing and Distribution**
387
- 7. **Complete the Data Safety** form
388
- 8. **Review and roll out** the internal testing release
389
-
390
- ## After completing all steps
391
- Just git push again - Codemagic will publish automatically.
392
- GUIDE
393
- echo "Google Play not ready - see HOW_TO_GOOGLE_PLAY.md in artifacts"
394
- else
395
- echo "GOOGLE_PLAY_READY=true" >> $CM_ENV
396
- # Detect first store sync: if marker file doesn't exist, force IAP/data safety upload
397
- if [ ! -f "$CM_BUILD_DIR/.codemagic/android_store_synced" ]; then
398
- echo "FIRST_STORE_SYNC=true" >> $CM_ENV
399
- echo "First store sync detected - IAP and data safety will be force-uploaded"
400
- else
401
- echo "FIRST_STORE_SYNC=false" >> $CM_ENV
402
- echo "Marker found - using change detection for IAP/data safety"
403
- fi
404
- echo "Google Play ready for automated publishing"
405
- fi
406
-
407
- - name: Install Fastlane
408
- script: |
409
- set -euo pipefail
410
- cd $CM_BUILD_DIR/$APP_ROOT/android
411
- gem install bundler
412
- bundle install
413
-
414
- - name: Upload Android metadata and screenshots
415
- script: |
416
- set -euo pipefail
417
- if [ "$GOOGLE_PLAY_READY" != "true" ]; then
418
- echo "Google Play not ready - skipping metadata upload."
419
- exit 0
420
- fi
421
- export GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
422
- cd $CM_BUILD_DIR/$APP_ROOT/android
423
- bundle exec fastlane upload_metadata_android
424
- echo "Metadata uploaded successfully"
425
-
426
- - name: Sync subscriptions and IAP
427
- script: |
428
- set -euo pipefail
429
- if [ "$GOOGLE_PLAY_READY" != "true" ]; then
430
- echo "Google Play not ready - skipping IAP sync."
431
- exit 0
432
- fi
433
- FORCE_FLAG=""
434
- if [ "${FIRST_STORE_SYNC:-false}" = "true" ]; then
435
- FORCE_FLAG="--force"
436
- fi
437
- if ./scripts/check_changed.sh $FORCE_FLAG fastlane/iap_config.json; then
438
- export GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
439
- cd $CM_BUILD_DIR/$APP_ROOT/android
440
- bundle exec fastlane sync_google_iap
441
- echo "IAP sync completed successfully"
442
- else
443
- echo "IAP config unchanged - skipping"
444
- fi
445
-
446
- - name: Update data safety form
447
- script: |
448
- set -euo pipefail
449
- if [ "$GOOGLE_PLAY_READY" != "true" ]; then
450
- echo "Google Play not ready - skipping data safety update."
451
- exit 0
452
- fi
453
- FORCE_FLAG=""
454
- if [ "${FIRST_STORE_SYNC:-false}" = "true" ]; then
455
- FORCE_FLAG="--force"
456
- fi
457
- if ./scripts/check_changed.sh $FORCE_FLAG fastlane/data_safety.csv; then
458
- export GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
459
- cd $CM_BUILD_DIR/$APP_ROOT/android
460
- bundle exec fastlane update_data_safety
461
- echo "Data safety form updated successfully"
462
- else
463
- echo "Data safety unchanged - skipping"
464
- fi
465
- # After successful sync, create marker so future builds use change detection
466
- if [ "${FIRST_STORE_SYNC:-false}" = "true" ]; then
467
- mkdir -p "$CM_BUILD_DIR/.codemagic"
468
- touch "$CM_BUILD_DIR/.codemagic/android_store_synced"
469
- cd "$CM_BUILD_DIR"
470
- git add .codemagic/android_store_synced
471
- git commit -m "chore: mark Android store sync complete [skip ci]" || true
472
- git push origin HEAD || true
473
- echo "Store sync marker committed"
474
- fi
475
-
476
- - name: Manage Android version
477
- script: |
478
- set -euo pipefail
479
- # Read iOS version for consistency (if iOS workflow ran)
480
- pip3 install --break-system-packages PyJWT cryptography requests
481
- echo "APP_STORE_CONNECT_KEY_IDENTIFIER=$APPLE_KEY_ID" >> $CM_ENV
482
- echo "APP_STORE_CONNECT_ISSUER_ID=$APPLE_ISSUER_ID" >> $CM_ENV
483
- echo "APP_STORE_CONNECT_PRIVATE_KEY<<KEYDELIMITER" >> $CM_ENV
484
- cat "$CM_BUILD_DIR/$P8_KEY_PATH" >> $CM_ENV
485
- echo "" >> $CM_ENV
486
- echo "KEYDELIMITER" >> $CM_ENV
487
- VERSION_JSON=$(python3 scripts/manage_version_ios.py)
488
- eval "$(echo "$VERSION_JSON" | python3 -c "
489
- import sys, json
490
- d = json.load(sys.stdin)
491
- v = d['version']
492
- print(f\"APP_VERSION='{v}'\")
493
- ")"
494
- echo "APP_VERSION=$APP_VERSION" >> $CM_ENV
495
- if [ "$GOOGLE_PLAY_READY" != "true" ]; then
496
- LATEST_BUILD=0
497
- else
498
- export GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS="$(cat $CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH)"
499
- LATEST_BUILD_OUTPUT=$(google-play get-latest-build-number \
500
- --package-name "$PACKAGE_NAME" \
501
- --tracks=production,beta,alpha,internal)
502
- # Extract just the number from output
503
- LATEST_BUILD=$(echo "$LATEST_BUILD_OUTPUT" | grep -oE '^[0-9]+$' | tail -1)
504
- if [ -z "$LATEST_BUILD" ]; then
505
- echo "ERROR: Could not get latest build number from Google Play." >&2
506
- echo "Raw output: $LATEST_BUILD_OUTPUT" >&2
507
- exit 1
508
- fi
509
- echo "Latest build number from Google Play: $LATEST_BUILD"
510
- fi
511
- NEW_BUILD=$(($LATEST_BUILD + 1))
512
- if [ -z "$APP_VERSION" ]; then
513
- APP_VERSION="1.0.0"
514
- fi
515
- # Normalize to semver (1.0 -> 1.0.0, 1 -> 1.0.0)
516
- PARTS=$(echo "$APP_VERSION" | tr '.' '\n' | wc -l | tr -d ' ')
517
- if [ "$PARTS" -eq 1 ]; then APP_VERSION="${APP_VERSION}.0.0"; fi
518
- if [ "$PARTS" -eq 2 ]; then APP_VERSION="${APP_VERSION}.0"; fi
519
- sed -i '' "s/^version:.*/version: ${APP_VERSION}+${NEW_BUILD}/" $APP_ROOT/pubspec.yaml
520
- echo "ANDROID_VERSION_CODE=$NEW_BUILD" >> $CM_ENV
521
- echo "Android versionCode: $NEW_BUILD, versionName: $APP_VERSION"
522
-
523
- - name: Flutter packages
524
- script: |
525
- set -euo pipefail
526
- cd $CM_BUILD_DIR/$APP_ROOT && flutter pub get
527
-
528
- - name: Build Android
529
- script: |
530
- set -euo pipefail
531
- cd $CM_BUILD_DIR/$APP_ROOT && flutter build appbundle --release
532
-
533
- - name: Upload AAB to Google Play
534
- script: |
535
- set -euo pipefail
536
- if [ "$GOOGLE_PLAY_READY" != "true" ]; then
537
- cat >&2 <<'MSG'
538
- FIRST RUN: AAB built but NOT uploaded.
539
- Download the AAB from build artifacts and upload it manually
540
- to Google Play Console. See HOW_TO_GOOGLE_PLAY.md in artifacts.
541
- After completing manual setup, push again for automated publishing.
542
- MSG
543
- exit 1
544
- fi
545
- export GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
546
- cd $CM_BUILD_DIR/$APP_ROOT/android
547
- bundle exec fastlane upload_binary_android
548
-
549
- artifacts:
550
- - ${APP_ROOT}/build/app/outputs/**/*.aab
551
- - $CM_BUILD_DIR/HOW_TO_GOOGLE_PLAY.md
@@ -1,78 +0,0 @@
1
- name: Trigger Codemagic Builds
2
-
3
- on:
4
- push:
5
- branches: [main]
6
-
7
- # Cancel in-progress runs when a newer build is triggered
8
- concurrency:
9
- group: codemagic-trigger-${{ github.ref }}
10
- cancel-in-progress: true
11
-
12
- jobs:
13
- trigger:
14
- runs-on: ubuntu-latest
15
- steps:
16
- - name: Checkout config
17
- uses: actions/checkout@v4
18
- with:
19
- sparse-checkout: ci.config.yaml
20
- sparse-checkout-cone-mode: false
21
-
22
- - name: Install yq
23
- run: |
24
- if ! command -v yq &> /dev/null; then
25
- sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
26
- sudo chmod +x /usr/local/bin/yq
27
- fi
28
-
29
- - name: Read Codemagic config
30
- id: config
31
- run: |
32
- if [ ! -f ci.config.yaml ]; then
33
- echo "skip=true" >> "$GITHUB_OUTPUT"
34
- echo "ci.config.yaml not found - skipping trigger."
35
- exit 0
36
- fi
37
-
38
- APP_ID=$(yq -r '.codemagic.app_id // ""' ci.config.yaml)
39
- if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then
40
- echo "skip=true" >> "$GITHUB_OUTPUT"
41
- echo "app_id not configured - skipping trigger."
42
- exit 0
43
- fi
44
-
45
- echo "app_id=$APP_ID" >> "$GITHUB_OUTPUT"
46
-
47
- WORKFLOWS=$(yq -r '.codemagic.workflows[]' ci.config.yaml)
48
- DELIMITER="EOF_$(date +%s%N)"
49
- echo "workflows<<$DELIMITER" >> "$GITHUB_OUTPUT"
50
- echo "$WORKFLOWS" >> "$GITHUB_OUTPUT"
51
- echo "$DELIMITER" >> "$GITHUB_OUTPUT"
52
-
53
- - name: Trigger Codemagic builds
54
- if: steps.config.outputs.skip != 'true'
55
- env:
56
- CM_TOKEN: ${{ secrets.CM_API_TOKEN }}
57
- APP_ID: ${{ steps.config.outputs.app_id }}
58
- BRANCH: ${{ github.ref_name }}
59
- run: |
60
- FAILED=0
61
- while IFS= read -r workflow; do
62
- [ -z "$workflow" ] && continue
63
- if ! echo "$workflow" | grep -Eq '^[a-zA-Z0-9_-]+$'; then
64
- echo "ERROR: Invalid workflow name: $workflow" >&2
65
- exit 1
66
- fi
67
- echo "Triggering: $workflow"
68
- curl --max-time 30 --retry 2 -sf -X POST https://api.codemagic.io/builds \
69
- -H "Content-Type: application/json" \
70
- -H "x-auth-token: $CM_TOKEN" \
71
- -d "{\"appId\": \"$APP_ID\", \"workflowId\": \"$workflow\", \"branch\": \"$BRANCH\"}" \
72
- && echo " -> success" \
73
- || { echo " -> FAILED"; FAILED=1; }
74
- done <<< "${{ steps.config.outputs.workflows }}"
75
- if [ "$FAILED" -ne 0 ]; then
76
- echo "ERROR: One or more Codemagic workflow triggers failed" >&2
77
- exit 1
78
- fi