Skip to content

Normalize PURLs constructed via New/MakePURL/MakePURLString - #30

Merged
andrew merged 1 commit into
mainfrom
fix/normalize-new-makepurl
Aug 16, 2026
Merged

Normalize PURLs constructed via New/MakePURL/MakePURLString#30
andrew merged 1 commit into
mainfrom
fix/normalize-new-makepurl

Conversation

@andrew

@andrew andrew commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Parse runs packageurl-go's Normalize (per-type lowercasing, PyPI underscore-to-dash, etc) but New and the string-builder helpers did not, so pkg:pypi/Django and pkg:pypi/django were distinct depending on which entry point produced them.

New now calls Normalize on the constructed struct. MakePURLString and BuildPURLString run the same normalization on a stack-allocated struct before the fast string builder, passing repository_url through so registry-dependent rules (mlflow) apply. Normalize errors are discarded and whatever fields it wrote are kept, so New and the string path stay in step even for inputs that fail per-type validation.

This changes output for callers that persist non-canonical strings; git-pkgs/proxy#207 tracks the migration there.

BenchmarkMakePURLString goes from 1 alloc/call to 2 (npm: 242ns to 405ns) because upstream Normalize splits an empty subpath. Still well under the pre-#26 numbers.

Fixes #21

Parse runs packageurl-go's Normalize (per-type lowercasing, PyPI
underscore-to-dash, etc) but New and the string-builder helpers did not,
so pkg:pypi/Django and pkg:pypi/django were treated as distinct depending
on which entry point produced them.

New now calls Normalize on the constructed struct. MakePURLString and
BuildPURLString run the same normalization on a stack-allocated struct
before the fast string builder, passing repository_url through so
registry-dependent rules (mlflow) apply. Normalize errors are discarded
and whatever fields it wrote are kept, so New and the string path stay in
step even for inputs that fail per-type validation.

Fixes #21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Aligns all PURL construction entry points (New, MakePURLString, BuildPURLString) with Parse by applying packageurl-go’s per-type normalization rules (e.g., PyPI underscore→dash, per-ecosystem lowercasing, mlflow registry-dependent casing), preventing logically identical packages from producing distinct PURL strings depending on which constructor was used.

Changes:

  • Normalize New(...) results via PackageURL.Normalize() (ignoring the returned error, keeping any in-place adjustments).
  • Add normalizeComponents(...) and apply it to the fast string-building paths (BuildPURLString, MakePURLString) while passing repository_url for registry-dependent normalization.
  • Add/extend tests to cover normalization behavior and ensure parity between “struct path” and “string path”, including a case where Normalize() returns an error after making partial adjustments.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
purl.go Calls Normalize() in New so constructed PURLs match Parse canonicalization.
purl_test.go Adds normalization-specific cases and a New vs Parse round-trip equivalence test.
makepurl.go Introduces normalizeComponents and applies it before the fast string builder (including registry-dependent normalization).
makepurl_test.go Adds normalization coverage for composer/pypi/golang/mlflow and expands parity tests.
ecosystem.go Normalizes components in MakePURLString to match MakePURL/Parse canonical output.
ecosystem_test.go Adds normalization cases and verifies MakePURLString matches MakePURL even when Normalize() errors.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@andrew
andrew merged commit acdc843 into main Aug 16, 2026
6 checks passed
@andrew
andrew deleted the fix/normalize-new-makepurl branch August 16, 2026 22:15
andrew added a commit to git-pkgs/proxy that referenced this pull request Aug 17, 2026
MakePURL/MakePURLString/New now apply the same per-type normalization as
Parse (git-pkgs/purl#30), so canonicalPackagePURL no longer needs its own
Normalize call and DB writes/lookups produce canonical keys.

Existing rows written under a non-canonical purl (mixed-case pypi,
composer, etc) become cache misses on lookup and re-populate under the
canonical key on the next fetch; the old rows are left in place.

Closes #207
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.

MakePURL/New skip normalization that Parse applies

2 participants