Skip to content

Docker: one definition of the registry in an image reference - #8599

Merged
timtebeek merged 2 commits into
mainfrom
tim/docker-from-registry-grammar
Aug 21, 2026
Merged

Docker: one definition of the registry in an image reference#8599
timtebeek merged 2 commits into
mainfrom
tim/docker-from-registry-grammar

Conversation

@timtebeek

Copy link
Copy Markdown
Member

Whether the first path component of an image name is a registry cannot be decided from the syntax — mcr.microsoft.com/windows/servercore names a registry, while redhat/ubi9-minimal names an organisation on Docker Hub — so this stays out of the grammar and applies Docker's own rule (a registry only when the component holds a . or a :, is exactly localhost, or carries an uppercase character) once, in a new ImageName value object that decomposes a name into its registry, namespace and repository and gives the familiar and canonical spellings of the same image.

DockerImageReference exposes it through getImage(), getRegistry(), getFamiliarImageName() and getCanonicalImageName(), so a FROM and a COPY --from are served by one implementation; that replaces NormalizeDockerHubImageName's own regular expression and EolImage's own library/ prefix strip, and adds a registry column to the base image data table.

Image name matching now also matches a name that spells the same image differently, so ubuntu finds docker.io/library/ubuntu and the other way round; this is a strict superset of what matched before, and ChangeFrom captures a $N backref from whichever spelling matched.

NormalizeDockerHubImageName also gains two fixes along the way: it now normalizes a bare library/ubuntu, and it keeps an EnvironmentVariable node in the image name rather than flattening it to a literal.

Verified beyond the suite by decomposing every FROM in a 335-Dockerfile docker-library corpus: 350 named references, no round-trip loss, and redhat/ubi9-minimal correctly classified as implicit docker.io rather than a registry.

Whether the first path component of an image name is a registry cannot be
decided from the syntax: `mcr.microsoft.com/windows/servercore` names a
registry, while `redhat/ubi9-minimal` names an organisation on Docker Hub.
Docker resolves this by treating the component as a registry only when it
holds a `.` or a `:`, is exactly `localhost`, or carries an uppercase
character, which a path component may not.

`ImageName` applies that rule once, decomposing a name into its registry,
namespace and repository and giving the two other spellings of the same
image: the familiar one that drops a Docker Hub prefix, and the canonical
one that writes it out. `DockerImageReference` exposes it, so a `FROM` and
a `COPY --from` are served by the same implementation.

