Skip to main content

macOS PKG

Build macOS installer packages using pkgbuild

The PKG stage builds macOS .pkg installer packages from your Darwin binaries. On macOS it uses the native pkgbuild tool; on Linux it assembles the identical flat XAR package from xar + mkbom + cpio, so you can produce .pkg installers in CI without a Mac. Installers are placed in dist/macos/.

Classification

Packager — creates macOS PKG installers from Darwin binaries. Required: not a publisher. Builds on macOS (pkgbuild) or Linux (xar/mkbom/cpio).

Minimal config

crates:
  - name: myapp
    pkgs:
      - identifier: com.example.myapp

Full config reference

crates:
  - name: myapp
    pkgs:
      - identifier: com.example.myapp  # required; reverse-domain bundle identifier
        id: ""                          # optional; unique identifier
        ids: []                         # optional; filter by build IDs
        name: ""                        # optional; output filename template (user controls extension)
        install_location: /usr/local/bin  # optional; installation path on target system
        use: binary                     # optional; binary | appbundle (what to package)
        min_os_version: ""              # optional; minimum macOS version (e.g. "10.13")
        scripts: ""                     # optional; directory with preinstall/postinstall scripts
        extra_files: []                 # optional; additional files in the package payload (anodizer-additive)
        templated_extra_files: []       # optional; extra files rendered through template engine (anodizer-additive)
        replace: false                  # optional; remove archive artifacts, keep PKG only
        mod_timestamp: ""               # optional; fixed timestamp for reproducible builds (templates allowed)
        skip: false                     # optional; also accepts `disable:` as an alias

Authentication

Not applicable — PKG creation is a local build step using the native pkgbuild tool. Distribution/notarization requires a separate signing step (see Signing).

Common gotchas

  • Linux build path: when pkgbuild is absent (e.g. on Linux CI), anodizer assembles the same flat XAR package from xar + mkbom (bomutils) + cpio (gzip is done in-process). Install all three to build .pkg installers without a Mac. Note: signing/notarization still requires macOS.
  • Signing: pkgbuild does not sign packages. For distribution outside the Mac App Store, pipe the output through the notarize stage using use: pkg. The notarize stage handles productsign and xcrun notarytool automatically.
  • install_location: the default /usr/local/bin requires admin privileges. Use /usr/local/bin for CLI tools; use a user-writable path only for user-space tools.
  • Multi-arch identifier collisions: pkg produces one package per binary. If identifier: is a literal string (no template vars), two builds for different architectures will share the same identifier. Disambiguate with a template (e.g. com.example.myapp.{{ Arch }}).

Republish / update behavior

Not applicable — this is a local packaging stage, not a publisher.

Required tools

The stage prefers pkgbuild when present, otherwise the Linux flat-package toolchain — at least one of these two must be available:

  • pkgbuild — part of Xcode Command Line Tools on macOS. Install with xcode-select --install.
  • xar + mkbom (bomutils) + cpio — the Linux-native path. All three must be present together; anodizer assembles the identical XAR layout pkgbuild would produce (gzip is handled in-process).

Platform

PKG only processes binary artifacts targeting Darwin (macOS) — the output is always a macOS installer. Binaries for other operating systems are ignored. The build host, however, may be macOS or Linux (see Required tools).

Config fields

FieldTypeDefaultDescription
idstringUnique identifier for referencing this config from other stages.
idslistallFilter to specific build IDs.
identifierstringrequiredBundle identifier in reverse-domain notation (e.g. com.example.myapp). Templates allowed.
namestring{{ ProjectName }}_{{ Arch }}Output filename template. User controls the extension — include .pkg explicitly if desired.
install_locationstring/usr/local/binInstallation path on the target system. Templates allowed.
usestringbinaryWhich artifact type to package: binary (the built executable) or appbundle (a .app bundle from the app-bundles stage).
min_os_versionstringMinimum macOS version (e.g. 10.13). Forwarded to pkgbuild --min-os-version.
scriptsstringPath to a directory containing preinstall and/or postinstall scripts. Templates allowed.
extra_fileslistAdditional files to include in the package payload. Anodizer-additive (not in GoReleaser Pro pkg).
templated_extra_fileslistExtra files rendered through the template engine before inclusion. Anodizer-additive.
replaceboolfalseRemove matching archive artifacts, keeping only the PKG.
mod_timestampstringFixed timestamp for reproducible builds. Templates allowed (e.g. {{ CommitTimestamp }}). Applied to the staging directory contents before pkgbuild bundles them — timestamps propagate into the pkg payload tar.
skipbool/stringfalseSkip this PKG config. Accepts true/false or a Tera template. Also accepts the disable: spelling for back-compat with imported GoReleaser configs.
ifstringTemplate-conditional: skip if the rendered result is false or empty. Render failure is a hard error.

How it works

One .pkg is produced per binary — pkg installers are single-binary by design. Unlike DMG (which groups multiple binaries into one container image), each pkg wraps exactly one payload binary so that Homebrew formula installers and macOS Installer.app each target a discrete, independently versionable package. Multi-binary crates therefore emit N packages per target triple.

For each macOS binary artifact, the stage:

  1. Creates a temporary staging directory and copies the binary into it.
  2. Copies any extra_files into the staging directory (as-is).
  3. Renders and copies any templated_extra_files into the staging directory.
  4. Applies mod_timestamp to all staged files if set (template-rendered first).
  5. Runs pkgbuild --root <staging> --identifier <id> --version <ver> --install-location <path> <output>.

If scripts is set, a --scripts <dir> argument is added pointing to your pre/postinstall scripts.

Template variables

All of the following fields support standard template variables: identifier, install_location, scripts, mod_timestamp, and name.

Available variables: {{ ProjectName }}, {{ Version }}, {{ Arch }}, {{ Os }}, {{ Tag }}, {{ CommitTimestamp }}, and all other standard variables.

Install scripts

Place a preinstall and/or postinstall shell script in the directory specified by scripts. Both scripts must be executable. They are run by macOS Installer before and after the package payload is installed, respectively.

scripts/
  preinstall
  postinstall

Full example

crates:
  - name: myapp
    pkgs:
      - identifier: "com.example.{{ ProjectName }}"
        name: "{{ ProjectName }}_{{ Version }}_{{ Arch }}.pkg"
        install_location: /usr/local/bin
        scripts: installer/scripts
        extra_files:
          - LICENSE
        replace: true
        mod_timestamp: "{{ CommitTimestamp }}"

Signing

pkgbuild itself does not sign packages. To notarize for distribution outside the Mac App Store, pipe the output through the notarize stage with use: pkg. The notarize stage handles productsign (signing with a Developer ID Installer certificate) and xcrun notarytool submission automatically.