@daemux/store-automator 0.9.1 → 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.
Files changed (44) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +5 -23
  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 +8 -6
  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 +6 -4
  26. package/templates/github/workflows/ios-release.yml +15 -4
  27. package/templates/scripts/check_changed.sh +1 -1
  28. package/templates/scripts/ci/android/sync-iap.sh +8 -11
  29. package/templates/scripts/ci/android/update-data-safety.sh +20 -21
  30. package/templates/scripts/ci/android/upload-metadata.sh +11 -21
  31. package/templates/scripts/ci/common/ci-notify.sh +39 -0
  32. package/templates/scripts/ci/common/link-fastlane.sh +1 -1
  33. package/templates/scripts/ci/common/read-config.sh +1 -5
  34. package/templates/scripts/ci/ios/sync-iap.sh +13 -26
  35. package/templates/scripts/ci/ios/upload-metadata.sh +6 -13
  36. package/templates/scripts/update_data_safety.py +14 -10
  37. package/scripts/codemagic-setup.mjs +0 -44
  38. package/scripts/generate.sh +0 -107
  39. package/src/codemagic-api.mjs +0 -75
  40. package/src/codemagic-setup.mjs +0 -190
  41. package/src/github-setup.mjs +0 -43
  42. package/templates/codemagic.template.yaml +0 -551
  43. package/templates/github/workflows/codemagic-trigger.yml +0 -78
  44. package/templates/scripts/generate.sh +0 -107
package/src/install.mjs CHANGED
@@ -12,7 +12,7 @@ import { promptForTokens } from './prompt.mjs';
12
12
  import { getMcpServers, writeMcpJson } from './mcp-setup.mjs';
13
13
  import { installClaudeMd, installCiTemplates, installFirebaseTemplates } from './templates.mjs';
14
14
  import { writeCiBundleId, writeCiPackageName } from './ci-config.mjs';
15
- import { installGitHubActionsPath, installCodemagicPath } from './install-paths.mjs';
15
+ import { installGitHubActionsPath } from './install-paths.mjs';
16
16
 
17
17
  function checkClaudeCli() {
18
18
  const result = exec('command -v claude') || exec('which claude');
@@ -94,20 +94,13 @@ function printSummary(scope, oldVersion, newVersion) {
94
94
  }
95
95
  }
96
96
 
97
- function printNextSteps(isGitHubActions) {
97
+ function printNextSteps() {
98
98
  console.log('');
99
99
  console.log('Next steps:');
100
- if (isGitHubActions) {
101
- console.log(' 1. Fill ci.config.yaml with credentials');
102
- console.log(' 2. Add creds/AuthKey.p8 and creds/play-service-account.json');
103
- console.log(' 3. Set MATCH_PASSWORD secret in GitHub repository settings');
104
- console.log(' 4. Start Claude Code');
105
- } else {
106
- console.log(' 1. Fill ci.config.yaml (codemagic.app_id is auto-configured if token was provided)');
107
- console.log(' 2. Add creds/AuthKey.p8 and creds/play-service-account.json');
108
- console.log(' 3. Start Claude Code');
109
- console.log(' Note: For auto-trigger, install gh CLI and run "gh auth login"');
110
- }
100
+ console.log(' 1. Fill ci.config.yaml with credentials');
101
+ console.log(' 2. Add creds/AuthKey.p8 and creds/play-service-account.json');
102
+ console.log(' 3. Set MATCH_PASSWORD secret in GitHub repository settings');
103
+ console.log(' 4. Start Claude Code');
111
104
  }
112
105
 
113
106
  export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
@@ -154,11 +147,7 @@ export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
154
147
  writeCiPackageName(projectDir, tokens.bundleId);
155
148
  }
156
149
 
157
- if (isGitHubActions) {
158
- installGitHubActionsPath(projectDir, packageDir, cliTokens);
159
- } else {
160
- await installCodemagicPath(projectDir, tokens);
161
- }
150
+ installGitHubActionsPath(projectDir, packageDir, cliTokens);
162
151
 
