Skip to content

Pin server images by digest and cache CI builds (Phase 0) - #340

Open
sibson wants to merge 2 commits into
mainfrom
claude/phase0-pin-server-images
Open

Pin server images by digest and cache CI builds (Phase 0)#340
sibson wants to merge 2 commits into
mainfrom
claude/phase0-pin-server-images

Conversation

@sibson

@sibson sibson commented Aug 13, 2026

Copy link
Copy Markdown
Owner

What changed

This is PR 2 of the Phase 0 stack. It was branched from claude/server-testing-plan-vl69ll (PR 1, #339); by the time this PR was opened, #339 had already merged to main and GitHub deleted that branch, so this targets main directly — the diff is identical to what it would have been against the old base (git diff 34d61f3 68820ed — PR 1's branch tip vs. main post-merge — is empty).

Covers the plan's "one source of version truth" and "stop paying the image build tax" follow-ups from docs/server-compatibility-plan.md:

  1. Pin the Tier 1 base image by digest. tests/servers/Dockerfile's FROM debian:bookworm-slim now pins @sha256:abd67ffcfa541b485a3dff59865ab629aa048a6c613e639d36e7456b0b229241, resolved live against Docker Hub's registry API (not invented — see verification below). It's the only FROM in tests/servers/**; the two TigerVNC services and x11vnc all build from this one pinned base via multi-stage targets. Digest pinning matters for reproducible gold files: the plan's "Tier 1 gold files are deterministic" acceptance criterion depends on the fleet actually being byte-identical across runs, which a floating bookworm-slim tag doesn't guarantee (Debian point releases move it).

  2. tests/servers/versions.md — the single source-of-version-truth table the plan calls for: base image digest, the distro packages each image installs (tigervnc-standalone-server/tigervnc-tools/x11vnc/xvfb/etc.), LIBVNCSERVER_VERSION from libvncserver.mk, the UltraVNC Chocolatey package, and the Tier 2 windows-latest/macos-latest runner images. Each row says exactly where the pin lives and how to bump it, plus a documented recipe for resolving a fresh digest without a local Docker daemon (registry HTTP API v2 + a Docker Hub anonymous token) — useful in sandboxes where docker manifest inspect isn't available.

  3. Cache the servers job's image build. GitHub-hosted runners start every job with an empty Docker layer cache, so up -d --build --wait rebuilt from scratch every run (~35s of the ~70s total, per the Tier 1 spike notes). Landed the smallest change I could verify end-to-end: docker/setup-buildx-action + docker/bake-action (which reads build targets straight out of docker-compose.yml, so there's still exactly one image definition) with cache-from=type=gha / cache-to=type=gha,mode=max, load: true, followed by a build-less docker compose up -d --wait. I validated the bake target parsing and the --set cache-option syntax locally against a real (temporarily-started) Docker daemon (docker buildx bake -f tests/servers/docker-compose.yml --set '*.cache-from=type=gha' --set '*.cache-to=type=gha,mode=max' --print) and confirmed it produces the expected three-target bake plan with cache options applied to all of them — I did not have GitHub's Actions cache backend available locally to prove an actual cache hit, but the mechanics (compose→bake target resolution, cache flag plumbing) are confirmed correct.

  4. docs/server-compatibility-plan.md — added a factual note under Tier 1 follow-ups recording that digest pinning and build caching are done, and pointing at tests/servers/versions.md. No restructuring.

Verification

  • flake8 --count --statistics vncdotool tests — clean (0).
  • make test (python -m unittest discover tests/unit) — 67 tests pass in a clean venv built from requirements-dev.txt. (The sandbox's system Python had a stale cryptography/rust-bindings panic unrelated to this change; a fresh venv confirms it's an environment artifact, not a regression.)
  • YAML validity of .github/workflows/*.yml confirmed via yaml.safe_load.
  • Digest resolution was done for real, not fabricated: queried Docker Hub's registry v2 API directly (auth.docker.io token + registry-1.docker.io manifest HEAD) and got back docker-content-digest: sha256:abd67ffcfa541b485a3dff59865ab629aa048a6c613e639d36e7456b0b229241 for library/debian:bookworm-slim.
  • Docker build attempted for real: got a Docker daemon running in-sandbox and ran docker compose -f tests/servers/docker-compose.yml build. The pinned digest resolved correctly and FROM succeeded (buildx confirmed the manifest and began pulling layers); the build then failed pulling an image blob from production.cloudfront.docker.com, which this sandbox's egress policy doesn't allow (only the registry API host is reachable, not the CDN that serves blob bytes). That's a sandbox network-policy limitation, not a problem with the digest or the Dockerfile — reported here rather than worked around, per the task instructions. A normal CI runner or a developer machine with ordinary Docker Hub access will pull the pinned digest fine.

Scope note

Per instructions, tests/functional/** is untouched — that's the scenarios-framework PR later in this stack.


Generated by Claude Code

Pins tests/servers/Dockerfile's debian:bookworm-slim base to a resolved
sha256 digest so Tier 1 gold files stay reproducible, adds
tests/servers/versions.md as the single table of every version pin
(base image, distro server packages, LIBVNCSERVER_VERSION, UltraVNC's
Chocolatey package, Tier 2 runner images), and switches the `servers`
CI job to build via docker/setup-buildx-action + docker/bake-action
with the GitHub Actions cache backend ahead of a build-less
`docker compose up --wait`, so an unchanged Dockerfile hits cache
instead of rebuilding on every run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VfqHjnFvPM6g1RspPk4uBo
…ibility-plan.md

The full explanation already lives in the plan doc; the inline comment
only needs the pointer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants