The current builds are unsigned, which means:
- App functions perfectly on your own Mac
- You can run it locally without issues
- All features work correctly
- Automated builds work in CI/CD
- Gatekeeper will block it when other users download it
- Users see: "Jiggler cannot be opened because it is from an unidentified developer"
- No automatic security verification
- Not listed in Security & Privacy settings properly
When users download the unsigned app:
- Double-click Jiggler.app → Gatekeeper blocks it
- Error message: "Jiggler cannot be opened because it is from an unidentified developer"
- Workaround required:
- Right-click → Open
- Click "Open" in the dialog
- Or: System Settings → Privacy & Security → Click "Open Anyway"
This is annoying but not a blocker - users CAN still run it, just with extra steps.
- App runs immediately when downloaded
- No scary warning messages
- Users trust it more
- Professional appearance
- Required for Mac App Store distribution
- Apple verifies the app is malware-free
- Gatekeeper allows it to run immediately
- Better user experience
- Builds user trust
You need:
- Apple Developer Account ($99/year)
- Sign up at: https://developer.apple.com/programs/
- Developer ID Certificate
- Type: "Developer ID Application"
- For distributing outside the Mac App Store
- Xcode with your Apple ID signed in
# Open Xcode
# Go to: Xcode → Settings → Accounts
# Click your Apple ID → Manage Certificates
# Click + → Select "Developer ID Application"
# Certificate will be downloaded automaticallyIn Xcode:
- Open
Jiggler.xcodeproj - Select project → Jiggler target → Signing & Capabilities
- Enable "Automatically manage signing"
- Select your Team (your Apple ID)
- Or manually select your "Developer ID Application" certificate
For signing in CI/CD, you need to:
-
Export your certificate
# In Keychain Access: # Find "Developer ID Application: Your Name" # Right-click → Export → Save as .p12 file # Set a password
-
Add secrets to GitHub
# Convert certificate to base64 base64 -i certificate.p12 | pbcopy # Add to GitHub: # Settings → Secrets and variables → Actions → New repository secret # Name: BUILD_CERTIFICATE_BASE64 # Value: (paste the base64 string) # Also add: # Name: P12_PASSWORD # Value: (your certificate password)
-
Update workflow files
Edit .github/workflows/release.yml to import the certificate before building:
- name: Import Code Signing Certificate
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
run: |
# Create temporary keychain
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(uuidgen)
# Decode certificate
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $RUNNER_TEMP/certificate.p12
# Create keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Import certificate
security import $RUNNER_TEMP/certificate.p12 \
-P "$P12_PASSWORD" \
-A \
-t cert \
-f pkcs12 \
-k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# Set partition list
security set-key-partition-list -S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
- name: Build Release
run: |
xcodebuild clean archive \
-project Jiggler.xcodeproj \
-scheme Jiggler \
-configuration Release \
-archivePath ./build/Jiggler.xcarchive
# Remove CODE_SIGN_IDENTITY="" etc - let it sign naturallyAfter building, notarize with Apple:
# Store credentials (one time)
xcrun notarytool store-credentials "notarytool-profile" \
--apple-id "your@email.com" \
--team-id "YOUR_TEAM_ID" \
--password "app-specific-password"
# Notarize (in CI/CD)
xcrun notarytool submit Jiggler.dmg \
--keychain-profile "notarytool-profile" \
--wait
# Staple the notarization ticket
xcrun stapler staple Jiggler.appIf you don't want to set up full CI/CD signing:
- Build releases locally on your Mac (where you have the certificate)
- Sign and notarize manually
- Upload to GitHub manually
# Build
xcodebuild clean build \
-project Jiggler.xcodeproj \
-scheme Jiggler \
-configuration Release
# Create DMG (already signed from build)
hdiutil create -volname "Jiggler" \
-srcfolder ./build/Release/Jiggler.app \
-ov -format UDZO Jiggler.dmg
# Notarize
xcrun notarytool submit Jiggler.dmg \
--keychain-profile "notarytool-profile" \
--wait
# Staple
xcrun stapler staple Jiggler.dmg
# Create release
gh release create v1.11 Jiggler.dmg --title "Jiggler 1.11" --notes "Release notes here"If you're submitting this to the upstream project, suggest:
- The maintainer should handle signing (they likely already have a certificate)
- Document that unsigned builds are for testing
- Only official releases should be signed
- Keep the unsigned CI builds for quick testing
For personal/internal use only:
# Create self-signed certificate
# Keychain Access → Certificate Assistant → Create a Certificate
# Name: "Jiggler Developer"
# Identity Type: Self Signed Root
# Certificate Type: Code Signing
# Build with it (users still need to trust it manually)
xcodebuild build -project Jiggler.xcodeproj CODE_SIGN_IDENTITY="Jiggler Developer"For your fork:
- ✅ Keep unsigned builds in CI/CD (works for testing)
- ✅ Document that users need to right-click → Open
- ⏳ Add signing later if you distribute widely
For upstream contribution:
- ✅ Keep the CI/CD workflows unsigned (maintainer handles signing)
- ✅ Document the workflows are for automated testing
- ✅ Maintainer can add signing secrets if they want automated signed builds
Your v1.11 release at https://github.com/pjaol/Jiggler/releases is:
- ✅ Built successfully
- ✅ DMG and ZIP available
- ❌ Not signed
- ❌ Not notarized
Users can still use it by right-clicking → Open.
| Scenario | Unsigned (Current) | Signed | Signed + Notarized |
|---|---|---|---|
| Local testing | ✅ Works | ✅ Works | ✅ Works |
| Downloaded by others | ✅ Works immediately | ||
| CI/CD builds | ✅ Easy | 🔧 Requires secrets | 🔧 Requires secrets + notarization |
| Cost | Free | $99/year | $99/year |
| Professional | ✅ Trusted | ✅✅ Fully trusted |
Bottom line: Unsigned works but isn't ideal for public distribution. For a personal fork, it's fine. For official releases, signing and notarization provide a much better user experience.