Class: Bibliothecary::Parsers::Maven
- Inherits:
-
Object
- Object
- Bibliothecary::Parsers::Maven
- Includes:
- Analyser
- Defined in:
- lib/bibliothecary/parsers/maven.rb
Constant Summary collapse
- GRADLE_TYPE_REGEX =
e.g. “annotationProcessor - Annotation processors and their dependencies for source set ‘main’.”
/^(\w+)/
- GRADLE_DEP_REGEX =
“| \— com.google.guava:guava:23.5-jre (*)”
/(\+---|\\---){1}/
- MAVEN_PROPERTY_REGEX =
/\$\{(.+?)\}/
- MAX_DEPTH =
5
Class Method Summary collapse
- .extract_pom_dep_info(xml, dependency, name, parent_properties = {}) ⇒ Object
- .extract_pom_info(xml, location, parent_properties = {}) ⇒ Object
- .extract_property(xml, property_name, value, parent_properties = {}, depth = 0) ⇒ Object
- .ivy_report?(file_contents) ⇒ Boolean
- .mapping ⇒ Object
- .parse_gradle(manifest) ⇒ Object
- .parse_gradle_resolved(file_contents) ⇒ Object
- .parse_ivy_manifest(file_contents) ⇒ Object
- .parse_ivy_report(file_contents) ⇒ Object
- .parse_maven_resolved(file_contents) ⇒ Object
- .parse_pom_manifest(file_contents, parent_properties = {}) ⇒ Object
- .parse_resolved_dep_line(line) ⇒ Object
- .property_value(xml, property_name, parent_properties) ⇒ Object
- .replace_value_with_prop(original_value, property_value, property_name) ⇒ Object
Methods included from Analyser
create_analysis, create_error_analysis, included
Class Method Details
.extract_pom_dep_info(xml, dependency, name, parent_properties = {}) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/bibliothecary/parsers/maven.rb', line 175 def self.extract_pom_dep_info(xml, dependency, name, parent_properties = {}) field = dependency.locate(name).first return nil if field.nil? value = field.nodes.first match = value.match(MAVEN_PROPERTY_REGEX) if match return extract_property(xml, match[1], value, parent_properties) else return value end end |
.extract_pom_info(xml, location, parent_properties = {}) ⇒ Object
171 172 173 |
# File 'lib/bibliothecary/parsers/maven.rb', line 171 def self.extract_pom_info(xml, location, parent_properties = {}) extract_pom_dep_info(xml, xml, location, parent_properties) end |
.extract_property(xml, property_name, value, parent_properties = {}, depth = 0) ⇒ Object
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/bibliothecary/parsers/maven.rb', line 192 def self.extract_property(xml, property_name, value, parent_properties = {}, depth = 0) prop_value = property_value(xml, property_name, parent_properties) return value unless prop_value # don't resolve more than 5 levels deep to avoid potential circular references resolved_value = replace_value_with_prop(value, prop_value, property_name) # check to see if we just resolved to another property name match = resolved_value.match(MAVEN_PROPERTY_REGEX) if match && depth < MAX_DEPTH depth += 1 return extract_property(xml, match[1], resolved_value, parent_properties, depth) else return resolved_value end end |
.ivy_report?(file_contents) ⇒ Boolean
60 61 62 63 64 65 66 67 68 69 |
# File 'lib/bibliothecary/parsers/maven.rb', line 60 def self.ivy_report?(file_contents) doc = Ox.parse file_contents root = doc&.locate("ivy-report")&.first return !root.nil? rescue Exception # rubocop:disable Lint/RescueException # We rescue exception here since native libs can throw a non-StandardError # We don't want to throw errors during the matching phase, only during # parsing after we match. false end |
.mapping ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/bibliothecary/parsers/maven.rb', line 18 def self.mapping { match_filename("ivy.xml", case_insensitive: true) => { kind: 'manifest', parser: :parse_ivy_manifest }, match_filename("pom.xml", case_insensitive: true) => { kind: 'manifest', parser: :parse_pom_manifest }, match_filename("build.gradle", case_insensitive: true) => { kind: 'manifest', parser: :parse_gradle }, match_extension(".xml", case_insensitive: true) => { content_matcher: :ivy_report?, kind: 'lockfile', parser: :parse_ivy_report }, match_filename("gradle-dependencies-q.txt", case_insensitive: true) => { kind: 'lockfile', parser: :parse_gradle_resolved }, match_filename("maven-resolved-dependencies.txt", case_insensitive: true) => { kind: 'lockfile', parser: :parse_maven_resolved } } end |
.parse_gradle(manifest) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/bibliothecary/parsers/maven.rb', line 155 def self.parse_gradle(manifest) response = Typhoeus.post("#{Bibliothecary.configuration.gradle_parser_host}/parse", body: manifest) raise Bibliothecary::RemoteParsingError.new("Http Error #{response.response_code} when contacting: #{Bibliothecary.configuration.gradle_parser_host}/parse", response.response_code) unless response.success? json = JSON.parse(response.body) return [] unless json['dependencies'] json['dependencies'].map do |dependency| name = [dependency["group"], dependency["name"]].join(':') next unless name =~ (/[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+(\.[A-Za-z0-9_-])?\:[A-Za-z0-9_-]/) { name: name, requirement: dependency["version"], type: dependency["type"] } end.compact end |
.parse_gradle_resolved(file_contents) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/bibliothecary/parsers/maven.rb', line 96 def self.parse_gradle_resolved(file_contents) type = nil file_contents.split("\n").map do |line| type_match = GRADLE_TYPE_REGEX.match(line) type = type_match.captures[0] if type_match gradle_dep_match = GRADLE_DEP_REGEX.match(line) next unless gradle_dep_match split = gradle_dep_match.captures[0] # org.springframework.boot:spring-boot-starter-web:2.1.0.M3 (*) # Lines can end with (n) or (*) to indicate that something was not resolved (n) or resolved previously (*). dep = line.split(split)[1].sub(/\(n\)$/, "").sub(/\(\*\)$/,"").strip.split(":") version = dep[-1] version = version.split("->")[-1].strip if line.include?("->") { name: dep[0, dep.length - 1].join(":"), requirement: version, type: type } end.compact.uniq {|item| [item[:name], item[:requirement], item[:type]]} end |
.parse_ivy_manifest(file_contents) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/bibliothecary/parsers/maven.rb', line 48 def self.parse_ivy_manifest(file_contents) manifest = Ox.parse file_contents manifest.dependencies.locate('dependency').map do |dependency| attrs = dependency.attributes { name: "#{attrs[:org]}:#{attrs[:name]}", requirement: attrs[:rev], type: 'runtime' } end end |
.parse_ivy_report(file_contents) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/bibliothecary/parsers/maven.rb', line 71 def self.parse_ivy_report(file_contents) doc = Ox.parse file_contents root = doc.locate("ivy-report").first raise "ivy-report document does not have ivy-report at the root" if root.nil? info = doc.locate("ivy-report/info").first raise "ivy-report document lacks <info> element" if info.nil? type = info.attributes[:conf] type = "unknown" if type.nil? modules = doc.locate("ivy-report/dependencies/module") modules.map do |mod| attrs = mod.attributes org = attrs[:organisation] name = attrs[:name] version = mod.locate('revision').first&.attributes[:name] next nil if org.nil? or name.nil? or version.nil? { name: "#{org}:#{name}", requirement: version, type: type } end.compact end |
.parse_maven_resolved(file_contents) ⇒ Object
120 121 122 123 124 125 126 |
# File 'lib/bibliothecary/parsers/maven.rb', line 120 def self.parse_maven_resolved(file_contents) Strings::ANSI.sanitize(file_contents) .split("\n") .map(&method(:parse_resolved_dep_line)) .compact .uniq end |
.parse_pom_manifest(file_contents, parent_properties = {}) ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/bibliothecary/parsers/maven.rb', line 139 def self.parse_pom_manifest(file_contents, parent_properties = {}) manifest = Ox.parse file_contents xml = manifest.respond_to?('project') ? manifest.project : manifest [].tap do |deps| ['dependencies/dependency', 'dependencyManagement/dependencies/dependency'].each do |deps_xpath| xml.locate(deps_xpath).each do |dep| deps.push({ name: "#{extract_pom_dep_info(xml, dep, 'groupId', parent_properties)}:#{extract_pom_dep_info(xml, dep, 'artifactId', parent_properties)}", requirement: extract_pom_dep_info(xml, dep, 'version', parent_properties), type: extract_pom_dep_info(xml, dep, 'scope', parent_properties) || 'runtime' }) end end end end |
.parse_resolved_dep_line(line) ⇒ Object
128 129 130 131 132 133 134 135 136 137 |
# File 'lib/bibliothecary/parsers/maven.rb', line 128 def self.parse_resolved_dep_line(line) dep_parts = line.strip.split(":") return unless dep_parts.length == 5 # org.springframework.boot:spring-boot-starter-web:jar:2.0.3.RELEASE:compile[36m -- module spring.boot.starter.web[0;1m [auto][m { name: dep_parts[0, 2].join(":"), requirement: dep_parts[3], type: dep_parts[4].split("--").first.strip } end |
.property_value(xml, property_name, parent_properties) ⇒ Object
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/bibliothecary/parsers/maven.rb', line 208 def self.property_value(xml, property_name, parent_properties) # the xml root is <project> so lookup the non property name in the xml # this converts ${project/group.id} -> ${group/id} non_prop_name = property_name.gsub(".", "/").gsub("project/", "") return value if !xml.respond_to?("properties") && parent_properties.empty? && !xml.locate(non_prop_name) prop_field = xml.properties.locate(property_name).first parent_prop = parent_properties[property_name] if prop_field prop_field.nodes.first elsif parent_prop parent_prop elsif xml.locate(non_prop_name).first # see if the value to look up is a field under the project # examples are ${project.groupId} or ${project.version} xml.locate(non_prop_name).first.nodes.first elsif xml.locate("parent/#{non_prop_name}").first # see if the value to look up is a field under the project parent # examples are ${project.groupId} or ${project.version} xml.locate("parent/#{non_prop_name}").first.nodes.first end end |
.replace_value_with_prop(original_value, property_value, property_name) ⇒ Object
188 189 190 |
# File 'lib/bibliothecary/parsers/maven.rb', line 188 def self.replace_value_with_prop(original_value, property_value, property_name) original_value.gsub("${#{property_name}}", property_value) end |