purl 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/exe/purl +49 -0
- data/lib/purl/lookup.rb +194 -0
- data/lib/purl/lookup_formatter.rb +119 -0
- data/lib/purl/package_url.rb +30 -0
- data/lib/purl/version.rb +1 -1
- data/lib/purl.rb +8 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5be58ada7f5731a371cee07f0175e020aa7a49ea7d228c88e1af4c2554d5ecc
|
|
4
|
+
data.tar.gz: 96e6ff76c5626c4c414651324eee74c4845e4fb337b93470dc534d49622facfc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: adfb3b0b945ad8787738388c3b40087ae662974ba60ff3783100ba845c8e19e8fc73c4480b3296a5df701080af31919dbea66d0c5d3803ec29e18a098da0d837
|
|
7
|
+
data.tar.gz: a96abd807ede7d4c6eaff1cb0bdd6bacdfc8723e3dad839f17ecbc7b3502edb9b24e2f32f84fec3b3c100e7ec5cd3a940229fc6421f964c9927fc370587948a3
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.5.0] - 2025-08-06
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `versionless` convenience method to create a PackageURL without version component
|
|
14
|
+
- `lookup` command to CLI for fetching package information from ecosyste.ms API with version-specific details
|
|
15
|
+
- `Purl::Lookup` class for programmatic package information lookup
|
|
16
|
+
- `lookup` instance method on `PackageURL` for convenient package information retrieval
|
|
17
|
+
- `Purl::LookupFormatter` class for customizable lookup result formatting
|
|
18
|
+
- Package maintainer information display in lookup results
|
|
19
|
+
|
|
10
20
|
## [1.4.0] - 2025-01-06
|
|
11
21
|
|
|
12
22
|
### Added
|
data/exe/purl
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
require "optparse"
|
|
5
5
|
require "json"
|
|
6
|
+
require "net/http"
|
|
7
|
+
require "uri"
|
|
6
8
|
require_relative "../lib/purl"
|
|
7
9
|
|
|
8
10
|
class PurlCLI
|
|
@@ -40,6 +42,8 @@ class PurlCLI
|
|
|
40
42
|
url_command(args)
|
|
41
43
|
when "info"
|
|
42
44
|
info_command(args)
|
|
45
|
+
when "lookup"
|
|
46
|
+
lookup_command(args)
|
|
43
47
|
when "--help", "-h", "help"
|
|
44
48
|
puts usage
|
|
45
49
|
exit 0
|
|
@@ -66,6 +70,7 @@ class PurlCLI
|
|
|
66
70
|
purl [--json] url <purl-string> Convert PURL to registry URL
|
|
67
71
|
purl [--json] generate [options] Generate PURL from components
|
|
68
72
|
purl [--json] info [type] Show information about PURL types
|
|
73
|
+
purl [--json] lookup <purl-string> Look up package information from ecosyste.ms
|
|
69
74
|
purl --version Show version
|
|
70
75
|
purl --help Show this help
|
|
71
76
|
|
|
@@ -80,6 +85,7 @@ class PurlCLI
|
|
|
80
85
|
purl url "pkg:gem/rails@7.0.0"
|
|
81
86
|
purl generate --type gem --name rails --version 7.0.0
|
|
82
87
|
purl --json info gem
|
|
88
|
+
purl lookup "pkg:cargo/rand"
|
|
83
89
|
USAGE
|
|
84
90
|
end
|
|
85
91
|
|
|
@@ -417,6 +423,49 @@ class PurlCLI
|
|
|
417
423
|
end
|
|
418
424
|
end
|
|
419
425
|
|
|
426
|
+
def lookup_command(args)
|
|
427
|
+
if args.empty?
|
|
428
|
+
output_error("PURL string required")
|
|
429
|
+
exit 1
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
purl_string = args[0]
|
|
433
|
+
|
|
434
|
+
begin
|
|
435
|
+
# Validate PURL first
|
|
436
|
+
purl = Purl.parse(purl_string)
|
|
437
|
+
|
|
438
|
+
# Use the library lookup method
|
|
439
|
+
info = purl.lookup(user_agent: "purl-ruby-cli/#{Purl::VERSION}")
|
|
440
|
+
|
|
441
|
+
# Use formatter to generate output
|
|
442
|
+
formatter = Purl::LookupFormatter.new
|
|
443
|
+
|
|
444
|
+
if @json_output
|
|
445
|
+
result = formatter.format_json(info, purl)
|
|
446
|
+
puts JSON.pretty_generate(result)
|
|
447
|
+
exit 1 unless result[:success]
|
|
448
|
+
else
|
|
449
|
+
if info
|
|
450
|
+
puts formatter.format_text(info, purl)
|
|
451
|
+
else
|
|
452
|
+
puts "Package not found in ecosyste.ms database"
|
|
453
|
+
exit 1
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
rescue Purl::Error => e
|
|
458
|
+
output_error("Invalid PURL: #{e.message}")
|
|
459
|
+
exit 1
|
|
460
|
+
rescue Purl::LookupError => e
|
|
461
|
+
output_error("Lookup failed: #{e.message}")
|
|
462
|
+
exit 1
|
|
463
|
+
rescue StandardError => e
|
|
464
|
+
output_error("Lookup failed: #{e.message}")
|
|
465
|
+
exit 1
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
420
469
|
def output_error(message)
|
|
421
470
|
if @json_output
|
|
422
471
|
result = {
|
data/lib/purl/lookup.rb
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "uri"
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
module Purl
|
|
8
|
+
# Provides lookup functionality for packages using the ecosyste.ms API
|
|
9
|
+
class Lookup
|
|
10
|
+
ECOSYSTE_MS_API_BASE = "https://packages.ecosyste.ms/api/v1"
|
|
11
|
+
|
|
12
|
+
# Initialize a new Lookup instance
|
|
13
|
+
#
|
|
14
|
+
# @param user_agent [String] User agent string for API requests
|
|
15
|
+
# @param timeout [Integer] Request timeout in seconds
|
|
16
|
+
def initialize(user_agent: nil, timeout: 10)
|
|
17
|
+
@user_agent = user_agent || "purl-ruby/#{Purl::VERSION}"
|
|
18
|
+
@timeout = timeout
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Look up package information for a given PURL
|
|
22
|
+
#
|
|
23
|
+
# @param purl [String, PackageURL] PURL string or PackageURL object
|
|
24
|
+
# @return [Hash, nil] Package information hash or nil if not found
|
|
25
|
+
# @raise [LookupError] if the lookup fails due to network or API errors
|
|
26
|
+
#
|
|
27
|
+
# @example
|
|
28
|
+
# lookup = Purl::Lookup.new
|
|
29
|
+
# info = lookup.package_info("pkg:cargo/rand@0.9.2")
|
|
30
|
+
# puts info[:package][:name] # => "rand"
|
|
31
|
+
# puts info[:version][:published_at] if info[:version] # => "2025-07-20T17:47:01.870Z"
|
|
32
|
+
def package_info(purl)
|
|
33
|
+
purl_obj = purl.is_a?(PackageURL) ? purl : PackageURL.parse(purl.to_s)
|
|
34
|
+
|
|
35
|
+
# Make API request to ecosyste.ms
|
|
36
|
+
uri = URI("#{ECOSYSTE_MS_API_BASE}/packages/lookup")
|
|
37
|
+
uri.query = URI.encode_www_form({ purl: purl_obj.to_s })
|
|
38
|
+
|
|
39
|
+
response_data = make_request(uri)
|
|
40
|
+
|
|
41
|
+
return nil unless response_data.is_a?(Array) && response_data.length > 0
|
|
42
|
+
|
|
43
|
+
package_data = response_data[0]
|
|
44
|
+
|
|
45
|
+
result = {
|
|
46
|
+
purl: purl_obj.to_s,
|
|
47
|
+
package: extract_package_info(package_data)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# If PURL has a version and we have a versions_url, fetch version-specific details
|
|
51
|
+
if purl_obj.version && package_data["versions_url"]
|
|
52
|
+
version_info = fetch_version_info(package_data["versions_url"], purl_obj.version)
|
|
53
|
+
result[:version] = version_info if version_info
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
result
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Look up version information for a specific version of a package
|
|
60
|
+
#
|
|
61
|
+
# @param purl [String, PackageURL] PURL string or PackageURL object (must include version)
|
|
62
|
+
# @return [Hash, nil] Version information hash or nil if not found
|
|
63
|
+
# @raise [LookupError] if the lookup fails due to network or API errors
|
|
64
|
+
# @raise [ArgumentError] if the PURL doesn't include a version
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# lookup = Purl::Lookup.new
|
|
68
|
+
# version_info = lookup.version_info("pkg:cargo/rand@0.9.2")
|
|
69
|
+
# puts version_info[:published_at] # => "2025-07-20T17:47:01.870Z"
|
|
70
|
+
def version_info(purl)
|
|
71
|
+
purl_obj = purl.is_a?(PackageURL) ? purl : PackageURL.parse(purl.to_s)
|
|
72
|
+
|
|
73
|
+
raise ArgumentError, "PURL must include a version" unless purl_obj.version
|
|
74
|
+
|
|
75
|
+
# First get the package info to get the versions_url
|
|
76
|
+
package_result = package_info(purl_obj.versionless)
|
|
77
|
+
return nil unless package_result && package_result[:package][:versions_url]
|
|
78
|
+
|
|
79
|
+
fetch_version_info(package_result[:package][:versions_url], purl_obj.version)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def make_request(uri)
|
|
85
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
86
|
+
http.use_ssl = true
|
|
87
|
+
http.read_timeout = @timeout
|
|
88
|
+
http.open_timeout = @timeout
|
|
89
|
+
|
|
90
|
+
request = Net::HTTP::Get.new(uri)
|
|
91
|
+
request["User-Agent"] = @user_agent
|
|
92
|
+
|
|
93
|
+
response = http.request(request)
|
|
94
|
+
|
|
95
|
+
case response.code.to_i
|
|
96
|
+
when 200
|
|
97
|
+
JSON.parse(response.body)
|
|
98
|
+
when 404
|
|
99
|
+
nil
|
|
100
|
+
else
|
|
101
|
+
raise LookupError, "API request failed with status #{response.code}"
|
|
102
|
+
end
|
|
103
|
+
rescue JSON::ParserError => e
|
|
104
|
+
raise LookupError, "Failed to parse API response: #{e.message}"
|
|
105
|
+
rescue Net::TimeoutError, Net::OpenTimeout, Net::ReadTimeout => e
|
|
106
|
+
raise LookupError, "Request timeout: #{e.message}"
|
|
107
|
+
rescue StandardError => e
|
|
108
|
+
raise LookupError, "Lookup failed: #{e.message}"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def extract_package_info(package_data)
|
|
112
|
+
{
|
|
113
|
+
name: package_data["name"],
|
|
114
|
+
ecosystem: package_data["ecosystem"],
|
|
115
|
+
description: package_data["description"],
|
|
116
|
+
homepage: package_data["homepage"],
|
|
117
|
+
repository_url: package_data["repository_url"],
|
|
118
|
+
registry_url: package_data["registry_url"],
|
|
119
|
+
licenses: package_data["licenses"],
|
|
120
|
+
latest_version: package_data["latest_release_number"],
|
|
121
|
+
latest_version_published_at: package_data["latest_release_published_at"],
|
|
122
|
+
versions_count: package_data["versions_count"],
|
|
123
|
+
keywords: package_data["keywords_array"],
|
|
124
|
+
install_command: package_data["install_command"],
|
|
125
|
+
documentation_url: package_data["documentation_url"],
|
|
126
|
+
maintainers: extract_maintainers(package_data["maintainers"]),
|
|
127
|
+
versions_url: package_data["versions_url"]
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def fetch_version_info(versions_url, version)
|
|
132
|
+
return nil unless versions_url && version
|
|
133
|
+
|
|
134
|
+
begin
|
|
135
|
+
uri = URI("#{versions_url}/#{URI.encode_www_form_component(version)}")
|
|
136
|
+
data = make_request(uri)
|
|
137
|
+
|
|
138
|
+
return nil unless data
|
|
139
|
+
|
|
140
|
+
# Extract relevant version information
|
|
141
|
+
version_info = {
|
|
142
|
+
number: data["number"],
|
|
143
|
+
published_at: data["published_at"],
|
|
144
|
+
version_url: data["version_url"],
|
|
145
|
+
download_url: data["download_url"],
|
|
146
|
+
registry_url: data["registry_url"],
|
|
147
|
+
documentation_url: data["documentation_url"],
|
|
148
|
+
install_command: data["install_command"]
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Add metadata if available
|
|
152
|
+
if data["metadata"]
|
|
153
|
+
metadata = data["metadata"]
|
|
154
|
+
version_info[:downloads] = metadata["downloads"] if metadata["downloads"]
|
|
155
|
+
version_info[:size] = metadata["crate_size"] || metadata["size"] if metadata["crate_size"] || metadata["size"]
|
|
156
|
+
version_info[:yanked] = metadata["yanked"] if metadata.key?("yanked")
|
|
157
|
+
|
|
158
|
+
if metadata["published_by"] && metadata["published_by"].is_a?(Hash)
|
|
159
|
+
published_by = metadata["published_by"]
|
|
160
|
+
if published_by["name"] && published_by["login"]
|
|
161
|
+
version_info[:published_by] = "#{published_by["name"]} (#{published_by["login"]})"
|
|
162
|
+
elsif published_by["login"]
|
|
163
|
+
version_info[:published_by] = published_by["login"]
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
version_info
|
|
169
|
+
rescue StandardError
|
|
170
|
+
# Don't fail if version lookup fails
|
|
171
|
+
nil
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def extract_maintainers(maintainers_data)
|
|
176
|
+
return nil unless maintainers_data && maintainers_data.is_a?(Array)
|
|
177
|
+
|
|
178
|
+
maintainers_data.map do |maintainer|
|
|
179
|
+
{
|
|
180
|
+
login: maintainer["login"],
|
|
181
|
+
name: maintainer["name"],
|
|
182
|
+
url: maintainer["url"]
|
|
183
|
+
}.compact # Remove nil values
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Error raised when package lookup fails
|
|
189
|
+
class LookupError < Error
|
|
190
|
+
def initialize(message)
|
|
191
|
+
super(message)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Purl
|
|
4
|
+
# Formats package lookup results for human-readable display
|
|
5
|
+
class LookupFormatter
|
|
6
|
+
def initialize
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Format package lookup results for console output
|
|
10
|
+
#
|
|
11
|
+
# @param lookup_result [Hash] Result from Purl::Lookup#package_info
|
|
12
|
+
# @param purl [PackageURL] Original PURL object
|
|
13
|
+
# @return [String] Formatted output string
|
|
14
|
+
def format_text(lookup_result, purl)
|
|
15
|
+
return "Package not found" unless lookup_result
|
|
16
|
+
|
|
17
|
+
package = lookup_result[:package]
|
|
18
|
+
version_info = lookup_result[:version]
|
|
19
|
+
|
|
20
|
+
output = []
|
|
21
|
+
|
|
22
|
+
# Package header
|
|
23
|
+
output << "Package: #{package[:name]} (#{package[:ecosystem]})"
|
|
24
|
+
output << "#{package[:description]}" if package[:description]
|
|
25
|
+
|
|
26
|
+
# Keywords - add without extra spacing, the blank line before Version Info will handle spacing
|
|
27
|
+
if package[:keywords] && !package[:keywords].empty?
|
|
28
|
+
output << "Keywords: #{package[:keywords].join(", ")}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Version Information section
|
|
32
|
+
output << ""
|
|
33
|
+
output << "Version Information:"
|
|
34
|
+
if package[:latest_version]
|
|
35
|
+
output << " Latest: #{package[:latest_version]}"
|
|
36
|
+
output << " Published: #{package[:latest_version_published_at]}" if package[:latest_version_published_at]
|
|
37
|
+
end
|
|
38
|
+
output << " Total versions: #{format_number(package[:versions_count])}" if package[:versions_count]
|
|
39
|
+
|
|
40
|
+
# Links section
|
|
41
|
+
output << ""
|
|
42
|
+
output << "Links:"
|
|
43
|
+
output << " Homepage: #{package[:homepage]}" if package[:homepage]
|
|
44
|
+
output << " Repository: #{package[:repository_url]}" if package[:repository_url]
|
|
45
|
+
output << " Registry: #{package[:registry_url]}" if package[:registry_url]
|
|
46
|
+
output << " Documentation: #{package[:documentation_url]}" if package[:documentation_url]
|
|
47
|
+
|
|
48
|
+
# Package Info section
|
|
49
|
+
if package[:licenses] || package[:install_command] || package[:maintainers]
|
|
50
|
+
output << ""
|
|
51
|
+
output << "Package Info:"
|
|
52
|
+
output << " License: #{package[:licenses]}" if package[:licenses]
|
|
53
|
+
output << " Install: #{package[:install_command]}" if package[:install_command]
|
|
54
|
+
|
|
55
|
+
if package[:maintainers] && !package[:maintainers].empty?
|
|
56
|
+
output << " Maintainers:"
|
|
57
|
+
package[:maintainers].each do |maintainer|
|
|
58
|
+
if maintainer[:name] && maintainer[:login]
|
|
59
|
+
output << " #{maintainer[:name]} (#{maintainer[:login]})"
|
|
60
|
+
elsif maintainer[:login]
|
|
61
|
+
output << " #{maintainer[:login]}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Version-specific details
|
|
68
|
+
if version_info
|
|
69
|
+
output << ""
|
|
70
|
+
output << "Specific Version (#{purl.version}):"
|
|
71
|
+
output << " Published: #{version_info[:published_at]}" if version_info[:published_at]
|
|
72
|
+
output << " Published by: #{version_info[:published_by]}" if version_info[:published_by]
|
|
73
|
+
output << " Downloads: #{format_number(version_info[:downloads])}" if version_info[:downloads]
|
|
74
|
+
output << " Size: #{format_number(version_info[:size])} bytes" if version_info[:size]
|
|
75
|
+
output << " Yanked: #{version_info[:yanked] ? 'Yes' : 'No'}" if version_info.key?(:yanked)
|
|
76
|
+
|
|
77
|
+
if version_info[:registry_url] || version_info[:documentation_url] || version_info[:download_url]
|
|
78
|
+
output << " Version Links:"
|
|
79
|
+
output << " Registry: #{version_info[:registry_url]}" if version_info[:registry_url]
|
|
80
|
+
output << " Documentation: #{version_info[:documentation_url]}" if version_info[:documentation_url]
|
|
81
|
+
output << " Download: #{version_info[:download_url]}" if version_info[:download_url]
|
|
82
|
+
output << " API: #{version_info[:version_url]}" if version_info[:version_url]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
output.join("\n")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Format package lookup results for JSON output
|
|
90
|
+
#
|
|
91
|
+
# @param lookup_result [Hash] Result from Purl::Lookup#package_info
|
|
92
|
+
# @param purl [PackageURL] Original PURL object
|
|
93
|
+
# @return [Hash] JSON-ready hash structure
|
|
94
|
+
def format_json(lookup_result, purl)
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
purl: purl.to_s,
|
|
98
|
+
error: "Package not found in ecosyste.ms database"
|
|
99
|
+
} unless lookup_result
|
|
100
|
+
|
|
101
|
+
result = {
|
|
102
|
+
success: true,
|
|
103
|
+
purl: purl.to_s,
|
|
104
|
+
package: lookup_result[:package]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
result[:version] = lookup_result[:version] if lookup_result[:version]
|
|
108
|
+
|
|
109
|
+
result
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def format_number(num)
|
|
115
|
+
return num.to_s unless num.is_a?(Numeric)
|
|
116
|
+
num.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
data/lib/purl/package_url.rb
CHANGED
|
@@ -360,6 +360,36 @@ module Purl
|
|
|
360
360
|
self.class.new(**new_attrs)
|
|
361
361
|
end
|
|
362
362
|
|
|
363
|
+
# Create a new PackageURL without the version component
|
|
364
|
+
#
|
|
365
|
+
# @return [PackageURL] new PackageURL instance with version set to nil
|
|
366
|
+
#
|
|
367
|
+
# @example
|
|
368
|
+
# purl = PackageURL.parse("pkg:gem/rails@7.0.0")
|
|
369
|
+
# versionless = purl.versionless
|
|
370
|
+
# puts versionless.to_s # "pkg:gem/rails"
|
|
371
|
+
def versionless
|
|
372
|
+
with(version: nil)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Look up package information using the ecosyste.ms API
|
|
376
|
+
#
|
|
377
|
+
# @param user_agent [String] User agent string for API requests
|
|
378
|
+
# @param timeout [Integer] Request timeout in seconds
|
|
379
|
+
# @return [Hash, nil] Package information hash or nil if not found
|
|
380
|
+
# @raise [LookupError] if the lookup fails due to network or API errors
|
|
381
|
+
#
|
|
382
|
+
# @example
|
|
383
|
+
# purl = PackageURL.parse("pkg:cargo/rand@0.9.2")
|
|
384
|
+
# info = purl.lookup
|
|
385
|
+
# puts info[:package][:name] # => "rand"
|
|
386
|
+
# puts info[:version][:published_at] if info[:version] # => "2025-07-20T17:47:01.870Z"
|
|
387
|
+
def lookup(user_agent: nil, timeout: 10)
|
|
388
|
+
require_relative "lookup"
|
|
389
|
+
lookup_client = Lookup.new(user_agent: user_agent, timeout: timeout)
|
|
390
|
+
lookup_client.package_info(self)
|
|
391
|
+
end
|
|
392
|
+
|
|
363
393
|
private
|
|
364
394
|
|
|
365
395
|
def validate_and_normalize_type(type)
|
data/lib/purl/version.rb
CHANGED
data/lib/purl.rb
CHANGED
|
@@ -4,6 +4,8 @@ require_relative "purl/version"
|
|
|
4
4
|
require_relative "purl/errors"
|
|
5
5
|
require_relative "purl/package_url"
|
|
6
6
|
require_relative "purl/registry_url"
|
|
7
|
+
require_relative "purl/lookup"
|
|
8
|
+
require_relative "purl/lookup_formatter"
|
|
7
9
|
|
|
8
10
|
# The main PURL (Package URL) module providing functionality to parse,
|
|
9
11
|
# validate, and generate package URLs according to the PURL specification.
|
|
@@ -21,6 +23,12 @@ require_relative "purl/registry_url"
|
|
|
21
23
|
# purl = Purl.from_registry_url("https://rubygems.org/gems/rails")
|
|
22
24
|
# puts purl.to_s # "pkg:gem/rails"
|
|
23
25
|
#
|
|
26
|
+
# @example Package information lookup
|
|
27
|
+
# purl = Purl.parse("pkg:cargo/rand@0.9.2")
|
|
28
|
+
# info = purl.lookup
|
|
29
|
+
# puts info[:package][:description]
|
|
30
|
+
# puts info[:version][:published_at] if info[:version]
|
|
31
|
+
#
|
|
24
32
|
# @see https://github.com/package-url/purl-spec PURL Specification
|
|
25
33
|
module Purl
|
|
26
34
|
# Base error class for all PURL-related errors
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: purl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Nesbitt
|
|
@@ -44,6 +44,8 @@ files:
|
|
|
44
44
|
- exe/purl
|
|
45
45
|
- lib/purl.rb
|
|
46
46
|
- lib/purl/errors.rb
|
|
47
|
+
- lib/purl/lookup.rb
|
|
48
|
+
- lib/purl/lookup_formatter.rb
|
|
47
49
|
- lib/purl/package_url.rb
|
|
48
50
|
- lib/purl/registry_url.rb
|
|
49
51
|
- lib/purl/version.rb
|