aiblueprint-cli 1.4.88 → 1.4.90
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/agents-config/skills/agents-manager/SKILL.md +14 -3
- package/agents-config/skills/agents-manager/references/writing-agent-prompts.md +1 -1
- package/agents-config/skills/apex/SKILL.md +116 -55
- package/agents-config/skills/apex/scripts/setup-templates.sh +154 -0
- package/agents-config/skills/apex/scripts/update-progress.sh +80 -0
- package/agents-config/skills/apex/steps/step-00-init.md +278 -0
- package/agents-config/skills/apex/steps/step-00b-branch.md +126 -0
- package/agents-config/skills/apex/steps/step-00b-economy.md +247 -0
- package/agents-config/skills/apex/steps/step-00b-interactive.md +170 -0
- package/agents-config/skills/apex/steps/step-00b-save.md +125 -0
- package/agents-config/skills/apex/steps/step-01-analyze.md +388 -0
- package/agents-config/skills/apex/steps/step-02-plan.md +593 -0
- package/agents-config/skills/apex/steps/step-02b-tasks.md +301 -0
- package/agents-config/skills/apex/steps/step-03-execute-teams.md +296 -0
- package/agents-config/skills/apex/steps/step-03-execute.md +240 -0
- package/agents-config/skills/apex/steps/step-04-validate.md +272 -0
- package/agents-config/skills/apex/steps/step-05-examine.md +400 -0
- package/agents-config/skills/apex/steps/step-06-resolve.md +239 -0
- package/agents-config/skills/apex/steps/step-07-tests.md +250 -0
- package/agents-config/skills/apex/steps/step-08-run-tests.md +314 -0
- package/agents-config/skills/apex/steps/step-09-finish.md +223 -0
- package/agents-config/skills/apex/steps/step-10-verify.md +296 -0
- package/agents-config/skills/apex/templates/00-context.md +55 -0
- package/agents-config/skills/apex/templates/01-analyze.md +10 -0
- package/agents-config/skills/apex/templates/02-plan.md +10 -0
- package/agents-config/skills/apex/templates/03-execute.md +10 -0
- package/agents-config/skills/apex/templates/04-validate.md +10 -0
- package/agents-config/skills/apex/templates/05-examine.md +10 -0
- package/agents-config/skills/apex/templates/06-resolve.md +10 -0
- package/agents-config/skills/apex/templates/07-tests.md +10 -0
- package/agents-config/skills/apex/templates/08-run-tests.md +10 -0
- package/agents-config/skills/apex/templates/09-finish.md +10 -0
- package/agents-config/skills/apex/templates/10-verify.md +9 -0
- package/agents-config/skills/apex/templates/README.md +195 -0
- package/agents-config/skills/apex/templates/step-complete.md +7 -0
- package/agents-config/skills/appstore-connect/SKILL.md +3 -9
- package/agents-config/skills/environments-manager/SKILL.md +86 -94
- package/agents-config/skills/environments-manager/examples/{skills/dev/SKILL.md → claude/commands/dev.md} +1 -2
- package/agents-config/skills/environments-manager/examples/{skills/lint/SKILL.md → claude/commands/lint.md} +1 -2
- package/agents-config/skills/environments-manager/examples/{skills/test/SKILL.md → claude/commands/test.md} +1 -2
- package/agents-config/skills/environments-manager/examples/{skills/typecheck/SKILL.md → claude/commands/typecheck.md} +1 -2
- package/agents-config/skills/environments-manager/examples/scripts/worktree-down.sh +5 -12
- package/agents-config/skills/environments-manager/examples/scripts/worktree-up.sh +11 -21
- package/agents-config/skills/environments-manager/references/claude.md +9 -13
- package/agents-config/skills/environments-manager/references/convex.md +120 -0
- package/agents-config/skills/environments-manager/references/cursor.md +2 -2
- package/agents-config/skills/environments-manager/references/postgresql.md +89 -0
- package/agents-config/skills/oneshot/SKILL.md +21 -37
- package/agents-config/skills/rules-manager/SKILL.md +15 -2
- package/agents-config/skills/skill-manager/SKILL.md +30 -2
- package/agents-config/skills/ultrathink/SKILL.md +1 -1
- package/agents-config/skills/use-fable/SKILL.md +64 -0
- package/package.json +1 -1
- package/agents-config/skills/appstore-connect/references/setup.md +0 -156
- package/agents-config/skills/appstore-connect/references/testflight.md +0 -212
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
# App Store Connect Setup (asc auth)
|
|
2
|
-
|
|
3
|
-
Read this when `asc` is **not yet authenticated** — `asc auth status --validate` reports no working credential, the user asks to "log in to App Store Connect", or credentials are unknown before TestFlight/App Store release work.
|
|
4
|
-
|
|
5
|
-
<objective>
|
|
6
|
-
`asc auth login` needs three things: a **key ID**, an **issuer ID**, and a **.p8 private key file**. Users rarely remember where these are. This is a battle-tested workflow for finding all three without asking the user to dig through App Store Connect manually.
|
|
7
|
-
|
|
8
|
-
Key insight from a real session: the `.p8` files and key IDs live on disk, but the **issuer ID is almost never stored locally** — it only exists in the App Store Connect web UI. The trick is to read it from the user's already-signed-in browser session via CDP, since Apple login requires 2FA and cannot be automated.
|
|
9
|
-
</objective>
|
|
10
|
-
|
|
11
|
-
<credentials_anatomy>
|
|
12
|
-
| Credential | What it looks like | Where it lives |
|
|
13
|
-
| --- | --- | --- |
|
|
14
|
-
| Key ID | 10 chars, e.g. `T397KWC8K7` | In the `.p8` filename: `AuthKey_<KEY_ID>.p8` |
|
|
15
|
-
| Issuer ID | UUID, e.g. `35b197bd-...` | App Store Connect → Users and Access → Integrations → App Store Connect API (top of Keys page). Team-level, same for all keys. |
|
|
16
|
-
| Private key | `AuthKey_*.p8` file, ~257 bytes | User's disk — Downloads is the most common spot (Apple only lets you download it once) |
|
|
17
|
-
|
|
18
|
-
Beware look-alikes that are NOT API auth keys:
|
|
19
|
-
- `SubscriptionKey_*.p8` — in-app purchase key, won't work for `asc auth login`.
|
|
20
|
-
- `.p8` files for APNs (push notifications).
|
|
21
|
-
</credentials_anatomy>
|
|
22
|
-
|
|
23
|
-
<step n="1" title="Check if already authenticated">
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
asc auth status --validate
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
If a credential validates as "works", stop — nothing to do.
|
|
30
|
-
</step>
|
|
31
|
-
|
|
32
|
-
<step n="2" title="Hunt for .p8 files on disk">
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# Standard locations first
|
|
36
|
-
ls ~/.asc ~/private_keys ~/.appstoreconnect/private_keys 2>/dev/null
|
|
37
|
-
|
|
38
|
-
# Then the places people actually put them (Downloads wins in practice)
|
|
39
|
-
find ~/Downloads ~/Documents ~/Desktop ~/Developer -maxdepth 5 -name "*.p8" 2>/dev/null
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Real finding: keys were in `~/Downloads/AuthKey_X.p8` and `~/Downloads/Dev/AuthKey_Y.p8`, downloaded months earlier. The key ID is the filename suffix.
|
|
43
|
-
</step>
|
|
44
|
-
|
|
45
|
-
<step n="3" title="Try to find the issuer ID locally (usually fails)">
|
|
46
|
-
|
|
47
|
-
Worth 30 seconds, but expect nothing:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
grep -riE "issuer" ~/Developer --include="*.json" --include="*.env*" --include="*.rb" --include="*.yml" -l 2>/dev/null | grep -v node_modules
|
|
51
|
-
grep -iE "issuer" ~/.zsh_history 2>/dev/null
|
|
52
|
-
grep -ri "issuer" ~/.fastlane ~/.expo 2>/dev/null
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Real finding: zero hits across the entire `~/Developer` tree, shell history, fastlane and expo config. Docs in repos only contain `ISSUER_ID` placeholders. **Do not burn time here — go to step 4.**
|
|
56
|
-
</step>
|
|
57
|
-
|
|
58
|
-
<step n="4" title="Read the issuer ID from the user's signed-in browser (the key move)">
|
|
59
|
-
|
|
60
|
-
Apple login = password + 2FA, so never try to log in yourself. Instead, attach to the browser where the user is **already signed in** and read the page.
|
|
61
|
-
|
|
62
|
-
**4a. Confirm with the user** which browser is signed in to App Store Connect (Chrome, Helium, etc.).
|
|
63
|
-
|
|
64
|
-
**4b. Check if the browser exposes CDP:**
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
curl -s http://127.0.0.1:9222/json/version # Chrome default
|
|
68
|
-
curl -s http://127.0.0.1:9334/json/version # custom port
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
**4c. If not (the usual case), quit and relaunch it with a debug port.** Session cookies survive a graceful quit, and `--restore-last-session` brings the tabs back:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
osascript -e 'quit app "Helium"' # or "Google Chrome"
|
|
75
|
-
sleep 3
|
|
76
|
-
nohup "/Applications/Helium.app/Contents/MacOS/Helium" --remote-debugging-port=9334 --restore-last-session >/dev/null 2>&1 &
|
|
77
|
-
sleep 5
|
|
78
|
-
curl -s http://127.0.0.1:9334/json/version | head -3 # must answer before continuing
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
**4d. Navigate to the API keys page and extract the issuer ID:**
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
dev-browser --browser helium --connect http://127.0.0.1:9334 --timeout 60 <<'EOF'
|
|
85
|
-
const page = await browser.getPage("asc");
|
|
86
|
-
await page.goto("https://appstoreconnect.apple.com/access/integrations/api", { waitUntil: "domcontentloaded" });
|
|
87
|
-
await page.waitForTimeout(8000);
|
|
88
|
-
const text = await page.evaluate(() => document.body.innerText);
|
|
89
|
-
const m = text.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
|
|
90
|
-
console.log(JSON.stringify({ issuerId: m ? m[1] : null, loggedOut: page.url().includes("/login") }));
|
|
91
|
-
EOF
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
If `loggedOut` is true, ask the user to sign in in that browser window, then re-run.
|
|
95
|
-
|
|
96
|
-
**4e. Cross-check which keys are actually ACTIVE.** The same page lists active keys with their key IDs. A `.p8` found on disk may belong to a revoked key — match the filename key ID against the active list before using it:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
dev-browser --browser helium --connect http://127.0.0.1:9334 --timeout 30 <<'EOF'
|
|
100
|
-
const page = await browser.getPage("asc");
|
|
101
|
-
const text = await page.evaluate(() => document.body.innerText);
|
|
102
|
-
console.log(text); // active keys table: NAME / KEY ID / LAST USED / ACCESS
|
|
103
|
-
EOF
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Real finding: the first `.p8` we picked (`AuthKey_6QD5RMPU9F.p8`) was NOT in the active list — login would have failed. The second one matched an active Admin key and worked.
|
|
107
|
-
</step>
|
|
108
|
-
|
|
109
|
-
<step n="5" title="Organize keys, then log in">
|
|
110
|
-
|
|
111
|
-
Move keys to the canonical folder so the next agent finds them instantly:
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
mkdir -p ~/Developer/app-store
|
|
115
|
-
mv ~/Downloads/AuthKey_*.p8 ~/Developer/app-store/ 2>/dev/null
|
|
116
|
-
chmod 600 ~/Developer/app-store/*.p8
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Then authenticate and verify end-to-end.
|
|
120
|
-
|
|
121
|
-
**Naming the credential:** use a stable, descriptive name tied to the machine + user, not the app — the same ASC API key is team-level and works across every app, so naming it per-app is misleading. Convention: `asc-macos-<user>-key` (e.g. `asc-macos-melvynx-key`). **Prefer a key with Admin access** when several active keys exist (Admin can read/write everything the release flow needs); check the ACCESS column from step 4e and pick the Admin key.
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
asc auth login \
|
|
125
|
-
--name "asc-macos-melvynx-key" \
|
|
126
|
-
--key-id "KEY_ID" \
|
|
127
|
-
--issuer-id "ISSUER_ID" \
|
|
128
|
-
--private-key ~/Developer/app-store/AuthKey_KEY_ID.p8 \
|
|
129
|
-
--network
|
|
130
|
-
|
|
131
|
-
asc auth status --validate # must report "works"
|
|
132
|
-
asc apps list # must list the target app
|
|
133
|
-
```
|
|
134
|
-
</step>
|
|
135
|
-
|
|
136
|
-
<step n="6" title="Persist the findings">
|
|
137
|
-
|
|
138
|
-
Record in the agent memory / AGENTS.md so this hunt never happens twice:
|
|
139
|
-
|
|
140
|
-
- The keys folder (`~/Developer/app-store/`), each key ID and whether it is active
|
|
141
|
-
- The issuer ID (team-level, stable)
|
|
142
|
-
- The `asc` credential name (convention `asc-macos-<user>-key`), its access level (prefer Admin), and that it is stored in the system keychain
|
|
143
|
-
- The app's numeric App Store Connect ID and bundle ID from `asc apps list`
|
|
144
|
-
</step>
|
|
145
|
-
|
|
146
|
-
<critical_safety>
|
|
147
|
-
- Never commit `.p8` files or paste their contents anywhere. `chmod 600` them.
|
|
148
|
-
- Never attempt the Apple login form yourself — 2FA makes it pointless and looks like account takeover. Always reuse the user's signed-in session, with their explicit OK to attach to/restart their browser.
|
|
149
|
-
- Restarting the browser closes the user's windows; warn them and rely on session restore.
|
|
150
|
-
</critical_safety>
|
|
151
|
-
|
|
152
|
-
<success_criteria>
|
|
153
|
-
- `asc auth status --validate` reports the credential as "works".
|
|
154
|
-
- `asc apps list` returns the expected app(s).
|
|
155
|
-
- Keys live in `~/Developer/app-store/` and the findings are written to memory/AGENTS.md.
|
|
156
|
-
</success_criteria>
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
# TestFlight: build an iOS app and ship it to TestFlight
|
|
2
|
-
|
|
3
|
-
Loaded for `appstore-connect testflight`. Takes an Expo / React Native iOS app from local dev to an installable TestFlight build, fully non-interactively.
|
|
4
|
-
|
|
5
|
-
**Two key insights that make this automatable:**
|
|
6
|
-
|
|
7
|
-
1. **ASC API key signing avoids Apple ID 2FA.** Interactive `eas build` credential setup needs Apple ID 2FA and cannot be scripted. Creating the distribution certificate + App Store provisioning profile through the App Store Connect API (`$SKILL_DIR/scripts/asc-api.mjs`) avoids 2FA entirely.
|
|
8
|
-
2. **`eas build --local` avoids Expo cloud build credits.** Default to the local Mac build with `credentialsSource: "local"`; use `--expo` only when the user explicitly wants a cloud build.
|
|
9
|
-
|
|
10
|
-
`$SKILL_DIR` is the `appstore-connect` skill directory; `<APP_DIR>` is the Expo project root (often the repo root, or `mobile-app/` / `apps/<name>/` in a monorepo).
|
|
11
|
-
|
|
12
|
-
## Arguments
|
|
13
|
-
|
|
14
|
-
- **default** (`appstore-connect testflight`): run setup → local build → upload → verify.
|
|
15
|
-
- **`--expo`**: use the EAS **cloud** build instead of local (consumes EAS build quota — confirm once).
|
|
16
|
-
- **setup-only** (`testflight setup`, "prepare credentials"): run phases A–D, then stop and report readiness.
|
|
17
|
-
|
|
18
|
-
## Prerequisites (ask once, as a group — the agent cannot create these)
|
|
19
|
-
|
|
20
|
-
1. **Apple Developer Program** membership (paid, enrolled).
|
|
21
|
-
2. **App Store Connect API key**: the `AuthKey_<KEY_ID>.p8` file, its key ID, and the team issuer ID (App Store Connect → Users and Access → Integrations; needs Admin or App Manager). If unknown, follow [setup.md](setup.md).
|
|
22
|
-
3. **Expo account** logged in: `npx eas-cli@latest whoami` (else `login`).
|
|
23
|
-
4. **App record** in App Store Connect for the bundle ID. The public API **cannot** create app records — if missing, the user creates it once at appstoreconnect.apple.com (My Apps → + → New App). Verified in Phase D.
|
|
24
|
-
5. **`asc` CLI** (`brew install asc`) — used only for the final upload.
|
|
25
|
-
6. For the default local build: macOS with Xcode CLT + CocoaPods (`xcodebuild -version`, `command -v pod`). If missing, stop and ask whether to use `--expo`.
|
|
26
|
-
|
|
27
|
-
## Critical safety
|
|
28
|
-
|
|
29
|
-
- Never commit or print `.p8` keys, `.p12` files, p12 passwords, provisioning profiles, or `credentials.json`. Verify `.gitignore` excludes them before any commit.
|
|
30
|
-
- Export ASC credentials as env vars for `asc-api.mjs`; never write them into repo files.
|
|
31
|
-
- Get explicit confirmation before: creating Apple certificates (low per-account limit), `--expo` cloud builds (consumes credits), and the TestFlight upload.
|
|
32
|
-
- Env vars are **baked in at build time** — confirm the production config points at prod backends before building, or testers ship with a dev backend.
|
|
33
|
-
|
|
34
|
-
## Phase A — Preflight
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
cd <APP_DIR>
|
|
38
|
-
npx tsc --noEmit && npm run lint # whatever checks the project has
|
|
39
|
-
npx expo-doctor # surface config problems early
|
|
40
|
-
npx eas-cli@latest whoami # Expo account logged in?
|
|
41
|
-
command -v asc && asc version
|
|
42
|
-
xcodebuild -version && command -v pod # required for default local builds
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Confirm the permanent values with the user: app `title`, `bundleId`, Apple `teamId`. The bundle ID is permanent once the app record exists.
|
|
46
|
-
|
|
47
|
-
Export ASC credentials for the rest of the session:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
export ASC_KEY_ID="<10-char key id>"
|
|
51
|
-
export ASC_ISSUER_ID="<issuer uuid>"
|
|
52
|
-
export ASC_P8_PATH="/path/to/AuthKey_<KEY_ID>.p8"
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Phase B — EAS project init
|
|
56
|
-
|
|
57
|
-
Skip if the project already has a real `eas.projectId` (a UUID).
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
cd <APP_DIR>
|
|
61
|
-
npx eas-cli@latest project:init --non-interactive --force
|
|
62
|
-
npx eas-cli@latest project:info
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Write the printed project ID into the Expo config (`app.json`/`app.config.ts` → `extra.eas.projectId`, or your project config).
|
|
66
|
-
|
|
67
|
-
## Phase C — Production env + eas.json
|
|
68
|
-
|
|
69
|
-
Point the build at production. Set every required production backend env var (auth secrets, API keys, URLs), then wire the prod values into `<APP_DIR>/eas.json → build.production`:
|
|
70
|
-
|
|
71
|
-
```json
|
|
72
|
-
"production": {
|
|
73
|
-
"autoIncrement": true,
|
|
74
|
-
"credentialsSource": "local",
|
|
75
|
-
"env": {
|
|
76
|
-
"EXPO_PUBLIC_API_URL": "https://<prod-backend>"
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
`credentialsSource: "local"` is what makes Phase E fully non-interactive.
|
|
82
|
-
|
|
83
|
-
## Phase D — Apple signing credentials via the ASC API
|
|
84
|
-
|
|
85
|
-
All calls: `node "$SKILL_DIR/scripts/asc-api.mjs" <METHOD> <path> [body.json]` with the Phase A env vars. Work in a directory **outside** the repo (e.g. `~/ios-credentials/<slug>/`) so nothing secret can be committed.
|
|
86
|
-
|
|
87
|
-
**1. Resolve the app record (hard gate):**
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
node "$SKILL_DIR/scripts/asc-api.mjs" GET "/v1/apps?filter[bundleId]=<bundle_id>"
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Empty `data` → STOP: tell the user to create the app record (My Apps → + → New App) with this exact bundle ID, then resume. Otherwise save `app_id = data[0].id`.
|
|
94
|
-
|
|
95
|
-
**2. Bundle ID registration + capabilities:**
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
node "$SKILL_DIR/scripts/asc-api.mjs" GET "/v1/bundleIds?filter[identifier]=<bundle_id>"
|
|
99
|
-
# if missing: POST /v1/bundleIds {"data":{"type":"bundleIds","attributes":{"identifier":"<bundle_id>","name":"<title>","platform":"IOS"}}}
|
|
100
|
-
# enable capabilities the app uses (IN_APP_PURCHASE, APPLE_ID_AUTH, PUSH_NOTIFICATIONS) via POST /v1/bundleIdCapabilities
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
**3. Distribution certificate** (Apple caps these at ~2–3 per account — check first):
|
|
104
|
-
|
|
105
|
-
```bash
|
|
106
|
-
node "$SKILL_DIR/scripts/asc-api.mjs" GET "/v1/certificates?filter[certificateType]=DISTRIBUTION"
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Reuse only if the user has its private key/.p12 locally. Otherwise create one (with confirmation):
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
openssl req -new -newkey rsa:2048 -nodes -keyout dist.key -out dist.csr \
|
|
113
|
-
-subj "/emailAddress=<user-email>/CN=<title> Distribution/C=US"
|
|
114
|
-
node -e "const fs=require('fs');fs.writeFileSync('cert-req.json',JSON.stringify({data:{type:'certificates',attributes:{certificateType:'DISTRIBUTION',csrContent:fs.readFileSync('dist.csr','utf8')}}}))"
|
|
115
|
-
node "$SKILL_DIR/scripts/asc-api.mjs" POST /v1/certificates cert-req.json
|
|
116
|
-
# save body.data.id (cert id) and body.data.attributes.certificateContent (base64) -> dist.cer
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
**4. App Store provisioning profile:**
|
|
120
|
-
|
|
121
|
-
```bash
|
|
122
|
-
node -e "const fs=require('fs');fs.writeFileSync('profile-req.json',JSON.stringify({data:{type:'profiles',attributes:{name:'<title> AppStore',profileType:'IOS_APP_STORE'},relationships:{bundleId:{data:{type:'bundleIds',id:'<BUNDLE_DB_ID>'}},certificates:{data:[{type:'certificates',id:'<CERT_ID>'}]},devices:{data:[]}}}}))"
|
|
123
|
-
node "$SKILL_DIR/scripts/asc-api.mjs" POST /v1/profiles profile-req.json
|
|
124
|
-
# save body.data.attributes.profileContent (base64) -> appstore.mobileprovision
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
**5. Build the `.p12` (the `-legacy` flag is mandatory — Apple tooling rejects OpenSSL 3.x default ciphers):**
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
echo "<certificateContent-b64>" | base64 -d > dist.cer
|
|
131
|
-
P12PASS=$(openssl rand -hex 12)
|
|
132
|
-
openssl x509 -inform der -in dist.cer -out dist.pem 2>/dev/null || cp dist.cer dist.pem
|
|
133
|
-
openssl pkcs12 -export -in dist.pem -inkey dist.key -out dist.p12 \
|
|
134
|
-
-name "<title> Distribution" -passout pass:"$P12PASS" -legacy
|
|
135
|
-
echo "<profileContent-b64>" | base64 -d > appstore.mobileprovision
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
**6. Wire into the project (gitignored paths only):**
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
mkdir -p <APP_DIR>/credentials
|
|
142
|
-
cp dist.p12 appstore.mobileprovision <APP_DIR>/credentials/
|
|
143
|
-
node -e "const fs=require('fs');fs.writeFileSync('<APP_DIR>/credentials.json',JSON.stringify({ios:{provisioningProfilePath:'credentials/appstore.mobileprovision',distributionCertificate:{path:'credentials/dist.p12',password:process.argv[1]}}},null,2))" "$P12PASS"
|
|
144
|
-
git check-ignore <APP_DIR>/credentials.json <APP_DIR>/credentials/dist.p12 # MUST print both
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
**Setup-only mode STOPS here** — report readiness (app record, cert, profile, credentials.json, eas.json) and the next command.
|
|
148
|
-
|
|
149
|
-
## Phase E — Local iOS build (default)
|
|
150
|
-
|
|
151
|
-
Uses the local Mac/Xcode toolchain; does **not** consume Expo cloud credits.
|
|
152
|
-
|
|
153
|
-
```bash
|
|
154
|
-
cd <APP_DIR>
|
|
155
|
-
npx eas-cli@latest build --platform ios --profile production \
|
|
156
|
-
--local --non-interactive --output /tmp/<slug>.ipa
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
If `expo-doctor` blocks on dependency patch mismatches, run `npx expo install --check`, apply the minimal patch updates, re-run `npx tsc --noEmit && npm run lint`, then rebuild. Don't blindly upgrade deps mid-release.
|
|
160
|
-
|
|
161
|
-
## Phase E (--expo) — EAS cloud build
|
|
162
|
-
|
|
163
|
-
Only when the user passes `--expo` (confirm once — consumes EAS credits):
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
cd <APP_DIR>
|
|
167
|
-
npx eas-cli@latest build --platform ios --profile production --non-interactive --no-wait
|
|
168
|
-
npx eas-cli@latest build:list --platform ios --limit 1 --json --non-interactive # poll to FINISHED/ERRORED
|
|
169
|
-
curl -sL -o /tmp/<slug>.ipa "<artifacts.buildUrl>" # download on FINISHED
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
## Phase F — TestFlight upload
|
|
173
|
-
|
|
174
|
-
**1. Internal beta group** (`asc publish testflight` fails without `--group`):
|
|
175
|
-
|
|
176
|
-
```bash
|
|
177
|
-
node -e "const fs=require('fs');fs.writeFileSync('group-req.json',JSON.stringify({data:{type:'betaGroups',attributes:{name:'Internal',isInternalGroup:true,hasAccessToAllBuilds:true},relationships:{app:{data:{type:'apps',id:'<app_id>'}}}}}))"
|
|
178
|
-
node "$SKILL_DIR/scripts/asc-api.mjs" POST /v1/betaGroups group-req.json
|
|
179
|
-
# 409 "already exists" is fine — fetch the id: GET "/v1/apps/<app_id>/betaGroups"
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
**2. Upload and wait** (`asc auth status --validate`, else `asc auth login` with the same ASC key):
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
asc publish testflight --app "<app_id>" --ipa /tmp/<slug>.ipa --group "<BETA_GROUP_ID>" --wait --timeout 45m
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**3. Add testers** (internal testers must be ASC team members; external emails work via groups):
|
|
189
|
-
|
|
190
|
-
```bash
|
|
191
|
-
node -e "const fs=require('fs');fs.writeFileSync('tester-req.json',JSON.stringify({data:{type:'betaTesters',attributes:{email:'<tester-email>'},relationships:{betaGroups:{data:[{type:'betaGroups',id:'<BETA_GROUP_ID>'}]}}}}))"
|
|
192
|
-
node "$SKILL_DIR/scripts/asc-api.mjs" POST /v1/betaTesters tester-req.json
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
**4. Verify:** `asc status --app "<app_id>" --output table` shows the build processed and attached to the group. Report build number, group, and testers.
|
|
196
|
-
|
|
197
|
-
## Failure modes
|
|
198
|
-
|
|
199
|
-
- **`.p12` rejected during signing** → exported without `-legacy`. Re-export with `-legacy`.
|
|
200
|
-
- **Certificate creation 409 / limit reached** → list existing DISTRIBUTION certs; ask which to revoke (`DELETE /v1/certificates/<id>`) or whether the user has its `.p12` to reuse. Never revoke without confirmation — it breaks other apps.
|
|
201
|
-
- **Local build fails before Xcode archive** → check `xcodebuild -version`, `xcode-select -p`, `command -v pod`. If unavailable and the user accepts cloud quota, rerun with `--expo`.
|
|
202
|
-
- **Build asks for credentials** → `credentialsSource: "local"` missing in `eas.json`, or `credentials.json` paths wrong (relative to `<APP_DIR>`).
|
|
203
|
-
- **`asc publish testflight` "--group is required"** → create the beta group first (Phase F step 1).
|
|
204
|
-
- **Upload rejected: missing export compliance** → answer the encryption question once in App Store Connect, or add `"ITSAppUsesNonExemptEncryption": false` to `infoPlist` in the Expo config.
|
|
205
|
-
- **App opens but can't reach backend** → built before `eas.json` had prod URLs, or prod env vars missing. Env is baked at build time — rebuild after fixing.
|
|
206
|
-
- **User insists on EAS-managed credentials (Apple ID login)** → that needs interactive 2FA; run `npx eas-cli@latest credentials` in a real terminal with the user present. Do not script the 2FA prompt.
|
|
207
|
-
|
|
208
|
-
## Success metrics
|
|
209
|
-
|
|
210
|
-
- Default local build produces `/tmp/<slug>.ipa` with no interactive prompt; `--expo` cloud builds reach FINISHED.
|
|
211
|
-
- The build shows as processed in TestFlight, attached to a beta group with at least one tester.
|
|
212
|
-
- `git status` shows no credential files staged; nothing secret printed in the transcript.
|