Skip to content

Commit 91ac0f3

Browse files
committed
Update SKILL.md
1 parent c349c51 commit 91ac0f3

File tree

1 file changed

+115
-20
lines changed

1 file changed

+115
-20
lines changed
Lines changed: 115 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
---
22
name: release-preparation
3-
description: Use when preparing a new Mos release (stable, beta, or alpha) - covers zip packaging, changelog generation, appcast signing, and GitHub release draft creation.
3+
description: Use when preparing a new Mos release (stable, beta, or alpha) - covers build, notarize, zip packaging, changelog generation, appcast signing, and GitHub release draft creation.
44
---
55

66
# Release Preparation
77

8-
Prepare and publish a Mos release: package app → generate changelog → sign → create appcast → draft GitHub release.
8+
Full release pipeline: bump version → build → sign → notarize → package zip → generate changelog → sign appcast → create GH draft.
99

1010
## Inputs
1111

1212
| Input | Source |
1313
|-------|--------|
14-
| Mos.app path | User provides |
15-
| Version / Build | Read from app's Info.plist |
14+
| Mos.app | Built via xcodebuild archive + Developer ID export (Step 0) |
15+
| Version / Build | `MARKETING_VERSION` / `CURRENT_PROJECT_VERSION` in `Mos.xcodeproj/project.pbxproj` |
1616
| Channel | User specifies: `stable`, `beta`, or `alpha` |
17-
| Signing key | macOS Keychain (Sparkle EdDSA) |
17+
| Signing key | macOS Keychain — Apple Developer ID (code signing) + Sparkle EdDSA (appcast) |
18+
| Notarization | macOS Keychain profile `notarytool` (stores Apple ID + app-specific password) |
1819

1920
## Flow
2021

