Complete guide for building, testing, and distributing Pindrop.
- Xcode 15+ with Command Line Tools
- macOS 14+ (Sonoma or later)
- just - Command runner (
brew install just)
The development pipeline is native macOS only: Xcode builds the app, SwiftPM resolves dependencies, and just wraps the common workflows.
- create-dmg - DMG creation (
brew install create-dmg) - Apple Developer Account - Required for the default signed release/export workflow and notarization
- swiftlint - Code linting (
brew install swiftlint) - swiftformat - Code formatting (
brew install swiftformat)
Check installed tools:
just check-toolsjust build
just test
just runjust build uses Xcode-managed signing. If you do not have a signing certificate configured, use just build-unsigned instead.
just build # Debug build with signing
just build-unsigned # Debug build without signing
just test # Run test suite
just test-coverage # Run tests with coverage
just dev # Clean + build + test
just build-unsigned # Debug build without signing (CI/fallback)just build-release # Release build
just export-app # Archive + export Developer ID-signed app
just dmg # Export signed app + create DMG
just dmg-self-signed # Fallback self-signed DMG (only if Apple signing is unavailable)
just appcast dist/Pindrop.dmg # Generate appcast.xml for DMG
just release-notes 1.9.0 # Create draft release notes file
just release 1.9.0 # Manual GitHub release workflow (local)just clean # Remove build artifacts
just lint # Lint Swift code
just format # Format Swift codeFor local testing and development:
just devThis runs:
clean- Remove old artifactsbuild- Debug buildtest- Run test suite
For creating a local Release build with Xcode-managed signing:
just build-releaseOutput: DerivedData/Build/Products/Release/Pindrop.app
For a public Developer ID-signed app bundle:
just export-appThis runs:
archive- Create an Xcode archivexcodebuild -exportArchive- Export a Developer ID-signed app with automatic signing
Output: DerivedData/Build/Products/Release/Pindrop.app
For distribution to users:
just dmgThis runs:
export-app- Export Developer ID-signed appcreate-dmg.sh- Package into DMG
Output: dist/Pindrop.dmg
For maintainer releases (local machine, not CI):
just release 1.9.0This runs:
- Ensure contextual release notes exist (
release-notes/vX.Y.Z.md) - Bump version/build in
project.pbxproj(if needed) - Commit version bump (if needed)
just testjust dmgjust appcast dist/Pindrop.dmg- Create and push tag (
vX.Y.Z) - Create GitHub release with notes + DMG +
appcast.xmlviagh
Optional notarization/stapling for signed distribution:
just notarize dist/Pindrop.dmg
just staple dist/Pindrop.dmg-
Sign into Xcode with your Apple Developer account and enable automatic signing for the
Pindroptarget. -
Verify signing identities:
security find-identity -v -p codesigningjust export-app uses scripts/ExportOptions.plist with method=developer-id and automatic signing, so it defaults to the team used for the archive.
just signUse this only for manual re-signing; the default release flow goes through just export-app / just dmg.
just verify-signature-
Create app-specific password at appleid.apple.com
-
Store credentials:
xcrun notarytool store-credentials "notarytool-password" \
--apple-id "your@email.com" \
--team-id "YOUR_TEAM_ID" \
--password "app-specific-password"just notarize dist/Pindrop.dmgjust staple dist/Pindrop.dmgjust versionjust bump-patch # 1.0.0 → 1.0.1
just bump-minor # Manual: 1.0.0 → 1.1.0just testjust test-coveragexcodebuild test \
-project Pindrop.xcodeproj \
-scheme Pindrop \
-destination 'platform=macOS'name: Build and Test
on: [push, pull_request]
jobs:
build:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Install just
run: brew install just
- name: Run CI workflow
run: just ciThe just ci command runs:
cleanbuild-unsigned(Debug)test-unsignedbuild-release-unsigned
just clean
just buildjust clean
just testCheck requirements:
brew install create-dmg
just export-app
just dmg-quickVerify certificate:
security find-identity -v -p codesigningCheck credentials:
xcrun notarytool history --keychain-profile "notarytool-password"pindrop/
├── DerivedData/ # Local build + export output
│ └── Build/Products/Release/Pindrop.app
├── dist/ # Distribution files
│ └── Pindrop.dmg
├── scripts/ # Build scripts
│ ├── create-dmg.sh
│ ├── create-dmg-self-signed.sh
│ ├── sign-app-bundle.sh
│ └── ExportOptions.plist
├── justfile # Build commands
└── Pindrop.xcodeproj # Xcode project
just show-settingsjust archive
just export-appjust xcode- Use
justfor everything - Consistent, documented commands - Run tests before committing -
just test - Clean before release builds -
just clean build-release - Verify signatures -
just verify-signature - Test DMG on clean Mac - Before distribution