codebyplan 1.5.1 → 1.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/dist/cli.js +4462 -748
- package/package.json +5 -1
- package/templates/.gitkeep +0 -0
- package/templates/README.md +20 -0
- package/templates/agents/cbp-cc-executor.md +213 -0
- package/templates/agents/cbp-database-agent.md +229 -0
- package/templates/agents/cbp-improve-claude.md +245 -0
- package/templates/agents/cbp-improve-round.md +284 -0
- package/templates/agents/cbp-mechanical-edits.md +111 -0
- package/templates/agents/cbp-research.md +282 -0
- package/templates/agents/cbp-round-executor.md +604 -0
- package/templates/agents/cbp-security-agent.md +134 -0
- package/templates/agents/cbp-task-check.md +213 -0
- package/templates/agents/cbp-task-planner.md +582 -0
- package/templates/agents/cbp-test-e2e-agent.md +363 -0
- package/templates/agents/cbp-testing-qa-agent.md +400 -0
- package/templates/context/mcp-docs.md +139 -0
- package/templates/hooks/README.md +236 -0
- package/templates/hooks/cbp-auto-test-hooks.sh +44 -0
- package/templates/hooks/cbp-lint-format-on-edit.sh +159 -0
- package/templates/hooks/cbp-maestro-yaml-validate.sh +100 -0
- package/templates/hooks/cbp-mcp-migration-guard.sh +32 -0
- package/templates/hooks/cbp-mcp-round-sync.sh +79 -0
- package/templates/hooks/cbp-mcp-worktree-inject.sh +76 -0
- package/templates/hooks/cbp-notify.sh +68 -0
- package/templates/hooks/cbp-plugin-dispatch.sh +29 -0
- package/templates/hooks/cbp-pre-commit-quality-gate.sh +204 -0
- package/templates/hooks/cbp-statusline.sh +347 -0
- package/templates/hooks/cbp-subagent-statusline.sh +182 -0
- package/templates/hooks/cbp-test-coverage-gate.sh +144 -0
- package/templates/hooks/cbp-test-hooks.sh +320 -0
- package/templates/hooks/hooks.json +85 -0
- package/templates/hooks/validate-context-usage.sh +59 -0
- package/templates/hooks/validate-git-commit.sh +78 -0
- package/templates/hooks/validate-git-stash-deny.sh +32 -0
- package/templates/hooks/validate-structure-lengths.sh +57 -0
- package/templates/hooks/validate-structure-lib.sh +104 -0
- package/templates/hooks/validate-structure-patterns.sh +54 -0
- package/templates/hooks/validate-structure-scope.sh +33 -0
- package/templates/hooks/validate-structure-smoke.sh +95 -0
- package/templates/hooks/validate-structure-templates.sh +34 -0
- package/templates/hooks/validate-structure.sh +69 -0
- package/templates/rules/.gitkeep +0 -0
- package/templates/rules/README.md +47 -0
- package/templates/rules/context-file-loading.md +52 -0
- package/templates/rules/scope-vocabulary.md +64 -0
- package/templates/rules/todo-backend.md +109 -0
- package/templates/settings.project.base.json +55 -0
- package/templates/settings.user.base.json +25 -0
- package/templates/skills/cbp-build-cc-agent/SKILL.md +139 -0
- package/templates/skills/cbp-build-cc-agent/examples/read-only-reviewer.md +32 -0
- package/templates/skills/cbp-build-cc-agent/examples/with-hooks.md +41 -0
- package/templates/skills/cbp-build-cc-agent/examples/with-skills-preload.md +25 -0
- package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +153 -0
- package/templates/skills/cbp-build-cc-agent/reference/frontmatter-fields.md +37 -0
- package/templates/skills/cbp-build-cc-agent/reference/permission-modes.md +18 -0
- package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +67 -0
- package/templates/skills/cbp-build-cc-agent/templates/agent.md +66 -0
- package/templates/skills/cbp-build-cc-claude-file/SKILL.md +178 -0
- package/templates/skills/cbp-build-cc-claude-file/examples/minimal-project.md +33 -0
- package/templates/skills/cbp-build-cc-claude-file/examples/monorepo-with-imports.md +39 -0
- package/templates/skills/cbp-build-cc-claude-file/reference/imports.md +72 -0
- package/templates/skills/cbp-build-cc-claude-file/reference/what-belongs.md +39 -0
- package/templates/skills/cbp-build-cc-claude-file/templates/project-claude-md.md +48 -0
- package/templates/skills/cbp-build-cc-claude-file/templates/user-claude-md.md +22 -0
- package/templates/skills/cbp-build-cc-memory/SKILL.md +201 -0
- package/templates/skills/cbp-build-cc-memory/examples/feedback-memory.md +11 -0
- package/templates/skills/cbp-build-cc-memory/examples/project-memory.md +11 -0
- package/templates/skills/cbp-build-cc-memory/examples/reference-memory.md +13 -0
- package/templates/skills/cbp-build-cc-memory/examples/user-memory.md +14 -0
- package/templates/skills/cbp-build-cc-memory/reference/memory-types.md +59 -0
- package/templates/skills/cbp-build-cc-memory/reference/when-to-save.md +62 -0
- package/templates/skills/cbp-build-cc-memory/templates/MEMORY-index.md +4 -0
- package/templates/skills/cbp-build-cc-memory/templates/memory-entry.md +15 -0
- package/templates/skills/cbp-build-cc-mode/SKILL.md +99 -0
- package/templates/skills/cbp-build-cc-rule/SKILL.md +176 -0
- package/templates/skills/cbp-build-cc-rule/examples/global-rule.md +19 -0
- package/templates/skills/cbp-build-cc-rule/examples/scoped-rule.md +41 -0
- package/templates/skills/cbp-build-cc-rule/reference/paths-patterns.md +48 -0
- package/templates/skills/cbp-build-cc-rule/templates/rule.md +32 -0
- package/templates/skills/cbp-build-cc-settings/SKILL.md +220 -0
- package/templates/skills/cbp-build-cc-settings/examples/hooks-config.json +64 -0
- package/templates/skills/cbp-build-cc-settings/examples/permissions-config.json +34 -0
- package/templates/skills/cbp-build-cc-settings/examples/sandbox-config.json +42 -0
- package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +104 -0
- package/templates/skills/cbp-build-cc-settings/reference/permission-rules.md +61 -0
- package/templates/skills/cbp-build-cc-settings/reference/scope-precedence.md +73 -0
- package/templates/skills/cbp-build-cc-settings/reference/settings-fields.md +166 -0
- package/templates/skills/cbp-build-cc-settings/templates/settings.json +23 -0
- package/templates/skills/cbp-build-cc-settings/templates/settings.local.json +10 -0
- package/templates/skills/cbp-build-cc-skill/SKILL.md +154 -0
- package/templates/skills/cbp-build-cc-skill/examples/dynamic-context.md +31 -0
- package/templates/skills/cbp-build-cc-skill/examples/fork-skill.md +22 -0
- package/templates/skills/cbp-build-cc-skill/examples/knowledge-skill.md +25 -0
- package/templates/skills/cbp-build-cc-skill/examples/task-skill.md +29 -0
- package/templates/skills/cbp-build-cc-skill/reference/cbp-quality.md +157 -0
- package/templates/skills/cbp-build-cc-skill/reference/frontmatter-fields.md +35 -0
- package/templates/skills/cbp-build-cc-skill/reference/string-substitutions.md +60 -0
- package/templates/skills/cbp-build-cc-skill/scripts/validate-skill.sh +90 -0
- package/templates/skills/cbp-build-cc-skill/templates/skill.md +51 -0
- package/templates/skills/cbp-checkpoint-check/SKILL.md +156 -0
- package/templates/skills/cbp-checkpoint-complete/SKILL.md +109 -0
- package/templates/skills/cbp-checkpoint-create/SKILL.md +287 -0
- package/templates/skills/cbp-checkpoint-end/SKILL.md +241 -0
- package/templates/skills/cbp-checkpoint-update/SKILL.md +115 -0
- package/templates/skills/cbp-frontend-a11y/SKILL.md +109 -0
- package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +130 -0
- package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +122 -0
- package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +154 -0
- package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +111 -0
- package/templates/skills/cbp-frontend-design/SKILL.md +145 -0
- package/templates/skills/cbp-frontend-design/reference/nextjs-scss.md +118 -0
- package/templates/skills/cbp-frontend-design/reference/rn-expo.md +101 -0
- package/templates/skills/cbp-frontend-design/reference/tauri-react.md +82 -0
- package/templates/skills/cbp-frontend-ui/SKILL.md +262 -0
- package/templates/skills/cbp-frontend-ui/reference/ui-label-maps.md +42 -0
- package/templates/skills/cbp-frontend-ui/reference/ui-layout-patterns.md +105 -0
- package/templates/skills/cbp-frontend-ui/reference/variant-defaults.md +149 -0
- package/templates/skills/cbp-frontend-ux/SKILL.md +181 -0
- package/templates/skills/cbp-git-branch-feat-create/SKILL.md +115 -0
- package/templates/skills/cbp-git-commit/SKILL.md +278 -0
- package/templates/skills/cbp-git-worktree-create/SKILL.md +226 -0
- package/templates/skills/cbp-git-worktree-remove/SKILL.md +145 -0
- package/templates/skills/cbp-merge-main/SKILL.md +228 -0
- package/templates/skills/cbp-round-check/SKILL.md +104 -0
- package/templates/skills/cbp-round-end/SKILL.md +183 -0
- package/templates/skills/cbp-round-end/reference/findings-presentation.md +44 -0
- package/templates/skills/cbp-round-end/reference/inline-fallback.md +35 -0
- package/templates/skills/cbp-round-execute/SKILL.md +211 -0
- package/templates/skills/cbp-round-execute/reference/inline-fallback.md +59 -0
- package/templates/skills/cbp-round-input/SKILL.md +165 -0
- package/templates/skills/cbp-round-start/SKILL.md +222 -0
- package/templates/skills/cbp-round-update/SKILL.md +163 -0
- package/templates/skills/cbp-session-end/SKILL.md +187 -0
- package/templates/skills/cbp-session-start/SKILL.md +155 -0
- package/templates/skills/cbp-ship/SKILL.md +332 -0
- package/templates/skills/cbp-ship/reference/changesets-overview.md +120 -0
- package/templates/skills/cbp-ship/reference/eas-cli-overview.md +60 -0
- package/templates/skills/cbp-ship/reference/gh-cli-overview.md +135 -0
- package/templates/skills/cbp-ship/reference/gh-cli-shipment-commands.md +283 -0
- package/templates/skills/cbp-ship/reference/npm-publish-monorepo.md +252 -0
- package/templates/skills/cbp-ship/reference/npm-publish-oidc-trusted.md +157 -0
- package/templates/skills/cbp-ship/reference/npm-publish-overview.md +171 -0
- package/templates/skills/cbp-ship/reference/preflight-checklist.md +88 -0
- package/templates/skills/cbp-ship/reference/railway-nestjs-deployment.md +169 -0
- package/templates/skills/cbp-ship/reference/railway-overview.md +120 -0
- package/templates/skills/cbp-ship/reference/railway-troubleshooting.md +168 -0
- package/templates/skills/cbp-ship/reference/release-please-overview.md +99 -0
- package/templates/skills/cbp-ship/reference/surface-expo-eas.md +155 -0
- package/templates/skills/cbp-ship/reference/surface-npm.md +180 -0
- package/templates/skills/cbp-ship/reference/surface-railway.md +152 -0
- package/templates/skills/cbp-ship/reference/surface-supabase.md +178 -0
- package/templates/skills/cbp-ship/reference/surface-tauri.md +138 -0
- package/templates/skills/cbp-ship/reference/surface-vercel.md +124 -0
- package/templates/skills/cbp-ship/reference/surface-vscode-ext.md +144 -0
- package/templates/skills/cbp-ship/reference/surfaces.md +60 -0
- package/templates/skills/cbp-ship/reference/testflight-automation.md +215 -0
- package/templates/skills/cbp-ship/reference/testflight-internal-vs-external.md +69 -0
- package/templates/skills/cbp-ship/reference/testflight-overview.md +98 -0
- package/templates/skills/cbp-ship/reference/versioning.md +116 -0
- package/templates/skills/cbp-ship/scripts/detect-surfaces.sh +217 -0
- package/templates/skills/cbp-ship/scripts/verify-expo-eas.sh +35 -0
- package/templates/skills/cbp-ship/scripts/verify-npm.sh +21 -0
- package/templates/skills/cbp-ship/scripts/verify-railway.sh +41 -0
- package/templates/skills/cbp-ship/scripts/verify-supabase.sh +19 -0
- package/templates/skills/cbp-ship/scripts/verify-tauri.sh +24 -0
- package/templates/skills/cbp-ship/scripts/verify-vercel.sh +32 -0
- package/templates/skills/cbp-ship/scripts/verify-vscode-ext.sh +25 -0
- package/templates/skills/cbp-ship/templates/eas.json +66 -0
- package/templates/skills/cbp-ship/templates/railway.toml +15 -0
- package/templates/skills/cbp-ship/templates/release-please-config.json +17 -0
- package/templates/skills/cbp-ship/templates/vercel.json +19 -0
- package/templates/skills/cbp-ship/templates/vscodeignore +21 -0
- package/templates/skills/cbp-ship/templates/workflow-changesets.yml +41 -0
- package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +53 -0
- package/templates/skills/cbp-ship/templates/workflow-npm-publish.yml +36 -0
- package/templates/skills/cbp-ship/templates/workflow-release-please.yml +21 -0
- package/templates/skills/cbp-ship/templates/workflow-tauri-release.yml +69 -0
- package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +31 -0
- package/templates/skills/cbp-ship-configure/SKILL.md +296 -0
- package/templates/skills/cbp-ship-configure/reference/expo-mobile.md +204 -0
- package/templates/skills/cbp-ship-configure/reference/npm-package.md +165 -0
- package/templates/skills/cbp-ship-configure/reference/railway-backend.md +199 -0
- package/templates/skills/cbp-ship-configure/reference/supabase.md +200 -0
- package/templates/skills/cbp-ship-configure/reference/tauri-desktop.md +181 -0
- package/templates/skills/cbp-ship-configure/reference/vercel.md +117 -0
- package/templates/skills/cbp-ship-configure/reference/vscode-ext.md +155 -0
- package/templates/skills/cbp-ship-main/SKILL.md +65 -0
- package/templates/skills/cbp-supabase-branch-check/SKILL.md +337 -0
- package/templates/skills/cbp-supabase-branch-check/reference/dag-steps.md +29 -0
- package/templates/skills/cbp-supabase-migrate/SKILL.md +314 -0
- package/templates/skills/cbp-supabase-migrate/reference/advisor-triage.md +70 -0
- package/templates/skills/cbp-supabase-migrate/reference/cli-fallback.md +87 -0
- package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +58 -0
- package/templates/skills/cbp-supabase-setup/SKILL.md +239 -0
- package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +121 -0
- package/templates/skills/cbp-supabase-setup/reference/cli-fallback.md +109 -0
- package/templates/skills/cbp-task-check/SKILL.md +166 -0
- package/templates/skills/cbp-task-complete/SKILL.md +206 -0
- package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +48 -0
- package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +56 -0
- package/templates/skills/cbp-task-create/SKILL.md +167 -0
- package/templates/skills/cbp-task-start/SKILL.md +239 -0
- package/templates/skills/cbp-task-testing/SKILL.md +277 -0
- package/templates/skills/cbp-todo/SKILL.md +97 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Surface: tauri-desktop
|
|
2
|
+
|
|
3
|
+
Build, sign, notarize, and release a Tauri desktop app via GitHub Actions tag-trigger.
|
|
4
|
+
|
|
5
|
+
This surface file is the orchestrator's interface to the Tauri release pipeline — secrets, CI workflow, signing, notarization, and gotchas are all covered in the sections below.
|
|
6
|
+
|
|
7
|
+
## Detection
|
|
8
|
+
|
|
9
|
+
Signals:
|
|
10
|
+
|
|
11
|
+
- `src-tauri/tauri.conf.json` present
|
|
12
|
+
- `apps/desktop/src-tauri/` (monorepo) — instance is `apps/desktop`
|
|
13
|
+
|
|
14
|
+
## Configured indicators
|
|
15
|
+
|
|
16
|
+
`configured: true` requires:
|
|
17
|
+
|
|
18
|
+
1. `src-tauri/tauri.conf.json` parseable, with `productName` + `version`
|
|
19
|
+
2. `.github/workflows/release-desktop.yml` (or similar tag-trigger workflow) present
|
|
20
|
+
3. Required GitHub secrets set (per architecture doc — 9 secrets for the WBP setup)
|
|
21
|
+
4. `gh auth status` succeeds (CLI authenticated)
|
|
22
|
+
|
|
23
|
+
Probe: `gh secret list -R OWNER/REPO` and check that the 9 secret names from the architecture doc are present.
|
|
24
|
+
|
|
25
|
+
## Variants
|
|
26
|
+
|
|
27
|
+
| Variant | When |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `tag-and-release` | Version bump detected in `src-tauri/tauri.conf.json` `version` OR root `package.json` |
|
|
30
|
+
| `skip` | No version change since last shipment — desktop releases are tag-driven, not branch-driven |
|
|
31
|
+
|
|
32
|
+
## STEPS — tag-and-release variant
|
|
33
|
+
|
|
34
|
+
1. **Read version from tauri config**:
|
|
35
|
+
```bash
|
|
36
|
+
VERSION=$(jq -r .version "$APP_PATH/src-tauri/tauri.conf.json")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
2. **Verify tag doesn't already exist**:
|
|
40
|
+
```bash
|
|
41
|
+
git fetch origin --tags
|
|
42
|
+
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
|
|
43
|
+
echo "Tag v$VERSION already exists — skip or bump version"; exit 1
|
|
44
|
+
fi
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
3. **Confirm with user** via AskUserQuestion:
|
|
48
|
+
```
|
|
49
|
+
Push tag v$VERSION? This triggers GH Actions build + sign + notarize + release.
|
|
50
|
+
The build takes ~15 min. You'll receive a GH Releases page when complete.
|
|
51
|
+
A) Push tag — start the release
|
|
52
|
+
B) Skip — bump version in another commit first
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
4. **Push the tag**:
|
|
56
|
+
```bash
|
|
57
|
+
git tag -a "v$VERSION" -m "Release v$VERSION"
|
|
58
|
+
git push origin "v$VERSION"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
5. **Get the workflow run** triggered by the tag:
|
|
62
|
+
```bash
|
|
63
|
+
sleep 5 # GH Actions registration lag
|
|
64
|
+
RUN_ID=$(gh run list --workflow=release-desktop.yml --limit 1 --json databaseId,headBranch,event \
|
|
65
|
+
| jq -r ".[] | select(.event == \"push\" and .headBranch == \"v$VERSION\") | .databaseId")
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
6. **Watch the run** (15 min cap):
|
|
69
|
+
```bash
|
|
70
|
+
gh run watch "$RUN_ID" --exit-status
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
7. **Verify GH Release was created**:
|
|
74
|
+
```bash
|
|
75
|
+
gh release view "v$VERSION" --json assets,publishedAt | jq
|
|
76
|
+
```
|
|
77
|
+
Expect: 2 assets minimum (ARM .dmg + Intel .dmg) for macOS-only builds; more for Win/Linux if configured.
|
|
78
|
+
|
|
79
|
+
8. **Result**:
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"status": "verified",
|
|
83
|
+
"version": "v1.4.0",
|
|
84
|
+
"release_url": "https://github.com/OWNER/REPO/releases/tag/v1.4.0",
|
|
85
|
+
"assets": ["WorkByPlan_1.4.0_aarch64.dmg", "WorkByPlan_1.4.0_x64.dmg"],
|
|
86
|
+
"workflow_run_id": "1234567890"
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Verification
|
|
91
|
+
|
|
92
|
+
`scripts/verify-tauri.sh`:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
VERSION="$1"
|
|
96
|
+
|
|
97
|
+
# Tag exists locally + remote
|
|
98
|
+
git rev-parse "v$VERSION" >/dev/null 2>&1 || { echo "Tag missing"; exit 1; }
|
|
99
|
+
git ls-remote --tags origin "v$VERSION" | grep -q . || { echo "Tag not pushed"; exit 1; }
|
|
100
|
+
|
|
101
|
+
# GH Release published
|
|
102
|
+
PUBLISHED=$(gh release view "v$VERSION" --json publishedAt -q .publishedAt 2>/dev/null)
|
|
103
|
+
[ -n "$PUBLISHED" ] || { echo "Release not published"; exit 1; }
|
|
104
|
+
|
|
105
|
+
# At least one signed .dmg in assets
|
|
106
|
+
ASSETS=$(gh release view "v$VERSION" --json assets -q '.assets | length')
|
|
107
|
+
[ "$ASSETS" -ge 1 ] || { echo "No release assets"; exit 1; }
|
|
108
|
+
|
|
109
|
+
# Updater manifest (latest.json) updated — Tauri auto-update needs this
|
|
110
|
+
gh release view "v$VERSION" --json assets -q '.assets[].name' | grep -q "latest.json" \
|
|
111
|
+
|| echo "WARN: latest.json missing from release — auto-update may not work"
|
|
112
|
+
|
|
113
|
+
echo "OK"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Common failures
|
|
117
|
+
|
|
118
|
+
| Symptom | Cause | Fix |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| Workflow fails at "Notarize" step | Apple API key file path issue | See architecture doc § "Apple Notarization Key" — `.p8` content must be written to a file path, not passed as string |
|
|
121
|
+
| `latest.json` missing | Tauri updater config disabled in tauri.conf.json | Enable `tauri.bundle.updater` |
|
|
122
|
+
| ARM/Intel asset missing | matrix build for one arch failed | Check workflow logs; commonly Rust toolchain issue on the failed runner |
|
|
123
|
+
| "Invalid signing identity" | `APPLE_SIGNING_IDENTITY` secret has wrong format | Must be exact match: `Developer ID Application: Name (TEAMID)` |
|
|
124
|
+
| Public download URL broken | Vercel Blob upload step failed | Check `BLOB_READ_WRITE_TOKEN` secret + Vercel storage quota |
|
|
125
|
+
|
|
126
|
+
## Rollback
|
|
127
|
+
|
|
128
|
+
Tauri's auto-updater honors the most recent `latest.json` published. To rollback:
|
|
129
|
+
|
|
130
|
+
1. Delete the bad release: `gh release delete v$VERSION --yes`
|
|
131
|
+
2. Republish a prior tag: `gh release create v$PRIOR --notes "Republish after rollback" $PRIOR_ASSETS`
|
|
132
|
+
3. The auto-updater will roll users back at their next launch
|
|
133
|
+
|
|
134
|
+
The orchestrator surfaces this as an option when verification fails.
|
|
135
|
+
|
|
136
|
+
## Configure-once setup
|
|
137
|
+
|
|
138
|
+
See `${CLAUDE_PLUGIN_ROOT}/skills/ship-configure/reference/tauri-desktop.md` for first-time setup (Apple Developer cert, key generation, secrets upload, workflow scaffolding).
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Surface: vercel-web
|
|
2
|
+
|
|
3
|
+
Deploy a Next.js (or static) app to Vercel.
|
|
4
|
+
|
|
5
|
+
## Detection
|
|
6
|
+
|
|
7
|
+
Signals (any one is sufficient):
|
|
8
|
+
|
|
9
|
+
- `vercel.json` at app root
|
|
10
|
+
- `.vercel/project.json` at app root (links repo to Vercel project)
|
|
11
|
+
- App directory contains `next` in `package.json` dependencies AND no other surface signal claims it
|
|
12
|
+
|
|
13
|
+
A repo can have multiple `vercel-web` instances — one per app folder under `apps/*`.
|
|
14
|
+
|
|
15
|
+
## Configured indicators
|
|
16
|
+
|
|
17
|
+
`configured: true` requires:
|
|
18
|
+
|
|
19
|
+
1. `.vercel/project.json` present with `projectId` and `orgId`
|
|
20
|
+
2. Vercel CLI authenticated (`vercel whoami` succeeds)
|
|
21
|
+
3. `.codebyplan/shipment.json` `.surfaces.vercel-web.{instance}` block (optional but recommended)
|
|
22
|
+
|
|
23
|
+
If `.vercel/project.json` is missing → `configured: false`, reason "not linked to Vercel project — run /cbp-ship-configure".
|
|
24
|
+
|
|
25
|
+
## Variant: production
|
|
26
|
+
|
|
27
|
+
Default for shipping to PRODUCTION branch (per `branch_config.production`). Vercel auto-deploys on push to the linked production branch — this variant **observes** the auto-deploy, doesn't trigger it.
|
|
28
|
+
|
|
29
|
+
### STEPS — production variant
|
|
30
|
+
|
|
31
|
+
1. **Read project ID** from `.vercel/project.json`:
|
|
32
|
+
```bash
|
|
33
|
+
PROJECT_ID=$(jq -r .projectId .vercel/project.json)
|
|
34
|
+
ORG_ID=$(jq -r .orgId .vercel/project.json)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
2. **Get the deployment triggered by the merge commit**:
|
|
38
|
+
```bash
|
|
39
|
+
MERGE_SHA=$(git rev-parse HEAD)
|
|
40
|
+
DEPLOYMENT=$(vercel ls --json 2>/dev/null | jq -r ".[] | select(.meta.githubCommitSha == \"$MERGE_SHA\") | .uid" | head -1)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If no deployment found within 60s, Vercel is slow — wait up to 5min:
|
|
44
|
+
```bash
|
|
45
|
+
timeout 300 bash -c "while ! vercel ls --json | jq -e '.[] | select(.meta.githubCommitSha == \"$MERGE_SHA\")' > /dev/null; do sleep 10; done"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
3. **Wait for READY state**:
|
|
49
|
+
```bash
|
|
50
|
+
vercel inspect "$DEPLOYMENT" --wait
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
4. **Capture URL + readyState**:
|
|
54
|
+
```bash
|
|
55
|
+
STATUS=$(vercel inspect "$DEPLOYMENT" --json | jq -r '.readyState')
|
|
56
|
+
URL=$(vercel inspect "$DEPLOYMENT" --json | jq -r '.alias[0] // .url')
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
5. **Result**: `{ status: STATUS, url: URL, deployment_id: DEPLOYMENT }` to orchestrator.
|
|
60
|
+
|
|
61
|
+
## Variant: manual-redeploy
|
|
62
|
+
|
|
63
|
+
For "the auto-deploy failed, force a redeploy" path. User triggers explicitly.
|
|
64
|
+
|
|
65
|
+
### STEPS — manual-redeploy variant
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
cd "$APP_PATH"
|
|
69
|
+
vercel --prod --yes
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Capture deployment ID from output, then run Steps 3–5 from production variant.
|
|
73
|
+
|
|
74
|
+
## Variant: preview
|
|
75
|
+
|
|
76
|
+
Vercel auto-creates a preview deployment per PR. The orchestrator typically does NOT need to trigger anything for staging shipments — but it CAN verify the preview deployment for the integration branch:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
INTEGRATION=$(jq -r .branch_config.integration .codebyplan/git.json)
|
|
80
|
+
INTEGRATION_SHA=$(git rev-parse "origin/$INTEGRATION")
|
|
81
|
+
vercel ls --json | jq ".[] | select(.meta.githubCommitSha == \"$INTEGRATION_SHA\" and .target == \"preview\")"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Verification
|
|
85
|
+
|
|
86
|
+
`scripts/verify-vercel.sh` does:
|
|
87
|
+
|
|
88
|
+
1. `vercel inspect "$DEPLOYMENT_ID" --json | jq -r .readyState` returns `READY`
|
|
89
|
+
2. `curl -sI "$URL" | head -1` returns `HTTP/2 200` (or 401 for password-protected previews — also success)
|
|
90
|
+
3. If `apps/{instance}/public/health.json` or `/api/health` exists, hit it: expect 200
|
|
91
|
+
|
|
92
|
+
Timeouts:
|
|
93
|
+
- Build: 5 min (Vercel Hobby is slower; allow 8 min)
|
|
94
|
+
- HTTP probe: 30s
|
|
95
|
+
|
|
96
|
+
## Common failures
|
|
97
|
+
|
|
98
|
+
| Symptom | Likely cause | Fix |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `vercel ls` shows no deployment for the merge SHA | GitHub integration not connected, or push didn't trigger | Check Vercel dashboard → Settings → Git; reconnect if needed |
|
|
101
|
+
| Build fails with "Module not found" | Monorepo root not configured | In Vercel project settings: Build Command → `cd ../.. && pnpm turbo build --filter={instance}`, Root Directory → `apps/{instance}` |
|
|
102
|
+
| Preview URL 404s | Project paused or env var missing | Check Vercel logs for the deployment; common: missing `DATABASE_URL` |
|
|
103
|
+
| Build succeeds but page is blank | Production env vars missing | `vercel env ls production` — compare to `.env.example` |
|
|
104
|
+
|
|
105
|
+
## Rollback
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
vercel rollback # interactive: pick a prior deployment
|
|
109
|
+
vercel promote "$PRIOR_DEPLOYMENT" # promote a specific deployment to production
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The orchestrator surfaces this as an option when a verification fails:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
Vercel deployment failed verification.
|
|
116
|
+
Options:
|
|
117
|
+
A) Retry — the deploy may complete async
|
|
118
|
+
B) Rollback to prior production deployment
|
|
119
|
+
C) Investigate manually — show me the build logs
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Configure-once setup
|
|
123
|
+
|
|
124
|
+
See `${CLAUDE_PLUGIN_ROOT}/skills/ship-configure/reference/vercel.md` for first-time setup (vercel link, env var sync, GH integration verification).
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Surface: vscode-ext
|
|
2
|
+
|
|
3
|
+
Publish a VS Code extension to the Visual Studio Marketplace.
|
|
4
|
+
|
|
5
|
+
## Detection
|
|
6
|
+
|
|
7
|
+
Signals:
|
|
8
|
+
|
|
9
|
+
- `package.json` with `engines.vscode` field present
|
|
10
|
+
- `package.json` with `publisher` field present
|
|
11
|
+
- A `.vsixmanifest` template or built `.vsix` may be present (optional)
|
|
12
|
+
|
|
13
|
+
## Configured indicators
|
|
14
|
+
|
|
15
|
+
`configured: true` requires:
|
|
16
|
+
|
|
17
|
+
1. `package.json` has `name`, `displayName`, `publisher`, `version`, `engines.vscode`
|
|
18
|
+
2. `vsce` CLI installed (`npx @vscode/vsce --version` succeeds)
|
|
19
|
+
3. The user has a Personal Access Token (PAT) for the marketplace publisher
|
|
20
|
+
4. `vsce verify-pat <publisher>` succeeds
|
|
21
|
+
|
|
22
|
+
PAT scope required: "Marketplace → Manage" on Azure DevOps for the publisher.
|
|
23
|
+
|
|
24
|
+
## Variants
|
|
25
|
+
|
|
26
|
+
| Variant | When |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `vsce-publish` | `package.json` version > marketplace version |
|
|
29
|
+
| `skip` | No version bump |
|
|
30
|
+
|
|
31
|
+
## STEPS — vsce-publish variant
|
|
32
|
+
|
|
33
|
+
The `vsce` flow is similar to npm — token auth, the user runs the publish command for security.
|
|
34
|
+
|
|
35
|
+
1. **Detect extension package**:
|
|
36
|
+
```bash
|
|
37
|
+
EXT_PATH=$(find apps packages -maxdepth 3 -name package.json -exec sh -c \
|
|
38
|
+
'jq -e ".engines.vscode and .publisher" "$1" >/dev/null 2>&1 && dirname "$1"' _ {} \; | head -1)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
2. **Check version vs marketplace**:
|
|
42
|
+
```bash
|
|
43
|
+
PUBLISHER=$(jq -r .publisher "$EXT_PATH/package.json")
|
|
44
|
+
NAME=$(jq -r .name "$EXT_PATH/package.json")
|
|
45
|
+
LOCAL=$(jq -r .version "$EXT_PATH/package.json")
|
|
46
|
+
MARKET=$(npx @vscode/vsce show "$PUBLISHER.$NAME" --json 2>/dev/null | jq -r '.versions[0].version' || echo "")
|
|
47
|
+
|
|
48
|
+
[ "$LOCAL" = "$MARKET" ] && { echo "Already published"; exit 0; }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
3. **Build the extension** (orchestrator-side):
|
|
52
|
+
```bash
|
|
53
|
+
cd "$EXT_PATH"
|
|
54
|
+
pnpm run build # if defined
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
4. **Package locally to verify** (no publish):
|
|
58
|
+
```bash
|
|
59
|
+
cd "$EXT_PATH"
|
|
60
|
+
npx @vscode/vsce package --no-yarn -o "/tmp/$NAME-$LOCAL.vsix"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If packaging fails (typical: missing icon, README too short), HALT — surface error.
|
|
64
|
+
|
|
65
|
+
5. **Display publish command** for the user:
|
|
66
|
+
```
|
|
67
|
+
## VS Code Marketplace publish — manual step
|
|
68
|
+
|
|
69
|
+
Extension: $PUBLISHER.$NAME @ $LOCAL
|
|
70
|
+
Directory: $EXT_PATH
|
|
71
|
+
|
|
72
|
+
Run in your terminal:
|
|
73
|
+
cd $EXT_PATH
|
|
74
|
+
npx @vscode/vsce publish
|
|
75
|
+
|
|
76
|
+
You'll be prompted for the PAT if not cached. Reply 'done' when published, 'fail' if it errored.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
6. **Verify publication**:
|
|
80
|
+
```bash
|
|
81
|
+
sleep 30 # marketplace propagation
|
|
82
|
+
RESOLVED=$(npx @vscode/vsce show "$PUBLISHER.$NAME" --json | jq -r '.versions[0].version')
|
|
83
|
+
[ "$RESOLVED" = "$LOCAL" ] || { echo "Not yet visible — Marketplace can lag 1-5min"; exit 2; }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
7. **Result**:
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"status": "verified",
|
|
90
|
+
"extension": "publisher.name",
|
|
91
|
+
"version": "1.4.0",
|
|
92
|
+
"marketplace_url": "https://marketplace.visualstudio.com/items?itemName=publisher.name"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Pre-release channel
|
|
97
|
+
|
|
98
|
+
VS Code supports a "pre-release" channel via `vsce publish --pre-release`. Useful for staged rollouts:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx @vscode/vsce publish --pre-release # version must already include preid (e.g., 1.4.0-rc.1)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
If the extension supports pre-release, the orchestrator asks the user which channel:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
A) Stable channel — 1.4.0 → all users
|
|
108
|
+
B) Pre-release channel — 1.4.0-rc.1 → users opted into pre-release
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Verification
|
|
112
|
+
|
|
113
|
+
`scripts/verify-vscode-ext.sh`:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
PUBLISHER_NAME="$1" # e.g., publisher.name
|
|
117
|
+
VERSION="$2"
|
|
118
|
+
|
|
119
|
+
RESOLVED=$(npx @vscode/vsce show "$PUBLISHER_NAME" --json 2>/dev/null | jq -r '.versions[0].version')
|
|
120
|
+
[ "$RESOLVED" = "$VERSION" ] || { echo "Not on marketplace: $VERSION (latest: $RESOLVED)"; exit 1; }
|
|
121
|
+
|
|
122
|
+
# Activation events resolvable
|
|
123
|
+
EVENTS=$(npx @vscode/vsce show "$PUBLISHER_NAME" --json | jq -r '.activationEvents | length // 0')
|
|
124
|
+
[ "$EVENTS" -gt 0 ] || echo "WARN: no activation events declared — extension won't load"
|
|
125
|
+
|
|
126
|
+
echo "OK"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Common failures
|
|
130
|
+
|
|
131
|
+
| Symptom | Cause | Fix |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| `vsce publish` fails with 401 | PAT expired or wrong scope | Regenerate PAT with "Marketplace → Manage" scope |
|
|
134
|
+
| Package size > 50MB | `.vscodeignore` missing critical entries | Add `node_modules/**`, `*.test.*`, `.git/**` to `.vscodeignore` |
|
|
135
|
+
| Icon validation fails | PNG must be ≥128×128 | Replace icon, ensure PNG format |
|
|
136
|
+
| Marketplace 5xx during publish | Azure DevOps infra | Wait 5-10 min, retry |
|
|
137
|
+
|
|
138
|
+
## Rollback
|
|
139
|
+
|
|
140
|
+
`vsce unpublish <publisher>.<name> --force` removes the entire extension. To unpublish just one version, use the marketplace web UI → Manage Extension → Versions → remove. Surface as manual step.
|
|
141
|
+
|
|
142
|
+
## Configure-once setup
|
|
143
|
+
|
|
144
|
+
See `${CLAUDE_PLUGIN_ROOT}/skills/ship-configure/reference/vscode-ext.md` for first-time setup (publisher account creation, PAT generation, vsce login).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Shipment Surface Catalog
|
|
2
|
+
|
|
3
|
+
Authoritative list of every surface `/cbp-ship` knows how to deploy. The detection script (`scripts/detect-surfaces.sh`) maps repo signals to one of these IDs.
|
|
4
|
+
|
|
5
|
+
## Surfaces
|
|
6
|
+
|
|
7
|
+
| ID | Runtime | Detection signal | Deploy mechanism | Reference |
|
|
8
|
+
| ----------------- | ------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------- |
|
|
9
|
+
| `vercel-web` | Web (Next.js, static) | `vercel.json` OR `.vercel/project.json` OR Next.js app under `apps/*` with `next` in deps | Auto-deploy on git push to linked branch + optional `vercel --prod` for force redeploy | [surface-vercel.md](surface-vercel.md) |
|
|
10
|
+
| `expo-mobile` | Mobile (iOS/Android) | `app.json` / `app.config.{ts,js}` AND `expo` in deps | EAS Build → EAS Submit → TestFlight (iOS) / Play Console (Android) | [surface-expo-eas.md](surface-expo-eas.md) |
|
|
11
|
+
| `tauri-desktop` | Desktop (macOS/Windows/Linux) | `src-tauri/tauri.conf.json` | Push `v*.*.*` tag → GH Actions builds + signs + releases | [surface-tauri.md](surface-tauri.md) |
|
|
12
|
+
| `npm-package` | Package distribution | `package.json` with `private: false` OR `publishConfig` AND not under `apps/` | `npm publish` from local (2FA) or GH Actions (OIDC trusted publishing) | [surface-npm.md](surface-npm.md) |
|
|
13
|
+
| `vscode-ext` | VS Code Marketplace | `package.json` with `engines.vscode` AND `publisher` field | `vsce publish` from local OR GH Actions workflow | [surface-vscode-ext.md](surface-vscode-ext.md) |
|
|
14
|
+
| `railway-backend` | Backend (NestJS / Node service) | `railway.toml` OR `railway.json` OR `.railway/` AND non-frontend `package.json` | `railway up` from local OR auto-deploy on git push to linked branch | [surface-railway.md](surface-railway.md) |
|
|
15
|
+
| `supabase` | Database migrations | `supabase/migrations/` with at least one .sql newer than last shipment | `supabase db push` (interactive review of diff) | [surface-supabase.md](surface-supabase.md) |
|
|
16
|
+
|
|
17
|
+
## Variant rules
|
|
18
|
+
|
|
19
|
+
Each surface has variant choices that the orchestrator picks per-checkpoint:
|
|
20
|
+
|
|
21
|
+
| Surface | Variants | Default rule |
|
|
22
|
+
| ----------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
23
|
+
| `vercel-web` | `production`, `preview`, `manual-redeploy` | `production` if shipped to PRODUCTION branch this checkpoint, else skip (preview already auto-created on PR) |
|
|
24
|
+
| `expo-mobile` | `expo-go-only`, `eas-internal`, `eas-external` | Always ASK — never default; mobile shipment is opt-in per checkpoint |
|
|
25
|
+
| `tauri-desktop` | `tag-and-release`, `skip` | `tag-and-release` if version bump detected in `src-tauri/tauri.conf.json` or root, else `skip` |
|
|
26
|
+
| `npm-package` | `publish`, `skip` | `publish` if `package.json` version is unpublished on registry, else `skip` |
|
|
27
|
+
| `vscode-ext` | `vsce-publish`, `skip` | `vsce-publish` if version bump, else `skip` |
|
|
28
|
+
| `railway-backend` | `deploy`, `skip` | `deploy` if linked AND any service code changed this checkpoint, else `skip` |
|
|
29
|
+
| `supabase` | `push-migrations`, `skip` | `push-migrations` if pending migration count > 0, else `skip` |
|
|
30
|
+
|
|
31
|
+
## Detection precedence
|
|
32
|
+
|
|
33
|
+
When a surface signal is ambiguous (e.g., `apps/web` has both `vercel.json` and a `Dockerfile`), use this precedence:
|
|
34
|
+
|
|
35
|
+
1. Explicit `.codebyplan/shipment.json` `surfaces.{id}` block — definitive
|
|
36
|
+
2. Explicit `.codebyplan/shipment.json` `disabled[]` list — surface is hidden
|
|
37
|
+
3. Filesystem signal in priority order (above table)
|
|
38
|
+
4. Fallback — emit with `configured: false` and surface to user
|
|
39
|
+
|
|
40
|
+
## Multiple instances of one surface
|
|
41
|
+
|
|
42
|
+
A monorepo can have multiple instances of the same surface — e.g., `tarkur` ships 3 Vercel apps (`apps/homepage`, `apps/creator`, `apps/tenant`). Detection emits each as a separate entry:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{ "id": "vercel-web", "instance": "apps/homepage", "configured": true }
|
|
46
|
+
{ "id": "vercel-web", "instance": "apps/creator", "configured": true }
|
|
47
|
+
{ "id": "vercel-web", "instance": "apps/tenant", "configured": true }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`/cbp-ship` deploys each instance independently and reports each separately.
|
|
51
|
+
|
|
52
|
+
## Excluded by design
|
|
53
|
+
|
|
54
|
+
These don't have surfaces because shipment doesn't apply:
|
|
55
|
+
|
|
56
|
+
- `apps/cli` — bundled inside an npm package; ships via `npm-package`
|
|
57
|
+
- `packages/auth`, `packages/design-tokens`, etc. — internal workspace packages, never published
|
|
58
|
+
- Storybook builds — typically deployed as part of `vercel-web` (Storybook on Vercel) or skipped
|
|
59
|
+
|
|
60
|
+
If a project genuinely needs a missing surface (Cloudflare Pages, Fly.io, Heroku, AWS Amplify), open a feedback report — adding a surface is a structured change to this catalog plus a new reference file plus detection signal.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# TestFlight Automation
|
|
2
|
+
|
|
3
|
+
Automation contract for shipping to TestFlight from `/cbp-ship` (expo-mobile surface): App Store Connect API key setup, EAS Submit wiring, the common ITMS-90xxx error catalogue with remediations, build-expiration handling, and beta-group management via API.
|
|
4
|
+
|
|
5
|
+
## App Store Connect API Key
|
|
6
|
+
|
|
7
|
+
The API key replaces interactive Apple ID + 2FA prompts. It is the **only** sane authentication mechanism for CI / agent-driven submission.
|
|
8
|
+
|
|
9
|
+
### Generation
|
|
10
|
+
|
|
11
|
+
1. Sign in to [App Store Connect](https://appstoreconnect.apple.com/) as the Account Holder (or an Admin with the right permission).
|
|
12
|
+
2. Navigate to **Users and Access → Integrations → App Store Connect API → Team Keys**.
|
|
13
|
+
3. Click **Generate API Key**, give it a name (e.g. `cbp-ship-ci`), and pick an access role:
|
|
14
|
+
- **Developer** — sufficient for build upload + TestFlight distribution.
|
|
15
|
+
- **App Manager** — needed to create beta groups, manage testers, edit metadata.
|
|
16
|
+
- **Admin** — only if you also need account-level operations (rarely needed for `/cbp-ship`).
|
|
17
|
+
4. Download the `.p8` private key file. **Apple shows this exactly once** — store it immediately in the secret manager.
|
|
18
|
+
5. Note the **Key ID** (10-char alphanumeric, shown next to the key) and the **Issuer ID** (UUID, shown at the top of the API page — same for all keys on the team).
|
|
19
|
+
|
|
20
|
+
You now have three secrets:
|
|
21
|
+
|
|
22
|
+
| Field | Format | Where it lives |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `ASC_API_KEY_ID` | 10-char string | Plain env var |
|
|
25
|
+
| `ASC_API_ISSUER_ID` | UUID | Plain env var |
|
|
26
|
+
| `ASC_API_KEY` (the `.p8` content) | PEM-encoded private key | Secret manager |
|
|
27
|
+
|
|
28
|
+
Reference: [App Store Connect API — Get started](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api/).
|
|
29
|
+
|
|
30
|
+
### Rotation
|
|
31
|
+
|
|
32
|
+
Keys do not expire automatically, but Apple recommends rotating annually. Revoke via the same Integrations page; no grace period — revoked keys 401 immediately. Plan rotations during low-ship windows.
|
|
33
|
+
|
|
34
|
+
## EAS Submit Wiring
|
|
35
|
+
|
|
36
|
+
EAS Submit is the canonical upload path for Expo apps. Configuration lives in `eas.json` under the `submit` profile.
|
|
37
|
+
|
|
38
|
+
### Minimal `eas.json`
|
|
39
|
+
|
|
40
|
+
```jsonc
|
|
41
|
+
{
|
|
42
|
+
"submit": {
|
|
43
|
+
"production": {
|
|
44
|
+
"ios": {
|
|
45
|
+
"ascAppId": "1234567890",
|
|
46
|
+
"appleTeamId": "ABCDE12345",
|
|
47
|
+
"ascApiKeyPath": "./secrets/AuthKey_ABC1234567.p8",
|
|
48
|
+
"ascApiKeyId": "ABC1234567",
|
|
49
|
+
"ascApiKeyIssuerId": "11111111-2222-3333-4444-555555555555"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- `ascAppId` — the numeric App Store Connect app ID (visible in the URL when viewing the app in ASC, NOT the bundle identifier).
|
|
57
|
+
- `appleTeamId` — 10-char Team ID from the Apple Developer Portal **Membership** page.
|
|
58
|
+
- `ascApiKey*` fields — the three values from key generation above. The `.p8` path is relative to the repo root.
|
|
59
|
+
|
|
60
|
+
For CI, prefer the env-var form so the `.p8` doesn't get checked in:
|
|
61
|
+
|
|
62
|
+
```jsonc
|
|
63
|
+
{
|
|
64
|
+
"submit": {
|
|
65
|
+
"production": {
|
|
66
|
+
"ios": {
|
|
67
|
+
"ascAppId": "$ASC_APP_ID",
|
|
68
|
+
"ascApiKeyPath": "$ASC_API_KEY_PATH",
|
|
69
|
+
"ascApiKeyId": "$ASC_API_KEY_ID",
|
|
70
|
+
"ascApiKeyIssuerId": "$ASC_API_ISSUER_ID"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The CI step writes `$ASC_API_KEY` (the PEM content) to a temp file and points `$ASC_API_KEY_PATH` at it.
|
|
78
|
+
|
|
79
|
+
Reference: [EAS Submit — Submit to the Apple App Store](https://docs.expo.dev/submit/ios/).
|
|
80
|
+
|
|
81
|
+
### Invocation
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
eas submit --platform ios --profile production --non-interactive
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`--non-interactive` is required for agent-driven runs; it fails fast instead of prompting for missing credentials. Exit code 0 on successful upload (NOT on processing completion — see polling below).
|
|
88
|
+
|
|
89
|
+
### Post-Upload Polling
|
|
90
|
+
|
|
91
|
+
`eas submit` returns once the binary is handed off to App Store Connect. Apple's processing takes 10–15 minutes typically. To wait for `VALID` state, query `/v1/builds`:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
GET /v1/builds?filter[app]={ascAppId}&filter[preReleaseVersion.version]={version}&sort=-uploadedDate&limit=1
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Poll every 30 seconds. The `processingState` attribute transitions `PROCESSING` → `VALID` or `INVALID`. On `INVALID`, fetch the build's `buildBetaDetails` for the rejection reason.
|
|
98
|
+
|
|
99
|
+
## Common ITMS-90xxx Errors
|
|
100
|
+
|
|
101
|
+
These come back as email from Apple AND on the build record after upload. Capture and surface them in `/cbp-ship` failure output.
|
|
102
|
+
|
|
103
|
+
| Code | Meaning | Remediation |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| ITMS-90034 | Missing or invalid signature | EAS credentials drift — run `eas credentials` and re-sync. Often caused by an expired distribution certificate. |
|
|
106
|
+
| ITMS-90078 | Missing Push Notification Entitlement | `app.config` declares APNs capability but provisioning profile lacks it. Regenerate profile via `eas credentials`. |
|
|
107
|
+
| ITMS-90161 | Invalid provisioning profile | Profile mismatch with bundle ID or team. Re-fetch via EAS. |
|
|
108
|
+
| ITMS-90164 | Invalid code-signing entitlements | Entitlements in build don't match what the provisioning profile authorises. Common after adding capabilities (HealthKit, Background Modes) without regenerating the profile. |
|
|
109
|
+
| ITMS-90189 | Redundant binary upload | Build number already used for this version. Increment `CFBundleVersion` and re-upload. Apple never lets a build number be reused. |
|
|
110
|
+
| ITMS-90338 | Non-public API usage | A dependency uses a private symbol. Identify via the error's symbol list; usually a stale native module. Update or remove. |
|
|
111
|
+
| ITMS-90683 | Missing Purpose String | Info.plist lacks a `NSXxxUsageDescription` for a permission the app declares. Add the string in `app.config` under `ios.infoPlist`. |
|
|
112
|
+
| ITMS-90685 | CFBundleIdentifier collision | Two frameworks share a bundle ID. Usually a duplicated CocoaPod. Clean derived data and rebuild. |
|
|
113
|
+
| ITMS-90713 | Missing Info.plist value | A required key (e.g. `CFBundleIconName`) is missing. Check `app.config` and `eas build` output. |
|
|
114
|
+
| ITMS-91053 | Missing API declaration | New as of iOS 17 — privacy manifest required for certain "required reason" APIs (e.g. `UserDefaults`, `fileTimestamp`). Add `PrivacyInfo.xcprivacy` to the iOS bundle. |
|
|
115
|
+
| ITMS-91061 | Missing privacy manifest | Same family as 91053 — the manifest file itself is absent. |
|
|
116
|
+
| ITMS-4088 | Beta upload missing build | EAS submit uploaded but ASC didn't register the build. Often a transient ASC issue; retry. See [eas-cli#824](https://github.com/expo/eas-cli/issues/824). |
|
|
117
|
+
|
|
118
|
+
When `/cbp-ship` sees an ITMS-90xxx error, it should:
|
|
119
|
+
|
|
120
|
+
1. Capture the full error text from the ASC API response or email.
|
|
121
|
+
2. Match against the table above for a known remediation.
|
|
122
|
+
3. If unknown, surface to the user verbatim — do not guess.
|
|
123
|
+
|
|
124
|
+
## Build Expiration Handling
|
|
125
|
+
|
|
126
|
+
The 90-day rule (see [testflight-overview.md](./testflight-overview.md)) means long-running checkpoints risk silent expiry. Detection:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
GET /v1/builds/{id}?fields[builds]=expirationDate,expired
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`/cbp-ship` should:
|
|
133
|
+
|
|
134
|
+
- On every ship, query the previous build's `expirationDate`. If `< now + 14 days`, log a warning so the user knows a refresh is imminent.
|
|
135
|
+
- On `expired = true`, treat the previous TestFlight as gone — new testers will not be able to install. The next ship is no longer additive; it's a replacement.
|
|
136
|
+
|
|
137
|
+
There is no API to extend expiry. The only remedy is a fresh upload.
|
|
138
|
+
|
|
139
|
+
## Beta Groups via API
|
|
140
|
+
|
|
141
|
+
Group management is necessary when a checkpoint introduces a new tester segment.
|
|
142
|
+
|
|
143
|
+
### Create an internal group
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
POST /v1/betaGroups
|
|
147
|
+
{
|
|
148
|
+
"data": {
|
|
149
|
+
"type": "betaGroups",
|
|
150
|
+
"attributes": {
|
|
151
|
+
"name": "Engineering",
|
|
152
|
+
"isInternalGroup": true,
|
|
153
|
+
"publicLinkEnabled": false
|
|
154
|
+
},
|
|
155
|
+
"relationships": {
|
|
156
|
+
"app": { "data": { "type": "apps", "id": "{ascAppId}" } }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Create an external group with public link
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
POST /v1/betaGroups
|
|
166
|
+
{
|
|
167
|
+
"data": {
|
|
168
|
+
"type": "betaGroups",
|
|
169
|
+
"attributes": {
|
|
170
|
+
"name": "Public Beta",
|
|
171
|
+
"isInternalGroup": false,
|
|
172
|
+
"publicLinkEnabled": true,
|
|
173
|
+
"publicLinkLimitEnabled": true,
|
|
174
|
+
"publicLinkLimit": 1000
|
|
175
|
+
},
|
|
176
|
+
"relationships": {
|
|
177
|
+
"app": { "data": { "type": "apps", "id": "{ascAppId}" } }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`publicLink` is returned in the response — that's the URL to share.
|
|
184
|
+
|
|
185
|
+
### Assign a build to a group
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
POST /v1/betaGroups/{groupId}/relationships/builds
|
|
189
|
+
{ "data": [{ "type": "builds", "id": "{buildId}" }] }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
For external groups, the build must be `VALID` AND have an approved Beta App Review submission before assignment results in distribution.
|
|
193
|
+
|
|
194
|
+
### Submit for Beta App Review
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
POST /v1/betaAppReviewSubmissions
|
|
198
|
+
{
|
|
199
|
+
"data": {
|
|
200
|
+
"type": "betaAppReviewSubmissions",
|
|
201
|
+
"relationships": {
|
|
202
|
+
"build": { "data": { "type": "builds", "id": "{buildId}" } }
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Poll `/v1/betaAppReviewSubmissions/{id}` for `betaReviewState`: `WAITING_FOR_REVIEW` → `IN_REVIEW` → `APPROVED` / `REJECTED`. On rejection, fetch the rejection reason from the related `betaAppReviewDetails`.
|
|
209
|
+
|
|
210
|
+
## References
|
|
211
|
+
|
|
212
|
+
- [App Store Connect API — Get started](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api/)
|
|
213
|
+
- [EAS Submit — Submit to the Apple App Store](https://docs.expo.dev/submit/ios/)
|
|
214
|
+
- [EAS Submit — Introduction](https://docs.expo.dev/submit/introduction/)
|
|
215
|
+
- [TestFlight overview](https://developer.apple.com/help/app-store-connect/test-a-beta-version/testflight-overview/)
|