That replaces `NormalizeDockerHubImageName`'s own regular expression and
`EolImage`'s own `library/` prefix strip, adds a registry column to the
base image data table, and lets an image name pattern match a name that
spells the same image differently, so `ubuntu` finds
`docker.io/library/ubuntu` and the other way round.
Familiarizing the pattern as well as the name dropped a registry the
pattern wrote, so `docker.io/*`, `library/*` and `docker.io/library/*`
all collapsed to `*` and matched every image on every registry. Only the
canonical spelling, which fills in what a name leaves out rather than
dropping what a pattern states, can be compared. `ChangeFrom` captures
its backrefs from that spelling for the same reason.
@timtebeek
timtebeek marked this pull request as ready for review August 21, 2026 23:09
@timtebeek
timtebeek merged commit e60967d into main Aug 21, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Aug 21, 2026
@timtebeek
timtebeek deleted the tim/docker-from-registry-grammar branch August 21, 2026 23:11
timtebeek added a commit to openrewrite/rewrite-migrate-java that referenced this pull request Aug 22, 2026
…ns (#1213)" (#1215)

This reverts commit d33eca3.

The recipe reached for rewrite-docker API that no released version of rewrite
carries yet. `Docker.Argument.getText()`, `getTextWithVariables()` and
`hasEnvironmentVariables()` arrived in openrewrite/rewrite#8576, and
`org.openrewrite.docker.trait.ImageName` in openrewrite/rewrite#8599, both
landed 2026-08-21, one day after v8.90.3.

The Moderne CLI loads the LST classes in its own classloader, so a recipe runs
against the rewrite-docker the CLI bundles rather than the one this artifact
resolves. CLI 4.6.3 bundles rewrite-docker 8.90.3, where `Docker.Argument`
exposes only `getContents()` and `ImageName` does not exist. `visitFile` reads
a global `ARG` through `getText()` and `visitFrom` opens with
`hasEnvironmentVariables()`, so the first `FROM` of every Dockerfile would
raise `NoSuchMethodError`, surfacing as error markup on every Dockerfile of
every `UpgradeToJava*` run.

Nothing is lost by waiting: v3.42.1 predates this commit, so the breakage has
not shipped. The state restored here is the #1212 fix, which reads an image
reference through `DockerFrom` alone and so links against 8.90.3.

Reapplied in a follow-up PR, to merge once a rewrite release carries #8576,
#8590 and #8599 and the CLI picks it up.
timtebeek added a commit to openrewrite/rewrite-migrate-java that referenced this pull request Aug 22, 2026
… for rewrite release) (#1216)

* Revert "Upgrade `ARG` default values used in Docker `FROM` instructions (#1213)"

This reverts commit d33eca3.

The recipe reached for rewrite-docker API that no released version of rewrite
carries yet. `Docker.Argument.getText()`, `getTextWithVariables()` and
`hasEnvironmentVariables()` arrived in openrewrite/rewrite#8576, and
`org.openrewrite.docker.trait.ImageName` in openrewrite/rewrite#8599, both
landed 2026-08-21, one day after v8.90.3.

The Moderne CLI loads the LST classes in its own classloader, so a recipe runs
against the rewrite-docker the CLI bundles rather than the one this artifact
resolves. CLI 4.6.3 bundles rewrite-docker 8.90.3, where `Docker.Argument`
exposes only `getContents()` and `ImageName` does not exist. `visitFile` reads
a global `ARG` through `getText()` and `visitFrom` opens with
`hasEnvironmentVariables()`, so the first `FROM` of every Dockerfile would
raise `NoSuchMethodError`, surfacing as error markup on every Dockerfile of
every `UpgradeToJava*` run.

Nothing is lost by waiting: v3.42.1 predates this commit, so the breakage has
not shipped. The state restored here is the #1212 fix, which reads an image
reference through `DockerFrom` alone and so links against 8.90.3.

Reapplied in a follow-up PR, to merge once a rewrite release carries #8576,
#8590 and #8599 and the CLI picks it up.

* Upgrade `ARG` default values used in Docker `FROM` instructions

Reapplies #1213, reverted in #1215 because the rewrite-docker API it reads
`ARG` defaults through had not been released yet.

Hold until a rewrite release carries openrewrite/rewrite#8576 (the
`Docker.Argument` accessors), #8590 (the image reference grammar) and #8599
(`ImageName`), and confirm the Moderne CLI bundles that release, as the CLI
loads the LST classes in its own classloader and so decides which
rewrite-docker a recipe actually links against.

* Revert "Merge branch 'main' into tim/docker-arg-defaults-redo"

This reverts commit 901210a, keeping the branch side.

Merging main in pulled #1215 across, and #1215 is the revert of the very
commit this branch exists to reapply. The merge base still carried the `ARG`
work and main had removed it, so the merge resolved to main's removal and
emptied the branch: `git diff main...HEAD` came back with nothing, leaving the
pull request proposing no change at all.

Reverting the merge rather than dropping it also settles the branch. The merge
stays in history, so main's revert counts as already merged here and undone on
purpose. A later `main` merge brings its new commits without resurrecting the
removal, which resetting the branch would leave it open to on the next
`Update branch`.

* Read an argument's text through `ArgumentContents`

openrewrite/rewrite#8608 moves `getText()`, `getTextWithVariables()`,
`getQuoteStyle()` and `hasEnvironmentVariables()` off `Docker.Argument` and
into `org.openrewrite.docker.internal.ArgumentContents`, then drops them from
the LST type.

That matters because of how the Moderne CLI splits one rewrite-docker jar
across two classloaders: `org.openrewrite.docker.tree` resolves to the
rewrite-docker the CLI bundles, while recipes, traits and `internal` load
child-first from the recipe artifact. Reading an argument through the LST type
therefore linked against the CLI's copy, which is where #1215 came from. The
helpers now sit on the recipe's side of that split and read only members that
predate the CLIs in the field, so they travel with this artifact.

`ImageName` already sits on that side, so it needed no change.

Verified against the pull request rather than assumed: rewrite-docker built at
6f5fd253 and published locally, and every one of the fifteen
`org.openrewrite.docker.tree` members this recipe links against confirmed
present in the 8.90.3 jar CLI 4.6.3 bundles, matching on descriptor.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant