@daemux/store-automator 0.8.0 → 0.9.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.
- package/.claude-plugin/marketplace.json +2 -2
- package/package.json +1 -1
- package/plugins/store-automator/.claude-plugin/plugin.json +1 -1
- package/plugins/store-automator/agents/appstore-meta-creator.md +13 -8
- package/plugins/store-automator/agents/architect.md +42 -1
- package/plugins/store-automator/agents/devops.md +26 -27
- package/scripts/check_google_play.py +1 -1
- package/scripts/manage_version_ios.py +1 -1
- package/src/install-paths.mjs +2 -3
- package/src/templates.mjs +2 -2
- package/templates/CLAUDE.md.template +70 -16
- package/templates/codemagic.template.yaml +131 -96
- package/templates/fastlane/android/Fastfile.template +21 -3
- package/templates/fastlane/ios/Fastfile.template +4 -3
- package/templates/github/workflows/android-release.yml +108 -39
- package/templates/github/workflows/codemagic-trigger.yml +15 -5
- package/templates/github/workflows/ios-release.yml +94 -37
- package/templates/scripts/check_google_play.py +1 -1
- package/templates/scripts/ci/android/check-readiness.sh +18 -24
- package/templates/scripts/ci/android/manage-version.sh +17 -16
- package/templates/scripts/ci/android/sync-iap.sh +6 -6
- package/templates/scripts/ci/android/update-data-safety.sh +2 -2
- package/templates/scripts/ci/android/upload-metadata.sh +23 -5
- package/templates/scripts/ci/common/install-fastlane.sh +1 -1
- package/templates/scripts/ci/common/link-fastlane.sh +1 -1
- package/templates/scripts/ci/common/read-config.sh +3 -0
- package/templates/scripts/ci/ios/build.sh +39 -0
- package/templates/scripts/ci/ios/set-build-number.sh +17 -20
- package/templates/scripts/ci/ios/setup-signing.sh +48 -7
- package/templates/scripts/ci/ios/sync-iap.sh +6 -17
- package/templates/scripts/ci/ios/upload-binary.sh +0 -1
- package/templates/scripts/ci/ios/upload-metadata.sh +3 -13
- package/templates/scripts/create_app_record.py +1 -1
- package/templates/scripts/manage_version_ios.py +1 -1
- package/templates/scripts/update_data_safety.py +1 -1
|
@@ -34,17 +34,19 @@ workflows:
|
|
|
34
34
|
scripts:
|
|
35
35
|
- name: Link Fastlane directories
|
|
36
36
|
script: |
|
|
37
|
+
set -euo pipefail
|
|
37
38
|
ln -sfn "$CM_BUILD_DIR/fastlane/ios" "$CM_BUILD_DIR/$APP_ROOT/ios/fastlane"
|
|
38
39
|
ln -sfn "$CM_BUILD_DIR/fastlane" "$CM_BUILD_DIR/$APP_ROOT/fastlane"
|
|
39
40
|
|
|
40
41
|
- name: Ensure CERTIFICATE_PRIVATE_KEY
|
|
41
42
|
script: |
|
|
43
|
+
set -euo pipefail
|
|
42
44
|
KEY_FILE="$CM_BUILD_DIR/creds/ios_dist_private_key"
|
|
43
45
|
if [ -f "$KEY_FILE" ]; then
|
|
44
46
|
echo "Using CERTIFICATE_PRIVATE_KEY from repo creds/"
|
|
45
47
|
else
|
|
46
|
-
echo "ERROR: creds/ios_dist_private_key not found in repo."
|
|
47
|
-
echo "Generate it with: ssh-keygen -t rsa -b 2048 -m PEM -f creds/ios_dist_private_key -q -N ''"
|
|
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
|
|
48
50
|
exit 1
|
|
49
51
|
fi
|
|
50
52
|
echo "CERTIFICATE_PRIVATE_KEY<<DELIMITER" >> $CM_ENV
|
|
@@ -54,6 +56,7 @@ workflows:
|
|
|
54
56
|
|
|
55
57
|
- name: Set up App Store Connect API key
|
|
56
58
|
script: |
|
|
59
|
+
set -euo pipefail
|
|
57
60
|
echo "APP_STORE_CONNECT_KEY_IDENTIFIER=$APPLE_KEY_ID" >> $CM_ENV
|
|
58
61
|
echo "APP_STORE_CONNECT_ISSUER_ID=$APPLE_ISSUER_ID" >> $CM_ENV
|
|
59
62
|
echo "APP_STORE_CONNECT_PRIVATE_KEY<<KEYDELIMITER" >> $CM_ENV
|
|
@@ -67,54 +70,70 @@ workflows:
|
|
|
67
70
|
|
|
68
71
|
- name: Install Fastlane
|
|
69
72
|
script: |
|
|
73
|
+
set -euo pipefail
|
|
70
74
|
cd $CM_BUILD_DIR/$APP_ROOT/ios
|
|
71
75
|
gem install bundler
|
|
72
76
|
bundle install
|
|
73
77
|
|
|
74
78
|
- name: Ensure app record exists
|
|
75
79
|
script: |
|
|
80
|
+
set -euo pipefail
|
|
76
81
|
echo "=== Checking if $BUNDLE_ID exists in App Store Connect ==="
|
|
77
82
|
|
|
78
83
|
# First try: list apps filtering by bundle ID
|
|
79
84
|
echo "--- Running: app-store-connect apps list --bundle-id-identifier $BUNDLE_ID ---"
|
|
80
|
-
APP_JSON=$(app-store-connect apps list \
|
|
85
|
+
if ! APP_JSON=$(app-store-connect apps list \
|
|
81
86
|
--bundle-id-identifier "$BUNDLE_ID" \
|
|
82
87
|
--strict-match-identifier \
|
|
83
|
-
--json 2>/tmp/asc_apps_err.log
|
|
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
|
|
84
93
|
|
|
85
|
-
echo "Apps list stderr:"
|
|
86
|
-
cat /tmp/asc_apps_err.log 2>/dev/null || true
|
|
87
94
|
echo "Apps list stdout (first 500 chars):"
|
|
88
95
|
echo "$APP_JSON" | head -c 500
|
|
89
96
|
|
|
90
|
-
EXISTING=$(echo "$APP_JSON" | python3 -c "
|
|
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
|
+
")
|
|
91
103
|
|
|
92
104
|
echo "App exists result: $EXISTING"
|
|
93
105
|
|
|
94
106
|
if [ "$EXISTING" = "yes" ]; then
|
|
95
107
|
echo "App record found for $BUNDLE_ID"
|
|
96
108
|
else
|
|
97
|
-
echo ""
|
|
98
|
-
echo "
|
|
99
|
-
echo "
|
|
100
|
-
echo "
|
|
101
|
-
echo ""
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
|
108
125
|
exit 1
|
|
109
126
|
fi
|
|
110
127
|
|
|
111
128
|
- name: Upload iOS metadata and screenshots
|
|
112
129
|
script: |
|
|
130
|
+
set -euo pipefail
|
|
113
131
|
cd $CM_BUILD_DIR/$APP_ROOT/ios
|
|
114
132
|
bundle exec fastlane upload_metadata_ios
|
|
115
133
|
|
|
116
134
|
- name: Sync IAP and subscriptions
|
|
117
135
|
script: |
|
|
136
|
+
set -euo pipefail
|
|
118
137
|
FORCE_FLAG=""
|
|
119
138
|
if [ ! -f "$CM_BUILD_DIR/.codemagic/ios_iap_synced" ]; then
|
|
120
139
|
FORCE_FLAG="--force"
|
|
@@ -139,6 +158,7 @@ workflows:
|
|
|
139
158
|
|
|
140
159
|
- name: Set up iOS code signing
|
|
141
160
|
script: |
|
|
161
|
+
set -euo pipefail
|
|
142
162
|
keychain initialize
|
|
143
163
|
|
|
144
164
|
# Attempt to fetch or create signing files.
|
|
@@ -189,7 +209,7 @@ workflows:
|
|
|
189
209
|
# Verify signing artifacts exist
|
|
190
210
|
P12_COUNT=$(ls /Users/builder/Library/MobileDevice/Certificates/*.p12 2>/dev/null | wc -l)
|
|
191
211
|
if [ "$P12_COUNT" -eq 0 ]; then
|
|
192
|
-
echo "ERROR: No .p12 certificates found after signing setup"
|
|
212
|
+
echo "ERROR: No .p12 certificates found after signing setup" >&2
|
|
193
213
|
exit 1
|
|
194
214
|
fi
|
|
195
215
|
|
|
@@ -198,11 +218,19 @@ workflows:
|
|
|
198
218
|
|
|
199
219
|
- name: Manage iOS version
|
|
200
220
|
script: |
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
+
")"
|
|
206
234
|
echo "APP_VERSION=$APP_VERSION" >> $CM_ENV
|
|
207
235
|
echo "APP_VERSION_ID=$APP_VERSION_ID" >> $CM_ENV
|
|
208
236
|
echo "APP_STATUS=$APP_STATUS" >> $CM_ENV
|
|
@@ -210,7 +238,8 @@ workflows:
|
|
|
210
238
|
|
|
211
239
|
- name: Set Flutter version
|
|
212
240
|
script: |
|
|
213
|
-
|
|
241
|
+
set -euo pipefail
|
|
242
|
+
BUILD_NUMBER=$(($(app-store-connect get-latest-app-store-build-number "$BUNDLE_ID") + 1))
|
|
214
243
|
if [ -z "$APP_VERSION" ]; then
|
|
215
244
|
APP_VERSION="1.0.0"
|
|
216
245
|
fi
|
|
@@ -222,13 +251,21 @@ workflows:
|
|
|
222
251
|
echo "Building: $APP_VERSION+$BUILD_NUMBER"
|
|
223
252
|
|
|
224
253
|
- name: Flutter packages
|
|
225
|
-
script:
|
|
254
|
+
script: |
|
|
255
|
+
set -euo pipefail
|
|
256
|
+
cd $CM_BUILD_DIR/$APP_ROOT && flutter pub get
|
|
226
257
|
|
|
227
258
|
- name: Build iOS
|
|
228
|
-
script:
|
|
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
|
|
229
265
|
|
|
230
266
|
- name: Upload IPA to App Store
|
|
231
267
|
script: |
|
|
268
|
+
set -euo pipefail
|
|
232
269
|
cd $CM_BUILD_DIR/$APP_ROOT/ios
|
|
233
270
|
bundle exec fastlane upload_binary_ios
|
|
234
271
|
|
|
@@ -272,11 +309,13 @@ workflows:
|
|
|
272
309
|
scripts:
|
|
273
310
|
- name: Link Fastlane directories
|
|
274
311
|
script: |
|
|
312
|
+
set -euo pipefail
|
|
275
313
|
ln -sfn "$CM_BUILD_DIR/fastlane/android" "$CM_BUILD_DIR/$APP_ROOT/android/fastlane"
|
|
276
314
|
ln -sfn "$CM_BUILD_DIR/fastlane" "$CM_BUILD_DIR/$APP_ROOT/fastlane"
|
|
277
315
|
|
|
278
316
|
- name: Ensure Android upload keystore
|
|
279
317
|
script: |
|
|
318
|
+
set -euo pipefail
|
|
280
319
|
KEYSTORE_PATH="$CM_BUILD_DIR/$APP_ROOT/android/upload.keystore"
|
|
281
320
|
CREDS_PATH="$CM_BUILD_DIR/creds/android_upload.keystore"
|
|
282
321
|
GENERATED=false
|
|
@@ -320,33 +359,37 @@ workflows:
|
|
|
320
359
|
|
|
321
360
|
- name: Check Google Play readiness
|
|
322
361
|
script: |
|
|
362
|
+
set -euo pipefail
|
|
323
363
|
echo "Checking Google Play setup status..."
|
|
324
|
-
pip3 install PyJWT cryptography requests
|
|
364
|
+
pip3 install --break-system-packages PyJWT cryptography requests
|
|
325
365
|
export SA_JSON="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
|
|
326
366
|
export PACKAGE_NAME="$PACKAGE_NAME"
|
|
327
|
-
RESULT=$(python3 scripts/check_google_play.py
|
|
328
|
-
READY=$(echo "$RESULT" | python3 -c "
|
|
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
|
+
")
|
|
329
372
|
if [ "$READY" != "true" ]; then
|
|
330
373
|
echo "GOOGLE_PLAY_READY=false" >> $CM_ENV
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
|
350
393
|
echo "Google Play not ready - see HOW_TO_GOOGLE_PLAY.md in artifacts"
|
|
351
394
|
else
|
|
352
395
|
echo "GOOGLE_PLAY_READY=true" >> $CM_ENV
|
|
@@ -363,31 +406,28 @@ workflows:
|
|
|
363
406
|
|
|
364
407
|
- name: Install Fastlane
|
|
365
408
|
script: |
|
|
409
|
+
set -euo pipefail
|
|
366
410
|
cd $CM_BUILD_DIR/$APP_ROOT/android
|
|
367
411
|
gem install bundler
|
|
368
412
|
bundle install
|
|
369
413
|
|
|
370
414
|
- name: Upload Android metadata and screenshots
|
|
371
415
|
script: |
|
|
416
|
+
set -euo pipefail
|
|
372
417
|
if [ "$GOOGLE_PLAY_READY" != "true" ]; then
|
|
373
|
-
echo "
|
|
374
|
-
echo "First AAB must be uploaded manually before metadata can sync."
|
|
418
|
+
echo "Google Play not ready - skipping metadata upload."
|
|
375
419
|
exit 0
|
|
376
420
|
fi
|
|
377
421
|
export GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
|
|
378
422
|
cd $CM_BUILD_DIR/$APP_ROOT/android
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
else
|
|
382
|
-
echo "WARNING: Metadata upload failed (app may still be in draft state)."
|
|
383
|
-
echo "Metadata will sync on next build after app leaves draft."
|
|
384
|
-
echo "Continuing build..."
|
|
385
|
-
fi
|
|
423
|
+
bundle exec fastlane upload_metadata_android
|
|
424
|
+
echo "Metadata uploaded successfully"
|
|
386
425
|
|
|
387
426
|
- name: Sync subscriptions and IAP
|
|
388
427
|
script: |
|
|
428
|
+
set -euo pipefail
|
|
389
429
|
if [ "$GOOGLE_PLAY_READY" != "true" ]; then
|
|
390
|
-
echo "
|
|
430
|
+
echo "Google Play not ready - skipping IAP sync."
|
|
391
431
|
exit 0
|
|
392
432
|
fi
|
|
393
433
|
FORCE_FLAG=""
|
|
@@ -397,21 +437,17 @@ workflows:
|
|
|
397
437
|
if ./scripts/check_changed.sh $FORCE_FLAG fastlane/iap_config.json; then
|
|
398
438
|
export GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
|
|
399
439
|
cd $CM_BUILD_DIR/$APP_ROOT/android
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
else
|
|
403
|
-
echo "WARNING: IAP sync failed (fastlane-plugin-iap may not be available yet)."
|
|
404
|
-
echo "IAP products can be configured manually in Google Play Console."
|
|
405
|
-
echo "Continuing build..."
|
|
406
|
-
fi
|
|
440
|
+
bundle exec fastlane sync_google_iap
|
|
441
|
+
echo "IAP sync completed successfully"
|
|
407
442
|
else
|
|
408
443
|
echo "IAP config unchanged - skipping"
|
|
409
444
|
fi
|
|
410
445
|
|
|
411
446
|
- name: Update data safety form
|
|
412
447
|
script: |
|
|
448
|
+
set -euo pipefail
|
|
413
449
|
if [ "$GOOGLE_PLAY_READY" != "true" ]; then
|
|
414
|
-
echo "
|
|
450
|
+
echo "Google Play not ready - skipping data safety update."
|
|
415
451
|
exit 0
|
|
416
452
|
fi
|
|
417
453
|
FORCE_FLAG=""
|
|
@@ -421,13 +457,8 @@ workflows:
|
|
|
421
457
|
if ./scripts/check_changed.sh $FORCE_FLAG fastlane/data_safety.csv; then
|
|
422
458
|
export GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
|
|
423
459
|
cd $CM_BUILD_DIR/$APP_ROOT/android
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
else
|
|
427
|
-
echo "WARNING: Data safety update failed."
|
|
428
|
-
echo "Data safety can be configured manually in Google Play Console."
|
|
429
|
-
echo "Continuing build..."
|
|
430
|
-
fi
|
|
460
|
+
bundle exec fastlane update_data_safety
|
|
461
|
+
echo "Data safety form updated successfully"
|
|
431
462
|
else
|
|
432
463
|
echo "Data safety unchanged - skipping"
|
|
433
464
|
fi
|
|
@@ -444,16 +475,22 @@ workflows:
|
|
|
444
475
|
|
|
445
476
|
- name: Manage Android version
|
|
446
477
|
script: |
|
|
478
|
+
set -euo pipefail
|
|
447
479
|
# Read iOS version for consistency (if iOS workflow ran)
|
|
448
|
-
pip3 install PyJWT cryptography requests
|
|
480
|
+
pip3 install --break-system-packages PyJWT cryptography requests
|
|
449
481
|
echo "APP_STORE_CONNECT_KEY_IDENTIFIER=$APPLE_KEY_ID" >> $CM_ENV
|
|
450
482
|
echo "APP_STORE_CONNECT_ISSUER_ID=$APPLE_ISSUER_ID" >> $CM_ENV
|
|
451
483
|
echo "APP_STORE_CONNECT_PRIVATE_KEY<<KEYDELIMITER" >> $CM_ENV
|
|
452
484
|
cat "$CM_BUILD_DIR/$P8_KEY_PATH" >> $CM_ENV
|
|
453
485
|
echo "" >> $CM_ENV
|
|
454
486
|
echo "KEYDELIMITER" >> $CM_ENV
|
|
455
|
-
VERSION_JSON=$(python3 scripts/manage_version_ios.py
|
|
456
|
-
|
|
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
|
+
")"
|
|
457
494
|
echo "APP_VERSION=$APP_VERSION" >> $CM_ENV
|
|
458
495
|
if [ "$GOOGLE_PLAY_READY" != "true" ]; then
|
|
459
496
|
LATEST_BUILD=0
|
|
@@ -461,14 +498,13 @@ workflows:
|
|
|
461
498
|
export GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS="$(cat $CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH)"
|
|
462
499
|
LATEST_BUILD_OUTPUT=$(google-play get-latest-build-number \
|
|
463
500
|
--package-name "$PACKAGE_NAME" \
|
|
464
|
-
--tracks=production,beta,alpha,internal
|
|
501
|
+
--tracks=production,beta,alpha,internal)
|
|
465
502
|
# Extract just the number from output
|
|
466
503
|
LATEST_BUILD=$(echo "$LATEST_BUILD_OUTPUT" | grep -oE '^[0-9]+$' | tail -1)
|
|
467
504
|
if [ -z "$LATEST_BUILD" ]; then
|
|
468
|
-
echo "
|
|
469
|
-
echo "Raw output: $LATEST_BUILD_OUTPUT"
|
|
470
|
-
|
|
471
|
-
LATEST_BUILD=1
|
|
505
|
+
echo "ERROR: Could not get latest build number from Google Play." >&2
|
|
506
|
+
echo "Raw output: $LATEST_BUILD_OUTPUT" >&2
|
|
507
|
+
exit 1
|
|
472
508
|
fi
|
|
473
509
|
echo "Latest build number from Google Play: $LATEST_BUILD"
|
|
474
510
|
fi
|
|
@@ -485,26 +521,25 @@ workflows:
|
|
|
485
521
|
echo "Android versionCode: $NEW_BUILD, versionName: $APP_VERSION"
|
|
486
522
|
|
|
487
523
|
- name: Flutter packages
|
|
488
|
-
script:
|
|
524
|
+
script: |
|
|
525
|
+
set -euo pipefail
|
|
526
|
+
cd $CM_BUILD_DIR/$APP_ROOT && flutter pub get
|
|
489
527
|
|
|
490
528
|
- name: Build Android
|
|
491
|
-
script:
|
|
529
|
+
script: |
|
|
530
|
+
set -euo pipefail
|
|
531
|
+
cd $CM_BUILD_DIR/$APP_ROOT && flutter build appbundle --release
|
|
492
532
|
|
|
493
533
|
- name: Upload AAB to Google Play
|
|
494
534
|
script: |
|
|
535
|
+
set -euo pipefail
|
|
495
536
|
if [ "$GOOGLE_PLAY_READY" != "true" ]; then
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
echo "See HOW_TO_GOOGLE_PLAY.md in artifacts for"
|
|
503
|
-
echo "step-by-step instructions."
|
|
504
|
-
echo ""
|
|
505
|
-
echo "After completing manual setup, push again"
|
|
506
|
-
echo "for fully automated publishing."
|
|
507
|
-
echo "============================================"
|
|
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
|
|
508
543
|
exit 1
|
|
509
544
|
fi
|
|
510
545
|
export GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$CM_BUILD_DIR/$GOOGLE_SA_JSON_PATH"
|
|
@@ -3,9 +3,10 @@ default_platform(:android)
|
|
|
3
3
|
# Resolve paths absolutely to avoid symlink-relative breakage.
|
|
4
4
|
# CM_BUILD_DIR is set by Codemagic CI; locally we derive from __FILE__.
|
|
5
5
|
ROOT_DIR = ENV.fetch("CM_BUILD_DIR", File.expand_path("../..", __FILE__))
|
|
6
|
-
|
|
6
|
+
APP_DIR = ENV.fetch("APP_ROOT", "#{ROOT_DIR}/app")
|
|
7
|
+
APP_DIR = "#{ROOT_DIR}/#{APP_DIR}" unless APP_DIR.start_with?("/")
|
|
7
8
|
|
|
8
|
-
AAB_PATH = "#{
|
|
9
|
+
AAB_PATH = "#{APP_DIR}/build/app/outputs/bundle/release/app-release.aab"
|
|
9
10
|
|
|
10
11
|
def metadata_changed?(path)
|
|
11
12
|
!sh("git diff --name-only HEAD~1 -- #{path}").strip.empty?
|
|
@@ -46,15 +47,32 @@ platform :android do
|
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
lane :upload_metadata_android do
|
|
50
|
+
# supply always calls fetch_track_and_release! even for metadata-only
|
|
51
|
+
# uploads. When the track contains multiple releases and no version_code
|
|
52
|
+
# is given it errors with "more than one release found". We resolve this
|
|
53
|
+
# by fetching the latest version code from the track first.
|
|
54
|
+
track = ENV.fetch("TRACK", "internal")
|
|
55
|
+
codes = google_play_track_version_codes(
|
|
56
|
+
package_name: ENV["PACKAGE_NAME"],
|
|
57
|
+
json_key: ENV["GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH"],
|
|
58
|
+
track: track
|
|
59
|
+
)
|
|
60
|
+
latest_code = codes.max
|
|
61
|
+
|
|
62
|
+
# Draft apps only accept draft releases. Use RELEASE_STATUS env var so
|
|
63
|
+
# this keeps working after the first production release.
|
|
64
|
+
status = ENV.fetch("RELEASE_STATUS", "draft")
|
|
65
|
+
|
|
49
66
|
upload_to_play_store(
|
|
50
67
|
base_play_store_options.merge(
|
|
68
|
+
version_code: latest_code,
|
|
69
|
+
release_status: status,
|
|
51
70
|
skip_upload_aab: true,
|
|
52
71
|
skip_upload_apk: true,
|
|
53
72
|
skip_upload_metadata: false,
|
|
54
73
|
skip_upload_screenshots: false,
|
|
55
74
|
skip_upload_images: false,
|
|
56
75
|
skip_upload_changelogs: true,
|
|
57
|
-
release_status: "draft",
|
|
58
76
|
metadata_path: "#{ROOT_DIR}/fastlane/metadata/android"
|
|
59
77
|
)
|
|
60
78
|
)
|
|
@@ -3,7 +3,8 @@ default_platform(:ios)
|
|
|
3
3
|
# Resolve paths absolutely to avoid symlink-relative breakage.
|
|
4
4
|
# CM_BUILD_DIR is set by Codemagic CI; locally we derive from __FILE__.
|
|
5
5
|
ROOT_DIR = ENV.fetch("CM_BUILD_DIR", File.expand_path("../..", __FILE__))
|
|
6
|
-
|
|
6
|
+
APP_DIR = ENV.fetch("APP_ROOT", "#{ROOT_DIR}/app")
|
|
7
|
+
APP_DIR = "#{ROOT_DIR}/#{APP_DIR}" unless APP_DIR.start_with?("/")
|
|
7
8
|
|
|
8
9
|
# Monkey-patch: Fastlane deliver crashes with "No data" when fetching
|
|
9
10
|
# app_store_review_detail on the first version (fastlane/fastlane#20538).
|
|
@@ -74,7 +75,7 @@ platform :ios do
|
|
|
74
75
|
lane :deploy_ios do
|
|
75
76
|
deliver(
|
|
76
77
|
metadata_deliver_options.merge(submission_options).merge(
|
|
77
|
-
ipa: Dir.glob("#{
|
|
78
|
+
ipa: Dir.glob("#{APP_DIR}/build/ios/ipa/*.ipa").first,
|
|
78
79
|
app_rating_config_path: "#{ROOT_DIR}/fastlane/app_rating_config.json",
|
|
79
80
|
skip_metadata: !metadata_changed?("fastlane/metadata/ios/"),
|
|
80
81
|
skip_screenshots: !metadata_changed?("fastlane/screenshots/ios/"),
|
|
@@ -99,7 +100,7 @@ platform :ios do
|
|
|
99
100
|
lane :upload_binary_ios do
|
|
100
101
|
deliver(
|
|
101
102
|
base_deliver_options.merge(submission_options).merge(
|
|
102
|
-
ipa: Dir.glob("#{
|
|
103
|
+
ipa: Dir.glob("#{APP_DIR}/build/ios/ipa/*.ipa").first,
|
|
103
104
|
skip_metadata: true,
|
|
104
105
|
skip_screenshots: true,
|
|
105
106
|
run_precheck_before_submit: true,
|