# Releasing
Releases are fully automated: pushing a `v*` tag builds a universal binary,
signs and notarizes it, publishes a GitHub release, and bumps the Homebrew tap.
```sh
# bump version in Cargo.toml first, commit, then:
git tag v0.1.0
git push origin v0.1.0
```
## One-time setup
### 1. Developer ID certificate
Distribution outside the App Store requires a **Developer ID Application**
certificate (the "Apple Development" certs in your keychain won't pass
Gatekeeper on other machines). Requires a paid Apple Developer membership.
1. Create a certificate signing request: Keychain Access → menu bar →
Keychain Access → Certificate Assistant → Request a Certificate From a
Certificate Authority → fill in your email, select "Saved to disk". This
also generates the private key in your keychain.
2. https://developer.apple.com/account/resources/certificates → `+` → choose
**Developer ID Application** → upload the CSR → download the issued
certificate and double-click it to import it into your keychain.
3. Export for CI: in Keychain Access (My Certificates), right-click the
"Developer ID Application: ..." entry → Export → `.p12` format. You will be
asked to invent an export password — this password only protects the `.p12`
file itself. Store the `.p12` and its password in 1Password; the keychain
copy plus that backup are your only copies (Apple cannot re-issue the
private key, only a new certificate).
4. Base64-encode for the GitHub secret: `base64 -i cert.p12 | pbcopy`
### 2. App Store Connect API key for notarization
Notarization means uploading the binary to Apple's malware-scanning service,
which CI must authenticate for. An App Store Connect API key is used instead
of an Apple ID + app-specific password because it is scoped to developer
services only — it cannot read iCloud mail, contacts, or anything personal.
1. https://appstoreconnect.apple.com → Users and Access → Integrations →
**Team Keys** → generate a key with the **Developer** role.
2. Download the `.p8` file — Apple offers it exactly once; store it in
1Password together with the **Key ID** (shown next to the key) and the
**Issuer ID** (shown at the top of the Integrations page).
3. Base64-encode for the GitHub secret: `base64 -i AuthKey_XXXXXX.p8 | pbcopy`
### 3. Repository secrets
`gh secret set <NAME>` in this repo, or Settings → Secrets → Actions:
| `MACOS_CERTIFICATE` | base64 of the `.p12` |
| `MACOS_CERTIFICATE_PASSWORD` | password chosen at export |
| `MACOS_SIGNING_IDENTITY` | `Developer ID Application: Your Name (TEAMID)` — check `security find-identity -v -p codesigning` after importing |
| `APP_STORE_CONNECT_KEY` | base64 of the `.p8` key |
| `APP_STORE_CONNECT_KEY_ID` | key ID from step 2 |
| `APP_STORE_CONNECT_ISSUER_ID` | issuer UUID from step 2 |
| `TAP_GITHUB_TOKEN` | fine-grained PAT with **contents: write** on `blaueeiner/homebrew-tap` (optional; tap bump is skipped without it) |
### 4. crates.io trusted publishing
The workflow publishes to crates.io via OIDC ("Trusted Publishing") — no
long-lived token stored anywhere. One-time setup (the crate must already
exist, i.e. after the first manual `cargo publish`):
https://crates.io/crates/typechar/settings → **Trusted Publishing** → add
GitHub publisher: repository owner `blaueeiner`, repository `typechar`,
workflow filename `release.yml`, environment: leave empty.
### 5. Homebrew tap repository
Create the (public) repo `blaueeiner/homebrew-tap` once — it can start empty.
The release workflow commits `Formula/typechar.rb` into it. Users then install with:
```sh
brew install blaueeiner/tap/typechar
```
## Notes
- Bare executables can't have a notarization ticket stapled; Gatekeeper
verifies them online. That's normal for CLI tools — and Homebrew-installed
binaries aren't quarantined anyway, so signing + notarization mainly covers
people downloading the tarball directly.
- To verify a release locally:
`codesign --verify --strict -v typechar && spctl -a -t open --context context:primary-signature -v typechar`