@daemux/store-automator 0.10.0 → 0.10.1

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.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "App Store & Google Play automation for Flutter apps",
8
- "version": "0.10.0"
8
+ "version": "0.10.1"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
12
  "name": "store-automator",
13
13
  "source": "./plugins/store-automator",
14
14
  "description": "3 agents for app store publishing: reviewer, meta-creator, media-designer",
15
- "version": "0.10.0",
15
+ "version": "0.10.1",
16
16
  "keywords": ["flutter", "app-store", "google-play", "fastlane"]
17
17
  }
18
18
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daemux/store-automator",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "Full App Store & Google Play automation for Flutter apps with Claude Code agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "store-automator",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "App Store & Google Play automation agents for Flutter app publishing",
5
5
  "author": {
6
6
  "name": "Daemux"
@@ -80,7 +80,8 @@ platform :ios do
80
80
  skip_metadata: !metadata_changed?("fastlane/metadata/ios/"),
81
81
  skip_screenshots: !metadata_changed?("fastlane/screenshots/ios/"),
82
82
  run_precheck_before_submit: true,
83
- precheck_include_in_app_purchases: false
83
+ precheck_include_in_app_purchases: false,
84
+ wait_processing_timeout_duration: 1800
84
85
  )
85
86
  )
86
87
  end
@@ -99,12 +100,13 @@ platform :ios do
99
100
 
100
101
  lane :upload_binary_ios do
101
102
  deliver(
102
- base_deliver_options.merge(submission_options).merge(
103
+ base_deliver_options.merge(
103
104
  ipa: Dir.glob("#{APP_DIR}/build/ios/ipa/*.ipa").first,
104
105
  skip_metadata: true,
105
106
  skip_screenshots: true,
106
- run_precheck_before_submit: true,
107
- precheck_include_in_app_purchases: false
107
+ run_precheck_before_submit: false,
108
+ submit_for_review: false,
109
+ wait_processing_timeout_duration: 1800
108
110
  )
109
111
  )
110
112
  end
@@ -35,6 +35,9 @@ jobs:
35
35
  - name: Install yq
36
36
  run: brew install yq
37
37
 
38
+ - name: Init Summary
39
+ run: echo "| Step | Status | Details |" >> $GITHUB_STEP_SUMMARY && echo "|------|--------|---------|" >> $GITHUB_STEP_SUMMARY
40
+
38
41
  - name: Link Fastlane
39
42
  run: scripts/ci/common/link-fastlane.sh android
40
43
 
@@ -45,15 +48,18 @@ jobs:
45
48
  run: scripts/ci/android/check-readiness.sh
46
49
 
47
50
  - name: Upload Metadata & Screenshots
51
+ id: upload-metadata
48
52
  run: scripts/ci/android/upload-metadata.sh
49
53
 
50
54
  - name: Sync IAP
55
+ id: sync-iap
51
56
  run: scripts/ci/android/sync-iap.sh
52
57
 
53
58
  - name: Install Python dependencies
54
59
  run: pip3 install --break-system-packages google-api-python-client google-auth
55
60
 
56
61
  - name: Update Data Safety
62
+ id: update-data-safety
57
63
  run: scripts/ci/android/update-data-safety.sh
58
64
 
59
65
  - name: Save .ci-state
@@ -35,6 +35,9 @@ jobs:
35
35
  - name: Install yq
36
36
  run: brew install yq
37
37
 
38
+ - name: Init Summary
39
+ run: echo "| Step | Status | Details |" >> $GITHUB_STEP_SUMMARY && echo "|------|--------|---------|" >> $GITHUB_STEP_SUMMARY
40
+
38
41
  - name: Link Fastlane
39
42
  run: scripts/ci/common/link-fastlane.sh ios
40
43
 
@@ -42,9 +45,11 @@ jobs:
42
45
  run: scripts/ci/common/install-fastlane.sh ios
43
46
 
44
47
  - name: Upload Metadata & Screenshots
48
+ id: upload-metadata
45
49
  run: scripts/ci/ios/upload-metadata.sh
46
50
 
47
51
  - name: Sync IAP
52
+ id: sync-iap
48
53
  run: scripts/ci/ios/sync-iap.sh
49
54
 
50
55
  - name: Save .ci-state
@@ -67,6 +72,16 @@ jobs:
67
72
  with:
68
73
  fetch-depth: 0
69
74
 
75
+ - name: Select Xcode 26
76
+ run: |
77
+ XCODE_PATH=$(ls -d /Applications/Xcode_26*.app 2>/dev/null | sort -V | tail -1)
78
+ if [ -z "$XCODE_PATH" ]; then
79
+ echo "ERROR: No Xcode 26.x found on this runner" >&2
80
+ exit 1
81
+ fi
82
+ sudo xcode-select -s "$XCODE_PATH"
83
+ xcodebuild -version
84
+
70
85
  - name: Setup Ruby
71
86
  uses: ruby/setup-ruby@v1
72
87
  with:
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env bash
2
+ # Requires link-fastlane.sh and install-fastlane.sh to have run first (workflow steps).
2
3
  set -euo pipefail
3
4
 
4
5
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
6
  source "$SCRIPT_DIR/../common/read-config.sh"
7
+ source "$SCRIPT_DIR/../common/ci-notify.sh"
6
8
 
7
9
  # --- Check Google Play readiness ---
8
10
  if [ "${GOOGLE_PLAY_READY:-false}" != "true" ]; then
@@ -13,16 +15,13 @@ fi
13
15
  # --- Check if IAP config exists ---
14
16
  IAP_CONFIG="$PROJECT_ROOT/fastlane/iap_config.json"
15
17
  if [ ! -f "$IAP_CONFIG" ]; then
16
- echo "No iap_config.json found at $IAP_CONFIG. Skipping IAP sync."
17
- exit 0
18
+ ci_skip "No Android IAP config file found"
18
19
  fi
19
20
 
20
21
  # --- Check if IAP plugin is available ---
21
22
  cd "$APP_ROOT/android"
22
- if ! bundle exec gem list fastlane-plugin-iap --installed > /dev/null 2>&1; then
23
- echo "WARNING: fastlane-plugin-iap not installed. Skipping IAP sync."
24
- echo "To enable: add it to app/android/Pluginfile and run 'bundle install'."
25
- exit 0
23
+ if ! bundle exec gem list fastlane-plugin-iap --installed >/dev/null 2>&1; then
24
+ ci_skip "fastlane-plugin-iap not installed"
26
25
  fi
27
26
 
28
27
  # --- Hash-based change detection ---
@@ -35,8 +34,7 @@ STATE_FILE="$STATE_DIR/android-iap-hash"
35
34
  if [ -f "$STATE_FILE" ]; then
36
35
  STORED_HASH=$(cat "$STATE_FILE")
37
36
  if [ "$HASH" = "$STORED_HASH" ]; then
38
- echo "No changes in iap_config.json (hash: ${HASH:0:12}...). Skipping."
39
- exit 0
37
+ ci_skip "Android IAP config unchanged since last sync"
40
38
  fi
41
39
  fi
42
40
 
@@ -56,8 +54,7 @@ PACKAGE_NAME="$PACKAGE_NAME" \
56
54
  GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$SA_FULL_PATH" \
57
55
  bundle exec fastlane sync_google_iap
58
56
 
59
- echo "Android IAP sync complete"
60
-
61
57
  # --- Update hash on success ---
62
58
  echo "$HASH" > "$STATE_FILE"
63
- echo "Updated state hash: ${HASH:0:12}..."
59
+
60
+ ci_done "Android IAP synced to Google Play"
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env bash
2
+ # Requires link-fastlane.sh and install-fastlane.sh to have run first (workflow steps).
2
3
  set -euo pipefail
3
4
 
4
5
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
6
  source "$SCRIPT_DIR/../common/read-config.sh"
7
+ source "$SCRIPT_DIR/../common/ci-notify.sh"
6
8
 
7
9
  # --- Check Google Play readiness ---
8
10
  if [ "${GOOGLE_PLAY_READY:-false}" != "true" ]; then
@@ -13,8 +15,7 @@ fi
13
15
  # --- Check if data safety CSV exists ---
14
16
  DATA_SAFETY_CSV="$PROJECT_ROOT/fastlane/data_safety.csv"
15
17
  if [ ! -f "$DATA_SAFETY_CSV" ]; then
16
- echo "No data_safety.csv found at $DATA_SAFETY_CSV. Skipping."
17
- exit 0
18
+ ci_skip "No data safety CSV found"
18
19
  fi
19
20
 
20
21
  # --- Hash-based change detection ---
@@ -27,22 +28,12 @@ STATE_FILE="$STATE_DIR/android-data-safety-hash"
27
28
  if [ -f "$STATE_FILE" ]; then
28
29
  STORED_HASH=$(cat "$STATE_FILE")
29
30
  if [ "$HASH" = "$STORED_HASH" ]; then
30
- echo "No changes in data_safety.csv (hash: ${HASH:0:12}...). Skipping."
31
- exit 0
31
+ ci_skip "Data safety CSV unchanged since last upload"
32
32
  fi
33
33
  fi
34
34
 
35
35
  echo "Changes detected in data safety config (hash: ${HASH:0:12}...)"
36
36
 
37
- # --- Link fastlane directories ---
38
- "$SCRIPT_DIR/../common/link-fastlane.sh" android
39
-
40
- # --- Ensure fastlane is installed ---
41
- cd "$APP_ROOT/android"
42
- if ! bundle exec fastlane --version >/dev/null 2>&1; then
43
- "$SCRIPT_DIR/../common/install-fastlane.sh" android
44
- fi
45
-
46
37
  # --- Resolve service account path ---
47
38
  SA_FULL_PATH="$PROJECT_ROOT/$GOOGLE_SA_JSON_PATH"
48
39
  if [ ! -f "$SA_FULL_PATH" ]; then
@@ -62,16 +53,11 @@ SAFETY_EXIT=$?
62
53
  set -e
63
54
 
64
55
  if [ $SAFETY_EXIT -eq 0 ]; then
65
- echo "Android data safety update complete"
66
56
  echo "$HASH" > "$STATE_FILE"
67
- echo "Updated state hash: ${HASH:0:12}..."
57
+ ci_done "Data safety section updated"
68
58
  elif [ $SAFETY_EXIT -eq 2 ]; then
69
59
  # API limitation is non-fatal; hash is NOT saved so the step retries next run
70
- echo ""
71
- echo "WARNING: Data safety update skipped due to API limitation."
72
- echo " The form must be updated manually via Google Play Console."
73
- echo " This step will retry on the next run."
74
- echo ""
60
+ ci_skip "API limitation — update manually via Google Play Console (will retry next run)"
75
61
  else
76
62
  echo "ERROR: Data safety update failed (exit code: $SAFETY_EXIT)" >&2
77
63
  exit $SAFETY_EXIT
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env bash
2
+ # Requires link-fastlane.sh and install-fastlane.sh to have run first (workflow steps).
2
3
  set -euo pipefail
3
4
 
4
5
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
6
  source "$SCRIPT_DIR/../common/read-config.sh"
7
+ source "$SCRIPT_DIR/../common/ci-notify.sh"
6
8
 
7
9
  # --- Check Google Play readiness ---
8
10
  if [ "${GOOGLE_PLAY_READY:-false}" != "true" ]; then
@@ -16,28 +18,22 @@ STATE_DIR="$PROJECT_ROOT/.ci-state"
16
18
  mkdir -p "$STATE_DIR"
17
19
 
18
20
  HASH=$(find "$PROJECT_ROOT/fastlane/metadata" "$PROJECT_ROOT/fastlane/screenshots/android" \
19
- -type f 2>/dev/null | sort | xargs shasum -a 256 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
21
+ -type f ! -name '.DS_Store' -print0 2>/dev/null \
22
+ | LC_ALL=C sort -z \
23
+ | xargs -0 shasum -a 256 2>/dev/null \
24
+ | shasum -a 256 \
25
+ | cut -d' ' -f1)
20
26
  STATE_FILE="$STATE_DIR/android-metadata-hash"
21
27
 
22
28
  if [ -f "$STATE_FILE" ]; then
23
29
  STORED_HASH=$(cat "$STATE_FILE")
24
30
  if [ "$HASH" = "$STORED_HASH" ]; then
25
- echo "No changes in Android metadata or screenshots (hash: ${HASH:0:12}...). Skipping."
26
- exit 0
31
+ ci_skip "Android metadata unchanged since last upload"
27
32
  fi
28
33
  fi
29
34
 
30
35
  echo "Changes detected in Android metadata (hash: ${HASH:0:12}...)"
31
36
 
32
- # --- Link fastlane directories ---
33
- "$SCRIPT_DIR/../common/link-fastlane.sh" android
34
-
35
- # --- Ensure fastlane is installed ---
36
- cd "$APP_ROOT/android"
37
- if ! bundle exec fastlane --version >/dev/null 2>&1; then
38
- "$SCRIPT_DIR/../common/install-fastlane.sh" android
39
- fi
40
-
41
37
  # --- Resolve service account path ---
42
38
  SA_FULL_PATH="$PROJECT_ROOT/$GOOGLE_SA_JSON_PATH"
43
39
  if [ ! -f "$SA_FULL_PATH" ]; then
@@ -62,18 +58,12 @@ if [ $FASTLANE_EXIT -ne 0 ]; then
62
58
  # published. This resolves after the first manual release via the Play
63
59
  # Console, so we warn instead of failing the workflow.
64
60
  if echo "$FASTLANE_OUTPUT" | grep -qi "draft app"; then
65
- echo ""
66
- echo "WARNING: Metadata upload failed because the app is still in draft"
67
- echo " state on Google Play. Complete the first release manually"
68
- echo " via the Play Console, then re-run this workflow."
69
- echo ""
70
- exit 0
61
+ ci_skip "App is in draft status — manual first release required on Google Play Console"
71
62
  fi
72
63
  exit $FASTLANE_EXIT
73
64
  fi
74
65
 
75
- echo "Android metadata uploaded successfully"
76
-
77
66
  # --- Update hash on success ---
78
67
  echo "$HASH" > "$STATE_FILE"
79
- echo "Updated state hash: ${HASH:0:12}..."
68
+
69
+ ci_done "Android metadata uploaded to Google Play"
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Shared CI notification helpers for GitHub Actions.
5
+ # Source this file; then call ci_skip or ci_done.
6
+
7
+ STEP_NAME="${STEP_NAME:-$(basename "${BASH_SOURCE[1]:-$0}" .sh)}"
8
+
9
+ _ci_summary_row() {
10
+ local status="$1" detail="$2"
11
+ if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
12
+ echo "| ${STEP_NAME} | ${status} | ${detail} |" >> "$GITHUB_STEP_SUMMARY"
13
+ fi
14
+ }
15
+
16
+ _ci_output() {
17
+ local key="$1" value="$2"
18
+ if [ -n "${GITHUB_OUTPUT:-}" ]; then
19
+ echo "${key}=${value}" >> "$GITHUB_OUTPUT"
20
+ fi
21
+ }
22
+
23
+ ci_skip() {
24
+ local reason="${1:-skipped}"
25
+ echo "::warning::${STEP_NAME}: ${reason}"
26
+ _ci_summary_row "Skipped" "$reason"
27
+ _ci_output "status" "skipped"
28
+ echo "[SKIP] ${STEP_NAME}: ${reason}"
29
+ exit 0
30
+ }
31
+
32
+ ci_done() {
33
+ local message="${1:-done}"
34
+ echo "::notice::${STEP_NAME}: ${message}"
35
+ _ci_summary_row "Done" "$message"
36
+ _ci_output "status" "done"
37
+ echo "[DONE] ${STEP_NAME}: ${message}"
38
+ exit 0
39
+ }
@@ -1,35 +1,30 @@
1
1
  #!/usr/bin/env bash
2
+ # Requires link-fastlane.sh and install-fastlane.sh to have run first (workflow steps).
2
3
  set -euo pipefail
3
4
 
4
5
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
- COMMON_DIR="$SCRIPT_DIR/../common"
6
- source "$COMMON_DIR/read-config.sh"
6
+ source "$SCRIPT_DIR/../common/read-config.sh"
7
+ source "$SCRIPT_DIR/../common/ci-notify.sh"
7
8
 
8
9
  echo "=== iOS IAP Sync ==="
9
10
 
10
- # ── Step 1: Check if IAP config file exists ──
11
+ # --- Check if IAP config file exists ---
11
12
  IAP_CONFIG="$PROJECT_ROOT/fastlane/iap_config.json"
12
13
 
13
14
  if [ ! -f "$IAP_CONFIG" ]; then
14
- echo "No IAP config found at $IAP_CONFIG. Skipping IAP sync."
15
- exit 0
15
+ ci_skip "No iOS IAP config file found"
16
16
  fi
17
17
 
18
- # ── Step 2: Install Fastlane (needed to check plugin availability) ──
19
- "$COMMON_DIR/install-fastlane.sh" ios
20
-
21
- # ── Step 3: Check if IAP plugin is available ──
18
+ # --- Check if IAP plugin is available ---
22
19
  cd "$APP_ROOT/ios"
23
20
 
24
21
  if ! bundle exec gem list fastlane-plugin-iap --installed >/dev/null 2>&1; then
25
- echo "WARNING: fastlane-plugin-iap not installed. Skipping IAP sync."
26
- echo "To enable: add 'fastlane-plugin-iap' to $APP_ROOT/ios/Gemfile and run 'bundle install'."
27
- exit 0
22
+ ci_skip "fastlane-plugin-iap not installed"
28
23
  fi
29
24
 
30
25
  echo "fastlane-plugin-iap is installed. Proceeding with sync."
31
26
 
32
- # ── Step 4: Hash-based change detection ──
27
+ # --- Hash-based change detection ---
33
28
  CURRENT_HASH=$(shasum -a 256 "$IAP_CONFIG" | cut -d' ' -f1)
34
29
 
35
30
  STATE_DIR="$PROJECT_ROOT/.ci-state"
@@ -39,18 +34,14 @@ STATE_FILE="$STATE_DIR/ios-iap-hash"
39
34
  if [ -f "$STATE_FILE" ]; then
40
35
  STORED_HASH=$(cat "$STATE_FILE")
41
36
  if [ "$CURRENT_HASH" = "$STORED_HASH" ]; then
42
- echo "IAP config unchanged (hash: ${CURRENT_HASH:0:12}...). Skipping sync."
43
- exit 0
37
+ ci_skip "iOS IAP config unchanged since last sync"
44
38
  fi
45
39
  echo "IAP config changed (old: ${STORED_HASH:0:12}..., new: ${CURRENT_HASH:0:12}...)"
46
40
  else
47
41
  echo "No cached hash found. First run — will sync IAPs."
48
42
  fi
49
43
 
50
- # ── Step 5: Setup Fastlane symlink and build dir ──
51
- "$COMMON_DIR/link-fastlane.sh" ios
52
-
53
- # ── Step 6: Set up App Store Connect API key ──
44
+ # --- Set up App Store Connect API key ---
54
45
  P8_FULL_PATH="$PROJECT_ROOT/$P8_KEY_PATH"
55
46
  if [ ! -f "$P8_FULL_PATH" ]; then
56
47
  echo "ERROR: P8 key file not found at $P8_FULL_PATH" >&2
@@ -64,17 +55,13 @@ export APP_STORE_CONNECT_API_KEY_IS_KEY_CONTENT_BASE64="false"
64
55
 
65
56
  echo "ASC API key configured (Key ID: $APPLE_KEY_ID)"
66
57
 
67
- # ── Step 7: Run IAP sync ──
58
+ # --- Run IAP sync ---
68
59
  echo "Syncing IAPs to App Store Connect..."
69
60
 
70
- cd "$APP_ROOT/ios"
71
-
72
61
  FASTLANE_API_KEY_PATH="$P8_FULL_PATH" \
73
62
  BUNDLE_ID="$BUNDLE_ID" \
74
63
  bundle exec fastlane sync_iap
75
64
 
76
- # ── Step 8: Update hash on success ──
65
+ # --- Update hash on success ---
77
66
  echo "$CURRENT_HASH" > "$STATE_FILE"
78
- echo "IAP sync successful. Hash cached: ${CURRENT_HASH:0:12}..."
79
-
80
- echo "=== iOS IAP Sync Complete ==="
67
+ ci_done "iOS IAP synced to App Store Connect"
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env bash
2
+ # Requires link-fastlane.sh and install-fastlane.sh to have run first (workflow steps).
2
3
  set -euo pipefail
3
4
 
4
5
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
- COMMON_DIR="$SCRIPT_DIR/../common"
6
- source "$COMMON_DIR/read-config.sh"
6
+ source "$SCRIPT_DIR/../common/read-config.sh"
7
+ source "$SCRIPT_DIR/../common/ci-notify.sh"
7
8
 
8
9
  echo "=== iOS Metadata & Screenshots Upload ==="
9
10
 
@@ -12,8 +13,7 @@ METADATA_DIR="$PROJECT_ROOT/fastlane/metadata"
12
13
  SCREENSHOTS_DIR="$PROJECT_ROOT/fastlane/screenshots/ios"
13
14
 
14
15
  if [ ! -d "$METADATA_DIR" ] && [ ! -d "$SCREENSHOTS_DIR" ]; then
15
- echo "No metadata or screenshots directories found. Skipping upload."
16
- exit 0
16
+ ci_skip "No iOS metadata directories found"
17
17
  fi
18
18
 
19
19
  HASH_DIRS=()
@@ -37,8 +37,7 @@ STATE_FILE="$STATE_DIR/ios-metadata-hash"
37
37
  if [ -f "$STATE_FILE" ]; then
38
38
  STORED_HASH=$(cat "$STATE_FILE")
39
39
  if [ "$HASH" = "$STORED_HASH" ]; then
40
- echo "Metadata and screenshots unchanged (hash: ${HASH:0:12}...). Skipping upload."
41
- exit 0
40
+ ci_skip "iOS metadata unchanged since last upload"
42
41
  fi
43
42
  echo "Changes detected (old: ${STORED_HASH:0:12}..., new: ${HASH:0:12}...)"
44
43
  else
@@ -65,10 +64,6 @@ export FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS=1
65
64
 
66
65
  echo "ASC API key configured (Key ID: $APPLE_KEY_ID)"
67
66
 
68
- # --- Link and install Fastlane ---
69
- "$COMMON_DIR/link-fastlane.sh" ios
70
- "$COMMON_DIR/install-fastlane.sh" ios
71
-
72
67
  # --- Run upload ---
73
68
  echo "Uploading iOS metadata and screenshots..."
74
69
  cd "$APP_ROOT/ios"
@@ -77,6 +72,4 @@ bundle exec fastlane upload_metadata_ios
77
72
 
78
73
  # --- Update hash on success ---
79
74
  echo "$HASH" > "$STATE_FILE"
80
- echo "iOS metadata uploaded successfully. Hash cached: ${HASH:0:12}..."
81
-
82
- echo "=== iOS Metadata & Screenshots Upload Complete ==="
75
+ ci_done "iOS metadata uploaded to App Store Connect"