Download on the Mac App Store · Download macOS binary (v1.0.1)
Uploading large photos through a browser kept failing mid-transfer on my photography portfolio site — overwhelming the server or the browser before finishing. WP-CLI solves it reliably, but I didn't want to live in the terminal. So I built this: a SwiftUI wrapper around rsync, ssh, and wp-cli that's simple to use once set up, and rock solid on large media libraries.
Free on the App Store — and open source.
Version 1.0.1 · macOS 14+
Quick visual tour of the app workflow:
-
Overview — three-pane workspace with profiles, drop zone, and operations drawer.
-
Queue + job status — queued files, per-file state, and active job progress in one view.
-
Profile setup — complete connection and WordPress settings in the built-in profile editor.
- Single-window profile setup — connection, WordPress path, import defaults
- Multiple server profiles
- Credentials stored in Keychain — password auth and optional key passphrase
- SSH auth modes — key-based (agent-friendly) or password via
SSH_ASKPASS - Drag-and-drop & file picker — JPG, JPEG, JPE, GIF, PNG, BMP, ICO, WebP, AVIF, HEIC, PDF
- Reliable job pipeline — preflight → upload (
rsync) → verify → import (wp media import) → regenerate thumbnails → cleanup - Retry failed files without reprocessing successful ones
- Streaming logs in-app + persisted log files
- Reports — copy text, export JSON or CSV
- macOS 14 (Sonoma) or later
- Xcode 16+ (to build from source)
- SSH access to target server
wp-cliinstalled on the remote server- WordPress installation on the remote server
swift buildswift testswift run WordpressMediaUploaderAppOr open the Xcode project and run from there:
xcodegen generate
open "WordpressMediaUploader.xcodeproj"Use the release script (this is the default distribution path):
./scripts/build_distribution.shCredentials for notarization:
- Preferred: set
NOTARY_KEYCHAIN_PROFILEto a notarytool keychain profile name. - Default fallback: if unset, the script uses
notary-profile. - Alternative: set
APPLE_ID,APPLE_TEAM_ID, andAPPLE_APP_SPECIFIC_PASSWORD.
The script will:
- Build a Release app
- Sign with Developer ID (
DEVELOPER_ID_APP_CERT, defaults to this repo's Developer ID cert) - Submit for notarization and wait for acceptance
- Staple the notarization ticket
- Produce
WPMediaUploader-v<version>-macOS.zip - Ensure git tag
v<version>points toHEADand is pushed toorigin - Create or update GitHub Release
v<version>(marked latest) with zip +sha256.txt - Send a macOS notification on success/failure
GitHub release publishing controls:
- Default: enabled (
PUBLISH_GITHUB_RELEASE=1) - Disable: set
PUBLISH_GITHUB_RELEASE=0 - Override repo slug detection: set
GITHUB_REPO=owner/repo - Requires authenticated GitHub CLI (
gh auth status)
Tag pushes (v*) trigger .github/workflows/release-package.yml, which runs the same native release flow:
- Build Release app
- Sign, notarize, and staple
- Publish GitHub Release assets
- Publish a GitHub Package (GHCR OCI artifact) containing the zip +
sha256.txt
Required repository secrets:
DEVELOPER_ID_CERT_P12_BASE64DEVELOPER_ID_CERT_PASSWORDKEYCHAIN_PASSWORDDEVELOPER_ID_APP_CERTAPPLE_IDAPPLE_TEAM_IDAPPLE_APP_SPECIFIC_PASSWORD
Optional repository secret:
NOTARY_KEYCHAIN_PROFILE(defaults tonotary-profile)
For the verified App Store Connect auth + submission workflow (including WP Media Uploader and Cat Scratches app IDs), see:
APP_STORE_CONNECT_SUBMISSION_RUNBOOK.md
- Create a server profile with your SSH credentials and WordPress root path
- Drop images onto the app (or use Browse)
- Click Upload — the app will:
- Verify SSH connectivity and
wp-cliavailability - Upload files via
rsyncwith progress tracking - Verify remote file integrity (size check)
- Import each file into WordPress media library
- Regenerate thumbnails
- Optionally clean up remote staging files
- Verify SSH connectivity and
- Default staging path is
~/wp-media-import - Job and profile data are saved under
~/Library/Application Support/WPMediaUploader/ - Passwords and key passphrases are stored in the macOS Keychain
MIT