mirror of
https://github.com/renovatebot/renovate.git
synced 2026-11-02 02:29:51 +00:00
629740f827
As part of future changes, we're introducing a means to parse
Dockerfiles for any `apk add` commands, so we can propose updates to any
packages using the APK datasource.
Before we do this, we need to correct a couple of bugs in our
implementation of the APK versioning scheme, so we can handle i.e.
apk add curl=~8.12.1
When the `apk` CLI builds a version constraint, it `OR`s one flag per
operator character[0].
For instance, `~` sets both "fuzzy" and "equal".
These operator characters may be in any order and may repeat, so `~`,
`=~` and `~=` are all the same operator, and `>>` and `>` are the same.
To correct our parsing, we should take this into account, rather than
using known patterns.
When we perform a fuzzy match, this is performed over version _tokens_
not the version itself, which means that:
- `~1.6` matches `1.6`, `1.6.0_pre1`, `1.6.0`, `1.6.5` and `1.6.9_p1`
- `~1.6` does not match `1.60`
- `~8.12.1` matches every `8.12.1-rN` i.e. `8.12.1-r1` and `8.12.1-r100`
While doing this, we also fix related issues:
- `~` previously treated this as an npm-style tilde ("same major and
minor, and greater than or equal"),
- admitted fuzzy versions that `apk` rejects - `~2.39.0-r0` should not have
matched `2.39.1-r0`
- rejected fuzzy versions that `apk` accepts - `~2.39.0` should have
matched `2.39.0-r1`
- `=~` and `~=` were not allowed
- `isSingleVersion('=~1.2.3')` was true, so a range looked like a pin
- `isLessThanRange` compared against the raw range, so `_parse` failed on
the operator and it always returned false
- `minSatisfyingVersion` was inherited, so it only ever found an exact
match
- `getNewValue` dropped the operator entirely, rewriting `~8.12.1` to a
bare version
We also set `supportsRanges`, which we'd forgotten to set despite `~`
and `>=` being ranges, and note that we support `pin`, `replace` and
`bump` strategies.
When using `getNewValue`, we now keep the precision the constraint was
written with, as otherwise we'd quietly narrow what the user asked for:
- `~8.12.1` becomes `~8.13.0` (previously `~8.13.0-r0`)
- `~8.12` becomes `~8.13`
- `~8.12.1-r0` becomes `~8.13.0-r2` (with a new revision number)
For `bump`, we raise a lower bound which includes its boundary, as with
`npm` and `pep440`, so:
- `>=8.12.1` becomes `>=8.13.0`
- `>~8.12` becomes `>~8.13`
Otherwise we return `null`, logging which constraint we couldn't handle
in the `bump` case, as `npm` does:
- `>` on its own can't be bumped, as `>8.13.0` would exclude the version
we bump to
- `<` and `<=` are upper bounds, so there's no lower bound to raise
- for `replace`, there's no single obvious new bound for `<`, `<=`, `>`,
`>=`, `>~` or `<~`, and a bound which already admits the new version
doesn't need one
- a prefix constraint whose version doesn't parse, i.e. `~foo`, which
would otherwise throw
`><` constrains a package to an identity hash rather than to a version,
so it's rejected outright.
Additionally, `isLessThanRange` and `matches` weren't agreeing on how to
resolve a fuzzy constraint.
For instance, `_compare` ranks `2.39.0_git20240101` below `2.39.0`, but
`~2.39.0` matches it, this would lead to a rollback of sorts.
Instead, we can ignore the `_git` and `_p` suffixes, which are common in
Alpine and Wolfi packages.
[0]: https://gitlab.alpinelinux.org/alpine/apk-tools/-/blob/44dcdfc255c26a4e19a4a27f66a54169cce5ca72/src/version.c
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
153 lines
5.8 KiB
Markdown
153 lines
5.8 KiB
Markdown
The APK datasource is designed to work with Alpine Linux package repositories.
|
|
It can fetch package information from APK repositories and provide version updates for Alpine Linux packages.
|
|
|
|
Alpine Linux uses the APK package manager, and packages are distributed through repositories.
|
|
Each repository contains an `APKINDEX.tar.gz` file that contains metadata about all available packages.
|
|
|
|
Files are typically located in this structure:
|
|
|
|
```
|
|
https://dl-cdn.alpinelinux.org/alpine/v3.19/main/x86_64/APKINDEX.tar.gz
|
|
https://dl-cdn.alpinelinux.org/alpine/v3.19/community/x86_64/APKINDEX.tar.gz
|
|
```
|
|
|
|
## Set URL when using an APK repository
|
|
|
|
To use an APK repository with the datasource, you must set a properly formatted URL with specific query parameters as `registryUrl`:
|
|
|
|
- `arch`: Architecture of the binary packages (e.g. `x86_64`, `aarch64`, `armv7`).
|
|
- `branch`: Alpine branch, either a rolling alias like `latest-stable` or `edge`, or a fixed release like `v3.19`.
|
|
Omit this parameter for repositories which do not have a branch in their path.
|
|
- `components`: Comma-separated list of repository components (e.g. `main,community,testing`).
|
|
Omit this parameter for repositories which serve their index directly below the repository root.
|
|
|
|
!!! note
|
|
Renovate does not fetch the `registryUrl` as-is.
|
|
It combines the base URL with these parameters to build the actual index URLs, for example `<base>/<branch>/<component>/<arch>/APKINDEX.tar.gz`.
|
|
Only the parameters above are accepted, so that a typo fails loudly instead of being ignored.
|
|
|
|
**Examples**:
|
|
|
|
```
|
|
https://dl-cdn.alpinelinux.org/alpine?branch=v3.19&components=main,community&arch=x86_64
|
|
https://packages.wolfi.dev/os?arch=x86_64
|
|
```
|
|
|
|
The first URL points to the `v3.19` branch of the Alpine repository for the `x86_64` architecture, including the `main` and `community` components.
|
|
Renovate looks up the package in every component you list, and aggregates the releases it finds.
|
|
|
|
The second URL points to the Wolfi repository, which has no branch or component in its path.
|
|
|
|
### How Renovate builds the index URLs
|
|
|
|
Say you set this `registryUrl` in your Renovate config:
|
|
|
|
```json title="Set the apk registryUrl in renovate.json"
|
|
{
|
|
"packageRules": [
|
|
{
|
|
"matchDatasources": ["apk"],
|
|
"registryUrls": [
|
|
"https://dl-cdn.alpinelinux.org/alpine?branch=v3.19&components=main,community&arch=x86_64"
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Renovate then fetches one index per component:
|
|
|
|
```
|
|
https://dl-cdn.alpinelinux.org/alpine/v3.19/main/x86_64/APKINDEX.tar.gz
|
|
https://dl-cdn.alpinelinux.org/alpine/v3.19/community/x86_64/APKINDEX.tar.gz
|
|
```
|
|
|
|
## Versioning
|
|
|
|
This datasource uses [`apk` versioning](../../versioning/apk/index.md) by default, which follows Alpine's version format (`3.2.1-r0`, `2.39.0_rc1-r0`, `6.5_p20250503-r0`) and understands APK's version constraints.
|
|
|
|
This means a `currentValue` may be a constraint rather than a plain version, e.g. `=~8.12.1` to accept any `8.12.1-rN`.
|
|
Read the [`apk` versioning](../../versioning/apk/index.md) docs for the operators it supports.
|
|
|
|
Depending on which APK repository you are using, you may want to use [the `loose` versioning scheme](../../versioning/loose/index.md), like so:
|
|
|
|
```json title="Specify loose versioning for apk lookups"
|
|
{
|
|
"packageRules": [
|
|
{
|
|
"matchDatasources": ["apk"],
|
|
"versioning": "loose"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
<!-- TODO #43711 -->
|
|
|
|
## Usage example
|
|
|
|
Say you pin Alpine packages in a `Dockerfile` and want Renovate to bump the versions.
|
|
Combine the `apk` datasource with a [regex manager](../../manager/regex/index.md).
|
|
|
|
Add a custom manager in `renovate.json`.
|
|
The optional `branch` capture group is filled from the Renovate comment and interpolated into `registryUrlTemplate` by the regex manager.
|
|
|
|
```json
|
|
{
|
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
"customManagers": [
|
|
{
|
|
"customType": "regex",
|
|
"managerFilePatterns": ["/^Dockerfile$/"],
|
|
"matchStrings": [
|
|
"#\\s*renovate:\\s*(?:branch=(?<branch>\\S+)\\s+)?depName=(?<depName>\\S+)\\s+ENV .*?_VERSION=\"(?<currentValue>.*)\""
|
|
],
|
|
"registryUrlTemplate": "https://dl-cdn.alpinelinux.org/alpine?branch={{#if branch}}{{branch}}{{else}}v3.19{{/if}}&components=main,community&arch=x86_64",
|
|
"datasourceTemplate": "apk"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
The regex manager supplies `depName` (becomes `packageName`) and `currentValue` (the pinned APK version).
|
|
The datasource then fetches `APKINDEX.tar.gz` for each component, finds `depName` in the index, and compares versions.
|
|
|
|
Match the `registryUrl` parameters to your image: the Alpine branch (`v3.19` in the default above) and the architecture (`x86_64` here, use `aarch64` on arm64).
|
|
|
|
```dockerfile
|
|
FROM alpine:3.19
|
|
|
|
# renovate: branch=v3.19 depName=nginx
|
|
ENV NGINX_VERSION="1.26.2-r0"
|
|
|
|
RUN apk add --no-cache "nginx=${NGINX_VERSION}"
|
|
```
|
|
|
|
`depName` must match the package name in `APKINDEX` (the `P:` field), e.g. `nginx` for the `nginx` package.
|
|
You can omit `branch=` in the comment when the template default (`v3.19` above) matches your Alpine line.
|
|
|
|
### Multiple Dockerfiles or Alpine versions
|
|
|
|
The datasource receives one `registryUrl` per lookup.
|
|
Besides the optional `branch=` pattern in the usage example, you can:
|
|
|
|
1. **Several custom managers** with different `managerFilePatterns` / `matchFilePatterns` and a fixed `registryUrlTemplate` each (e.g. one for `docker/alpine-3.18/**`, another for `docker/alpine-3.19/**`).
|
|
|
|
1. **`packageRules`** with `matchFileNames` and `registryUrls` to override the parameters for specific paths or packages.
|
|
|
|
For example, this `packageRules` entry overrides the `registryUrl` for the `nginx` package:
|
|
|
|
```json title="Override apk registryUrl with a packageRules entry"
|
|
{
|
|
"packageRules": [
|
|
{
|
|
"matchDatasources": ["apk"],
|
|
"matchPackageNames": ["nginx"],
|
|
"registryUrls": [
|
|
"https://dl-cdn.alpinelinux.org/alpine?branch=v3.18&components=main,community&arch=x86_64"
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|