Files
jasp-desktop-bin/README.md
T
agw f0f317ca2f Document binary provenance in the PKGBUILD header
Checked what the AUR actually requires rather than relying on my earlier
assumption. The submission guidelines say only that packages using prebuilt
deliverables must carry the -bin suffix; nothing requires the binary to come
from upstream, so -bin is correct and sufficient here.

There is no formal 'unofficial binary' flag, so disclose it where users look:
an explicit PROVENANCE block in the PKGBUILD comment header naming the tag,
commit, build recipe and release URL, plus '(prebuilt binary)' in pkgdesc.

Corrects the README, which overstated the risk of a maintainer objecting.
2026-09-01 22:30:00 +02:00

155 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# jasp-desktop-bin
Automated binary builds of [JASP](https://jasp-stats.org/) for Arch Linux, published
to the AUR as `jasp-desktop-bin`.
## Why this exists
Upstream JASP ships installers for Windows and macOS only — there is **no official
Linux binary**. The official Linux channel is the Flathub build. The existing AUR
package `jasp-desktop` therefore builds everything from source, including
`buildAllDefaultJaspModules.R`, which compiles several hundred R packages. That is
the multi-hour build this repo exists to avoid: the compile happens once, here, and
users download the result.
## The constraint that shapes everything
JASP pins its R package set with renv lockfiles against a **frozen CRAN snapshot**
— for 0.98.1, `Modules/Rcpp_RInside.lock` points at the Posit snapshot of
2026-03-31. That set targets R 4.5.
Building against Arch's current R (4.6.1) fails immediately: Rcpp 1.1.1 does not
compile there, because `R_NamespaceRegistry` is gone in R 4.6. The Rcpp release
that fixes this (1.1.2, 2026-07-05) is *newer than the pinned snapshot*, so the
lockfile cannot resolve it either. This is precisely the "old libraries with
issues" problem that makes the source AUR package painful.
So this build **compiles and bundles its own R 4.5.3** into
`/opt/jasp-desktop/R` and points `R_HOME` at it — which is what upstream's own CI
does (`.github/workflows/ubuntu.yml` sets `JASP_R_VERSION: 4.5.3` and an explicit
`R_HOME`). `Tools/CMake/R.cmake` reads `R_HOME` from the environment on Linux, so
this is a supported configuration rather than a hack.
## What is bundled, and why nothing is version-pinned
After staging, `build/bundle-libs.sh` walks every ELF object in the tree, copies
each non-system shared library it depends on into `/opt/jasp-desktop/lib`, and
rewrites the rpath to look there. It works by *denylist*: glibc, libstdc++, Qt6,
the GL/X11/Wayland/dbus stack, fontconfig and freetype stay system-provided;
everything else — ICU, jsoncpp, glpk, JAGS, readstat, librdata, BLAS/LAPACK,
pcre2, readline — is bundled.
The point is that **`depends` contains no version constraints at all**. That is a
deliberate reversal of the obvious design. Pinning (`icu>=78 <79`) looks safer but
is actively harmful on a rolling distro: when Arch ships the next ICU, pacman
would refuse to upgrade it while JASP is installed, blocking the user's entire
system upgrade until a rebuild lands. Bundling means JASP never constrains the
system, and a `pacman -Syu` cannot break it.
Qt6 is the deliberate exception and stays a system dependency. Qt guarantees
binary compatibility across Qt 6 minor releases, and bundling QtWebEngine would
add ~500 MB for very little gain. A Qt 7 transition will need a rebuild — which is
why `scripts/check-upstream.py` watches the Qt *major* and nothing else besides
the JASP version. Rebuilding for anything else would cost every user a ~1.7 GB
re-download for no benefit.
`jags` and `librdata-git` are AUR-only, and JASP needs `freexl >= 2.0.99` while Arch
ships 2.0.0 — so the build compiles those itself, matching what upstream CI does.
## Layout
| Path | Purpose |
| --- | --- |
| `build/build-jasp.sh` | The actual build. Runs in an `archlinux:base-devel` container, emits the tarball plus `build-info.json`. |
| `build/bundle-libs.sh` | Copies non-system shared libraries into `/opt/jasp-desktop/lib` and rewrites rpaths, so the package needs no version-pinned deps. |
| `scripts/check-upstream.py` | Compares upstream JASP and the Arch Qt major against `state.json`; decides whether to build. |
| `scripts/gen-pkgbuild.py` | Fills `packaging/PKGBUILD.in` from `build-info.json`; derives `pkgrel` from what is on the AUR. |
| `scripts/publish-github-release.sh` | Creates the GitHub release and uploads the tarball. |
| `scripts/push-aur.sh` | Generates `.SRCINFO` and pushes to `aur.archlinux.org`. |
| `scripts/record-state.sh` | Commits `state.json` so the next nightly run knows what was built. |
| `packaging/PKGBUILD.in` | Template for the AUR package. |
| `state.json` | Last successfully built combination. |
## Flow
```
nightly (03:00) ─► check-upstream.py
│ new JASP tag, or a Qt major transition?
▼
build-jasp.sh (Arch container, hours)
│ builds R 4.5.3, JASP, all R modules, then bundles libs
│ jasp-desktop-<ver>-x86_64.tar.zst + build-info.json
▼
GitHub release ──► gen-pkgbuild.py ──► push to AUR
│
▼
state.json committed back
```
## Required configuration
Set these in the Gitea repo before the first run.
**Variables** (Settings → Actions → Variables):
| Name | Example | Meaning |
| --- | --- | --- |
| `GH_OWNER` | `asgerw` | GitHub account hosting the release repo |
| `GH_REPO` | `jasp-desktop-bin` | Release repo name |
| `AUR_MAINTAINER` | `Asger Geel Weirsoe <asger@weircon.dk>` | Written into the PKGBUILD header |
**Secrets** (Settings → Actions → Secrets):
| Name | Meaning |
| --- | --- |
| `GH_TOKEN` | GitHub PAT with `contents: write` on the release repo |
| `AUR_SSH_PRIVATE_KEY` | Private SSH key registered with your AUR account. Copy the value already set on `agw/pi3-smart-workspace`. |
| `STATE_PUSH_TOKEN` | Gitea token with write access to this repo, for committing `state.json`. (Gitea reserves the `GITEA_` secret prefix.) |
The GitHub repo `GH_OWNER/GH_REPO` must exist and be public — the AUR PKGBUILD
downloads the tarball from its releases, so it has to be reachable anonymously.
## Runner requirements
The build runs for hours and needs disk. On the Gitea runner host:
- `runner.timeout` in `/var/lib/gitea-worker/config.yaml` must exceed the build
time (set to `16h`).
- Gitea's own job timeout (`[actions] ENDLESS_TASK_TIMEOUT` in `app.ini`, default
3h) must match, otherwise Gitea kills the job at 3h regardless of the runner.
- Roughly 40–60 GB of free disk for the build tree, the bundled R and the
staged install.
## Running it by hand
```
# Build a specific tag without waiting for the schedule
Actions → Build JASP binary and publish to AUR → Run workflow
jasp_tag = v0.98.1 force = true
```
## Where the binary comes from, and how that is disclosed
The AUR has no formal "unofficial binary" flag. What it has is the naming rule in
[AUR submission guidelines](https://wiki.archlinux.org/title/AUR_submission_guidelines#Rules_of_submission):
> Packages that use **prebuilt** deliverables, when the sources are available,
> must use the `-bin` suffix.
That rule is about the deliverable being prebuilt — it says nothing about who
built it, so `-bin` is the correct and sufficient signal here even though the
binary is ours rather than upstream's. The two rules that could bite do not:
JASP is not in any official repository, and the AUR repo carries only `PKGBUILD`
and `.SRCINFO`, never the makepkg tarball or a filelist.
Disclosure is therefore by convention rather than by mechanism, in the two places
a user actually looks:
- the **comment header** at the top of the `PKGBUILD` (the same header the
guidelines require for maintainer info) states that upstream ships no Linux
binary and links to the build pipeline that produced this one;
- the **`pkgdesc`** ends in `(prebuilt binary)`.
Anyone who wants to verify can read `build/build-jasp.sh` in this repo and
reproduce the tarball.