import subprocess
import shutil
import tempfile
import plistlib
import os
def patch_dmg_icon(dmg_path, new_icon_path):
"""Replace the volume icon in an existing DMG."""
temp_rw = tempfile.NamedTemporaryFile(suffix=".dmg", delete=False)
temp_rw.close()
subprocess.run([
"hdiutil", "convert", dmg_path,
"-format", "UDRW",
"-o", temp_rw.name,
"-ov"
], check=True)
result = subprocess.run(
["hdiutil", "attach", "-nobrowse", "-plist", temp_rw.name],
capture_output=True, check=True
)
plist = plistlib.loads(result.stdout)
mount_point = None
device = None
for entity in plist["system-entities"]:
if "mount-point" in entity:
mount_point = entity["mount-point"]
device = entity["dev-entry"]
break
try:
icon_target = os.path.join(mount_point, ".VolumeIcon.icns")
shutil.copyfile(new_icon_path, icon_target)
subprocess.run(["/usr/bin/SetFile", "-a", "C", mount_point], check=True)
subprocess.run(["sync", "--file-system", mount_point], check=True)
finally:
subprocess.run(["hdiutil", "detach", device], check=True)
subprocess.run([
"hdiutil", "convert", temp_rw.name,
"-format", "ULMO",
"-o", dmg_path,
"-ov"
], check=True)
os.unlink(temp_rw.name)
print(f"Successfully patched {dmg_path} with new icon")
if __name__ == "__main__":
import sys
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <dmg_path> <icon.icns>")
sys.exit(1)
patch_dmg_icon(sys.argv[1], sys.argv[2])