Serve from /srv/tarballz on CT148 via a 'location /tarballz/' alias, NOT from under /var/www/asger: the asger-web deploy swaps that whole directory atomically, so tarballs kept inside it would be deleted on the next site deploy. CT148's rootfs was 3G with 2.7G free - one tarball would have filled it - so it is grown to 15G; speedy's thin pool has room even though vgs reports the VG nearly full. Publishing uploads to a .part name and renames on arrival so a download can never see a truncated file, verifies sha256 on the host, then fetches the public URL and checks it against the sha256 baked into the PKGBUILD. Both checks run before the AUR push, so the package cannot go out pointing at a missing file. Old tarballs are pruned to the newest three. GH_TOKEN is kept but demoted to an optional GITHUB_PAT for R module downloads; it no longer has anything to do with publishing.
176 lines
8.7 KiB
Markdown
176 lines
8.7 KiB
Markdown
# 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-tarball.sh` | Uploads the tarball to `asger.weirsoe.dk`, verifies the checksum on arrival and prunes old releases. |
|
||
| `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
|
||
▼
|
||
upload to asger.weirsoe.dk ──► verify public URL
|
||
│ ──► 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 |
|
||
| --- | --- | --- |
|
||
| `AUR_MAINTAINER` | `Asger Geel Weirsoe <asger at weircon dot dk>` | Written into the PKGBUILD header. The guidelines suggest obfuscating the address against spam harvesting. |
|
||
| `TARBALL_HOST` | `192.168.10.148` | CT148, where tarballs are uploaded |
|
||
| `TARBALL_PATH` | `/srv/tarballz` | Directory on that host |
|
||
| `TARBALL_BASE_URL` | `https://asger.weirsoe.dk/tarballz` | Public URL the PKGBUILD downloads from |
|
||
|
||
**Secrets** (Settings → Actions → Secrets):
|
||
|
||
| Name | Meaning |
|
||
| --- | --- |
|
||
| `GH_TOKEN` | *Optional.* A GitHub PAT, passed to the build as `GITHUB_PAT`. Not used for publishing — several JASP R modules install from GitHub and anonymous API rate limits can fail the build. |
|
||
| `TARBALL_SSH_KEY` | Private key authorised on CT148 for uploading tarballs |
|
||
| `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.) |
|
||
|
||
### Where the tarballs live
|
||
|
||
Tarballs are served from `https://asger.weirsoe.dk/tarballz/`, backed by
|
||
`/srv/tarballz` on CT148.
|
||
|
||
They are **not** stored under `/var/www/asger`. The `asger-web` deploy replaces
|
||
that entire directory atomically — it unpacks into `.new`, swaps it in and
|
||
deletes the old one — so anything kept inside it would vanish on the next site
|
||
deploy. nginx exposes `/srv/tarballz` through a `location /tarballz/` alias
|
||
instead, with `gzip off` so makepkg gets a plain byte stream.
|
||
|
||
Note that `asger.weirsoe.dk` 301-redirects to the IDN form
|
||
`asger.xn--weirse-fya.dk`. makepkg follows redirects, so the readable hostname is
|
||
used in the PKGBUILD, but that redirect is load-bearing — if it is ever removed,
|
||
the `source=` URL must be repointed.
|
||
|
||
The publish step verifies the uploaded copy's sha256 on the host, and a separate
|
||
step then fetches the *public* URL and checks it against the sha256 baked into
|
||
the PKGBUILD. Both run before the AUR push, so the package can never be published
|
||
pointing at a missing or truncated file.
|
||
|
||
## 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.
|