Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .flake8

This file was deleted.

49 changes: 19 additions & 30 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,34 @@ name: build

on:
push:
paths:
- '.github/workflows/build.yml'
- 'Dockerfile'
- '**/*.py'
branches:
- '**'
tags:
- '!**'
pull_request:
paths:
- '.github/workflows/build.yml'
- 'Dockerfile'
- '**/*.py'
branches: [main]
pull_request: ~
workflow_dispatch: ~

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v2
- uses: actions/setup-python@v5
- uses: actions/checkout@v5
- uses: extractions/setup-just@v3
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
- run: just install lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v2
- uses: actions/setup-python@v5
- uses: actions/checkout@v5
- uses: extractions/setup-just@v3
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
- run: just install coverage
brew-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v2
- uses: actions/checkout@v5
- uses: extractions/setup-just@v3
- uses: Homebrew/actions/setup-homebrew@master
# These Homebrew packages are needed since we have them as fake dependencies for some test output
- name: Setup required Homebrew packages
Expand All @@ -50,32 +39,32 @@ jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build and run Docker image
run: |
docker build . -t justintime50/homebrew-releaser
docker run --workdir /github/workspace \
-e INPUT_SKIP_COMMIT=true \
-e INPUT_DEBUG=true \
-e GITHUB_REPOSITORY=justintime50/homebrew-releaser \
-e GITHUB_REPOSITORY=justintime50/github-archive \
-e INPUT_HOMEBREW_OWNER=justintime50 \
-e INPUT_HOMEBREW_TAP=homebrew-formulas \
-e INPUT_GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
-e INPUT_INSTALL=virtualenv_install_with_resources \
-e INPUT_UPDATE_README_TABLE=true \
-e INPUT_FORMULA_INCLUDES="include Language::Python::Virtualenv" \
-e INPUT_DEPENDS_ON='"python@3.13"' \
-e INPUT_DEPENDS_ON='"python@3.14"' \
-e INPUT_UPDATE_PYTHON_RESOURCES=true \
justintime50/homebrew-releaser
coverage:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v2
- uses: actions/setup-python@v5
- uses: actions/checkout@v5
- uses: extractions/setup-just@v3
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: '3.14'
- run: just install coverage
- uses: codecov/codecov-action@v5
with:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
name: release
name: update-stable

on:
release:
types: [published]
workflow_dispatch: ~

jobs:
release:
update-stable:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: '0'
- name: Update stable git tag
Expand All @@ -18,4 +18,4 @@ jobs:
git push origin --delete $STABLE_TAG
git push origin $STABLE_TAG
env:
STABLE_TAG: v2
STABLE_TAG: v3
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# CHANGELOG

## v3.0.0-rc1 (2025-12-07)

- Completely overhauls the Docker image
- Pre-builds the image and references instead of rebuilding on each run. This leads to faster releasing runs, much smaller image size (1.5gb+ -> ~500mb), and better control and maintainability
- Switches from official Brew image to stable debian
- Installs Homebrew manually
- Installs Python from Homebrew
- Bumps Python from v3.13 to v3.14
- Bump Homebrew from v4 to v5
- No longer uses `/tmp`, stores everything in `/app`
- Always runs `brew tap` on your tap (needed for `brew update-python-resources`, also serves as a pre-run validation your tap is valid)
- Homebrew version prints in output
- Bumps dependencies

## v2.2.1 (2025-12-01)

- Corrects the order and punctuation of various log messages
Expand Down
37 changes: 28 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
FROM homebrew/brew:4.5.6
FROM debian:stable-slim

ENV PATH="/home/linuxbrew/.linuxbrew/opt/[email protected]/libexec/bin:${PATH}" \
HOMEBREW_NO_AUTO_UPDATE=1 \
WORKDIR /app

ENV HOMEBREW_NO_AUTO_UPDATE=1 \
HOMEBREW_NO_INSTALL_CLEANUP=1 \
HOMEBREW_NO_ENV_HINTS=1 \
HOMEBREW_NO_ANALYTICS=1

COPY --chown=linuxbrew:linuxbrew homebrew_releaser homebrew_releaser
COPY --chown=linuxbrew:linuxbrew pyproject.toml pyproject.toml
RUN \
# Setup system dependencies
apt-get update && \
apt-get install -y --no-install-recommends \
build-essential curl git ca-certificates bash && \
rm -rf /var/lib/apt/lists/* && \
# Setup Homebrew
useradd -m linuxbrew && \
curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | \
su - linuxbrew -c "NONINTERACTIVE=1 /bin/bash" && \
su - linuxbrew -c "git -C /home/linuxbrew/.linuxbrew/Homebrew checkout 5.0.4" && \
chown linuxbrew:linuxbrew /app

USER linuxbrew

ENV PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:${PATH}"

RUN brew install [email protected]

COPY pyproject.toml .
COPY homebrew_releaser homebrew_releaser

RUN brew install [email protected] \
&& python3 -m venv /home/linuxbrew/venv \
&& /home/linuxbrew/venv/bin/pip install .
RUN python3 -m venv venv && \
venv/bin/pip install .

ENTRYPOINT [ "/home/linuxbrew/venv/bin/python3", "/home/linuxbrew/homebrew_releaser/app.py" ]
ENTRYPOINT ["/app/venv/bin/python", "/app/homebrew_releaser/app.py"]
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ When you cut a new release when using this GitHub Action, Homebrew Releaser will
- The Homebrew formula filename will match the github repo name.
- It is **highly** recommended to enable debug mode and skip the commit on the first run through to ensure you have configured your workflow correctly and that the generated formula looks the way you want.
- Homebrew Releaser is **not** compatible with monorepos.
- Every precaution will be made to ensure that major releases of this action remain compatible (eg: every release of this action rebuilds and packages the current major release such as `v2` as a convenience to users). If you value stability over convenience, it's highly recommended to pin a specific version or commit hash of this action when using it (eg: `v2.0.1`)
- Every precaution will be made to ensure that major releases of this action remain compatible (eg: every release of this action rebuilds and packages the current major release such as `v3` as a convenience to users). If you value stability over convenience, it's highly recommended to pin a specific version or commit hash of this action when using it (eg: `v2.0.1`).
- Because we pre-build and pin the Docker image used instead of referencing a local Dockerfile, you **cannot** use the `latest` version/tag of this action and expect it to reflect the latest changes.

### GitHub Actions YAML

Expand All @@ -45,7 +46,7 @@ jobs:
name: homebrew-releaser
steps:
- name: Release project to Homebrew tap
uses: Justintime50/homebrew-releaser@v2
uses: Justintime50/homebrew-releaser@v3
with:
# The name of the homebrew tap to publish your formula to as it appears on GitHub.
# Required - strings
Expand Down Expand Up @@ -161,7 +162,7 @@ jobs:
name: homebrew-releaser
steps:
- name: Release my project to my Homebrew tap
uses: Justintime50/homebrew-releaser@v2
uses: Justintime50/homebrew-releaser@v3
with:
homebrew_owner: Justintime50
homebrew_tap: homebrew-formulas
Expand All @@ -183,10 +184,6 @@ jobs:
just --list
```

### Docker & GitHub Actions Setup

We have to play a balancing game of using Homebrew (cannot be root) and writing various files to use Homebrew Releaser (must be root or have write access), to get around this we setup and install everything using a `linuxbrew` user and directory but then everything afterwards (git clone, writing files, etc) occur inside of `/tmp`. This is an unfortunate but required path to take since using the official Python Docker image won't let us use Brew and have write access while using the Brew image gets us Homebrew but we cannot write in the normal directory. Thus we hack it a bit and just play inside of `/tmp`.

### Run Manually via Docker

Homebrew Releaser does not clean up artifacts after completing since the temporary Docker image on GitHub Actions will be discarded anyway.
Expand All @@ -196,3 +193,24 @@ Homebrew Releaser does not clean up artifacts after completing since the tempora
```sh
docker compose up -d --build
```

### Releasing

Releasing Homebrew Releaser takes a few steps, unfortunately to ensure 0 downtime of end-users using the action, it is a manual process:

```sh
# 1. Login to Docker if needed
docker login

# 2. Enable buildx in Docker if not already enabled
docker buildx create --use

# 3. Build and push the Homebrew Releaser Docker image and push it to Docker registry
docker buildx build --platform linux/amd64 -t justintime50/homebrew-releaser:3.0.1 --push .

# 4. Update the image version in `action.yml`
# 5. Push the code to GitHub
# 6. Create a `Release` on GitHub matching the version pushed so GitHub Actions marketplace picks it up
```

By following these steps, we push the pre-built Docker image to Docker Hub, we then push our source code to GitHub, we then publish the new version of the Action which references the updated pre-built Docker image (dramatically improves performance by using a pre-built image instead of rebuilding on every run). Users will then either use the stable (eg: v3) tag or an explicit commit hash or version.
56 changes: 28 additions & 28 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,78 +1,78 @@
name: "Homebrew Releaser"
description: "Release scripts, binaries, and executables directly to Homebrew via GitHub Actions."
name: 'Homebrew Releaser'
description: 'Release scripts, binaries, and executables directly to Homebrew via GitHub Actions.'
branding:
icon: "package"
color: "orange"
icon: 'package'
color: 'orange'
inputs:
homebrew_owner:
description: "The name of the homebrew tap owner to publish your formula to as it appears on GitHub."
description: 'The name of the homebrew tap owner to publish your formula to as it appears on GitHub.'
required: true
homebrew_tap:
description: "The name of the homebrew tap to publish your formula to as it appears on GitHub."
description: 'The name of the homebrew tap to publish your formula to as it appears on GitHub.'
required: true
formula_folder:
description: "The name of the folder in your homebrew tap where formula will be committed to."
description: 'The name of the folder in your homebrew tap where formula will be committed to.'
required: true
default: "Formula"
default: 'Formula'
github_token:
description: "The GitHub Token (saved as a repo secret) that has `repo` permissions for the homebrew tap you want to release to."
description: 'The GitHub Token (saved as a repo secret) that has `repo` permissions for the homebrew tap you want to release to.'
required: true
commit_owner:
description: "Git author info used to commit to the homebrew tap."
description: 'Git author info used to commit to the homebrew tap.'
required: false
default: "[email protected]"
default: '[email protected]'
commit_email:
description: "Git author info used to commit to the homebrew tap."
description: 'Git author info used to commit to the homebrew tap.'
required: false
default: "[email protected]"
default: '[email protected]'
depends_on:
description: "Custom dependencies in case other formulas are needed to build the current one (can be multiline)."
description: 'Custom dependencies in case other formulas are needed to build the current one (can be multiline).'
required: false
install:
description: "Custom install command for your formula."
description: 'Custom install command for your formula.'
required: true
test:
description: "Custom test command for your formula so you can run `brew test`."
description: 'Custom test command for your formula so you can run `brew test`.'
required: false
download_strategy:
description: "The Homebrew download strategy to use for formulas."
description: 'The Homebrew download strategy to use for formulas.'
required: false
custom_require:
description: "Allows you to add a custom require_relative at the top of the formula template."
description: 'Allows you to add a custom require_relative at the top of the formula template.'
required: false
formula_includes:
description: "Allows you to add custom includes inside the formula class, before dependencies and install blocks."
description: 'Allows you to add custom includes inside the formula class, before dependencies and install blocks.'
required: false
update_python_resources:
description: "Run 'brew update-python-resources' on the formula to add Python resources."
required: false
version:
description: "Override the automatically detected version of a formula with an explicit value."
description: 'Override the automatically detected version of a formula with an explicit value.'
required: false
target_darwin_amd64:
description: "Add a custom URL/checksum target for AMD64 Darwin builds."
description: 'Add a custom URL/checksum target for AMD64 Darwin builds.'
required: false
target_darwin_arm64:
description: "Add a custom URL/checksum target for ARM64 Darwin builds."
description: 'Add a custom URL/checksum target for ARM64 Darwin builds.'
required: false
target_linux_amd64:
description: "Add a custom URL/checksum target for AMD64 Linux builds."
description: 'Add a custom URL/checksum target for AMD64 Linux builds.'
required: false
target_linux_arm64:
description: "Add a custom URL/checksum target for ARM64 Linux builds."
description: 'Add a custom URL/checksum target for ARM64 Linux builds.'
required: false
update_readme_table:
description: "Update your homebrew tap's README with a table of all projects in the tap."
required: false
skip_commit:
description: "Skips committing the generated formula to a homebrew tap (useful for local testing)."
description: 'Skips committing the generated formula to a homebrew tap (useful for local testing).'
required: false
debug:
description: "Logs debugging info to console."
description: 'Logs debugging info to console.'
required: false
runs:
using: "docker"
image: "Dockerfile"
using: docker
image: docker://justintime50/homebrew-releaser:v3.0.0
args:
- ${{ inputs.homebrew_owner }}
- ${{ inputs.homebrew_tap }}
Expand Down
14 changes: 7 additions & 7 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ services:
working_dir: /github/workspace
environment:
# Env vars that should be `false` should be left empty
- GITHUB_REPOSITORY=justintime50/homebrew-releaser
- GITHUB_REPOSITORY=justintime50/github-archive
- INPUT_SKIP_COMMIT=true
- INPUT_UPDATE_README_TABLE=
- INPUT_DEBUG=true
- INPUT_HOMEBREW_OWNER=justintime50
- INPUT_HOMEBREW_TAP=homebrew-formulas
- INPUT_FORMULA_FOLDER=Formula
- INPUT_GITHUB_TOKEN=123...
- INPUT_GITHUB_TOKEN=
- INPUT_COMMIT_OWNER=username
- [email protected]
- INPUT_DEPENDS_ON='"bash" => :build'
- INPUT_INSTALL='bin.install "src/my-script.sh" => "my-script"'
- INPUT_TEST='assert_match("my script output", shell_output("my-script-command"))'
- INPUT_DEPENDS_ON="[email protected]"
- INPUT_INSTALL=virtualenv_install_with_resources
- INPUT_TEST=assert_match "github-archive \#{version}", shell_output("#{bin}/github-archive --version")
- INPUT_DOWNLOAD_STRATEGY=
- INPUT_CUSTOM_REQUIRE=
- INPUT_FORMULA_INCLUDES=
- INPUT_UPDATE_PYTHON_RESOURCES=
- INPUT_FORMULA_INCLUDES='include Language::Python::Virtualenv'
- INPUT_UPDATE_PYTHON_RESOURCES=true
- INPUT_TARGET_DARWIN_AMD64=
- INPUT_TARGET_DARWIN_ARM64=
- INPUT_TARGET_LINUX_AMD64=
Expand Down
Loading