Skip to content

1.5 Step | InstallWkhtmltopdf

Omisen edited this page Aug 14, 2026 · 5 revisions

Installs the Qt-patched wkhtmltopdf build (0.12.6.1-3) from GitHub releases, verifying the SHA-256 checksum before installing. It lives in src/steps/install_wkhtmltopdf.rs. A port of install_wkhtmltopdf from lib/system.sh, with the addition Bash never had: the integrity check (gap G3).

The project's official build is needed because the distribution's own package (without the Qt patch) produces broken PDFs with Odoo.


Life cycle

Phase Behaviour
snapshot is wkhtmltopdf --version 0.12.6.1? → Preexisting (not ours); otherwise Untracked
run Preexisting → skip. Otherwise: pick the package for the family and codename → download → verify the checksum → install it through the family's manager (which resolves its dependencies), then CreatedByUs. dry_run → log only
undo CreatedByUs only: purge wkhtmltox. Idempotent, best-effort

On Fedora there is no codename: there is an .rpm

Release 0.12.6.1-3 publishes exactly one x86_64 package relevant to the rpm family: wkhtmltox-0.12.6.1-3.fedora37.x86_64.rpm. Every supported Fedora takes that one, with a warning when the release is newer than the mapped ones — acceptable because an .rpm declares its own Requires: an incompatible package is rejected, not half-installed. The TOFU pin applies just as it does to the .deb files: no pin, or a different hash, means no installation.

Codename → package map (deb family)

From the codename in ctx.os_info (populated by the OS checks):

Codename Package suffix
noble / mantic / lunar / jammy jammy
bookworm bookworm
bullseye bullseye
(unknown Ubuntu, focal included) jammy + warning
(unknown Debian, e.g. trixie) bookworm + warning

The fallback follows the OS family, not a single default. Previously every unknown codename fell back to jammy, which is an Ubuntu package — and the case was not as theoretical as it looked, because Debian 13 (trixie) passes the version check (the thresholds are open upwards) and would have taken a .deb built for Ubuntu 22.04, against another distribution's system libraries. A fallback that ignores the relevant dimension is not a prudent fallback: it is the wrong choice dressed up as a default.

It stays a fallback and not a refusal: blocking an installation without evidence is a certain harm to avoid a hypothetical one. The TOFU pin remains fail-closed on the contents regardless.

Release 0.12.6.1-3 publishes amd64 .deb files only for jammy, bullseye and bookworm: a focal_amd64.deb does not exist. focal (Ubuntu 20.04) is refused by the OS checks anyway (Ubuntu ≥ 22.04 is required), so it has no dedicated branch: it falls into the fallback like any unknown codename.


G3 — checksum verification (the point of this page)

Bash downloaded the package and installed it without verifying its integrity: a compromised mirror or a corrupted download would pass unnoticed. Here the sequence is:

download the package into a temporary file
   └─▶ compute the SHA-256
        └─▶ compare it with the expected checksum (table keyed by suffix)
             ├─ matches     → install it through the family's manager, which resolves dependencies
             └─ NO match    → error, NO installation

The temporary file is always cleaned up, on success or on error. It is created with an unpredictable name, fail-closed, keeping its .deb/.rpm extension — the extension is what makes the manager treat the path as a local package rather than a package name, so an unpredictable name that broke the command would not have been a fix (R9).

Rule: verification is never bypassable. If the expected checksum for a suffix is missing, the run refuses to install that package (it does not “install anyway”). That is the safe choice: better a rollback than an unverified binary on a customer machine.

Installing, not just unpacking

The installation goes through the manager's install_local_file (apt-get install -y <file> / dnf install), not through dpkg -i. This is A-RT-1, found on the very first real run: dpkg -i does not resolve dependencies, and the fix-up call that was supposed to repair the situation sat after a ? and was therefore unreachable on the failing path. Letting the manager install the local file resolves the dependencies in one go.

Current state of the table

default_checksums() is populated with the TOFU pins of the three amd64 .deb files of release 0.12.6.1-3 (jammy, bullseye, bookworm) plus the fedora37 .rpm — see Security for the nature and provenance of the pins. The key is the package suffix, not the OS codename: it is map_codename that translates one into the other. If an entry were missing (a future suffix, say) that package would not be installed — fail-closed, covered by tests.

The table is injectable, so the tests demonstrate both branches: valid checksum → installs, wrong checksum → fails without installing.


The testable boundary

  • SystemOps: wkhtmltopdf_version, plus ops.packages() for install_local_file, try_repair and remove.
  • Downloader: a separate boundary for networking, mockable. In tests a MockDownloader writes known bytes into the temporary file, so the SHA-256 is real and comparable with the injected table.

The tests run without network and without root.


Design notes

  • Idempotent: the correct version already present → no action.
  • undo purges wkhtmltox only if we installed it; best-effort.
  • Tests: checksum mismatch → fails without installing (the heart of G3), valid checksum → installs, missing checksum → refuses, Preexisting → total skip, and the codename map (noble→jammy, bookworm→bookworm, unknown→family fallback + warning).

Clone this wiki locally