Wire up GitHub Actions for deb/brew build, test, and release

- Fix debian.yml: pin nfpm version, add permissions, improve test job
  with user creation, init test, and status check
- Fix homebrew.yml: use PyPI JSON API (macOS-compatible, no grep -oP),
  wait for PyPI availability on release, use generated formula not template,
  add Linux (Linuxbrew) test job alongside macOS
- Add release.yml orchestrator: pip → deb + brew + docker in order
- Add workflow_call triggers to pip.yml and docker.yml
- Fix build_brew.sh: replace grep -oP with Python-based PyPI API,
  add on_linux deps (pkg-config, openssl, libffi)
- Fix setup.sh: use GitHub API to find correct .deb download URL
  (filename includes version number)
- Fix postinstall.sh: create archivebox system user, pin version from
  package, check for systemd before daemon-reload
- Fix preremove.sh: stop service before removal, check for systemd
- Fix install.sh: fallback to latest if pinned version not on PyPI
- Add on_linux deps to brew formula for Linuxbrew support
- Tested: .deb builds, installs, creates user, runs archivebox init

https://claude.ai/code/session_01Vx1EsNrNySgsc8Y67dGzCn
This commit is contained in:
Claude
2026-03-15 02:50:14 +00:00
parent f3fcc1584c
commit c8f562ee37
12 changed files with 336 additions and 27 deletions

View File

@@ -2,9 +2,13 @@ name: Build Homebrew formula
on:
workflow_dispatch:
workflow_call:
release:
types: [published]
permissions:
contents: read
jobs:
build:
runs-on: macos-latest
@@ -23,36 +27,141 @@ jobs:
with:
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..."
for i in $(seq 1 30); do
if pip index versions archivebox 2>/dev/null | grep -q "$VERSION"; then
echo "[√] archivebox==${VERSION} is available on PyPI"
break
fi
if [ "$i" -eq 30 ]; then
echo "[!] Timed out waiting for PyPI. Trying to install anyway..."
break
fi
echo " Attempt $i/30 - not yet available, waiting 30s..."
sleep 30
done
- name: Generate Homebrew formula
run: |
VERSION="${{ steps.version.outputs.version }}"
python3 -m venv /tmp/poet-venv
source /tmp/poet-venv/bin/activate
pip install --quiet "archivebox==${{ steps.version.outputs.version }}" homebrew-pypi-poet
poet -f archivebox > /tmp/archivebox-generated.rb
pip install --quiet "archivebox==${VERSION}" homebrew-pypi-poet
echo "[+] Generating resource stanzas with homebrew-pypi-poet..."
RESOURCES="$(poet archivebox)"
# Get sdist URL and SHA256 from PyPI JSON API
PYPI_JSON="$(curl -fsSL "https://pypi.org/pypi/archivebox/${VERSION}/json" 2>/dev/null || echo '')"
SDIST_URL=""
SDIST_SHA256=""
if [ -n "$PYPI_JSON" ]; then
SDIST_URL="$(echo "$PYPI_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(next((u['url'] for u in d['urls'] if u['packagetype']=='sdist'), ''))" 2>/dev/null || echo '')"
SDIST_SHA256="$(echo "$PYPI_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(next((u['digests']['sha256'] for u in d['urls'] if u['packagetype']=='sdist'), ''))" 2>/dev/null || echo '')"
fi
if [ -z "$SDIST_URL" ]; then
SDIST_URL="https://files.pythonhosted.org/packages/source/a/archivebox/archivebox-${VERSION}.tar.gz"
fi
if [ -z "$SDIST_SHA256" ]; then
pip download --no-binary :all: --no-deps -d /tmp/sdist "archivebox==${VERSION}" 2>/dev/null || true
SDIST_SHA256="$(shasum -a 256 /tmp/sdist/*.tar.gz 2>/dev/null | awk '{print $1}' || echo '')"
fi
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
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"
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
# Python dependency resource blocks auto-generated by homebrew-pypi-poet
${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 at /tmp/archivebox.rb"
cat /tmp/archivebox.rb
- name: Upload formula artifact
uses: actions/upload-artifact@v4
with:
name: archivebox.rb
path: /tmp/archivebox-generated.rb
path: /tmp/archivebox.rb
- name: Push to homebrew-archivebox tap
if: github.event_name == 'release'
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
run: |
# Clone the tap repo and update the formula
VERSION="${{ steps.version.outputs.version }}"
git clone "https://x-access-token:${GH_TOKEN}@github.com/ArchiveBox/homebrew-archivebox.git" /tmp/tap
cp brew_dist/archivebox.rb /tmp/tap/archivebox.rb 2>/dev/null || cp /tmp/archivebox-generated.rb /tmp/tap/archivebox.rb
# 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
cd /tmp/tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add archivebox.rb
git diff --cached --quiet || git commit -m "Update archivebox to v${{ steps.version.outputs.version }}"
git push origin HEAD
git add -A
if git diff --cached --quiet; then
echo "[i] No changes to formula, skipping push."
else
git commit -m "Update archivebox to v${VERSION}"
git push origin HEAD
echo "[√] Formula pushed to homebrew-archivebox tap"
fi
test:
test-macos:
needs: build
runs-on: macos-latest
@@ -65,7 +174,62 @@ jobs:
name: archivebox.rb
path: /tmp/
- name: Test Homebrew formula
- 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: |
brew install --build-from-source /tmp/archivebox-generated.rb || true
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