Implemented in commit 0e44b95 on master.h2. What was done LibreOffice is no longer installed via apt-get install libreoffice (which tied its version to whatever the base image's Debian release happens to ship). The image now installs a controlled version — the one XWiki officially supports and tests against, i.e. the LTS ("Previous Release") — downloaded from the Document Foundation and verified via sha256, for both amd64 and arm64 (the official image is multi-arch).
The pinned version (currently 25.8.7) plus one sha256 per architecture live in build.gradle and are injected into every XWiki cycle. XWiki supports a single LibreOffice version regardless of the XWiki version, so it is shared, not keyed per cycle.
template/Dockerfile installs LibreOffice's native library dependencies via apt, then downloads the architecture-matching .deb tarball (selected through Docker's TARGETARCH), verifies its sha256, installs it with dpkg, and symlinks /opt/libreoffice — one of the paths JODConverter probes automatically, so no XWiki configuration change was needed.
A new ./gradlew updateLibreOffice task resolves the current LTS version the same way XWiki does and rewrites the pinned values in build.gradle. It is meant to be run during the release (to be added to the "Update Docker" step of the release plan), followed by ./gradlew to regenerate the images.
Verified by building the arm64 image locally: LibreOffice 25.8.7 installs and soffice --version runs correctly on arm64.
Why the 2022 blocker is gone
This issue stalled in 2022 because the base image is multi-arch and the Document Foundation only shipped x86_64 debs at the time. That is no longer true: the Document Foundation now publishes aarch64 debs alongside x86_64, with a parallel naming scheme, for both the current and the LTS branches. So a controlled, architecture-correct install is now possible for both architectures the official image targets.
Architecture options considered
The core question was where to resolve the LTS version, given XWiki expresses it dynamically as libreoffice.version=lts (resolved by LibreOfficeResolver in xwiki-platform-test-docker, which scrapes the Document Foundation stable index for the latest patch of the previous branch).
Option 1 — Resolve lts at docker build time (scrape inside the Dockerfile). Rejected. The official images are built reproducibly from a pinned commit; scraping at build time makes the same Dockerfile produce different LibreOffice versions over time, and it breaks sha256 pinning (you cannot checksum a moving target).
Option 2 — Resolve lts dynamically at ./gradlew generation time (bake the resolved version + sha into the generated Dockerfiles on every run). Rejected. The generated files would stay pinned (so the docker build itself remains reproducible), but generation would stop being a pure function of the committed repo: a new LTS patch upstream would then produce a spurious diff on any unrelated push, reddening the gradlew-check on master for a change that never touched LibreOffice — and checking out an old commit would no longer reproduce that commit's LibreOffice version.
Option 3 — A dedicated updateLibreOffice Gradle task (chosen). It resolves the LTS and fetches both sha256 on demand, then rewrites the pinned values in build.gradle. Generation stays hermetic and reproducible, sha256 pinning is preserved, and LibreOffice bumps become explicit, reviewable, testable changes triggered during the release. This is the same "discover upstream, commit a pinned value" model already used for the XWiki WAR and the JDBC drivers.
(A scheduled job or a Renovate custom manager could later automate the discovery without giving up hermetic generation, if we ever want that.)
This message was sent by Atlassian Jira (v9.3.0#930000-sha1:287aeb6)
If image attachments aren't displayed, see this article.