Skip to content

Upgrade ARG default values used in Docker FROM instructions - #1213

Draft
timtebeek wants to merge 4 commits into
mainfrom
tim/kolkata-v4
Draft

Upgrade ARG default values used in Docker FROM instructions#1213
timtebeek wants to merge 4 commits into
mainfrom
tim/kolkata-v4

Conversation

@timtebeek

@timtebeek timtebeek commented Aug 20, 2026

Copy link
Copy Markdown
Member

What's changed

  • Do not rewrite Docker FROM images built from variables #1212 made UpgradeDockerImageVersion skip any FROM whose image name or tag contains a $, since the value can not be determined statically. That is the right call for a bare ARG IMAGE_TAG, but a great many Dockerfiles pin the Java version in a global ARG with a default value:
ARG java_version=17
FROM eclipse-temurin:${java_version}-jre

Here the value is statically known, and the version can be bumped by rewriting the ARG default rather than the FROM.

The visitor now collects the literal defaults of the global ARGs (those before the first FROM, which are the only ones a FROM can reference) and resolves variable image references against them. When the resolved reference is an upgradable Java image, it writes the upgrade back to whichever of the two carries the value:

Before After
ARG java_version=17
FROM eclipse-temurin:${java_version}
ARG java_version=25
FROM eclipse-temurin:${java_version}
ARG JAVA_VERSION=11
FROM eclipse-temurin:${JAVA_VERSION}-jre
ARG JAVA_VERSION=25
FROM eclipse-temurin:${JAVA_VERSION}-jre
ARG IMAGE_TAG=11-jre-alpine
FROM eclipse-temurin:${IMAGE_TAG}
ARG IMAGE_TAG=25-jre-alpine
FROM eclipse-temurin:${IMAGE_TAG}
ARG BASE_IMAGE=openjdk:11-jre
FROM ${BASE_IMAGE}
ARG BASE_IMAGE=eclipse-temurin:25-jre
FROM ${BASE_IMAGE}
ARG BASE_IMAGE=openjdk
FROM ${BASE_IMAGE}:11-jre
ARG BASE_IMAGE=eclipse-temurin
FROM ${BASE_IMAGE}:25-jre

Note the last two: an openjdk base has no tag beyond 17, so the deprecated image name has to move to eclipse-temurin along with the tag, whether that name sits in the FROM or in the ARG.

Everything that can not be resolved is still left untouched: an ARG without a default, an ARG whose default is itself built from another variable, a variable that only contributes part of the image name (FROM ${REGISTRY}/eclipse-temurin:11-jre), a variable that does not supply the leading version (FROM eclipse-temurin:11${SUFFIX}), and any ARG declared after the first FROM. ARGs that are not used in a FROM at all are never rewritten. As before, a digest pin is dropped when the tag is upgraded.

The fully literal FROM path is unchanged.

Tests

Added parameterized cases for each row of the table above, plus multi-stage ARG reuse, digest pin removal through an ARG, and a set of negative cases covering the unresolvable and unrelated shapes. The existing tests are unchanged and still pass.

Follow up to #1212, which left any `FROM` built from a variable untouched.
When the variable is a global `ARG` with a literal default, that default can
be upgraded instead, so `ARG java_version=17` used as
`FROM eclipse-temurin:${java_version}` becomes `ARG java_version=25`.
Three follow ups from review:

- A `FROM` whose image we do not upgrade now vetoes the arguments feeding it,
  so `ARG VERSION=11` used by both `eclipse-temurin:${VERSION}` and
  `node:${VERSION}` is left alone rather than turning the latter into `node:25`.
- Drop the digest pin when an argument holding a whole `name:tag` reference is
  upgraded, as the stale digest would keep resolving to the old image.
- Upgrade quoted default values, keeping their quotes. The parser hands an
  `ARG` value to us as a single literal with the quotes still in its text and
  no quote style, so `ARG JAVA_VERSION="11"` never matched a version before.
Rewriting a `FROM` as it was visited, and only withholding the matching `ARG`
bump after the traversal, left half applied edits behind: a dropped digest pin
or a rename to `eclipse-temurin` next to an argument still holding the old
version.

The whole file is now planned up front, and replayed until the set of withheld
arguments stops growing, as withholding one argument can rule out the images
that depend on it. Only the surviving plan is applied. Every give up path now
withholds the arguments that `FROM` reads.
@timtebeek
timtebeek marked this pull request as draft August 20, 2026 15:20
Comment on lines +326 to +328
private static class QuotedText {
String quote;
String text;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not yet happy with this handling, as I believe it's inconsistent with other usages. Investigating upstream first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

1 participant