[macos] How do I create a nice-looking DMG for Mac OS X using command-line tools?

I need to create a nice installer for a Mac application. I want it to be a disk image (DMG), with a predefined size, layout and background image.

I need to do this programmatically in a script, to be integrated in an existing build system (more of a pack system really, since it only create installers. The builds are done separately).

I already have the DMG creation done using "hdiutil", what I haven't found out yet is how to make an icon layout and specify a background bitmap.

This question is related to macos scripting installation dmg

The answer is


For creating a nice looking DMG, you can now just use some well written open sources:


My app, DropDMG, is an easy way to create disk images with background pictures, icon layouts, custom volume icons, and software license agreements. It can be controlled from a build system via the "dropdmg" command-line tool or AppleScript. If desired, the picture and license RTF files can be stored under your version control system.


There's a little Bash script called create-dmg that builds fancy DMGs with custom backgrounds, custom icon positioning and volume name.

I've built it many years ago for the company that I ran at the time; it survives on other people's contribution since then, and reportedly works well.

There's also node-appdmg which looks like a more modern and active effort based on Node.js; check it out as well.


Bringing this question up to date by providing this answer.

appdmg is a simple, easy-to-use, open-source command line program that creates dmg-files from a simple json specification. Take a look at the readme at the official website:

https://github.com/LinusU/node-appdmg

Quick example:

  1. Install appdmg

    npm install -g appdmg
    
  2. Write a json file (spec.json)

    {
      "title": "Test Title",
      "background": "background.png",
      "icon-size": 80,
      "contents": [
        { "x": 192, "y": 344, "type": "file", "path": "TestApp.app" },
        { "x": 448, "y": 344, "type": "link", "path": "/Applications" }
      ]
    }
    
  3. Run program

    appdmg spec.json test.dmg
    

(disclaimer. I'm the creator of appdmg)


Don't go there. As a long term Mac developer, I can assure you, no solution is really working well. I tried so many solutions, but they are all not too good. I think the problem is that Apple does not really document the meta data format for the necessary data.

Here's how I'm doing it for a long time, very successfully:

  1. Create a new DMG, writeable(!), big enough to hold the expected binary and extra files like readme (sparse might work).

  2. Mount the DMG and give it a layout manually in Finder or with whatever tools suits you for doing that (see FileStorm link at the bottom for a good tool). The background image is usually an image we put into a hidden folder (".something") on the DMG. Put a copy of your app there (any version, even outdated one will do). Copy other files (aliases, readme, etc.) you want there, again, outdated versions will do just fine. Make sure icons have the right sizes and positions (IOW, layout the DMG the way you want it to be).

  3. Unmount the DMG again, all settings should be stored by now.

  4. Write a create DMG script, that works as follows:

    • It copies the DMG, so the original one is never touched again.
    • It mounts the copy.
    • It replaces all files with the most up to date ones (e.g. latest app after build). You can simply use mv or ditto for that on command line. Note, when you replace a file like that, the icon will stay the same, the position will stay the same, everything but the file (or directory) content stays the same (at least with ditto, which we usually use for that task). You can of course also replace the background image with another one (just make sure it has the same dimensions).
    • After replacing the files, make the script unmount the DMG copy again.
    • Finally call hdiutil to convert the writable, to a compressed (and such not writable) DMG.

This method may not sound optimal, but trust me, it works really well in practice. You can put the original DMG (DMG template) even under version control (e.g. SVN), so if you ever accidentally change/destroy it, you can just go back to a revision where it was still okay. You can add the DMG template to your Xcode project, together with all other files that belong onto the DMG (readme, URL file, background image), all under version control and then create a target (e.g. external target named "Create DMG") and there run the DMG script of above and add your old main target as dependent target. You can access files in the Xcode tree using ${SRCROOT} in the script (is always the source root of your product) and you can access build products by using ${BUILT_PRODUCTS_DIR} (is always the directory where Xcode creates the build results).

Result: Actually Xcode can produce the DMG at the end of the build. A DMG that is ready to release. Not only you can create a relase DMG pretty easy that way, you can actually do so in an automated process (on a headless server if you like), using xcodebuild from command line (automated nightly builds for example).

Regarding the initial layout of the template, FileStorm is a good tool for doing it. It is commercial, but very powerful and easy to use. The normal version is less than $20, so it is really affordable. Maybe one can automate FileStorm to create a DMG (e.g. via AppleScript), never tried that, but once you have found the perfect template DMG, it's really easy to update it for every release.


I've just written a new (friendly) command line utility to do this. It doesn’t rely on Finder/AppleScript, or on any of the (deprecated) Alias Manager APIs, and it’s easy to configure and use.

Anyway, anyone who is interested can find it on PyPi; the documentation is available on Read The Docs.


.DS_Store files stores windows settings in Mac. Windows settings include the icons layout, the window background, the size of the window, etc. The .DS_Store file is needed in creating the window for mounted images to preserve the arrangement of files and the windows background.

Once you have .DS_Store file created, you can just copy it to your created installer (DMG).


Don't go there. As a long term Mac developer, I can assure you, no solution is really working well. I tried so many solutions, but they are all not too good. I think the problem is that Apple does not really document the meta data format for the necessary data.

Here's how I'm doing it for a long time, very successfully:

  1. Create a new DMG, writeable(!), big enough to hold the expected binary and extra files like readme (sparse might work).

  2. Mount the DMG and give it a layout manually in Finder or with whatever tools suits you for doing that (see FileStorm link at the bottom for a good tool). The background image is usually an image we put into a hidden folder (".something") on the DMG. Put a copy of your app there (any version, even outdated one will do). Copy other files (aliases, readme, etc.) you want there, again, outdated versions will do just fine. Make sure icons have the right sizes and positions (IOW, layout the DMG the way you want it to be).

  3. Unmount the DMG again, all settings should be stored by now.

  4. Write a create DMG script, that works as follows:

    • It copies the DMG, so the original one is never touched again.
    • It mounts the copy.
    • It replaces all files with the most up to date ones (e.g. latest app after build). You can simply use mv or ditto for that on command line. Note, when you replace a file like that, the icon will stay the same, the position will stay the same, everything but the file (or directory) content stays the same (at least with ditto, which we usually use for that task). You can of course also replace the background image with another one (just make sure it has the same dimensions).
    • After replacing the files, make the script unmount the DMG copy again.
    • Finally call hdiutil to convert the writable, to a compressed (and such not writable) DMG.

This method may not sound optimal, but trust me, it works really well in practice. You can put the original DMG (DMG template) even under version control (e.g. SVN), so if you ever accidentally change/destroy it, you can just go back to a revision where it was still okay. You can add the DMG template to your Xcode project, together with all other files that belong onto the DMG (readme, URL file, background image), all under version control and then create a target (e.g. external target named "Create DMG") and there run the DMG script of above and add your old main target as dependent target. You can access files in the Xcode tree using ${SRCROOT} in the script (is always the source root of your product) and you can access build products by using ${BUILT_PRODUCTS_DIR} (is always the directory where Xcode creates the build results).

Result: Actually Xcode can produce the DMG at the end of the build. A DMG that is ready to release. Not only you can create a relase DMG pretty easy that way, you can actually do so in an automated process (on a headless server if you like), using xcodebuild from command line (automated nightly builds for example).

Regarding the initial layout of the template, FileStorm is a good tool for doing it. It is commercial, but very powerful and easy to use. The normal version is less than $20, so it is really affordable. Maybe one can automate FileStorm to create a DMG (e.g. via AppleScript), never tried that, but once you have found the perfect template DMG, it's really easy to update it for every release.


I also in need of using command line approach to do the packaging and dmg creation "programmatically in a script". The best answer I found so far is from Adium project' Release building framework (See R1). There is a custom script(AdiumApplescriptRunner) to allow you avoid OSX WindowsServer GUI interaction. "osascript applescript.scpt" approach require you to login as builder and run the dmg creation from a command line vt100 session.

OSX package management system is not so advanced compared to other Unixen which can do this task easily and systematically.

R1: http://hg.adium.im/adium-1.4/file/00d944a3ef16/Release


I found this great mac app to automate the process - http://www.araelium.com/dmgcanvas/ you must have a look if you are creating dmg installer for your mac app


There's a little Bash script called create-dmg that builds fancy DMGs with custom backgrounds, custom icon positioning and volume name.

I've built it many years ago for the company that I ran at the time; it survives on other people's contribution since then, and reportedly works well.

There's also node-appdmg which looks like a more modern and active effort based on Node.js; check it out as well.


For creating a nice looking DMG, you can now just use some well written open sources:


Don't go there. As a long term Mac developer, I can assure you, no solution is really working well. I tried so many solutions, but they are all not too good. I think the problem is that Apple does not really document the meta data format for the necessary data.

Here's how I'm doing it for a long time, very successfully:

  1. Create a new DMG, writeable(!), big enough to hold the expected binary and extra files like readme (sparse might work).

  2. Mount the DMG and give it a layout manually in Finder or with whatever tools suits you for doing that (see FileStorm link at the bottom for a good tool). The background image is usually an image we put into a hidden folder (".something") on the DMG. Put a copy of your app there (any version, even outdated one will do). Copy other files (aliases, readme, etc.) you want there, again, outdated versions will do just fine. Make sure icons have the right sizes and positions (IOW, layout the DMG the way you want it to be).

  3. Unmount the DMG again, all settings should be stored by now.

  4. Write a create DMG script, that works as follows:

    • It copies the DMG, so the original one is never touched again.
    • It mounts the copy.
    • It replaces all files with the most up to date ones (e.g. latest app after build). You can simply use mv or ditto for that on command line. Note, when you replace a file like that, the icon will stay the same, the position will stay the same, everything but the file (or directory) content stays the same (at least with ditto, which we usually use for that task). You can of course also replace the background image with another one (just make sure it has the same dimensions).
    • After replacing the files, make the script unmount the DMG copy again.
    • Finally call hdiutil to convert the writable, to a compressed (and such not writable) DMG.

This method may not sound optimal, but trust me, it works really well in practice. You can put the original DMG (DMG template) even under version control (e.g. SVN), so if you ever accidentally change/destroy it, you can just go back to a revision where it was still okay. You can add the DMG template to your Xcode project, together with all other files that belong onto the DMG (readme, URL file, background image), all under version control and then create a target (e.g. external target named "Create DMG") and there run the DMG script of above and add your old main target as dependent target. You can access files in the Xcode tree using ${SRCROOT} in the script (is always the source root of your product) and you can access build products by using ${BUILT_PRODUCTS_DIR} (is always the directory where Xcode creates the build results).

Result: Actually Xcode can produce the DMG at the end of the build. A DMG that is ready to release. Not only you can create a relase DMG pretty easy that way, you can actually do so in an automated process (on a headless server if you like), using xcodebuild from command line (automated nightly builds for example).

Regarding the initial layout of the template, FileStorm is a good tool for doing it. It is commercial, but very powerful and easy to use. The normal version is less than $20, so it is really affordable. Maybe one can automate FileStorm to create a DMG (e.g. via AppleScript), never tried that, but once you have found the perfect template DMG, it's really easy to update it for every release.


I finally got this working in my own project (which happens to be in Xcode). Adding these 3 scripts to your build phase will automatically create a Disk Image for your product that is nice and neat. All you have to do is build your project and the DMG will be waiting in your products folder.

Script 1 (Create Temp Disk Image):

#!/bin/bash
#Create a R/W DMG

dir="$TEMP_FILES_DIR/disk"
dmg="$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg"

rm -rf "$dir"
mkdir "$dir"
cp -R "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app" "$dir"
ln -s "/Applications" "$dir/Applications"
mkdir "$dir/.background"
cp "$PROJECT_DIR/$PROJECT_NAME/some_image.png" "$dir/.background"
rm -f "$dmg"
hdiutil create "$dmg" -srcfolder "$dir" -volname "$PRODUCT_NAME" -format UDRW

#Mount the disk image, and store the device name
hdiutil attach "$dmg" -noverify -noautoopen -readwrite

Script 2 (Set Window Properties Script):

#!/usr/bin/osascript
#get the dimensions of the main window using a bash script

set {width, height, scale} to words of (do shell script "system_profiler SPDisplaysDataType | awk '/Main Display: Yes/{found=1} /Resolution/{width=$2; height=$4} /Retina/{scale=($2 == \"Yes\" ? 2 : 1)} /^ {8}[^ ]+/{if(found) {exit}; scale=1} END{printf \"%d %d %d\\n\", width, height, scale}'")
set x to ((width / 2) / scale)
set y to ((height / 2) / scale)

#get the product name using a bash script
set {product_name} to words of (do shell script "printf \"%s\", $PRODUCT_NAME")
set background to alias ("Volumes:"&product_name&":.background:some_image.png")

tell application "Finder"
    tell disk product_name
        open
        set current view of container window to icon view
        set toolbar visible of container window to false
        set statusbar visible of container window to false
        set the bounds of container window to {x, y, (x + 479), (y + 383)}
        set theViewOptions to the icon view options of container window
        set arrangement of theViewOptions to not arranged
        set icon size of theViewOptions to 128
        set background picture of theViewOptions to background
        set position of item (product_name & ".app") of container window to {100, 225}
        set position of item "Applications" of container window to {375, 225}
        update without registering applications
        close
    end tell
end tell

The above measurement for the window work for my project specifically due to the size of my background pic and icon resolution; you may need to modify these values for your own project.

Script 3 (Make Final Disk Image Script):

#!/bin/bash
dir="$TEMP_FILES_DIR/disk"
cp "$PROJECT_DIR/$PROJECT_NAME/some_other_image.png" "$dir/"

#unmount the temp image file, then convert it to final image file
sync
sync
hdiutil detach /Volumes/$PRODUCT_NAME
rm -f "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
hdiutil convert "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg" -format UDZO -imagekey zlib-level=9 -o "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
rm -f "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg"

#Change the icon of the image file
sips -i "$dir/some_other_image.png"
DeRez -only icns "$dir/some_other_image.png" > "$dir/tmpicns.rsrc"
Rez -append "$dir/tmpicns.rsrc" -o "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
SetFile -a C "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"

rm -rf "$dir"

Make sure the image files you are using are in the $PROJECT_DIR/$PROJECT_NAME/ directory!


I've just written a new (friendly) command line utility to do this. It doesn’t rely on Finder/AppleScript, or on any of the (deprecated) Alias Manager APIs, and it’s easy to configure and use.

Anyway, anyone who is interested can find it on PyPi; the documentation is available on Read The Docs.


For those of you that are interested in this topic, I should mention how I create the DMG:

hdiutil create XXX.dmg -volname "YYY" -fs HFS+ -srcfolder "ZZZ"

where

XXX == disk image file name (duh!)
YYY == window title displayed when DMG is opened
ZZZ == Path to a folder containing the files that will be copied into the DMG

.DS_Store files stores windows settings in Mac. Windows settings include the icons layout, the window background, the size of the window, etc. The .DS_Store file is needed in creating the window for mounted images to preserve the arrangement of files and the windows background.

Once you have .DS_Store file created, you can just copy it to your created installer (DMG).


I found this great mac app to automate the process - http://www.araelium.com/dmgcanvas/ you must have a look if you are creating dmg installer for your mac app


If you want to set custom volume icon then use below command

/*Add a drive icon*/
cp "/Volumes/customIcon.icns" "/Volumes/dmgName/.VolumeIcon.icns"  


/*SetFile -c icnC will change the creator of the file to icnC*/
SetFile -c icnC /<your path>/.VolumeIcon.icns

Now create read/write dmg

/*to set custom icon attribute*/
SetFile -a C /Volumes/dmgName

Don't go there. As a long term Mac developer, I can assure you, no solution is really working well. I tried so many solutions, but they are all not too good. I think the problem is that Apple does not really document the meta data format for the necessary data.

Here's how I'm doing it for a long time, very successfully:

  1. Create a new DMG, writeable(!), big enough to hold the expected binary and extra files like readme (sparse might work).

  2. Mount the DMG and give it a layout manually in Finder or with whatever tools suits you for doing that (see FileStorm link at the bottom for a good tool). The background image is usually an image we put into a hidden folder (".something") on the DMG. Put a copy of your app there (any version, even outdated one will do). Copy other files (aliases, readme, etc.) you want there, again, outdated versions will do just fine. Make sure icons have the right sizes and positions (IOW, layout the DMG the way you want it to be).

  3. Unmount the DMG again, all settings should be stored by now.

  4. Write a create DMG script, that works as follows:

    • It copies the DMG, so the original one is never touched again.
    • It mounts the copy.
    • It replaces all files with the most up to date ones (e.g. latest app after build). You can simply use mv or ditto for that on command line. Note, when you replace a file like that, the icon will stay the same, the position will stay the same, everything but the file (or directory) content stays the same (at least with ditto, which we usually use for that task). You can of course also replace the background image with another one (just make sure it has the same dimensions).
    • After replacing the files, make the script unmount the DMG copy again.
    • Finally call hdiutil to convert the writable, to a compressed (and such not writable) DMG.

This method may not sound optimal, but trust me, it works really well in practice. You can put the original DMG (DMG template) even under version control (e.g. SVN), so if you ever accidentally change/destroy it, you can just go back to a revision where it was still okay. You can add the DMG template to your Xcode project, together with all other files that belong onto the DMG (readme, URL file, background image), all under version control and then create a target (e.g. external target named "Create DMG") and there run the DMG script of above and add your old main target as dependent target. You can access files in the Xcode tree using ${SRCROOT} in the script (is always the source root of your product) and you can access build products by using ${BUILT_PRODUCTS_DIR} (is always the directory where Xcode creates the build results).

Result: Actually Xcode can produce the DMG at the end of the build. A DMG that is ready to release. Not only you can create a relase DMG pretty easy that way, you can actually do so in an automated process (on a headless server if you like), using xcodebuild from command line (automated nightly builds for example).

Regarding the initial layout of the template, FileStorm is a good tool for doing it. It is commercial, but very powerful and easy to use. The normal version is less than $20, so it is really affordable. Maybe one can automate FileStorm to create a DMG (e.g. via AppleScript), never tried that, but once you have found the perfect template DMG, it's really easy to update it for every release.


For those of you that are interested in this topic, I should mention how I create the DMG:

hdiutil create XXX.dmg -volname "YYY" -fs HFS+ -srcfolder "ZZZ"

where

XXX == disk image file name (duh!)
YYY == window title displayed when DMG is opened
ZZZ == Path to a folder containing the files that will be copied into the DMG

If you want to set custom volume icon then use below command

/*Add a drive icon*/
cp "/Volumes/customIcon.icns" "/Volumes/dmgName/.VolumeIcon.icns"  


/*SetFile -c icnC will change the creator of the file to icnC*/
SetFile -c icnC /<your path>/.VolumeIcon.icns

Now create read/write dmg

/*to set custom icon attribute*/
SetFile -a C /Volumes/dmgName

My app, DropDMG, is an easy way to create disk images with background pictures, icon layouts, custom volume icons, and software license agreements. It can be controlled from a build system via the "dropdmg" command-line tool or AppleScript. If desired, the picture and license RTF files can be stored under your version control system.


For those of you that are interested in this topic, I should mention how I create the DMG:

hdiutil create XXX.dmg -volname "YYY" -fs HFS+ -srcfolder "ZZZ"

where

XXX == disk image file name (duh!)
YYY == window title displayed when DMG is opened
ZZZ == Path to a folder containing the files that will be copied into the DMG

I also in need of using command line approach to do the packaging and dmg creation "programmatically in a script". The best answer I found so far is from Adium project' Release building framework (See R1). There is a custom script(AdiumApplescriptRunner) to allow you avoid OSX WindowsServer GUI interaction. "osascript applescript.scpt" approach require you to login as builder and run the dmg creation from a command line vt100 session.

OSX package management system is not so advanced compared to other Unixen which can do this task easily and systematically.

R1: http://hg.adium.im/adium-1.4/file/00d944a3ef16/Release


I finally got this working in my own project (which happens to be in Xcode). Adding these 3 scripts to your build phase will automatically create a Disk Image for your product that is nice and neat. All you have to do is build your project and the DMG will be waiting in your products folder.

Script 1 (Create Temp Disk Image):

#!/bin/bash
#Create a R/W DMG

dir="$TEMP_FILES_DIR/disk"
dmg="$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg"

rm -rf "$dir"
mkdir "$dir"
cp -R "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app" "$dir"
ln -s "/Applications" "$dir/Applications"
mkdir "$dir/.background"
cp "$PROJECT_DIR/$PROJECT_NAME/some_image.png" "$dir/.background"
rm -f "$dmg"
hdiutil create "$dmg" -srcfolder "$dir" -volname "$PRODUCT_NAME" -format UDRW

#Mount the disk image, and store the device name
hdiutil attach "$dmg" -noverify -noautoopen -readwrite

Script 2 (Set Window Properties Script):

#!/usr/bin/osascript
#get the dimensions of the main window using a bash script

set {width, height, scale} to words of (do shell script "system_profiler SPDisplaysDataType | awk '/Main Display: Yes/{found=1} /Resolution/{width=$2; height=$4} /Retina/{scale=($2 == \"Yes\" ? 2 : 1)} /^ {8}[^ ]+/{if(found) {exit}; scale=1} END{printf \"%d %d %d\\n\", width, height, scale}'")
set x to ((width / 2) / scale)
set y to ((height / 2) / scale)

#get the product name using a bash script
set {product_name} to words of (do shell script "printf \"%s\", $PRODUCT_NAME")
set background to alias ("Volumes:"&product_name&":.background:some_image.png")

tell application "Finder"
    tell disk product_name
        open
        set current view of container window to icon view
        set toolbar visible of container window to false
        set statusbar visible of container window to false
        set the bounds of container window to {x, y, (x + 479), (y + 383)}
        set theViewOptions to the icon view options of container window
        set arrangement of theViewOptions to not arranged
        set icon size of theViewOptions to 128
        set background picture of theViewOptions to background
        set position of item (product_name & ".app") of container window to {100, 225}
        set position of item "Applications" of container window to {375, 225}
        update without registering applications
        close
    end tell
end tell

The above measurement for the window work for my project specifically due to the size of my background pic and icon resolution; you may need to modify these values for your own project.

Script 3 (Make Final Disk Image Script):

#!/bin/bash
dir="$TEMP_FILES_DIR/disk"
cp "$PROJECT_DIR/$PROJECT_NAME/some_other_image.png" "$dir/"

#unmount the temp image file, then convert it to final image file
sync
sync
hdiutil detach /Volumes/$PRODUCT_NAME
rm -f "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
hdiutil convert "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg" -format UDZO -imagekey zlib-level=9 -o "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
rm -f "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg"

#Change the icon of the image file
sips -i "$dir/some_other_image.png"
DeRez -only icns "$dir/some_other_image.png" > "$dir/tmpicns.rsrc"
Rez -append "$dir/tmpicns.rsrc" -o "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
SetFile -a C "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"

rm -rf "$dir"

Make sure the image files you are using are in the $PROJECT_DIR/$PROJECT_NAME/ directory!


These answers are way too complicated and times have changed. The following works on 10.9 just fine, permissions are correct and it looks nice.

Create a read-only DMG from a directory

#!/bin/sh
# create_dmg Frobulator Frobulator.dmg path/to/frobulator/dir [ 'Your Code Sign Identity' ]
set -e

VOLNAME="$1"
DMG="$2"
SRC_DIR="$3"
CODESIGN_IDENTITY="$4"

hdiutil create -srcfolder "$SRC_DIR" \
  -volname "$VOLNAME" \
  -fs HFS+ -fsargs "-c c=64,a=16,e=16" \
  -format UDZO -imagekey zlib-level=9 "$DMG"

if [ -n "$CODESIGN_IDENTITY" ]; then
  codesign -s "$CODESIGN_IDENTITY" -v "$DMG"
fi

Create read-only DMG with an icon (.icns type)

#!/bin/sh
# create_dmg_with_icon Frobulator Frobulator.dmg path/to/frobulator/dir path/to/someicon.icns [ 'Your Code Sign Identity' ]
set -e
VOLNAME="$1"
DMG="$2"
SRC_DIR="$3"
ICON_FILE="$4"
CODESIGN_IDENTITY="$5"

TMP_DMG="$(mktemp -u -t XXXXXXX)"
trap 'RESULT=$?; rm -f "$TMP_DMG"; exit $RESULT' INT QUIT TERM EXIT
hdiutil create -srcfolder "$SRC_DIR" -volname "$VOLNAME" -fs HFS+ \
               -fsargs "-c c=64,a=16,e=16" -format UDRW "$TMP_DMG"
TMP_DMG="${TMP_DMG}.dmg" # because OSX appends .dmg
DEVICE="$(hdiutil attach -readwrite -noautoopen "$TMP_DMG" | awk 'NR==1{print$1}')"
VOLUME="$(mount | grep "$DEVICE" | sed 's/^[^ ]* on //;s/ ([^)]*)$//')"
# start of DMG changes
cp "$ICON_FILE" "$VOLUME/.VolumeIcon.icns"
SetFile -c icnC "$VOLUME/.VolumeIcon.icns"
SetFile -a C "$VOLUME"
# end of DMG changes
hdiutil detach "$DEVICE"
hdiutil convert "$TMP_DMG" -format UDZO -imagekey zlib-level=9 -o "$DMG"
if [ -n "$CODESIGN_IDENTITY" ]; then
  codesign -s "$CODESIGN_IDENTITY" -v "$DMG"
fi

If anything else needs to happen, these easiest thing is to make a temporary copy of the SRC_DIR and apply changes to that before creating a DMG.


Bringing this question up to date by providing this answer.

appdmg is a simple, easy-to-use, open-source command line program that creates dmg-files from a simple json specification. Take a look at the readme at the official website:

https://github.com/LinusU/node-appdmg

Quick example:

  1. Install appdmg

    npm install -g appdmg
    
  2. Write a json file (spec.json)

    {
      "title": "Test Title",
      "background": "background.png",
      "icon-size": 80,
      "contents": [
        { "x": 192, "y": 344, "type": "file", "path": "TestApp.app" },
        { "x": 448, "y": 344, "type": "link", "path": "/Applications" }
      ]
    }
    
  3. Run program

    appdmg spec.json test.dmg
    

(disclaimer. I'm the creator of appdmg)


These answers are way too complicated and times have changed. The following works on 10.9 just fine, permissions are correct and it looks nice.

Create a read-only DMG from a directory

#!/bin/sh
# create_dmg Frobulator Frobulator.dmg path/to/frobulator/dir [ 'Your Code Sign Identity' ]
set -e

VOLNAME="$1"
DMG="$2"
SRC_DIR="$3"
CODESIGN_IDENTITY="$4"

hdiutil create -srcfolder "$SRC_DIR" \
  -volname "$VOLNAME" \
  -fs HFS+ -fsargs "-c c=64,a=16,e=16" \
  -format UDZO -imagekey zlib-level=9 "$DMG"

if [ -n "$CODESIGN_IDENTITY" ]; then
  codesign -s "$CODESIGN_IDENTITY" -v "$DMG"
fi

Create read-only DMG with an icon (.icns type)

#!/bin/sh
# create_dmg_with_icon Frobulator Frobulator.dmg path/to/frobulator/dir path/to/someicon.icns [ 'Your Code Sign Identity' ]
set -e
VOLNAME="$1"
DMG="$2"
SRC_DIR="$3"
ICON_FILE="$4"
CODESIGN_IDENTITY="$5"

TMP_DMG="$(mktemp -u -t XXXXXXX)"
trap 'RESULT=$?; rm -f "$TMP_DMG"; exit $RESULT' INT QUIT TERM EXIT
hdiutil create -srcfolder "$SRC_DIR" -volname "$VOLNAME" -fs HFS+ \
               -fsargs "-c c=64,a=16,e=16" -format UDRW "$TMP_DMG"
TMP_DMG="${TMP_DMG}.dmg" # because OSX appends .dmg
DEVICE="$(hdiutil attach -readwrite -noautoopen "$TMP_DMG" | awk 'NR==1{print$1}')"
VOLUME="$(mount | grep "$DEVICE" | sed 's/^[^ ]* on //;s/ ([^)]*)$//')"
# start of DMG changes
cp "$ICON_FILE" "$VOLUME/.VolumeIcon.icns"
SetFile -c icnC "$VOLUME/.VolumeIcon.icns"
SetFile -a C "$VOLUME"
# end of DMG changes
hdiutil detach "$DEVICE"
hdiutil convert "$TMP_DMG" -format UDZO -imagekey zlib-level=9 -o "$DMG"
if [ -n "$CODESIGN_IDENTITY" ]; then
  codesign -s "$CODESIGN_IDENTITY" -v "$DMG"
fi

If anything else needs to happen, these easiest thing is to make a temporary copy of the SRC_DIR and apply changes to that before creating a DMG.


For those of you that are interested in this topic, I should mention how I create the DMG:

hdiutil create XXX.dmg -volname "YYY" -fs HFS+ -srcfolder "ZZZ"

where

XXX == disk image file name (duh!)
YYY == window title displayed when DMG is opened
ZZZ == Path to a folder containing the files that will be copied into the DMG

Examples related to macos

Problems with installation of Google App Engine SDK for php in OS X dyld: Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib error running php after installing node with brew on Mac Could not install packages due to an EnvironmentError: [Errno 13] How do I install Java on Mac OSX allowing version switching? Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) Can't compile C program on a Mac after upgrade to Mojave You don't have write permissions for the /Library/Ruby/Gems/2.3.0 directory. (mac user) How can I install a previous version of Python 3 in macOS using homebrew? Could not install packages due to a "Environment error :[error 13]: permission denied : 'usr/local/bin/f2py'"

Examples related to scripting

What does `set -x` do? Creating an array from a text file in Bash Windows batch - concatenate multiple text files into one Raise error in a Bash script How do I assign a null value to a variable in PowerShell? Difference between ${} and $() in Bash Using a batch to copy from network drive to C: or D: drive Check if a string matches a regex in Bash script How to run a script at a certain time on Linux? How to make an "alias" for a long path?

Examples related to installation

You don't have write permissions for the /Library/Ruby/Gems/2.3.0 directory. (mac user) Conda version pip install -r requirements.txt --target ./lib How to avoid the "Windows Defender SmartScreen prevented an unrecognized app from starting warning" PackagesNotFoundError: The following packages are not available from current channels: Tensorflow import error: No module named 'tensorflow' Downgrade npm to an older version Create Setup/MSI installer in Visual Studio 2017 how to install tensorflow on anaconda python 3.6 Application Installation Failed in Android Studio How to install pip for Python 3.6 on Ubuntu 16.10?

Examples related to dmg

How to download Xcode DMG or XIP file? How do I create a nice-looking DMG for Mac OS X using command-line tools?