ecosystems-bibliothecary 14.3.0 → 15.0.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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/README.md +8 -23
  4. data/bibliothecary.gemspec +5 -9
  5. data/lib/bibliothecary/analyser.rb +0 -31
  6. data/lib/bibliothecary/cli.rb +35 -26
  7. data/lib/bibliothecary/configuration.rb +1 -6
  8. data/lib/bibliothecary/dependency.rb +1 -4
  9. data/lib/bibliothecary/parsers/bentoml.rb +0 -2
  10. data/lib/bibliothecary/parsers/bower.rb +0 -1
  11. data/lib/bibliothecary/parsers/cargo.rb +12 -10
  12. data/lib/bibliothecary/parsers/carthage.rb +51 -15
  13. data/lib/bibliothecary/parsers/clojars.rb +14 -18
  14. data/lib/bibliothecary/parsers/cocoapods.rb +100 -19
  15. data/lib/bibliothecary/parsers/cog.rb +0 -2
  16. data/lib/bibliothecary/parsers/conan.rb +156 -0
  17. data/lib/bibliothecary/parsers/conda.rb +0 -3
  18. data/lib/bibliothecary/parsers/cpan.rb +0 -2
  19. data/lib/bibliothecary/parsers/cran.rb +40 -19
  20. data/lib/bibliothecary/parsers/docker.rb +0 -2
  21. data/lib/bibliothecary/parsers/dub.rb +33 -8
  22. data/lib/bibliothecary/parsers/dvc.rb +0 -2
  23. data/lib/bibliothecary/parsers/elm.rb +13 -3
  24. data/lib/bibliothecary/parsers/go.rb +14 -5
  25. data/lib/bibliothecary/parsers/hackage.rb +132 -24
  26. data/lib/bibliothecary/parsers/haxelib.rb +14 -4
  27. data/lib/bibliothecary/parsers/hex.rb +37 -20
  28. data/lib/bibliothecary/parsers/homebrew.rb +0 -2
  29. data/lib/bibliothecary/parsers/julia.rb +0 -2
  30. data/lib/bibliothecary/parsers/maven.rb +35 -25
  31. data/lib/bibliothecary/parsers/meteor.rb +14 -4
  32. data/lib/bibliothecary/parsers/mlflow.rb +0 -2
  33. data/lib/bibliothecary/parsers/npm.rb +47 -59
  34. data/lib/bibliothecary/parsers/nuget.rb +22 -21
  35. data/lib/bibliothecary/parsers/ollama.rb +0 -2
  36. data/lib/bibliothecary/parsers/packagist.rb +0 -3
  37. data/lib/bibliothecary/parsers/pub.rb +0 -2
  38. data/lib/bibliothecary/parsers/pypi.rb +54 -35
  39. data/lib/bibliothecary/parsers/rubygems.rb +98 -27
  40. data/lib/bibliothecary/parsers/shard.rb +0 -1
  41. data/lib/bibliothecary/parsers/swift_pm.rb +77 -29
  42. data/lib/bibliothecary/parsers/vcpkg.rb +68 -17
  43. data/lib/bibliothecary/runner.rb +2 -15
  44. data/lib/bibliothecary/version.rb +1 -1
  45. data/lib/bibliothecary.rb +0 -4
  46. metadata +2 -110
  47. data/.codeclimate.yml +0 -25
  48. data/.github/CONTRIBUTING.md +0 -195
  49. data/.github/workflows/ci.yml +0 -25
  50. data/.gitignore +0 -10
  51. data/.rspec +0 -2
  52. data/.rubocop.yml +0 -69
  53. data/.ruby-version +0 -1
  54. data/.tidelift +0 -1
  55. data/CODE_OF_CONDUCT.md +0 -74
  56. data/Gemfile +0 -35
  57. data/Rakefile +0 -18
  58. data/bin/benchmark +0 -386
  59. data/bin/console +0 -15
  60. data/bin/setup +0 -8
  61. data/lib/bibliothecary/multi_parsers/bundler_like_manifest.rb +0 -26
  62. data/lib/bibliothecary/multi_parsers/cyclonedx.rb +0 -170
  63. data/lib/bibliothecary/multi_parsers/dependencies_csv.rb +0 -155
  64. data/lib/bibliothecary/multi_parsers/json_runtime.rb +0 -22
  65. data/lib/bibliothecary/multi_parsers/spdx.rb +0 -149
  66. data/lib/bibliothecary/purl_util.rb +0 -37
  67. data/lib/bibliothecary/runner/multi_manifest_filter.rb +0 -92
  68. data/lib/sdl_parser.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c529c2cc8f2f35098f5bbca1294b7a36249580d6ef44a735b00257422dfe568c
