Skip to content

Latest commit

 

History

History
234 lines (202 loc) · 13.2 KB

dmg.md

File metadata and controls

234 lines (202 loc) · 13.2 KB

macOS disk images

load("@rules_electron//electron:dmg.bzl", "electron_dmg")

electron_dmg(
    name = "release",
    app = ":app",
    volume_name = "Sample Desktop",
    volume_icon = "//assets:volume.icns",
    source_date_epoch = 1577836800,
    finder_window = [120, 140, 600, 360],
    app_position = [140, 160],
    applications_position = [460, 160],
    icon_size = 112,
    text_size = 12,
    background_color = "#ddeeff",
    background_image = "//assets:installer-background.png",
    licenses = {"//legal:license.txt": "en_US"},
    license_labels = "//legal:labels.json",
)

The rule cross-packages a macOS ElectronAppInfo on BB Linux. Pinned xorriso and UDIF converter sources compile with native Bazel C actions and declared zlib. No configure, Make, CMake, mounted image or target executable runs during builds.

Output is a read-only, zlib-compressed UDIF disk image containing an HFS+ volume, the app bundle and a standard /Applications link. applications_link = False omits that link. Volume labels allow up to 32 UTF-8 bytes. Dates and ownership are explicit; filesystem and UDIF identities derive from input bytes. User configuration, host tools, filesystem times and libc PRNG state do not supply image inputs. Existing app file bytes, modes and relative links survive staging.

Sign and notarize the app before immutable import when preparing a production release. The example's app carries ad hoc signatures.

finder_window enables a deterministic .DS_Store and HFS+ auto-open folder. It declares Cocoa x, y, width and height, with a bottom-left screen origin. App/Applications icon positions use centers inside that window; icon_size allows 16..512 pixels and text_size 10..16. background_color accepts #RRGGBB. Omitting finder_window retains the plain volume layout.

background_image accepts a declared PNG, up to 64 MiB and 8192 pixels per axis. It requires finder_window, overrides the background color and copies unchanged bytes to .background.png. Its Carbon alias uses the explicit volume name/date and a path inside that volume, without build paths, assumed mount points or host file IDs. The writer reads the background's catalog ID/date from its first generated HFS+ image, finalizes the alias, then rebuilds and verifies unchanged catalog identity before UDIF conversion. No mounting or target execution is involved. A path-based form also appears in Chromium's Linux-compatible generator. BB tests decode the packaged alias and image bytes, check actual catalog IDs/dates and compare relocated builds. Native smoke resolves the packaged background alias through macOS descriptors and compares captured Finder-window pixels with the declared PNG through native color management. The picture getter is recorded as a diagnostic; it throws -10000 on the hosted Finder even when the generated alias resolves. The 74601b7 BB suite, seven cross-builds and all four native hosts passed. The macOS host resolved the packaged alias and compared three clear Finder-window samples with the same PNG through native color management: all samples were identical, [187, 187, 187, 255]. Screenshots stay temporary; only numeric samples enter logs. Existing signatures and updater lifecycle also passed.

License agreements

licenses maps declared UTF-8 .txt or .rtf files to explicit locales. license_default_language defaults to en_US and must appear in that mapping. The locale table includes English, French, German, Japanese, Korean and Chinese regional IDs. Text uses that locale's legacy encoding when representable, otherwise Unicode RTF. Declared RTF bytes stay unchanged. No host locale or filename discovery supplies inputs.

license_labels optionally provides a JSON object keyed by those same locales. Each locale can override languageName, agree, disagree, print, save and message. Defaults use the locale name and English controls. Labels must fit 255 bytes in that locale's legacy encoding. License inputs allow 2 MiB per file, 16 MiB total and at most 32 entries; labels allow 64 KiB and final XML 64 MiB.

{"en_US":{"languageName":"English","agree":"Agree","message":"Read license before installing."}}

The separate ElectronDmgLicense action adds LPic, STR# and TEXT/RTF resources to the unsigned image. License edits do not change filesystem-action inputs. Compressed disk bytes and block maps stay unchanged; image identity includes the new XML. Signed images, existing license resources, malformed bounds and output/input aliases fail. Sign/notarize only after this stage.

Mapping entries and labels follow the dmg-license format. The LPic default field is a resource index; region IDs belong in the mapping entries. Native tests cover Japanese fallback at indices 0 and 1, with distinct French text preventing a false match. BB tests independently decode mappings, preserve disk bytes and compare relocated/reordered inputs.

The 63c5336 suite, seven cross-builds and all four native hosts passed. macOS displayed each GUI license, refused mounting after Disagree and mounted the app after Agree. Both RTF variants required visible CJK/emoji text. Finder pixels, signatures, runtime and updater checks also passed.

The manual dmg_license_probe workflow input tests a previously verified BB image with the workflow's current macOS harness. Linux performs authenticated, commit-bound artifact retrieval; the matching Mac host only inspects the image. Probe results do not publish normal native-smoke commit status. Set dmg_consent_mode=cli to inspect terminal consent instead. This requires the complete decoded text, explicit refusal without mounting and acceptance with a mounted payload; raw RTF markup fails.

license_texts optionally maps declared UTF-8 .txt companions to RTF license locales. It emits TEXT beside unchanged RTF bytes at the same resource ID. Companion text must fit the locale's legacy encoding. Duplicate/undeclared locales, non-RTF primaries and invalid text fail. Companion files share the existing input size budget and deterministic staging. The example's English and Japanese CLI probes matched each decoded companion in full, refused mounting after No and mounted the payload after Yes. GUI tests retain the RTF Unicode check. RTF-only images mounted through hdiutil without requesting consent; declare a plain-text companion when terminal consent is required.

electron_dmg_license creates another legal variant without rebuilding the filesystem. Its image accepts an electron_dmg target (using that target's unlicensed base) or a raw unsigned, unlicensed .dmg. It shares the licenses, license_default_language, license_texts and license_labels attributes above. Signed or already-licensed raw files fail. ElectronDmgInfo exposes image/unlicensed; the original rule also exposes the unlicensed output group.

load("@rules_electron//electron:dmg.bzl", "electron_dmg_license")

electron_dmg_license(
    name = "release_japanese",
    image = ":release_dmg",
    licenses = {"//legal:license.rtf": "ja_JP"},
    license_default_language = "ja_JP",
)

The example's English text, English Unicode RTF and Japanese-default Unicode RTF variants use the same filesystem image. Hosted action inspection requires one filesystem action feeding all three license variants. Native RTF consent remains pending; select dmg_fixture=release_dmg_rtf.dmg or dmg_fixture=release_dmg_japanese.dmg with the manual probe to inspect them.

Metadata uses checksum-pinned MIT-licensed ds_store 1.3.3 and mac_alias 2.2.2 Python sources. No host alias discovery, Finder, mounting or AppleScript runs in artifact actions. Geometry/color inputs and metadata bytes participate in deterministic image identity. BB tests decode the packaged settings and auto-open catalog ID, preserve app seals and compare relocated input bytes. Native smoke reads settings through Finder without changing them. The 02da44f BB suite, seven cross-builds and all four native hosts passed window geometry, icon positions/sizes, background color, strict app signatures and the full existing installer/update smoke. The native geometry check converts Cocoa bounds using the actual display height. See the codec contract.

BB tests independently decode UDIF block maps, check compressed/uncompressed CRC32 values, compare relocated ZIP/tree inputs, and read the image payload with xorriso. The native macOS smoke verifies/mounts the BB image read-only, copies the app, checks code signatures, then launches Electron with renderer/addon checks. BB tests, seven cross-builds and all four native hosts passed. The macOS ARM64 host verified/mounted the image, checked strict app signatures and launched the copied app. The writer leaves regular-file Finder metadata empty; adding default type/creator codes invalidates strict signatures.

volume_icon accepts a declared ICNS file. The writer validates and copies its bytes to .VolumeIcon.icns and sets the HFS+ root folder's custom-icon flag. The signed app bundle stays immutable; icon bytes participate in volume identity. BB tests read catalog flags independently, compare relocated image bytes and reject malformed icons or output/input aliases. Native smoke asks NSWorkspace for the mounted volume icon and compares its rendered pixels against the same declared app icon through native color management. Exact ICNS byte equality is also required. The 69c3287 suite, seven cross-builds and all four native hosts passed. The flag follows Apple TN1150.

Release clients

load("@rules_electron//electron:release.bzl", "electron_macos_dmg_release_tool")

electron_macos_dmg_release_tool(
    name = "release_dmg",
    signer = "@rcodesign_linux_x64//:rcodesign",
)

Run on the BB release client, outside cacheable artifact actions:

bazel run --config=bb-workflow //:release_dmg -- \
  --input unsigned.dmg --output notarized.dmg \
  --identifier org.example.sample \
  --certificate-env RELEASE_CERTIFICATE_FILE --key-env RELEASE_PRIVATE_KEY_FILE \
  --password-env RELEASE_KEY_PASSWORD --ca-file apple-roots.pem \
  --expected-leaf-sha256 "$EXPECTED_LEAF_SHA256" --expected-team-id "$EXPECTED_TEAM_ID" \
  --signing-time 2026-11-09T01:02:03Z --timestamp-url https://tsa.example.com/ \
  --api-key-env APPLE_API_KEY_FILE

The client shares the app release client's real OpenSSL preflight: leaf pin, single team ID, explicit roots, encrypted key unlock and public-key match. The timestamp URL and identifier must be explicit. It requires Developer ID policy, then waits for accepted notarization and staples the image. Only the final DMG is committed; compute update hashes after this stage.

electron_macos_dmg_signing_tool stops after signing and does not accept notary credentials. electron_macos_dmg_notarization_tool accepts an already signed DMG, --input, --output, --api-key-env and optional --max-wait-seconds. Every client requires a declared execution backend; keys enter through runtime environment names, without inherited credentials or tool configuration in child processes. Input artifacts remain unchanged.

Signing preserves the image prefix and all footer fields except embedded signature location/size. Notarization additionally preserves every existing signature blob; only the ticket slot can change. Duplicate, overlapping, truncated or malformed signature layouts fail. Output races preserve the competing file. These checks protect artifact bytes, and do not verify CMS or Apple ticket trust independently of the declared backend.

Tests use real OpenSSL and real rcodesign layouts with simulated certificate signature/TSA/Apple boundaries. Live Developer ID/TSA/Apple interoperability and production native trust remain unverified.