Oskar Groth
Oskar GrothFebruary 6, 2025

Automating Sparkle Updates for macOS Apps

Automate Sparkle updates for macOS apps: build, sign, notarize, and publish Mac app updates with ease.
Script

Goodbye, App Center. Hello, Automation.

With App Center shutting down its servicesβ€”including Sparkle update hostingβ€”macOS developers need a new way to distribute updates. If you're using Sparkle to manage app updates, the process can be tedious:

  • Manually creating a .dmg
  • Notarizing it
  • Generating an update.xml with correct versions and changelogs
  • Ensuring the update is correctly signed
  • Distributing the appcast and updated build
  • Archiving previous builds
  • Maintaining a URL to the latest build

To make this process seamless, I’ve created a set of scripts that will automate everything from build selection to signing and publishing updates via S3 and CloudFront.

The Scripts

πŸ”— Get the scripts here: Cindori/sparkle-publisher

This automation consists of three main scripts:

prepare-dmg.sh

  • Finds the latest Xcode build of your app
  • Packages it into a signed .dmg file
  • Submits it for notarization
  • Staples the notarization ticket

publish-update.sh

  • Extracts version and build info from update.xml
  • Reads the Changelog.md file for the latest release notes
  • Uploads the .dmg and update.xml to Amazon S3
  • Invalidates CloudFront caches for instant availability

release.sh

  • Calls prepare-dmg.sh to generate the .dmg
  • Runs publish-update.sh to push the update online

The scripts are separated so you can easily debug or use parts of the whole workflow.

Requirements

To use these scripts, you need to set up a few things first:

1. Generate Signing Keys for Sparkle

Before you can sign Sparkle updates, you must generate signing keys. Sparkle provides tools for this, and you should have already created a key in your macOS Keychain labeled Private key for signing Sparkle updates.

If you haven't done this, follow Sparkle's documentation to generate and store the signing keys.

2. Set Up Amazon S3 + CloudFront

You'll need an S3 bucket to host the update files and CloudFront as a CDN for efficient and secure distribution. Using CloudFront ensures that:

  • Updates are delivered faster to global users.
  • Downloads are signed and served over HTTPS.
  • Updates remain accessible even if S3 rate limits are triggered.

Make sure you:

  • Set up an S3 bucket to store updates.
  • Configure CloudFront to serve files from S3.
  • Get your CloudFront Distribution ID (you'll need it for cache invalidation).

The scripts will create the following hierarchy in your bucket:

YourBucket
β”œβ”€β”€ apps
β”‚   β”œβ”€β”€ YourAppName (lowercased)
β”‚   β”‚   β”œβ”€β”€ YourAppName.dmg
β”‚   β”‚   β”œβ”€β”€ updates
β”‚   β”‚   β”‚   β”œβ”€β”€ update.xml
β”‚   β”‚   β”‚   β”œβ”€β”€ x.x.x-y
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ YourAppName.dmg

Where x.x.x is the version, and y is the build number.

The scripts will generate both a publicly facing latest version in the app folder and a versioned archive of update builds. This allows you to maintain a static URL for the latest version (e.g., for your landing page) while preserving older versions for rollbacks, historical reference, or cases where a new minimum OS requirement prevents certain users from updating beyond a specific version.

If you want to customize this structure, you will need to edit the scripts first.

3. Install DropDMG and Create a Template

This script uses DropDMG to generate disk images. You must:

  • Install DropDMG.
  • Create a custom DropDMG template that defines how your .dmg will appear to users.
  • Place the theme files inside the DMG directory (see folder structure below).

4. Required CLI Tools

Ensure the following tools are installed:

  • Sparkle Tools (sign_update, generate_appcast)
  • AWS CLI (configured for S3 and CloudFront)
  • DropDMG (and its command-line tool)
  • Xcode Command Line Tools (notarytool, stapler)
  • fzf (optional, for a better build selection UI)

5. Changelog Format

The scripts expect a changelog to exist in the project root folder in Markdown format. Each version section should follow this format:

## x.x.x (yy) (2025-02-06)
 
- Improvement A
- Improvement B
  ...
  • x.x.x β†’ Version string
  • yy β†’ Build number
  • 2025-02-06 β†’ Release date

The scripts expect that you've manually updated the changelog before running it.

6. Folder Structure

The scripts assume the following directory layout in your project:

YourProjectFolder
β”œβ”€β”€ Changelog.md
β”œβ”€β”€ Scripts
β”‚   β”œβ”€β”€ prepare-dmg.sh
β”‚   β”œβ”€β”€ release.sh
β”‚   β”œβ”€β”€ publish-update.sh
β”œβ”€β”€ DMG
β”‚   β”œβ”€β”€ <place the contents of your DropDMG template here>
β”‚   β”œβ”€β”€ Info.plist
β”‚   β”œβ”€β”€ Background
β”‚   β”œβ”€β”€ Icons
β”œβ”€β”€ Utilities
β”‚   β”œβ”€β”€ Sparkle
β”‚   β”‚   β”œβ”€β”€ generate_appcast
β”œβ”€β”€ Build
β”‚   β”œβ”€β”€ <place your exported builds here>

7. Update the script config

Once you're ready, edit the script to replace the configuration values with your own data:

  • NAME
  • WEBSITE
  • S3_BUCKET
  • CLOUDFRONT_DISTRIBUTION_ID
  • DEVELOPER_ID

Running the Script

Once your setup is complete, it's time to release updates!

  1. Archive your build in Xcode and export your app, selecting Custom method and then Direct Distribution.
Xcode Archive

Save it in your projects Build folder.

  1. Using the Terminal, navigate to your Scripts folder in your project and run the release script.

    ./release.sh
    

If multiple builds exist, you’ll be prompted to choose the latest.

Pick Build
  1. The script extracts the latest changelog entry for the current build. Confirm and approve the update.

  2. Done! The update is published, the current release is updated, and CloudFront caches are refreshed.

Any user around the world can now hit "Check for updates" in your app and update to the latest version.

Making Sparkle Updates Effortless

These scripts streamline your macOS app updates to a single commandβ€”no more manual notarization, signing, or appcast edits. Just focus on building your app, and let the automation handle the rest.

Give it a try, and let me know what you ship! πŸš€