本文へ移動
cccskills
無料GitHub で公開

macos-app-distribution-dmg

Use when shipping a macOS app outside the App Store — Developer ID signing with hardened runtime, notarization with notarytool, stapling, building a clean drag-to-Applications DMG, and verifying the result launches on a stranger's Mac.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md8.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

macOS App Distribution (DMG)

Ship a macOS app outside the App Store so it opens first-try on a stranger's Mac: Developer ID signature, hardened runtime, notarization, staple, DMG. Miss a step and the user gets "App is damaged and can't be opened" — which most users read as your app is broken.

When to use

Use this skill when the user says:

  • distribute outside the App Store
  • DMG / disk image / drag to Applications
  • codesign / Developer ID
  • notarize / notarization / notarytool
  • staple / Gatekeeper
  • "app is damaged and can't be opened"
  • "cannot be opened because the developer cannot be verified"
  • hardened runtime

Do not use this skill for Mac App Store submission (different signing + review pipeline), iOS distribution, or pkg-installer authoring beyond the note below.

Core rule

Sign inside-out with Developer ID + hardened runtime,
notarize the final artifact, staple both the app and the DMG,
and verify on a machine that has never seen your certificates.

Prerequisites

  • Apple Developer Program membership.
  • Developer ID Application certificate in the login keychain (Xcode > Settings > Accounts > Manage Certificates, or developer.apple.com). This is a different certificate from "Apple Development" and "Apple Distribution".
  • Once, store notarization credentials (App Store Connect API key, or Apple ID + app-specific password):
xcrun notarytool store-credentials "notary-profile" \
  --key ~/keys/AuthKey_ABC123.p8 --key-id ABC123 --issuer <issuer-uuid>

The pipeline

archive → export (Developer ID signed, hardened runtime)
        → build DMG → sign DMG
        → notarize DMG → staple DMG (and the app inside)
        → verify → publish

1. Archive and export

xcodebuild -project MyApp.xcodeproj -scheme MyApp -configuration Release \
  archive -archivePath build/MyApp.xcarchive

xcodebuild -exportArchive \
  -archivePath build/MyApp.xcarchive \
  -exportOptionsPlist ExportOptions.plist \
  -exportPath build/export

ExportOptions.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>            <string>developer-id</string>
    <key>teamID</key>            <string>YOURTEAMID</string>
    <key>signingStyle</key>      <string>automatic</string>
</dict>
</plist>

