ecosystems-bibliothecary 15.1.0 → 15.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78f959cefcb80f16e6e56d087449ec23576dc7177b5f4a2f0448654f9d7dd022
4
- data.tar.gz: fe94e344c4d43098281d613bfaf6168ec4464766fddedc61a97a9028f16f7174
3
+ metadata.gz: 49988f4a70c1d5dcf6b7c05bffba8d885b3e63f04545f0c524d32b46de0804f2
4
+ data.tar.gz: 121b4e75f934770b9967b0e3b2427ddc1dc6aaaa19f8ed0d72f1001e50d4d575
5
5
  SHA512:
6
- metadata.gz: 51e844390f10db2667ef885ad5a0ce300d33b8845808b4f2511869c03512176ce196f8de51c04d11fcfc429ec106849aa5e090800a3ae3aa151f989e8cfe0270
7
- data.tar.gz: 0f3a788b617189160bb45f9d73afc4e78ded47a010112a7369c28853a1dd842c3922e1dd7e22c2a62cf494ebaa6a550cf8e009c387e1c58fa27fe6f9bfedc724
6
+ metadata.gz: 227adbf47ce52d6a9bb70be154f0cc204f5f327b68adeb8c91fd2e668036359eebd07d3fb940f0d4eef00128af04fb2073304147b9285589917efef1b1d5d5cf
7
+ data.tar.gz: ca3b36ddfa71702dae9737500dcf09ad6a9e6c4daf5758738c793614bcdcbd730a50083d081ee45a899536b0f01c1bd7d4eef90117cc3c1aeee6063a780e0b3f
data/CHANGELOG.md CHANGED
@@ -13,6 +13,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
13
 
14
14
  ### Removed
15
15
 
16
+ ## [15.1.1]
17
+
18
+ ### Added
19
+
20
+ - CPAN: cpanfile parser for Perl dependency declarations
21
+ - CPAN: cpanfile.snapshot parser for Carton lockfiles
22
+ - CPAN: Makefile.PL parser for ExtUtils::MakeMaker build scripts
23
+ - CPAN: Build.PL parser for Module::Build scripts
24
+
25
+ ### Changed
26
+
27
+ - CPAN: META.json and META.yml are now classified as lockfiles (they are generated, not hand-written)
28
+
16
29
  ## [15.1.0]
17
30
 
18
31
  ### Added
data/README.md CHANGED
@@ -106,6 +106,10 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
106
106
  - CPAN
107
107
  - META.json
108
108
  - META.yml
109
+ - cpanfile
110
+ - cpanfile.snapshot
111
+ - Makefile.PL
112
+ - Build.PL
109
113
  - CocoaPods
110
114
  - Podfile
111
115
  - Podfile.lock
@@ -11,13 +11,29 @@ module Bibliothecary
11
11
  def self.mapping
12
12
  {
13
13
  match_filename("META.json", case_insensitive: true) => {
14
- kind: "manifest",
14
+ kind: "lockfile",
15
15
  parser: :parse_json_manifest,
16
16
  },
17
17
  match_filename("META.yml", case_insensitive: true) => {
18
- kind: "manifest",
18
+ kind: "lockfile",
19
19
  parser: :parse_yaml_manifest,
20
20
  },
21
+ match_filename("cpanfile", case_insensitive: true) => {
22
+ kind: "manifest",
23
+ parser: :parse_cpanfile,
24
+ },
25
+ match_filename("cpanfile.snapshot", case_insensitive: true) => {
26
+ kind: "lockfile",
27
+ parser: :parse_cpanfile_snapshot,
28
+ },
29
+ match_filename("Makefile.PL", case_insensitive: true) => {
30
+ kind: "manifest",
31
+ parser: :parse_makefile_pl,
32
+ },
33
+ match_filename("Build.PL", case_insensitive: true) => {
34
+ kind: "manifest",
35
+ parser: :parse_build_pl,
36
+ },
21
37
  }
22
38
  end
23
39
 
@@ -35,6 +51,174 @@ module Bibliothecary
35
51
  dependencies = map_dependencies(manifest, "requires", "runtime", options.fetch(:filename, nil))
36
52
  ParserResult.new(dependencies: dependencies)
37
53
  end