@@ -23,18 +24,77 @@ digraph release {
2324
rankdir=TB;
2425
node [shape=box];
2526
27+
"0. Build, sign & notarize" -> "1. Package zip";
2628
"1. Package zip" -> "2. Generate changelog";
2729
"2. Generate changelog" -> "3. Interactive confirm";
2830
"3. Interactive confirm" -> "4. Sign & update appcast";
29-
"4. Sign & update appcast" -> "5. Create GH draft";
30-
"5. Create GH draft" -> "6. User reviews draft";
31+
"4. Sign & update appcast" -> "5. Commit appcast";
32+
"5. Commit appcast" -> "6. Create GH draft";
33+
"6. Create GH draft" -> "7. Verify";
34+
"7. Verify" -> "8. User publishes + git push";
3135
}
3236
```
3337

38+
### Step 0: Build, Sign & Notarize
39+
40+
1. **Bump version** in `Mos.xcodeproj/project.pbxproj`:
41+
- `MARKETING_VERSION` — e.g., `4.0.2` (appears twice, use `replace_all`)
42+
- `CURRENT_PROJECT_VERSION` — build number, optional to update
43+
- Commit the version bump.
44+
45+
2. **Archive**:
46+
```bash
47+
xcodebuild archive \
48+
-scheme Debug \
49+
-project Mos.xcodeproj \
50+
-configuration Release \
51+
-archivePath /tmp/Mos.xcarchive
52+
```
53+
Note: The scheme is named "Debug" but `-configuration Release` ensures Release build settings.
54+
55+
3. **Export with Developer ID** (Direct Distribution):
56+
```bash
57+
cat > /tmp/ExportOptions.plist << 'PLIST'
58+
<?xml version="1.0" encoding="UTF-8"?>
59+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
60+
<plist version="1.0">
61+
<dict>
62+
<key>method</key><string>developer-id</string>
63+
<key>teamID</key><string>N7Z52F27XK</string>
64+
<key>signingStyle</key><string>automatic</string>
65+
</dict>
66+
</plist>
67+
PLIST
68+
69+
xcodebuild -exportArchive \
70+
-archivePath /tmp/Mos.xcarchive \
71+
-exportOptionsPlist /tmp/ExportOptions.plist \
72+
-exportPath /tmp/MosExport \
73+
-allowProvisioningUpdates
74+
```
75+
If export fails with "network connection was lost", retry — Apple's notarization service can be flaky.
76+
77+
4. **Notarize** (if Xcode export didn't auto-notarize):
78+
```bash
79+
ditto -c -k --keepParent /tmp/MosExport/Mos.app /tmp/Mos-notarize.zip
80+
xcrun notarytool submit /tmp/Mos-notarize.zip --keychain-profile "notarytool" --wait
81+
xcrun stapler staple /tmp/MosExport/Mos.app
82+
```
83+
84+
5. **Verify**:
85+
```bash
86+
codesign -dvv /tmp/MosExport/Mos.app 2>&1 | grep Authority
87+
# Should show: Developer ID Application: BIAO CHEN (N7Z52F27XK)
88+
spctl --assess --type execute /tmp/MosExport/Mos.app
89+
# Should pass (exit 0)
90+
```
91+
92+
The notarized app at `/tmp/MosExport/Mos.app` is used for Step 1.
93+
3494
### Step 1: Package Zip
3595
3696
```bash
37-
bash .codex/skills/release-preparation/scripts/prepare_zip.sh /path/to/Mos.app [--channel beta]
97+
bash .claude/skills/release-preparation/scripts/prepare_zip.sh /tmp/MosExport/Mos.app [--channel beta]
3898
```
3999
40100
Returns JSON with `zip_path`, `version`, `build`, `tag`, `zip_name`, `length`.
@@ -43,29 +103,42 @@ Returns JSON with `zip_path`, `version`, `build`, `tag`, `zip_name`, `length`.
43103
44104
**This is AI work, not scripted.** Follow these rules:
45105
46-
1. Find last release commit:
106+
1. Find last release tag:
47107
```bash
48108
gh release list --repo Caldis/Mos --limit 1 --json tagName,isPrerelease
49-
gh release view <tag> --json targetCommitish
109+
git rev-parse <tag> # get exact commit SHA
50110
```
51-
2. Get changes: `git log <last_commit>..HEAD --no-merges --oneline` excluding `website/`, `docs/`, `.issues-archive/`, `CLAUDE.md`, `LOCALIZATION.md`, `build/`, `dmg/`.
111+
2. Get changes: `git log <last_tag>..HEAD --no-merges --oneline` excluding `website/`, `docs/`, `.issues-archive/`, `CLAUDE.md`, `LOCALIZATION.md`, `build/`, `dmg/`, `CRASH_FIX_DESIGN*`.
52112
3. Categorize into: 新功能/New Features, 优化/Improvements, 修复/Fixes.
53113
4. Find contributors: cross-reference `git log --format="%an"` with `gh api repos/.../commits/<sha> --jq '.author.login'`. Inline credit in the relevant section (e.g., "修复鼠标中键映射问题, 感谢 @GonzFC"), NOT in a separate section.
54114
5. Match tone of `CHANGELOG.md` — bilingual (Chinese first, `---` separator, then English).
55115
6. Write both formats:
56-
- **Markdown**`~/Desktop/release-notes-{version}.md` (for GH release body)
116+
- **Markdown** → `~/Desktop/release-notes-{version}.md` (for GH release body). Must include wiki links header before each language section:
117+
```markdown
118+
> 如果应用无法启动或遇到权限问题, 请参考 [Wiki: 如果应用无法正常运行](https://github.com/Caldis/Mos/wiki/%E5%A6%82%E6%9E%9C%E5%BA%94%E7%94%A8%E6%97%A0%E6%B3%95%E6%AD%A3%E5%B8%B8%E8%BF%90%E8%A1%8C)
119+
120+
## 修复
121+
- ...
122+
123+
---
124+
125+
> If the application fails to start or encounters permission issues, please refer [Wiki: If the App not work properly](https://github.com/Caldis/Mos/wiki/If-the-App-not-work-properly)
126+
127+
## Fixes
128+
- ...
129+
```
57130
- **HTML** → `/tmp/changelog-{version}.html` (for appcast CDATA, `<h2>` + `<ul><li>`)
58131
59132
### Step 3: Interactive Confirm
60133
61-
**MUST use `AskUserQuestion` with `multiSelect: true`** to confirm changelog items by category. Never list items as text and ask user to type numbers.
134+
**MUST use `AskUserQuestion`** to confirm changelog items. Never list items as text and ask user to type numbers.
62135
63136
After confirmation, sync any user edits from markdown back to HTML. Always keep both formats in sync.
64137
65138
### Step 4: Sign & Update Appcast
66139
67140
```bash
68-
bash .codex/skills/release-preparation/scripts/update_appcast.sh <zip_path> /tmp/changelog-{version}.html [--tag TAG]
141+
bash .claude/skills/release-preparation/scripts/update_appcast.sh <zip_path> /tmp/changelog-{version}.html [--tag TAG]
69142
```
70143
71144
Uses Sparkle `sign_update` from Xcode DerivedData (reads EdDSA key from Keychain). If key missing, guide user:
@@ -76,22 +149,41 @@ find ~/Library/Developer/Xcode/DerivedData -name "generate_keys" -not -path "*/c
76149
# Export: generate_keys -x <output_file>
77150
```
78151
79-
### Step 5: Create GitHub Draft
152+
The script writes to both `build/appcast.xml` and `docs/appcast.xml`.
153+
154+
### Step 5: Commit Appcast
155+
156+
Only `docs/appcast.xml` is tracked by git (`build/` is gitignored):
157+
```bash
158+
git add docs/appcast.xml
159+
git commit -m "chore: update appcast for {version}"
160+
```
161+
162+
### Step 6: Create GitHub Draft
80163
81164
```bash
82-
bash .codex/skills/release-preparation/scripts/create_gh_draft.sh <tag> <zip_path> ~/Desktop/release-notes-{version}.md [--prerelease]
165+
bash .claude/skills/release-preparation/scripts/create_gh_draft.sh <tag> <zip_path> ~/Desktop/release-notes-{version}.md [--prerelease]
83166
```
84167
85168
Add `--prerelease` for beta/alpha channels. This creates a **draft** — never publish without user approval.
86169
87-
### Step 6: Verify
170+
### Step 7: Verify
88171
89-
After draft creation, verify:
90172
```bash
91-
gh release view <tag> --repo Caldis/Mos --json assets,tagName,isDraft
173+
gh release view <tag> --repo Caldis/Mos --json tagName,isDraft,assets \
174+
--jq '{tag: .tagName, draft: .isDraft, assets: [.assets[] | {name: .name, size: .size}]}'
92175
```
176+
93177
Confirm asset URL matches appcast `<enclosure url="...">`.
94178
179+
### Step 8: User Publishes
180+
181+
User reviews draft on GitHub and publishes. After publishing:
182+
```bash
183+
git push origin master
184+
```
185+
This pushes the appcast update so Sparkle auto-update can find it.
186+
95187
## Naming Conventions
96188
97189
| | Stable | Beta/Alpha |
@@ -104,7 +196,10 @@ Confirm asset URL matches appcast `<enclosure url="...">`.
104196
105197
| Problem | Fix |
106198
|---------|-----|
199+
| Export fails with "network connection was lost" | Retry — Apple notarization service can be intermittent |
200+
| `spctl --assess` rejected after export | Notarization not auto-applied; manually submit with `notarytool` + `stapler staple` |
107201
| `sign_update` not found | Build project in Xcode first to fetch Sparkle SPM package |
108-
| Signing key not in Keychain | Use `generate_keys -f <key_file>` to import |
202+
| Signing key not in Keychain | Use `generate_keys -f <key_file>` to import Sparkle EdDSA key |
203+
| Developer ID cert missing from `security find-identity` | Download from Apple Developer portal or use Xcode → Settings → Accounts → Manage Certificates |
109204
| Appcast URL mismatch | Verify GH release tag matches appcast `<enclosure>` URL path |
110205
| Changelog out of sync | Always edit markdown first, then regenerate HTML before updating appcast |

0 commit comments

Comments
 (0)