CI: Full brew install + deb install tested on every push

- homebrew.yml: Build local sdist, generate formula with file:// URL and
  real resource stanzas via homebrew-pypi-poet, run full
  `brew install --build-from-source` on both macOS and Linux (Linuxbrew)
- debian.yml: Pre-seed venv with local wheel before dpkg install so
  postinstall succeeds even for unreleased versions; test init/status/add
- Both workflows trigger on push (path-filtered) and release
- Release job generates formula with PyPI URL and pushes to tap

https://claude.ai/code/session_01Vx1EsNrNySgsc8Y67dGzCn
This commit is contained in:
Claude
2026-03-15 02:55:10 +00:00
parent c8f562ee37
commit fa11bee5b5
3 changed files with 281 additions and 135 deletions

View File

@@ -3,6 +3,14 @@ name: Build Homebrew formula
on:
workflow_dispatch:
workflow_call:
push:
branches: ['**']
paths:
- 'brew_dist/**'
- 'bin/build_brew.sh'
- 'bin/release_brew.sh'
- '.github/workflows/homebrew.yml'
- 'pyproject.toml'
release:
types: [published]
@@ -10,7 +18,161 @@ permissions:
contents: read
jobs:
build:
build-and-test:
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-24.04]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Get version
id: version
run: echo "version=$(grep '^version = ' pyproject.toml | awk -F'\"' '{print $2}')" >> "$GITHUB_OUTPUT"
- name: Validate formula template syntax
run: ruby -c brew_dist/archivebox.rb
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
- name: Install build dependencies (Linux)
if: runner.os == 'Linux'
uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: build-essential python3-dev python3-setuptools libssl-dev libldap2-dev libsasl2-dev zlib1g-dev libatomic1
version: 1.0
- name: Build local sdist
run: |
uv sync --frozen --all-extras --no-install-project --no-install-workspace
uv build --sdist --out-dir /tmp/sdist/
- name: Generate formula from local sdist
run: |
VERSION="${{ steps.version.outputs.version }}"
SDIST_PATH="$(ls /tmp/sdist/archivebox-*.tar.gz | head -1)"
SDIST_SHA256="$(shasum -a 256 "$SDIST_PATH" | awk '{print $1}')"
# Install archivebox + poet into a temp venv to generate resource stanzas
python3 -m venv /tmp/poet-venv
source /tmp/poet-venv/bin/activate
pip install --quiet "$SDIST_PATH" homebrew-pypi-poet
echo "[+] Generating resource stanzas with homebrew-pypi-poet..."
RESOURCES="$(poet archivebox)"
deactivate
# For CI: use file:// URL pointing to local sdist
# For release: this gets overridden with the PyPI URL
SDIST_URL="file://${SDIST_PATH}"
cat > /tmp/archivebox.rb << RUBY
class Archivebox < Formula
include Language::Python::Virtualenv
desc "Self-hosted internet archiving solution"
homepage "https://github.com/ArchiveBox/ArchiveBox"
url "${SDIST_URL}"
sha256 "${SDIST_SHA256}"
license "MIT"
depends_on "python@3.13"
depends_on "node"
depends_on "git"
depends_on "wget"
depends_on "curl"
depends_on "ripgrep"
depends_on "yt-dlp"
on_linux do
depends_on "pkg-config" => :build
depends_on "openssl@3"
depends_on "libffi"
end
${RESOURCES}
def install
virtualenv_install_with_resources
end
def post_install
system bin/"archivebox", "install", "--binproviders", "pip,npm"
end
service do
run [opt_bin/"archivebox", "server", "--quick-init", "0.0.0.0:8000"]
keep_alive crashed: true
working_dir var/"archivebox"
log_path var/"log/archivebox.log"
error_log_path var/"log/archivebox.log"
end
test do
assert_match version.to_s, shell_output("#{bin}/archivebox version")
end
end
RUBY
echo "[√] Generated formula:"
ruby -c /tmp/archivebox.rb
cat /tmp/archivebox.rb
- name: Install Homebrew (Linux only)
if: runner.os == 'Linux'
run: |
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo '/home/linuxbrew/.linuxbrew/bin' >> "$GITHUB_PATH"
- name: Set up brew shellenv (Linux only)
if: runner.os == 'Linux'
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
echo "HOMEBREW_PREFIX=$HOMEBREW_PREFIX" >> "$GITHUB_ENV"
echo "HOMEBREW_CELLAR=$HOMEBREW_CELLAR" >> "$GITHUB_ENV"
echo "HOMEBREW_REPOSITORY=$HOMEBREW_REPOSITORY" >> "$GITHUB_ENV"
echo "$HOMEBREW_PREFIX/bin" >> "$GITHUB_PATH"
echo "$HOMEBREW_PREFIX/sbin" >> "$GITHUB_PATH"
- name: Install brew dependencies
run: |
brew install python@3.13 node git wget curl ripgrep yt-dlp
if [ "$RUNNER_OS" = "Linux" ]; then
brew install pkg-config openssl@3 libffi
fi
- name: Install archivebox via brew from local formula
run: |
brew install --build-from-source --verbose /tmp/archivebox.rb
- name: Verify archivebox version
run: archivebox version
- name: Test archivebox init
run: |
mkdir -p /tmp/archivebox-test && cd /tmp/archivebox-test
archivebox init --setup
- name: Test archivebox status
run: |
cd /tmp/archivebox-test
archivebox status
# On release only: generate the real formula with PyPI URL and push to tap
release:
if: github.event_name == 'release'
needs: build-and-test
runs-on: macos-latest
steps:
@@ -28,7 +190,6 @@ jobs:
python-version: "3.13"
- name: Wait for PyPI package availability
if: github.event_name == 'release'
run: |
VERSION="${{ steps.version.outputs.version }}"
echo "[+] Waiting for archivebox==${VERSION} to be available on PyPI..."
@@ -45,7 +206,7 @@ jobs:
sleep 30
done
- name: Generate Homebrew formula
- name: Generate release formula with PyPI URL
run: |
VERSION="${{ steps.version.outputs.version }}"
python3 -m venv /tmp/poet-venv
@@ -74,61 +235,64 @@ jobs:
deactivate
# Generate the formula file
cat > /tmp/archivebox.rb << RUBY
# Auto-generated Homebrew formula for archivebox ${VERSION}
# Generated by GitHub Actions homebrew workflow using homebrew-pypi-poet
# Auto-generated Homebrew formula for archivebox ${VERSION}
# Generated by GitHub Actions on release using homebrew-pypi-poet
#
# Users install with:
# brew tap archivebox/archivebox
# brew install archivebox
class Archivebox < Formula
include Language::Python::Virtualenv
class Archivebox < Formula
include Language::Python::Virtualenv
desc "Self-hosted internet archiving solution"
homepage "https://github.com/ArchiveBox/ArchiveBox"
url "${SDIST_URL}"
sha256 "${SDIST_SHA256}"
license "MIT"
head "https://github.com/ArchiveBox/ArchiveBox.git", branch: "dev"
desc "Self-hosted internet archiving solution"
homepage "https://github.com/ArchiveBox/ArchiveBox"
url "${SDIST_URL}"
sha256 "${SDIST_SHA256}"
license "MIT"
head "https://github.com/ArchiveBox/ArchiveBox.git", branch: "dev"
depends_on "python@3.13"
depends_on "node"
depends_on "git"
depends_on "wget"
depends_on "curl"
depends_on "ripgrep"
depends_on "yt-dlp"
depends_on "python@3.13"
depends_on "node"
depends_on "git"
depends_on "wget"
depends_on "curl"
depends_on "ripgrep"
depends_on "yt-dlp"
on_linux do
depends_on "pkg-config" => :build
depends_on "openssl@3"
depends_on "libffi"
end
on_linux do
depends_on "pkg-config" => :build
depends_on "openssl@3"
depends_on "libffi"
end
# Python dependency resource blocks auto-generated by homebrew-pypi-poet
${RESOURCES}
${RESOURCES}
def install
virtualenv_install_with_resources
end
def install
virtualenv_install_with_resources
end
def post_install
system bin/"archivebox", "install", "--binproviders", "pip,npm"
end
def post_install
system bin/"archivebox", "install", "--binproviders", "pip,npm"
end
service do
run [opt_bin/"archivebox", "server", "--quick-init", "0.0.0.0:8000"]
keep_alive crashed: true
working_dir var/"archivebox"
log_path var/"log/archivebox.log"
error_log_path var/"log/archivebox.log"
end
service do
run [opt_bin/"archivebox", "server", "--quick-init", "0.0.0.0:8000"]
keep_alive crashed: true
working_dir var/"archivebox"
log_path var/"log/archivebox.log"
error_log_path var/"log/archivebox.log"
end
test do
assert_match version.to_s, shell_output("#{bin}/archivebox version")
end
end
RUBY
test do
assert_match version.to_s, shell_output("#{bin}/archivebox version")
end
end
RUBY
echo "[√] Generated formula at /tmp/archivebox.rb"
echo "[√] Generated release formula:"
ruby -c /tmp/archivebox.rb
cat /tmp/archivebox.rb
- name: Upload formula artifact
@@ -137,15 +301,18 @@ jobs:
name: archivebox.rb
path: /tmp/archivebox.rb
- name: Test formula install
run: |
brew install --build-from-source /tmp/archivebox.rb
archivebox version
- name: Push to homebrew-archivebox tap
if: github.event_name == 'release'
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
run: |
VERSION="${{ steps.version.outputs.version }}"
git clone "https://x-access-token:${GH_TOKEN}@github.com/ArchiveBox/homebrew-archivebox.git" /tmp/tap
# Use the generated formula (with real resources), NOT the template
cp /tmp/archivebox.rb /tmp/tap/Formula/archivebox.rb 2>/dev/null || \
cp /tmp/archivebox.rb /tmp/tap/archivebox.rb
@@ -160,76 +327,3 @@ jobs:
git push origin HEAD
echo "[√] Formula pushed to homebrew-archivebox tap"
fi
test-macos:
needs: build
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Download formula artifact
uses: actions/download-artifact@v4
with:
name: archivebox.rb
path: /tmp/
- name: Install dependencies
run: |
brew install python@3.13 node git wget curl ripgrep yt-dlp
- name: Test Homebrew formula install
run: |
brew install --build-from-source /tmp/archivebox.rb
- name: Verify archivebox CLI
run: |
archivebox version
- name: Test archivebox init
run: |
mkdir -p /tmp/archivebox-test && cd /tmp/archivebox-test
archivebox init --setup
archivebox status
test-linux:
needs: build
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Download formula artifact
uses: actions/download-artifact@v4
with:
name: archivebox.rb
path: /tmp/
- name: Install Homebrew on Linux
run: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo >> "$GITHUB_ENV"
echo 'HOMEBREW_PREFIX=/home/linuxbrew/.linuxbrew' >> "$GITHUB_ENV"
echo '/home/linuxbrew/.linuxbrew/bin' >> "$GITHUB_PATH"
- name: Install dependencies
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
brew install python@3.13 node git wget curl ripgrep yt-dlp pkg-config openssl@3 libffi
- name: Test Homebrew formula install
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
brew install --build-from-source /tmp/archivebox.rb
- name: Verify archivebox CLI
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
archivebox version
- name: Test archivebox init
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
mkdir -p /tmp/archivebox-test && cd /tmp/archivebox-test
archivebox init --setup
archivebox status