54
+
55
+ def self.parse_cpanfile(file_contents, options: {})
56
+ filename = options.fetch(:filename, nil)
57
+ dependencies = []
58
+ current_phase = "runtime"
59
+ current_feature = nil
60
+
61
+ file_contents.each_line do |line|
62
+ line = line.strip
63
+
64
+ # Track phase changes: on 'test' => sub {
65
+ if line =~ /\bon\s+['"](\w+)['"]\s*=>/
66
+ current_phase = $1
67
+ next
68
+ end
69
+
70
+ # Track feature blocks: feature 'name', 'desc' => sub {
71
+ if line =~ /\bfeature\s+['"]([\w-]+)['"]/
72
+ current_feature = $1
73
+ next
74
+ end
75
+
76
+ # End of block - reset to defaults
77
+ if line =~ /^\s*\};\s*$/
78
+ current_phase = "runtime"
79
+ current_feature = nil
80
+ next
81
+ end
82
+
83
+ # Parse dependency declarations
84
+ # requires 'Module::Name', 'version';
85
+ # requires 'Module::Name';
86
+ # recommends 'Module::Name', 'version';
87
+ if line =~ /\b(requires|recommends|suggests|conflicts)\s+['"]([^'"]+)['"](?:\s*,\s*['"]?([^'";]+)['"]?)?/
88
+ dep_type = $1
89
+ name = $2
90
+ version = $3&.strip || "*"
91
+
92
+ # Map cpanfile phases to our types
93
+ type = case current_phase
94
+ when "test" then "test"
95
+ when "develop" then "develop"
96
+ when "build" then "build"
97
+ when "configure" then "build"
98
+ else dep_type == "requires" ? "runtime" : dep_type
99
+ end
100
+
101
+ dependencies << Dependency.new(
102
+ name: name,
103
+ requirement: version,
104
+ type: type,
105
+ platform: "cpan",
106
+ source: filename
107
+ )
108
+ end
109
+ end
110
+
111
+ ParserResult.new(dependencies: dependencies)
112
+ end
113
+
114
+ def self.parse_cpanfile_snapshot(file_contents, options: {})
115
+ filename = options.fetch(:filename, nil)
116
+ dependencies = []
117
+
118
+ file_contents.each_line do |line|
119
+ # Match distribution header: Module-Name-1.23
120
+ if (match = line.match(/^ (\S+)-v?([\d._]+)$/))
121
+ dist_name = match[1].gsub("-", "::")
122
+ version = match[2]
123
+ dependencies << Dependency.new(
124
+ name: dist_name,
125
+ requirement: version,
126
+ type: "runtime",
127
+ platform: "cpan",
128
+ source: filename
129
+ )
130
+ end
131
+ end
132
+
133
+ ParserResult.new(dependencies: dependencies)
134
+ end
135
+
136
+ # Parse Makefile.PL (ExtUtils::MakeMaker format)
137
+ # Looks for PREREQ_PM, BUILD_REQUIRES, TEST_REQUIRES, CONFIGURE_REQUIRES
138
+ def self.parse_makefile_pl(file_contents, options: {})
139
+ filename = options.fetch(:filename, nil)
140
+ dependencies = []
141
+
142
+ # Map of hash key names to dependency types
143
+ type_mapping = {
144
+ "PREREQ_PM" => "runtime",
145
+ "BUILD_REQUIRES" => "build",
146
+ "TEST_REQUIRES" => "test",
147
+ "CONFIGURE_REQUIRES" => "build",
148
+ }
149
+
150
+ type_mapping.each do |key, type|
151
+ deps = extract_perl_hash(file_contents, key)
152
+ deps.each do |name, version|
153
+ dependencies << Dependency.new(
154
+ name: name,
155
+ requirement: version,
156
+ type: type,
157
+ platform: "cpan",
158
+ source: filename
159
+ )
160
+ end
161
+ end
162
+
163
+ ParserResult.new(dependencies: dependencies)
164
+ end
165
+
166
+ # Parse Build.PL (Module::Build format)
167
+ # Looks for requires, build_requires, test_requires, configure_requires
168
+ def self.parse_build_pl(file_contents, options: {})
169
+ filename = options.fetch(:filename, nil)
170
+ dependencies = []
171
+
172
+ # Map of hash key names to dependency types
173
+ type_mapping = {
174
+ "requires" => "runtime",
175
+ "build_requires" => "build",
176
+ "test_requires" => "test",
177
+ "configure_requires" => "build",
178
+ "recommends" => "runtime",
179
+ }
180
+
181
+ type_mapping.each do |key, type|
182
+ deps = extract_perl_hash(file_contents, key)
183
+ deps.each do |name, version|
184
+ dependencies << Dependency.new(
185
+ name: name,
186
+ requirement: version,
187
+ type: type,
188
+ platform: "cpan",
189
+ source: filename
190
+ )
191
+ end
192
+ end
193
+
194
+ ParserResult.new(dependencies: dependencies)
195
+ end
196
+
197
+ # Extract a Perl hash from source code
198
+ # Handles patterns like: KEY => { 'Module' => '1.0', ... }
199
+ def self.extract_perl_hash(content, key)
200
+ deps = {}
201
+
202
+ # Match the hash assignment: KEY => { ... }
203
+ # Use word boundary to avoid matching configure_requires when looking for requires
204
+ pattern = /(?:^|[^\w])#{Regexp.escape(key)}\s*=>\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/m
205
+
206
+ if (match = content.match(pattern))
207
+ hash_content = match[1]
208
+
209
+ # Extract 'Module::Name' => 'version' or 'Module::Name' => version patterns
210
+ hash_content.scan(/['"]([^'"]+)['"]\s*=>\s*['"]?([^'",}\s]+)['"]?/) do |name, version|
211
+ # Skip perl version requirements and non-module entries
212
+ next if name == "perl"
213
+
214
+ # Normalize version: 0 means any version
215
+ version = "*" if version == "0"
216
+ deps[name] = version
217
+ end
218
+ end
219
+
220
+ deps
221
+ end
38
222
  end
39
223
  end
40
224
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bibliothecary
4
- VERSION = "15.1.0"
4
+ VERSION = "15.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ecosystems-bibliothecary
3
3
  version: !ruby/object:Gem::Version
4
- version: 15.1.0
4
+ version: 15.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt