Skip to main content

AppleCrate

Package your command line tools into a native macOS installer.

AppleCrate is a tool for creating native macOS installers for your command line tools. It's useful for creating installers for command line tools written in any language. Tools written in interpreted languages like Python will need to be first processed with a tool like pyinstaller to create a standalone executable.

Installation

pip install applecrate

Simple Example

applecrate build \
--app mytool \
--version 1.0.0 \
--license LICENSE \
--install dist/mytool "/usr/local/bin/{{ app }}-{{ version }}" \
--link /usr/local/bin/mytool "/usr/local/bin/{{ app }}-{{ version }}"

This will create a native macOS installer for the tool dist/mytool which will install it to /usr/local/bin/mytool-1.0.0. The installer will create a symlink to the tool at /usr/local/bin/mytool and will also create an uninstaller to remove the tool.

You can also use applecrate from your own Python code to create installers programmatically:

"""Create a macOS installer package programmatically."""

from applecrate import build_installer

if __name__ == "__main__":
    build_installer(
        app="MyApp",
        version="1.0.0",
        license="LICENSE",
        install=[("dist/myapp", "/usr/local/bin/myapp")],
        output="build/{{ app }}-{{ version }}.pkg",
        verbose=print,
    )

Screenshot

How It Works

AppleCrate is a Python application that uses the pkgbuild and productbuild command line tools to create a macOS installer package. AppleCrate does not do anything that you couldn't do yourself with these tools, but it automates the process and provide a simple command line interface. Creating a macOS installer package is a multi-step process that requires the generation of multiple files such as HTML files for the welcome screen, pre and post install scripts, Distribution XML file, etc. AppleCrate takes care of all of this for you but also allows you to customize the installer by providing your own files for these steps.

AppleCrate uses Jinja2 templates to generate the files required for the installer. This allows you to use template variables in your files or command line parameters to customize the installer. For example, you can use {{ app }} and {{ version }} in your files to refer to the app name and version you provide on the command line.

Usage

Usage: applecrate build [OPTIONS]

  applecrate: A Python package for creating macOS installer packages.

Options:
  -a, --app TEXT                  App name.
  -v, --version TEXT              App version.
  -I, --identifier TEXT           Unique package identifier. The OS X Installer
                                  recognizes a package as being an upgrade to an
                                  already-installed package only if the package
                                  identifiers match, so it is advisable to set a
                                  meaningful, consistent identifier when you
                                  build the package. If not set, the identifier
                                  will be set to 'org.opensource.{{ app }}'.
  -l, --license FILE              Path to license file. If provided, the
                                  installer will include a click-through license
                                  agreement.
  -w, --welcome FILE              Path to welcome markdown or HTML file
  -c, --conclusion FILE           Path to conclusion markdown or HTML file
  -u, --uninstall FILE            Path to uninstall script; if not provided, an
                                  uninstall script will be created for you. See
                                  also '--no-uninstall'
  -U, --no-uninstall              Do not include an uninstall script in the
                                  package
  -L, --url NAME URL              Links to additional resources to include in
                                  conclusion HTML shown after installation. For
                                  example, the project website or documentation.
  -b, --banner FILE               Path to optional PNG banner image for
                                  installer package.
  -i, --install FILE_OR_DIR DEST  Install FILE_OR_DIR to destination DEST; DEST
                                  must be an absolute path, for example
                                  '/usr/local/bin/app'. DEST may include
                                  template variables {{ app }} and {{ version
                                  }}. For example: `--install dist/app
                                  "/usr/local/bin/{{ app }}-{{ version }}"` will
                                  install the file 'dist/app' to
                                  '/usr/local/bin/app-1.0.0' if --app=app and
                                  --version=1.0.0.
  -k, --link SRC TARGET           Create a symbolic link from SRC to DEST after
                                  installation. SRC and TARGET must be absolute
                                  paths and both may include template variables
                                  {{ app }} and {{ version }}. For example:
                                  `--link "/Library/Application Support/{{ app
                                  }}/{{ version }}/app" "/usr/local/bin/{{ app
                                  }}-{{ version }}"`
  -p, --pre-install FILE          Path to pre-install shell script; if not
                                  provided, a pre-install script will be created
                                  for you.
  -P, --post-install FILE         Path to post-install shell script; if not
                                  provided, a post-install script will be
                                  created for you. If provided, the installer
                                  will run this script after other post-install
                                  actions.
  -m, --chmod MODE PATH           Change the mode of PATH to MODE after
                                  installation. PATH must be an absolute path.
                                  PATH may contain template variables {{ app }}
                                  and {{ version }}. MODE must be an octal
                                  number, for example '755'.
  -s, --sign APPLE_DEVELOPER_CERTIFICATE_ID
                                  Sign the installer package with a developer
                                  ID. If APPLE_DEVELOPER_CERTIFICATE_ID starts
                                  with '$', it will be treated as an environment
                                  variable and the value of the environment
                                  variable will be used as the developer ID.
  -d, --build-dir DIRECTORY       Build directory to use for building the
                                  installer package. Default is
                                  build/applecrate/darwin if not provided.
  -o, --output FILE               Path to save the installer package.
  --help                          Show this message and exit.

Configuration

The command line tool applecrate can be configured via pyproject.toml or applecrate.toml in the current working directory or via command line options. The command line arguments will always take precedence over the configuration files. If present, applecrate.toml will take precedence over pyproject.toml. The configuration file should be in the following format:

pyproject.toml:

[tool.applecrate]
app = "mytool"
version = "1.0.0"
license = "LICENSE"
install = [
    ["dist/mytool", "/usr/local/bin/{{ app }}-{{ version }}"],
]
sign = "$APPLE_DEVELOPER_CERTIFICATE_ID"

applecrate.toml:

app = "mytool"
version = "1.0.0"
license = "LICENSE"
install = [
    ["dist/mytool", "/usr/local/bin/{{ app }}-{{ version }}"],
]
sign = "$APPLE_DEVELOPER_CERTIFICATE_ID"

Any command line option is a valid key in the configuration file. For example, the --app option can be set in the configuration file as app = "mytool". Command line options with a dash (-) should be converted to underscores (_) in the configuration file. For example, the --pre-install option should be set in the configuration file as pre_install = "scripts/preinstall.sh".

If the value of sign begins with a $, as in the example above, it will be treated as an environment variable and the value of the environment variable will be used as the certificate ID.

Template Variables

Destination paths, the package identifier, the welcome and conclusion HTML files, and the pre and post install scripts can include template variables. The following template variables are available:

  • app: The name of the app.
  • version: The version of the app.
  • identifier: The package identifier.
  • uninstall: The path to the uninstall shell script.
  • url: A list of URLs to include in the installer package.
  • install: A list of tuples of source and destination paths to install.
  • banner: The path to the banner image.
  • link: A list of tuples of source and target paths to create symlinks post-installation.
  • post_install: The path to the post-install shell script.
  • pre_install: The path to the pre-install shell script.
  • chmod: A list of tuples of mode and path to change the mode of files post-installation.
  • build_dir: The build directory.
  • output: The path to the installer package.
  • machine: The machine architecture, for example x86_64 or arm64.

For example, the default post-install script includes the following to create a symlink:

{% if link %}
# Create links if needed
{% for source, target in link %}
ln -s "{{ source }}" "{{ target }}"
{% endfor %}
{% endif %}

The Jinja2 template engine will replace {{ source }} and {{ target }} with the source and target paths provided on the command line and remove the if and for blocks.

See the Jinja2 template documentation for more information on how to use template variables.

The package identifier, the build path, and the output file path may also be templates but they will only have access to the app, version, and machine variables.

To Do

  • Add support for signing the installer with a developer certificate
  • Add support for notarizing the installer
  • Add python API to create installers programmatically
  • Add applecrate check command to check the configuration without building the installer
  • Documentation (set up mkdocs)
  • Tests

Credits

Heavily inspired by macOS Installer Builder by Kosala Herath. AppleCrate is a complete rewrite in Python but borrows many ideas from macOS Installer Builder and is thus licensed under the same Apache License, Version 2.0.

License

Copyright Rhet Turnbull, 2024. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License here.

Contributing

Contributions of all kinds are welcome! Please see CONTRIBUTING.md for more information.

See Also

macOS-Pkg-Builder, another Python package for creating macOS installer packages. macOS-Pkg-Builder provides a programmatic interface for creating installer packages and is more flexible than AppleCrate. AppleCrate is designed to be simpler and easier to use for common use cases. I wasn't aware of macOS-Pkg-Builder when I started AppleCrate, but I might have used it to implement AppleCrate had I known about it.

Metadata

Release files for applecrate 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for applecrate 0.1.4
File Size Uploaded
applecrate-0.1.4.tar.gz 339.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for applecrate 0.1.4
File Interpreter ABI Platform
applecrate-0.1.4-py2.py3-none-any.whl Python 3, Python 2 none any Details

Total release size: 364.0 kB

Release files / applecrate-0.1.4.tar.gz

Download URL applecrate-0.1.4.tar.gz
Size 339.8 kB
Tags Source
SHA-256 checksum
How to use checksums
5b459ddd16ba176b9e6449573513d2bebaa9433d6a484d12e7388bef7e2eb2fc
BLAKE2b-256 checksum
How to use checksums
91aa6b58c3461b48c3482d9c923128ec23a52fe1b5b1e7830267a02b28a90695
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.31.0

Release files / applecrate-0.1.4-py2.py3-none-any.whl

Download URL applecrate-0.1.4-py2.py3-none-any.whl
Size 24.2 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
16f3feff27d9ead896c3f507e3ba09c1c33a93bc3e648fdbd4790b5bc3dfa98f
BLAKE2b-256 checksum
How to use checksums
859a065e9dcf8c8ff36357c69352b1430ef9c3059755fa6fa83139faef11ed37
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.31.0

Release history Release notifications | RSS feed

0.2.0

2 release files

0.1.10

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

This release

0.1.4 This release

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page