Skip to content

feat: support image.docker.platform#1856

Open
inistor wants to merge 2 commits into
firecow:masterfrom
inistor:feat/image-docker-platform
Open

feat: support image.docker.platform#1856
inistor wants to merge 2 commits into
firecow:masterfrom
inistor:feat/image-docker-platform

Conversation

@inistor

@inistor inistor commented May 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds support for image.docker.platform per the GitLab CI YAML reference (introduced in GitLab 18.0). The platform string is forwarded to:

  • docker pull --platform <platform> — fetch the requested manifest
  • docker run --platform <platform> — guard against a multi-arch image already cached locally with the wrong default

The existing pulled log 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:

  1. ESLint clean — the eight import { X } style violations flagged inline by @bbrala on the original PR are fixed; all imports use import {X} (no inner spaces).
  2. Tests added — new image-platform fixture + integration test in tests/test-cases/image/.

A few small design tweaks vs #1595:

  • No new "pulling" log line. Support image.docker.platform #1595 emitted both pulling (new) and pulled (existing). I just suffix (<platform>) onto the existing pulled line. Avoids changing test snapshots for every other test that pulls an image.
  • Reuses the local imagePlatform in the run-cmd path instead of calling this.imagePlatform(expanded) twice.
  • pullImage signature change is additive (imagePlatform: string | null = null); the helper-image and service-pull callsites don't need touching.

Implementation

File Change
src/data-expander.ts imageComplex propagates docker.platform alongside docker.user. Preserves the spirit of #1138 (don't emit null fields) — verified via --preview that image-user and image-platform fixtures both render without spurious null keys.
src/job.ts New imagePlatform() private method (mirrors imageUser()); threaded through pullImage to add --platform to the pull spawn; same --platform appended to the docker run cmd; pulled log suffix.
tests/test-cases/image/.gitlab-ci.yml Adds image-platform job using linux/amd64 (matches CI runner arch).
tests/test-cases/image/integration.test.ts New integration test: pullPolicy: "always" to force a fresh pull, asserts the pulled log line includes (linux/amd64), asserts uname -m outputs x86_64.

Real-world validation

Beyond the integration test, validated against a private project that uses image.docker.platform: linux/${PROCESSOR} together with parallel.matrix: PROCESSOR: [x86_64, aarch64] and a before_script that asserts [ "$PROCESSOR" = "$(uname -m)" ]:

  • Released gitlab-ci-local 4.71.0 + [x86_64] permutation → ❌ Mismatch between host processor (aarch64) and target processor (x86_64) (the docker.platform field is silently dropped during YAML expansion, so docker pulls the host-arch manifest).
  • This branch + [x86_64] → ✅ PASS, full library compile under qemu-x86_64 emulation on an Apple-Silicon host (39 s).
  • This branch + [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.platform exists in the same GitLab 18.0 keyword family. Adding it cleanly would also require servicesComplex to start propagating docker.* (it currently strips it entirely — there's no services[].docker.user either). Sibling PR.

References

Test plan

  • bun run typecheck
  • bun run lint
  • bunx vitest run tests/test-cases/image/ (10/10 passed)
  • CI green on internal fork PR before opening this one (inistor#2 — all 11 checks passed including vitest, smoke-test × 4, CodeQL, eslint, typecheck, yamllint, unused-deps)

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.user from #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

    • Forwards image.docker.platform to docker pull and docker run via --platform <value>.
    • Adds the platform to the pulled log line, e.g. pulled alpine (linux/amd64) in 2.3 s.
    • Emits docker.platform only when set during YAML expansion.
    • Integration test (image-platform) verifies the log output and uname -m on linux/amd64 without pullPolicy: "always".
  • Bug Fixes

    • When a platform is set, bypasses the platform-agnostic image inspect cache check and always runs docker pull to prevent wrong-arch variants from being used.

Written for commit 5fc70f4. Summary will update on new commits.

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>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/job.ts
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>
@firecow

firecow commented May 9, 2026

Copy link
Copy Markdown
Owner

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?

@inistor

inistor commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

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:

docker run -it --rm ubuntu uname -a
Linux a03fd021bff7 6.12.76-linuxkit #1 SMP Thu Apr 30 15:11:03 UTC 2026 aarch64 GNU/Linux

However, if you're interested in a specific arch, you need to request it explicitly:

➜  ~ docker run -it --rm --platform linux/aarch64 ubuntu uname -a
Linux 8b22cc799367 6.12.76-linuxkit #1 SMP Thu Apr 30 15:11:03 UTC 2026 aarch64 GNU/Linux
➜  ~ docker run -it --rm --platform linux/amd64 ubuntu uname -a
Linux 74ac3b7bdb48 6.12.76-linuxkit #1 SMP Thu Apr 30 15:11:03 UTC 2026 x86_64 GNU/Linux

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:

.linux-template:
  tags:
    - docker-${PROCESSOR}
  image:
    name: my-builder:latest
    docker:
      platform: linux/${PROCESSOR}
  parallel:
    matrix:
      - PROCESSOR: [x86_64, aarch64]
  before_script:
    # Guard: make sure we're actually running on the requested arch.
    # Without image.docker.platform, both matrix permutations resolve to the
    # host's native arch and one of them trips this assertion.
    - if [ "$PROCESSOR" != "$(uname -m)" ]; then
        echo "Mismatch between host processor ($(uname -m)) and target processor ($PROCESSOR)";
        exit 1;
      fi
build-mylib:
  extends: .linux-template
  script:
    - ./build.sh --arch=$PROCESSOR

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 platform selector), at least, if the runner is incorrectly selected, it would trigger a cross-platform build; much much slower, but at least correct.

Similarly, debugging with gitlab-ci-local (since tag is irrelevant in this case) would by default use aarch64 - despite the fact that the intent is x86_64.

Not sure if the above answers your question - I'll be more than happy to provide more clarifications if needed.

@bbrala

bbrala commented May 13, 2026

Copy link
Copy Markdown
Contributor

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.

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.

3 participants