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>
When we introduced the `apk` datasource, we missed that the default for
the Datasource was the `loose` versioning, which doesn't make as much
sense as `apk`.
This may be a small breaking change for users who are working with the
APK datasource already, but this is a minimal enough change that this is
fine, and can be restored with a Package Rule.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
As this slipped past our code review, but `matchPaths` has been
deprecated for ~3 years, replaced by `matchFileNames`.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
* feat(datasource): add APK datasource support
* remove missing manager link for now
* lint docs
* update tests
* replace binary fixtures
* fix coverage
* Update lib/modules/datasource/apk/readme.md
Co-authored-by: Jamie Tanna <github@jamietanna.co.uk>
* Apply suggestion from @viceice
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
* use tar instead of tar-stream
* use codeBlock
* add ignore back
* make method private
* use an async extract instead of list
* add usage example
* stream apkindex download and parse line-by-line
* refactor(datasource): address review comments on APK datasource
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: sync pnpm-lock.yaml after merge
Co-authored-by: Cursor <cursoragent@cursor.com>
* test: remove ignored lines
As they seem to be covered, anyway.
* fixup! Merge remote-tracking branch 'origin/HEAD' into apk-datasource
* fixup! fixup! Merge remote-tracking branch 'origin/HEAD' into apk-datasource
* lint: use `node:` prefix
* Update lib/modules/datasource/apk/index.ts
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
* refactor(datasource/apk): use asTimestamp millis instead of new Date
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(datasource/apk): rename uncached method to _getPackages
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(datasource/apk): encode registry URL parts as query parameters
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(datasource/apk): reject invalid registry URLs, skip caching empty indexes
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(datasource/apk): throw on unparseable registry URL and drop unset sourceUrl support
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(datasource/apk): show how registry URL parameters build the index URLs
Co-authored-by: Cursor <cursoragent@cursor.com>
* Apply suggestions from code review
Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com>
* fix(datasource/apk): keep credentials and port in component URLs
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(datasource/apk): extract APKINDEX by basename and key packages by name
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(datasource/apk): report a version served by two components only once
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Jamie Tanna <github@jamietanna.co.uk>
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Jamie Tanna <jamie.tanna@mend.io>
Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com>