A macOS app for previewing Markdown files. AppKit, sandboxed, ships with a Quick Look extension. Updates via Sparkle, distributed via Amore.
| Thing | Value |
|---|---|
| Bundle id | doc.md-preview |
| Product name | Markdown Preview |
| Scheme | md-preview |
| Quick Look target | quick-look (embedded extension) |
| Min macOS | 15.0 |
| Sandboxed | yes — uses Sparkle XPC services for updates |
| Auto-updater | Sparkle 2.x (Swift package) |
| Distribution | Amore (managed) with custom domain storage.md-preview.app |
Version is managed centrally in Version.xcconfig (MARKETING_VERSION, CURRENT_PROJECT_VERSION). Both the app and the quick-look extension inherit from it.
One command:
./scripts/release.sh # release current Version.xcconfig
./scripts/release.sh --version 0.0.2 # bump marketing version (auto-bumps build)
./scripts/release.sh --version 0.0.2 --build 7
./scripts/release.sh --beta # amore --beta + GH prerelease
./scripts/release.sh --draft # amore --draft, no GH release
./scripts/release.sh --skip-github # local amore release onlyBefore running, add a CHANGELOG.md entry for the version being shipped:
## [0.0.2] – 2026-05-01
Short narrative summary.
- **Bullet for each change.**
- Bug fix bullet.The script:
- Validates the changelog entry exists for the resolved version
- Updates
Version.xcconfigand commits asRelease X.Y.Z (N)if it changed - Runs
amore release --scheme md-preview --release-notes "$NOTES"(full pipeline: archive → sign → DMG → notarize → EdDSA-sign → upload → publish appcast) - Tags
vX.Y.Z, pushes, creates GitHub release with DMG asset
Source of truth: Version.xcconfig for the version numbers, CHANGELOG.md for the notes.
./scripts/rollback-release.sh --latest # unpublish latest, delete GH release+tag
./scripts/rollback-release.sh 0.0.2 # unpublish specific version
./scripts/rollback-release.sh 0.0.2 --delete # permanently delete on Amore
./scripts/rollback-release.sh 0.0.2 --keep-github # leave GitHub release in place
./scripts/rollback-release.sh --latest --yes # skip the confirmation promptDefault is unpublish (reversible — flips published=false on Amore so it disappears from the appcast). Use --delete only when you're sure; it permanently removes the release. To re-publish after a non-destructive rollback: amore releases update <version> -b doc.md-preview --published true.
- Hosting: Amore-managed with custom domain
storage.md-preview.app - Codesign identity:
Developer ID Application: Mohamed Fauzaan (5P3TSMNV42) - Notary keychain profile:
md-preview-notary - EdDSA public key (in Info.plist
SUPublicEDKey):gIQjgqfjkIR+egQ4S1oBLxE/NCDxpXXGdZXSpn04VAY=— private key in login Keychain
To inspect or change: amore config show --bundle-id doc.md-preview / amore config set .... CLI lives at /usr/local/bin/amore.
SUFeedURLmismatch. Info.plist points tohttps://storage.md-preview.app/appcast.xmlbut Amore actually publishes tohttps://storage.md-preview.app/v1/apps/doc.md-preview/appcast.xml. Fix Info.plist before any non-test release ships to real users — already-installed copies will check the wrong URL forever. Either changeSUFeedURLto the/v1/apps/...path, or configure a CDN rewrite atstorage.md-preview.appto map/appcast.xml→ the real path.- No git remote yet.
git remote -vis empty. Rungh repo createbefore relying on the GitHub release portion ofscripts/release.sh(it auto-skips when no remote exists).
xcodebuild -project md-preview.xcodeproj -scheme md-preview -configuration Debug build
xcodebuild -resolvePackageDependencies -project md-preview.xcodeprojSparkle helper tools (sign_update / generate_keys / generate_appcast) live at:
~/Library/Developer/Xcode/DerivedData/md-preview-*/SourcePackages/artifacts/sparkle/Sparkle/bin/