bibliothecary 8.6.3 → 8.6.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbb80d11b51f48774a10ad1617525f5a01f580f10571a90b418bcc974ad07da4
4
- data.tar.gz: 9a3c8bf41e7a7e461e28ea966acdd98ee5b4fb49d3fb94c59294734a6bd04b6a
3
+ metadata.gz: 58c0282aec64e81ed313f123670287697f3718e64276e1260030833d70baf892
4
+ data.tar.gz: be3e9107d5fb2a0968acca81878d4c0bab062bf5b9d994121af2d682a4e8d278
5
5
  SHA512:
6
- metadata.gz: 0aa9c475c57d5a5fb902cfbd049f12d113ed0ab0472fcd0ee788c07ff3cece579a92fb6d65e0b91d2ecc4415b1cb10c3fcdf2487b539990a11c785ab4078046e
7
- data.tar.gz: 6d35f0e547438be4e1cb6fe981ed9a96ffb86a3cdfd3dc8b633b7502412505f7884e0c12792987659c7e547dd48c99333dbe45add7e39bc0b5d4d727ef1cdf0a
6
+ metadata.gz: 211d953af085e4495325cdd450816f6747ce42b1d8e9c55852e2a50786190466a885cd70b8c454ec1c5d6872ce55f6d8a8ecddd03be7ca2faa02450216f84242
7
+ data.tar.gz: 74ba743b92e5858c8cc6d7341bf2dda8ff9ce7fdabdff58f44e544554fbc538ae9909726cfcb588d222fa9e1b490d0db9e4ff45896f6f13c375fd1e4ea978f95
data/Gemfile CHANGED
@@ -1,5 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ # Temporarily pegging to HEAD until 0.2.1 is released: https://github.com/piotrmurach/strings-ansi/pull/2
4
+ gem "strings-ansi", ref: "35d0c9430cf0a8022dc12bdab005bce296cb9f00", git: "git@github.com:piotrmurach/strings-ansi.git"
5
+
3
6
  # Specify your gem's dependencies in bibliothecary.gemspec
4
7
  gemspec
5
8
 
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_dependency "deb_control"
26
26
  spec.add_dependency "sdl4r"
27
27
  spec.add_dependency "commander"
28
- spec.add_dependency "strings-ansi"
28
+ spec.add_dependency "strings-ansi" # NB this is also pegged to a git sha in Gemfile temporarily.
29
29
  spec.add_dependency "strings"
30
30
  spec.add_dependency "packageurl-ruby"
31
31
 
@@ -19,7 +19,7 @@ module Bibliothecary
19
19
  options.default path: './'
20
20
  output = Bibliothecary.analyse(options.path)
21
21
  output.each do |file_contents|
22
- puts "#{file_contents[:path]} (#{manifest[:platform]})"
22
+ puts "#{file_contents[:path]} (#{file_contents[:platform]})"
23
23
  file_contents[:dependencies].group_by{|d| d[:type] }.each do |type, deps|
24
24
  puts " #{type}"
25
25
  deps.each do |dep|
@@ -17,26 +17,6 @@ module Bibliothecary
17
17
  NoComponents = Class.new(StandardError)
18
18
 
19
19
  class ManifestEntries
20
- # If a purl type (key) exists, it will be used in a manifest for
21
- # the key's value. If not, it's ignored.
22
- #
23
- # https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst
24
- PURL_TYPE_MAPPING = {
25
- "golang" => :go,
26
- "maven" => :maven,
27
- "npm" => :npm,
28
- "cargo" => :cargo,
29
- "composer" => :packagist,
30
- "conda" => :conda,
31
- "cran" => :cran,
32
- "gem" => :rubygems,
33
- "hackage" => :hackage,
34
- "hex" => :hex,
35
- "nuget" => :nuget,
36
- "pypi" => :pypi,
37
- "swift" => :swift_pm
38
- }
39
-
40
20
  attr_reader :manifests
41
21
 
42
22
  def initialize(parse_queue:)
@@ -49,7 +29,7 @@ module Bibliothecary
49
29
  end
50
30
 
51
31
  def <<(purl)
52
- mapping = PURL_TYPE_MAPPING[purl.type]
32
+ mapping = Bibliothecary::PURL_TYPE_MAPPING[purl.type]
53
33
  return unless mapping
54
34
 
55
35
  @manifests[mapping] ||= Set.new
