Add the TZGyn dev skill
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
---
|
||||
name: TZGyn-skill
|
||||
description: Use for any TZGyn development workflow — starting a new project on Gitea, publishing to the TZGyn Homebrew tap (casks for GUI apps, formulae for CLIs), signing and notarizing a desktop/mobile build, cutting a release, or extending/updating this skill itself.
|
||||
---
|
||||
|
||||
# TZGyn dev skill
|
||||
|
||||
Personal conventions for TZGyn projects. Source of truth is the Git repo
|
||||
`https://gitea.tzgyn.dev/TZGyn/TZGyn-skill`; it is installed as a global Crush
|
||||
skill at `~/.agents/skills/TZGyn-skill` (a symlink to the checkout, so editing
|
||||
the repo updates the live skill).
|
||||
|
||||
The reference implementations to copy from are `~/Git/kapture` and
|
||||
`~/Git/konference` (macOS menu-bar apps).
|
||||
|
||||
## Ground rules
|
||||
|
||||
- **Host**: Gitea at `https://gitea.tzgyn.dev`, owner `TZGyn`, `tea` login name
|
||||
`gitea.tzgyn.dev`. SSH: `git@gitea.tzgyn.dev:TZGyn/<repo>.git`.
|
||||
- **Always print the upgrade script** (the `brew` commands below) in the final
|
||||
answer after any release or version bump. The user wants it every time.
|
||||
- Never commit build artifacts. `.gitignore` covers `.build/`, `*.app/`,
|
||||
`dist/`, `.swiftpm/`, `xcuserdata/`, `DerivedData/`, `.DS_Store`.
|
||||
- Never commit secrets (`.p8`, API keys) or push to a remote unless asked.
|
||||
- Commit messages: concise, why-focused, first line < 72 chars; add a body only
|
||||
when it helps a future reader.
|
||||
- Prefer SwiftPM (Swift/SwiftUI) for macOS apps; no external dependencies.
|
||||
|
||||
## 1. New project on Gitea
|
||||
|
||||
```bash
|
||||
# 1. Create the repo (as the authenticated user).
|
||||
tea repos create --name <repo> --desc "<one-line description>" --login gitea.tzgyn.dev
|
||||
# Do NOT pass --owner TZGyn: that hits /orgs/TZGyn/repos and 404s for a user.
|
||||
|
||||
# 2. Initialise and push.
|
||||
cd ~/Git/<repo>
|
||||
git init -b main
|
||||
git add -A && git commit -m "<why>"
|
||||
git remote add origin https://gitea.tzgyn.dev/TZGyn/<repo>.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
Scaffold every repo with `README.md`, `AGENTS.md` (architecture + commands +
|
||||
gotchas), and `.gitignore`. See `~/Git/simpletopbar` for the minimal shape:
|
||||
`Package.swift`, `Sources/<Name>/*.swift`, `build-app.sh`, `release.sh`,
|
||||
`scripts/`.
|
||||
|
||||
## 2. Homebrew tap
|
||||
|
||||
- The tap clone lives at **`~/.cache/homebrew-tap`**
|
||||
(origin `https://gitea.tzgyn.dev/TZGyn/homebrew-tap.git`).
|
||||
- Layout: **GUI apps → `Casks/<name>.rb`**; **CLI tools → `Formula/<name>.rb`**.
|
||||
- After pushing, sync Homebrew's own clone:
|
||||
|
||||
```bash
|
||||
git -C ~/.cache/homebrew-tap add -A
|
||||
git -C ~/.cache/homebrew-tap commit -m "Add <name> cask"
|
||||
git -C ~/.cache/homebrew-tap push
|
||||
git -C /opt/homebrew/Library/Taps/tzgyn/homebrew-tap pull --ff-only
|
||||
brew audit --cask tzgyn/tap/<name> # or: brew audit --formula tzgyn/tap/<name>
|
||||
```
|
||||
|
||||
- Users tap once (non-GitHub taps need the full URL):
|
||||
|
||||
```bash
|
||||
brew tap TZGyn/tap https://gitea.tzgyn.dev/TZGyn/homebrew-tap.git
|
||||
brew trust TZGyn/tap
|
||||
```
|
||||
|
||||
### Cask (GUI app)
|
||||
|
||||
```ruby
|
||||
cask "<name>" do
|
||||
version "X.Y.Z"
|
||||
sha256 "<sha256 of the release zip>"
|
||||
|
||||
url "https://gitea.tzgyn.dev/TZGyn/<repo>/releases/download/v#{version}/<Asset>.zip"
|
||||
name "<Display Name>"
|
||||
desc "<one line>"
|
||||
homepage "https://gitea.tzgyn.dev/TZGyn/<repo>"
|
||||
|
||||
app "<Name>.app"
|
||||
end
|
||||
```
|
||||
|
||||
### Formula (CLI)
|
||||
|
||||
```ruby
|
||||
class <Name> < Formula
|
||||
desc "<one line>"
|
||||
homepage "https://gitea.tzgyn.dev/TZGyn/<repo>"
|
||||
url "https://gitea.tzgyn.dev/TZGyn/<repo>/releases/download/v0.1.0/<asset>-0.1.0-darwin-arm64.tar.gz"
|
||||
sha256 "<sha256 of the tarball>"
|
||||
version "0.1.0"
|
||||
|
||||
def install
|
||||
bin.install "<binary>"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## 3. Releasing a macOS GUI app
|
||||
|
||||
Two scripts per repo: `build-app.sh <version>` and `release.sh <version>`.
|
||||
|
||||
1. **Build + sign** (`build-app.sh`): `swift build -c release` → handwritten
|
||||
`Info.plist` (NOT generated from `Package.swift`) → `make-icon.sh` → sign
|
||||
with the Developer ID identity resolved from the login keychain
|
||||
(`security find-identity -v -p codesigning | awk '/Developer ID Application:/{print $2;exit}'`),
|
||||
falling back to ad-hoc (`-`). GUI apps set `LSUIElement = true` and
|
||||
`CFBundleIdentifier = dev.tzgyn.<name>`.
|
||||
2. **Notarize + staple + zip** (`release.sh`): build → `ditto -c -k --keepParent
|
||||
<App>.app dist/<App>.zip` → submit to Apple → staple → re-zip → print sha256.
|
||||
See [Notarization](#4-notarization) for the credentials.
|
||||
3. **Publish the release** and attach the zip:
|
||||
|
||||
```bash
|
||||
tea releases create --tag v<version> --title "<App> <version>" \
|
||||
--note "<what changed>" --asset dist/<App>.zip \
|
||||
--repo TZGyn/<repo> --login gitea.tzgyn.dev
|
||||
```
|
||||
|
||||
4. **Bump the cask** (`version` + `sha256`), push the tap, pull Homebrew's
|
||||
clone, `brew audit`. Verify by downloading the asset back and comparing the
|
||||
sha256, and `spctl -a -vv <App>.app` → `accepted` /
|
||||
`source=Notarized Developer ID`.
|
||||
|
||||
5. **Print the upgrade script**:
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew upgrade --cask <name>
|
||||
```
|
||||
|
||||
## 4. Notarization
|
||||
|
||||
Signing identity: `Developer ID Application: Teh Zi Gyn (RX6767S6BW)`.
|
||||
App Store Connect API key (shared with Kapture):
|
||||
|
||||
| Value | Where |
|
||||
| --------- | ------------------------------------------------------------ |
|
||||
| Key file | `~/.config/kapture/AuthKey_K5KXMMSB28.p8` |
|
||||
| Key ID | `K5KXMMSB28` |
|
||||
| Issuer ID | `3e25e5dc-8b75-453f-a71c-c7f3feaac628` (not secret) |
|
||||
| Team ID | `RX6767S6BW` |
|
||||
|
||||
Signing must use a hardened runtime and a secure timestamp:
|
||||
|
||||
```bash
|
||||
codesign --force --options runtime --timestamp --sign "$IDENT" "<App>.app"
|
||||
```
|
||||
|
||||
Submit and verify:
|
||||
|
||||
```bash
|
||||
xcrun notarytool submit dist/<App>.zip \
|
||||
--key "$HOME/.config/kapture/AuthKey_K5KXMMSB28.p8" \
|
||||
--key-id K5KXMMSB28 --issuer 3e25e5dc-8b75-453f-a71c-c7f3feaac628 \
|
||||
--wait --output-format json
|
||||
xcrun stapler staple <App>.app && xcrun stapler validate <App>.app
|
||||
spctl -a -vv <App>.app # want: accepted, source=Notarized Developer ID
|
||||
```
|
||||
|
||||
Wire `release.sh` so it notarizes when `NOTARY_*` env vars are set and otherwise
|
||||
skips gracefully (build + zip + sha only). An unnotarized app is still signed but
|
||||
Gatekeeper warns on other machines; the user can bypass with
|
||||
`xattr -dr com.apple.quarantine <App>.app` or install with
|
||||
`brew install --cask --no-quarantine <name>`.
|
||||
|
||||
## 5. Mobile
|
||||
|
||||
For mobile (iOS/Android), notarization does not apply; the equivalent is store
|
||||
signing (App Store Connect / Play Console) or EAS. Ask for the specific stack
|
||||
before assuming; this skill currently only codifies the macOS path.
|
||||
|
||||
## 6. Extending this skill (self-improvement)
|
||||
|
||||
This skill is meant to grow. When the user asks for a new behaviour, preference,
|
||||
or convention:
|
||||
|
||||
1. Edit the relevant section of `SKILL.md` (add a new section if it is a new
|
||||
topic). Keep it concrete: exact commands, paths, and templates.
|
||||
2. Keep it truthful — only document what has actually worked; delete steps that
|
||||
became stale (e.g. a changed path or flag).
|
||||
3. Commit and push:
|
||||
|
||||
```bash
|
||||
cd ~/Git/TZGyn-skill
|
||||
git add -A && git commit -m "<what behaviour was added>"
|
||||
git push
|
||||
```
|
||||
|
||||
4. The installed copy is a symlink, so it updates immediately; a new Crush
|
||||
session picks it up. Confirm with `crush_info` (the skill should appear under
|
||||
`[skills]`).
|
||||
|
||||
Prefer updating this skill over repeating ad-hoc instructions in a project: if a
|
||||
convention applies to more than one repo, it belongs here.
|
||||
Reference in New Issue
Block a user