bibliothecary 7.3.6 → 8.0.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/.circleci/config.yml +1 -1
- data/README.md +7 -0
- data/bibliothecary.gemspec +1 -0
- data/lib/bibliothecary/analyser/analysis.rb +110 -0
- data/lib/bibliothecary/analyser/determinations.rb +27 -0
- data/lib/bibliothecary/analyser/matchers.rb +64 -0
- data/lib/bibliothecary/analyser.rb +32 -188
- data/lib/bibliothecary/cli.rb +3 -3
- data/lib/bibliothecary/file_info.rb +2 -0
- data/lib/bibliothecary/multi_parsers/bundler_like_manifest.rb +22 -0
- data/lib/bibliothecary/multi_parsers/cyclonedx.rb +138 -0
- data/lib/bibliothecary/multi_parsers/json_runtime.rb +16 -0
- data/lib/bibliothecary/parsers/bower.rb +2 -2
- data/lib/bibliothecary/parsers/cargo.rb +4 -2
- data/lib/bibliothecary/parsers/carthage.rb +6 -6
- data/lib/bibliothecary/parsers/clojars.rb +2 -2
- data/lib/bibliothecary/parsers/cocoapods.rb +5 -4
- data/lib/bibliothecary/parsers/conda.rb +11 -5
- data/lib/bibliothecary/parsers/cpan.rb +2 -2
- data/lib/bibliothecary/parsers/cran.rb +3 -1
- data/lib/bibliothecary/parsers/dub.rb +3 -2
- data/lib/bibliothecary/parsers/elm.rb +2 -1
- data/lib/bibliothecary/parsers/generic.rb +1 -1
- data/lib/bibliothecary/parsers/go.rb +13 -11
- data/lib/bibliothecary/parsers/hackage.rb +4 -2
- data/lib/bibliothecary/parsers/haxelib.rb +1 -0
- data/lib/bibliothecary/parsers/hex.rb +6 -4
- data/lib/bibliothecary/parsers/julia.rb +2 -2
- data/lib/bibliothecary/parsers/maven.rb +19 -11
- data/lib/bibliothecary/parsers/meteor.rb +1 -0
- data/lib/bibliothecary/parsers/npm.rb +7 -5
- data/lib/bibliothecary/parsers/nuget.rb +10 -7
- data/lib/bibliothecary/parsers/packagist.rb +4 -2
- data/lib/bibliothecary/parsers/pub.rb +2 -2
- data/lib/bibliothecary/parsers/pypi.rb +11 -9
- data/lib/bibliothecary/parsers/rubygems.rb +7 -4
- data/lib/bibliothecary/parsers/shard.rb +2 -2
- data/lib/bibliothecary/parsers/swift_pm.rb +4 -2
- data/lib/bibliothecary/runner.rb +8 -3
- data/lib/bibliothecary/version.rb +1 -1
- data/lib/bibliothecary.rb +3 -0
- metadata +22 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 50ba5ddf48a3b7d1051272fef2bcef8ca7bca465b13e251fe9656ae8751f1353
|
|
4
|
+
data.tar.gz: 40b88cb18308c0d624f00eedb67d1a5a946e42d2f3e242436fa376dd54b2c55a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 51675c6be455da5a996fe92f703245aac5f3a148eb65aed33bc48675e1a94677da65e3de1651d28cc511391d241e44bc774b287ff6ae2b27d7494037389f0391
|
|
7
|
+
data.tar.gz: 74ceaa45f07deaa972be3a50e5da4e91b2507be75f6a8cf7b46cd8c02319690063e4a6f52f394fafe918debba2af765efceb13e78195de8a827997a343b8128c
|
data/.circleci/config.yml
CHANGED
data/README.md
CHANGED
|
@@ -7,6 +7,8 @@ Dependency manifest parsing library for https://libraries.io
|
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
10
|
+
Requires Ruby 2.7 or above.
|
|
11
|
+
|
|
10
12
|
Add this line to your application's Gemfile:
|
|
11
13
|
|
|
12
14
|
```ruby
|
|
@@ -90,6 +92,11 @@ All available config options are in: https://github.com/librariesio/bibliothecar
|
|
|
90
92
|
- paket.lock
|
|
91
93
|
- *.csproj
|
|
92
94
|
- project.assets.json
|
|
95
|
+
- CycloneDX
|
|
96
|
+
- XML as cyclonedx.xml
|
|
97
|
+
- JSON as cyclonedx.json
|
|
98
|
+
- Note that CycloneDX manifests can contain information on multiple
|
|
99
|
+
package manager's packages!
|
|
93
100
|
- Bower
|
|
94
101
|
- bower.json
|
|
95
102
|
- CPAN
|
data/bibliothecary.gemspec
CHANGED
|
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.add_dependency "commander"
|
|
28
28
|
spec.add_dependency "strings-ansi"
|
|
29
29
|
spec.add_dependency "strings"
|
|
30
|
+
spec.add_dependency "packageurl-ruby"
|
|
30
31
|
|
|
31
32
|
spec.add_development_dependency "pry"
|
|
32
33
|
spec.add_development_dependency "rake", "~> 12.0"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module Bibliothecary
|
|
2
|
+
module Analyser
|
|
3
|
+
module Analysis
|
|
4
|
+
# Convenience method to create FileInfo objects from folder path and
|
|
5
|
+
# file list.
|
|
6
|
+
#
|
|
7
|
+
# @param folder_path [String]
|
|
8
|
+
# @param file_list [Array<String>]
|
|
9
|
+
# @param options [Hash]
|
|
10
|
+
def analyse(folder_path, file_list, options: {})
|
|
11
|
+
analyse_file_info(file_list.map { |full_path| FileInfo.new(folder_path, full_path) }, options: options)
|
|
12
|
+
end
|
|
13
|
+
alias analyze analyse
|
|
14
|
+
|
|
15
|
+
# Analyze a set of FileInfo objects and extract manifests from them all.
|
|
16
|
+
#
|
|
17
|
+
# @params file_info_list [Array<FileInfo>]
|
|
18
|
+
def analyse_file_info(file_info_list, options: {})
|
|
19
|
+
matching_info = file_info_list
|
|
20
|
+
.select(&method(:match_info?))
|
|
21
|
+
|
|
22
|
+
matching_info.flat_map do |info|
|
|
23
|
+
analyse_contents_from_info(info, options: options)
|
|
24
|
+
.merge(related_paths: related_paths(info, matching_info))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
alias analyze_file_info analyse_file_info
|
|
28
|
+
|
|
29
|
+
def analyse_contents(filename, contents, options: {})
|
|
30
|
+
analyse_contents_from_info(FileInfo.new(nil, filename, contents), options: options)
|
|
31
|
+
end
|
|
32
|
+
alias analyze_contents analyse_contents
|
|
33
|
+
|
|
34
|
+
# This is the place a parsing operation will eventually end up.
|
|
35
|
+
#
|
|
36
|
+
# @param info [FileInfo]
|
|
37
|
+
def analyse_contents_from_info(info, options: {})
|
|
38
|
+
# If your Parser needs to return multiple responses for one file, please override this method
|
|
39
|
+
# For example see conda.rb
|
|
40
|
+
kind = determine_kind_from_info(info)
|
|
41
|
+
dependencies = parse_file(info.relative_path, info.contents, options: options)
|
|
42
|
+
|
|
43
|
+
dependencies_to_analysis(info, kind, dependencies)
|
|
44
|
+
rescue Bibliothecary::FileParsingError => e
|
|
45
|
+
Bibliothecary::Analyser::create_error_analysis(platform_name, info.relative_path, kind, e.message)
|
|
46
|
+
end
|
|
47
|
+
alias analyze_contents_from_info analyse_contents_from_info
|
|
48
|
+
|
|
49
|
+
def dependencies_to_analysis(info, kind, dependencies)
|
|
50
|
+
dependencies = dependencies || [] # work around any legacy parsers that return nil
|
|
51
|
+
if generic?
|
|
52
|
+
grouped = dependencies.group_by { |dep| dep[:platform] }
|
|
53
|
+
all_analyses = grouped.keys.map do |platform|
|
|
54
|
+
deplatformed_dependencies = grouped[platform].map { |d| d.delete(:platform); d }
|
|
55
|
+
Bibliothecary::Analyser::create_analysis(platform, info.relative_path, kind, deplatformed_dependencies)
|
|
56
|
+
end
|
|
57
|
+
# this is to avoid a larger refactor for the time being. The larger refactor
|
|
58
|
+
# needs to make analyse_contents return multiple analysis, or add another
|
|
59
|
+
# method that can return multiple and deprecate analyse_contents, perhaps.
|
|
60
|
+
raise "File contains zero or multiple platforms, currently must have exactly one" if all_analyses.length != 1
|
|
61
|
+
all_analyses.first
|
|
62
|
+
else
|
|
63
|
+
Bibliothecary::Analyser::create_analysis(platform_name, info.relative_path, kind, dependencies)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Call the matching parse class method for this file with
|
|
68
|
+
# these contents
|
|
69
|
+
def parse_file(filename, contents, options: {})
|
|
70
|
+
details = first_matching_mapping_details(FileInfo.new(nil, filename, contents))
|
|
71
|
+
|
|
72
|
+
# this can be raised if we don't check match?/match_info?,
|
|
73
|
+
# OR don't have the file contents when we check them, so
|
|
74
|
+
# it turns out for example that a .xml file isn't a
|
|
75
|
+
# manifest after all.
|
|
76
|
+
raise Bibliothecary::FileParsingError.new("No parser for this file type", filename) unless details[:parser]
|
|
77
|
+
|
|
78
|
+
# The `parser` method should raise an exception if the file is malformed,
|
|
79
|
+
# should return empty [] if the file is fine but simply doesn't contain
|
|
80
|
+
# any dependencies, and should never return nil. At the time of writing
|
|
81
|
+
# this comment, some of the parsers return [] or nil to mean an error
|
|
82
|
+
# which is confusing to users.
|
|
83
|
+
send(details[:parser], contents, options: options.merge(filename: filename))
|
|
84
|
+
|
|
85
|
+
rescue Exception => e # default is StandardError but C bindings throw Exceptions
|
|
86
|
+
# the C xml parser also puts a newline at the end of the message
|
|
87
|
+
raise Bibliothecary::FileParsingError.new(e.message.strip, filename)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def related_paths(info, infos)
|
|
93
|
+
return [] unless determine_can_have_lockfile_from_info(info)
|
|
94
|
+
|
|
95
|
+
kind = determine_kind_from_info(info)
|
|
96
|
+
relate_to_kind = first_matching_mapping_details(info)
|
|
97
|
+
.fetch(:related_to, %w(manifest lockfile).reject { |k| k == kind })
|
|
98
|
+
dirname = File.dirname(info.relative_path)
|
|
99
|
+
|
|
100
|
+
infos
|
|
101
|
+
.reject { |i| i == info }
|
|
102
|
+
.select { |i| relate_to_kind.include?(determine_kind_from_info(i)) }
|
|
103
|
+
.select { |i| File.dirname(i.relative_path) == dirname }
|
|
104
|
+
.select(&method(:determine_can_have_lockfile_from_info))
|
|
105
|
+
.map(&:relative_path)
|
|
106
|
+
.sort
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Bibliothecary
|
|
2
|
+
module Analyser
|
|
3
|
+
module Determinations
|
|
4
|
+
# calling this with contents=nil can produce less-informed
|
|
5
|
+
# results, but kept for back compat
|
|
6
|
+
def determine_kind(filename, contents = nil)
|
|
7
|
+
determine_kind_from_info(FileInfo.new(nil, filename, contents))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def determine_kind_from_info(info)
|
|
11
|
+
first_matching_mapping_details(info)
|
|
12
|
+
.fetch(:kind, nil)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# calling this with contents=nil can produce less-informed
|
|
16
|
+
# results, but kept for back compat
|
|
17
|
+
def determine_can_have_lockfile(filename, contents = nil)
|
|
18
|
+
determine_can_have_lockfile_from_info(FileInfo.new(nil, filename, contents))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def determine_can_have_lockfile_from_info(info)
|
|
22
|
+
first_matching_mapping_details(info)
|
|
23
|
+
.fetch(:can_have_lockfile, true)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Bibliothecary
|
|
2
|
+
module Analyser
|
|
3
|
+
module Matchers
|
|
4
|
+
def match_filename(filename, case_insensitive: false)
|
|
5
|
+
if case_insensitive
|
|
6
|
+
lambda { |path| path.downcase == filename.downcase || path.downcase.end_with?("/" + filename.downcase) }
|
|
7
|
+
else
|
|
8
|
+
lambda { |path| path == filename || path.end_with?("/" + filename) }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def match_filenames(*filenames)
|
|
13
|
+
lambda do |path|
|
|
14
|
+
filenames.any? { |f| path == f } ||
|
|
15
|
+
filenames.any? { |f| path.end_with?("/" + f) }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def match_extension(filename, case_insensitive: false)
|
|
20
|
+
if case_insensitive
|
|
21
|
+
lambda { |path| path.downcase.end_with?(filename.downcase) }
|
|
22
|
+
else
|
|
23
|
+
lambda { |path| path.end_with?(filename) }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def mapping_entry_match?(matcher, details, info)
|
|
28
|
+
if matcher.call(info.relative_path)
|
|
29
|
+
# we only want to load contents if we don't have them already
|
|
30
|
+
# and there's a content_matcher method to use
|
|
31
|
+
return true if details[:content_matcher].nil?
|
|
32
|
+
# this is the libraries.io case where we won't load all .xml
|
|
33
|
+
# files (for example) just to look at their contents, we'll
|
|
34
|
+
# assume they are not manifests.
|
|
35
|
+
return false if info.contents.nil?
|
|
36
|
+
return send(details[:content_matcher], info.contents)
|
|
37
|
+
else
|
|
38
|
+
return false
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# this is broken with contents=nil because it can't look at file
|
|
43
|
+
# contents, so skips manifests that are ambiguously a
|
|
44
|
+
# manifest considering only the filename. However, those are
|
|
45
|
+
# the semantics that libraries.io uses since it doesn't have
|
|
46
|
+
# the files locally.
|
|
47
|
+
def match?(filename, contents = nil)
|
|
48
|
+
match_info?(FileInfo.new(nil, filename, contents))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def match_info?(info)
|
|
52
|
+
first_matching_mapping_details(info).any?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def first_matching_mapping_details(info)
|
|
58
|
+
mapping
|
|
59
|
+
.find { |matcher, details| mapping_entry_match?(matcher, details, info) }
|
|
60
|
+
&.last || {}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
require_relative './analyser/matchers.rb'
|
|
2
|
+
require_relative './analyser/determinations.rb'
|
|
3
|
+
require_relative './analyser/analysis.rb'
|
|
4
|
+
|
|
1
5
|
module Bibliothecary
|
|
2
6
|
module Analyser
|
|
3
7
|
def self.create_error_analysis(platform_name, relative_path, kind, message)
|
|
@@ -23,75 +27,34 @@ module Bibliothecary
|
|
|
23
27
|
|
|
24
28
|
def self.included(base)
|
|
25
29
|
base.extend(ClassMethods)
|
|
30
|
+
|
|
31
|
+
# Group like-methods into separate modules for easier comprehension.
|
|
32
|
+
base.extend(Bibliothecary::Analyser::Matchers)
|
|
33
|
+
base.extend(Bibliothecary::Analyser::Determinations)
|
|
34
|
+
base.extend(Bibliothecary::Analyser::Analysis)
|
|
26
35
|
end
|
|
27
|
-
module ClassMethods
|
|
28
|
-
def generic?
|
|
29
|
-
platform_name == "generic"
|
|
30
|
-
end
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# files (for example) just to look at their contents, we'll
|
|
39
|
-
# assume they are not manifests.
|
|
40
|
-
return false if info.contents.nil?
|
|
41
|
-
return send(details[:content_matcher], info.contents)
|
|
37
|
+
module TryCache
|
|
38
|
+
def try_cache(options, key)
|
|
39
|
+
if options[:cache]
|
|
40
|
+
options[:cache][key] ||= yield
|
|
41
|
+
|
|
42
|
+
options[:cache][key]
|
|
42
43
|
else
|
|
43
|
-
|
|
44
|
+
yield
|
|
44
45
|
end
|
|
45
46
|
end
|
|
47
|
+
end
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# this can be raised if we don't check match?/match_info?,
|
|
51
|
-
# OR don't have the file contents when we check them, so
|
|
52
|
-
# it turns out for example that a .xml file isn't a
|
|
53
|
-
# manifest after all.
|
|
54
|
-
raise Bibliothecary::FileParsingError.new("No parser for this file type", filename) unless details[:parser]
|
|
55
|
-
|
|
56
|
-
# The `parser` method should raise an exception if the file is malformed,
|
|
57
|
-
# should return empty [] if the file is fine but simply doesn't contain
|
|
58
|
-
# any dependencies, and should never return nil. At the time of writing
|
|
59
|
-
# this comment, some of the parsers return [] or nil to mean an error
|
|
60
|
-
# which is confusing to users.
|
|
61
|
-
send(details[:parser], contents)
|
|
62
|
-
|
|
63
|
-
rescue Exception => e # default is StandardError but C bindings throw Exceptions
|
|
64
|
-
# the C xml parser also puts a newline at the end of the message
|
|
65
|
-
raise Bibliothecary::FileParsingError.new(e.message.strip, filename)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# this is broken with contents=nil because it can't look at file
|
|
69
|
-
# contents, so skips manifests that are ambiguously a
|
|
70
|
-
# manifest considering only the filename. However, those are
|
|
71
|
-
# the semantics that libraries.io uses since it doesn't have
|
|
72
|
-
# the files locally.
|
|
73
|
-
def match?(filename, contents = nil)
|
|
74
|
-
match_info?(FileInfo.new(nil, filename, contents))
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def match_info?(info)
|
|
78
|
-
first_matching_mapping_details(info).any?
|
|
49
|
+
module ClassMethods
|
|
50
|
+
def generic?
|
|
51
|
+
platform_name == "generic"
|
|
79
52
|
end
|
|
80
53
|
|
|
81
54
|
def platform_name
|
|
82
55
|
self.name.to_s.split('::').last.downcase
|
|
83
56
|
end
|
|
84
57
|
|
|
85
|
-
def parse_json_runtime_manifest(file_contents)
|
|
86
|
-
JSON.parse(file_contents).fetch('dependencies',[]).map do |name, requirement|
|
|
87
|
-
{
|
|
88
|
-
name: name,
|
|
89
|
-
requirement: requirement,
|
|
90
|
-
type: 'runtime'
|
|
91
|
-
}
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
58
|
def map_dependencies(hash, key, type)
|
|
96
59
|
hash.fetch(key,[]).map do |name, requirement|
|
|
97
60
|
{
|
|
@@ -102,141 +65,22 @@ module Bibliothecary
|
|
|
102
65
|
end
|
|
103
66
|
end
|
|
104
67
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
matching_info.flat_map do |info|
|
|
115
|
-
analyse_contents_from_info(info)
|
|
116
|
-
.merge(related_paths: related_paths(info, matching_info))
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
alias analyze_file_info analyse_file_info
|
|
120
|
-
|
|
121
|
-
def analyse_contents(filename, contents)
|
|
122
|
-
analyse_contents_from_info(FileInfo.new(nil, filename, contents))
|
|
123
|
-
end
|
|
124
|
-
alias analyze_contents analyse_contents
|
|
125
|
-
|
|
126
|
-
def dependencies_to_analysis(info, kind, dependencies)
|
|
127
|
-
dependencies = dependencies || [] # work around any legacy parsers that return nil
|
|
128
|
-
if generic?
|
|
129
|
-
analyses = []
|
|
130
|
-
grouped = dependencies.group_by { |dep| dep[:platform] }
|
|
131
|
-
all_analyses = grouped.keys.map do |platform|
|
|
132
|
-
deplatformed_dependencies = grouped[platform].map { |d| d.delete(:platform); d }
|
|
133
|
-
Bibliothecary::Analyser::create_analysis(platform, info.relative_path, kind, deplatformed_dependencies)
|
|
134
|
-
end
|
|
135
|
-
# this is to avoid a larger refactor for the time being. The larger refactor
|
|
136
|
-
# needs to make analyse_contents return multiple analysis, or add another
|
|
137
|
-
# method that can return multiple and deprecate analyse_contents, perhaps.
|
|
138
|
-
raise "File contains zero or multiple platforms, currently must have exactly one" if all_analyses.length != 1
|
|
139
|
-
all_analyses.first
|
|
140
|
-
else
|
|
141
|
-
Bibliothecary::Analyser::create_analysis(platform_name, info.relative_path, kind, dependencies)
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def analyse_contents_from_info(info)
|
|
146
|
-
# If your Parser needs to return multiple responses for one file, please override this method
|
|
147
|
-
# For example see conda.rb
|
|
148
|
-
kind = determine_kind_from_info(info)
|
|
149
|
-
dependencies = parse_file(info.relative_path, info.contents)
|
|
150
|
-
|
|
151
|
-
dependencies_to_analysis(info, kind, dependencies)
|
|
152
|
-
rescue Bibliothecary::FileParsingError => e
|
|
153
|
-
Bibliothecary::Analyser::create_error_analysis(platform_name, info.relative_path, kind, e.message)
|
|
154
|
-
end
|
|
155
|
-
alias analyze_contents_from_info analyse_contents_from_info
|
|
68
|
+
# Add a MultiParser module to a Parser class. This extends the
|
|
69
|
+
# self.mapping method on the parser to include the multi parser's
|
|
70
|
+
# files to watch for, and it extends the Parser class with
|
|
71
|
+
# the multi parser for you.
|
|
72
|
+
#
|
|
73
|
+
# @param klass [Class] A Bibliothecary::MultiParsers class
|
|
74
|
+
def add_multi_parser(klass)
|
|
75
|
+
raise "No mapping found! You should place the add_multi_parser call below def self.mapping." unless respond_to?(:mapping)
|
|
156
76
|
|
|
157
|
-
|
|
158
|
-
# results, but kept for back compat
|
|
159
|
-
def determine_kind(filename, contents = nil)
|
|
160
|
-
determine_kind_from_info(FileInfo.new(nil, filename, contents))
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def determine_kind_from_info(info)
|
|
164
|
-
first_matching_mapping_details(info)
|
|
165
|
-
.fetch(:kind, nil)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# calling this with contents=nil can produce less-informed
|
|
169
|
-
# results, but kept for back compat
|
|
170
|
-
def determine_can_have_lockfile(filename, contents = nil)
|
|
171
|
-
determine_can_have_lockfile_from_info(FileInfo.new(nil, filename, contents))
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def determine_can_have_lockfile_from_info(info)
|
|
175
|
-
first_matching_mapping_details(info)
|
|
176
|
-
.fetch(:can_have_lockfile, true)
|
|
177
|
-
end
|
|
77
|
+
original_mapping = self.mapping
|
|
178
78
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
deps.push({
|
|
182
|
-
name: dep.name,
|
|
183
|
-
requirement: dep
|
|
184
|
-
.requirement
|
|
185
|
-
.requirements
|
|
186
|
-
.sort_by(&:last)
|
|
187
|
-
.map { |op, version| "#{op} #{version}" }
|
|
188
|
-
.join(", "),
|
|
189
|
-
type: dep.type
|
|
190
|
-
})
|
|
191
|
-
end.uniq
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def match_filename(filename, case_insensitive: false)
|
|
195
|
-
if case_insensitive
|
|
196
|
-
lambda { |path| path.downcase == filename.downcase || path.downcase.end_with?("/" + filename.downcase) }
|
|
197
|
-
else
|
|
198
|
-
lambda { |path| path == filename || path.end_with?("/" + filename) }
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def match_filenames(*filenames)
|
|
203
|
-
lambda do |path|
|
|
204
|
-
filenames.any? { |f| path == f } ||
|
|
205
|
-
filenames.any? { |f| path.end_with?("/" + f) }
|
|
79
|
+
define_singleton_method(:mapping) do
|
|
80
|
+
original_mapping.merge(klass.mapping)
|
|
206
81
|
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
def match_extension(filename, case_insensitive: false)
|
|
210
|
-
if case_insensitive
|
|
211
|
-
lambda { |path| path.downcase.end_with?(filename.downcase) }
|
|
212
|
-
else
|
|
213
|
-
lambda { |path| path.end_with?(filename) }
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
private
|
|
218
|
-
|
|
219
|
-
def related_paths(info, infos)
|
|
220
|
-
return [] unless determine_can_have_lockfile_from_info(info)
|
|
221
|
-
|
|
222
|
-
kind = determine_kind_from_info(info)
|
|
223
|
-
relate_to_kind = first_matching_mapping_details(info)
|
|
224
|
-
.fetch(:related_to, %w(manifest lockfile).reject { |k| k == kind })
|
|
225
|
-
dirname = File.dirname(info.relative_path)
|
|
226
|
-
|
|
227
|
-
infos
|
|
228
|
-
.reject { |i| i == info }
|
|
229
|
-
.select { |i| relate_to_kind.include?(determine_kind_from_info(i)) }
|
|
230
|
-
.select { |i| File.dirname(i.relative_path) == dirname }
|
|
231
|
-
.select(&method(:determine_can_have_lockfile_from_info))
|
|
232
|
-
.map(&:relative_path)
|
|
233
|
-
.sort
|
|
234
|
-
end
|
|
235
82
|
|
|
236
|
-
|
|
237
|
-
mapping
|
|
238
|
-
.find { |matcher, details| mapping_entry_match?(matcher, details, info) }
|
|
239
|
-
&.last || {}
|
|
83
|
+
send(:extend, klass)
|
|
240
84
|
end
|
|
241
85
|
end
|
|
242
86
|
end
|
data/lib/bibliothecary/cli.rb
CHANGED
|
@@ -18,9 +18,9 @@ module Bibliothecary
|
|
|
18
18
|
c.action do |_args, options|
|
|
19
19
|
options.default path: './'
|
|
20
20
|
output = Bibliothecary.analyse(options.path)
|
|
21
|
-
output.each do |
|
|
22
|
-
puts "#{
|
|
23
|
-
|
|
21
|
+
output.each do |file_contents|
|
|
22
|
+
puts "#{file_contents[:path]} (#{manifest[:platform]})"
|
|
23
|
+
file_contents[:dependencies].group_by{|d| d[:type] }.each do |type, deps|
|
|
24
24
|
puts " #{type}"
|
|
25
25
|
deps.each do |dep|
|
|
26
26
|
puts " #{dep[:name]} #{dep[:requirement]}"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Bibliothecary
|
|
2
|
+
module MultiParsers
|
|
3
|
+
module BundlerLikeManifest
|
|
4
|
+
# this takes parsed Bundler and Bundler-like (CocoaPods)
|
|
5
|
+
# manifests and turns them into a list of dependencies.
|
|
6
|
+
def parse_ruby_manifest(manifest)
|
|
7
|
+
manifest.dependencies.inject([]) do |deps, dep|
|
|
8
|
+
deps.push({
|
|
9
|
+
name: dep.name,
|
|
10
|
+
requirement: dep
|
|
11
|
+
.requirement
|
|
12
|
+
.requirements
|
|
13
|
+
.sort_by(&:last)
|
|
14
|
+
.map { |op, version| "#{op} #{version}" }
|
|
15
|
+
.join(", "),
|
|
16
|
+
type: dep.type
|
|
17
|
+
})
|
|
18
|
+
end.uniq
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|