purl 1.3.1 → 1.4.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 +5 -0
- data/README.md +170 -1
- data/exe/purl +435 -0
- data/lib/purl/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4a3bf4cd374d69577598e05fa94658b7768c94b3522314a3382d1f9b872b203b
|
|
4
|
+
data.tar.gz: e1ddc0ec9b9ec7d675c9eeff8527af612610bcce7bc344313fe5c79a4975bd7d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6a4e84903907467f13205f4f9c31c29e67945f83c87d68f577008598d50da3f702d98b1c0e115a5c4a441a2babfdae6b6463da5e125f5f1630476d983a116849
|
|
7
|
+
data.tar.gz: b6e45ef48f2fb2d52eea945b658a24a3e3cc4236c7656ba21b8287bb5eb7b662d7adf9d46148771b48cfcb884f5f3fcce0bbbaffb54f3d7126a3d4f1f5f297a8
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.4.0] - 2025-01-06
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Command-line interface with parse, validate, convert, url, generate, and info commands plus JSON output support
|
|
14
|
+
|
|
10
15
|
## [1.3.1] - 2025-08-04
|
|
11
16
|
|
|
12
17
|
### Fixed
|
data/README.md
CHANGED
|
@@ -16,6 +16,7 @@ This library features comprehensive error handling with namespaced error types,
|
|
|
16
16
|
|
|
17
17
|
## Features
|
|
18
18
|
|
|
19
|
+
- **Command-line interface** with parse, validate, convert, generate, and info commands plus JSON output
|
|
19
20
|
- **Comprehensive PURL parsing and validation** with 37 package types (32 official + 5 additional ecosystems)
|
|
20
21
|
- **Better error handling** with namespaced error classes and contextual information
|
|
21
22
|
- **Bidirectional registry URL conversion** - generate registry URLs from PURLs and parse PURLs from registry URLs
|
|
@@ -46,7 +47,175 @@ Or install it yourself as:
|
|
|
46
47
|
gem install purl
|
|
47
48
|
```
|
|
48
49
|
|
|
49
|
-
##
|
|
50
|
+
## Command Line Interface
|
|
51
|
+
|
|
52
|
+
The purl gem includes a command-line interface that provides convenient access to all parsing, validation, conversion, and generation functionality.
|
|
53
|
+
|
|
54
|
+
### Installation
|
|
55
|
+
|
|
56
|
+
The CLI is automatically available after installing the gem:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
gem install purl
|
|
60
|
+
purl --help
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Available Commands
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
purl parse <purl-string> # Parse and display PURL components
|
|
67
|
+
purl validate <purl-string> # Validate a PURL (exit code indicates success)
|
|
68
|
+
purl convert <registry-url> # Convert registry URL to PURL
|
|
69
|
+
purl url <purl-string> # Convert PURL to registry URL
|
|
70
|
+
purl generate [options] # Generate PURL from components
|
|
71
|
+
purl info [type] # Show information about PURL types
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### JSON Output
|
|
75
|
+
|
|
76
|
+
All commands support JSON output with the `--json` flag:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
purl --json parse "pkg:gem/rails@7.0.0"
|
|
80
|
+
purl --json info gem
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Command Examples
|
|
84
|
+
|
|
85
|
+
#### Parse a PURL
|
|
86
|
+
```bash
|
|
87
|
+
$ purl parse "pkg:gem/rails@7.0.0"
|
|
88
|
+
Valid PURL: pkg:gem/rails@7.0.0
|
|
89
|
+
Components:
|
|
90
|
+
Type: gem
|
|
91
|
+
Namespace: (none)
|
|
92
|
+
Name: rails
|
|
93
|
+
Version: 7.0.0
|
|
94
|
+
Qualifiers: (none)
|
|
95
|
+
Subpath: (none)
|
|
96
|
+
|
|
97
|
+
$ purl --json parse "pkg:npm/@babel/core@7.0.0"
|
|
98
|
+
{
|
|
99
|
+
"success": true,
|
|
100
|
+
"purl": "pkg:npm/%40babel/core@7.0.0",
|
|
101
|
+
"components": {
|
|
102
|
+
"type": "npm",
|
|
103
|
+
"namespace": "@babel",
|
|
104
|
+
"name": "core",
|
|
105
|
+
"version": "7.0.0",
|
|
106
|
+
"qualifiers": {},
|
|
107
|
+
"subpath": null
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Validate a PURL
|
|
113
|
+
```bash
|
|
114
|
+
$ purl validate "pkg:gem/rails@7.0.0"
|
|
115
|
+
Valid PURL
|
|
116
|
+
|
|
117
|
+
$ purl validate "invalid-purl"
|
|
118
|
+
Invalid PURL: PURL must start with 'pkg:'
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### Convert Registry URL to PURL
|
|
122
|
+
```bash
|
|
123
|
+
$ purl convert "https://rubygems.org/gems/rails"
|
|
124
|
+
pkg:gem/rails
|
|
125
|
+
|
|
126
|
+
$ purl convert "https://www.npmjs.com/package/@babel/core"
|
|
127
|
+
pkg:npm/@babel/core
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### Convert PURL to Registry URL
|
|
131
|
+
```bash
|
|
132
|
+
$ purl url "pkg:gem/rails@7.0.0"
|
|
133
|
+
https://rubygems.org/gems/rails
|
|
134
|
+
|
|
135
|
+
$ purl url "pkg:npm/@babel/core@7.0.0"
|
|
136
|
+
https://www.npmjs.com/package/@babel/core
|
|
137
|
+
|
|
138
|
+
$ purl --json url "pkg:gem/rails@7.0.0"
|
|
139
|
+
{
|
|
140
|
+
"success": true,
|
|
141
|
+
"purl": "pkg:gem/rails@7.0.0",
|
|
142
|
+
"registry_url": "https://rubygems.org/gems/rails",
|
|
143
|
+
"type": "gem"
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Generate a PURL
|
|
148
|
+
```bash
|
|
149
|
+
$ purl generate --type gem --name rails --version 7.0.0
|
|
150
|
+
pkg:gem/rails@7.0.0
|
|
151
|
+
|
|
152
|
+
$ purl generate --type npm --namespace @babel --name core --version 7.0.0 --qualifiers "arch=x64,os=linux"
|
|
153
|
+
pkg:npm/%40babel/core@7.0.0?arch=x64&os=linux
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### Show Type Information
|
|
157
|
+
```bash
|
|
158
|
+
$ purl info gem
|
|
159
|
+
Type: gem
|
|
160
|
+
Known: Yes
|
|
161
|
+
Description: RubyGems
|
|
162
|
+
Default registry: https://rubygems.org
|
|
163
|
+
Registry URL generation: Yes
|
|
164
|
+
Reverse parsing: Yes
|
|
165
|
+
Examples:
|
|
166
|
+
pkg:gem/ruby-advisory-db-check@0.12.4
|
|
167
|
+
pkg:gem/rails@7.0.4
|
|
168
|
+
pkg:gem/bundler@2.3.26
|
|
169
|
+
Registry URL patterns:
|
|
170
|
+
https://rubygems.org/gems/:name
|
|
171
|
+
https://rubygems.org/gems/:name/versions/:version
|
|
172
|
+
|
|
173
|
+
$ purl info # Shows all types
|
|
174
|
+
Known PURL types:
|
|
175
|
+
|
|
176
|
+
alpm
|
|
177
|
+
Description: Arch Linux packages
|
|
178
|
+
Registry support: No
|
|
179
|
+
Reverse parsing: No
|
|
180
|
+
...
|
|
181
|
+
Total types: 37
|
|
182
|
+
Registry supported: 20
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Generate Options
|
|
186
|
+
|
|
187
|
+
The `generate` command supports all PURL components:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
purl generate --help
|
|
191
|
+
Usage: purl generate [options]
|
|
192
|
+
--type TYPE Package type (required)
|
|
193
|
+
--name NAME Package name (required)
|
|
194
|
+
--namespace NAMESPACE Package namespace
|
|
195
|
+
--version VERSION Package version
|
|
196
|
+
--qualifiers QUALIFIERS Qualifiers as key=value,key2=value2
|
|
197
|
+
--subpath SUBPATH Package subpath
|
|
198
|
+
-h, --help Show this help
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Exit Codes
|
|
202
|
+
|
|
203
|
+
The CLI uses standard exit codes:
|
|
204
|
+
- `0` - Success
|
|
205
|
+
- `1` - Error (invalid PURL, unsupported operation, etc.)
|
|
206
|
+
|
|
207
|
+
This makes the CLI suitable for use in scripts and CI/CD pipelines:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
if purl validate "pkg:gem/rails@7.0.0"; then
|
|
211
|
+
echo "Valid PURL"
|
|
212
|
+
else
|
|
213
|
+
echo "Invalid PURL"
|
|
214
|
+
exit 1
|
|
215
|
+
fi
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Library Usage
|
|
50
219
|
|
|
51
220
|
### Basic PURL Parsing
|
|
52
221
|
|
data/exe/purl
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "optparse"
|
|
5
|
+
require "json"
|
|
6
|
+
require_relative "../lib/purl"
|
|
7
|
+
|
|
8
|
+
class PurlCLI
|
|
9
|
+
def self.run(args = ARGV)
|
|
10
|
+
new.run(args)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@json_output = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run(args)
|
|
18
|
+
if args.empty?
|
|
19
|
+
puts usage
|
|
20
|
+
exit 1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Check for global --json flag
|
|
24
|
+
if args.include?("--json")
|
|
25
|
+
@json_output = true
|
|
26
|
+
args.delete("--json")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
command = args.shift
|
|
30
|
+
case command
|
|
31
|
+
when "parse"
|
|
32
|
+
parse_command(args)
|
|
33
|
+
when "validate"
|
|
34
|
+
validate_command(args)
|
|
35
|
+
when "convert"
|
|
36
|
+
convert_command(args)
|
|
37
|
+
when "generate"
|
|
38
|
+
generate_command(args)
|
|
39
|
+
when "url"
|
|
40
|
+
url_command(args)
|
|
41
|
+
when "info"
|
|
42
|
+
info_command(args)
|
|
43
|
+
when "--help", "-h", "help"
|
|
44
|
+
puts usage
|
|
45
|
+
exit 0
|
|
46
|
+
when "--version", "-v"
|
|
47
|
+
puts Purl::VERSION
|
|
48
|
+
exit 0
|
|
49
|
+
else
|
|
50
|
+
puts "Unknown command: #{command}"
|
|
51
|
+
puts usage
|
|
52
|
+
exit 1
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def usage
|
|
59
|
+
<<~USAGE
|
|
60
|
+
purl - Parse, validate, convert and generate Package URLs (PURLs)
|
|
61
|
+
|
|
62
|
+
Usage:
|
|
63
|
+
purl [--json] parse <purl-string> Parse and display PURL components
|
|
64
|
+
purl [--json] validate <purl-string> Validate a PURL (exit code indicates success)
|
|
65
|
+
purl [--json] convert <registry-url> Convert registry URL to PURL
|
|
66
|
+
purl [--json] url <purl-string> Convert PURL to registry URL
|
|
67
|
+
purl [--json] generate [options] Generate PURL from components
|
|
68
|
+
purl [--json] info [type] Show information about PURL types
|
|
69
|
+
purl --version Show version
|
|
70
|
+
purl --help Show this help
|
|
71
|
+
|
|
72
|
+
Global Options:
|
|
73
|
+
--json Output results in JSON format
|
|
74
|
+
|
|
75
|
+
Examples:
|
|
76
|
+
purl parse "pkg:gem/rails@7.0.0"
|
|
77
|
+
purl --json parse "pkg:gem/rails@7.0.0"
|
|
78
|
+
purl validate "pkg:npm/@babel/core@7.0.0"
|
|
79
|
+
purl convert "https://rubygems.org/gems/rails"
|
|
80
|
+
purl url "pkg:gem/rails@7.0.0"
|
|
81
|
+
purl generate --type gem --name rails --version 7.0.0
|
|
82
|
+
purl --json info gem
|
|
83
|
+
USAGE
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def parse_command(args)
|
|
87
|
+
if args.empty?
|
|
88
|
+
output_error("PURL string required")
|
|
89
|
+
exit 1
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
purl_string = args[0]
|
|
93
|
+
|
|
94
|
+
begin
|
|
95
|
+
purl = Purl.parse(purl_string)
|
|
96
|
+
|
|
97
|
+
if @json_output
|
|
98
|
+
result = {
|
|
99
|
+
success: true,
|
|
100
|
+
purl: purl.to_s,
|
|
101
|
+
components: {
|
|
102
|
+
type: purl.type,
|
|
103
|
+
namespace: purl.namespace,
|
|
104
|
+
name: purl.name,
|
|
105
|
+
version: purl.version,
|
|
106
|
+
qualifiers: purl.qualifiers || {},
|
|
107
|
+
subpath: purl.subpath
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
puts JSON.pretty_generate(result)
|
|
111
|
+
else
|
|
112
|
+
puts "Valid PURL: #{purl.to_s}"
|
|
113
|
+
puts "Components:"
|
|
114
|
+
puts " Type: #{purl.type}"
|
|
115
|
+
puts " Namespace: #{purl.namespace || '(none)'}"
|
|
116
|
+
puts " Name: #{purl.name}"
|
|
117
|
+
puts " Version: #{purl.version || '(none)'}"
|
|
118
|
+
|
|
119
|
+
if purl.qualifiers && !purl.qualifiers.empty?
|
|
120
|
+
puts " Qualifiers:"
|
|
121
|
+
purl.qualifiers.each do |key, value|
|
|
122
|
+
puts " #{key}: #{value}"
|
|
123
|
+
end
|
|
124
|
+
else
|
|
125
|
+
puts " Qualifiers: (none)"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
puts " Subpath: #{purl.subpath || '(none)'}"
|
|
129
|
+
end
|
|
130
|
+
rescue Purl::Error => e
|
|
131
|
+
output_error("Error parsing PURL: #{e.message}")
|
|
132
|
+
exit 1
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def validate_command(args)
|
|
137
|
+
if args.empty?
|
|
138
|
+
output_error("PURL string required")
|
|
139
|
+
exit 1
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
purl_string = args[0]
|
|
143
|
+
|
|
144
|
+
begin
|
|
145
|
+
purl = Purl.parse(purl_string)
|
|
146
|
+
if @json_output
|
|
147
|
+
result = {
|
|
148
|
+
success: true,
|
|
149
|
+
valid: true,
|
|
150
|
+
purl: purl.to_s,
|
|
151
|
+
message: "Valid PURL"
|
|
152
|
+
}
|
|
153
|
+
puts JSON.pretty_generate(result)
|
|
154
|
+
else
|
|
155
|
+
puts "Valid PURL"
|
|
156
|
+
end
|
|
157
|
+
exit 0
|
|
158
|
+
rescue Purl::Error => e
|
|
159
|
+
if @json_output
|
|
160
|
+
result = {
|
|
161
|
+
success: false,
|
|
162
|
+
valid: false,
|
|
163
|
+
purl: purl_string,
|
|
164
|
+
error: e.message
|
|
165
|
+
}
|
|
166
|
+
puts JSON.pretty_generate(result)
|
|
167
|
+
else
|
|
168
|
+
puts "Invalid PURL: #{e.message}"
|
|
169
|
+
end
|
|
170
|
+
exit 1
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def convert_command(args)
|
|
175
|
+
if args.empty?
|
|
176
|
+
output_error("Registry URL required")
|
|
177
|
+
exit 1
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
registry_url = args[0]
|
|
181
|
+
|
|
182
|
+
begin
|
|
183
|
+
purl = Purl.from_registry_url(registry_url)
|
|
184
|
+
if @json_output
|
|
185
|
+
result = {
|
|
186
|
+
success: true,
|
|
187
|
+
registry_url: registry_url,
|
|
188
|
+
purl: purl.to_s,
|
|
189
|
+
components: {
|
|
190
|
+
type: purl.type,
|
|
191
|
+
namespace: purl.namespace,
|
|
192
|
+
name: purl.name,
|
|
193
|
+
version: purl.version,
|
|
194
|
+
qualifiers: purl.qualifiers || {},
|
|
195
|
+
subpath: purl.subpath
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
puts JSON.pretty_generate(result)
|
|
199
|
+
else
|
|
200
|
+
puts purl.to_s
|
|
201
|
+
end
|
|
202
|
+
rescue Purl::Error => e
|
|
203
|
+
if @json_output
|
|
204
|
+
result = {
|
|
205
|
+
success: false,
|
|
206
|
+
registry_url: registry_url,
|
|
207
|
+
error: e.message
|
|
208
|
+
}
|
|
209
|
+
puts JSON.pretty_generate(result)
|
|
210
|
+
else
|
|
211
|
+
puts "Error converting URL: #{e.message}"
|
|
212
|
+
end
|
|
213
|
+
exit 1
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def url_command(args)
|
|
218
|
+
if args.empty?
|
|
219
|
+
output_error("PURL string required")
|
|
220
|
+
exit 1
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
purl_string = args[0]
|
|
224
|
+
|
|
225
|
+
begin
|
|
226
|
+
purl = Purl.parse(purl_string)
|
|
227
|
+
|
|
228
|
+
unless purl.supports_registry_url?
|
|
229
|
+
if @json_output
|
|
230
|
+
result = {
|
|
231
|
+
success: false,
|
|
232
|
+
purl: purl_string,
|
|
233
|
+
error: "Registry URL generation not supported for type '#{purl.type}'"
|
|
234
|
+
}
|
|
235
|
+
puts JSON.pretty_generate(result)
|
|
236
|
+
else
|
|
237
|
+
puts "Error: Registry URL generation not supported for type '#{purl.type}'"
|
|
238
|
+
end
|
|
239
|
+
exit 1
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
registry_url = purl.registry_url
|
|
243
|
+
|
|
244
|
+
if @json_output
|
|
245
|
+
result = {
|
|
246
|
+
success: true,
|
|
247
|
+
purl: purl.to_s,
|
|
248
|
+
registry_url: registry_url,
|
|
249
|
+
type: purl.type
|
|
250
|
+
}
|
|
251
|
+
puts JSON.pretty_generate(result)
|
|
252
|
+
else
|
|
253
|
+
puts registry_url
|
|
254
|
+
end
|
|
255
|
+
rescue Purl::Error => e
|
|
256
|
+
if @json_output
|
|
257
|
+
result = {
|
|
258
|
+
success: false,
|
|
259
|
+
purl: purl_string,
|
|
260
|
+
error: e.message
|
|
261
|
+
}
|
|
262
|
+
puts JSON.pretty_generate(result)
|
|
263
|
+
else
|
|
264
|
+
puts "Error: #{e.message}"
|
|
265
|
+
end
|
|
266
|
+
exit 1
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def generate_command(args)
|
|
271
|
+
options = {}
|
|
272
|
+
OptionParser.new do |opts|
|
|
273
|
+
opts.banner = "Usage: purl generate [options]"
|
|
274
|
+
|
|
275
|
+
opts.on("--type TYPE", "Package type (required)") do |v|
|
|
276
|
+
options[:type] = v
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
opts.on("--name NAME", "Package name (required)") do |v|
|
|
280
|
+
options[:name] = v
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
opts.on("--namespace NAMESPACE", "Package namespace") do |v|
|
|
284
|
+
options[:namespace] = v
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
opts.on("--version VERSION", "Package version") do |v|
|
|
288
|
+
options[:version] = v
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
opts.on("--qualifiers QUALIFIERS", "Qualifiers as key=value,key2=value2") do |v|
|
|
292
|
+
qualifiers = {}
|
|
293
|
+
v.split(",").each do |pair|
|
|
294
|
+
key, value = pair.split("=", 2)
|
|
295
|
+
if key && value
|
|
296
|
+
qualifiers[key.strip] = value.strip
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
options[:qualifiers] = qualifiers unless qualifiers.empty?
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
opts.on("--subpath SUBPATH", "Package subpath") do |v|
|
|
303
|
+
options[:subpath] = v
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
opts.on("-h", "--help", "Show this help") do
|
|
307
|
+
puts opts
|
|
308
|
+
exit 0
|
|
309
|
+
end
|
|
310
|
+
end.parse!(args)
|
|
311
|
+
|
|
312
|
+
unless options[:type] && options[:name]
|
|
313
|
+
output_error("--type and --name are required")
|
|
314
|
+
puts "Use 'purl generate --help' for more information" unless @json_output
|
|
315
|
+
exit 1
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
begin
|
|
319
|
+
purl = Purl::PackageURL.new(**options)
|
|
320
|
+
if @json_output
|
|
321
|
+
result = {
|
|
322
|
+
success: true,
|
|
323
|
+
purl: purl.to_s,
|
|
324
|
+
components: {
|
|
325
|
+
type: purl.type,
|
|
326
|
+
namespace: purl.namespace,
|
|
327
|
+
name: purl.name,
|
|
328
|
+
version: purl.version,
|
|
329
|
+
qualifiers: purl.qualifiers || {},
|
|
330
|
+
subpath: purl.subpath
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
puts JSON.pretty_generate(result)
|
|
334
|
+
else
|
|
335
|
+
puts purl.to_s
|
|
336
|
+
end
|
|
337
|
+
rescue Purl::Error => e
|
|
338
|
+
if @json_output
|
|
339
|
+
result = {
|
|
340
|
+
success: false,
|
|
341
|
+
error: e.message,
|
|
342
|
+
options: options
|
|
343
|
+
}
|
|
344
|
+
puts JSON.pretty_generate(result)
|
|
345
|
+
else
|
|
346
|
+
puts "Error generating PURL: #{e.message}"
|
|
347
|
+
end
|
|
348
|
+
exit 1
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def info_command(args)
|
|
353
|
+
if args.empty?
|
|
354
|
+
# Show all types
|
|
355
|
+
all_info = Purl.all_type_info
|
|
356
|
+
metadata = Purl.types_config_metadata
|
|
357
|
+
|
|
358
|
+
if @json_output
|
|
359
|
+
result = {
|
|
360
|
+
success: true,
|
|
361
|
+
types: all_info,
|
|
362
|
+
metadata: metadata
|
|
363
|
+
}
|
|
364
|
+
puts JSON.pretty_generate(result)
|
|
365
|
+
else
|
|
366
|
+
puts "Known PURL types:"
|
|
367
|
+
puts
|
|
368
|
+
|
|
369
|
+
all_info.each do |type, info|
|
|
370
|
+
puts " #{type}"
|
|
371
|
+
puts " Description: #{info[:description] || 'No description available'}"
|
|
372
|
+
puts " Registry support: #{info[:registry_url_generation] ? 'Yes' : 'No'}"
|
|
373
|
+
puts " Reverse parsing: #{info[:reverse_parsing] ? 'Yes' : 'No'}"
|
|
374
|
+
puts
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
puts "Total types: #{metadata[:total_types]}"
|
|
378
|
+
puts "Registry supported: #{metadata[:registry_supported_types]}"
|
|
379
|
+
end
|
|
380
|
+
else
|
|
381
|
+
# Show specific type info
|
|
382
|
+
type = args[0]
|
|
383
|
+
info = Purl.type_info(type)
|
|
384
|
+
|
|
385
|
+
if @json_output
|
|
386
|
+
result = {
|
|
387
|
+
success: true,
|
|
388
|
+
type: info
|
|
389
|
+
}
|
|
390
|
+
puts JSON.pretty_generate(result)
|
|
391
|
+
else
|
|
392
|
+
puts "Type: #{info[:type]}"
|
|
393
|
+
puts "Known: #{info[:known] ? 'Yes' : 'No'}"
|
|
394
|
+
puts "Description: #{info[:description] || 'No description available'}"
|
|
395
|
+
|
|
396
|
+
if info[:default_registry]
|
|
397
|
+
puts "Default registry: #{info[:default_registry]}"
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
puts "Registry URL generation: #{info[:registry_url_generation] ? 'Yes' : 'No'}"
|
|
401
|
+
puts "Reverse parsing: #{info[:reverse_parsing] ? 'Yes' : 'No'}"
|
|
402
|
+
|
|
403
|
+
if info[:examples] && !info[:examples].empty?
|
|
404
|
+
puts "Examples:"
|
|
405
|
+
info[:examples].each do |example|
|
|
406
|
+
puts " #{example}"
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
if info[:route_patterns] && !info[:route_patterns].empty?
|
|
411
|
+
puts "Registry URL patterns:"
|
|
412
|
+
info[:route_patterns].each do |pattern|
|
|
413
|
+
puts " #{pattern}"
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def output_error(message)
|
|
421
|
+
if @json_output
|
|
422
|
+
result = {
|
|
423
|
+
success: false,
|
|
424
|
+
error: message
|
|
425
|
+
}
|
|
426
|
+
puts JSON.pretty_generate(result)
|
|
427
|
+
else
|
|
428
|
+
puts "Error: #{message}"
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
if __FILE__ == $0
|
|
434
|
+
PurlCLI.run
|
|
435
|
+
end
|
data/lib/purl/version.rb
CHANGED
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.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Nesbitt
|
|
@@ -28,7 +28,8 @@ description: |-
|
|
|
28
28
|
It supports 37 package types (32 official + 5 additional ecosystems) and is fully compliant with the official PURL specification test suite.
|
|
29
29
|
email:
|
|
30
30
|
- andrewnez@gmail.com
|
|
31
|
-
executables:
|
|
31
|
+
executables:
|
|
32
|
+
- purl
|
|
32
33
|
extensions: []
|
|
33
34
|
extra_rdoc_files: []
|
|
34
35
|
files:
|
|
@@ -40,6 +41,7 @@ files:
|
|
|
40
41
|
- README.md
|
|
41
42
|
- Rakefile
|
|
42
43
|
- SECURITY.md
|
|
44
|
+
- exe/purl
|
|
43
45
|
- lib/purl.rb
|
|
44
46
|
- lib/purl/errors.rb
|
|
45
47
|
- lib/purl/package_url.rb
|