The build machine has produced Example.app, and the release job has generated a DMG. Before delivery, three questions remain: Does the mounted image contain the complete app and an Applications shortcut? Is the copied app’s signature valid? Can a user complete the drag-to-install process in the graphical interface? The checks below make each question repeatable. Replace Example.app with the name of your actual build artifact.
Establish the packaging boundary
The input must be the final version of the app bundle. Fix its artifact path in the build job, confirm that the app executable exists, and complete the planned signing process before packaging. Do not treat DMG creation as an opportunity to repair the app: changing resources, executables, or embedded components can invalidate its signature.
Use test -d to check the app bundle, rather than merely checking for a file with the same name. Also confirm that no old DMG exists at the output path. An explicit failure is easier to investigate than silently overwriting the previous release artifact. If the pipeline retries jobs, have the pipeline clean up its own output directory instead of letting the packaging script delete unknown files.
An intact image, a valid app signature, and system trust in the app are three different conclusions. Passing one check does not replace the other two.
Build an image with an installation shortcut
This script assembles the volume contents in a temporary directory: it copies the signed app and creates a symbolic link to /Applications. ditto copies the app bundle; do not edit files inside the bundle afterward.
#!/bin/bash
set -euo pipefail
APP="$PWD/build/Release/Example.app"
OUT="$PWD/dist/Example.dmg"
test -d "$APP"
test -f "$APP/Contents/MacOS/Example"
mkdir -p "$(dirname "$OUT")"
test ! -e "$OUT"
work="$(mktemp -d "${TMPDIR:-/tmp}/dmg-build.XXXXXX")"
trap 'rm -rf "$work"' EXIT
stage="$work/stage"
mkdir -p "$stage"
ditto "$APP" "$stage/Example.app"
ln -s /Applications "$stage/Applications"
hdiutil create -volname "Example" \
-srcfolder "$stage" -fs HFS+ -format UDZO "$OUT"
If the app’s executable is not named Example, adjust the check path as well. The volume name is what users see after mounting; it does not determine where the app is installed. Keeping the temporary staging directory separate from the final output directory makes cleanup straightforward and prevents an old image from accidentally being included in the new one.
What to leave out of the volume
Check the staging directory listing first to confirm that it contains only the app bundle and the installation shortcut. Build logs, signing materials, debug files, and the project repository should not ship in the DMG. The script’s stage directory is its only input directory. If you later add a README or other files, add and check them explicitly rather than running hdiutil create on the entire workspace.
Remount the image and inspect what you will deliver
Do not inspect only the source directory; open the DMG you just created. hdiutil verify checks the image. Then mount it read-only, check the paths inside the volume, copy the app into a separate directory, and verify its signature:
#!/bin/bash
set -euo pipefail
OUT="$PWD/dist/Example.dmg"
work="$(mktemp -d "${TMPDIR:-/tmp}/dmg-check.XXXXXX")"
mountpoint="$work/mount"
attached=0
cleanup() {
if [ "$attached" -eq 1 ]; then
hdiutil detach "$mountpoint" >/dev/null || true
fi
rm -rf "$work"
}
trap cleanup EXIT
mkdir -p "$mountpoint" "$work/installed"
hdiutil verify "$OUT"
hdiutil attach -readonly -nobrowse \
-mountpoint "$mountpoint" "$OUT" >/dev/null
attached=1
test -d "$mountpoint/Example.app"
test -L "$mountpoint/Applications"
test "$(readlink "$mountpoint/Applications")" = /Applications
ditto "$mountpoint/Example.app" "$work/installed/Example.app"
codesign --verify --deep --strict \
"$work/installed/Example.app"
The script returns a nonzero status on failure, so it can be used in a release job. If detaching the volume fails, check manually whether a process still has it open; do not use a forced detach to hide the problem. codesign --verify checks whether the copied bundle still meets signature requirements, but it does not mean the system trusts the app. If you use a signing and notarization workflow for external distribution, validate the system’s assessment separately as required by that workflow.
Test the user journey in a graphical session
Command-line checks can validate paths and signatures, but they cannot show whether the icon layout is clear, the drag target is visible, or the first launch behaves as expected. Open the DMG in a logged-in macOS graphical session and follow the steps a user would take:
- Confirm that the volume contains only the expected app and Applications shortcut, with names that are neither truncated nor confusing.
- Drag the app into Applications and confirm that copying finishes. Running it directly from the read-only volume is not a substitute for testing installation.
- Launch the copied app and check its first window and any necessary file-access workflow.
- Detach the volume, then launch the installed app again to rule out a dependency on resources inside the volume.
Before running these checks, make sure there is no older app with the same name at the test destination; otherwise, you may launch the previous installation. Verify the path before removing the test copy, too, so you do not delete an app you use regularly. If a graphical session is unavailable, mark the release as “command-line checks passed; graphical acceptance incomplete.” Do not report both as passed.
Locate failures at the right layer
If hdiutil verify fails, retain the original DMG and generation logs, then check whether the artifact was uploaded or read before writing finished. If files are missing from the volume, inspect the staging directory and the ditto input path. If signature verification fails, check whether any script modified the app bundle after signing, then create a new image from the final app. Repeatedly patching the existing DMG makes the deliverable difficult to trace.
If all command-line checks pass but the app will not launch after being dragged into Applications, first confirm the path you actually launched, the app signature, and the system trust checks required for external distribution. Finally, record the app’s build identifier, the DMG file checksum, the automated check results, and the graphical acceptance outcome. That record will help determine whether a problem in the next release arose during the build, packaging, or installation.
Frequently asked questions
Does hdiutil verify prove that the app will launch?
No. It checks the disk image. Validate the app signature and launch the copied app separately in a GUI session.
Should I sign the app before creating the DMG?
Yes. Sign and verify the final app bundle first. If the bundle changes, rebuild and revalidate the DMG.
Need your own cloud Mac?
Choose a model, location, and billing period. Each machine is a dedicated physical server. Availability is shown live in the console.