Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dff7700e1 | ||
|
|
4c2f498e41 | ||
|
|
86b22226e2 | ||
|
|
fffeb7f227 | ||
|
|
4d5156f7d3 | ||
|
|
4fd688e673 | ||
|
|
512c9a663f |
No files matched your search
@@ -52,8 +52,13 @@ repo-wide reformat as a side effect of an unrelated change.
|
|||||||
- **`contrib/homebrew/`** — a **git submodule** of the tap at
|
- **`contrib/homebrew/`** — a **git submodule** of the tap at
|
||||||
`https://tildeforge.dev/beefcarver/homebrew`, not a copy. Formula changes
|
`https://tildeforge.dev/beefcarver/homebrew`, not a copy. Formula changes
|
||||||
belong in the tap repository; commit them there and bump the gitlink here.
|
belong in the tap repository; commit them there and bump the gitlink here.
|
||||||
Never add loose files under that path. The formula still carries a
|
Never add loose files under that path. The formula's Python dependencies are
|
||||||
placeholder `sha256`, so it only installs with `--HEAD`.
|
vendored as `resource` blocks: Homebrew installs the sdist with `--no-deps`,
|
||||||
|
so a dependency that has no block (or whose constraint changed in
|
||||||
|
`pyproject.toml`) is simply missing at runtime. Regenerate them in the tap
|
||||||
|
with `brew update-python-resources Formula/b/beefy-player.rb`;
|
||||||
|
`uv run python bump_packaging.py <version> --check` reports a dependency
|
||||||
|
without one.
|
||||||
|
|
||||||
## Releases
|
## Releases
|
||||||
|
|
||||||
|
|||||||
+91
-2
@@ -33,15 +33,18 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
import ssl
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tarfile
|
import tarfile
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parent.parent
|
ROOT = Path(__file__).resolve().parent
|
||||||
PYPROJECT = ROOT / "pyproject.toml"
|
PYPROJECT = ROOT / "pyproject.toml"
|
||||||
INIT = ROOT / "beefy_player/__init__.py"
|
INIT = ROOT / "beefy_player/__init__.py"
|
||||||
FLAKE = ROOT / "flake.nix"
|
FLAKE = ROOT / "flake.nix"
|
||||||
@@ -193,6 +196,30 @@ def sha256(path: Path) -> str:
|
|||||||
return digest.hexdigest()
|
return digest.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def ca_file() -> str | None:
|
||||||
|
"""The CA bundle to verify with, or None to let OpenSSL use its own default.
|
||||||
|
|
||||||
|
`uv python install` fetches a python-build-standalone interpreter whose
|
||||||
|
OpenSSL is compiled with default trust paths that need not exist on the
|
||||||
|
host (on macOS, and on distros that keep certs only in
|
||||||
|
/etc/ssl/certs/ca-certificates.crt, they often do not). Verification then
|
||||||
|
fails with `unable to get local issuer certificate` for every site, even
|
||||||
|
though curl and browsers are happy. Fall back to certifi, which `httpx`
|
||||||
|
already puts in the environment.
|
||||||
|
"""
|
||||||
|
if os.environ.get("SSL_CERT_FILE"):
|
||||||
|
return None # ssl honours the variable itself; do not second-guess it
|
||||||
|
paths = ssl.get_default_verify_paths()
|
||||||
|
for candidate in (paths.cafile, paths.openssl_cafile):
|
||||||
|
if candidate and Path(candidate).is_file():
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
import certifi
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
return None
|
||||||
|
return certifi.where()
|
||||||
|
|
||||||
|
|
||||||
def fetch_asset(version: str, override: Path | None) -> Path:
|
def fetch_asset(version: str, override: Path | None) -> Path:
|
||||||
if override is not None:
|
if override is not None:
|
||||||
if not override.is_file():
|
if not override.is_file():
|
||||||
@@ -200,10 +227,25 @@ def fetch_asset(version: str, override: Path | None) -> Path:
|
|||||||
return override
|
return override
|
||||||
url = RELEASE_ASSET.format(version=version, name=ASSET_NAME)
|
url = RELEASE_ASSET.format(version=version, name=ASSET_NAME)
|
||||||
say(f"downloading {url}")
|
say(f"downloading {url}")
|
||||||
|
cafile = ca_file()
|
||||||
|
context = ssl.create_default_context(cafile=cafile)
|
||||||
target = Path(tempfile.mkdtemp()) / f"{ASSET_NAME}-{version}.tar.gz"
|
target = Path(tempfile.mkdtemp()) / f"{ASSET_NAME}-{version}.tar.gz"
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(url) as response, target.open("wb") as handle:
|
with urllib.request.urlopen(url, context=context) as response, target.open(
|
||||||
|
"wb"
|
||||||
|
) as handle:
|
||||||
handle.write(response.read())
|
handle.write(response.read())
|
||||||
|
except urllib.error.URLError as error:
|
||||||
|
# urlopen wraps socket/SSL failures; the cause is in `.reason`.
|
||||||
|
if isinstance(error.reason, ssl.SSLCertVerificationError):
|
||||||
|
bundle = cafile or ssl.get_default_verify_paths().cafile or "no bundle found"
|
||||||
|
raise fail(
|
||||||
|
f"cannot fetch the release asset ({error.reason.verify_message or error.reason}); "
|
||||||
|
f"verified against {bundle}. If that bundle is stale, point SSL_CERT_FILE "
|
||||||
|
"at a current one (on Arch and Debian that is "
|
||||||
|
"/etc/ssl/certs/ca-certificates.crt)"
|
||||||
|
) from error
|
||||||
|
raise fail(f"cannot fetch the release asset ({error.reason}); is the tag published?") from error
|
||||||
except OSError as error:
|
except OSError as error:
|
||||||
raise fail(f"cannot fetch the release asset ({error}); is the tag published?") from error
|
raise fail(f"cannot fetch the release asset ({error}); is the tag published?") from error
|
||||||
return target
|
return target
|
||||||
@@ -293,6 +335,50 @@ def bump_srcinfo() -> None:
|
|||||||
say("regenerated contrib/aur/.SRCINFO")
|
say("regenerated contrib/aur/.SRCINFO")
|
||||||
|
|
||||||
|
|
||||||
|
#: Names that may appear as `resource` blocks in the formula. A Python
|
||||||
|
#: dependency without one is silently skipped at install time because Homebrew
|
||||||
|
#: installs the sdist `--no-deps`, which surfaces as an ImportError on launch
|
||||||
|
#: rather than as a packaging error.
|
||||||
|
RESOURCE_LINE = re.compile(r'(?m)^ resource "([^"]+)" do$')
|
||||||
|
|
||||||
|
|
||||||
|
def pypi_name(name: str) -> str:
|
||||||
|
"""PEP 503 name: lowercase, runs of -_. collapsed to a single hyphen."""
|
||||||
|
return re.sub(r"[-_.]+", "-", name).lower()
|
||||||
|
|
||||||
|
|
||||||
|
def runtime_dependencies() -> list[str]:
|
||||||
|
"""The distribution names in `pyproject.toml`'s `[project] dependencies`."""
|
||||||
|
block = re.search(r"(?ms)^dependencies = \[(.*?)^\]$", PYPROJECT.read_text())
|
||||||
|
if block is None:
|
||||||
|
raise fail("no dependencies array in pyproject.toml")
|
||||||
|
return re.findall(r'^\s*"([A-Za-z0-9._-]+)', block.group(1), re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
def formula_excluded_resources() -> set[str]:
|
||||||
|
"""Dependencies the formula deliberately leaves to other formulae."""
|
||||||
|
match = re.search(r'pypi_packages .*exclude_packages: "([^"]*)"', FORMULA.read_text())
|
||||||
|
if match is None:
|
||||||
|
return set()
|
||||||
|
return {pypi_name(name.strip()) for name in match.group(1).split(",") if name.strip()}
|
||||||
|
|
||||||
|
|
||||||
|
def check_formula_resources(problems: list[str]) -> None:
|
||||||
|
"""Every runtime dependency needs a `resource`, unless another formula has it."""
|
||||||
|
resources = {pypi_name(name) for name in RESOURCE_LINE.findall(FORMULA.read_text())}
|
||||||
|
if not resources:
|
||||||
|
problems.append("formula vendors no Python resources; it would install --no-deps")
|
||||||
|
excluded = formula_excluded_resources()
|
||||||
|
for dependency in runtime_dependencies():
|
||||||
|
name = pypi_name(dependency)
|
||||||
|
if name in excluded or name in resources:
|
||||||
|
continue
|
||||||
|
problems.append(
|
||||||
|
f"formula has no resource for {name}"
|
||||||
|
" (run `brew update-python-resources Formula/b/beefy-player.rb` in the tap)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def bump_formula(version: str, checksum: str) -> None:
|
def bump_formula(version: str, checksum: str) -> None:
|
||||||
text = FORMULA.read_text()
|
text = FORMULA.read_text()
|
||||||
url = RELEASE_ASSET.format(version=version, name=ASSET_NAME)
|
url = RELEASE_ASSET.format(version=version, name=ASSET_NAME)
|
||||||
@@ -302,6 +388,8 @@ def bump_formula(version: str, checksum: str) -> None:
|
|||||||
)
|
)
|
||||||
# The default branch is master, and --HEAD is only useful if it resolves.
|
# The default branch is master, and --HEAD is only useful if it resolves.
|
||||||
text = text.replace('branch: "main"', 'branch: "master"')
|
text = text.replace('branch: "main"', 'branch: "master"')
|
||||||
|
# The `resource` blocks pin versions, not the release: they only go stale
|
||||||
|
# when pyproject.toml's dependencies change, which `--check` reports.
|
||||||
FORMULA.write_text(text)
|
FORMULA.write_text(text)
|
||||||
say("wrote contrib/homebrew/Formula/b/beefy-player.rb")
|
say("wrote contrib/homebrew/Formula/b/beefy-player.rb")
|
||||||
|
|
||||||
@@ -393,6 +481,7 @@ def check(version: str) -> int:
|
|||||||
problems.append('formula head branch is "main"; this repository uses master')
|
problems.append('formula head branch is "main"; this repository uses master')
|
||||||
if re.search(r'sha256 "0{64}"', formula):
|
if re.search(r'sha256 "0{64}"', formula):
|
||||||
problems.append("formula still has the placeholder sha256")
|
problems.append("formula still has the placeholder sha256")
|
||||||
|
check_formula_resources(problems)
|
||||||
|
|
||||||
for problem in problems:
|
for problem in problems:
|
||||||
say(f"drift: {problem}")
|
say(f"drift: {problem}")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
pkgbase = beefy-player
|
pkgbase = beefy-player
|
||||||
pkgdesc = Flashy terminal player for internet radio with a live spectrum analyser
|
pkgdesc = Flashy terminal player for internet radio with a live spectrum analyser
|
||||||
pkgver = 1.2.2
|
pkgver = 1.2.5
|
||||||
pkgrel = 1
|
pkgrel = 1
|
||||||
url = https://tildeforge.dev/beefcarver/beefy-player
|
url = https://tildeforge.dev/beefcarver/beefy-player
|
||||||
arch = any
|
arch = any
|
||||||
@@ -22,7 +22,7 @@ pkgbase = beefy-player
|
|||||||
optdepends = libpulse: capture the output monitor so the analyser follows what you hear (parec/pactl)
|
optdepends = libpulse: capture the output monitor so the analyser follows what you hear (parec/pactl)
|
||||||
optdepends = mpv: preferred audio backend
|
optdepends = mpv: preferred audio backend
|
||||||
optdepends = tmux: detect the outer terminal for kitty graphics passthrough inside tmux
|
optdepends = tmux: detect the outer terminal for kitty graphics passthrough inside tmux
|
||||||
source = beefy-player-1.2.2.tar.gz::https://tildeforge.dev/beefcarver/beefy-player/archive/941c7446384bf346e40d1438a7ce67939d9615df.tar.gz
|
source = beefy-player-1.2.5.tar.gz::https://tildeforge.dev/beefcarver/beefy-player/releases/download/v1.2.5/beefy_player-1.2.5.tar.gz
|
||||||
sha256sums = 8ac568676ef5ef8345441c28e4f8f5d61fb06d5225e566ac578acae18672c778
|
sha256sums = 344f2266cb63d05bf294349599ecef636b3c8c25c8b29bcf0eff8818394b5f74
|
||||||
|
|
||||||
pkgname = beefy-player
|
pkgname = beefy-player
|
||||||
+10
-12
@@ -1,7 +1,7 @@
|
|||||||
# Maintainer: beefy-player contributors <[email protected]>
|
# Maintainer: beefy-player contributors <[email protected]>
|
||||||
|
|
||||||
pkgname=beefy-player
|
pkgname=beefy-player
|
||||||
pkgver=1.2.2
|
pkgver=1.2.5
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Flashy terminal player for internet radio with a live spectrum analyser"
|
pkgdesc="Flashy terminal player for internet radio with a live spectrum analyser"
|
||||||
arch=('any')
|
arch=('any')
|
||||||
@@ -33,27 +33,25 @@ optdepends=(
|
|||||||
'tmux: detect the outer terminal for kitty graphics passthrough inside tmux'
|
'tmux: detect the outer terminal for kitty graphics passthrough inside tmux'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Upstream does not tag releases yet, so the source is the archive of the
|
# The sdist attached to the v$pkgver release. bump_packaging.py keeps
|
||||||
# commit that carries this version. Once a `v$pkgver` tag exists, switch to
|
# this and sha256sums in step with the tag; build/check/package use
|
||||||
#
|
# $_srcname because the sdist unpacks to hatchling's PEP 503 name.
|
||||||
# source=("$pkgname-$pkgver.tar.gz::$url/archive/v$pkgver.tar.gz")
|
_srcname="beefy_player-$pkgver"
|
||||||
#
|
source=("$pkgname-$pkgver.tar.gz::$url/releases/download/v$pkgver/$_srcname.tar.gz")
|
||||||
_commit=941c7446384bf346e40d1438a7ce67939d9615df
|
sha256sums=('344f2266cb63d05bf294349599ecef636b3c8c25c8b29bcf0eff8818394b5f74')
|
||||||
source=("$pkgname-$pkgver.tar.gz::$url/archive/$_commit.tar.gz")
|
|
||||||
sha256sums=('8ac568676ef5ef8345441c28e4f8f5d61fb06d5225e566ac578acae18672c778')
|
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "$pkgname"
|
cd "$_srcname"
|
||||||
python -m build --wheel --no-isolation
|
python -m build --wheel --no-isolation
|
||||||
}
|
}
|
||||||
|
|
||||||
check() {
|
check() {
|
||||||
cd "$pkgname"
|
cd "$_srcname"
|
||||||
# The suite is offline: catalogues are fixtures, mpv/parec are faked.
|
# The suite is offline: catalogues are fixtures, mpv/parec are faked.
|
||||||
python -m pytest -q
|
python -m pytest -q
|
||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd "$pkgname"
|
cd "$_srcname"
|
||||||
python -m installer --destdir="$pkgdir" dist/*.whl
|
python -m installer --destdir="$pkgdir" dist/*.whl
|
||||||
}
|
}
|
||||||
+1
-1
Submodule contrib/homebrew updated: ad6fcc5732...c958da0067.
Reference in new issue
Block a user