purl 0.1.0 → 1.1.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 +43 -2
- data/CODE_OF_CONDUCT.md +1 -1
- data/CONTRIBUTING.md +167 -0
- data/LICENSE +21 -0
- data/README.md +409 -13
- data/Rakefile +538 -0
- data/SECURITY.md +164 -0
- data/lib/purl/errors.rb +64 -0
- data/lib/purl/package_url.rb +520 -0
- data/lib/purl/registry_url.rb +543 -0
- data/lib/purl/version.rb +1 -1
- data/lib/purl.rb +133 -1
- data/purl-types.json +583 -0
- data/schemas/purl-types.schema.json +154 -0
- data/schemas/test-suite-data.schema.json +134 -0
- data/test-suite-data.json +710 -0
- metadata +15 -2
data/README.md
CHANGED
|
@@ -1,39 +1,435 @@
|
|
|
1
|
-
# Purl
|
|
1
|
+
# Purl - Package URL Parser for Ruby
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A Ruby library for parsing, validating, and generating Package URLs (PURLs) as defined by the [PURL specification](https://github.com/package-url/purl-spec).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This library features comprehensive error handling with namespaced error types, bidirectional registry URL conversion, and JSON-based configuration for cross-language compatibility.
|
|
6
|
+
|
|
7
|
+
[](https://www.ruby-lang.org/)
|
|
8
|
+
[](https://rubygems.org/gems/purl)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
|
|
11
|
+
**[Available on RubyGems](https://rubygems.org/gems/purl)**
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Comprehensive PURL parsing and validation** with 37 package types (32 official + 5 additional ecosystems)
|
|
16
|
+
- **Better error handling** with namespaced error classes and contextual information
|
|
17
|
+
- **Bidirectional registry URL conversion** - generate registry URLs from PURLs and parse PURLs from registry URLs
|
|
18
|
+
- **Type-specific validation** for conan, cran, and swift packages
|
|
19
|
+
- **Registry URL generation** for 20 package ecosystems (npm, gem, maven, pypi, etc.)
|
|
20
|
+
- **Rails-style route patterns** for registry URL templates
|
|
21
|
+
- **100% compliance** with official PURL specification test suite (59/59 tests passing)
|
|
22
|
+
- **Cross-language compatibility** with JSON-based configuration
|
|
23
|
+
- **Comprehensive documentation** and examples
|
|
6
24
|
|
|
7
25
|
## Installation
|
|
8
26
|
|
|
9
|
-
|
|
27
|
+
Add this line to your application's Gemfile:
|
|
10
28
|
|
|
11
|
-
|
|
29
|
+
```ruby
|
|
30
|
+
gem 'purl'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
And then execute:
|
|
12
34
|
|
|
13
35
|
```bash
|
|
14
|
-
bundle
|
|
36
|
+
bundle install
|
|
15
37
|
```
|
|
16
38
|
|
|
17
|
-
|
|
39
|
+
Or install it yourself as:
|
|
18
40
|
|
|
19
41
|
```bash
|
|
20
|
-
gem install
|
|
42
|
+
gem install purl
|
|
21
43
|
```
|
|
22
44
|
|
|
23
45
|
## Usage
|
|
24
46
|
|
|
25
|
-
|
|
47
|
+
### Basic PURL Parsing
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
require 'purl'
|
|
51
|
+
|
|
52
|
+
# Parse a PURL string
|
|
53
|
+
purl = Purl.parse("pkg:gem/rails@7.0.0")
|
|
54
|
+
puts purl.type # => "gem"
|
|
55
|
+
puts purl.name # => "rails"
|
|
56
|
+
puts purl.version # => "7.0.0"
|
|
57
|
+
puts purl.namespace # => nil
|
|
58
|
+
|
|
59
|
+
# Parse with namespace and qualifiers
|
|
60
|
+
purl = Purl.parse("pkg:npm/@babel/core@7.0.0?arch=x86_64")
|
|
61
|
+
puts purl.type # => "npm"
|
|
62
|
+
puts purl.namespace # => "@babel"
|
|
63
|
+
puts purl.name # => "core"
|
|
64
|
+
puts purl.version # => "7.0.0"
|
|
65
|
+
puts purl.qualifiers # => {"arch" => "x86_64"}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Creating PURLs
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
# Create a PURL object
|
|
72
|
+
purl = Purl::PackageURL.new(
|
|
73
|
+
type: "maven",
|
|
74
|
+
namespace: "org.apache.commons",
|
|
75
|
+
name: "commons-lang3",
|
|
76
|
+
version: "3.12.0"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
puts purl.to_s # => "pkg:maven/org.apache.commons/commons-lang3@3.12.0"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Modifying PURL Objects
|
|
83
|
+
|
|
84
|
+
PURL objects are immutable by design, but you can create new objects with modified attributes using the `with` method:
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
# Create original PURL
|
|
88
|
+
original = Purl::PackageURL.new(
|
|
89
|
+
type: "npm",
|
|
90
|
+
namespace: "@babel",
|
|
91
|
+
name: "core",
|
|
92
|
+
version: "7.20.0",
|
|
93
|
+
qualifiers: { "arch" => "x64" }
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Create new PURL with updated version
|
|
97
|
+
updated = original.with(version: "7.21.0")
|
|
98
|
+
puts updated.to_s # => "pkg:npm/@babel/core@7.21.0?arch=x64"
|
|
99
|
+
|
|
100
|
+
# Update qualifiers
|
|
101
|
+
with_new_qualifiers = original.with(
|
|
102
|
+
qualifiers: { "arch" => "arm64", "os" => "linux" }
|
|
103
|
+
)
|
|
104
|
+
puts with_new_qualifiers.to_s # => "pkg:npm/@babel/core@7.20.0?arch=arm64&os=linux"
|
|
105
|
+
|
|
106
|
+
# Update multiple attributes at once
|
|
107
|
+
fully_updated = original.with(
|
|
108
|
+
version: "8.0.0",
|
|
109
|
+
qualifiers: { "dev" => "true" },
|
|
110
|
+
subpath: "lib/index.js"
|
|
111
|
+
)
|
|
112
|
+
puts fully_updated.to_s # => "pkg:npm/@babel/core@8.0.0#lib/index.js?dev=true"
|
|
113
|
+
|
|
114
|
+
# Original remains unchanged
|
|
115
|
+
puts original.to_s # => "pkg:npm/@babel/core@7.20.0?arch=x64"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Registry URL Generation
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
# Generate registry URLs from PURLs
|
|
122
|
+
purl = Purl.parse("pkg:gem/rails@7.0.0")
|
|
123
|
+
puts purl.registry_url # => "https://rubygems.org/gems/rails"
|
|
124
|
+
puts purl.registry_url_with_version # => "https://rubygems.org/gems/rails/versions/7.0.0"
|
|
125
|
+
|
|
126
|
+
# Check if registry URL generation is supported
|
|
127
|
+
puts purl.supports_registry_url? # => true
|
|
128
|
+
|
|
129
|
+
# NPM with scoped packages
|
|
130
|
+
purl = Purl.parse("pkg:npm/@babel/core@7.0.0")
|
|
131
|
+
puts purl.registry_url # => "https://www.npmjs.com/package/@babel/core"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Reverse Parsing: Registry URLs to PURLs
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
# Parse registry URLs back to PURLs
|
|
138
|
+
purl = Purl.from_registry_url("https://rubygems.org/gems/rails/versions/7.0.0")
|
|
139
|
+
puts purl.to_s # => "pkg:gem/rails@7.0.0"
|
|
140
|
+
|
|
141
|
+
# Works with various registries
|
|
142
|
+
purl = Purl.from_registry_url("https://www.npmjs.com/package/@babel/core")
|
|
143
|
+
puts purl.to_s # => "pkg:npm/@babel/core"
|
|
144
|
+
|
|
145
|
+
purl = Purl.from_registry_url("https://pypi.org/project/django/4.0.0/")
|
|
146
|
+
puts purl.to_s # => "pkg:pypi/django@4.0.0"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Custom Registry Domains
|
|
150
|
+
|
|
151
|
+
You can parse registry URLs from custom domains or generate URLs for private registries:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
# Parse from custom domain (specify type to help with parsing)
|
|
155
|
+
purl = Purl.from_registry_url("https://npm.company.com/package/@babel/core", type: "npm")
|
|
156
|
+
puts purl.to_s # => "pkg:npm/@babel/core"
|
|
157
|
+
|
|
158
|
+
# Generate URLs for custom registries
|
|
159
|
+
purl = Purl.parse("pkg:gem/rails@7.0.0")
|
|
160
|
+
custom_url = purl.registry_url(base_url: "https://gems.internal.com/gems")
|
|
161
|
+
puts custom_url # => "https://gems.internal.com/gems/rails"
|
|
162
|
+
|
|
163
|
+
# With version-specific URLs
|
|
164
|
+
with_version = purl.registry_url_with_version(base_url: "https://gems.internal.com/gems")
|
|
165
|
+
puts with_version # => "https://gems.internal.com/gems/rails/versions/7.0.0"
|
|
166
|
+
|
|
167
|
+
# Works with all supported package types
|
|
168
|
+
composer_purl = Purl.parse("pkg:composer/symfony/console@5.4.0")
|
|
169
|
+
private_composer = composer_purl.registry_url(base_url: "https://packagist.company.com/packages")
|
|
170
|
+
puts private_composer # => "https://packagist.company.com/packages/symfony/console"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Route Patterns
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
# Get route patterns for a package type (Rails-style)
|
|
177
|
+
patterns = Purl::RegistryURL.route_patterns_for("gem")
|
|
178
|
+
# => ["https://rubygems.org/gems/:name", "https://rubygems.org/gems/:name/versions/:version"]
|
|
179
|
+
|
|
180
|
+
# Get all route patterns
|
|
181
|
+
all_patterns = Purl::RegistryURL.all_route_patterns
|
|
182
|
+
puts all_patterns["npm"]
|
|
183
|
+
# => ["https://www.npmjs.com/package/:namespace/:name", "https://www.npmjs.com/package/:name", ...]
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Working with Qualifiers
|
|
187
|
+
|
|
188
|
+
Qualifiers are key-value pairs that provide additional metadata about packages:
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
# Create PURL with qualifiers
|
|
192
|
+
purl = Purl::PackageURL.new(
|
|
193
|
+
type: "apk",
|
|
194
|
+
name: "curl",
|
|
195
|
+
version: "7.83.0-r0",
|
|
196
|
+
qualifiers: {
|
|
197
|
+
"distro" => "alpine-3.16",
|
|
198
|
+
"arch" => "x86_64",
|
|
199
|
+
"repository_url" => "https://dl-cdn.alpinelinux.org"
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
puts purl.to_s # => "pkg:apk/curl@7.83.0-r0?arch=x86_64&distro=alpine-3.16&repository_url=https://dl-cdn.alpinelinux.org"
|
|
203
|
+
|
|
204
|
+
# Access qualifiers
|
|
205
|
+
puts purl.qualifiers["distro"] # => "alpine-3.16"
|
|
206
|
+
puts purl.qualifiers["arch"] # => "x86_64"
|
|
207
|
+
|
|
208
|
+
# Parse PURL with qualifiers
|
|
209
|
+
parsed = Purl.parse("pkg:rpm/httpd@2.4.53?distro=fedora-36&arch=x86_64")
|
|
210
|
+
puts parsed.qualifiers # => {"distro" => "fedora-36", "arch" => "x86_64"}
|
|
211
|
+
|
|
212
|
+
# Add qualifiers to existing PURL
|
|
213
|
+
with_qualifiers = purl.with(qualifiers: purl.qualifiers.merge("signed" => "true"))
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Package Type Information
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
# Get all known PURL types
|
|
220
|
+
puts Purl.known_types.length # => 37
|
|
221
|
+
puts Purl.known_types.include?("gem") # => true
|
|
222
|
+
|
|
223
|
+
# Check type support
|
|
224
|
+
puts Purl.known_type?("gem") # => true
|
|
225
|
+
puts Purl.registry_supported_types # => ["cargo", "gem", "maven", "npm", ...]
|
|
226
|
+
puts Purl.reverse_parsing_supported_types # => ["bioconductor", "cargo", "clojars", ...]
|
|
227
|
+
|
|
228
|
+
# Get default registry for a type
|
|
229
|
+
puts Purl.default_registry("gem") # => "https://rubygems.org"
|
|
230
|
+
puts Purl.default_registry("npm") # => "https://registry.npmjs.org"
|
|
231
|
+
puts Purl.default_registry("golang") # => nil (no default)
|
|
232
|
+
|
|
233
|
+
# Get official examples for a type
|
|
234
|
+
puts Purl.type_examples("gem") # => ["pkg:gem/rails@7.0.4", "pkg:gem/bundler@2.3.26", ...]
|
|
235
|
+
puts Purl.type_examples("npm") # => ["pkg:npm/lodash@4.17.21", "pkg:npm/@babel/core@7.20.0", ...]
|
|
236
|
+
puts Purl.type_examples("unknown") # => []
|
|
237
|
+
|
|
238
|
+
# Get detailed type information
|
|
239
|
+
info = Purl.type_info("gem")
|
|
240
|
+
puts info[:known] # => true
|
|
241
|
+
puts info[:description] # => "RubyGems"
|
|
242
|
+
puts info[:default_registry] # => "https://rubygems.org"
|
|
243
|
+
puts info[:examples] # => ["pkg:gem/rails@7.0.4", ...]
|
|
244
|
+
puts info[:registry_url_generation] # => true
|
|
245
|
+
puts info[:reverse_parsing] # => true
|
|
246
|
+
puts info[:route_patterns] # => ["https://rubygems.org/gems/:name", ...]
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Error Handling
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
# Detailed error types with context
|
|
253
|
+
begin
|
|
254
|
+
Purl.parse("invalid-purl")
|
|
255
|
+
rescue Purl::InvalidSchemeError => e
|
|
256
|
+
puts "Scheme error: #{e.message}"
|
|
257
|
+
rescue Purl::ParseError => e
|
|
258
|
+
puts "Parse error: #{e.message}"
|
|
259
|
+
puts "Component: #{e.component}"
|
|
260
|
+
puts "Value: #{e.value}"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Type-specific validation errors
|
|
264
|
+
begin
|
|
265
|
+
Purl::PackageURL.new(type: "swift", name: "Alamofire") # Swift requires namespace
|
|
266
|
+
rescue Purl::ValidationError => e
|
|
267
|
+
puts e.message # => "Swift PURLs require a namespace to be unambiguous"
|
|
268
|
+
end
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Supported Package Types
|
|
272
|
+
|
|
273
|
+
The library supports 37 package types (32 official + 5 additional ecosystems):
|
|
274
|
+
|
|
275
|
+
**Registry URL Generation (20 types):**
|
|
276
|
+
- `bioconductor` (R/Bioconductor) - bioconductor.org
|
|
277
|
+
- `cargo` (Rust) - crates.io
|
|
278
|
+
- `clojars` (Clojure) - clojars.org
|
|
279
|
+
- `cocoapods` (iOS) - cocoapods.org
|
|
280
|
+
- `composer` (PHP) - packagist.org
|
|
281
|
+
- `conda` (Python) - anaconda.org
|
|
282
|
+
- `cpan` (Perl) - metacpan.org
|
|
283
|
+
- `deno` (Deno) - deno.land/x
|
|
284
|
+
- `elm` (Elm) - package.elm-lang.org
|
|
285
|
+
- `gem` (Ruby) - rubygems.org
|
|
286
|
+
- `golang` (Go) - pkg.go.dev
|
|
287
|
+
- `hackage` (Haskell) - hackage.haskell.org
|
|
288
|
+
- `hex` (Elixir) - hex.pm
|
|
289
|
+
- `homebrew` (macOS) - formulae.brew.sh
|
|
290
|
+
- `maven` (Java) - mvnrepository.com
|
|
291
|
+
- `npm` (Node.js) - npmjs.com
|
|
292
|
+
- `nuget` (.NET) - nuget.org
|
|
293
|
+
- `pub` (Dart) - pub.dev
|
|
294
|
+
- `pypi` (Python) - pypi.org
|
|
295
|
+
- `swift` (Swift) - swiftpackageindex.com
|
|
296
|
+
|
|
297
|
+
**Reverse Parsing (20 types):**
|
|
298
|
+
- `bioconductor`, `cargo`, `clojars`, `cocoapods`, `composer`, `conda`, `cpan`, `deno`, `elm`, `gem`, `golang`, `hackage`, `hex`, `homebrew`, `maven`, `npm`, `nuget`, `pub`, `pypi`, `swift`
|
|
299
|
+
|
|
300
|
+
**All 37 Supported Types:**
|
|
301
|
+
`alpm`, `apk`, `bioconductor`, `bitbucket`, `bitnami`, `cargo`, `clojars`, `cocoapods`, `composer`, `conan`, `conda`, `cpan`, `cran`, `deb`, `deno`, `docker`, `elm`, `gem`, `generic`, `github`, `golang`, `hackage`, `hex`, `homebrew`, `huggingface`, `luarocks`, `maven`, `mlflow`, `npm`, `nuget`, `oci`, `pub`, `pypi`, `qpkg`, `rpm`, `swid`, `swift`
|
|
302
|
+
|
|
303
|
+
## Specification Compliance
|
|
304
|
+
|
|
305
|
+
- **100% compliance** with the official PURL specification test suite (59/59 tests passing)
|
|
306
|
+
- **All 32 official package types** plus 5 additional ecosystem types supported
|
|
307
|
+
- **Type-specific validation** for conan, cran, swift, cpan, and mlflow packages
|
|
308
|
+
- **Proper error handling** for invalid PURLs that should be rejected
|
|
309
|
+
|
|
310
|
+
## JSON Configuration
|
|
311
|
+
|
|
312
|
+
Package types and registry patterns are stored in `purl-types.json` for easy contribution and cross-language compatibility:
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
{
|
|
316
|
+
"version": "1.0.0",
|
|
317
|
+
"description": "PURL types and registry URL patterns for package ecosystems",
|
|
318
|
+
"source": "https://github.com/package-url/purl-spec/blob/main/PURL-TYPES.rst",
|
|
319
|
+
"last_updated": "2025-07-24",
|
|
320
|
+
"types": {
|
|
321
|
+
"gem": {
|
|
322
|
+
"description": "RubyGems",
|
|
323
|
+
"default_registry": "https://rubygems.org",
|
|
324
|
+
"registry_config": {
|
|
325
|
+
"path_template": "/gems/:name",
|
|
326
|
+
"version_path_template": "/gems/:name/versions/:version",
|
|
327
|
+
"reverse_regex": "/gems/([^/?#]+)(?:/versions/([^/?#]+))?",
|
|
328
|
+
"components": {
|
|
329
|
+
"namespace": false,
|
|
330
|
+
"version_in_url": true,
|
|
331
|
+
"version_path": "/versions/"
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**Key Configuration Improvements:**
|
|
340
|
+
- **Domain-agnostic patterns**: `path_template` without hardcoded domains enables custom registries
|
|
341
|
+
- **Flexible URL generation**: Combine `default_registry` + `path_template` for any domain
|
|
342
|
+
- **Cleaner JSON**: Reduced duplication and easier maintenance
|
|
343
|
+
- **Cross-registry compatibility**: Same URL structure works with public and private registries
|
|
344
|
+
|
|
345
|
+
## JSON Schema Validation
|
|
346
|
+
|
|
347
|
+
The library includes JSON schemas for validation and documentation:
|
|
348
|
+
|
|
349
|
+
- **`schemas/purl-types.schema.json`** - Schema for the PURL types configuration file
|
|
350
|
+
- **`schemas/test-suite-data.schema.json`** - Schema for the official test suite data
|
|
351
|
+
|
|
352
|
+
These schemas provide:
|
|
353
|
+
- **Structure validation** - Ensure JSON files conform to expected format
|
|
354
|
+
- **Documentation** - Self-documenting configuration with descriptions
|
|
355
|
+
- **IDE support** - Enable autocomplete and validation in editors
|
|
356
|
+
- **CI/CD integration** - Validate configuration in automated pipelines
|
|
357
|
+
|
|
358
|
+
Validate JSON files against their schemas:
|
|
359
|
+
```bash
|
|
360
|
+
rake spec:validate_schemas
|
|
361
|
+
```
|
|
26
362
|
|
|
27
363
|
## Development
|
|
28
364
|
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then
|
|
365
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then:
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
# Run tests
|
|
369
|
+
rake test
|
|
370
|
+
|
|
371
|
+
# Run specification compliance tests
|
|
372
|
+
rake spec:compliance
|
|
373
|
+
|
|
374
|
+
# Update test cases from official PURL spec
|
|
375
|
+
rake spec:update
|
|
376
|
+
|
|
377
|
+
# Show type information
|
|
378
|
+
rake spec:types
|
|
379
|
+
|
|
380
|
+
# Verify types against official specification
|
|
381
|
+
rake spec:verify_types
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Rake Tasks
|
|
30
385
|
|
|
31
|
-
|
|
386
|
+
- `rake spec:update` - Fetch latest test cases from official PURL spec repository
|
|
387
|
+
- `rake spec:compliance` - Run compliance tests against official test suite
|
|
388
|
+
- `rake spec:types` - Show information about all PURL types and their support
|
|
389
|
+
- `rake spec:verify_types` - Verify our types list against official specification
|
|
390
|
+
- `rake spec:validate_schemas` - Validate JSON files against their schemas
|
|
391
|
+
- `rake spec:debug` - Show detailed info about failing test cases
|
|
392
|
+
|
|
393
|
+
## Funding
|
|
394
|
+
|
|
395
|
+
If you find this project useful, please consider supporting its development:
|
|
396
|
+
|
|
397
|
+
- [GitHub Sponsors](https://github.com/sponsors/andrew)
|
|
398
|
+
- [Ko-fi](https://ko-fi.com/andrewnez)
|
|
399
|
+
- [Buy Me a Coffee](https://www.buymeacoffee.com/andrewnez)
|
|
400
|
+
|
|
401
|
+
Your support helps maintain and improve this library for the entire Ruby community.
|
|
32
402
|
|
|
33
403
|
## Contributing
|
|
34
404
|
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub
|
|
405
|
+
Bug reports and pull requests are welcome on GitHub. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
406
|
+
|
|
407
|
+
1. Fork it
|
|
408
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
409
|
+
3. Make your changes
|
|
410
|
+
4. Add tests for your changes
|
|
411
|
+
5. Ensure all tests pass (`rake test`)
|
|
412
|
+
6. Commit your changes (`git commit -am 'Add some feature'`)
|
|
413
|
+
7. Push to the branch (`git push origin my-new-feature`)
|
|
414
|
+
8. Create new Pull Request
|
|
415
|
+
|
|
416
|
+
### Adding New Package Types
|
|
417
|
+
|
|
418
|
+
To add support for a new package type:
|
|
419
|
+
|
|
420
|
+
1. Update `purl-types.json` with the new type configuration
|
|
421
|
+
2. Add registry URL patterns if applicable
|
|
422
|
+
3. Add type-specific validation rules if needed in `lib/purl/package_url.rb`
|
|
423
|
+
4. Add tests for the new functionality
|
|
424
|
+
|
|
425
|
+
## License
|
|
426
|
+
|
|
427
|
+
This gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
428
|
+
|
|
429
|
+
## Changelog
|
|
430
|
+
|
|
431
|
+
See [CHANGELOG.md](CHANGELOG.md) for a detailed history of changes and releases.
|
|
36
432
|
|
|
37
433
|
## Code of Conduct
|
|
38
434
|
|
|
39
|
-
Everyone interacting in the Purl project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [
|
|
435
|
+
Everyone interacting in the Purl project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md).
|