4
- data.tar.gz: 2680b1d61e665b2bef8ef5b987f3b58b29559266549e3cd68ccdaa8a65d2c4b0
3
+ metadata.gz: 508531974284b48df7e084f6d0c7c3a4762f7cf5f1af7ed624c86f469a413bfb
4
+ data.tar.gz: 2442b323ba7169decede7da3c68284402440613dd1091fec7ff1dee8c5bdbe56
5
5
  SHA512:
6
- metadata.gz: 7518c86a4817297d41c9bbab194901de59a0ca6f07dcad90a9b7d7a78584042d08f9765fc21080475767d399fc6e59f274f8e0132f6673d5e6f7dc2e7bc24908
7
- data.tar.gz: e916a1d6f2c4fba1f908c8abc0717013ca21d870f7ddea1c0257c579c3de619037e6cf01ab0eb6f57be2c8191482655267b0765020d19cf8d5da7597dbf56ba8
6
+ metadata.gz: bf3b5d65d9d02cbafa1e86f04652cdb7f00ffcefd04f55410850bd904e4e1f1e09bf95cd4893270cd93b5219af6fe14c1cc4e7954fe21bd1deb0979b450a37b4
7
+ data.tar.gz: 24d881e490fcd2dc28647a3936015d552ded40a0567a11f28fea160e277a3b70f736d9a640b3f3773d7aec5a6c8622d8a10349b2d0c820a0338d31cce9d0ba07
data/CHANGELOG.md CHANGED
@@ -13,6 +13,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
13
 
14
14
  ### Removed
15
15
 
16
+ ## [15.0.1]
17
+
18
+ ### Changed
19
+
20
+ - Fixed Gemfile parser incorrectly prepending "=" to version requirements that already have operators (e.g., `~> 7.0` was becoming `= ~> 7.0`)
21
+
22
+ ## [15.0.0]
23
+
24
+ ### Added
25
+
26
+ - Conan parser: conanfile.py, conanfile.txt, conan.lock
27
+ - vcpkg lockfile support: _generated-vcpkg-list.json
28
+ - vcpkg improvements: overrides support, dev dependency detection (host: true)
29
+
30
+ ### Changed
31
+
32
+ - NuGet packages.lock.json now returns dependencies from all target frameworks instead of arbitrarily picking one
33
+ - Optimized Maven text parsers: lazy ANSI stripping, skip newline normalization when not needed (10-20% faster)
34
+ - Optimized yarn.lock v1 parser with lazy newline normalization (16% faster)
35
+ - Optimized requirements.txt parser with each_line iteration and cached source lookup (10% faster)
36
+
37
+ ### Removed
38
+
39
+ - SPDX parser and support for *.spdx, *.spdx.json files
40
+ - CycloneDX parser and support for cyclonedx.xml, cyclonedx.json, *.cdx.xml, *.cdx.json files
41
+ - DependenciesCSV multi_parser and support for dependencies.csv files
42
+ - packageurl-ruby dependency
43
+ - Multi-parser infrastructure (add_multi_parser, MultiManifestFilter)
44
+
45
+ ## [14.4.0]
46
+
47
+ ### Changed
48
+
49
+ - Switched Cargo.lock, poetry.lock, uv.lock, Gopkg.lock, and pylock.toml parsers from full TOML parsing to regex-based parsing for 50-250x faster lockfile parsing on these formats.
50
+ - Switched Gemfile.lock parser from Bundler::LockfileParser to regex-based parsing for 6x faster parsing.
51
+ - Switched Podfile.lock parser from YAML to regex-based parsing for 5x faster parsing.
52
+ - Switched yarn.lock v2+ parser from YAML to regex-based parsing for 14x faster parsing.
53
+
16
54
  ## [14.3.0]
