Files
james.maesandClaude Sonnet 5.5 e19feb01a8
maven / build (push) Successful in 6m2s
build / maven (push) Successful in 6m2s
build(renderer): build the engine and service images from a released version
Both Dockerfiles copied 0.1.0-SNAPSHOT jars that the reactor no longer produces
(version is ${revision}), so neither built a released version. They now resolve the
released qbit-document-renderer and qqq-remote-renderer-transport (and their runtime
libraries) at an exact, required RENDERER_VERSION / TRANSPORT_VERSION from the
QRun-IO Gitea Maven registry and Maven Central, refusing SNAPSHOT and local
(*forge*) versions. The reviewed hardening is unchanged: digest-pinned
BUILD_IMAGE/JAVA_IMAGE, non-root 65532, read-only-root friendly, Zint built from
vendor/. The reactor build context is no longer needed.

Adds IMAGE-PUBLISH.md (repeatable build/scan/publish steps and the 1.0.0 pins) and
image-pins.sh (prints the gg-forge-cd classpath and SHA256 pins from a published
image). Images for 1.0.0 were built, smoke-tested over mTLS and published to GHCR
under gg-engineering by hand; no workflow publishes images.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 20:07:14 -06:00

7.6 KiB

Renderer container images: build from a released version and publish

Two images, built in this order. Both are built from released Maven artifacts (QRun-IO Gitea registry https://git.kof22.com/api/packages/QRun-IO/maven and Maven Central); there is no SNAPSHOT, local-cache or reactor input. The version is a required build argument with no default, and the Dockerfiles refuse a SNAPSHOT or forge version.

Image Dockerfile (context) Version argument Contents
ghcr.io/gg-engineering/qqq-document-renderer renderer/Dockerfile (renderer/) RENDERER_VERSION (qbit-document-renderer) engine, its libraries, patched Zint built from renderer/vendor, Temurin runtime
ghcr.io/gg-engineering/qqq-remote-renderer bridges/remote-renderer/Dockerfile (bridges/remote-renderer/) TRANSPORT_VERSION (qqq-remote-renderer-transport), RENDERER_IMAGE the mTLS service, on top of the engine image

The service image is the one Forge deploys. The adapter (qbit-document-generation-remote-renderer) is a client-side library and is not in either image.

Version choice

1.0.0 is the released, non-RC version and the one gg-forge pins for the document-generation family. 1.1.0-RC.1 contains the same renderer/ and bridges/remote-renderer/ sources as 1.0.0 (only POM/CI alignment differs), so it adds nothing for the renderer and is a release candidate. Build the version the host library pins.

Reviewed base images

BUILD_IMAGE and JAVA_IMAGE must be immutable digests. JAVA_IMAGE is the reviewed runtime in DEPLOYMENT.md. BUILD_IMAGE only resolves artifacts; no layer of it reaches the final image.

BUILD_IMAGE=maven@sha256:99e61abcff91a9b1333463bd8451fb18495d6eba9250ac66a338b518f8278320   # maven:3.9-eclipse-temurin-21
JAVA_IMAGE=eclipse-temurin@sha256:db724162e4ff5f71fa0a2815026314d9e5e0e9a139f49be1d4717e6930e93341  # 21.0.13_9-jre-noble (amd64)
VERSION=1.0.0

Build, smoke, push

Disable BuildKit provenance and SBOM attestations so each tag is a single OCI image manifest (the digest the chart pins).

docker build --provenance=false --sbom=false --build-arg BUILD_IMAGE=$BUILD_IMAGE --build-arg JAVA_IMAGE=$JAVA_IMAGE   --build-arg RENDERER_VERSION=$VERSION -t qqq-document-renderer:$VERSION renderer
docker build --provenance=false --sbom=false --build-arg BUILD_IMAGE=$BUILD_IMAGE --build-arg RENDERER_IMAGE=qqq-document-renderer:$VERSION   --build-arg TRANSPORT_VERSION=$VERSION -t qqq-remote-renderer:$VERSION bridges/remote-renderer

# Optional scan of the final image
docker save qqq-remote-renderer:$VERSION -o svc.tar
docker run --rm -v "$PWD/svc.tar:/svc.tar:ro" aquasec/trivy:latest image --input /svc.tar --severity HIGH,CRITICAL --scanners vuln

# Publish (docker must be logged in to ghcr.io with a token that has write:packages)
for n in qqq-document-renderer qqq-remote-renderer; do
  docker tag $n:$VERSION ghcr.io/gg-engineering/$n:$VERSION
  docker push ghcr.io/gg-engineering/$n:$VERSION      # prints the manifest digest
done

New GHCR packages are private. Package access is per package and is managed in the GitHub UI (Package settings, Manage access); the REST API cannot grant it. The credential behind ghcr-pull-secret must be able to read both packages.

Pins for gg-forge-cd

sh renderer/image-pins.sh ghcr.io/gg-engineering/qqq-remote-renderer@sha256:<manifest digest> pulls the published image and prints the echoRenderer.* image, classpath and file-hash pins from the image itself. imageId is docker image inspect --format '{{.Id}}'; with the containerd image store this equals the manifest digest, otherwise it is the config digest. Whatever value is chosen must be the same on Forge and the service.

Values for 1.0.0 as published (the java, zint and patchset hashes match the 2026-11-05 local qualification; the engine JAR and the OpenHTML JAR differ because they are the released artifacts):

echoRenderer:
  image:
    repository: ghcr.io/gg-engineering/qqq-remote-renderer
    digest: sha256:4152a4253e543068bedfd438775ed5327807bdddf679bc5a8e6eab26855e09ea
  imageId: sha256:4152a4253e543068bedfd438775ed5327807bdddf679bc5a8e6eab26855e09ea
  classpath:
    - /renderer/renderer.jar
    - /renderer/lib/commons-logging-1.4.0.jar
    - /renderer/lib/fontbox-3.0.8.jar
    - /renderer/lib/freemarker-2.3.35.jar
    - /renderer/lib/graphics2d-3.0.1.jar
    - /renderer/lib/jackson-annotations-2.22.jar
    - /renderer/lib/jackson-core-2.22.4.jar
    - /renderer/lib/jackson-databind-2.22.4.jar
    - /renderer/lib/jsoup-1.23.2.jar
    - /renderer/lib/okapibarcode-0.5.7.jar
    - /renderer/lib/openhtmltopdf-core-1.1.105.jar
    - /renderer/lib/openhtmltopdf-pdfbox-1.0.0.jar
    - /renderer/lib/pdfbox-3.0.8.jar
    - /renderer/lib/pdfbox-io-3.0.8.jar
    - /renderer/lib/slf4j-api-2.0.17.jar
    - /renderer/lib/slf4j-nop-2.0.17.jar
    - /renderer/lib/xmpbox-3.0.8.jar
  fileHashes:
    /opt/java/openjdk/bin/java: 7afc28801f744050aeba646b20e7314265279f76f9d0273b21a2b66411e1bf6d
    /renderer/renderer.jar: 0061e1bce631e468b0d7d08735bb764ba2b6d85260c79c144beabad7ffbc19db
    /usr/local/bin/zint: ea8dc40c917abd7e8ccc824ef0adafed245a2425abbdfac6a30faeb8e4642233
    /usr/share/doc/qqq-zint/patchset-id.txt: a287946580d5eeb8691ac1d98d6a7f50bf22dcc5dc0ef039d7033e76e15c52d2
    /renderer/lib/commons-logging-1.4.0.jar: d175dbd751dd782a63bde28c7a039520e971f25e84b79c19b8435edc3603e0dc
    /renderer/lib/fontbox-3.0.8.jar: a1915c24e3edbe0ecec93896dfbf6d41427810b663ade97bd4e8bae86ec3fdab
    /renderer/lib/freemarker-2.3.35.jar: 0fac87dddd78f1223139e8ef88e819c7f483c0a3835cdf5982ad5e4576d1d896
    /renderer/lib/graphics2d-3.0.1.jar: 4849aaa6f536d75dea1b835a71086702216787036f42716adfad438c6e7928c0
    /renderer/lib/jackson-annotations-2.22.jar: 21ddb598807d3a51a876704eb979d9296e1c6a6f47ab1826ff88c6d6a127a2d0
    /renderer/lib/jackson-core-2.22.4.jar: 2d30c375cddea9657a1e1ef97547a2694b663b355facb4de53a097782b312d1a
    /renderer/lib/jackson-databind-2.22.4.jar: d4cbf8947087f305869826801913abdc50735a1fab2074a1993e7bc67ca974a3
    /renderer/lib/jsoup-1.23.2.jar: e9d8c856856680427f096d156f02a414289985c930998fcb519ce5be39b42003
    /renderer/lib/okapibarcode-0.5.7.jar: f33b7d17b7040aa1d9c23b1f3bb274e492b786c340b02d9557bcc027ac874355
    /renderer/lib/openhtmltopdf-core-1.1.105.jar: d5951a8407c3be41108698066e6d67cf952e526be39d4e49b0b9fbcee761de7d
    /renderer/lib/openhtmltopdf-pdfbox-1.0.0.jar: ec2865bfa45513e63adaa1a3c6387eb6bbd481e6b23f4d68a33cfedfc753cfe8
    /renderer/lib/pdfbox-3.0.8.jar: 97647cfbde61ebcfc06b4cf8c9b0ffcaaee073396eceb4a7f6836a9b9128903c
    /renderer/lib/pdfbox-io-3.0.8.jar: 36a0e04001010b4c764857817412b96339930b19755e728959805cc0352061b2
    /renderer/lib/slf4j-api-2.0.17.jar: 7b751d952061954d5abfed7181c1f645d336091b679891591d63329c622eb832
    /renderer/lib/slf4j-nop-2.0.17.jar: 3716f83649ec66161a2edefd4f49df34d1dd1c51cdcf941996c6987260f0a829
    /renderer/lib/xmpbox-3.0.8.jar: f044d2e316e57714a0adf272ad8abf4a863b8978fd17dc5b86dcfa8135469b10

Not covered by these pins: identity.*, certificateNamespaces, TLS identities, and chart switches.

What was and was not verified (1.0.0 build)

  • Verified: renderer (174) and transport (19) tests and Checkstyle pass on this source (same sources as the v1.0.0 tag); both images build; the service image runs read-only, non-root, all capabilities dropped, and served CATALOG, PDF (Code128, GS1-128, Zint DotCode) and ZPL renders over a real mutual-TLS handshake with ephemeral certificates.
  • Not verified: no cluster run, no handshake against Forge, no gVisor or hostile-input qualification (the service is TRUSTED_LAB only), no byte-for-byte rebuild of the OpenHTML jar from vendor/.