Skip to content

Make the image OS explicit and pin the version used in the base layer#198

Merged
IsaacG merged 2 commits into
mainfrom
pin
May 12, 2026
Merged

Make the image OS explicit and pin the version used in the base layer#198
IsaacG merged 2 commits into
mainfrom
pin

Conversation

@IsaacG

@IsaacG IsaacG commented May 8, 2026

Copy link
Copy Markdown
Member

No description provided.

@IsaacG IsaacG requested a review from a team as a code owner May 8, 2026 07:21
@IsaacG IsaacG added the x:module/test-runner Work on Test Runners label May 8, 2026
Exercism's policy is to prefer pinned versions.
Using the same hash across all runners allows us to store and reuse the same image, rather than needing to store a per-runner base image. This helps cut down on storage costs.
@senekor

senekor commented May 9, 2026

Copy link
Copy Markdown
Contributor

These base images are only used in the build stages. Pinning them wouldn't have any effect on final image, right? I'd just be a little more tedious to update them.

@IsaacG

IsaacG commented May 9, 2026

Copy link
Copy Markdown
Member Author

These base images are only used in the build stages. Pinning them wouldn't have any effect on final image, right? I'd just be a little more tedious to update them.

Yes and no. Using the same image for the build and run gives consistency in the Dockerfile, in the libraries used at various stages, and means the build process only needs to fetch one base image.

I'm sorta expecting a mass hash update via sed to bump all versions 1-4 times a year, in which case having the hash once or twice in the file wouldn't matter.

@senekor

senekor commented May 9, 2026

Copy link
Copy Markdown
Contributor

Using the same image for the build and run gives consistency in the Dockerfile

This isn't doing that though. The final image uses a pinned version of rust:nightly. The other build stages are using a stable Rust toolchain. So there's no efficiency gain here.

consistency [...] in the libraries used

The test-runner is statically linked. The local registry doesn't produce any executables that end up in the final image, just archives of the available libraries in source form. There won't be any problem with library mismatch with this setup.

means the build process only needs to fetch one base image

I don't think so? There is no guarantee the pinned version of rust:nightly uses the same debian base as the pinned version in the other build stages.

I guess we could get that "consistency improvment" by also using the nightly toolchain in the build stages. But the cost of downloading two versions of debain-slim during the build process is paid by Microsoft, not Exercism, so I don't see the point of making our dockerfile more complicated for a couple (likely cached) MB of download time in GitHub actions.

I'm sorta expecting a mass hash update via sed to bump all versions 1-4 times a year, in which case having the hash once or twice in the file wouldn't matter.

We'll need to update the hash of the nightly toolchain independently, if we want to update the Rust version available to users. This can happen up to every six weeks. If we switch to the nightly toolchain for every build stage, would the bulk update process be able to handle that as well? Or does it only work with a selection of known base images?

@IsaacG

IsaacG commented May 10, 2026

Copy link
Copy Markdown
Member Author

The other build stages are using a stable Rust toolchain. So there's no efficiency gain here.

You mean the rust:1.95.0 image? That tag was updated in the last 24 hours. It's "stable" but different builds will end up with a different image.

If we switch to the nightly toolchain for every build stage, would the bulk update process be able to handle that as well?

A bulk update is only needed to update pinned versions used across repos. It can be used to update the hash in a single repo. A shell script running a sed command could also be used to update multiple hashes in one repo.

@senekor

senekor commented May 10, 2026

Copy link
Copy Markdown
Contributor

The other build stages are using a stable Rust toolchain. So there's no efficiency gain here.

You mean the rust:1.95.0 image? That tag was updated in the last 24 hours. It's "stable" but different builds will end up with a different image.

No, I mean the stable Rust toolchain. A new version is released every six weeks:
https://doc.rust-lang.org/book/appendix-07-nightly-rust.html

@IsaacG

IsaacG commented May 10, 2026

Copy link
Copy Markdown
Member Author

Let's back up here. TL;DR there's no value in using common images here unless it's the final layer? I see the Dockerfile says it has shared layers between the test runner and analyzer. Does that actually share anything if they both start with FROM rust:1.95.0 which may be a different hash across builds?

@senekor

senekor commented May 10, 2026

Copy link
Copy Markdown
Contributor

there's no value in using common images here unless it's the final layer?

I think so, yes.

I see the Dockerfile says it has shared layers between the test runner and analyzer. Does that actually share anything if they both start with FROM rust:1.95.0 which may be a different hash across builds?

The FROM rust:1.95.0 AS build-local-registry stage only produces the local cargo registry, which is copied to the final image. The base image of that stage doesn't factor into the final image, so it doesn't matter if the test-runner and analyzer happen to use different base image hashes.

@IsaacG

IsaacG commented May 12, 2026

Copy link
Copy Markdown
Member Author

The FROM rust:1.95.0 AS build-local-registry stage only produces the local cargo registry, which is copied to the final image. The base image of that stage doesn't factor into the final image, so it doesn't matter if the test-runner and analyzer happen to use different base image hashes.

Does that mean that big wall of text about sharing common steps between the two repos incorrect? Since each build is isolated, there's no value in ensuring the Dockerfiles in the two repos are the same.

@senekor

senekor commented May 12, 2026

Copy link
Copy Markdown
Contributor

The FROM rust:1.95.0 AS build-local-registry stage only produces the local cargo registry, which is copied to the final image. The base image of that stage doesn't factor into the final image, so it doesn't matter if the test-runner and analyzer happen to use different base image hashes.

Does that mean that big wall of text about sharing common steps between the two repos incorrect? Since each build is isolated, there's no value in ensuring the Dockerfiles in the two repos are the same.

No. The local registry produced by these steps should be the same between the test-runner and analyzer. Otherwise that layer is duplicated (+40MB) and we run the risk of inconsistent output between the test-runner and analyzer due to a version mismatch.

Tmat being said, the precise details of some steps in the build-local-registry stage could be slightly different, and it wouldn't matter. For example, if one image used Rust 1.85 as the base image and the other 1.86, that likely wouldn't matter and the resulting local registry would be identical. Still, making sure the steps are the same is the easiest way to ensure the result is consistent.

@kotp What is the value of this PR in your opinion?

@senekor senekor 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.

Ah, I see now it's only the OS version, without the hash pinned. That's fine by me. The PR title might be updated to reflect that before squashing.

@IsaacG

IsaacG commented May 12, 2026

Copy link
Copy Markdown
Member Author

No. The local registry produced by these steps should be the same between the test-runner and analyzer. Otherwise that layer is duplicated (+40MB) and we run the risk of inconsistent output between the test-runner and analyzer due to a version mismatch.

The local registry would only be the same if they both get the same SHA when they pull rust:1.95.0-trixie. Given that the tag is frequently updated to a new SHA (the last update being 3 days ago), you'd only produce the same image if you ensure that the rust-test-runner and rust-analyzer images are built at roughly the same time (ie both builds would need to happen while rust:1.95.0-trixie is pointing to the same SHA). This can be done if you ensure any build in one repo is "synced" with a build in the other repo while the SHA is the same.

Are you rebuilding the image on both repos every time there is a PR in either repo? Or am I misunderstanding something?

@IsaacG IsaacG changed the title Make the image OS explicit and pin the version used Make the image OS explicit and pin the version used in the base layer May 12, 2026
@senekor

senekor commented May 12, 2026

Copy link
Copy Markdown
Contributor

The local registry would only be the same if they both get the same SHA when they pull rust:1.95.0-trixie.

This is not true. Take these two Dockerfiles for example:

FROM rust:1.94 as build
RUN echo whatever > /foo.txt

FROM debian as final
COPY --from=build /foo.txt /foo.txt
FROM rust:1.95 as build
RUN echo whatever > /foo.txt

FROM debian as final
COPY --from=build /foo.txt /foo.txt

They simulate the structure we're talking about. They are identical, except for the base image of the build stage (rust:1.94 and rust:1.95). If you build these two images, you will see that they produce an identical output with a matching hash. That's because the hash of a COPY step is only determined by the files being copied over, not by which specific layers and steps were used to produce those files.

A more realistic but slower way to confirm this is to take the normal test-runner Dockerfile, change the base image tag, rebuild it, then check the hashes of the layers. The step copying the local registry has the same hash, independent of the base image used.

@IsaacG

IsaacG commented May 12, 2026

Copy link
Copy Markdown
Member Author

That explains a whole lot! When I think about it, that all makes sense. I assumed the hash represented the history/steps and not the content. But now I understand much more. Thank you for taking the time and patience to help me understand what's going on here!

@IsaacG

IsaacG commented May 12, 2026

Copy link
Copy Markdown
Member Author

Related: exercism/rust-representer#98

@IsaacG IsaacG merged commit 2c5ed61 into main May 12, 2026
1 check passed
@IsaacG IsaacG deleted the pin branch May 12, 2026 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

x:module/test-runner Work on Test Runners

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants