Repository navigation
A tag ending in a variable is updated and pinned, which breaks the reference #412
Description
Version
Update-time 0.0.44
Summary
An image tag can start with a version and end in a variable, such as java:1.1.0-${VARIANT}. Update-time reads the part before the variable as the whole tag. It then updates that part, appends a digest, and leaves the variable behind the digest. The reference it writes is invalid, so the build that uses it fails.
The README says Update-time leaves such a reference alone. It says Update-time ignores an image whose tag it cannot read, such as a tag through a {{ ... }} template or a ${VAR} variable substitution. That holds for a tag that consists of the variable alone, such as app:${VAR}. It does not hold for a tag that only ends in one.
Steps to reproduce
In a git repository, create these files:
Dockerfile:
FROM python:3.12-$VARIANTdocker-compose.yml:
services:
cache:
image: redis:7.2-${VARIANT}Run update-time.
Expected
Both files stay as they are, as the README promises for a reference through a variable.
Actual
Update-time reports a new version for each reference:
INFO New version available for redis in docker-compose.yml:3: 8.10.1, published: 2026-09-19 06:06
INFO New version available for python in Dockerfile:1: 3.14.7, published: 2026-09-19 17:09It rewrites both files:
FROM python:3.14.7@sha256:be8ccd085666c34273c9dc5607c9842f8b2e3116128aae45148ce164c07ce09d$VARIANT image: redis:8.10.1@sha256:8a1efc5f479551822b47424ccae982026b633f28818eab0387348120a61e10e2${VARIANT}Docker substitutes the variable after the digest, so neither reference is valid.
The variable also hides the variant from the update. Update-time reads 7.2- as the tag 7.2 without a suffix, so it picks the newest tag without a suffix. That tag can name another variant than the one the variable selects. Here it also moved redis from 7.2 to 8.10.1, a new major version.
The same happens in the wild. TheAlgorithms/Java has this line in .devcontainer/Dockerfile:
FROM mcr.microsoft.com/vscode/devcontainers/java:1.1.0-${VARIANT}A copy of that file named Dockerfile became:
FROM mcr.microsoft.com/vscode/devcontainers/java:25@sha256:da82e11bce1d8c01f36e1dc559f1aadaa28fcbd3e1356fd0bd9fa100f5b3d75d${VARIANT}That repository's own file escaped only because Update-time does not scan hidden folders (see HIDDEN_DOCKERFILES.md).
Analysis
OPTIONALLY_TAGGED_IMAGE_REFERENCE in sources/oci.py reads a tag as [\d\w\.\-]*. It then requires the reference to end with the lookahead (?![\w\d\./:@-]). A $ is not in that class, so the reference ends at 1.1.0-. The comment above the pattern says a tag through a variable does not match. It considers app:${VAR} only, where the colon is followed by the $ itself.
YAML_IMAGE_REFERENCE builds on the same pattern. So the defect reaches the Dockerfile updater and the Docker Compose, Helm, CircleCI, and GitLab CI updaters. A probe of the patterns gives:
| Reference | OPTIONALLY_TAGGED_IMAGE_REFERENCE reads the tag |
IMAGE_REFERENCE reads the tag |
|---|---|---|
java:1.1.0-${VARIANT} |
1.1.0- |
1.1.0- |
java:1.1.0-$VARIANT |
1.1.0- |
1.1.0- |
IMAGE_REFERENCE does not end in a lookahead at all. The devcontainer updater is its only user, and there a closing " follows the pattern, so a variable after the tag fails the match. The devcontainer image uses the optional pattern, also followed by a closing ".
Suggested fix
Let the reference end only where a reference can end. Adding $ to the lookahead stops the match at 1.1.0-, so the reference is passed over as a reference through a variable. A closing quote ends each reference in a devcontainer.json, so its patterns can stay as they are.
Two things are worth deciding beside the fix:
- Whether Update-time reports the skipped reference at
DEBUG, as it reports an image the file does not pull. Today Update-time does not log a reference through a variable. - How this relates to A Dockerfile's base image assembled from build arguments #357, which proposes resolving a Dockerfile's
ARGdefaults. Once A Dockerfile's base image assembled from build arguments #357 lands, aFROMlike the one above resolves to a tag Update-time can read. Until then, the fix here keeps such a reference intact.
Documentation
Once the fix lands, the README is right about a tag through a variable. The fix gets a changelog entry.
Activity
Ignore Docker image references with variables
Ignore Docker image references with variables