Files
Marius b22f58045e fix(manager): Handle variables with default values in registry aliases (#36293)
* fix(manager): handle registryAliases with shell variable defaults

Allows `registryAliases` keys to contain shell-style variables with
empty (or other) defaults, e.g. `${DEPENDENCY_PROXY:-}` or
`${CI_REGISTRY:-}`. The same image string is then resolved both
online (variable expanded) and offline (variable empty).

Changes in `dockerfile/extract.ts` (used by `gitlabci` too):
* Match the alias key as either `${name}/...` (existing) or `${name}...`
  when `name` already ends in a non-identifier boundary character such
  as `}`. Bare-identifier keys like `$CI_REGISTRY` continue to require
  the slash boundary so they no longer overmatch keys that begin with
  the same prefix (e.g. `$CI_REGISTRY` vs `$CI_REGISTRY_IMAGE`).
* Use `ensureTrailingSlash` to handle alias values that already include
  a trailing slash.
* Preserve the original `name` and path structure in `dep.depName`
  instead of substringing only after the alias `value`. This keeps the
  variable in the depName so it survives a `splitImageParts` round-trip
  and the resulting branch name comes out clean.

Adds `sanitizeDepName` regex passes in
`lib/workers/repository/updates/flatten.ts` so any leftover
`${VAR}`/`${VAR:-...}` placeholder is stripped from the depName before
it becomes a branch name (otherwise we'd get
`renovate/${dependency_proxy}python-3.x` instead of
`renovate/python-3.x`).

Tests cover:
* `${VAR:-}/image` (with slash) and `${VAR:-}image` (no slash, requires
  the boundary form).
* Overlapping prefixes: `$CI_REGISTRY` vs `$CI_REGISTRY_IMAGE`.
* Registry alias with port (`localhost:5000/repo`).
* `sanitizeDepName` strips `${VAR}` / `${VAR:-}` / bare `$VAR`.

Reproducer: https://github.com/Wayneoween/renovate-36293-repro

* revert(manager/gitlabci): keep regex group name as 'prefix'

Reverting the unrelated `prefix` -> `rawPrefix` rename in `depProxyRe`.
Per review on #36293, this rename is out of scope for the registry-alias
fix and just adds review surface.

* refactor(manager/dockerfile): narrow alias boundary check to '}'

Per review on #36293. The previous regex `/[^A-Za-z0-9_]$/` matched any
non-word ending, which is broader than the documented case (alias keys
ending in `}` from `${VAR}` / `${VAR:-...}`). Replace with a plain
`name.endsWith('}')` check, which also avoids re-building a regex on
every iteration of the alias loop.

Existing tests (`'variables with default and slash'`,
`'variables with default no slash'`, `'alias with digest only'`)
continue to exercise the `}`-boundary path; the `'overlapping prefixes'`
case continues to exercise the slash-only path.

* chore(manager/dockerfile): trim comment and TODO library/ strip edge

Per review on #36293:
* Drop the verbose explanation block above the depName-restore branch;
  the spec rows already document the expected shape.
* Add a TODO above the `dep.depName?.startsWith(valueWithSlash)` guard
  noting a pre-existing edge case: when the inner `getDep` strips a
  `library/` prefix (or similar) the depName no longer starts with
  `valueWithSlash`, so the alias-rooted depName is not restored. Out of
  scope for this PR.

* fix(manager/dockerfile): handle empty registry alias value

- strip trailing slash after ${VAR} placeholders in sanitizeDepName
- treat an empty registry alias value as Docker Hub instead of '/'
- clarify registry alias docs wording

* refactor(manager/dockerfile): drop redundant alias boundary guard

- `matchedAtBoundary` is only read behind `!matchedWithSlash`
- add docker-compose test for `${VAR:-}` registry aliases

* test(manager/dockerfile): pin special-prefix registry alias behavior

`getDep` strips `library/`, `amd64/` and `arm64/` as special prefixes, so
an alias whose value is one of those loses its alias-rooted `depName`.
Both the slash form and the variable form behave this way on `main`.
2026-09-03 12:43:47 +00:00

103 lines
2.8 KiB
Markdown

### Supported dependencies
This manager extracts image references in a `Dockerfile` and/or `Containerfile` and supports:
- [`FROM`](https://docs.docker.com/reference/dockerfile/#from) images
- [`COPY --from`](https://docs.docker.com/reference/dockerfile/#copy---from) images
- [`RUN --mount`](https://docs.docker.com/reference/dockerfile/#run---mount) images
- [`syntax`](https://docs.docker.com/reference/dockerfile/#syntax) images
#### `FROM` support
Renovate can update images referenced in `FROM` directives.
This even works for multi-stage builds with many `FROM` directives in one Dockerfile.
```dockerfile
FROM node:20.9.0
```
Advanced `FROM` flags like `--platform` or `AS <name>` are also supported:
```dockerfile
FROM --platform=linux/amd64 node:20.9.0 AS installer
```
Also, Renovate will automatically expand variables and [`ARG` directives](https://docs.docker.com/reference/dockerfile/#understand-how-arg-and-from-interact):
```dockerfile
ARG TAG=3.19.4
FROM alpine:${TAG}
```
Renovate supports variables with default values (using the `:-` syntax) when they are configured with registry aliases:
```dockerfile
FROM ${CI_REGISTRY:-}alpine:3.19.4
```
```json
{
"registryAliases": {
"${CI_REGISTRY:-}": "my-registry.io"
}
}
```
Both image reference formats work:
- With a slash after the variable: `${CI_REGISTRY:-}/alpine:3.19.4`
- Without a slash after the variable: `${CI_REGISTRY:-}alpine:3.19.4`
The alias value works with or without a trailing slash.
#### `COPY --from` support
Renovate can update images referenced in `COPY --from` directives.
```dockerfile
FROM node:20.9.0
COPY --from alpine:3.19.4 /bin/sh /usr/local/sh
```
#### `RUN --mount` support
Images referenced in `RUN --mount` directives are also supported.
```dockerfile
FROM python:3.12
RUN --mount=from=ghcr.io/astral-sh/uv:0.5,source=/uv,target=/bin/uv \
uv venv
```
#### `syntax` support
Renovate can update `syntax` references.
```dockerfile
# syntax=docker/dockerfile:1.9.0
FROM alpine:3.19.4
```
### Versioning
Renovate's managers does not understand versioning, that's up to Renovate's versioning modules.
The default `docker` versioning for container image datasources treats suffixes as "compatibility", for example: `-alpine`.
Many container images are _not_ SemVer compliant because they use such suffixes in their tags.
If Renovate does not update your container images correctly, you may need to tell Renovate what versioning it should use.
For example, if you know that an image follows SemVer, you can tell Renovate to use `"semver"` versioning for that image:
```json
{
"packageRules": [
{
"matchDatasources": ["docker"],
"matchPackageNames": ["whitesource/renovate"],
"versioning": "semver"
}
]
}
```
Read [Renovate's Docker Versioning](../../versioning/docker/index.md) docs to learn more.