Skip to content

fix(alpine,hackage): keep constraints out of PURL names and encode components - #1352

Merged
mstykow merged 1 commit into
mainfrom
fix/encode-alpine-and-hackage-purls
Aug 11, 2026
Merged

mstykow merged 1 commit into
mainfrom
fix/encode-alpine-and-hackage-purls

Conversation

@mstykow

@mstykow mstykow commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • An apk dependency token carries its constraint inline — musl>=1.2.0, busybox=1.36.1-r5, zlib~1.3 — so it is not a package name. Treating it as one put the constraint inside the PURL: pkg:apk/alpine/musl>=1.2.0 re-parses to a name of musl>=1.2.0, and this form ships in a golden today.
  • On the installed-db path it was worse: the constraint went into the PURL and extracted_requirement was left null, so the version requirement was recorded nowhere usable.
  • hackage takes its name from a free-text .cabal field. A / there is read back as a namespace separator, which hackage prohibits — so the PURL does not parse at all.

Scope and exclusions

  • Included: split the apk token so the name identifies the package and the full token becomes the declared requirement; route apk and hackage PURLs through the shared encoders.
  • A bare D:musl keeps extracted_requirement unset — repeating the identity the PURL already carries would be noise, and "prefer honest unknowns" applies: there is no version requirement to record.

How to verify

provenant --package --json-pp - some.apk | jq '[.files[].package_data[].dependencies[] | {purl, extracted_requirement}]'

Before: {"purl": "pkg:apk/alpine/musl>=1.2.0", ...}. After: {"purl": "pkg:apk/alpine/musl", "extracted_requirement": "musl>=1.2.0"}.

For hackage, a .cabal with name: ns/pkg gave pkg:hackage/ns/pkg@2.0 (rejected by any PURL parser) and now gives pkg:hackage/ns%2Fpkg@2.0, which decodes back to the literal name.

Intentional differences from Python

  • None.

Expected-output fixture changes

  • Files changed: testdata/alpine/apk/basic/test-package-1.0-r0.apk.expected.json, one line.
  • Why the new expected output is correct: pkg:apk/alpine/musl>=1.2.0 was never a valid identity — the PURL name is the package, and >=1.2.0 is a constraint. The same entry already carried "extracted_requirement": "musl>=1.2.0", so the constraint is not lost; it simply stops being part of the package's identity. No other fixture moves, including the alpine assembly golden, whose fixture declares a bare D:musl.

…mponents

An apk dependency token carries its constraint inline — `musl>=1.2.0`,
`busybox=1.36.1-r5`, `zlib~1.3` — so it is not a package name. Treating it as one
put the constraint inside the PURL, which re-parses to a name of `musl>=1.2.0`,
and on the installed-db path left the constraint recorded nowhere else at all.
Split the token: the name identifies the package, the full token becomes the
declared requirement. A bare name keeps no requirement, since that would only
repeat the identity the PURL already carries.

hackage takes its name from a free-text `.cabal` field, where a `/` would be read
back as a namespace separator — prohibited for that type, so the PURL does not
parse at all.

Both now build through the shared encoders.

One golden line changes: the apk archive fixture's `pkg:apk/alpine/musl>=1.2.0`
becomes `pkg:apk/alpine/musl`, with the constraint already present in its
`extracted_requirement`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Maxim Stykow <maxim.stykow@gmail.com>
@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown

Greptile Summary

The PR corrects Alpine dependency identities by separating inline constraints from package names and routes Alpine and Hackage PURLs through shared component encoders.

  • Alpine dependency constraints are retained in extracted_requirement instead of being embedded in PURL names.
  • Alpine source and dependency PURLs now use the shared namespaced encoder.
  • Hackage names and versions now use the shared PURL builder, with a round-trip test for slash-containing names.
  • The Alpine APK golden fixture records the corrected dependency PURL.

Confidence Score: 5/5

The PR appears safe to merge with no concrete blocking or non-blocking defects identified.

The changed parsers preserve dependency requirements while producing component-encoded PURLs, and the investigated edge cases did not establish a realistic reachable regression.

Important Files Changed

Filename Overview
src/parsers/alpine.rs Splits inline APK constraints from dependency identities and consistently encodes Alpine source and dependency PURLs.
src/parsers/hackage.rs Replaces direct Hackage PURL formatting with the shared component-aware encoder across dependency paths.
src/parsers/hackage_test.rs Verifies that slash-containing Hackage names are encoded, parse successfully, and round-trip without becoming namespaces.
testdata/alpine/apk/basic/test-package-1.0-r0.apk.expected.json Updates the expected Alpine dependency identity while preserving its declared requirement.

Reviews (1): Last reviewed commit: "fix(alpine,hackage): keep constraints ou..." | Re-trigger Greptile

@mstykow
mstykow merged commit 5d3e950 into main Aug 11, 2026
13 checks passed
@mstykow
mstykow deleted the fix/encode-alpine-and-hackage-purls branch August 11, 2026 23:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant