Skip to content

Homebrew Formula PR #11

Homebrew Formula PR

Homebrew Formula PR #11

name: Homebrew Formula PR
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: "Release tag to package (e.g. v0.1.0). Defaults to current ref name."
required: false
tap_repo:
description: "Override tap repository (org/homebrew-foo). Defaults to repository variable HOMEBREW_TAP_REPO."
required: false
jobs:
open-tap-pr:
runs-on: ubuntu-latest
env:
DEFAULT_FORMULA_NAME: meta-ads-cli
DEFAULT_FORMULA_PATH: Formula/meta-ads-cli.rb
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
steps:
- name: Checkout source repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Resolve workflow configuration
id: cfg
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
INPUT_TAG: ${{ github.event.inputs.tag }}
INPUT_TAP_REPO: ${{ github.event.inputs.tap_repo }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
REF_NAME: ${{ github.ref_name }}
REPO_VAR_TAP_REPO: ${{ vars.HOMEBREW_TAP_REPO }}
REPO_VAR_FORMULA_NAME: ${{ vars.HOMEBREW_FORMULA_NAME }}
REPO_VAR_FORMULA_PATH: ${{ vars.HOMEBREW_FORMULA_PATH }}
REPO_VAR_BASE_BRANCH: ${{ vars.HOMEBREW_TAP_BASE_BRANCH }}
run: |
set -euo pipefail
TAG="${INPUT_TAG:-}"
if [ -z "$TAG" ]; then
TAG="${RELEASE_TAG:-}"
fi
if [ -z "$TAG" ] && [ "$GITHUB_EVENT_NAME" != "workflow_dispatch" ]; then
TAG="${REF_NAME:-}"
fi
if [ -z "$TAG" ]; then
echo "Unable to resolve release tag. For manual runs, provide workflow input 'tag' (for example: v0.1.0)."
exit 1
fi
TAP_REPO="${INPUT_TAP_REPO:-}"
if [ -z "$TAP_REPO" ]; then
TAP_REPO="${REPO_VAR_TAP_REPO:-}"
fi
if [ -z "$TAP_REPO" ]; then
echo "HOMEBREW_TAP_REPO is not configured. Set repository variable HOMEBREW_TAP_REPO or pass tap_repo input."
exit 1
fi
FORMULA_NAME="${REPO_VAR_FORMULA_NAME:-$DEFAULT_FORMULA_NAME}"
FORMULA_PATH="${REPO_VAR_FORMULA_PATH:-$DEFAULT_FORMULA_PATH}"
BASE_BRANCH="${REPO_VAR_BASE_BRANCH:-}"
SOURCE_URL="https://github.com/${GITHUB_REPOSITORY}/archive/refs/tags/${TAG}.tar.gz"
HOMEPAGE="https://github.com/${GITHUB_REPOSITORY}"
{
echo "tag=$TAG"
echo "tap_repo=$TAP_REPO"
echo "formula_name=$FORMULA_NAME"
echo "formula_path=$FORMULA_PATH"
echo "base_branch=$BASE_BRANCH"
echo "source_url=$SOURCE_URL"
echo "homepage=$HOMEPAGE"
} >> "$GITHUB_OUTPUT"
- name: Show resolved release configuration
run: |
echo "Tag: ${{ steps.cfg.outputs.tag }}"
echo "Tap repo: ${{ steps.cfg.outputs.tap_repo }}"
echo "Formula path: ${{ steps.cfg.outputs.formula_path }}"
echo "Base branch: ${{ steps.cfg.outputs.base_branch }}"
echo "Source URL: ${{ steps.cfg.outputs.source_url }}"
- name: Validate tap repository access
id: tap
env:
TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
TAP_REPO: ${{ steps.cfg.outputs.tap_repo }}
CFG_BASE_BRANCH: ${{ steps.cfg.outputs.base_branch }}
run: |
set -euo pipefail
if [ -z "${TOKEN:-}" ]; then
echo "HOMEBREW_TAP_TOKEN is not set. Add it in repository secrets."
exit 1
fi
STATUS_CODE="$(curl -sS -o tap_repo.json -w "%{http_code}" \
-H "Authorization: token ${TOKEN}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${TAP_REPO}")"
if [ "$STATUS_CODE" != "200" ]; then
echo "Unable to access tap repo '${TAP_REPO}' with provided token (HTTP ${STATUS_CODE})."
echo "Response:"
cat tap_repo.json
exit 1
fi
python3 - <<'PY'
import json
import os
from pathlib import Path
payload = json.loads(Path('tap_repo.json').read_text())
name = payload.get('full_name')
permissions = payload.get('permissions') or {}
default_branch = (payload.get('default_branch') or '').strip()
configured_branch = (os.getenv('CFG_BASE_BRANCH') or '').strip()
resolved_branch = configured_branch or default_branch
print(f"Verified tap repository access: {name}")
print(f"Tap default branch: {default_branch or '<none>'}")
print(f"Resolved base branch for PR: {resolved_branch or '<none>'}")
if permissions and not permissions.get('push', False):
raise SystemExit(
"Token can read tap repo but has no push permission. "
"Grant contents write access for PR branch creation."
)
if not default_branch:
raise SystemExit(
"Tap repository has no default branch. "
"Initialize it with an initial commit (for example README.md on main) "
"and rerun the workflow."
)
if not resolved_branch:
raise SystemExit(
"Could not resolve base branch for tap repository. "
"Set HOMEBREW_TAP_BASE_BRANCH or initialize default branch."
)
output_path = os.environ['GITHUB_OUTPUT']
with open(output_path, 'a', encoding='utf-8') as handle:
handle.write(f"base_branch={resolved_branch}\n")
PY
- name: Compute source archive SHA256
id: sha
env:
SOURCE_URL: ${{ steps.cfg.outputs.source_url }}
TAG: ${{ steps.cfg.outputs.tag }}
run: |
set -euo pipefail
echo "Downloading source archive: $SOURCE_URL"
if ! curl -fLSs "$SOURCE_URL" -o source.tar.gz; then
echo "Failed to download source archive for tag '$TAG'."
echo "Confirm the tag exists in ${GITHUB_REPOSITORY} and is published."
echo "URL attempted: $SOURCE_URL"
exit 1
fi
SOURCE_SHA256="$(sha256sum source.tar.gz | awk '{print $1}')"
echo "source_sha256=$SOURCE_SHA256" >> "$GITHUB_OUTPUT"
- name: Checkout Homebrew tap repository
uses: actions/checkout@v4
with:
repository: ${{ steps.cfg.outputs.tap_repo }}
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: tap
ref: ${{ steps.tap.outputs.base_branch }}
- name: Generate formula file
env:
FORMULA_NAME: ${{ steps.cfg.outputs.formula_name }}
FORMULA_PATH: ${{ steps.cfg.outputs.formula_path }}
HOMEPAGE: ${{ steps.cfg.outputs.homepage }}
SOURCE_URL: ${{ steps.cfg.outputs.source_url }}
SOURCE_SHA256: ${{ steps.sha.outputs.source_sha256 }}
run: |
set -euo pipefail
python3 scripts/generate_brew_formula.py \
--formula-name "$FORMULA_NAME" \
--homepage "$HOMEPAGE" \
--source-url "$SOURCE_URL" \
--source-sha256 "$SOURCE_SHA256" \
--output "tap/$FORMULA_PATH"
- name: Create pull request in tap repository
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: tap
branch: automation/update-${{ steps.cfg.outputs.formula_name }}-${{ steps.cfg.outputs.tag }}
base: ${{ steps.tap.outputs.base_branch }}
delete-branch: true
title: "chore(homebrew): update ${{ steps.cfg.outputs.formula_name }} for ${{ steps.cfg.outputs.tag }}"
commit-message: "chore(homebrew): update ${{ steps.cfg.outputs.formula_name }} for ${{ steps.cfg.outputs.tag }}"
body: |
Automated update from `${{ github.repository }}`.
- Tag: `${{ steps.cfg.outputs.tag }}`
- Source URL: `${{ steps.cfg.outputs.source_url }}`
- Source SHA256: `${{ steps.sha.outputs.source_sha256 }}`
Generated via `scripts/generate_brew_formula.py`.