Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spec for brew package URLs #281

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
27 changes: 26 additions & 1 deletion PURL-TYPES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,32 @@ bitnami
pkg:bitnami/wordpress@6.2.0?arch=arm64&distro=debian-12
pkg:bitnami/wordpress@6.2.0?arch=arm64&distro=photon-4

brew
----
``brew`` for Homebrew-based packages:

- There is no default package repository; this should be implied either from
the ``namespace`` or using a tap URL via the ``tap_url`` qualifier.
- The ``namespace``, which is called a "Tap" in Homebrew terminology, defaults to ``homebrew/core``.

- When the ``tap_url`` qualifier is not specified, the Tap identifier corresponds to the URL
``https://github.com/{org}/homebrew-{tap}``, such as
``https://github.com/homebrew/homebrew-core`` for ``homebrew/core``.
- When the ``tap_url`` qualifier is specified, the Tap identifier is the local name of the Tap.

- The ``name`` is the formula name. Formula names that contain ``@`` must be percent-encoded,
such as ``postgresql%4012`` for ``postgres@12``.
- The ``version`` is the formula version.

Choose a reason for hiding this comment

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

question (non-blocking): When thinking about the examples below, I think you should clarify how purl spec should handle version-in-formula formulae.

As in, if I want PostgreSQL 12.17 (current release as of this posting), should the purl be

pkg:brew/postgresql@12@12.17

or

pkg:brew/postgresql@12.17

and let whatever's reading the purl — Homebrew, ostensibly — figure out how that version maps to our major-version formulae?

I speculate only the latter is a valid purl, a safe assumption because I don't think it's a good idea to allow two @ in the purl.

Copy link
Author

@woodruffw woodruffw Dec 8, 2023

Choose a reason for hiding this comment

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

Thanks for calling this out -- my read of the purl spec is that the name and other components are always URL-escaped, e.g. pkg:brew/postgresql@12@12.17 would actually be encoded as pkg:brew/postgresql%4012@12.17 in a "wire format" context. So the purl should always unambiguously map to a package, without the end client having to do additional disambiguation work.

This is a readability sacrifice, but my (potentially wrong) understanding is that purls are mostly meant to show up only in machine-readable contexts anyways.

Choose a reason for hiding this comment

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

Sigh, it's not pretty, but if that's the way it needs to be, so be it. If that's the case, I'd recommend adding an example with that…

Copy link
Author

Choose a reason for hiding this comment

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

Done! Looks like the other examples for other ecosystem purls also contain URL-escaping examples, so we're in-line here 🙂

Copy link
Contributor

Choose a reason for hiding this comment

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

pkg:brew/postgresql@12@12.17 is incorrect. The instructions for parsing a PURL (which not all implementations follow) say to split on @ from the right, so it should parse as you expect for Homebrew, but the spec says that @ must always be escaped when it's not being used as the delimiter before the version number. It'd definitely be good to have an example and maybe even call this out since it's common for people to install packages with names like this.

giterlizzi/perl-URI-PackageURL has name "postgresql" version "12" (and discards the "12.17")

maenchen/purl, sonatype/package-url-java have name "postgresql" version "12@12.17"

package-url/packageurl-js throws an error (I thought I saw a fix for this, but maybe the PR hasn't merged)

Copy link
Author

Choose a reason for hiding this comment

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

Yep, I added @colindean's example below. Adding an explicit callout also makes sense to me; I'll add some language for that.

- Qualifier ``tap_url``: for taps that are not on GitHub or otherwise require an explicit URL,
this is the full ``git`` URL to the tap.
- Examples::

pkg:brew/sqlite@3.43.2
woodruffw marked this conversation as resolved.
Show resolved Hide resolved
pkg:brew/postgresql%4012@12.17
pkg:brew/homebrew/core/sqlite@3.43.2
pkg:brew/some-org/some-tap/some-app@1.2.3
pkg:brew/some-org/some-tap/some-app@1.2.3?tap_url=https://git.example.com/some-org/some-tap.git

cocoapods
---------
``cocoapods`` for CocoaPods:
Expand Down Expand Up @@ -586,7 +612,6 @@ Other candidate types to define:
- ``android`` for Android apk packages:
- ``atom`` for Atom packages:
- ``bower`` for Bower JavaScript packages:
- ``brew`` for Homebrew packages:
- ``buildroot`` for Buildroot packages
- ``carthage`` for Cocoapods Cocoa packages:
- ``chef`` for Chef packages:
Expand Down
108 changes: 108 additions & 0 deletions test-suite-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -550,5 +550,113 @@
"qualifiers": null,
"subpath": null,
"is_invalid": false
},
{
"description": "brew names may contain at signs",
"purl": "pkg:brew/postgres%4016",
"canonical_purl": "pkg:brew/postgres%4016",
"type": "brew",
"namespace": null,
"name": "postgres@16",
"version": null,
"qualifiers": null,
"subpath": null,
"is_invalid": false
},
{
"description": "brew may contain multiple at signs",
"purl": "pkg:brew/postgres@16@16.1",
"canonical_purl": "pkg:brew/postgres%4016@16.1",
"type": "brew",
"namespace": null,
"name": "postgres@16",
"version": "16.1",
"qualifiers": null,
"subpath": null,
"is_invalid": false
},
{
"description": "brew may specify a namespace that uses the Homebrew default tap lookup mechanism",
"purl": "pkg:brew/some-org/some-tap/sqlite@3.43.2?tap_url=https://git.example.com/some-org/some-tap.git",
"canonical_purl": "pkg:brew/some-org/some-tap/sqlite@3.43.2?tap_url=https://git.example.com/some-org/some-tap.git",
"type": "brew",
"namespace": "some-org/some-tap",
"name": "sqlite",
"version": "3.43.2",
"qualifiers": { "tap_url": "https://git.example.com/some-org/some-tap.git"},
"subpath": null,
"is_invalid": false
},
{
"description": "brew may specify a namespace that uses the Homebrew default tap lookup mechanism",
"purl": "pkg:brew/some-org/some-tap/sqlite@3.43.2",
"canonical_purl": "pkg:brew/some-org/some-tap/sqlite@3.43.2",
"type": "brew",
"namespace": "some-org/some-tap",
"name": "sqlite",
"version": "3.43.2",
"qualifiers": null,
"subpath": null,
"is_invalid": false
},
{
"description": "brew may specify a namespace that is its default repository",
"purl": "pkg:brew/homebrew/core/sqlite@3.43.2",
"canonical_purl": "pkg:brew/homebrew/core/sqlite@3.43.2",
"type": "brew",
"namespace": "homebrew/core",
"name": "sqlite",
"version": "3.43.2",
"qualifiers": null,
"subpath": null,
"is_invalid": false
},
{
"description": "brew typical formula",
"purl": "pkg:brew/sqlite@3.43.2",
"canonical_purl": "pkg:brew/sqlite@3.43.2",
"type": "brew",
"namespace": null,
"name": "sqlite",
"version": "3.43.2",
"qualifiers": null,
"subpath": null,
"is_invalid": false
},
{
"description": "invalid brew purl without name",
"purl": "pkg:brew/@0.9.1",
"canonical_purl": "pkg:brew/@0.9.1",
"type": "brew",
"namespace": null,
"name": null,
"version": "0.9.1",
"qualifiers": null,
"subpath": null,
"is_invalid": true
},
{
"description": "invalid brew purl without version",
"purl": "pkg:brew/A3",
"canonical_purl": "pkg:brew/A3",
"type": "brew",
"namespace": null,
"name": "A3",
"version": null,
"qualifiers": null,
"subpath": null,
"is_invalid": true
},
{
"description": "brew requires name and version at minimum",
"purl": "pkg:brew",
"canonical_purl": "pkg:brew",
"type": "brew",
"namespace": null,
"name": null,
"version": null,
"qualifiers": null,
"subpath": null,
"is_invalid": true
}
]