feat: support image.docker.platform#1856
Conversation
Adds support for `image.docker.platform` per GitLab CI spec (introduced in GitLab 18.0). The platform string is forwarded to both: - `docker pull --platform <platform>` to fetch the requested manifest - `docker run --platform <platform>` so the container runs against that platform even if a multi-arch image was already cached locally Mirrors the existing `image.docker.user` plumbing. Refs https://docs.gitlab.com/ci/yaml/#imagedockerplatform — supersedes the abandoned attempt in firecow#1595 (closed for missing tests + style). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/job.ts">
<violation number="1" location="src/job.ts:686">
P2: Platform-specific pulls can be skipped by the platform-agnostic inspect cache check.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Cubic flagged on PR firecow#1856 that the `image inspect` cache-check inside pullImage is platform-agnostic — a different-arch variant of the same image name will satisfy it, so the requested platform silently diverges from what's actually on disk. Fix: short-circuit the cache check when `imagePlatform` is set and always invoke the explicit pull (which then carries `--platform` to docker). Pulls are idempotent: if the requested variant is already local, `docker pull` returns "Image is up to date" quickly. Tightens the integration test by dropping `pullPolicy: "always"` — the new short-circuit is now the path under test. Identified by cubic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Isn't docker run on a ARM64 machine using ARM64 docker images on its own or what is it that I'm not getting here? |
Indeed, docker run will use the host's architecture, unless otherwise requested: However, if you're interested in a specific arch, you need to request it explicitly: For my cross-compiling case, let's take the following example (my production runners are mainly x86_64 - but we also have some arm64s - but the local env is Mac - aarch64). The recipe looks like this: Now, if for some reason, the wrong arch runner would pick it up (of course, we do have the "tags", selecting the arch, so in production this should not theoretically happen), you would end-up with a job "named" aarch64 but actually being x86_64 (it actually happened at some point, due to some tag confusion - and it's quite hard to figure out, as you can imaging). This way (with the Similarly, debugging with Not sure if the above answers your question - I'll be more than happy to provide more clarifications if needed. |
|
Yeah im running into this also. Some of the images i need to run against don't have builds for all architectures so i want to force amd64 and let the virtualization handle things. |
Summary
Adds support for
image.docker.platformper the GitLab CI YAML reference (introduced in GitLab 18.0). The platform string is forwarded to:docker pull --platform <platform>— fetch the requested manifestdocker run --platform <platform>— guard against a multi-arch image already cached locally with the wrong defaultThe existing
pulledlog line is suffixed with(<platform>)when set, e.g.pulled alpine (linux/amd64) in 2.3 s. This gives the user feedback about what was actually pulled, and gives the integration test a concrete assertion surface.Relationship to #1595
This PR supersedes the abandoned attempt in #1595 (closed Feb 17 2026 with "Feel free to reopen, once eslint has been fixed and tests have been added"). Same goal, but:
import { X }style violations flagged inline by @bbrala on the original PR are fixed; all imports useimport {X}(no inner spaces).image-platformfixture + integration test intests/test-cases/image/.A few small design tweaks vs #1595:
pulling(new) andpulled(existing). I just suffix(<platform>)onto the existingpulledline. Avoids changing test snapshots for every other test that pulls an image.imagePlatformin the run-cmd path instead of callingthis.imagePlatform(expanded)twice.pullImagesignature change is additive (imagePlatform: string | null = null); the helper-image and service-pull callsites don't need touching.Implementation
src/data-expander.tsimageComplexpropagatesdocker.platformalongsidedocker.user. Preserves the spirit of #1138 (don't emit null fields) — verified via--previewthatimage-userandimage-platformfixtures both render without spurious null keys.src/job.tsimagePlatform()private method (mirrorsimageUser()); threaded throughpullImageto add--platformto the pull spawn; same--platformappended to thedocker runcmd;pulledlog suffix.tests/test-cases/image/.gitlab-ci.ymlimage-platformjob usinglinux/amd64(matches CI runner arch).tests/test-cases/image/integration.test.tspullPolicy: "always"to force a fresh pull, asserts thepulledlog line includes(linux/amd64), assertsuname -moutputsx86_64.Real-world validation
Beyond the integration test, validated against a private project that uses
image.docker.platform: linux/${PROCESSOR}together withparallel.matrix: PROCESSOR: [x86_64, aarch64]and abefore_scriptthat asserts[ "$PROCESSOR" = "$(uname -m)" ]:gitlab-ci-local4.71.0 +[x86_64]permutation → ❌Mismatch between host processor (aarch64) and target processor (x86_64)(thedocker.platformfield is silently dropped during YAML expansion, so docker pulls the host-arch manifest).[x86_64]→ ✅ PASS, full library compile under qemu-x86_64 emulation on an Apple-Silicon host (39 s).[aarch64]→ ✅ PASS, native (3.6 s).The 10× wall-time difference is itself proof that x86_64 is genuinely running under emulation, not on the aarch64 host.
Out of scope (follow-up if requested)
services[].docker.platformexists in the same GitLab 18.0 keyword family. Adding it cleanly would also requireservicesComplexto start propagatingdocker.*(it currently strips it entirely — there's noservices[].docker.usereither). Sibling PR.References
image.docker.user)image:docker:platform,services:docker:platformimage.docker.user: Implement image.docker.user instruction #1130Test plan
bun run typecheckbun run lintbunx vitest run tests/test-cases/image/(10/10 passed)Acknowledgment
This PR was implemented with assistance from Claude (Anthropic) as a pair-programming tool. The implementation was audited against the GitLab CI YAML spec and existing patterns in this codebase (notably
image.docker.userfrom #1130, the null-emission fix in #1138, and the eight import-style review comments on #1595). All code and tests were reviewed by me before pushing; CI on the internal fork PR confirmed the full suite passes. Co-authorship is preserved in the commit trailer.Summary by cubic
Adds support for
image.docker.platform(GitLab 18.0) and ensures the requested architecture is always used by forwarding the platform to Docker and reflecting it in logs. Also forces a pull when a platform is set to avoid cache mismatches.New Features
image.docker.platformtodocker pullanddocker runvia--platform <value>.pulledlog line, e.g.pulled alpine (linux/amd64) in 2.3 s.docker.platformonly when set during YAML expansion.image-platform) verifies the log output anduname -monlinux/amd64withoutpullPolicy: "always".Bug Fixes
image inspectcache check and always runsdocker pullto prevent wrong-arch variants from being used.Written for commit 5fc70f4. Summary will update on new commits.