diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 0000000..179ccc6 --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,8 @@ +[bumpversion] +current_version = 0.0.1 +parse = (?P\d+)\.(?P\d+)\.(?P\d+) +serialize = {major}.{minor}.{patch} + +[bumpversion:file:applecrate/version.py] +parse = __version__\s=\s\"(?P\d+)\.(?P\d+)\.(?P\d+)\" +serialize = {major}.{minor}.{patch} diff --git a/README.md b/README.md index c574729..a2c0215 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Package your command line tools into a native macOS installer. -AppleCrate is a tool for creating native macOS installers for your command line tools. AppleCrate can be used as a command line interface (CLI) tool or as a library in your own Python code. 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](https://www.pyinstaller.org/) to create a standalone executable. +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](https://www.pyinstaller.org/) to create a standalone executable. ## Installation @@ -10,3 +10,88 @@ AppleCrate is a tool for creating native macOS installers for your command line pip install applecrate ``` +## Simple Example + +```bash +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` and install it to `/usr/local/bin/mytool-1.0.0`. It will create a symlink to the tool at `/usr/local/bin/mytool` and will also create an uninstaller to remove the tool. + +## 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 + -l, --license FILE Path to license file + -w, --welcome PATH Path to welcome markdown or HTML file + -c, --conclusion PATH 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. + --help Show this message and exit. + +``` + + +## 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 init` command to create a TOML configuration via a wizard +- [ ] Add `applecrate check` command to check the configuration without building the installer +- [ ] Documentation +- [ ] Tests + +## Credits + +Heavily inspired by [macOS Installer Buidler](https://github.com/KosalaHerath/macos-installer-builder) by [Kosala Herath](https://github.com/KosalaHerath). 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 + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. diff --git a/README_DEV.md b/README_DEV.md index 62f49ef..3c4e1f2 100644 --- a/README_DEV.md +++ b/README_DEV.md @@ -28,3 +28,11 @@ Linting and formatting utilizes [ruff](https://github.com/astral-sh/ruff) ## Publishing - `flit publish` + +## Updating the README + +- `cog -r README.md` + +## Updating version + +- `bump2version [patch|minor|major] --verbose [--dry-run]` diff --git a/applecrate/cli.py b/applecrate/cli.py index b6ecc8d..bf15e22 100644 --- a/applecrate/cli.py +++ b/applecrate/cli.py @@ -55,20 +55,22 @@ def cli(): @click.option( "--welcome", "-w", - type=click.Path(exists=True), + type=click.Path(dir_okay=False, exists=True), help="Path to welcome markdown or HTML file", ) @click.option( "--conclusion", "-c", - type=click.Path(exists=True), + type=click.Path(dir_okay=False, exists=True), help="Path to conclusion markdown or HTML file", ) @click.option( "--uninstall", "-u", type=click.Path(dir_okay=False, exists=True), - help="Path to uninstall script; " "if not provided, an uninstall script will be created for you." "See also '--no-uninstall'", + help="Path to uninstall script; " + "if not provided, an uninstall script will be created for you." + "See also '--no-uninstall'", ) @click.option( "--no-uninstall", @@ -182,9 +184,13 @@ def build(**kwargs): # Render the welcome and conclusion templates echo("Creating welcome.html") - create_html_file(welcome, BUILD_DIR / "Resources" / "welcome.html", data, "welcome.md") + create_html_file( + welcome, BUILD_DIR / "Resources" / "welcome.html", data, "welcome.md" + ) echo("Creating conclusion.html") - create_html_file(conclusion, BUILD_DIR / "Resources" / "conclusion.html", data, "conclusion.md") + create_html_file( + conclusion, BUILD_DIR / "Resources" / "conclusion.html", data, "conclusion.md" + ) echo("Copying license file") copy_and_create_parents(license, BUILD_DIR / "Resources" / "LICENSE.txt") @@ -196,7 +202,15 @@ def build(**kwargs): # Render the uninstall script if not no_uninstall: echo("Creating uninstall script") - target = BUILD_DIR / "darwinpkg" / "Library" / "Application Support" / app / version / "uninstall.sh" + target = ( + BUILD_DIR + / "darwinpkg" + / "Library" + / "Application Support" + / app + / version + / "uninstall.sh" + ) if uninstall: copy_and_create_parents(uninstall, target) else: diff --git a/pyproject.toml b/pyproject.toml index bd75dcc..4b4f387 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ dependencies = [ ] [project.optional-dependencies] +dev = ["bump2version>=1.0.1,<2.0.0", "cogapp>=3.3.0,<4.0.0"] lint = ["ruff>=0.1.14"] [project.urls]