@daemux/store-automator 0.7.0 → 0.8.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/README.md +6 -2
- package/bin/cli.mjs +43 -15
- package/package.json +1 -1
- package/plugins/store-automator/.claude-plugin/plugin.json +1 -1
- package/src/ci-config.mjs +33 -1
- package/src/install-paths.mjs +93 -0
- package/src/install.mjs +39 -78
- package/src/prompt.mjs +31 -7
- package/src/templates.mjs +71 -1
- package/templates/Matchfile.template +8 -0
- package/templates/ci.config.yaml.template +3 -0
- package/templates/codemagic.template.yaml +329 -111
- package/templates/fastlane/android/Fastfile.template +56 -8
- package/templates/fastlane/android/Pluginfile.template +4 -1
- package/templates/fastlane/ios/Fastfile.template +72 -17
- package/templates/fastlane/ios/Pluginfile.template +4 -1
- package/templates/github/workflows/android-release.yml +72 -0
- package/templates/github/workflows/ios-release.yml +62 -0
- package/templates/scripts/ci/android/build.sh +50 -0
- package/templates/scripts/ci/android/check-readiness.sh +99 -0
- package/templates/scripts/ci/android/manage-version.sh +133 -0
- package/templates/scripts/ci/android/setup-keystore.sh +80 -0
- package/templates/scripts/ci/android/sync-iap.sh +63 -0
- package/templates/scripts/ci/android/update-data-safety.sh +65 -0
- package/templates/scripts/ci/android/upload-binary.sh +62 -0
- package/templates/scripts/ci/android/upload-metadata.sh +59 -0
- package/templates/scripts/ci/common/check-changed.sh +74 -0
- package/templates/scripts/ci/common/flutter-setup.sh +22 -0
- package/templates/scripts/ci/common/install-fastlane.sh +39 -0
- package/templates/scripts/ci/common/link-fastlane.sh +56 -0
- package/templates/scripts/ci/common/read-config.sh +71 -0
- package/templates/scripts/ci/ios/build.sh +52 -0
- package/templates/scripts/ci/ios/manage-version.sh +64 -0
- package/templates/scripts/ci/ios/set-build-number.sh +95 -0
- package/templates/scripts/ci/ios/setup-signing.sh +242 -0
- package/templates/scripts/ci/ios/sync-iap.sh +91 -0
- package/templates/scripts/ci/ios/upload-binary.sh +44 -0
- package/templates/scripts/ci/ios/upload-metadata.sh +92 -0
- package/templates/scripts/update_data_safety.py +220 -0
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "App Store & Google Play automation for Flutter apps",
|
|
8
|
-
"version": "0.7.
|
|
8
|
+
"version": "0.7.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.7.
|
|
15
|
+
"version": "0.7.1",
|
|
16
16
|
"keywords": ["flutter", "app-store", "google-play", "fastlane", "codemagic"]
|
|
17
17
|
}
|
|
18
18
|
]
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ npm install @daemux/store-automator
|
|
|
27
27
|
|
|
28
28
|
The postinstall script will:
|
|
29
29
|
|
|
30
|
-
1. Prompt for MCP server tokens (Stitch, Cloudflare, Codemagic)
|
|
30
|
+
1. Prompt for bundle ID and MCP server tokens (Stitch, Cloudflare, Codemagic)
|
|
31
31
|
2. Configure `.mcp.json` with MCP servers (Playwright, mobile-mcp, Stitch, Cloudflare, Codemagic)
|
|
32
32
|
3. Install the plugin marketplace and register agents
|
|
33
33
|
4. Copy `CLAUDE.md` template to `.claude/CLAUDE.md`
|
|
@@ -122,13 +122,14 @@ For CI/CD environments or scripted setups, pass tokens as CLI flags to skip inte
|
|
|
122
122
|
|
|
123
123
|
```bash
|
|
124
124
|
npx @daemux/store-automator \
|
|
125
|
+
--bundle-id=com.company.app \
|
|
125
126
|
--codemagic-token=YOUR_CM_TOKEN \
|
|
126
127
|
--stitch-key=YOUR_STITCH_KEY \
|
|
127
128
|
--cloudflare-token=YOUR_CF_TOKEN \
|
|
128
129
|
--cloudflare-account-id=YOUR_CF_ACCOUNT_ID
|
|
129
130
|
```
|
|
130
131
|
|
|
131
|
-
Any tokens provided via flags will skip the corresponding interactive prompt. If all four tokens are provided, the entire interactive session is skipped.
|
|
132
|
+
Any tokens provided via flags will skip the corresponding interactive prompt. If all four tokens are provided, the entire interactive session is skipped. The bundle ID, if provided, is automatically written to `bundle_id` and `package_name` in `ci.config.yaml`.
|
|
132
133
|
|
|
133
134
|
## CLI Options
|
|
134
135
|
|
|
@@ -142,6 +143,9 @@ Options:
|
|
|
142
143
|
-v, --version Show version number
|
|
143
144
|
-h, --help Show help
|
|
144
145
|
|
|
146
|
+
App Configuration:
|
|
147
|
+
--bundle-id=ID Bundle ID / Package Name (e.g., com.company.app)
|
|
148
|
+
|
|
145
149
|
MCP Token Flags (skip interactive prompts):
|
|
146
150
|
--codemagic-token=TOKEN Codemagic API token
|
|
147
151
|
--stitch-key=KEY Stitch MCP API key
|
package/bin/cli.mjs
CHANGED
|
@@ -27,12 +27,15 @@ function flagValue(arg, prefix) {
|
|
|
27
27
|
return arg.startsWith(prefix) ? arg.slice(prefix.length) : undefined;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const
|
|
30
|
+
const valueFlags = {
|
|
31
31
|
'--codemagic-token=': 'codemagicToken',
|
|
32
32
|
'--codemagic-team-id=': 'codemagicTeamId',
|
|
33
33
|
'--stitch-key=': 'stitchApiKey',
|
|
34
34
|
'--cloudflare-token=': 'cloudflareToken',
|
|
35
35
|
'--cloudflare-account-id=': 'cloudflareAccountId',
|
|
36
|
+
'--bundle-id=': 'bundleId',
|
|
37
|
+
'--match-deploy-key=': 'matchDeployKey',
|
|
38
|
+
'--match-git-url=': 'matchGitUrl',
|
|
36
39
|
};
|
|
37
40
|
|
|
38
41
|
for (const arg of args) {
|
|
@@ -41,15 +44,11 @@ for (const arg of args) {
|
|
|
41
44
|
if ((v = flagValue(arg, '--branch=')) !== undefined) { cmBranch = v; continue; }
|
|
42
45
|
if ((v = flagValue(arg, '--workflow=')) !== undefined) { cmWorkflowId = v; continue; }
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
matched = true;
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
47
|
+
const valueFlagEntry = Object.entries(valueFlags).find(([prefix]) => arg.startsWith(prefix));
|
|
48
|
+
if (valueFlagEntry) {
|
|
49
|
+
cliTokens[valueFlagEntry[1]] = arg.slice(valueFlagEntry[0].length);
|
|
50
|
+
continue;
|
|
51
51
|
}
|
|
52
|
-
if (matched) continue;
|
|
53
52
|
|
|
54
53
|
switch (arg) {
|
|
55
54
|
case '-g':
|
|
@@ -69,6 +68,9 @@ for (const arg of args) {
|
|
|
69
68
|
case '--github-setup':
|
|
70
69
|
action = 'github-setup';
|
|
71
70
|
break;
|
|
71
|
+
case '--github-actions':
|
|
72
|
+
cliTokens.githubActions = true;
|
|
73
|
+
break;
|
|
72
74
|
case '--trigger':
|
|
73
75
|
cmTrigger = true;
|
|
74
76
|
break;
|
|
@@ -86,6 +88,9 @@ Options:
|
|
|
86
88
|
-v, --version Show version number
|
|
87
89
|
-h, --help Show help
|
|
88
90
|
|
|
91
|
+
App Configuration:
|
|
92
|
+
--bundle-id=ID Bundle ID / Package Name (e.g., com.company.app)
|
|
93
|
+
|
|
89
94
|
MCP Token Flags (skip interactive prompts):
|
|
90
95
|
--codemagic-token=TOKEN Codemagic API token
|
|
91
96
|
--codemagic-team-id=ID Codemagic Team ID (from Teams page)
|
|
@@ -101,34 +106,57 @@ Codemagic:
|
|
|
101
106
|
--trigger Trigger build after setup
|
|
102
107
|
--wait Wait for build completion (implies --trigger)
|
|
103
108
|
|
|
109
|
+
GitHub Actions CI mode:
|
|
110
|
+
--github-actions Use GitHub Actions instead of Codemagic (skip MCP setup)
|
|
111
|
+
--match-deploy-key=PATH Path to Match deploy key file (required with --github-actions)
|
|
112
|
+
--match-git-url=URL Git URL for Match certificates repo (required with --github-actions)
|
|
113
|
+
|
|
104
114
|
GitHub Actions (auto-configured during install if gh CLI available):
|
|
105
115
|
--github-setup Set CM_API_TOKEN secret for GitHub Actions
|
|
106
116
|
--token=TOKEN API token (or set CM_API_TOKEN env var)
|
|
107
117
|
|
|
108
118
|
Examples:
|
|
109
|
-
npx @daemux/store-automator Install for project
|
|
119
|
+
npx @daemux/store-automator Install for project (Codemagic)
|
|
110
120
|
npx @daemux/store-automator -g Install globally
|
|
111
121
|
npx @daemux/store-automator -u Uninstall from project
|
|
112
122
|
npx @daemux/store-automator -g -u Uninstall globally
|
|
113
123
|
npx @daemux/store-automator --codemagic-setup Register with Codemagic
|
|
114
124
|
npx @daemux/store-automator --codemagic-setup --trigger --wait Trigger and wait
|
|
115
|
-
npx @daemux/store-automator --github-setup Configure GitHub Actions
|
|
125
|
+
npx @daemux/store-automator --github-setup Configure GitHub Actions secret
|
|
126
|
+
|
|
127
|
+
GitHub Actions install:
|
|
128
|
+
npx @daemux/store-automator --github-actions --bundle-id=ID --match-deploy-key=PATH --match-git-url=URL
|
|
116
129
|
|
|
117
|
-
Non-interactive install (
|
|
118
|
-
npx @daemux/store-automator --
|
|
130
|
+
Non-interactive install (Codemagic):
|
|
131
|
+
npx @daemux/store-automator --bundle-id=ID --codemagic-token=TOKEN --stitch-key=KEY
|
|
119
132
|
npx @daemux/store-automator --cloudflare-token=TOKEN --cloudflare-account-id=ID`);
|
|
120
133
|
process.exit(0);
|
|
121
|
-
break; // eslint: no-fallthrough
|
|
122
134
|
case '-v':
|
|
123
135
|
case '--version':
|
|
124
136
|
console.log(pkg.version);
|
|
125
137
|
process.exit(0);
|
|
126
|
-
break; // eslint: no-fallthrough
|
|
127
138
|
}
|
|
128
139
|
}
|
|
129
140
|
|
|
130
141
|
if (cmWait) cmTrigger = true;
|
|
131
142
|
|
|
143
|
+
if (cliTokens.githubActions) {
|
|
144
|
+
const missing = [];
|
|
145
|
+
if (!cliTokens.matchDeployKey) missing.push('--match-deploy-key');
|
|
146
|
+
if (!cliTokens.matchGitUrl) missing.push('--match-git-url');
|
|
147
|
+
if (!cliTokens.bundleId) missing.push('--bundle-id');
|
|
148
|
+
if (missing.length > 0) {
|
|
149
|
+
console.error(`Error: --github-actions requires: ${missing.join(', ')}`);
|
|
150
|
+
console.error('');
|
|
151
|
+
console.error('Example:');
|
|
152
|
+
console.error(' npx @daemux/store-automator --github-actions \\');
|
|
153
|
+
console.error(' --bundle-id=com.company.app \\');
|
|
154
|
+
console.error(' --match-deploy-key=creds/match_deploy_key \\');
|
|
155
|
+
console.error(' --match-git-url=git@github.com:org/certs.git');
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
132
160
|
notifier.notify();
|
|
133
161
|
|
|
134
162
|
try {
|
package/package.json
CHANGED
package/src/ci-config.mjs
CHANGED
|
@@ -5,6 +5,10 @@ const CI_CONFIG_FILE = 'ci.config.yaml';
|
|
|
5
5
|
const FIELD_PATTERNS = {
|
|
6
6
|
app_id: /^(\s*app_id:\s*)"[^"]*"/m,
|
|
7
7
|
team_id: /^(\s*team_id:\s*)"[^"]*"/m,
|
|
8
|
+
bundle_id: /^(\s*bundle_id:\s*)"[^"]*"/m,
|
|
9
|
+
package_name: /^(\s*package_name:\s*)"[^"]*"/m,
|
|
10
|
+
deploy_key_path: /^(\s*deploy_key_path:\s*)"[^"]*"/m,
|
|
11
|
+
git_url: /^(\s*git_url:\s*)"[^"]*"/m,
|
|
8
12
|
};
|
|
9
13
|
|
|
10
14
|
function writeCiField(projectDir, field, value) {
|
|
@@ -16,7 +20,8 @@ function writeCiField(projectDir, field, value) {
|
|
|
16
20
|
const content = readFileSync(configPath, 'utf8');
|
|
17
21
|
if (!pattern.test(content)) return false;
|
|
18
22
|
|
|
19
|
-
const
|
|
23
|
+
const safeValue = value.replace(/\$/g, '$$$$');
|
|
24
|
+
const updated = content.replace(pattern, `$1"${safeValue}"`);
|
|
20
25
|
if (updated === content) return false;
|
|
21
26
|
|
|
22
27
|
writeFileSync(configPath, updated, 'utf8');
|
|
@@ -33,3 +38,30 @@ export function writeCiAppId(projectDir, appId) {
|
|
|
33
38
|
export function writeCiTeamId(projectDir, teamId) {
|
|
34
39
|
return writeCiField(projectDir, 'team_id', teamId);
|
|
35
40
|
}
|
|
41
|
+
|
|
42
|
+
export function writeCiBundleId(projectDir, bundleId) {
|
|
43
|
+
return writeCiField(projectDir, 'bundle_id', bundleId);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function writeCiPackageName(projectDir, packageName) {
|
|
47
|
+
return writeCiField(projectDir, 'package_name', packageName);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function writeMatchConfig(projectDir, { deployKeyPath, gitUrl }) {
|
|
51
|
+
const wrote1 = writeCiField(projectDir, 'deploy_key_path', deployKeyPath);
|
|
52
|
+
const wrote2 = writeCiField(projectDir, 'git_url', gitUrl);
|
|
53
|
+
return wrote1 || wrote2;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function readFlutterRoot(projectDir) {
|
|
57
|
+
const configPath = join(projectDir, CI_CONFIG_FILE);
|
|
58
|
+
if (!existsSync(configPath)) return '.';
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const content = readFileSync(configPath, 'utf8');
|
|
62
|
+
const match = content.match(/^flutter_root:\s*"([^"]*)"/m);
|
|
63
|
+
return match ? match[1] : '.';
|
|
64
|
+
} catch {
|
|
65
|
+
return '.';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { execSync, execFileSync } from 'node:child_process';
|
|
2
|
+
import { installGitHubActionsTemplates, installMatchfile } from './templates.mjs';
|
|
3
|
+
import { findAppByRepo, addApp, normalizeRepoUrl } from './codemagic-api.mjs';
|
|
4
|
+
import {
|
|
5
|
+
writeCiAppId, writeCiTeamId, writeMatchConfig, readFlutterRoot,
|
|
6
|
+
} from './ci-config.mjs';
|
|
7
|
+
import { updateMcpAppId, updateMcpTeamId } from './mcp-setup.mjs';
|
|
8
|
+
|
|
9
|
+
function setupGitHubActionsSecret(codemagicToken) {
|
|
10
|
+
if (!codemagicToken) return false;
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
execFileSync('which', ['gh'], { encoding: 'utf8', stdio: 'pipe' });
|
|
14
|
+
const authStatus = execFileSync('gh', ['auth', 'status'], { encoding: 'utf8', stdio: 'pipe' });
|
|
15
|
+
if (authStatus.includes('not logged')) return false;
|
|
16
|
+
|
|
17
|
+
execFileSync('gh', ['secret', 'set', 'CM_API_TOKEN', '--body', codemagicToken], {
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
stdio: 'pipe',
|
|
20
|
+
});
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function setupCodemagicApp(projectDir, codemagicToken, codemagicTeamId) {
|
|
28
|
+
if (!codemagicToken) return;
|
|
29
|
+
|
|
30
|
+
let repoUrl;
|
|
31
|
+
try {
|
|
32
|
+
const raw = execSync('git remote get-url origin', {
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
35
|
+
}).trim();
|
|
36
|
+
if (!raw) return;
|
|
37
|
+
repoUrl = normalizeRepoUrl(raw);
|
|
38
|
+
} catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
let app = await findAppByRepo(codemagicToken, repoUrl);
|
|
44
|
+
if (!app) {
|
|
45
|
+
app = await addApp(codemagicToken, repoUrl, codemagicTeamId);
|
|
46
|
+
console.log(`Codemagic app created: ${app.appName || app._id}`);
|
|
47
|
+
} else {
|
|
48
|
+
console.log(`Codemagic app found: ${app.appName || app._id}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const written = writeCiAppId(projectDir, app._id);
|
|
52
|
+
if (written) {
|
|
53
|
+
console.log(`Codemagic app_id written to ci.config.yaml`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
updateMcpAppId(projectDir, app._id);
|
|
57
|
+
|
|
58
|
+
if (codemagicTeamId) {
|
|
59
|
+
writeCiTeamId(projectDir, codemagicTeamId);
|
|
60
|
+
updateMcpTeamId(projectDir, codemagicTeamId);
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.log(`Codemagic auto-setup skipped: ${err.message || err}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function installGitHubActionsPath(projectDir, packageDir, cliTokens) {
|
|
68
|
+
console.log('Configuring GitHub Actions mode...');
|
|
69
|
+
installGitHubActionsTemplates(projectDir, packageDir);
|
|
70
|
+
|
|
71
|
+
const flutterRoot = readFlutterRoot(projectDir);
|
|
72
|
+
installMatchfile(projectDir, packageDir, flutterRoot, {
|
|
73
|
+
matchGitUrl: cliTokens.matchGitUrl,
|
|
74
|
+
bundleId: cliTokens.bundleId,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const wrote = writeMatchConfig(projectDir, {
|
|
78
|
+
deployKeyPath: cliTokens.matchDeployKey,
|
|
79
|
+
gitUrl: cliTokens.matchGitUrl,
|
|
80
|
+
});
|
|
81
|
+
if (wrote) console.log('Match credentials written to ci.config.yaml');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function installCodemagicPath(projectDir, tokens) {
|
|
85
|
+
await setupCodemagicApp(projectDir, tokens.codemagicToken, tokens.codemagicTeamId);
|
|
86
|
+
|
|
87
|
+
const ghConfigured = setupGitHubActionsSecret(tokens.codemagicToken);
|
|
88
|
+
if (ghConfigured) {
|
|
89
|
+
console.log('GitHub Actions: CM_API_TOKEN secret configured.');
|
|
90
|
+
} else if (tokens.codemagicToken) {
|
|
91
|
+
console.log('GitHub Actions: secret not set (gh CLI unavailable or not authenticated).');
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/install.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, rmSync, cpSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
import { execSync
|
|
4
|
+
import { execSync } from 'node:child_process';
|
|
5
5
|
import {
|
|
6
6
|
MARKETPLACE_DIR, KNOWN_MP_PATH, CACHE_DIR,
|
|
7
7
|
MARKETPLACE_NAME, PLUGIN_REF,
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
} from './utils.mjs';
|
|
10
10
|
import { injectEnvVars, injectStatusLine } from './settings.mjs';
|
|
11
11
|
import { promptForTokens } from './prompt.mjs';
|
|
12
|
-
import { getMcpServers, writeMcpJson
|
|
12
|
+
import { getMcpServers, writeMcpJson } from './mcp-setup.mjs';
|
|
13
13
|
import { installClaudeMd, installCiTemplates, installFirebaseTemplates } from './templates.mjs';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { writeCiBundleId, writeCiPackageName } from './ci-config.mjs';
|
|
15
|
+
import { installGitHubActionsPath, installCodemagicPath } from './install-paths.mjs';
|
|
16
16
|
|
|
17
17
|
function checkClaudeCli() {
|
|
18
18
|
const result = exec('command -v claude') || exec('which claude');
|
|
@@ -94,61 +94,19 @@ function printSummary(scope, oldVersion, newVersion) {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} catch {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async function setupCodemagicApp(projectDir, codemagicToken, codemagicTeamId) {
|
|
116
|
-
if (!codemagicToken) return;
|
|
117
|
-
|
|
118
|
-
let repoUrl;
|
|
119
|
-
try {
|
|
120
|
-
const raw = execSync('git remote get-url origin', {
|
|
121
|
-
encoding: 'utf8',
|
|
122
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
123
|
-
}).trim();
|
|
124
|
-
if (!raw) return;
|
|
125
|
-
repoUrl = normalizeRepoUrl(raw);
|
|
126
|
-
} catch {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
let app = await findAppByRepo(codemagicToken, repoUrl);
|
|
132
|
-
if (!app) {
|
|
133
|
-
app = await addApp(codemagicToken, repoUrl, codemagicTeamId);
|
|
134
|
-
console.log(`Codemagic app created: ${app.appName || app._id}`);
|
|
135
|
-
} else {
|
|
136
|
-
console.log(`Codemagic app found: ${app.appName || app._id}`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const written = writeCiAppId(projectDir, app._id);
|
|
140
|
-
if (written) {
|
|
141
|
-
console.log(`Codemagic app_id written to ci.config.yaml`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
updateMcpAppId(projectDir, app._id);
|
|
145
|
-
|
|
146
|
-
if (codemagicTeamId) {
|
|
147
|
-
writeCiTeamId(projectDir, codemagicTeamId);
|
|
148
|
-
updateMcpTeamId(projectDir, codemagicTeamId);
|
|
149
|
-
}
|
|
150
|
-
} catch (err) {
|
|
151
|
-
console.log(`Codemagic auto-setup skipped: ${err.message || err}`);
|
|
97
|
+
function printNextSteps(isGitHubActions) {
|
|
98
|
+
console.log('');
|
|
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"');
|
|
152
110
|
}
|
|
153
111
|
}
|
|
154
112
|
|
|
@@ -157,11 +115,18 @@ export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
|
|
|
157
115
|
|
|
158
116
|
console.log('Installing/updating Daemux Store Automator...');
|
|
159
117
|
|
|
160
|
-
const
|
|
118
|
+
const isGitHubActions = Boolean(cliTokens.githubActions);
|
|
119
|
+
|
|
120
|
+
const tokens = isGitHubActions
|
|
121
|
+
? { bundleId: cliTokens.bundleId ?? '' }
|
|
122
|
+
: await promptForTokens(cliTokens);
|
|
161
123
|
|
|
162
124
|
const projectDir = process.cwd();
|
|
163
|
-
|
|
164
|
-
|
|
125
|
+
|
|
126
|
+
if (!isGitHubActions) {
|
|
127
|
+
const servers = getMcpServers(tokens);
|
|
128
|
+
writeMcpJson(projectDir, servers);
|
|
129
|
+
}
|
|
165
130
|
|
|
166
131
|
const oldVersion = readMarketplaceVersion();
|
|
167
132
|
const packageDir = getPackageDir();
|
|
@@ -183,7 +148,17 @@ export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
|
|
|
183
148
|
installCiTemplates(projectDir, packageDir);
|
|
184
149
|
installFirebaseTemplates(projectDir, packageDir);
|
|
185
150
|
|
|
186
|
-
|
|
151
|
+
if (tokens.bundleId) {
|
|
152
|
+
const written = writeCiBundleId(projectDir, tokens.bundleId);
|
|
153
|
+
if (written) console.log(`Bundle ID set in ci.config.yaml: ${tokens.bundleId}`);
|
|
154
|
+
writeCiPackageName(projectDir, tokens.bundleId);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (isGitHubActions) {
|
|
158
|
+
installGitHubActionsPath(projectDir, packageDir, cliTokens);
|
|
159
|
+
} else {
|
|
160
|
+
await installCodemagicPath(projectDir, tokens);
|
|
161
|
+
}
|
|
187
162
|
|
|
188
163
|
const scopeLabel = scope === 'user' ? 'global' : 'project';
|
|
189
164
|
console.log(`Configuring ${scopeLabel} settings...`);
|
|
@@ -191,20 +166,6 @@ export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
|
|
|
191
166
|
injectEnvVars(settingsPath);
|
|
192
167
|
injectStatusLine(settingsPath);
|
|
193
168
|
|
|
194
|
-
const ghConfigured = setupGitHubActions(tokens.codemagicToken);
|
|
195
|
-
if (ghConfigured) {
|
|
196
|
-
console.log('GitHub Actions: CM_API_TOKEN secret configured.');
|
|
197
|
-
} else if (tokens.codemagicToken) {
|
|
198
|
-
console.log('GitHub Actions: secret not set (gh CLI unavailable or not authenticated).');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
169
|
printSummary(scope, oldVersion, newVersion);
|
|
202
|
-
|
|
203
|
-
console.log('Next steps:');
|
|
204
|
-
console.log(' 1. Fill ci.config.yaml (codemagic.app_id is auto-configured if token was provided)');
|
|
205
|
-
console.log(' 2. Add creds/AuthKey.p8 and creds/play-service-account.json');
|
|
206
|
-
console.log(' 3. Start Claude Code');
|
|
207
|
-
if (!ghConfigured) {
|
|
208
|
-
console.log(' Note: For auto-trigger, install gh CLI and run "gh auth login"');
|
|
209
|
-
}
|
|
170
|
+
printNextSteps(isGitHubActions);
|
|
210
171
|
}
|
package/src/prompt.mjs
CHANGED
|
@@ -22,8 +22,16 @@ function allTokensProvided(cliTokens) {
|
|
|
22
22
|
);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function allPromptsProvided(cliTokens) {
|
|
26
|
+
return (
|
|
27
|
+
cliTokens.bundleId !== undefined &&
|
|
28
|
+
allTokensProvided(cliTokens)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
export async function promptForTokens(cliTokens = {}) {
|
|
26
33
|
const result = {
|
|
34
|
+
bundleId: cliTokens.bundleId ?? '',
|
|
27
35
|
stitchApiKey: cliTokens.stitchApiKey ?? '',
|
|
28
36
|
cloudflareToken: cliTokens.cloudflareToken ?? '',
|
|
29
37
|
cloudflareAccountId: cliTokens.cloudflareAccountId ?? '',
|
|
@@ -31,14 +39,14 @@ export async function promptForTokens(cliTokens = {}) {
|
|
|
31
39
|
codemagicTeamId: cliTokens.codemagicTeamId ?? '',
|
|
32
40
|
};
|
|
33
41
|
|
|
34
|
-
if (
|
|
35
|
-
console.log('All
|
|
42
|
+
if (allPromptsProvided(cliTokens)) {
|
|
43
|
+
console.log('All configuration provided via CLI flags, skipping prompts.');
|
|
36
44
|
return result;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
if (!isInteractive()) {
|
|
40
|
-
console.log('Non-interactive terminal detected, skipping
|
|
41
|
-
console.log('Run "npx store-automator" manually to configure
|
|
48
|
+
console.log('Non-interactive terminal detected, skipping prompts.');
|
|
49
|
+
console.log('Run "npx store-automator" manually to configure.');
|
|
42
50
|
return result;
|
|
43
51
|
}
|
|
44
52
|
|
|
@@ -47,12 +55,28 @@ export async function promptForTokens(cliTokens = {}) {
|
|
|
47
55
|
output: process.stdout,
|
|
48
56
|
});
|
|
49
57
|
|
|
50
|
-
console.log('');
|
|
51
|
-
console.log('MCP Server Configuration');
|
|
52
|
-
console.log('Press Enter to skip any token you do not have yet.');
|
|
53
58
|
console.log('');
|
|
54
59
|
|
|
55
60
|
try {
|
|
61
|
+
if (cliTokens.bundleId === undefined) {
|
|
62
|
+
console.log('App Configuration');
|
|
63
|
+
console.log('');
|
|
64
|
+
result.bundleId = await ask(
|
|
65
|
+
rl,
|
|
66
|
+
'Bundle ID / Package Name (e.g., com.company.app): '
|
|
67
|
+
);
|
|
68
|
+
console.log('');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (allTokensProvided(cliTokens)) {
|
|
72
|
+
console.log('All MCP tokens provided via CLI flags.');
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log('MCP Server Configuration');
|
|
77
|
+
console.log('Press Enter to skip any token you do not have yet.');
|
|
78
|
+
console.log('');
|
|
79
|
+
|
|
56
80
|
if (cliTokens.stitchApiKey === undefined) {
|
|
57
81
|
result.stitchApiKey = await ask(
|
|
58
82
|
rl,
|
package/src/templates.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, cpSync, copyFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, cpSync, copyFileSync, chmodSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { ensureDir } from './utils.mjs';
|
|
4
4
|
|
|
@@ -22,6 +22,17 @@ const FIREBASE_COPIES = [
|
|
|
22
22
|
['firebase/.firebaserc.template', 'backend/.firebaserc'],
|
|
23
23
|
];
|
|
24
24
|
|
|
25
|
+
const GH_ACTIONS_WORKFLOWS = [
|
|
26
|
+
['github/workflows/ios-release.yml', '.github/workflows/ios-release.yml'],
|
|
27
|
+
['github/workflows/android-release.yml', '.github/workflows/android-release.yml'],
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const GH_ACTIONS_SCRIPT_DIRS = [
|
|
31
|
+
'scripts/ci/common',
|
|
32
|
+
'scripts/ci/android',
|
|
33
|
+
'scripts/ci/ios',
|
|
34
|
+
];
|
|
35
|
+
|
|
25
36
|
function copyIfMissing(srcPath, destPath, label, isDirectory) {
|
|
26
37
|
if (!existsSync(srcPath)) return;
|
|
27
38
|
if (existsSync(destPath)) {
|
|
@@ -81,3 +92,62 @@ export function installFirebaseTemplates(projectDir, packageDir) {
|
|
|
81
92
|
copyIfMissing(join(templateDir, src), join(projectDir, dest), dest, false);
|
|
82
93
|
}
|
|
83
94
|
}
|
|
95
|
+
|
|
96
|
+
function copyOrUpdate(srcPath, destPath, label) {
|
|
97
|
+
if (!existsSync(srcPath)) return;
|
|
98
|
+
ensureDir(join(destPath, '..'));
|
|
99
|
+
if (existsSync(destPath)) {
|
|
100
|
+
console.log(` ${label} exists, overwriting.`);
|
|
101
|
+
}
|
|
102
|
+
copyFileSync(srcPath, destPath);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function chmodShFiles(dirPath) {
|
|
106
|
+
if (!existsSync(dirPath)) return;
|
|
107
|
+
for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
|
|
108
|
+
const fullPath = join(dirPath, entry.name);
|
|
109
|
+
if (entry.isDirectory()) {
|
|
110
|
+
chmodShFiles(fullPath);
|
|
111
|
+
} else if (entry.name.endsWith('.sh')) {
|
|
112
|
+
chmodSync(fullPath, 0o755);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function installGitHubActionsTemplates(projectDir, packageDir) {
|
|
118
|
+
console.log('Installing GitHub Actions templates...');
|
|
119
|
+
const templateDir = join(packageDir, 'templates');
|
|
120
|
+
|
|
121
|
+
for (const [src, dest] of GH_ACTIONS_WORKFLOWS) {
|
|
122
|
+
copyOrUpdate(join(templateDir, src), join(projectDir, dest), dest);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const scriptDir of GH_ACTIONS_SCRIPT_DIRS) {
|
|
126
|
+
const scriptsSrc = join(templateDir, scriptDir);
|
|
127
|
+
const scriptsDest = join(projectDir, scriptDir);
|
|
128
|
+
if (existsSync(scriptsSrc)) {
|
|
129
|
+
ensureDir(scriptsDest);
|
|
130
|
+
cpSync(scriptsSrc, scriptsDest, { recursive: true });
|
|
131
|
+
chmodShFiles(scriptsDest);
|
|
132
|
+
console.log(` ${scriptDir}/ copied.`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function installMatchfile(projectDir, packageDir, flutterRoot, { matchGitUrl, bundleId }) {
|
|
138
|
+
console.log('Installing Matchfile...');
|
|
139
|
+
const src = join(packageDir, 'templates', 'Matchfile.template');
|
|
140
|
+
if (!existsSync(src)) return;
|
|
141
|
+
|
|
142
|
+
const destDir = join(projectDir, flutterRoot, 'ios', 'fastlane');
|
|
143
|
+
ensureDir(destDir);
|
|
144
|
+
const dest = join(destDir, 'Matchfile');
|
|
145
|
+
|
|
146
|
+
copyOrUpdate(src, dest, 'Matchfile');
|
|
147
|
+
|
|
148
|
+
const content = readFileSync(dest, 'utf8');
|
|
149
|
+
const updated = content
|
|
150
|
+
.replace('{{MATCH_GIT_URL}}', matchGitUrl)
|
|
151
|
+
.replace('{{BUNDLE_ID}}', bundleId);
|
|
152
|
+
writeFileSync(dest, updated, 'utf8');
|
|
153
|
+
}
|