The developer-id export re-signs everything correctly and enables hardened runtime per your target settings. In the target: Signing & Capabilities > Hardened Runtime must be on; add exception entitlements only for what the app truly does (e.g. com.apple.security.cs.allow-jit for JS engines — not "all of them to be safe"; every exception weakens notarization's meaning).

If you sign manually instead (SPM/Xcode-less builds), sign inside-out — frameworks and helpers first, app bundle last:

codesign --force --options runtime --timestamp \
  --sign "Developer ID Application: Your Name (TEAMID)" \
  MyApp.app/Contents/Frameworks/Sparkle.framework
# …every nested framework, XPC service, helper, CLI…
codesign --force --options runtime --timestamp \
  --sign "Developer ID Application: Your Name (TEAMID)" \
  --entitlements MyApp.entitlements \
  MyApp.app

Never use --deep for distribution signing: it applies the app's entitlements to nested code and mis-signs frameworks. It exists for quick local checks only.

2. Build the DMG

create-dmg (brew install create-dmg) gives the canonical drag-to-Applications layout:

create-dmg \
  --volname "MyApp" \
  --window-size 540 380 \
  --icon-size 128 \
  --icon "MyApp.app" 130 190 \
  --app-drop-link 410 190 \
  --background "dmg-background.png" \
  "build/MyApp-1.2.0.dmg" \
  "build/export/MyApp.app"

Or plain hdiutil with no styling:

mkdir -p build/dmg && cp -R build/export/MyApp.app build/dmg/
ln -s /Applications build/dmg/Applications
hdiutil create -volname "MyApp" -srcfolder build/dmg -ov \
  -format UDZO "build/MyApp-1.2.0.dmg"

DMG etiquette: version in the filename, app + Applications symlink and nothing else, a background that says "drag this there" at a glance, total size sane (check what you're bundling if it's suddenly 300 MB).

Sign the DMG too:

codesign --force --timestamp \
  --sign "Developer ID Application: Your Name (TEAMID)" \
  "build/MyApp-1.2.0.dmg"

3. Notarize and staple

Notarize the DMG — Apple scans it and the app inside:

xcrun notarytool submit "build/MyApp-1.2.0.dmg" \
  --keychain-profile "notary-profile" --wait

--wait blocks until the verdict (usually minutes). On Invalid, get the reasons:

xcrun notarytool log <submission-id> --keychain-profile "notary-profile"

Then staple, so Gatekeeper passes even offline:

xcrun stapler staple "build/MyApp-1.2.0.dmg"

If you also distribute a bare .zip/.app (e.g. for Sparkle updates), staple the app itself and re-zip after stapling.

4. Verify — before publishing, every release

# Signature deep-check
codesign --verify --deep --strict --verbose=2 build/export/MyApp.app

# What Gatekeeper will say
spctl --assess --type open --context context:primary-signature -v build/MyApp-1.2.0.dmg
spctl --assess --type execute -v build/export/MyApp.app
#   want: "accepted · source=Notarized Developer ID"

# Staple present
xcrun stapler validate "build/MyApp-1.2.0.dmg"

# The real test: simulate a download (quarantine bit), then open
xattr -w com.apple.quarantine "0081;$(printf %x $(date +%s));Safari;" \
  "build/MyApp-1.2.0.dmg"
open "build/MyApp-1.2.0.dmg"

The quarantine test on a clean machine (or at least a fresh user account / VM) is the one that catches what your dev Mac can't — your machine trusts your certificates and will happily open things a stranger's Mac will block.

Common failures

SymptomCauseFix
"damaged and can't be opened"Not notarized, or broken signatureFull pipeline; check notarytool log
"developer cannot be verified"Ad-hoc or Apple Development certSign with Developer ID Application
Notarization Invalid: "not signed with a valid Developer ID" on a frameworkNested code unsigned/ad-hoc (SPM binaries, Sparkle, helpers)Sign inside-out; no --deep
Invalid: "hardened runtime not enabled"A nested binary missing --options runtimeAdd it everywhere, re-sign
Invalid: "contains get-task-allow"Debug build exportedRelease config, proper export
Works on your Mac, blocked elsewhereNever tested with quarantineThe xattr test above
App runs from DMG but breaks (bookmarks, login item)Translocation — running in-place from the imageUsers must drag to /Applications; that's what the layout is for

Updates and the release record

  • Sparkle is the standard update framework for non-App-Store apps. Budget for it from v1 — retrofitting updates after users exist is painful. Its XPC services are exactly the kind of nested code that must be correctly signed (inside-out, hardened runtime).
  • Bump CFBundleShortVersionString (marketing) and CFBundleVersion (build) every release; notarization treats each build as new.
  • Keep the pipeline as a script in the repo (scripts/release.sh) — a release process that lives in someone's shell history isn't a process. Record per release: version, date, archive hash, notarization submission ID.

Checklist

[ ] Developer ID Application certificate (not Apple Development)
[ ] Hardened runtime on, minimal exception entitlements
[ ] Nested code signed inside-out; no --deep in the pipeline
[ ] Release configuration; no get-task-allow in the exported app
[ ] DMG: app + /Applications symlink only, versioned filename, signed
[ ] Notarized via notarytool; log checked on any Invalid
[ ] Stapled: DMG (and bare .app if distributed separately)
[ ] spctl says "accepted · Notarized Developer ID"
[ ] Quarantine-bit open test passed on a machine without your certs
[ ] Pipeline scripted in the repo, not in shell history

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use when creating a new project folder, starter pack, app scaffold, or agent-ready workspace — AGENTS.md, README, TASKS, DECISIONS, PROMPTS, an idempotent bootstrap script, and starter-pack modes.

日本語の概要は準備中です。原文の説明を表示しています。

aka-kika/akakika-skills112026年9月12日 更新

Use when designing or implementing states for AI agent tasks, background jobs, local model runs, queues, and notifications — one shared set of states, only legal transitions, and a recovery path out of every failure.

日本語の概要は準備中です。原文の説明を表示しています。

aka-kika/akakika-skills112026年9月12日 更新

Use when exposing app features to App Intents, Shortcuts, Siri, Spotlight, widgets, controls, or system actions — small safe AppIntents, AppShortcutsProvider, AppEnum/AppEntity, parameters, and open-vs-background behavior.

日本語の概要は準備中です。原文の説明を表示しています。

aka-kika/akakika-skills112026年9月12日 更新

Use when designing, reviewing, or implementing a command-palette / ⌘K layer for an Apple-platform app — keyboard-first global search, ranked and grouped results, native floating panel, and protected destructive commands.

日本語の概要は準備中です。原文の説明を表示しています。

aka-kika/akakika-skills112026年9月12日 更新

Use when designing or implementing empty, first-run, no-result, offline, error, or waiting states in Apple-platform apps — every empty state explains what is missing, why it matters, and what to do next.

日本語の概要は準備中です。原文の説明を表示しています。

aka-kika/akakika-skills112026年9月12日 更新

Use when designing or implementing feedback, status, progress, alerts, errors, success states, and live activity indicators in Apple-platform apps — inline status, banners, alerts, notifications, and loading states.

日本語の概要は準備中です。原文の説明を表示しています。

aka-kika/akakika-skills112026年9月12日 更新

aka-kika のスキルをすべて見る

このスキルの問題を報告する