163
152
  const scopeLabel = scope === 'user' ? 'global' : 'project';
164
153
  console.log(`Configuring ${scopeLabel} settings...`);
@@ -167,5 +156,5 @@ export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
167
156
  injectStatusLine(settingsPath);
168
157
 
169
158
  printSummary(scope, oldVersion, newVersion);
170
- printNextSteps(isGitHubActions);
159
+ printNextSteps();
171
160
  }
package/src/mcp-setup.mjs CHANGED
@@ -32,17 +32,6 @@ export function getMcpServers(tokens) {
32
32
  };
33
33
  }
34
34
 
35
- if (tokens.codemagicToken) {
36
- const codemagicEnv = { CODEMAGIC_API_TOKEN: tokens.codemagicToken };
37
- if (tokens.codemagicTeamId) codemagicEnv.CODEMAGIC_TEAM_ID = tokens.codemagicTeamId;
38
- if (tokens.codemagicAppId) codemagicEnv.CODEMAGIC_APP_ID = tokens.codemagicAppId;
39
- servers.codemagic = {
40
- command: 'npx',
41
- args: ['-y', '@daemux/codemagic-mcp@latest'],
42
- env: codemagicEnv,
43
- };
44
- }
45
-
46
35
  return servers;
47
36
  }
48
37
 
@@ -78,29 +67,6 @@ export function writeMcpJson(projectDir, servers) {
78
67
  }
79
68
  }
80
69
 
81
- function updateMcpEnvVar(projectDir, envKey, value) {
82
- const mcpPath = join(projectDir, '.mcp.json');
83
- if (!existsSync(mcpPath)) return false;
84
-
85
- try {
86
- const data = readJson(mcpPath);
87
- if (!data.mcpServers?.codemagic?.env) return false;
88
- data.mcpServers.codemagic.env[envKey] = value;
89
- writeJson(mcpPath, data);
90
- return true;
91
- } catch {
92
- return false;
93
- }
94
- }
95
-
96
- export function updateMcpAppId(projectDir, appId) {
97
- return updateMcpEnvVar(projectDir, 'CODEMAGIC_APP_ID', appId);
98
- }
99
-
100
- export function updateMcpTeamId(projectDir, teamId) {
101
- return updateMcpEnvVar(projectDir, 'CODEMAGIC_TEAM_ID', teamId);
102
- }
103
-
104
70
  export function removeMcpServers(projectDir) {
105
71
  const mcpPath = join(projectDir, '.mcp.json');
106
72
  if (!existsSync(mcpPath)) return;
@@ -109,7 +75,7 @@ export function removeMcpServers(projectDir) {
109
75
  const data = readJson(mcpPath);
110
76
  if (!data.mcpServers) return;
111
77
 
112
- const toRemove = ['playwright', 'mobile-mcp', 'stitch', 'cloudflare', 'codemagic'];
78
+ const toRemove = ['playwright', 'mobile-mcp', 'stitch', 'cloudflare'];
113
79
  for (const name of toRemove) {
114
80
  delete data.mcpServers[name];
115
81
  }
package/src/prompt.mjs CHANGED
@@ -16,9 +16,7 @@ function allTokensProvided(cliTokens) {
16
16
  return (
17
17
  cliTokens.stitchApiKey !== undefined &&
18
18
  cliTokens.cloudflareToken !== undefined &&
19
- cliTokens.cloudflareAccountId !== undefined &&
20
- cliTokens.codemagicToken !== undefined &&
21
- cliTokens.codemagicTeamId !== undefined
19
+ cliTokens.cloudflareAccountId !== undefined
22
20
  );
23
21
  }
24
22
 
@@ -35,8 +33,6 @@ export async function promptForTokens(cliTokens = {}) {
35
33
  stitchApiKey: cliTokens.stitchApiKey ?? '',
36
34
  cloudflareToken: cliTokens.cloudflareToken ?? '',
37
35
  cloudflareAccountId: cliTokens.cloudflareAccountId ?? '',
38
- codemagicToken: cliTokens.codemagicToken ?? '',
39
- codemagicTeamId: cliTokens.codemagicTeamId ?? '',
40
36
  };
41
37
 
42
38
  if (allPromptsProvided(cliTokens)) {
@@ -98,20 +94,6 @@ export async function promptForTokens(cliTokens = {}) {
98
94
  );
99
95
  }
100
96
 
101
- if (cliTokens.codemagicToken === undefined) {
102
- result.codemagicToken = await ask(
103
- rl,
104
- 'Codemagic API Token (CM_API_TOKEN for CI/CD builds): '
105
- );
106
- }
107
-
108
- if (result.codemagicToken && cliTokens.codemagicTeamId === undefined) {
109
- result.codemagicTeamId = await ask(
110
- rl,
111
- 'Codemagic Team ID (optional, from Teams page): '
112
- );
113
- }
114
-
115
97
  return result;
116
98
  } finally {
117
99
  rl.close();
package/src/templates.mjs CHANGED
@@ -4,7 +4,6 @@ import { ensureDir } from './utils.mjs';
4
4
 
5
5
  const FILE_COPIES = [
6
6
  ['ci.config.yaml.template', 'ci.config.yaml'],
7
- ['codemagic.template.yaml', 'ci-templates/codemagic.template.yaml'],
8
7
  ['Gemfile.template', 'Gemfile'],
9
8
  ];
10
9
 
package/src/uninstall.mjs CHANGED
@@ -43,7 +43,6 @@ function removeFileIfExists(filePath, label) {
43
43
  function removeCiTemplates(projectDir) {
44
44
  const files = [
45
45
  'ci.config.yaml',
46
- 'ci-templates/codemagic.template.yaml',
47
46
  'Gemfile',
48
47
  ];
49
48
  for (const file of files) {
@@ -68,9 +67,6 @@ function isDirEmpty(dirPath) {
68
67
  }
69
68
 
70
69
  function removeGitHubWorkflow(projectDir) {
71
- const workflowFile = join(projectDir, '.github', 'workflows', 'codemagic-trigger.yml');
72
- removeFileIfExists(workflowFile, '.github/workflows/codemagic-trigger.yml');
73
-
74
70
  const workflowsDir = join(projectDir, '.github', 'workflows');
75
71
  const githubDir = join(projectDir, '.github');
76
72
 
package/src/utils.mjs CHANGED
@@ -44,12 +44,3 @@ export function readJson(filePath) {
44
44
  export function writeJson(filePath, data) {
45
45
  writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
46
46
  }
47
-
48
- export function resolveToken(tokenArg) {
49
- const token = process.env.CM_API_TOKEN || tokenArg;
50
- if (!token) {
51
- console.error('Codemagic API token required. Set CM_API_TOKEN or pass --token=...');
52
- process.exit(1);
53
- }
54
- return token;
55
- }
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Project Overview
4
4
 
5
- This is a Flutter app with Firebase backend, automated for App Store and Google Play publishing via Codemagic CI/CD. This repository MUST be PRIVATE as it contains credential files.
5
+ This is a Flutter app with Firebase backend, automated for App Store and Google Play publishing via GitHub Actions CI/CD. This repository MUST be PRIVATE as it contains credential files.
6
6
 
7
7
  ## Mandatory Rules
8
8
 
@@ -14,7 +14,7 @@ This is a Flutter app with Firebase backend, automated for App Store and Google
14
14
  - If unsure which agent to use, use the Task tool with a general-purpose, explore or any other built-in agent
15
15
 
16
16
  ### DevOps Agent Required
17
- ALL deployment and infrastructure operations SHOULD use the devops agent (codemagic, firebase, cloudflare, database/deploy, database/optimize).
17
+ ALL deployment and infrastructure operations SHOULD use the devops agent (deploy, firebase, cloudflare, database/deploy, database/optimize).
18
18
  Prefer using the devops agent over direct Bash/SSH for structured operations.
19
19
 
20
20
  ### Code Quality (enforced by agents)
@@ -38,7 +38,7 @@ Prefer using the devops agent over direct Bash/SSH for structured operations.
38
38
  ## Configuration Files
39
39
 
40
40
  ### ci.config.yaml
41
- Single source of truth: credentials, app identity, store settings, web domain, Codemagic CI/CD (app_id for GitHub Actions auto-trigger, workflow IDs). The Codemagic API token is managed by the codemagic MCP server in `.mcp.json`.
41
+ Single source of truth: credentials, app identity, store settings, web domain.
42
42
 
43
43
  ### fastlane/metadata/ Structure
44
44
  All store listing texts are stored in fastlane directories and managed by the appstore-meta-creator agent:
@@ -95,7 +95,7 @@ Detect from user request:
95
95
  - **design** - App design, store screenshots, web page design (Stitch MCP)
96
96
  - **metadata** - Store listing texts, IAP config, app rating
97
97
  - **database** - Firestore schema, indexes, data migration
98
- - **infra** - Codemagic CI/CD, Firebase deploy, Cloudflare web deploy
98
+ - **infra** - GitHub Actions CI/CD, Firebase deploy, Cloudflare web deploy
99
99
  - **standard** - Mixed or unclear scope
100
100
 
101
101
  ### Agent Flows
@@ -107,12 +107,12 @@ architect → product-manager(PRE) → developer → simplifier → reviewer →
107
107
 
108
108
  #### Flutter Flow
109
109
  ```
110
- architect → product-manager(PRE) → [app-designer] → developer(flutter) → simplifier → reviewer → tester(flutter) → tester(mobile-ui) → product-manager(POST) → [devops(codemagic)]
110
+ architect → product-manager(PRE) → [app-designer] → developer(flutter) → simplifier → reviewer → tester(flutter) → tester(mobile-ui) → product-manager(POST) → [devops(deploy)]
111
111
  ```
112
112
 
113
113
  #### Backend Flow
114
114
  ```
115
- architect → product-manager(PRE) → developer(backend) → simplifier → reviewer → tester(backend) → product-manager(POST) → [devops(firebase)]
115
+ architect → product-manager(PRE) → developer(backend) → simplifier → reviewer → tester(backend) → product-manager(POST) → [devops(deploy)]
116
116
  ```
117
117
 
118
118
  #### Design Flow
@@ -127,7 +127,7 @@ appstore-meta-creator → appstore-reviewer
127
127
 
128
128
  #### Database Flow
129
129
  ```
130
- architect → developer(backend) → simplifier → reviewer → product-manager(POST) → [devops(firebase)]
130
+ architect → developer(backend) → simplifier → reviewer → product-manager(POST) → [devops(deploy)]
131
131
  ```
132
132
 
133
133
  #### Infra Flow
@@ -137,7 +137,7 @@ devops (standalone)
137
137
 
138
138
  #### Full Publishing Flow (Design to App Store)
139
139
  ```
140
- architect → product-manager(PRE) → app-designer → developer(flutter) → simplifier → reviewer → tester(flutter) → tester(mobile-ui) → appstore-meta-creator → appstore-reviewer → product-manager(POST) → devops(codemagic)
140
+ architect → product-manager(PRE) → app-designer → developer(flutter) → simplifier → reviewer → tester(flutter) → tester(mobile-ui) → appstore-meta-creator → appstore-reviewer → product-manager(POST) → devops(deploy)
141
141
  ```
142
142
 
143
143
  #### Team Size Annotations
@@ -160,17 +160,17 @@ architect{T:2} → product-manager(PRE) → developer{T:2-4} → simplifier →
160
160
 
161
161
  ##### Flutter Flow (with teams)
162
162
  ```
163
- architect{T:2} → product-manager(PRE) → [app-designer{T:2-3}] → developer(flutter){T:2-4} → simplifier → reviewer{T:2-3} → tester(flutter){T:2} → tester(mobile-ui){T:2} → product-manager(POST) → [devops(codemagic)]
163
+ architect{T:2} → product-manager(PRE) → [app-designer{T:2-3}] → developer(flutter){T:2-4} → simplifier → reviewer{T:2-3} → tester(flutter){T:2} → tester(mobile-ui){T:2} → product-manager(POST) → [devops(deploy)]
164
164
  ```
165
165
 
166
166
  ##### Backend Flow (with teams)
167
167
  ```
168
- architect{T:2} → product-manager(PRE) → developer(backend){T:2-4} → simplifier → reviewer{T:2} → tester(backend){T:2} → product-manager(POST) → [devops(firebase)]
168
+ architect{T:2} → product-manager(PRE) → developer(backend){T:2-4} → simplifier → reviewer{T:2} → tester(backend){T:2} → product-manager(POST) → [devops(deploy)]
169
169
  ```
170
170
 
171
171
  ##### Full Publishing Flow (with teams)
172
172
  ```
173
- architect{T:2} → product-manager(PRE) → app-designer{T:2-3} → developer(flutter){T:2-4} → simplifier → reviewer{T:2-3} → tester(flutter){T:2} → tester(mobile-ui){T:2} → appstore-meta-creator{T:2-5} → appstore-reviewer → product-manager(POST) → devops(codemagic)
173
+ architect{T:2} → product-manager(PRE) → app-designer{T:2-3} → developer(flutter){T:2-4} → simplifier → reviewer{T:2-3} → tester(flutter){T:2} → tester(mobile-ui){T:2} → appstore-meta-creator{T:2-5} → appstore-reviewer → product-manager(POST) → devops(deploy)
174
174
  ```
175
175
 
176
176
  ### Agents Reference
@@ -183,7 +183,7 @@ architect{T:2} → product-manager(PRE) → app-designer{T:2-3} → developer(fl
183
183
  | simplifier | AFTER developer - simplifies Dart/Flutter code |
184
184
  | reviewer | After ANY code changes - Dart/Flutter quality review |
185
185
  | tester | After review passes (type: flutter/mobile-ui/web/backend/integration) |
186
- | devops | Operations (mode: codemagic/firebase/cloudflare/database+deploy/database+optimize) |
186
+ | devops | Operations (mode: deploy/firebase/cloudflare/database+deploy/database+optimize) |
187
187
  | app-designer | Stitch MCP design: app screens + store screenshots + web page design |
188
188
  | appstore-meta-creator | Generate store listing texts for all configured languages |
189
189
  | appstore-reviewer | Full App Store/Play compliance review including live app UI testing via mobile-mcp |
@@ -228,7 +228,7 @@ If the same error persists unchanged after one full fix cycle, try a fundamental
228
228
  - `APPROVED` or `COMPLETE` from product-manager
229
229
  - Deployment is optional when not configured
230
230
 
231
- **Before devops(codemagic):**
231
+ **Before devops(deploy):**
232
232
  - appstore-reviewer APPROVED (if publishing)
233
233
  - All metadata files present for configured languages
234
234
  - All screenshots present and verified (dimensions match requirements)
@@ -260,13 +260,13 @@ appstore-meta-creator generates texts. Fill fastlane/iap_config.json if needed.
260
260
  app-designer designs marketing page in Stitch MCP. Develop web pages. Deploy via Cloudflare Pages (*.pages.dev domain sufficient).
261
261
 
262
262
  ### Phase 5: Finalize and CI/CD
263
- Create .gitignore (.claude/.tasks/, Flutter ignores; do NOT ignore *.g.dart). Run scripts/generate.sh. Fill codemagic.app_id. Push to private repo. GitHub Actions auto-triggers on push.
263
+ Create .gitignore (.claude/.tasks/, Flutter ignores; do NOT ignore *.g.dart). Push to private repo. GitHub Actions auto-triggers on push.
264
264
 
265
265
  ### Phase 6: First Publish
266
266
  iOS: fully automated. Android: first build creates AAB + manual steps guide; subsequent builds fully automated.
267
267
 
268
268
  ### Phase 7: Ongoing Updates
269
- Push to main. Codemagic detects changes and uploads modified assets. Version auto-incremented.
269
+ Push to main. GitHub Actions detects changes and uploads modified assets. Version auto-incremented.
270
270
 
271
271
  ---
272
272
  ## FOR ORCHESTRATORS ONLY
@@ -392,11 +392,11 @@ LAUNCHING: [first-agent-name]
392
392
 
393
393
  **Expected RECOMMENDED FLOW outputs (copy exactly):**
394
394
 
395
- - **flutter**: `architect → product-manager(PRE) → [app-designer] → developer(flutter) → simplifier → reviewer → tester(flutter) → tester(mobile-ui) → product-manager(POST) → [devops(codemagic)]`
396
- - **backend**: `architect → product-manager(PRE) → developer(backend) → simplifier → reviewer → tester(backend) → product-manager(POST) → [devops(firebase)]`
395
+ - **flutter**: `architect → product-manager(PRE) → [app-designer] → developer(flutter) → simplifier → reviewer → tester(flutter) → tester(mobile-ui) → product-manager(POST) → [devops(deploy)]`
396
+ - **backend**: `architect → product-manager(PRE) → developer(backend) → simplifier → reviewer → tester(backend) → product-manager(POST) → [devops(deploy)]`
397
397
  - **design**: `app-designer → appstore-reviewer`
398
398
  - **metadata**: `appstore-meta-creator → appstore-reviewer`
399
- - **database**: `architect → developer(backend) → simplifier → reviewer → product-manager(POST) → [devops(firebase)]`
399
+ - **database**: `architect → developer(backend) → simplifier → reviewer → product-manager(POST) → [devops(deploy)]`
400
400
  - **infra**: `devops (standalone)`
401
401
  - **standard**: `architect → product-manager(PRE) → developer → simplifier → reviewer → tester → product-manager(POST) → [devops]`
402
402
 
@@ -56,14 +56,3 @@ web:
56
56
  jurisdiction: "State of California, United States"
57
57
  app_store_url: ""
58
58
  google_play_url: ""
59
-
60
- # === CODEMAGIC CI/CD ===
61
- # Fill after creating app in Codemagic (https://codemagic.io/apps)
62
- # Find app_id in your Codemagic dashboard URL: codemagic.io/app/{app_id}
63
- # API token is stored in .mcp.json (codemagic MCP server, set during install)
64
- codemagic:
65
- team_id: "" # Team ID from Codemagic Teams page
66
- app_id: ""
67
- workflows:
68
- - ios-release
69
- - android-release
@@ -1,8 +1,8 @@
1
1
  default_platform(:android)
2
2
 
3
3
  # Resolve paths absolutely to avoid symlink-relative breakage.
4
- # CM_BUILD_DIR is set by Codemagic CI; locally we derive from __FILE__.
5
- ROOT_DIR = ENV.fetch("CM_BUILD_DIR", File.expand_path("../..", __FILE__))
4
+ # PROJECT_ROOT is set by CI scripts (read-config.sh); locally we derive from __FILE__.
5
+ ROOT_DIR = ENV.fetch("PROJECT_ROOT", File.expand_path("../..", __FILE__))
6
6
  APP_DIR = ENV.fetch("APP_ROOT", "#{ROOT_DIR}/app")
7
7
  APP_DIR = "#{ROOT_DIR}/#{APP_DIR}" unless APP_DIR.start_with?("/")
8
8
 
@@ -1,4 +1,4 @@
1
1
  # fastlane-plugin-iap is not yet published.
2
- # IAP sync is handled gracefully in codemagic.yaml (non-blocking).
2
+ # IAP sync is handled gracefully in CI workflows (non-blocking).
3
3
  # Uncomment when the plugin is available:
4
4
  # gem "fastlane-plugin-iap", git: "https://github.com/daemux/fastlane-plugin-iap"
@@ -1,8 +1,8 @@
1
1
  default_platform(:ios)
2
2
 
3
3
  # Resolve paths absolutely to avoid symlink-relative breakage.
4
- # CM_BUILD_DIR is set by Codemagic CI; locally we derive from __FILE__.
5
- ROOT_DIR = ENV.fetch("CM_BUILD_DIR", File.expand_path("../..", __FILE__))
4
+ # PROJECT_ROOT is set by CI scripts (read-config.sh); locally we derive from __FILE__.
5
+ ROOT_DIR = ENV.fetch("PROJECT_ROOT", File.expand_path("../..", __FILE__))
6
6
  APP_DIR = ENV.fetch("APP_ROOT", "#{ROOT_DIR}/app")
7
7
  APP_DIR = "#{ROOT_DIR}/#{APP_DIR}" unless APP_DIR.start_with?("/")
8
8
 
@@ -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
@@ -1,4 +1,4 @@
1
1
  # fastlane-plugin-iap is not yet published.
2
- # IAP sync is handled gracefully in codemagic.yaml (non-blocking).
2
+ # IAP sync is handled gracefully in CI workflows (non-blocking).
3
3
  # Uncomment when the plugin is available:
4
4
  # gem "fastlane-plugin-iap", git: "https://github.com/daemux/fastlane-plugin-iap"
@@ -1,4 +1,4 @@
1
- root_dir = ENV.fetch("CM_BUILD_DIR", File.expand_path("../..", __FILE__))
1
+ root_dir = ENV.fetch("PROJECT_ROOT", File.expand_path("../..", __FILE__))
2
2
 
3
3
  devices([
4
4
  "iPhone 16 Pro Max",
@@ -1,8 +1,4 @@
1
1
  name: Android Release
2
- # POST-MIGRATION CLEANUP (after GitHub Actions workflows are verified in production):
3
- # - Remove .github/workflows/codemagic-trigger.yml
4
- # - Remove any Codemagic-specific configuration files
5
- # - Update README/docs to reference GitHub Actions instead of Codemagic
6
2
  on:
7
3
  push:
8
4
  branches: [main]
@@ -39,6 +35,9 @@ jobs:
39
35
  - name: Install yq
40
36
  run: brew install yq
41
37
 
38
+ - name: Init Summary
39
+ run: echo "| Step | Status | Details |" >> $GITHUB_STEP_SUMMARY && echo "|------|--------|---------|" >> $GITHUB_STEP_SUMMARY
40
+
42
41
  - name: Link Fastlane
43
42
  run: scripts/ci/common/link-fastlane.sh android
44
43
 
@@ -49,15 +48,18 @@ jobs:
49
48
  run: scripts/ci/android/check-readiness.sh
50
49
 
51
50
  - name: Upload Metadata & Screenshots
51
+ id: upload-metadata
52
52
  run: scripts/ci/android/upload-metadata.sh
53
53
 
54
54
  - name: Sync IAP
55
+ id: sync-iap
55
56
  run: scripts/ci/android/sync-iap.sh
56
57
 
57
58
  - name: Install Python dependencies
58
59
  run: pip3 install --break-system-packages google-api-python-client google-auth
59
60
 
60
61
  - name: Update Data Safety
62
+ id: update-data-safety
61
63
  run: scripts/ci/android/update-data-safety.sh
62
64
 
63
65
  - name: Save .ci-state
@@ -1,8 +1,4 @@
1
1
  name: iOS Release
2
- # POST-MIGRATION CLEANUP (after GitHub Actions workflows are verified in production):
3
- # - Remove .github/workflows/codemagic-trigger.yml
4
- # - Remove any Codemagic-specific configuration files
5
- # - Update README/docs to reference GitHub Actions instead of Codemagic
6
2
  on:
7
3
  push:
8
4
  branches: [main]
@@ -39,6 +35,9 @@ jobs:
39
35
  - name: Install yq
40
36
  run: brew install yq
41
37
 
38
+ - name: Init Summary
39
+ run: echo "| Step | Status | Details |" >> $GITHUB_STEP_SUMMARY && echo "|------|--------|---------|" >> $GITHUB_STEP_SUMMARY
40
+
42
41
  - name: Link Fastlane
43
42
  run: scripts/ci/common/link-fastlane.sh ios
44
43
 
@@ -46,9 +45,11 @@ jobs:
46
45
  run: scripts/ci/common/install-fastlane.sh ios
47
46
 
48
47
  - name: Upload Metadata & Screenshots
48
+ id: upload-metadata
49
49
  run: scripts/ci/ios/upload-metadata.sh
50
50
 
51
51
  - name: Sync IAP
52
+ id: sync-iap
52
53
  run: scripts/ci/ios/sync-iap.sh
53
54
 
54
55
  - name: Save .ci-state
@@ -71,6 +72,16 @@ jobs:
71
72
  with:
72
73
  fetch-depth: 0
73
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
+
74
85
  - name: Setup Ruby
75
86
  uses: ruby/setup-ruby@v1
76
87
  with:
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
  # Usage: ./scripts/check_changed.sh <path>
3
3
  # Exit 0 if changed, exit 1 if unchanged
4
- # Used by codemagic.yaml for conditional metadata/screenshot uploads
4
+ # Used by CI workflows for conditional metadata/screenshot uploads
5
5
 
6
6
  set -euo pipefail
7
7
 
@@ -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
@@ -50,16 +41,24 @@ if [ ! -f "$SA_FULL_PATH" ]; then
50
41
  exit 1
51
42
  fi
52
43
 
53
- # --- Update data safety via Fastlane ---
44
+ # --- Update data safety via Python script ---
45
+ # Exit codes: 0 = success, 2 = API limitation (manual update needed), other = error
54
46
  echo "Updating Android data safety..."
55
47
 
48
+ set +e
56
49
  PACKAGE_NAME="$PACKAGE_NAME" \
57
50
  GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH="$SA_FULL_PATH" \
58
- DATA_SAFETY_CSV_PATH="$DATA_SAFETY_CSV" \
59
- bundle exec fastlane update_data_safety
51
+ python3 "$PROJECT_ROOT/scripts/update_data_safety.py" "$DATA_SAFETY_CSV"
52
+ SAFETY_EXIT=$?
53
+ set -e
60
54
 
61
- echo "Android data safety update complete"
62
-
63
- # --- Update hash on success ---
64
- echo "$HASH" > "$STATE_FILE"
65
- echo "Updated state hash: ${HASH:0:12}..."
55
+ if [ $SAFETY_EXIT -eq 0 ]; then
56
+ echo "$HASH" > "$STATE_FILE"
57
+ ci_done "Data safety section updated"
58
+ elif [ $SAFETY_EXIT -eq 2 ]; then
59
+ # API limitation is non-fatal; hash is NOT saved so the step retries next run
60
+ ci_skip "API limitation — update manually via Google Play Console (will retry next run)"
61
+ else
62
+ echo "ERROR: Data safety update failed (exit code: $SAFETY_EXIT)" >&2
63
+ exit $SAFETY_EXIT
64
+ fi