Skip to content

alessio/hdiutil

Repository files navigation

hdiutil

Build Go Reference Go Report Card Codecov License

Package hdiutil provides a Go wrapper around the macOS hdiutil command-line tool for creating, manipulating, and signing DMG disk images.

Note: This package requires macOS — it wraps native macOS tooling (hdiutil, codesign, xcrun, bless).

Features

Image formats UDZO (zlib, default), UDBZ (bzip2), ULFO (lzfse), ULMO (lzma)
Filesystems HFS+ (default, with tuned allocation parameters) and APFS
Workflow orchestration Runner manages the full lifecycle: create, mount, modify, convert, sign, notarize
Sandbox-safe images Produce DMGs openable by sandboxed macOS applications
Code signing & notarization Integrated codesign and Apple notarytool/stapler support
JSON configuration Load/save Config from JSON files for CI/CD pipelines
Input sanitization Rejects null bytes and dash-prefixed paths to prevent argument injection
Dry-run mode Simulate() option previews all hdiutil invocations without executing them
Testable Typed CommandExecutor interface with WithExecutor and Simulate options for mock injection

Installation

go get al.essio.dev/pkg/hdiutil

Quick start

package main

import (
	"log"

	"al.essio.dev/pkg/hdiutil"
)

func main() {
	cfg := &hdiutil.Config{
		SourceDir:  "./dist",
		OutputPath: "MyApp.dmg",
		VolumeName: "My App",
	}

	runner := hdiutil.New(cfg)
	defer runner.Cleanup()

	// 1. Validate config, create temp directory.
	if err := runner.Setup(); err != nil {
		log.Fatal(err)
	}

	// 2. Create a writable temporary image populated from SourceDir.
	if err := runner.Start(); err != nil {
		log.Fatal(err)
	}

	// 3. (Optional) Mount, modify contents, unmount.
	// if err := runner.AttachDiskImage(); err != nil { log.Fatal(err) }
	// ... copy files, customise .DS_Store, etc.
	// _ = runner.Bless()           // mark bootable (no-op unless Config.Bless is set)
	// _ = runner.DetachDiskImage() // fixes permissions and unmounts

	// 4. Convert to final compressed DMG.
	if err := runner.FinalizeDMG(); err != nil {
		log.Fatal(err)
	}

	// 5. (Optional) Sign and notarize — no-ops when credentials are empty.
	if err := runner.Codesign(); err != nil {
		log.Fatal(err)
	}
	if err := runner.Notarize(); err != nil {
		log.Fatal(err)
	}
}

Sandbox-safe images

cfg := &hdiutil.Config{
	SourceDir:   "./dist",
	OutputPath:  "MyApp.dmg",
	SandboxSafe: true,
	FileSystem:  "HFS+", // APFS is not supported in sandbox-safe mode
}

Runner lifecycle

The Runner goes through a fixed sequence of steps — calling methods out of order returns an error.

Setup ➜ Start ➜ [Attach ➜ modify ➜ Bless ➜ Detach] ➜ FinalizeDMG ➜ [Codesign] ➜ [Notarize]
  │                        optional                                      optional
  └─ validates config, creates temp dir

License

This package is part of the unixtools project and is released under the same license.

Contributors

Languages