Skip to content

Commit bdf0504

Browse files
GordonBeemingclaudegitbutler-client
authored
feat: Use release version and run number in app bundle (#10)
* feat: Use release version and run number in app bundle Build script now accepts version and build number args. CFBundleShortVersionString = tag version (e.g., 0.2.0) CFBundleVersion = version.runNumber (e.g., 0.2.0.42) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com> * fix: Use major.minor.runNumber format (e.g., 0.2.42) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com> * fix: Strip patch from tag in CI, pass major.minor to build script Tag v0.2.0 → VERSION=0.2 → bundle version 0.2.42 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com> * fix: Use v0.2 tag format — no patch number in tags Tags are v{major}.{minor}, run number becomes the patch. Tag v0.2 + run 42 → bundle version 0.2.42 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com> * docs: Add CLAUDE.md project conventions and create-release skill Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com> * fix: Address PR review feedback - Update build-release.sh usage docs with new args - Add version and build number validation with clear errors - Include run_attempt in build number for unique reruns Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com> * fix: Stage remaining build-release.sh changes Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
1 parent 16b3bef commit bdf0504

4 files changed

Lines changed: 185 additions & 5 deletions

File tree

.claude/skills/create-release.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
description: Create a new draft release for Insomnia. Use when the user says "create a release", "new release", "cut a release", or "ship it".
3+
---
4+
5+
# Create Release
6+
7+
Create a new GitHub release for Insomnia with the correct versioning.
8+
9+
## Steps
10+
11+
1. Pull latest: `but pull`
12+
2. Determine the next version by checking existing releases:
13+
```bash
14+
gh release list --repo gordonbeeming/insomnia --limit 5
15+
```
16+
3. Bump the minor version (e.g., v0.2 → v0.3). Never use a patch number in the tag.
17+
4. Create the release:
18+
```bash
19+
gh release create v{major}.{minor} \
20+
--repo gordonbeeming/insomnia \
21+
--target main \
22+
--title "v{major}.{minor} — {short description}" \
23+
--notes "$(cat <<'EOF'
24+
# Insomnia v{major}.{minor} — {short description}
25+
26+
## What's New
27+
28+
- {list changes since last release using git log}
29+
30+
## Install
31+
32+
```bash
33+
brew upgrade --cask gordonbeeming/tap/insomnia
34+
```
35+
36+
Or download the DMG and CLI binary from the assets below.
37+
EOF
38+
)"
39+
```
40+
5. The release pipeline will automatically:
41+
- Build + test
42+
- Sign with Developer ID
43+
- Notarize with Apple
44+
- Create DMG
45+
- Upload assets to the release
46+
- Update the Homebrew tap cask
47+
6. Report the release URL and pipeline run to the user
48+
49+
## Version Format
50+
51+
- Tags: `v{major}.{minor}` (e.g., `v0.3`) — NO patch number
52+
- Bundle version: `{major}.{minor}.{runNumber}` — CI adds the run number as patch
53+
- The tag `v0.3` with run number 45 produces bundle version `0.3.45`
54+
55+
## Generating Release Notes
56+
57+
Use git log to find changes since the last release tag:
58+
```bash
59+
LAST_TAG=$(gh release list --repo gordonbeeming/insomnia --limit 1 --json tagName --jq '.[0].tagName')
60+
git log ${LAST_TAG}..HEAD --oneline
61+
```
62+
63+
## Important
64+
65+
- Never reuse or delete existing release tags
66+
- Always bump the minor version for new releases
67+
- Never use `.0` patch in tags (v0.3 not v0.3.0)
68+
- The release triggers the full CI pipeline — wait for it to complete before telling the user to `brew upgrade`

.github/workflows/build.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,13 @@ jobs:
9090
# Runs the build script that produces the CLI binary and .app bundle
9191
- name: Build release artifacts
9292
run: |
93+
# Extract version from the release tag (e.g., "v0.2" → "0.2")
94+
VERSION="${GITHUB_REF_NAME#v}"
9395
# Make the script executable (git may not preserve the +x bit)
9496
chmod +x Scripts/build-release.sh
95-
# Run the build — outputs to Distribution/
96-
./Scripts/build-release.sh
97+
# Run the build with version and a unique run+attempt number for the bundle
98+
# run_attempt ensures reruns of the same workflow produce unique bundle versions
99+
./Scripts/build-release.sh "${VERSION}" "${{ github.run_number }}.${{ github.run_attempt }}"
97100
98101
# --- Import signing certificate ----------------------------------------
99102
# The Developer ID certificate is stored as a base64-encoded secret.

CLAUDE.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Insomnia
2+
3+
A macOS caffeinate utility — menu bar app + CLI. Mascot: Dapple the Mushroom.
4+
5+
## Tech Stack
6+
7+
- Swift (SwiftUI + AppKit hybrid)
8+
- macOS 14+, Apple Silicon (arm64) only
9+
- IOKit power assertions (native, no shelling out to `caffeinate`)
10+
- Swift ArgumentParser for CLI
11+
- XCTest for unit + integration tests
12+
13+
## Project Structure
14+
15+
Three targets in `Package.swift`:
16+
- `InsomniaCore` — shared library (power management, scheduling, IPC, models)
17+
- `Insomnia` — macOS GUI app (MenuBarExtra + AppKit)
18+
- `InsomniaCLI` — CLI tool
19+
20+
## Build & Test
21+
22+
```bash
23+
swift build # debug build
24+
swift test # 118+ tests
25+
swift run Insomnia # run GUI locally
26+
swift run InsomniaCLI status # run CLI
27+
```
28+
29+
## Code Comments
30+
31+
85%+ comment coverage required. Every file needs:
32+
- File header explaining purpose
33+
- `///` doc comments on public APIs
34+
- Inline comments explaining intent on significant code blocks
35+
36+
## Versioning
37+
38+
- Tags: `v{major}.{minor}` (e.g., `v0.2`, `v0.3`) — no patch number
39+
- Build number: GitHub Actions run number becomes the patch
40+
- Bundle version: `{major}.{minor}.{runNumber}` (e.g., `0.2.42`)
41+
- The `.0` patch in tags is meaningless — never use `v0.2.0`
42+
43+
## Distribution
44+
45+
- Homebrew: `brew install --cask gordonbeeming/tap/insomnia`
46+
- Always use fully qualified name (upstream "Insomnia" API client conflicts)
47+
- Cask auto-updated by CI on release via deploy key
48+
- GitHub Releases: signed + notarized DMG + CLI binary
49+
- No App Store (IOKit needs unsandboxed access)
50+
51+
## CI/CD
52+
53+
- `.github/workflows/build.yml`
54+
- Push/PR: build + test only
55+
- Release (published): build + test + sign + notarize + DMG + upload + update Homebrew tap
56+
- Secrets: `DEVELOPER_ID_CERTIFICATE`, `DEVELOPER_ID_PASSWORD`, `APPLE_ID`, `APPLE_TEAM_ID`, `APPLE_APP_PASSWORD`, `HOMEBREW_TAP_DEPLOY_KEY`
57+
58+
## Dev vs Prod
59+
60+
Debug builds use separate identifiers (`BuildEnvironment.swift`):
61+
- App name: "Insomnia Dev"
62+
- IPC socket: `~/Library/Application Support/Insomnia Dev/insomnia.sock`
63+
- UserDefaults prefix: `com.insomnia.dev.*`
64+
- "(Dev)" suffix in status text
65+
66+
This lets dev and prod run side-by-side.
67+
68+
## Key Files
69+
70+
- `Sources/InsomniaCore/Power/PowerAssertionManager.swift` — IOKit assertion lifecycle
71+
- `Sources/InsomniaCore/IPC/IPCServer.swift` — Unix domain socket server
72+
- `Sources/InsomniaCore/Models/BuildEnvironment.swift` — dev/prod separation
73+
- `Sources/Insomnia/InsomniaApp.swift`@main, MenuBarExtra scene
74+
- `Sources/Insomnia/Views/MenuBarView.swift` — main dropdown menu
75+
- `Sources/Insomnia/AppDelegate.swift` — lifecycle, IPC server, icon loading
76+
- `Scripts/build-release.sh` — builds CLI + .app bundle (args: version, build number)
77+
- `Resources/AppIcon.icns` — Dapple mushroom icon
78+
79+
## Window Focus Pattern
80+
81+
LSUIElement apps need special handling to show windows:
82+
1. `NSApp.setActivationPolicy(.regular)` before opening
83+
2. `openWindow(id:)` to open
84+
3. Reapply app icon via `AppDelegate.reapplyAppIcon()`
85+
4. `NSApp.activate(ignoringOtherApps: true)` after short delay
86+
5. Return to `.accessory` in `onDisappear` (unless dock icon enabled)

Scripts/build-release.sh

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@
77
# project root.
88
#
99
# Usage:
10-
# ./Scripts/build-release.sh
10+
# ./Scripts/build-release.sh <version> <build-number>
11+
#
12+
# Arguments:
13+
# version — major.minor version string (e.g., "0.2"). Defaults to "1.0".
14+
# build-number — numeric build number (e.g., GitHub Actions run number). Defaults to "1".
15+
#
16+
# Example:
17+
# ./Scripts/build-release.sh 0.2 42 # → bundle version 0.2.42
1118
#
1219
# Prerequisites:
1320
# - Xcode command-line tools (swift, xcodebuild)
@@ -39,6 +46,22 @@ GUI_TARGET="Insomnia"
3946
ARCH="arm64"
4047
# Build configuration — Release enables optimizations and strips debug symbols
4148
BUILD_CONFIG="release"
49+
# Version from the first argument (e.g., "0.2"), defaults to "1.0"
50+
APP_VERSION="${1:-1.0}"
51+
# Build number from the second argument (e.g., GitHub Actions run number), defaults to "1"
52+
BUILD_NUMBER="${2:-1}"
53+
54+
# --- Validate inputs ----------------------------------------------------------
55+
# Ensure version looks like digits.digits (e.g., "0.2", "1.0")
56+
if [[ ! "${APP_VERSION}" =~ ^[0-9]+\.[0-9]+$ ]]; then
57+
echo "❌ Invalid version '${APP_VERSION}' — expected format: major.minor (e.g., 0.2)" >&2
58+
exit 1
59+
fi
60+
# Ensure build number is numeric (may contain dots for run_attempt, e.g., "42.1")
61+
if [[ ! "${BUILD_NUMBER}" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
62+
echo "❌ Invalid build number '${BUILD_NUMBER}' — expected numeric (e.g., 42 or 42.1)" >&2
63+
exit 1
64+
fi
4265

4366
# --- Clean previous artifacts ------------------------------------------------
4467
echo "🧹 Cleaning previous Distribution/ contents..."
@@ -145,9 +168,9 @@ cat > "${CONTENTS_DIR}/Info.plist" <<PLIST
145168
<key>CFBundleExecutable</key>
146169
<string>${GUI_TARGET}</string>
147170
<key>CFBundleVersion</key>
148-
<string>1.0.0</string>
171+
<string>${APP_VERSION}.${BUILD_NUMBER}</string>
149172
<key>CFBundleShortVersionString</key>
150-
<string>1.0.0</string>
173+
<string>${APP_VERSION}.${BUILD_NUMBER}</string>
151174
<key>CFBundlePackageType</key>
152175
<string>APPL</string>
153176
<key>CFBundleIconFile</key>

0 commit comments

Comments
 (0)