poet (last release 2018) still hits the legacy pypi.io endpoint and 404s on names that don't normalize cleanly through PyPI's redirect. Replace with a small inline Python script that walks the venv's installed distributions, queries https://pypi.org/pypi/<name>/json, picks the sdist file and emits Homebrew resource blocks. Hard-fails when any package is missing an sdist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
169 lines
5.8 KiB
YAML
169 lines
5.8 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v[0-9]*'
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: macos-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.12'
|
|
|
|
- name: Compute release metadata
|
|
id: meta
|
|
run: |
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
TAG="${GITHUB_REF_NAME}"
|
|
TARBALL_URL="https://github.com/${{ github.repository }}/archive/refs/tags/${TAG}.tar.gz"
|
|
curl -fsSL -o /tmp/source.tar.gz "${TARBALL_URL}"
|
|
SOURCE_SHA=$(shasum -a 256 /tmp/source.tar.gz | awk '{print $1}')
|
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
|
echo "tarball_url=${TARBALL_URL}" >> "$GITHUB_OUTPUT"
|
|
echo "source_sha=${SOURCE_SHA}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Generate Python resource blocks
|
|
id: poet
|
|
run: |
|
|
python -m venv .poet-venv
|
|
. .poet-venv/bin/activate
|
|
pip install --upgrade pip
|
|
pip install .
|
|
python <<'PY' > resources.rb
|
|
# Walk every installed package in the venv (excluding pip/setuptools/
|
|
# wheel and the project itself), fetch sdist URL + sha256 from PyPI,
|
|
# and emit Homebrew `resource` blocks. Replaces homebrew-pypi-poet,
|
|
# which is unmaintained (2018) and breaks on modern setuptools and
|
|
# PyPI URL changes.
|
|
import json, sys, urllib.request
|
|
from importlib.metadata import distributions
|
|
|
|
SKIP = {"pip", "setuptools", "wheel", "dsmview"}
|
|
|
|
def fetch(name: str) -> dict:
|
|
url = f"https://pypi.org/pypi/{name}/json"
|
|
with urllib.request.urlopen(url, timeout=20) as r:
|
|
return json.load(r)
|
|
|
|
def sdist(releases: list) -> dict | None:
|
|
for f in releases:
|
|
if f.get("packagetype") == "sdist":
|
|
return f
|
|
return None
|
|
|
|
def block(name: str, version: str, url: str, sha: str) -> str:
|
|
return (
|
|
f' resource "{name}" do\n'
|
|
f' url "{url}"\n'
|
|
f' sha256 "{sha}"\n'
|
|
f' end\n'
|
|
)
|
|
|
|
packages = sorted(
|
|
(d.metadata["Name"], d.version)
|
|
for d in distributions()
|
|
if d.metadata["Name"].lower() not in SKIP
|
|
)
|
|
out = []
|
|
missing = []
|
|
for name, version in packages:
|
|
data = fetch(name)
|
|
files = data.get("releases", {}).get(version, [])
|
|
s = sdist(files)
|
|
if s is None:
|
|
print(f"ERROR: no sdist for {name} {version}", file=sys.stderr)
|
|
missing.append(f"{name}=={version}")
|
|
continue
|
|
out.append(block(name, version, s["url"], s["digests"]["sha256"]))
|
|
sys.stdout.write("\n".join(out))
|
|
if missing:
|
|
print(f"missing sdists: {missing}", file=sys.stderr)
|
|
sys.exit(1)
|
|
PY
|
|
echo "--- resources.rb ---"
|
|
cat resources.rb
|
|
|
|
- name: Render Formula/dsmview.rb
|
|
run: |
|
|
python <<'PY'
|
|
import re, pathlib
|
|
version = "${{ steps.meta.outputs.version }}"
|
|
tag = "${{ steps.meta.outputs.tag }}"
|
|
url = "${{ steps.meta.outputs.tarball_url }}"
|
|
src_sha = "${{ steps.meta.outputs.source_sha }}"
|
|
resources = pathlib.Path("resources.rb").read_text().rstrip() + "\n"
|
|
|
|
formula = f'''class Dsmview < Formula
|
|
include Language::Python::Virtualenv
|
|
|
|
desc "TUI SSH monitor for Synology DSM 7.x"
|
|
homepage "https://github.com/janvete/dsmview"
|
|
url "{url}"
|
|
sha256 "{src_sha}"
|
|
license "MIT"
|
|
head "https://github.com/janvete/dsmview.git", branch: "main"
|
|
|
|
depends_on "python@3.12"
|
|
depends_on "rust" => :build
|
|
|
|
{resources}
|
|
def install
|
|
virtualenv_install_with_resources
|
|
end
|
|
|
|
test do
|
|
assert_match "dsmview", shell_output("#{{bin}}/dsmview --help")
|
|
end
|
|
end
|
|
'''
|
|
pathlib.Path("Formula/dsmview.rb").write_text(formula)
|
|
PY
|
|
cat Formula/dsmview.rb
|
|
|
|
- name: Create GitHub Release
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh release create "${{ steps.meta.outputs.tag }}" \
|
|
--title "${{ steps.meta.outputs.tag }}" \
|
|
--generate-notes
|
|
|
|
- name: Push formula to tap repository
|
|
env:
|
|
TAP_TOKEN: ${{ secrets.TAP_TOKEN }}
|
|
run: |
|
|
git clone "https://x-access-token:${TAP_TOKEN}@github.com/janvete/homebrew-tools.git" tap
|
|
cp Formula/dsmview.rb tap/Formula/dsmview.rb
|
|
cd tap
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
git config user.name "github-actions[bot]"
|
|
git add Formula/dsmview.rb
|
|
git commit -m "chore: bump dsmview to ${{ steps.meta.outputs.tag }}"
|
|
git push
|
|
|
|
- name: Commit updated formula back to source repo
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
git config user.name "github-actions[bot]"
|
|
git fetch origin main
|
|
git checkout main
|
|
cp Formula/dsmview.rb /tmp/dsmview.rb
|
|
git pull --rebase origin main || true
|
|
cp /tmp/dsmview.rb Formula/dsmview.rb
|
|
git add Formula/dsmview.rb
|
|
if ! git diff --cached --quiet; then
|
|
git commit -m "chore: refresh formula for ${{ steps.meta.outputs.tag }}"
|
|
git push origin main
|
|
fi
|