Skip to content

Commit 89bdf39

Browse files
committed
chore: improve CI and add release workflow
CI improvements: - Add SPM dependency caching for faster builds - Add concurrency control (cancel in-progress runs) - Add 30-minute timeout - Add release build verification step - Add app bundle creation and codesign verification New release workflow (release.yml): - Triggered by version tags (v*) - Builds release binary and .app bundle - Creates DMG with drag-to-Applications installer - Creates ZIP archive - Generates SHA-256 checksums - Publishes draft GitHub Release with all artifacts New docs/RELEASE.md: - Complete release process documentation - Version checklist and testing steps - CI workflow descriptions - Manual release instructions - Hotfix process - Code signing roadmap
1 parent f68b3b5 commit 89bdf39

3 files changed

Lines changed: 268 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,40 @@ on:
66
pull_request:
77
branches: [main]
88

9+
concurrency:
10+
group: ci-${{ github.ref }}
11+
cancel-in-progress: true
12+
913
jobs:
1014
build-and-test:
1115
name: Build & Test
1216
runs-on: macos-15
17+
timeout-minutes: 30
1318

1419
steps:
1520
- name: Checkout
1621
uses: actions/checkout@v4
1722

18-
- name: Build
23+
- name: Cache SPM dependencies
24+
uses: actions/cache@v4
25+
with:
26+
path: .build
27+
key: spm-${{ runner.os }}-${{ hashFiles('Package.resolved') }}
28+
restore-keys: |
29+
spm-${{ runner.os }}-
30+
31+
- name: Build (Debug)
1932
run: swift build
2033

2134
- name: Test
2235
run: swift test
36+
37+
- name: Build (Release)
38+
run: swift build -c release
39+
40+
- name: Verify app bundle
41+
run: |
42+
./scripts/build.sh release
43+
test -d VocaMac.app || exit 1
44+
codesign -v VocaMac.app
45+
echo "App bundle verified"

.github/workflows/release.yml

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
build-and-release:
13+
name: Build & Release
14+
runs-on: macos-15
15+
timeout-minutes: 30
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Extract version from tag
22+
id: version
23+
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
24+
25+
- name: Update version in build script
26+
run: |
27+
sed -i '' "s/CFBundleVersion.*/<key>CFBundleVersion<\/key>/" scripts/build.sh || true
28+
echo "Building version: ${{ steps.version.outputs.VERSION }}"
29+
30+
- name: Build release app bundle
31+
run: ./scripts/build.sh release
32+
33+
- name: Verify code signature
34+
run: codesign -v --deep --strict VocaMac.app
35+
36+
- name: Run tests
37+
run: swift test
38+
39+
- name: Create DMG
40+
run: |
41+
mkdir -p dmg-staging
42+
cp -R VocaMac.app dmg-staging/
43+
ln -s /Applications dmg-staging/Applications
44+
45+
hdiutil create -volname "VocaMac" \
46+
-srcfolder dmg-staging \
47+
-ov -format UDZO \
48+
"VocaMac-${{ steps.version.outputs.VERSION }}-arm64.dmg"
49+
50+
echo "DMG created:"
51+
ls -lh VocaMac-*.dmg
52+
53+
- name: Create ZIP archive
54+
run: |
55+
ditto -c -k --sequesterRsrc --keepParent VocaMac.app \
56+
"VocaMac-${{ steps.version.outputs.VERSION }}-arm64.zip"
57+
58+
echo "ZIP created:"
59+
ls -lh VocaMac-*.zip
60+
61+
- name: Generate checksums
62+
run: |
63+
shasum -a 256 VocaMac-*.dmg VocaMac-*.zip > checksums.txt
64+
cat checksums.txt
65+
66+
- name: Create GitHub Release
67+
uses: softprops/action-gh-release@v2
68+
with:
69+
generate_release_notes: true
70+
draft: true
71+
files: |
72+
VocaMac-*.dmg
73+
VocaMac-*.zip
74+
checksums.txt
75+
body: |
76+
## VocaMac ${{ steps.version.outputs.VERSION }}
77+
78+
### Installation
79+
80+
1. Download `VocaMac-${{ steps.version.outputs.VERSION }}-arm64.dmg`
81+
2. Open the DMG and drag VocaMac to Applications
82+
3. Open VocaMac from Applications
83+
4. Grant Microphone, Accessibility, and Input Monitoring permissions when prompted
84+
85+
### Checksums (SHA-256)
86+
See `checksums.txt` for verification.
87+
88+
### Requirements
89+
- macOS 13 (Ventura) or later
90+
- Apple Silicon (arm64)

docs/RELEASE.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# VocaMac Release Process
2+
3+
## Overview
4+
5+
VocaMac uses GitHub Actions for CI/CD. Releases are triggered by pushing a version tag (e.g., `v0.1.0`). The release workflow builds the app, creates a DMG and ZIP archive, generates checksums, and publishes a draft GitHub Release.
6+
7+
## Versioning
8+
9+
We follow [Semantic Versioning](https://semver.org/):
10+
11+
```
12+
vMAJOR.MINOR.PATCH
13+
```
14+
15+
- **MAJOR** - Breaking changes or significant redesigns
16+
- **MINOR** - New features, backward compatible
17+
- **PATCH** - Bug fixes, minor improvements
18+
19+
Pre-release versions use suffixes: `v0.1.0-alpha`, `v0.1.0-beta.1`
20+
21+
## Release Checklist
22+
23+
### Before Tagging
24+
25+
1. **Ensure all PRs are merged** into `main`
26+
2. **Verify CI passes** on the latest `main` commit
27+
3. **Update version number** in `scripts/build.sh` (both `CFBundleVersion` and `CFBundleShortVersionString`)
28+
4. **Test locally**:
29+
```bash
30+
./scripts/build.sh release
31+
open VocaMac.app
32+
```
33+
5. **Verify core functionality**:
34+
- App appears in menu bar
35+
- Push-to-talk recording works
36+
- Transcription produces correct text
37+
- Text injection works at cursor
38+
- Settings dialog opens and all tabs function
39+
- Model download and switching works
40+
- Sound effects play on start/stop
41+
6. **Review README** for accuracy
42+
43+
### Creating a Release
44+
45+
1. **Tag the release**:
46+
```bash
47+
git tag -a v0.1.0 -m "VocaMac v0.1.0 - Alpha Release"
48+
git push origin v0.1.0
49+
```
50+
51+
2. **GitHub Actions automatically**:
52+
- Builds the release binary
53+
- Creates `VocaMac.app` bundle with ad-hoc signing
54+
- Packages as DMG (`VocaMac-0.1.0-arm64.dmg`)
55+
- Packages as ZIP (`VocaMac-0.1.0-arm64.zip`)
56+
- Generates SHA-256 checksums
57+
- Creates a **draft** GitHub Release with all artifacts
58+
59+
3. **Review the draft release** at https://github.com/jatinkrmalik/vocamac/releases
60+
- Edit release notes if needed
61+
- Verify artifacts are attached
62+
- **Publish** the release when ready
63+
64+
4. **Website auto-deploys** when the release is published (via `deploy-website.yml`)
65+
66+
## Release Artifacts
67+
68+
Each release produces:
69+
70+
| Artifact | Description |
71+
|----------|-------------|
72+
| `VocaMac-X.Y.Z-arm64.dmg` | DMG disk image with drag-to-Applications installer |
73+
| `VocaMac-X.Y.Z-arm64.zip` | ZIP archive of VocaMac.app |
74+
| `checksums.txt` | SHA-256 checksums for verification |
75+
76+
## Architecture Support
77+
78+
Currently **Apple Silicon (arm64) only**. The build runs on `macos-15` runners which are arm64.
79+
80+
For universal binary support (arm64 + x86_64) in the future:
81+
```bash
82+
swift build -c release --arch arm64 --arch x86_64
83+
```
84+
85+
## Code Signing
86+
87+
### Current (Alpha)
88+
89+
- **Ad-hoc signing** (no Apple Developer certificate)
90+
- Users must manually grant permissions after each install
91+
- Gatekeeper will show "unidentified developer" warning
92+
- Users bypass with: Right-click > Open > Open
93+
94+
### Future (GA Release)
95+
96+
- Sign with Apple Developer ID certificate
97+
- Notarize with Apple for Gatekeeper clearance
98+
- No security warnings for end users
99+
- Permissions persist across updates
100+
101+
See [Issue #27](https://github.com/jatinkrmalik/vocamac/issues/27) for tracking.
102+
103+
## CI Workflows
104+
105+
### `ci.yml` - Build & Test
106+
107+
- **Triggers**: Push to `main`, pull requests to `main`
108+
- **Steps**: Debug build, test suite, release build, app bundle verification
109+
- **Caching**: SPM dependencies cached for faster builds
110+
- **Concurrency**: Cancels in-progress runs for the same branch
111+
112+
### `release.yml` - Release
113+
114+
- **Triggers**: Push of version tags (`v*`)
115+
- **Steps**: Build, test, create DMG + ZIP, generate checksums, create draft release
116+
- **Output**: Draft GitHub Release with downloadable artifacts
117+
118+
### `deploy-website.yml` - Website
119+
120+
- **Triggers**: Release published, manual dispatch
121+
- **Steps**: Deploy `web/` directory to GitHub Pages
122+
123+
## Manual Release (Without CI)
124+
125+
If you need to create a release locally:
126+
127+
```bash
128+
# Build the app
129+
./scripts/build.sh release
130+
131+
# Create DMG
132+
mkdir -p dmg-staging
133+
cp -R VocaMac.app dmg-staging/
134+
ln -s /Applications dmg-staging/Applications
135+
hdiutil create -volname "VocaMac" -srcfolder dmg-staging -ov -format UDZO "VocaMac-0.1.0-arm64.dmg"
136+
137+
# Create ZIP
138+
ditto -c -k --sequesterRsrc --keepParent VocaMac.app "VocaMac-0.1.0-arm64.zip"
139+
140+
# Generate checksums
141+
shasum -a 256 VocaMac-*.dmg VocaMac-*.zip > checksums.txt
142+
```
143+
144+
Then upload the artifacts manually to the GitHub Release page.
145+
146+
## Hotfix Process
147+
148+
For critical bugs in a released version:
149+
150+
1. Create a branch from the release tag: `git checkout -b fix/critical-bug v0.1.0`
151+
2. Fix the bug, commit, push
152+
3. Create a PR to `main`
153+
4. After merge, tag a patch release: `v0.1.1`
154+
5. Push the tag to trigger the release workflow

0 commit comments

Comments
 (0)