# Release

> Prepare, validate, and publish a MiaoYan direct-download GitHub Release. Not for App Store builds.

- Skill: `tw93/release` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tw93/release`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tw93/release/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools, AI & ML
- Author: tw93 (https://skillmd.com/u/tw93)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tw93/release

---


# MiaoYan Release Workflow

Use this skill only when the maintainer explicitly asks for a GitHub Release.

## Version Rules

- Release tags use uppercase `Vx.y.z`.
- `MARKETING_VERSION` in `MiaoYan.xcodeproj/project.pbxproj` must match the tag without the leading `V`.
- `CURRENT_PROJECT_VERSION` must equal `MARKETING_VERSION`. Sparkle compares `sparkle:version` in appcast.xml against the app's `CFBundleVersion` (which maps to `CURRENT_PROJECT_VERSION`). If they diverge, users get an infinite update prompt loop (see V3.5.1 incident, #524).
- Release notes should be prepared before tagging.
- Signing, notarization, and Sparkle credentials are maintainer-managed. Do not commit credential paths, private key filenames, passwords, or secret values.
- Sparkle signing must use the MiaoYan release key. Do not rely on the default Sparkle Keychain account because it may belong to another app.

## Release Notes Format

- Before drafting, read the previous published release and treat it as the hard format template: `gh release view $(gh release list --limit 1 --json tagName --jq '.[0].tagName')`. Do not rebuild the shape from memory.
- Title is `V{x.y.z} {Codename} {emoji}`, e.g. `V4.0.0 Valstrax 🚀`. The codename follows the monster-name-plus-emoji convention used by `scripts/release-ci/generate_release_content.sh`.
- Source of truth is `.github/RELEASE_NOTES.md`: `# V{x.y.z} {Codename} {emoji}` heading, Chinese numbered list, `---` separator, English numbered list, items mapped one-to-one. `scripts/release-ci/render_release_body.sh` renders it into the HTML release body (centered logo block + tagline, `<h3>Changelog</h3>` English list, `<h3>更新日志</h3>` Chinese list, closing star ask + repo blockquote).
- Keep 3 to 6 items per language, one sentence each, engineer-facing.

## Preflight

```bash
git diff --quiet && git diff --cached --quiet
grep "MARKETING_VERSION" MiaoYan.xcodeproj/project.pbxproj | head -1
grep "CURRENT_PROJECT_VERSION" MiaoYan.xcodeproj/project.pbxproj | head -1
gh release list --limit 10
gh run list --limit 10
```

Stop if:
- The working tree is dirty.
- The version is unclear.
- `CURRENT_PROJECT_VERSION` does not equal `MARKETING_VERSION` (hard stop, fix before proceeding).
- The intended tag already exists and recovery has not been discussed.

## Build And Publish

Use the repository's release scripts for the actual build, packaging, signing, notarization, and appcast update. The only tracked workflow is `ci.yml`; do not assume a `release.yml` workflow exists.

When a local release script is required, confirm these before running it:

- Required signing identities are available on the maintainer machine.
- Required secrets are available through the intended channel.
- The generated DMG, ZIP, and Sparkle metadata point to the same version.
- The ZIP used by the appcast is the file that was signed for Sparkle.

## Verification

```bash
gh release view Vx.y.z
gh run list --limit 5
```

After publication, confirm that the release assets exist, the appcast points at the intended ZIP, and the Sparkle signature metadata matches the published ZIP.
Use `scripts/release-ci/verify_sparkle_signature.sh --zip <zip> --signature <signature>` to verify the appcast signature against the ZIP bytes and the app's embedded `SUPublicEDKey` before pushing appcast changes.

Reactions are part of publish completion. After the release is live, add the six positive reactions and read them back to confirm:

```bash
rid=$(gh api repos/tw93/MiaoYan/releases/tags/V<x.y.z> --jq .id)
for r in +1 laugh heart hooray rocket eyes; do gh api -X POST repos/tw93/MiaoYan/releases/$rid/reactions -f content="$r"; done
gh api repos/tw93/MiaoYan/releases/$rid/reactions --jq '.[].content'
```

Never add `-1` or `confused`; a negative reaction on our own release reads as self-deprecation.

## Safety Rules

- Never tag, upload assets, update appcast, or publish a release without explicit maintainer confirmation.
- Never commit local credential paths or secret filenames.
- If notarization or signing fails, report the exact failure and stop before changing credentials.

