Files
jasp-desktop-bin/scripts/gen-pkgbuild.py
T
agw d880e6748c Bundle our own R 4.5.3 instead of building against Arch's R
Arch ships R 4.6.1, but JASP pins its R packages against a frozen CRAN
snapshot (2026-03-31 for 0.98.1) that targets R 4.5. Configuring against
Arch's R fails at Rcpp 1.1.1, which cannot compile on R 4.6 since
R_NamespaceRegistry was removed; the Rcpp release that fixes it is newer
than the pinned snapshot, so the lockfile cannot reach it either.

Build R 4.5.3 into /opt/jasp-desktop/R and point R_HOME at it, matching
what upstream CI does. This drops 'r' from depends entirely, so an Arch R
upgrade can no longer break an installed JASP, and the rebuild triggers
become Qt6 and ICU - the libraries actually still linked against.
2026-09-01 21:40:00 +02:00

107 lines
3.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Turn build-info.json into a ready-to-publish PKGBUILD.
pkgrel is derived from what is currently on the AUR: same pkgver means this is a
rebuild (against a new R/Qt6), so pkgrel increments; a new pkgver resets it to 1.
"""
import argparse
import json
import re
import sys
import urllib.request
AUR_RPC = "https://aur.archlinux.org/rpc/v5/info?arg[]={}"
def next_minor(version: str) -> str:
"""4.6 -> 4.7, 6.11 -> 6.12 — the upper bound of the compatible range."""
major, minor = version.split(".")[:2]
return f"{major}.{int(minor) + 1}"
def current_aur_version(pkgname: str) -> tuple[str, int] | None:
"""(pkgver, pkgrel) currently published on the AUR, or None if absent."""
try:
with urllib.request.urlopen(AUR_RPC.format(pkgname), timeout=30) as r:
results = json.load(r).get("results", [])
except Exception as exc: # network trouble must not silently reset pkgrel
print(f"warning: could not query AUR: {exc}", file=sys.stderr)
return None
if not results:
return None
full = results[0]["Version"] # e.g. "0.98.1-2"
ver, _, rel = full.rpartition("-")
return ver, int(rel) if rel.isdigit() else 1
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--build-info", required=True)
ap.add_argument("--template", required=True)
ap.add_argument("--out", required=True)
ap.add_argument("--gh-owner", required=True)
ap.add_argument("--gh-repo", default="jasp-desktop-bin")
ap.add_argument("--maintainer", required=True)
ap.add_argument("--pkgname", default="jasp-desktop-bin")
args = ap.parse_args()
info = json.loads(open(args.build_info).read())
jasp_version = info["jasp_version"]
published = current_aur_version(args.pkgname)
if published and published[0] == jasp_version:
pkgrel = published[1] + 1
print(f"rebuild of {jasp_version}: pkgrel {published[1]} -> {pkgrel}")
else:
pkgrel = 1
print(f"new version {jasp_version}: pkgrel 1")
tag = f"v{jasp_version}-{pkgrel}"
download_url = (
f"https://github.com/{args.gh_owner}/{args.gh_repo}"
f"/releases/download/{tag}/{info['tarball']}"
)
qt_minor = info["qt6_minor"]
icu_major = info["icu_major"]
substitutions = {
"@MAINTAINER@": args.maintainer,
"@JASP_VERSION@": jasp_version,
"@PKGREL@": str(pkgrel),
"@BUNDLED_R@": info["bundled_r_version"],
"@QT_MINOR@": qt_minor,
"@QT_NEXT_MINOR@": next_minor(qt_minor),
"@ICU_MAJOR@": icu_major,
"@ICU_NEXT_MAJOR@": str(int(icu_major) + 1),
"@DOWNLOAD_URL@": download_url,
"@SHA256@": info["sha256"],
}
text = open(args.template).read()
for key, value in substitutions.items():
text = text.replace(key, value)
leftover = re.findall(r"@[A-Z0-9_]+@", text)
if leftover:
print(f"error: unsubstituted placeholders: {sorted(set(leftover))}", file=sys.stderr)
return 1
with open(args.out, "w") as fh:
fh.write(text)
# Consumed by the release + AUR-push steps of the workflow.
with open("release-meta.json", "w") as fh:
json.dump({"tag": tag, "pkgrel": pkgrel, "download_url": download_url, **info}, fh, indent=2)
print(f"wrote {args.out} ({args.pkgname} {jasp_version}-{pkgrel})")
print(f" R bundled at {info['bundled_r_version']} (not an Arch dependency)")
print(f" Qt pinned to >={qt_minor} <{next_minor(qt_minor)}")
print(f" ICU pinned to >={icu_major} <{int(icu_major) + 1}")
return 0
if __name__ == "__main__":
sys.exit(main())