@@ -0,0 +1,98 @@
1
+ # packageurl-ruby uses pattern-matching (https://docs.ruby-lang.org/en/2.7.0/NEWS.html#label-Pattern+matching)
2
+ # which warns a whole bunch in Ruby 2.7 as being an experimental feature, but has
3
+ # been accepted in Ruby 3.0 (https://rubyreferences.github.io/rubychanges/3.0.html#pattern-matching).
4
+ Warning[:experimental] = false
5
+ require 'package_url'
6
+ Warning[:experimental] = true
7
+
8
+ module Bibliothecary
9
+ module MultiParsers
10
+ module Spdx
11
+ include Bibliothecary::Analyser
12
+ include Bibliothecary::Analyser::TryCache
13
+
14
+ # e.g. 'SomeText:' (allowing for leading whitespace)
15
+ WELLFORMED_LINE_REGEX = /^\s*[a-zA-Z]+:/
16
+
17
+ # e.g. 'PackageName: (allowing for excessive whitespace)
18
+ PACKAGE_NAME_REGEX = /^\s*PackageName:\s*(.*)/
19
+
20
+ # e.g. 'PackageVersion:' (allowing for excessive whitespace)
21
+ PACKAGE_VERSION_REGEX =/^\s*PackageVersion:\s*(.*)/
22
+
23
+ # e.g. "ExternalRef: PACKAGE-MANAGER purl (allowing for excessive whitespace)
24
+ PURL_REGEX = /^\s*ExternalRef:\s*PACKAGE[-|_]MANAGER\s*purl\s*(.*)/
25
+
26
+ NoEntries = Class.new(StandardError)
27
+ MalformedFile = Class.new(StandardError)
28
+
29
+ def self.mapping
30
+ {
31
+ match_extension('.spdx') => {
32
+ kind: 'lockfile',
33
+ parser: :parse_spdx_tag_value,
34
+ ungroupable: true
35
+ }
36
+ }
37
+ end
38
+
39
+ def parse_spdx_tag_value(file_contents, options: {})
40
+ entries = try_cache(options, options[:filename]) do
41
+ parse_spdx_tag_value_file_contents(file_contents)
42
+ end
43
+
44
+ raise NoEntries if entries.empty?
45
+
46
+ entries[platform_name.to_sym]
47
+ end
48
+
49
+ def get_platform(purl_string)
50
+ platform = PackageURL.parse(purl_string).type
51
+
52
+ Bibliothecary::PURL_TYPE_MAPPING[platform]
53
+ end
54
+
55
+ def parse_spdx_tag_value_file_contents(file_contents)
56
+ entries = {}
57
+
58
+ package_name = nil
59
+ package_version = nil
60
+ platform = nil
61
+
62
+ file_contents.split("\n").each do |line|
63
+ stripped_line = line.strip
64
+
65
+ next if skip_line?(stripped_line)
66
+
67
+ raise MalformedFile unless stripped_line.match(WELLFORMED_LINE_REGEX)
68
+
69
+ if (match = stripped_line.match(PACKAGE_NAME_REGEX))
70
+ package_name = match[1]
71
+ elsif (match = stripped_line.match(PACKAGE_VERSION_REGEX))
72
+ package_version = match[1]
73
+ elsif (match = stripped_line.match(PURL_REGEX))
74
+ platform ||= get_platform(match[1])
75
+ end
76
+
77
+ unless package_name.nil? || package_version.nil? || platform.nil?
78
+ entries[platform.to_sym] ||= []
79
+ entries[platform.to_sym] << {
80
+ name: package_name,
81
+ requirement: package_version,
82
+ type: 'lockfile'
83
+ }
84
+
85
+ package_name = package_version = platform = nil
86
+ end
87
+ end
88
+
89
+ entries
90
+ end
91
+
92
+ def skip_line?(stripped_line)
93
+ # Ignore blank lines and comments
94
+ stripped_line == "" || stripped_line[0] == "#"
95
+ end
96
+ end
97
+ end
98
+ end
@@ -17,6 +17,7 @@ module Bibliothecary
17
17
  end
18
18
 
19
19
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
20
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
20
21
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
21
22
 
22
23
  def self.parse_manifest(file_contents, options: {})
@@ -28,6 +28,7 @@ module Bibliothecary
28
28
 
29
29
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
30
30
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
31
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
31
32
 