17
55
 
18
56
  ### Added
data/README.md CHANGED
@@ -4,8 +4,6 @@ Dependency manifest parsing library for https://github.com/ecosyste-ms
4
4
 
5
5
  This is a maintained fork of the original [Bibliothecary](https://github.com/librariesio/bibliothecary) gem, with support for additional manifest formats and bug fixes.
6
6
 
7
- [![license](https://img.shields.io/github/license/ecosyste-ms/bibliothecary.svg)](https://github.com/ecosyste-ms/bibliothecary/blob/master/LICENSE.txt)
8
-
9
7
  ## Installation
10
8
 
11
9
  Requires Ruby 3.4 or above.
@@ -18,7 +16,9 @@ gem "ecosystems-bibliothecary", git: "https://github.com/ecosyste-ms/bibliotheca
18
16
 
19
17
  And then execute:
20
18
 
21
- $ bundle install
19
+ ```shell
20
+ bundle install
21
+ ```
22
22
 
23
23
  ## Usage
24
24
 
@@ -40,14 +40,6 @@ Search a directory for manifest files and parse the contents:
40
40
  Bibliothecary.analyse('./')
41
41
  ```
42
42
 
43
- There are a number of parsers that rely on web services to parse the file formats, those urls can be configured like so:
44
-
45
- ```ruby
46
- Bibliothecary.configure do |config|
47
- config.carthage_parser_host = 'http://my-carthage-parsing-service.com'
48
- end
49
- ```
50
-
51
43
  All available config options are in: https://github.com/ecosyste-ms/bibliothecary/blob/master/lib/bibliothecary/configuration.rb
52
44
 
53
45
  ## Supported package manager file formats
@@ -103,18 +95,6 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
103
95
  - paket.lock
104
96
  - *.csproj
105
97
  - project.assets.json
106
- - CycloneDX
107
- - cyclonedx.xml
108
- - cyclonedx.json
109
- - *.cdx.xml
110
- - *.cdx.json
111
- - Note that CycloneDX manifests can contain information on multiple
112
- package manager's packages!
113
- - SPDX
114
- - tag:value as *.spdx
115
- - JSON as *.spdx.json
116
- - Note that SPDX manifests can contain information on multiple
117
- package manager's packages!
118
98
  - Bower
119
99
  - bower.json
120
100
  - BentoML
@@ -134,6 +114,10 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
134
114
  - project.clj
135
115
  - Cog
136
116
  - cog.yaml
117
+ - Conan
118
+ - conanfile.py
119
+ - conanfile.txt
120
+ - conan.lock
137
121
  - Meteor
138
122
  - versions.json
139
123
  - MLflow
@@ -198,6 +182,7 @@ All available config options are in: https://github.com/ecosyste-ms/bibliothecar
198
182
  - dvc.yaml
199
183
  - Vcpkg
200
184
  - vcpkg.json
185
+ - _generated-vcpkg-list.json
201
186
  - Homebrew
202
187
  - Brewfile
203
188
  - Brewfile.lock.json
@@ -16,23 +16,19 @@ Gem::Specification.new do |spec|
16
16
  spec.homepage = "https://github.com/ecosyste-ms/bibliothecary"
17
17
  spec.license = "AGPL-3.0"
18
18
 
19
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
20
+ f.match(%r{^(test|spec|features|\.github)/|^bin/(benchmark|console|setup)|^\.|^(Gemfile|Rakefile|CODE_OF_CONDUCT)})
21
+ end
20
22
  spec.bindir = "bin"
21
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
23
+ spec.executables = %w[bibliothecary]
22
24
  spec.require_paths = ["lib"]
23
25
 
24
26
  spec.add_dependency "bundler"
25
- spec.add_dependency "commander"
26
27
  spec.add_dependency "csv"
27
- spec.add_dependency "deb_control"
28
28
  spec.add_dependency "json", "~> 2.8"
29
- spec.add_dependency "librariesio-gem-parser"
30
29
  spec.add_dependency "ox", ">= 2.8.1"
31
- spec.add_dependency "packageurl-ruby"
32
- spec.add_dependency "racc"
33
- spec.add_dependency "sdl4r"
30
+ spec.add_dependency "racc" # required by tomlrb but not declared as a dependency
34
31
  spec.add_dependency "tomlrb", "~> 2.0"
35
- spec.add_dependency "typhoeus"
36
32
 
37
33
  spec.metadata["rubygems_mfa_required"] = "true"
38
34
  end
@@ -38,18 +38,6 @@ module Bibliothecary
38
38
  base.extend(Bibliothecary::Analyser::Analysis)
39
39
  end
40
40
 
41
- module TryCache
42
- def try_cache(options, key)
43
- if options[:cache]
44
- options[:cache][key] ||= yield
45
-
46
- options[:cache][key]
47
- else
48
- yield
49
- end
50
- end
51
- end
52
-
53
41
  module ClassMethods
54
42
  def platform_name
55
43
  @platform_name ||= name.to_s.split("::").last.downcase.freeze
@@ -66,25 +54,6 @@ module Bibliothecary
66
54
  )
67
55
  end
68
56
  end
69
-
70
- # Add a MultiParser module to a Parser class. This extends the
71
- # self.mapping method on the parser to include the multi parser's
72
- # files to watch for, and it extends the Parser class with
73
- # the multi parser for you.
74
- #
75
- # @param klass [Class] A Bibliothecary::MultiParsers class
76
- def add_multi_parser(klass)
77
- raise "No mapping found! You should place the add_multi_parser call below def self.mapping." unless respond_to?(:mapping)
78
-
79
- original_mapping = mapping
80
-
81
- singleton_class.remove_method(:mapping)
82
- define_singleton_method(:mapping) do
83
- original_mapping.merge(klass.mapping)
84
- end
85
-
86
- send(:extend, klass)
87
- end
88
57
  end
89
58
  end
90
59
  end
@@ -2,39 +2,48 @@
2
2
 
3
3
  require "bibliothecary/version"
4
4
  require "bibliothecary"
5
- require "commander"
5
+ require "optparse"
6
6
 
7
7
  module Bibliothecary
8
8
  class CLI
9
- include Commander::Methods
10
-
11
9
  def run
12
- program :name, "Bibliothecary"
13
- program :version, Bibliothecary::VERSION
14
- program :description, "Parse dependency information from a file or folder of code"
15
-
16
- command(:list) do |c|
17
- c.syntax = "bibliothecary list"
18
- c.description = "List dependencies"
19
- c.option("--path FILENAME", String, "Path to file/folder to analyse")
20
- c.action do |_args, options|
21
- options.default path: "./"
22
- output = Bibliothecary.analyse(options.path)
23
- output.each do |file_contents|
24
- puts "#{file_contents[:path]} (#{file_contents[:platform]})"
25
- file_contents[:dependencies].group_by { |d| d[:type] }.each do |type, deps|
26
- puts " #{type}"
27
- deps.each do |dep|
28
- puts " #{dep[:name]} #{dep[:requirement]}"
29
- end
30
- puts
31
- end
32
- puts
33
- end
10
+ options = { path: "./" }
11
+
12
+ parser = OptionParser.new do |opts|
13
+ opts.banner = "Usage: bibliothecary [options]"
14
+ opts.separator ""
15
+ opts.separator "Parse dependency information from a file or folder of code"
16
+ opts.separator ""
17
+
18
+ opts.on("-p", "--path PATH", "Path to file/folder to analyse (default: ./)") do |path|
19
+ options[:path] = path
20
+ end
21
+
22
+ opts.on("-v", "--version", "Show version") do
23
+ puts Bibliothecary::VERSION
24
+ exit
25
+ end
26
+
27
+ opts.on("-h", "--help", "Show this help") do
28
+ puts opts
29
+ exit
34
30
  end
35
31
  end
36
32
 
37
- run!
33
+ parser.parse!
34
+
35
+ output = Bibliothecary.analyse(options[:path])
36
+ output.each do |file_contents|
37
+ puts "#{file_contents[:path]} (#{file_contents[:platform]})"
38
+ file_contents[:dependencies].group_by { |d| d[:type] }.each do |type, deps|
39
+ puts " #{type}"
40
+ deps.each do |dep|
41
+ puts " #{dep[:name]} #{dep[:requirement]}"
42
+ end
43
+ puts
44
+ end
45
+ puts
46
+ end
38
47
  end
39
48
  end
40
49
  end
@@ -2,16 +2,11 @@
2
2
 
3
3
  module Bibliothecary
4
4
  class Configuration
5
- attr_accessor :ignored_dirs, :ignored_files, :carthage_parser_host, :clojars_parser_host, :mix_parser_host, :conda_parser_host, :swift_parser_host, :cabal_parser_host
5
+ attr_accessor :ignored_dirs, :ignored_files
6
6
 
7
7
  def initialize
8
8
  @ignored_dirs = [".git", "node_modules", "bower_components", "vendor", "dist"]
9
9
  @ignored_files = []
10
- @carthage_parser_host = "https://carthage.libraries.io"
11
- @clojars_parser_host = "https://clojars.libraries.io"
12
- @mix_parser_host = "https://mix.libraries.io"
13
- @swift_parser_host = "http://swift.libraries.io"
14
- @cabal_parser_host = "http://cabal.libraries.io"
15
10
  end
16
11
  end
17
12
  end
@@ -5,10 +5,7 @@ module Bibliothecary
5
5
  #
6
6
  # @attr_reader [String] name The name of the package, e.g. "ansi-string-colors"
7
7
  # @attr_reader [String] requirement The version requirement of the release, e.g. "1.0.0" or "^1.0.0"
8
- # @attr_reader [String] platform The platform of the package, e.g. "maven". This is optional because
9
- # it's implicit in most parser results, and the analyzer returns the platform name itself. One
10
- # exception are multi-parsers like DependenciesCSV, because they may return deps from multiple platforms.
11
- # Bibliothecary could start returning this field for *all* deps in future, and make it required. (default: nil)
8
+ # @attr_reader [String] platform The platform of the package, e.g. "maven".
12
9
  # @attr_reader [String] type The type or scope of dependency, e.g. "runtime" or "test". In some ecosystems a
13
10
  # default may be set and in other ecosystems it may make sense to return nil when not found.
14
11
  # @attr_reader [Boolean] direct Is this dependency a direct dependency (vs transitive dependency)? (default: nil)
@@ -15,8 +15,6 @@ module Bibliothecary
15
15
  }
16
16
  end
17
17
 
18
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
19
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
20
18
 
21
19
  def self.parse_bentofile(file_contents, options: {})
22
20
  source = options.fetch(:filename, 'bentofile.yaml')
@@ -16,7 +16,6 @@ module Bibliothecary
16
16
  }
17
17
  end
18
18
 
19
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
20
19
 
21
20
  def self.parse_manifest(file_contents, options: {})
22
21
  json = JSON.parse(file_contents)
@@ -18,9 +18,6 @@ module Bibliothecary
18
18
  }
19
19
  end
20
20
 
21
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
22
- add_multi_parser(Bibliothecary::MultiParsers::Spdx)
23
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
24
21
 
25
22
  def self.parse_manifest(file_contents, options: {})
26
23
  manifest = Tomlrb.parse(file_contents)
@@ -48,19 +45,24 @@ module Bibliothecary
48
45
  end
49
46
 
50
47
  def self.parse_lockfile(file_contents, options: {})
51
- manifest = Tomlrb.parse(file_contents)
52
- dependencies = manifest.fetch("package", []).map do |dependency|
53
- next if !dependency["source"] || !dependency["source"].start_with?("registry+")
48
+ dependencies = []
49
+ # Split into [[package]] blocks and extract fields from each
50
+ file_contents.split(/\[\[package\]\]/).drop(1).each do |block|
51
+ name = block[/name\s*=\s*"([^"]+)"/, 1]
52
+ version = block[/version\s*=\s*"([^"]+)"/, 1]
53
+ source = block[/source\s*=\s*"([^"]+)"/, 1]
54
+
55
+ # Skip packages without a registry source (local/workspace packages)
56
+ next unless source&.start_with?("registry+")
54
57
 
55
- Dependency.new(
56
- name: dependency["name"],
57
- requirement: dependency["version"],
58
+ dependencies << Dependency.new(
59
+ name: name,
60
+ requirement: version,
58
61
  type: "runtime",
59
62
  source: options.fetch(:filename, nil),
60
63
  platform: platform_name
61
64
  )
62
65
  end
63
- .compact
64
66
  ParserResult.new(dependencies: dependencies)
65
67
  end
66
68
  end
@@ -3,6 +3,18 @@ module Bibliothecary
3
3
  class Carthage
4
4
  include Bibliothecary::Analyser
5
5
 
6
+ # Matches Cartfile entries:
7
+ # github "owner/repo" >= 1.0
8
+ # github "owner/repo" "branch"
9
+ # github "owner/repo"
10
+ # git "url" "ref"
11
+ # binary "url" >= 1.0
12
+ # Group 1: source type (github, git, binary)
13
+ # Group 2: identifier (owner/repo or URL)
14
+ # Group 3: quoted version/branch
15
+ # Group 4: unquoted requirement (e.g., >= 1.0, ~> 2.0)
16
+ CARTFILE_REGEXP = /^(github|git|binary)\s+"([^"]+)"(?:\s+(?:"([^"]+)"|((?:>=|<=|~>|==|>|<)\s*[\d.]+)))?/
17
+
6
18
  def self.mapping
7
19
  {
8
20
  match_filename("Cartfile") => {
@@ -20,36 +32,60 @@ module Bibliothecary
20
32
  }
21
33
  end
22
34
 
23
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
24
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
25
35
 
26
36
  def self.parse_cartfile(file_contents, options: {})
27
- map_dependencies(file_contents, "cartfile", options.fetch(:filename, "Cartfile"))
37
+ parse_cartfile_contents(file_contents, options.fetch(:filename, "Cartfile"), "runtime")
28
38
  end
29
39
 
30
40
  def self.parse_cartfile_private(file_contents, options: {})
31
- map_dependencies(file_contents, "cartfile.private", options.fetch(:filename, "Cartfile.private"))
41
+ parse_cartfile_contents(file_contents, options.fetch(:filename, "Cartfile.private"), "development")
32
42
  end
33
43
 
34
44
  def self.parse_cartfile_resolved(file_contents, options: {})
35
- map_dependencies(file_contents, "cartfile.resolved", options.fetch(:filename, "Cartfile.resolved"))
45
+ parse_cartfile_contents(file_contents, options.fetch(:filename, "Cartfile.resolved"), "runtime")
36
46
  end
37
47
 
38
- def self.map_dependencies(manifest, path, source)
39
- response = Typhoeus.post("#{Bibliothecary.configuration.carthage_parser_host}/#{path}", params: {body: manifest}, timeout: 60)
40
- raise Bibliothecary::RemoteParsingError.new("Http Error #{response.response_code} when contacting: #{Bibliothecary.configuration.carthage_parser_host}/#{path}", response.response_code) unless response.success?
41
- json = JSON.parse(response.body)
48
+ def self.parse_cartfile_contents(contents, source, type)
49
+ deps = []
50
+
51
+ contents.each_line do |line|
52
+ # Remove inline comments
53
+ line = line.sub(/#.*$/, "").strip
54
+ next if line.empty?
55
+
56
+ match = line.match(CARTFILE_REGEXP)
57
+ next unless match
42
58
 
43
- deps = json.map do |dependency|
44
- Bibliothecary::Dependency.new(
59
+ source_type = match[1] # github, git, or binary
60
+ identifier = match[2] # owner/repo or URL
61
+ # match[3] is quoted version/branch, match[4] is unquoted requirement
62
+ version = match[3] || match[4] || "*"
63
+
64
+ # For github sources, use identifier as-is (could be owner/repo or full URL)
65
+ # For git/binary sources, extract repo name from URL
66
+ name = case source_type
67
+ when "github"
68
+ # Could be "owner/repo" or a full URL like "https://enterprise.local/..."
69
+ if identifier.include?("://")
70
+ identifier.split("/").last&.sub(/\.git$/, "") || identifier
71
+ else
72
+ identifier
73
+ end
74
+ else
75
+ # Extract name from URL (last path component without .git)
76
+ identifier.split("/").last&.sub(/\.git$/, "") || identifier
77
+ end
78
+
79
+ deps << Dependency.new(
45
80
  platform: platform_name,
46
- name: dependency["name"],
47
- requirement: dependency["version"],
48
- type: dependency["type"],
81
+ name: name,
82
+ requirement: version,
83
+ type: type,
49
84
  source: source
50
85
  )
51
86
  end
52
- Bibliothecary::ParserResult.new(dependencies: deps)
87
+
88
+ ParserResult.new(dependencies: deps)
53
89
  end
54
90
  end
55
91
  end
@@ -1,11 +1,12 @@
1
- require "json"
2
- require "typhoeus"
3
-
4
1
  module Bibliothecary
5
2
  module Parsers
6
3
  class Clojars
7
4
  include Bibliothecary::Analyser
8
5
 
6
+ # Matches individual dependency: [name "version"]
7
+ # Name can be like: org.clojure/clojure, cheshire, ring/ring-defaults
8
+ DEPENDENCY_REGEXP = %r{\[([a-zA-Z0-9_./\-]+)\s+"([^"]+)"\]}
9
+
9
10
  def self.mapping
10
11
  {
11
12
  match_filename("project.clj") => {
@@ -15,31 +16,26 @@ module Bibliothecary
15
16
  }
16
17
  end
17
18
 
18
- add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
19
- add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
20
19
 
21
20
  def self.parse_manifest(file_contents, options: {})
22
21
  source = options.fetch(:filename, "project.clj")
23
- response = Typhoeus.post("#{Bibliothecary.configuration.clojars_parser_host}/project.clj", body: file_contents, timeout: 60)
24
- raise Bibliothecary::RemoteParsingError.new("Http Error #{response.response_code} when contacting: #{Bibliothecary.configuration.clojars_parser_host}/project.clj", response.response_code) unless response.success?
25
- json = JSON.parse response.body
26
- index = json.index("dependencies")
22
+ deps = []
27
23
 
28
- deps = if index
29
- dependencies = json[index + 1]
30
- dependencies.map do |dependency|
31
- Bibliothecary::Dependency.new(
24
+ # Find the :dependencies section and extract deps
25
+ # Look for :dependencies followed by a vector of vectors
26
+ if (deps_section = file_contents[/:dependencies\s*\[.*?\]\]/m])
27
+ deps_section.scan(DEPENDENCY_REGEXP) do |name, version|
28
+ deps << Dependency.new(
32
29
  platform: platform_name,
33
- name: dependency[0],
34
- requirement: dependency[1],
30
+ name: name,
31
+ requirement: version,
35
32
  type: "runtime",
36
33
  source: source
37
34
  )
38
35
  end
39
- else
40
- []
41
36
  end
42
- Bibliothecary::ParserResult.new(dependencies: deps)
37
+
38
+ ParserResult.new(dependencies: deps)
43
39
  end
44
40
  end
45
41
  end