Sniffly macOS Packaging Walkthrough
This is a step-by-step guide to package Sniffly for macOS distribution.
Prerequisites
Before starting, make sure you have:
- ✅ Sniffly built successfully (
./build/snifflyexists) - ✅
dylibbundlerinstalled:brew install dylibbundler - ✅ An application icon (optional, but recommended)
- 📝 Apple Developer account (only needed for code signing)
Version Management
The version is controlled by a single VERSION file at the project root. To release a new version:
echo "0.3.0" > VERSION
meson setup build --reconfigure
meson compile -C build
The version automatically appears in:
- The binary output (
./build/sniffly --version) - Info.plist in the app bundle
- The DMG filename
Quick Start: Automated Packaging
# One command to create the .app and .dmg
./scripts/package-macos.sh
This creates:
Sniffly.app- The application bundleSniffly-0.2.0-macOS.dmg- Distributable installer
What The Script Does
- Reads version from
VERSIONfile - Creates
.appbundle structure - Copies the executable
- Creates
Info.plistwith version info - Bundles GTK4 libraries using
dylibbundler - Creates a distributable DMG
Step-by-Step Manual Process
If you want to understand what's happening or customize the process:
Step 1: Build the Release Binary
# Clean build for release
rm -rf build
meson setup build --buildtype=release
meson compile -C build
# Verify it works
./build/sniffly --version
Step 2: Create the App Bundle Structure
mkdir -p Sniffly.app/Contents/MacOS
mkdir -p Sniffly.app/Contents/Resources
mkdir -p Sniffly.app/Contents/Frameworks
Step 3: Copy the Executable
cp build/sniffly Sniffly.app/Contents/MacOS/sniffly
chmod +x Sniffly.app/Contents/MacOS/sniffly
Step 4: Create Info.plist
cat > Sniffly.app/Contents/Info.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>sniffly</string>
<key>CFBundleIdentifier</key>
<string>org.fortrangoingonforty.sniffly</string>
<key>CFBundleName</key>
<string>Sniffly</string>
<key>CFBundleDisplayName</key>
<string>Sniffly</string>
<key>CFBundleVersion</key>
<string>0.2.0</string>
<key>CFBundleShortVersionString</key>
<string>0.2.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleIconFile</key>
<string>sniffly</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
</dict>
</plist>
EOF
Step 5: Add an Icon (Optional but Recommended)
You need a .icns file. If you have a 1024x1024 PNG:
# Create iconset directory
mkdir sniffly.iconset
# Generate all sizes
sips -z 16 16 icon-1024.png --out sniffly.iconset/icon_16x16.png
sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_16x16@2x.png
sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_32x32.png
sips -z 64 64 icon-1024.png --out sniffly.iconset/icon_32x32@2x.png
sips -z 128 128 icon-1024.png --out sniffly.iconset/icon_128x128.png
sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_128x128@2x.png
sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_256x256.png
sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_256x256@2x.png
sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_512x512.png
sips -z 1024 1024 icon-1024.png --out sniffly.iconset/icon_512x512@2x.png
# Convert to .icns
iconutil -c icns sniffly.iconset -o Sniffly.app/Contents/Resources/sniffly.icns
# Cleanup
rm -rf sniffly.iconset
Step 6: Bundle GTK4 Libraries
This is the crucial step that makes your app "just work" on any Mac:
dylibbundler -od -b \
-x Sniffly.app/Contents/MacOS/sniffly \
-d Sniffly.app/Contents/Frameworks/ \
-p @executable_path/../Frameworks/
What this does:
- Finds all dylib dependencies (GTK4, Cairo, GLib, etc.)
- Copies them into
Frameworks/ - Rewrites the executable to look in
@executable_path/../Frameworks/ - Makes the app fully self-contained
Expected size: ~100-150MB (GTK4 is big!)
Step 7: Test the App Bundle
open Sniffly.app
If it works, you're ready to create the DMG!
Step 8: Create the DMG
# Create temporary folder
mkdir dmg_temp
cp -r Sniffly.app dmg_temp/
# Add Applications folder shortcut
ln -s /Applications dmg_temp/Applications
# Create DMG
hdiutil create -volname "Sniffly" \
-srcfolder dmg_temp \
-ov -format UDZO \
Sniffly-0.2.0-macOS.dmg
# Cleanup
rm -rf dmg_temp
Code Signing (Optional but Recommended)
If you have an Apple Developer account:
# Sign the app
codesign --deep --force --verify --verbose \
--sign "Developer ID Application: Your Name (TEAM_ID)" \
Sniffly.app
# Verify signature
codesign -dv Sniffly.app
# Create DMG from signed app
# (repeat Step 8 after signing)
# Notarize (required for distribution outside App Store)
xcrun notarytool submit Sniffly-0.2.0-macOS.dmg \
--apple-id "your@email.com" \
--password "app-specific-password" \
--team-id "YOUR_TEAM_ID" \
--wait
# Staple notarization ticket
xcrun stapler staple Sniffly-0.2.0-macOS.dmg
Why sign and notarize?
- Without: Users see "unidentified developer" warning
- With: Clean installation experience, no warnings
Cost: $99/year for Apple Developer account
Distribution
Once you have Sniffly-0.2.0-macOS.dmg:
- Test on a clean Mac (or VM) without development tools installed
- Upload to your server:
scp Sniffly-0.2.0-macOS.dmg user@yourserver.com:/var/www/downloads/ - Create a download page with:
- Link to the DMG
- System requirements (macOS 11.0+)
- Installation instructions
- Screenshot/demo
Example Download Page
<!DOCTYPE html>
<html>
<head>
<title>Download Sniffly</title>
</head>
<body>
<h1>Download Sniffly v0.2.0</h1>
<p>A fast, visual disk space analyzer for macOS</p>
<a href="Sniffly-0.2.0-macOS.dmg" class="download-button">
Download for macOS (150 MB)
</a>
<h2>Requirements</h2>
<ul>
<li>macOS 11.0 (Big Sur) or later</li>
<li>Apple Silicon or Intel processor</li>
</ul>
<h2>Installation</h2>
<ol>
<li>Download Sniffly-0.2.0-macOS.dmg</li>
<li>Open the DMG file</li>
<li>Drag Sniffly to Applications folder</li>
<li>Launch from Applications</li>
</ol>
</body>
</html>
Release Checklist
Before releasing a new version:
- Update
VERSIONfile - Test on clean macOS installation
- Verify
--versionshows correct version - Test basic functionality (scan, navigation, etc.)
- Build release binary (
--buildtype=release) - Bundle GTK4 libraries with dylibbundler
- Test app bundle works standalone
- Create DMG
- (Optional) Code sign and notarize
- Upload to distribution server
- Update download page
- Announce release (social media, website, etc.)
Troubleshooting
"Sniffly is damaged and can't be opened"
This happens when macOS quarantines unsigned apps. Users can bypass with:
xattr -cr /Applications/Sniffly.app
Or you need to code sign and notarize the app.
App crashes immediately
- Check if GTK4 libraries are bundled:
ls Sniffly.app/Contents/Frameworks/ - Should see many
.dylibfiles - Verify with:
otool -L Sniffly.app/Contents/MacOS/sniffly- Should show
@executable_path/../Frameworks/paths
- Should show
DMG too large (>200MB)
Normal! GTK4 is big. Options:
- Accept it (users download once)
- Require system GTK4 (smaller DMG, but users must
brew install gtk4)
Can't sign the app
You need an Apple Developer account ($99/year). For free distribution:
- Skip signing (users will see warning)
- Document how to bypass Gatekeeper
Version Number Advice
You asked about version numbering. Here's my take:
Current State of Sniffly (0.2.0 suggestion):
- ✅ Core functionality works (scanning, treemap, navigation)
- ✅ Progressive scanning is solid
- ✅ 3D effects, navigation locking
- ⚠️ Some rough edges (had bugs we fixed)
- ⚠️ No installers yet (this guide creates them)
- ❌ Not feature-complete vs original SpaceSniffer
Version recommendations:
- 0.1.0 - Tech preview, barely works
- 0.2.0 - Alpha release (current state) ← I recommend this
- 0.5.0 - Beta release (feature complete, but bugs)
- 0.9.0 - Release candidate
- 1.0.0 - Stable, production-ready
Why 0.2.0?
- Shows progress from initial experiments
- Sets expectations (alpha quality)
- Room to grow before 1.0
- Conservative (you prefer this)
Semantic versioning for future:
0.x.y- Pre-1.0 development1.0.0- First stable release1.1.0- Add features (backwards compatible)1.0.1- Bug fixes2.0.0- Breaking changes
Next Steps
After successful macOS packaging, consider:
- Linux AppImage (simpler than .deb/.rpm)
- Homebrew cask formula (
brew install --cask sniffly) - Automatic updates (Sparkle framework)
- Analytics (crash reporting, usage stats)
- Website with documentation
Questions? The packaging script at scripts/package-macos.sh automates all of this!
View source
| 1 | # Sniffly macOS Packaging Walkthrough |
| 2 | |
| 3 | This is a step-by-step guide to package Sniffly for macOS distribution. |
| 4 | |
| 5 | ## Prerequisites |
| 6 | |
| 7 | Before starting, make sure you have: |
| 8 | - ✅ Sniffly built successfully (`./build/sniffly` exists) |
| 9 | - ✅ `dylibbundler` installed: `brew install dylibbundler` |
| 10 | - ✅ An application icon (optional, but recommended) |
| 11 | - 📝 Apple Developer account (only needed for code signing) |
| 12 | |
| 13 | ## Version Management |
| 14 | |
| 15 | The version is controlled by a single `VERSION` file at the project root. To release a new version: |
| 16 | |
| 17 | ```bash |
| 18 | echo "0.3.0" > VERSION |
| 19 | meson setup build --reconfigure |
| 20 | meson compile -C build |
| 21 | ``` |
| 22 | |
| 23 | The version automatically appears in: |
| 24 | - The binary output (`./build/sniffly --version`) |
| 25 | - Info.plist in the app bundle |
| 26 | - The DMG filename |
| 27 | |
| 28 | ## Quick Start: Automated Packaging |
| 29 | |
| 30 | ```bash |
| 31 | # One command to create the .app and .dmg |
| 32 | ./scripts/package-macos.sh |
| 33 | ``` |
| 34 | |
| 35 | This creates: |
| 36 | - `Sniffly.app` - The application bundle |
| 37 | - `Sniffly-0.2.0-macOS.dmg` - Distributable installer |
| 38 | |
| 39 | ### What The Script Does |
| 40 | |
| 41 | 1. Reads version from `VERSION` file |
| 42 | 2. Creates `.app` bundle structure |
| 43 | 3. Copies the executable |
| 44 | 4. Creates `Info.plist` with version info |
| 45 | 5. Bundles GTK4 libraries using `dylibbundler` |
| 46 | 6. Creates a distributable DMG |
| 47 | |
| 48 | ## Step-by-Step Manual Process |
| 49 | |
| 50 | If you want to understand what's happening or customize the process: |
| 51 | |
| 52 | ### Step 1: Build the Release Binary |
| 53 | |
| 54 | ```bash |
| 55 | # Clean build for release |
| 56 | rm -rf build |
| 57 | meson setup build --buildtype=release |
| 58 | meson compile -C build |
| 59 | |
| 60 | # Verify it works |
| 61 | ./build/sniffly --version |
| 62 | ``` |
| 63 | |
| 64 | ### Step 2: Create the App Bundle Structure |
| 65 | |
| 66 | ```bash |
| 67 | mkdir -p Sniffly.app/Contents/MacOS |
| 68 | mkdir -p Sniffly.app/Contents/Resources |
| 69 | mkdir -p Sniffly.app/Contents/Frameworks |
| 70 | ``` |
| 71 | |
| 72 | ### Step 3: Copy the Executable |
| 73 | |
| 74 | ```bash |
| 75 | cp build/sniffly Sniffly.app/Contents/MacOS/sniffly |
| 76 | chmod +x Sniffly.app/Contents/MacOS/sniffly |
| 77 | ``` |
| 78 | |
| 79 | ### Step 4: Create Info.plist |
| 80 | |
| 81 | ```bash |
| 82 | cat > Sniffly.app/Contents/Info.plist << 'EOF' |
| 83 | <?xml version="1.0" encoding="UTF-8"?> |
| 84 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| 85 | <plist version="1.0"> |
| 86 | <dict> |
| 87 | <key>CFBundleExecutable</key> |
| 88 | <string>sniffly</string> |
| 89 | <key>CFBundleIdentifier</key> |
| 90 | <string>org.fortrangoingonforty.sniffly</string> |
| 91 | <key>CFBundleName</key> |
| 92 | <string>Sniffly</string> |
| 93 | <key>CFBundleDisplayName</key> |
| 94 | <string>Sniffly</string> |
| 95 | <key>CFBundleVersion</key> |
| 96 | <string>0.2.0</string> |
| 97 | <key>CFBundleShortVersionString</key> |
| 98 | <string>0.2.0</string> |
| 99 | <key>CFBundlePackageType</key> |
| 100 | <string>APPL</string> |
| 101 | <key>CFBundleIconFile</key> |
| 102 | <string>sniffly</string> |
| 103 | <key>LSMinimumSystemVersion</key> |
| 104 | <string>11.0</string> |
| 105 | <key>LSApplicationCategoryType</key> |
| 106 | <string>public.app-category.utilities</string> |
| 107 | </dict> |
| 108 | </plist> |
| 109 | EOF |
| 110 | ``` |
| 111 | |
| 112 | ### Step 5: Add an Icon (Optional but Recommended) |
| 113 | |
| 114 | You need a `.icns` file. If you have a 1024x1024 PNG: |
| 115 | |
| 116 | ```bash |
| 117 | # Create iconset directory |
| 118 | mkdir sniffly.iconset |
| 119 | |
| 120 | # Generate all sizes |
| 121 | sips -z 16 16 icon-1024.png --out sniffly.iconset/icon_16x16.png |
| 122 | sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_16x16@2x.png |
| 123 | sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_32x32.png |
| 124 | sips -z 64 64 icon-1024.png --out sniffly.iconset/icon_32x32@2x.png |
| 125 | sips -z 128 128 icon-1024.png --out sniffly.iconset/icon_128x128.png |
| 126 | sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_128x128@2x.png |
| 127 | sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_256x256.png |
| 128 | sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_256x256@2x.png |
| 129 | sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_512x512.png |
| 130 | sips -z 1024 1024 icon-1024.png --out sniffly.iconset/icon_512x512@2x.png |
| 131 | |
| 132 | # Convert to .icns |
| 133 | iconutil -c icns sniffly.iconset -o Sniffly.app/Contents/Resources/sniffly.icns |
| 134 | |
| 135 | # Cleanup |
| 136 | rm -rf sniffly.iconset |
| 137 | ``` |
| 138 | |
| 139 | ### Step 6: Bundle GTK4 Libraries |
| 140 | |
| 141 | This is the crucial step that makes your app "just work" on any Mac: |
| 142 | |
| 143 | ```bash |
| 144 | dylibbundler -od -b \ |
| 145 | -x Sniffly.app/Contents/MacOS/sniffly \ |
| 146 | -d Sniffly.app/Contents/Frameworks/ \ |
| 147 | -p @executable_path/../Frameworks/ |
| 148 | ``` |
| 149 | |
| 150 | What this does: |
| 151 | - Finds all dylib dependencies (GTK4, Cairo, GLib, etc.) |
| 152 | - Copies them into `Frameworks/` |
| 153 | - Rewrites the executable to look in `@executable_path/../Frameworks/` |
| 154 | - Makes the app fully self-contained |
| 155 | |
| 156 | **Expected size**: ~100-150MB (GTK4 is big!) |
| 157 | |
| 158 | ### Step 7: Test the App Bundle |
| 159 | |
| 160 | ```bash |
| 161 | open Sniffly.app |
| 162 | ``` |
| 163 | |
| 164 | If it works, you're ready to create the DMG! |
| 165 | |
| 166 | ### Step 8: Create the DMG |
| 167 | |
| 168 | ```bash |
| 169 | # Create temporary folder |
| 170 | mkdir dmg_temp |
| 171 | cp -r Sniffly.app dmg_temp/ |
| 172 | |
| 173 | # Add Applications folder shortcut |
| 174 | ln -s /Applications dmg_temp/Applications |
| 175 | |
| 176 | # Create DMG |
| 177 | hdiutil create -volname "Sniffly" \ |
| 178 | -srcfolder dmg_temp \ |
| 179 | -ov -format UDZO \ |
| 180 | Sniffly-0.2.0-macOS.dmg |
| 181 | |
| 182 | # Cleanup |
| 183 | rm -rf dmg_temp |
| 184 | ``` |
| 185 | |
| 186 | ## Code Signing (Optional but Recommended) |
| 187 | |
| 188 | If you have an Apple Developer account: |
| 189 | |
| 190 | ```bash |
| 191 | # Sign the app |
| 192 | codesign --deep --force --verify --verbose \ |
| 193 | --sign "Developer ID Application: Your Name (TEAM_ID)" \ |
| 194 | Sniffly.app |
| 195 | |
| 196 | # Verify signature |
| 197 | codesign -dv Sniffly.app |
| 198 | |
| 199 | # Create DMG from signed app |
| 200 | # (repeat Step 8 after signing) |
| 201 | |
| 202 | # Notarize (required for distribution outside App Store) |
| 203 | xcrun notarytool submit Sniffly-0.2.0-macOS.dmg \ |
| 204 | --apple-id "your@email.com" \ |
| 205 | --password "app-specific-password" \ |
| 206 | --team-id "YOUR_TEAM_ID" \ |
| 207 | --wait |
| 208 | |
| 209 | # Staple notarization ticket |
| 210 | xcrun stapler staple Sniffly-0.2.0-macOS.dmg |
| 211 | ``` |
| 212 | |
| 213 | **Why sign and notarize?** |
| 214 | - Without: Users see "unidentified developer" warning |
| 215 | - With: Clean installation experience, no warnings |
| 216 | |
| 217 | **Cost**: $99/year for Apple Developer account |
| 218 | |
| 219 | ## Distribution |
| 220 | |
| 221 | Once you have `Sniffly-0.2.0-macOS.dmg`: |
| 222 | |
| 223 | 1. **Test on a clean Mac** (or VM) without development tools installed |
| 224 | 2. **Upload to your server**: |
| 225 | ```bash |
| 226 | scp Sniffly-0.2.0-macOS.dmg user@yourserver.com:/var/www/downloads/ |
| 227 | ``` |
| 228 | 3. **Create a download page** with: |
| 229 | - Link to the DMG |
| 230 | - System requirements (macOS 11.0+) |
| 231 | - Installation instructions |
| 232 | - Screenshot/demo |
| 233 | |
| 234 | ### Example Download Page |
| 235 | |
| 236 | ```html |
| 237 | <!DOCTYPE html> |
| 238 | <html> |
| 239 | <head> |
| 240 | <title>Download Sniffly</title> |
| 241 | </head> |
| 242 | <body> |
| 243 | <h1>Download Sniffly v0.2.0</h1> |
| 244 | <p>A fast, visual disk space analyzer for macOS</p> |
| 245 | |
| 246 | <a href="Sniffly-0.2.0-macOS.dmg" class="download-button"> |
| 247 | Download for macOS (150 MB) |
| 248 | </a> |
| 249 | |
| 250 | <h2>Requirements</h2> |
| 251 | <ul> |
| 252 | <li>macOS 11.0 (Big Sur) or later</li> |
| 253 | <li>Apple Silicon or Intel processor</li> |
| 254 | </ul> |
| 255 | |
| 256 | <h2>Installation</h2> |
| 257 | <ol> |
| 258 | <li>Download Sniffly-0.2.0-macOS.dmg</li> |
| 259 | <li>Open the DMG file</li> |
| 260 | <li>Drag Sniffly to Applications folder</li> |
| 261 | <li>Launch from Applications</li> |
| 262 | </ol> |
| 263 | </body> |
| 264 | </html> |
| 265 | ``` |
| 266 | |
| 267 | ## Release Checklist |
| 268 | |
| 269 | Before releasing a new version: |
| 270 | |
| 271 | - [ ] Update `VERSION` file |
| 272 | - [ ] Test on clean macOS installation |
| 273 | - [ ] Verify `--version` shows correct version |
| 274 | - [ ] Test basic functionality (scan, navigation, etc.) |
| 275 | - [ ] Build release binary (`--buildtype=release`) |
| 276 | - [ ] Bundle GTK4 libraries with dylibbundler |
| 277 | - [ ] Test app bundle works standalone |
| 278 | - [ ] Create DMG |
| 279 | - [ ] (Optional) Code sign and notarize |
| 280 | - [ ] Upload to distribution server |
| 281 | - [ ] Update download page |
| 282 | - [ ] Announce release (social media, website, etc.) |
| 283 | |
| 284 | ## Troubleshooting |
| 285 | |
| 286 | ### "Sniffly is damaged and can't be opened" |
| 287 | |
| 288 | This happens when macOS quarantines unsigned apps. Users can bypass with: |
| 289 | ```bash |
| 290 | xattr -cr /Applications/Sniffly.app |
| 291 | ``` |
| 292 | |
| 293 | Or you need to code sign and notarize the app. |
| 294 | |
| 295 | ### App crashes immediately |
| 296 | |
| 297 | - Check if GTK4 libraries are bundled: `ls Sniffly.app/Contents/Frameworks/` |
| 298 | - Should see many `.dylib` files |
| 299 | - Verify with: `otool -L Sniffly.app/Contents/MacOS/sniffly` |
| 300 | - Should show `@executable_path/../Frameworks/` paths |
| 301 | |
| 302 | ### DMG too large (>200MB) |
| 303 | |
| 304 | Normal! GTK4 is big. Options: |
| 305 | - Accept it (users download once) |
| 306 | - Require system GTK4 (smaller DMG, but users must `brew install gtk4`) |
| 307 | |
| 308 | ### Can't sign the app |
| 309 | |
| 310 | You need an Apple Developer account ($99/year). For free distribution: |
| 311 | - Skip signing (users will see warning) |
| 312 | - Document how to bypass Gatekeeper |
| 313 | |
| 314 | ## Version Number Advice |
| 315 | |
| 316 | You asked about version numbering. Here's my take: |
| 317 | |
| 318 | **Current State of Sniffly (0.2.0 suggestion)**: |
| 319 | - ✅ Core functionality works (scanning, treemap, navigation) |
| 320 | - ✅ Progressive scanning is solid |
| 321 | - ✅ 3D effects, navigation locking |
| 322 | - ⚠️ Some rough edges (had bugs we fixed) |
| 323 | - ⚠️ No installers yet (this guide creates them) |
| 324 | - ❌ Not feature-complete vs original SpaceSniffer |
| 325 | |
| 326 | **Version recommendations**: |
| 327 | - **0.1.0** - Tech preview, barely works |
| 328 | - **0.2.0** - Alpha release (current state) ← **I recommend this** |
| 329 | - **0.5.0** - Beta release (feature complete, but bugs) |
| 330 | - **0.9.0** - Release candidate |
| 331 | - **1.0.0** - Stable, production-ready |
| 332 | |
| 333 | **Why 0.2.0?** |
| 334 | - Shows progress from initial experiments |
| 335 | - Sets expectations (alpha quality) |
| 336 | - Room to grow before 1.0 |
| 337 | - Conservative (you prefer this) |
| 338 | |
| 339 | **Semantic versioning for future**: |
| 340 | - `0.x.y` - Pre-1.0 development |
| 341 | - `1.0.0` - First stable release |
| 342 | - `1.1.0` - Add features (backwards compatible) |
| 343 | - `1.0.1` - Bug fixes |
| 344 | - `2.0.0` - Breaking changes |
| 345 | |
| 346 | ## Next Steps |
| 347 | |
| 348 | After successful macOS packaging, consider: |
| 349 | - Linux AppImage (simpler than .deb/.rpm) |
| 350 | - Homebrew cask formula (`brew install --cask sniffly`) |
| 351 | - Automatic updates (Sparkle framework) |
| 352 | - Analytics (crash reporting, usage stats) |
| 353 | - Website with documentation |
| 354 | |
| 355 | --- |
| 356 | |
| 357 | **Questions?** The packaging script at `scripts/package-macos.sh` automates all of this! |