32
33
  def self.parse_conda(file_contents, options: {})
33
34
  parse_conda_with_kind(file_contents, "manifest")
@@ -18,6 +18,7 @@ module Bibliothecary
18
18
 
19
19
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
20
20
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
21
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
21
22
 
22
23
  def self.parse_description(file_contents, options: {})
23
24
  manifest = DebControl::ControlFileBase.parse(file_contents)
@@ -66,6 +66,7 @@ module Bibliothecary
66
66
  end
67
67
 
68
68
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
69
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
69
70
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
70
71
 
71
72
  def self.parse_godep_json(file_contents, options: {})
@@ -21,6 +21,7 @@ module Bibliothecary
21
21
 
22
22
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
23
23
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
24
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
24
25
 
25
26
  def self.parse_cabal(file_contents, options: {})
26
27
  headers = {
@@ -20,6 +20,7 @@ module Bibliothecary
20
20
 
21
21
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
22
22
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
23
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
23
24
 
24
25
  def self.parse_mix(file_contents, options: {})
25
26
  response = Typhoeus.post("#{Bibliothecary.configuration.mix_parser_host}/", body: file_contents)
@@ -25,15 +25,15 @@ module Bibliothecary
25
25
  # Deprecated methods: https://docs.gradle.org/current/userguide/upgrading_version_6.html#sec:configuration_removal
26
26
  GRADLE_DEPENDENCY_METHODS = %w(api compile compileClasspath compileOnly compileOnlyApi implementation runtime runtimeClasspath runtimeOnly testCompile testCompileOnly testImplementation testRuntime testRuntimeOnly)
27
27
 
28
- # Intentionally overly-simplified regexes to scrape deps from build.gradle (Groovy) and build.gradle.kts (Kotlin) files.
29
- # To be truly useful bibliothecary would need full Groovy / Kotlin parsers that speaks Gradle,
28
+ # Intentionally overly-simplified regexes to scrape deps from build.gradle (Groovy) and build.gradle.kts (Kotlin) files.
29
+ # To be truly useful bibliothecary would need full Groovy / Kotlin parsers that speaks Gradle,
30
30
  # because the Groovy and Kotlin DSLs have many dynamic ways of declaring dependencies.
31
31
  GRADLE_VERSION_REGEX = /[\w.-]+/ # e.g. '1.2.3'
32
32
  GRADLE_VAR_INTERPOLATION_REGEX = /\$\w+/ # e.g. '$myVersion'
33
33
  GRADLE_CODE_INTERPOLATION_REGEX = /\$\{.*\}/ # e.g. '${my-project-settings["version"]}'
34
34
  GRADLE_GAV_REGEX = /([\w.-]+)\:([\w.-]+)(?:\:(#{GRADLE_VERSION_REGEX}|#{GRADLE_VAR_INTERPOLATION_REGEX}|#{GRADLE_CODE_INTERPOLATION_REGEX}))?/ # e.g. "group:artifactId:1.2.3"
35
- GRADLE_GROOVY_SIMPLE_REGEX = /(#{GRADLE_DEPENDENCY_METHODS.join('|')})\s*\(?\s*['"]#{GRADLE_GAV_REGEX}['"]/m
36
- GRADLE_KOTLIN_SIMPLE_REGEX = /(#{GRADLE_DEPENDENCY_METHODS.join('|')})\s*\(\s*"#{GRADLE_GAV_REGEX}"/m
35
+ GRADLE_GROOVY_SIMPLE_REGEX = /(#{GRADLE_DEPENDENCY_METHODS.join('|')})\s*\(?\s*['"]#{GRADLE_GAV_REGEX}['"]/m
36
+ GRADLE_KOTLIN_SIMPLE_REGEX = /(#{GRADLE_DEPENDENCY_METHODS.join('|')})\s*\(\s*"#{GRADLE_GAV_REGEX}"/m
37
37
 
38
38
  MAVEN_PROPERTY_REGEX = /\$\{(.+?)\}/
39
39
  MAX_DEPTH = 5
@@ -96,6 +96,7 @@ module Bibliothecary
96
96
  end
97
97
 
98
98
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
99
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
99
100
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
100
101
 
101
102
  def self.parse_ivy_manifest(file_contents, options: {})
@@ -162,7 +163,7 @@ module Bibliothecary
162
163
  # so we treat these projects as "internal" deps with requirement of "1.0.0"
163
164
  if (project_match = line.match(GRADLE_PROJECT_REGEX))
164
165
  # an empty project name is self-referential (i.e. a cycle), and we don't need to track the manifest's project itself, e.g. "+--- project :"
165
- next if project_match[1].nil?
166
+ next if project_match[1].nil?
166
167
 
167
168
  # project names can have colons (e.g. for gradle projects in subfolders), which breaks maven artifact naming assumptions, so just replace them with hyphens.
168
169
  project_name = project_match[1].gsub(/:/, "-")
@@ -221,8 +222,11 @@ module Bibliothecary
221
222
  end
222
223
 
223
224
  def self.parse_maven_tree(file_contents, options: {})
224
- file_contents = file_contents.gsub(/\r\n?/, "\n")
225
- captures = file_contents.scan(/^\[INFO\](?:(?:\+-)|\||(?:\\-)|\s)+((?:[\w\.-]+:)+[\w\.\-${}]+)/).flatten.uniq
225
+ captures = Strings::ANSI.sanitize(file_contents)
226
+ .gsub(/\r\n?/, "\n")
227
+ .scan(/^\[INFO\](?:(?:\+-)|\||(?:\\-)|\s)+((?:[\w\.-]+:)+[\w\.\-${}]+)/)
228
+ .flatten
229
+ .uniq
226
230
 
227
231
  deps = captures.map do |item|
228
232
  parts = item.split(":")
@@ -34,6 +34,7 @@ module Bibliothecary
34
34
  end
35
35
 
36
36
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
37
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
37
38
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
38
39
 
39
40
  def self.parse_package_lock(file_contents, options: {})
@@ -57,9 +58,9 @@ module Bibliothecary
57
58
  def self.parse_package_lock_v1(manifest)
58
59
  parse_package_lock_deps_recursively(manifest.fetch('dependencies', []))
59
60
  end
60
-
61
+
61
62
  def self.parse_package_lock_v2(manifest)
62
- # "packages" is a flat object where each key is the installed location of the dep, e.g. node_modules/foo/node_modules/bar.
63
+ # "packages" is a flat object where each key is the installed location of the dep, e.g. node_modules/foo/node_modules/bar.
63
64
  manifest
64
65
  .fetch("packages")
65
66
  .reject { |name, dep| name == "" } # this is the lockfile's package itself
@@ -68,7 +69,7 @@ module Bibliothecary
68
69
  name: name.split("node_modules/").last,
69
70
  requirement: dep["version"],
70
71
  type: dep.fetch("dev", false) || dep.fetch("devOptional", false) ? "development" : "runtime"
71
- }
72
+ }
72
73
  end
73
74
  end
74
75
 
@@ -94,7 +95,7 @@ module Bibliothecary
94
95
  def self.parse_manifest(file_contents, options: {})
95
96
  manifest = JSON.parse(file_contents)
96
97
  raise "appears to be a lockfile rather than manifest format" if manifest.key?('lockfileVersion')
97
-
98
+
98
99
  (
99
100
  map_dependencies(manifest, 'dependencies', 'runtime') +
100
101
  map_dependencies(manifest, 'devDependencies', 'development')
@@ -46,6 +46,7 @@ module Bibliothecary
46
46
 
47
47
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
48
48
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
49
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
49
50
 
50
51
  def self.parse_project_lock_json(file_contents, options: {})
51
52
  manifest = JSON.parse file_contents
@@ -20,6 +20,7 @@ module Bibliothecary
20
20
 
21
21
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
22
22
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
23
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
23
24
 
24
25
  def self.parse_lockfile(file_contents, options: {})
25
26
  manifest = JSON.parse file_contents
@@ -87,6 +87,7 @@ module Bibliothecary
87
87
 
88
88
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
89
89
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
90
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
90
91
 
91
92
  def self.parse_pipfile(file_contents, options: {})
92
93
  manifest = Tomlrb.parse(file_contents)
@@ -94,10 +95,10 @@ module Bibliothecary
94
95
  end
95
96
 
96
97
  def self.parse_pyproject(file_contents, options: {})
97
- deps = []
98
+ deps = []
98
99
 
99
100
  file_contents = Tomlrb.parse(file_contents)
100
-
101
+
101
102
  # Parse poetry [tool.poetry] deps
102
103
  poetry_manifest = file_contents.fetch('tool', {}).fetch('poetry', {})
103
104
  deps += map_dependencies(poetry_manifest['dependencies'], 'runtime')
@@ -32,6 +32,7 @@ module Bibliothecary
32
32
 
33
33
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
34
34
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
35
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
35
36
 
36
37
  def self.parse_gemfile_lock(file_contents, options: {})
37
38
  file_contents.lines(chomp: true).map do |line|
@@ -14,6 +14,7 @@ module Bibliothecary
14
14
 
15
15
  add_multi_parser(Bibliothecary::MultiParsers::CycloneDX)
16
16
  add_multi_parser(Bibliothecary::MultiParsers::DependenciesCSV)
17
+ add_multi_parser(Bibliothecary::MultiParsers::Spdx)
17
18
 
18
19
  def self.parse_package_swift(file_contents, options: {})
19
20
  response = Typhoeus.post("#{Bibliothecary.configuration.swift_parser_host}/to-json", body: file_contents)
@@ -0,0 +1,21 @@
1
+ module Bibliothecary
2
+ # If a purl type (key) exists, it will be used in a manifest for
3
+ # the key's value. If not, it's ignored.
4
+ #
5
+ # https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst
6
+ PURL_TYPE_MAPPING = {
7
+ "golang" => :go,
8
+ "maven" => :maven,
9
+ "npm" => :npm,
10
+ "cargo" => :cargo,
11
+ "composer" => :packagist,
12
+ "conda" => :conda,
13
+ "cran" => :cran,
14
+ "gem" => :rubygems,
15
+ "hackage" => :hackage,
16
+ "hex" => :hex,
17
+ "nuget" => :nuget,
18
+ "pypi" => :pypi,
19
+ "swift" => :swift_pm
20
+ }.freeze
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Bibliothecary
2
- VERSION = "8.6.3"
2
+ VERSION = "8.6.5"
3
3
  end
data/lib/bibliothecary.rb CHANGED
@@ -5,6 +5,7 @@ require "bibliothecary/runner"
5
5
  require "bibliothecary/exceptions"
6
6
  require "bibliothecary/file_info"
7
7
  require "bibliothecary/related_files_info"
8
+ require "bibliothecary/purl_util"
8
9
  require "find"
9
10
  require "tomlrb"
10
11
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bibliothecary
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.6.3
4
+ version: 8.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-30 00:00:00.000000000 Z
11
+ date: 2023-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tomlrb
@@ -248,7 +248,7 @@ dependencies:
248
248
  - - ">="
249
249
  - !ruby/object:Gem::Version
250
250
  version: '0'
251
- description:
251
+ description:
252
252
  email:
253
253
  - andrewnez@gmail.com
254
254
  executables:
@@ -290,6 +290,7 @@ files:
290
290
  - lib/bibliothecary/multi_parsers/cyclonedx.rb
291
291
  - lib/bibliothecary/multi_parsers/dependencies_csv.rb
292
292
  - lib/bibliothecary/multi_parsers/json_runtime.rb
293
+ - lib/bibliothecary/multi_parsers/spdx.rb
293
294
  - lib/bibliothecary/parsers/bower.rb
294
295
  - lib/bibliothecary/parsers/cargo.rb
295
296
  - lib/bibliothecary/parsers/carthage.rb
@@ -315,6 +316,7 @@ files:
315
316
  - lib/bibliothecary/parsers/rubygems.rb
316
317
  - lib/bibliothecary/parsers/shard.rb
317
318
  - lib/bibliothecary/parsers/swift_pm.rb
319
+ - lib/bibliothecary/purl_util.rb
318
320
  - lib/bibliothecary/related_files_info.rb
319
321
  - lib/bibliothecary/runner.rb
320
322
  - lib/bibliothecary/runner/multi_manifest_filter.rb
@@ -324,7 +326,7 @@ homepage: https://github.com/librariesio/bibliothecary
324
326
  licenses:
325
327
  - AGPL-3.0
326
328
  metadata: {}
327
- post_install_message:
329
+ post_install_message:
328
330
  rdoc_options: []
329
331
  require_paths:
330
332
  - lib
@@ -340,7 +342,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
340
342
  version: '0'
341
343
  requirements: []
342
344
  rubygems_version: 3.1.6
343
- signing_key:
345
+ signing_key:
344
346
  specification_version: 4
345
347
  summary: Find and parse manifests
346
348
  test_files: []