license_scout 1.3.17 → 2.0.2

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +195 -0
  3. data/bin/license_scout +3 -59
  4. data/bin/mix_lock_json +0 -0
  5. data/bin/rebar_lock_json +0 -0
  6. data/lib/license_scout/cli.rb +99 -0
  7. data/lib/license_scout/collector.rb +25 -77
  8. data/lib/license_scout/config.rb +94 -0
  9. data/lib/license_scout/data/dependeny_manifest_v2_schema.json +62 -0
  10. data/lib/license_scout/data/exceptions.json +306 -0
  11. data/lib/license_scout/data/licenses.json +4653 -0
  12. data/lib/license_scout/dependency.rb +79 -7
  13. data/lib/license_scout/dependency_manager/base.rb +74 -42
  14. data/lib/license_scout/dependency_manager/berkshelf.rb +25 -50
  15. data/lib/license_scout/dependency_manager/bundler/_bundler_script.rb +1 -1
  16. data/lib/license_scout/dependency_manager/bundler.rb +47 -69
  17. data/lib/license_scout/dependency_manager/cpanm.rb +62 -112
  18. data/lib/license_scout/dependency_manager/dep.rb +29 -36
  19. data/lib/license_scout/dependency_manager/glide.rb +25 -36
  20. data/lib/license_scout/dependency_manager/godep.rb +27 -26
  21. data/lib/license_scout/dependency_manager/habitat.rb +126 -0
  22. data/lib/license_scout/dependency_manager/mix.rb +105 -0
  23. data/lib/license_scout/dependency_manager/npm.rb +30 -86
  24. data/lib/license_scout/dependency_manager/rebar.rb +26 -45
  25. data/lib/license_scout/dependency_manager.rb +19 -5
  26. data/lib/license_scout/exceptions.rb +2 -43
  27. data/lib/license_scout/license.rb +126 -0
  28. data/lib/license_scout/{license_file_analyzer.rb → log.rb} +4 -6
  29. data/lib/license_scout/reporter.rb +149 -55
  30. data/lib/license_scout/spdx.rb +123 -0
  31. data/lib/license_scout/version.rb +1 -1
  32. data/lib/license_scout.rb +2 -0
  33. data/native_parsers/mix_lock_json/README.md +21 -0
  34. data/native_parsers/mix_lock_json/lib/mix_lock_json.ex +20 -0
  35. data/native_parsers/mix_lock_json/mix.exs +31 -0
  36. data/native_parsers/mix_lock_json/mix.lock +3 -0
  37. data/{erl_src → native_parsers}/rebar_lock_json/rebar.lock +2 -2
  38. metadata +144 -67
  39. data/lib/license_scout/canonical_licenses/BSD-2-Clause.txt +0 -19
  40. data/lib/license_scout/canonical_licenses/BSD-3-Clause.txt +0 -27
  41. data/lib/license_scout/canonical_licenses/BSD-4-Clause.txt +0 -31
  42. data/lib/license_scout/canonical_licenses/Chef-MLSA.txt +0 -5
  43. data/lib/license_scout/canonical_licenses/ISC.txt +0 -14
  44. data/lib/license_scout/canonical_licenses/MIT.txt +0 -20
  45. data/lib/license_scout/dependency_manager/bundler/LICENSE.md +0 -23
  46. data/lib/license_scout/dependency_manager/json/README.md +0 -392
  47. data/lib/license_scout/dependency_manager/manual.rb +0 -67
  48. data/lib/license_scout/license_file_analyzer/any_matcher.rb +0 -37
  49. data/lib/license_scout/license_file_analyzer/definitions.rb +0 -219
  50. data/lib/license_scout/license_file_analyzer/header_matcher.rb +0 -34
  51. data/lib/license_scout/license_file_analyzer/matcher.rb +0 -46
  52. data/lib/license_scout/license_file_analyzer/template.rb +0 -45
  53. data/lib/license_scout/license_file_analyzer/templates/Apache2-short.txt +0 -11
  54. data/lib/license_scout/license_file_analyzer/templates/Apache2.txt +0 -170
  55. data/lib/license_scout/license_file_analyzer/templates/BSD-2-Clause-bullets.txt +0 -18
  56. data/lib/license_scout/license_file_analyzer/templates/BSD-2-Clause.txt +0 -19
  57. data/lib/license_scout/license_file_analyzer/templates/BSD-3-Clause-alt-format.txt +0 -24
  58. data/lib/license_scout/license_file_analyzer/templates/BSD-3-Clause.txt +0 -21
  59. data/lib/license_scout/license_file_analyzer/templates/BSD.txt +0 -24
  60. data/lib/license_scout/license_file_analyzer/templates/Chef-MLSA.txt +0 -5
  61. data/lib/license_scout/license_file_analyzer/templates/EPLICENSE.txt +0 -286
  62. data/lib/license_scout/license_file_analyzer/templates/GPL-2.0.txt +0 -339
  63. data/lib/license_scout/license_file_analyzer/templates/GPL-3.0.txt +0 -674
  64. data/lib/license_scout/license_file_analyzer/templates/ISC.txt +0 -2
  65. data/lib/license_scout/license_file_analyzer/templates/LGPL-3.0.txt +0 -165
  66. data/lib/license_scout/license_file_analyzer/templates/MIT.txt +0 -9
  67. data/lib/license_scout/license_file_analyzer/templates/MPL2.txt +0 -373
  68. data/lib/license_scout/license_file_analyzer/templates/Python-2.0.txt +0 -47
  69. data/lib/license_scout/license_file_analyzer/templates/Ruby.txt +0 -52
  70. data/lib/license_scout/license_file_analyzer/text.rb +0 -46
  71. data/lib/license_scout/net_fetcher.rb +0 -106
  72. data/lib/license_scout/options.rb +0 -47
  73. data/lib/license_scout/overrides.rb +0 -1125
  74. /data/{erl_src → native_parsers}/rebar_lock_json/README.md +0 -0
  75. /data/{erl_src → native_parsers}/rebar_lock_json/rebar.config +0 -0
  76. /data/{erl_src → native_parsers}/rebar_lock_json/src/rebar_lock_json.app.src +0 -0
  77. /data/{erl_src → native_parsers}/rebar_lock_json/src/rebar_lock_json.erl +0 -0
@@ -15,146 +15,96 @@
15
15
  # limitations under the License.
16
16
  #
17
17
 
18
- require "ffi_yajl" unless defined?(FFI_Yajl)
19
- require "psych"
20
- require "mixlib/shellout" unless defined?(Mixlib::ShellOut)
21
-
22
18
  require "license_scout/dependency_manager/base"
23
- require "license_scout/exceptions"
24
- require "license_scout/dependency"
19
+
20
+ require "ffi_yajl"
21
+ require "psych"
22
+ require "mixlib/shellout"
25
23
 
26
24
  module LicenseScout
27
25
  module DependencyManager
28
26
  class Cpanm < Base
29
27
 
30
- class CpanmDependency
31
-
32
- LICENSE_TYPE_MAP = {
33
- "perl_5" => "Perl-5",
34
- "perl" => "Perl-5",
35
- "apache_2_0" => "Apache-2.0",
36
- "artistic_2" => "Artistic-2.0",
37
- "gpl_3" => "GPL-3.0",
38
- }.freeze
39
-
40
- attr_reader :unpack_path
41
- attr_reader :overrides
42
- attr_reader :metadata
43
-
44
- def initialize(unpack_path, overrides)
45
- @unpack_path = unpack_path
46
- @overrides = overrides
47
- end
48
-
49
- def to_dep
50
- parse_metadata!
28
+ def name
29
+ "perl_cpanm"
30
+ end
51
31
 
52
- Dependency.new(
53
- name,
54
- version.to_s,
55
- license,
56
- license_files,
57
- "perl_cpanm"
58
- )
59
- end
32
+ def type
33
+ "perl"
34
+ end
60
35
 
61
- def parse_metadata!
62
- # Packages can contain metadata files named META.yml, META.json,
63
- # MYMETA.json, MYMETA.yml. META.* files are created by the authors of
64
- # the plugins whereas MYMETA.* files are created by the build system
65
- # after dynamic dependencies are resolved. For our purposes META.*
66
- # files are enough. And for no good reason we prioritize json files
67
- # over yml files.
68
- @metadata ||= begin
69
- json_path = File.join(unpack_path, "META.json")
70
- yml_path = File.join(unpack_path, "META.yml")
71
-
72
- if File.exist?(json_path)
73
- FFI_Yajl::Parser.parse(File.read(json_path))
74
- elsif File.exist?(yml_path)
75
- Psych.safe_load(File.read(yml_path))
76
- else
77
- raise LicenseScout::Exceptions::Error.new("Can not find a metadata file for the perl package at '#{unpack_path}'.")
78
- end
79
- end
80
- end
36
+ def signature
37
+ File.exist?(meta_json_path) ? "META.json file" : "META.yml file"
38
+ end
81
39
 
82
- def name
83
- metadata["name"]
84
- end
40
+ def install_command
41
+ "cpanm --installdeps ."
42
+ end
85
43
 
86
- def version
87
- metadata["version"]
88
- end
44
+ # NOTE: it's possible that projects won't have a META.yml, but the two
45
+ # that we care about for Chef Server do have one. As of 2015, 84% of perl
46
+ # distribution packages have one: http://neilb.org/2015/10/18/spotters-guide.html
47
+ def detected?
48
+ File.exist?(meta_json_path) || File.exist?(meta_yml_path)
49
+ end
89
50
 
90
- def license
91
- @license ||= begin
92
- override_license = overrides.license_for("perl_cpanm", name, version)
51
+ def dependencies
52
+ Dir.glob("#{cpanm_root}/latest-build/*").map do |dep_path|
53
+ next unless File.directory?(dep_path)
93
54
 
94
- if override_license
95
- override_license
96
- elsif metadata && metadata.key?("license")
97
- given_type = Array(metadata["license"]).reject { |l| l == "unknown" }.first
55
+ dep_data = manifest(dep_path)
56
+ metafile = dep_data["metafile"]
57
+ dep_name = dep_data["name"]
58
+ dep_version = dep_data["version"]
98
59
 
99
- # Normalize the common perl license strings to the strings we commonly use
100
- LICENSE_TYPE_MAP[given_type] || given_type
101
- end
102
- end
103
- end
60
+ dependency = new_dependency(dep_name, dep_version, dep_path)
104
61
 
105
- def license_files
106
- @license_files ||= begin
107
- override_license_files = overrides.license_files_for("perl_cpanm", name, version)
62
+ # CPANM projects contain license metadata - include it!
63
+ unless dep_data["license"].nil?
64
+ Array(dep_data["license"]).each do |license|
65
+ next if license == "unknown"
108
66
 
109
- if override_license_files.empty?
110
- find_license_files
111
- else
112
- override_license_files.resolve_locations(unpack_path)
67
+ dependency.add_license(license, metafile)
113
68
  end
114
69
  end
115
- end
116
-
117
- def find_license_files
118
- Dir["#{unpack_path}/*"].select do |f|
119
- Cpanm::POSSIBLE_LICENSE_FILES.include?(File.basename(f))
120
- end
121
- end
122
70
 
71
+ dependency
72
+ end.compact
123
73
  end
124
74
 
125
- def name
126
- "perl_cpanm"
127
- end
75
+ private
128
76
 
129
- def cpanm_root
130
- # By default cpanm downloads all the dependencies into ~/.cpanm directory
131
- File.expand_path("~/.cpanm")
77
+ def meta_yml_path
78
+ File.join(directory, "META.yml")
132
79
  end
133
80
 
134
- def dependencies
135
- @dependencies ||= begin
136
- deps = []
137
-
138
- Dir.glob("#{cpanm_root}/latest-build/*").each do |dep_path|
139
- next unless File.directory?(dep_path)
140
-
141
- deps << CpanmDependency.new(dep_path, options.overrides).to_dep
142
- end
81
+ def meta_json_path
82
+ File.join(directory, "META.json")
83
+ end
143
84
 
144
- deps
85
+ # Packages can contain metadata files named META.yml, META.json,
86
+ # MYMETA.json, MYMETA.yml. META.* files are created by the authors of
87
+ # the plugins whereas MYMETA.* files are created by the build system
88
+ # after dynamic dependencies are resolved. For our purposes META.*
89
+ # files are enough. And for no good reason we prioritize json files
90
+ # over yml files.
91
+ def manifest(unpack_path)
92
+ json_path = File.join(unpack_path, "META.json")
93
+ yml_path = File.join(unpack_path, "META.yml")
94
+
95
+ if File.exist?(json_path)
96
+ FFI_Yajl::Parser.parse(File.read(json_path)).merge({ "metafile" => "META.json" })
97
+ elsif File.exist?(yml_path)
98
+ Psych.safe_load(File.read(yml_path)).merge({ "metafile" => "META.yml" })
99
+ else
100
+ raise LicenseScout::Exceptions::Error.new("Can not find a metadata file for the perl package at '#{unpack_path}'.")
145
101
  end
146
102
  end
147
103
 
148
- # NOTE: it's possible that projects won't have a META.yml, but the two
149
- # that we care about for Chef Server do have one. As of 2015, 84% of perl
150
- # distribution packages have one: http://neilb.org/2015/10/18/spotters-guide.html
151
- def detected?
152
- meta_yml_path = File.join(project_dir, "META.yml")
153
- meta_json_path = File.join(project_dir, "META.json")
154
-
155
- File.exist?(meta_yml_path) || File.exist?(meta_json_path)
104
+ def cpanm_root
105
+ # By default cpanm downloads all the dependencies into ~/.cpanm directory
106
+ File.expand_path(LicenseScout::Config.cpanm_root)
156
107
  end
157
-
158
108
  end
159
109
  end
160
110
  end
@@ -15,8 +15,8 @@
15
15
  # limitations under the License.
16
16
  #
17
17
 
18
- require "ffi_yajl" unless defined?(FFI_Yajl)
19
- require "yaml" unless defined?(YAML)
18
+ require "ffi_yajl"
19
+ require "yaml"
20
20
  require "toml-rb"
21
21
  require "license_scout/dependency_manager/base"
22
22
 
@@ -26,62 +26,55 @@ module LicenseScout
26
26
  class Dep < Base
27
27
 
28
28
  def name
29
- "go_dep"
29
+ "golang_dep"
30
30
  end
31
31
 
32
- def detected?
33
- File.exist?(root_dep_file)
32
+ def type
33
+ "golang"
34
34
  end
35
35
 
36
- def dependencies
37
- deps = File.open(root_dep_file) do |f|
38
- TomlRB.parse(f)
39
- end
40
- return [] unless deps.key?("projects")
36
+ def signature
37
+ "Gopkg.lock file"
38
+ end
41
39
 
42
- deps["projects"].map do |pkg_info|
43
- pkg_import_name = pkg_info["name"]
44
- pkg_file_name = pkg_import_name.tr("/", "_")
45
- pkg_version = pkg_info["version"] || pkg_info["revision"]
46
- license = options.overrides.license_for("go", pkg_import_name, pkg_version)
40
+ def install_command
41
+ "dep ensure"
42
+ end
47
43
 
48
- override_license_files = options.overrides.license_files_for("go", pkg_import_name, pkg_version)
49
- if override_license_files.empty?
50
- license_files = find_license_files_for_package_in_gopath_or_vendor_dir(pkg_import_name)
51
- else
52
- license_files = override_license_files.resolve_locations(gopath(pkg_import_name))
53
- end
44
+ def detected?
45
+ File.exist?(gopkg_lock_path)
46
+ end
54
47
 
55
- if license.nil? && !license_files.empty?
56
- license = scan_licenses(license_files)
57
- end
48
+ def dependencies
49
+ Array(gopkg.dig("projects")).map do |pkg_info|
50
+ dep_name = pkg_info["name"]
51
+ dep_version = pkg_info["version"] || pkg_info["revision"]
52
+ dep_path = package_path(dep_name)
58
53
 
59
- create_dependency(pkg_file_name, pkg_version, license, license_files)
60
- end
54
+ new_dependency(dep_name, dep_version, dep_path)
55
+ end.compact
61
56
  end
62
57
 
63
58
  private
64
59
 
65
- def scan_licenses(license_files)
66
- found_license = LicenseScout::LicenseFileAnalyzer.find_by_text(IO.read(license_files.first))
67
- found_license && found_license.short_name
60
+ def gopkg
61
+ File.open(gopkg_lock_path) { |f| TomlRB.parse(f) }
68
62
  end
69
63
 
70
- def root_dep_file
71
- File.join(project_dir, "Gopkg.lock")
64
+ def gopkg_lock_path
65
+ File.join(directory, "Gopkg.lock")
72
66
  end
73
67
 
74
68
  def gopath(pkg)
75
- "#{ENV["GOPATH"]}/src/#{pkg}"
69
+ "#{ENV['GOPATH']}/src/#{pkg}"
76
70
  end
77
71
 
78
72
  def vendor_dir(pkg = nil)
79
- File.join(project_dir, "vendor/#{pkg}")
73
+ File.join(directory, "vendor/#{pkg}")
80
74
  end
81
75
 
82
- def find_license_files_for_package_in_gopath_or_vendor_dir(pkg)
83
- root_files = Dir["#{gopath(pkg)}/*"] + Dir["#{vendor_dir(pkg)}/*"]
84
- root_files.select { |f| POSSIBLE_LICENSE_FILES.include?(File.basename(f)) }
76
+ def package_path(pkg)
77
+ (Dir[vendor_dir(pkg)] + Dir[gopath(pkg)]).first
85
78
  end
86
79
  end
87
80
  end
@@ -15,7 +15,7 @@
15
15
  # limitations under the License.
16
16
  #
17
17
 
18
- require "yaml" unless defined?(YAML)
18
+ require "psych"
19
19
  require "license_scout/dependency_manager/base"
20
20
 
21
21
  module LicenseScout
@@ -23,56 +23,45 @@ module LicenseScout
23
23
  class Glide < Base
24
24
 
25
25
  def name
26
- "go_glide"
26
+ "golang_glide"
27
27
  end
28
28
 
29
- def detected?
30
- File.exist?(glide_yaml)
29
+ def type
30
+ "golang"
31
31
  end
32
32
 
33
- def dependencies
34
- unless File.file?(glide_yaml_locked)
35
- raise "Detected Go/Glide project that is missing its \"glide.lock\" "\
36
- "file in #{project_dir}"
37
- end
38
-
39
- deps = YAML.safe_load(File.read(glide_yaml_locked), permitted_classes: [Date, Symbol, Time])
40
- deps["imports"].map { |i| add_glide_dep(i) }
33
+ def signature
34
+ "glide.lock file"
41
35
  end
42
36
 
43
- private
37
+ def install_command
38
+ "glide install"
39
+ end
44
40
 
45
- def add_glide_dep(import_field)
46
- pkg_import_name = import_field["name"]
47
- pkg_file_name = pkg_import_name.tr("/", "_")
48
- pkg_version = import_field["version"]
49
- license = options.overrides.license_for("go", pkg_import_name, pkg_version)
41
+ def detected?
42
+ File.exist?(glide_lock_path)
43
+ end
50
44
 
51
- override_license_files = options.overrides.license_files_for("go", pkg_import_name, pkg_version)
52
- if override_license_files.empty?
53
- license_files = find_license_files_for_package_in_gopath(pkg_import_name)
54
- else
55
- license_files = override_license_files.resolve_locations(gopath(pkg_import_name))
56
- end
45
+ def dependencies
46
+ # We cannot use YAML.safe_load because Psych throws a fit about the
47
+ # updated field. We should circle back and see what we can do to fix that.
48
+ YAML.load(File.read(glide_lock_path))["imports"].map do |import|
49
+ dep_name = import["name"]
50
+ dep_version = import["version"]
51
+ dep_path = gopath(dep_name)
57
52
 
58
- create_dependency(pkg_file_name, pkg_version, license, license_files)
53
+ new_dependency(dep_name, dep_version, dep_path)
54
+ end.compact
59
55
  end
60
56
 
61
- def glide_yaml
62
- File.join(project_dir, "glide.yaml")
63
- end
57
+ private
64
58
 
65
- def glide_yaml_locked
66
- File.join(project_dir, "glide.lock")
59
+ def glide_lock_path
60
+ File.join(directory, "glide.lock")
67
61
  end
68
62
 
69
63
  def gopath(pkg)
70
- "#{ENV["GOPATH"]}/src/#{pkg}"
71
- end
72
-
73
- def find_license_files_for_package_in_gopath(pkg)
74
- root_files = Dir["#{gopath(pkg)}/*"]
75
- root_files.select { |f| POSSIBLE_LICENSE_FILES.include?(File.basename(f)) }
64
+ "#{ENV['GOPATH']}/src/#{pkg}"
76
65
  end
77
66
  end
78
67
  end
@@ -15,7 +15,7 @@
15
15
  # limitations under the License.
16
16
  #
17
17
 
18
- require "ffi_yajl" unless defined?(FFI_Yajl)
18
+ require "ffi_yajl"
19
19
  require "license_scout/dependency_manager/base"
20
20
 
21
21
  module LicenseScout
@@ -23,7 +23,19 @@ module LicenseScout
23
23
  class Godep < Base
24
24
 
25
25
  def name
26
- "go_godep"
26
+ "golang_godep"
27
+ end
28
+
29
+ def type
30
+ "golang"
31
+ end
32
+
33
+ def signature
34
+ "Godeps/Godeps.json file"
35
+ end
36
+
37
+ def install_command
38
+ "godep restore"
27
39
  end
28
40
 
29
41
  def detected?
@@ -31,40 +43,29 @@ module LicenseScout
31
43
  end
32
44
 
33
45
  def dependencies
34
- godeps = File.open(root_godeps_file) do |f|
35
- FFI_Yajl::Parser.parse(f)
36
- end
37
-
38
46
  godeps["Deps"].map do |pkg_info|
39
- pkg_import_name = pkg_info["ImportPath"]
40
- pkg_file_name = pkg_import_name.tr("/", "_")
41
- pkg_version = pkg_info["Comment"] || pkg_info["Rev"]
42
- license = options.overrides.license_for("go", pkg_import_name, pkg_version)
47
+ dep_name = pkg_info["ImportPath"]
48
+ dep_version = pkg_info["Comment"] || pkg_info["Rev"]
49
+ dep_path = gopath(dep_name)
43
50
 
44
- override_license_files = options.overrides.license_files_for("go", pkg_import_name, pkg_version)
45
- if override_license_files.empty?
46
- license_files = find_license_files_for_package_in_gopath(pkg_import_name)
47
- else
48
- license_files = override_license_files.resolve_locations(gopath(pkg_import_name))
49
- end
50
-
51
- create_dependency(pkg_file_name, pkg_version, license, license_files)
52
- end
51
+ new_dependency(dep_name, dep_version, dep_path)
52
+ end.compact
53
53
  end
54
54
 
55
55
  private
56
56
 
57
- def root_godeps_file
58
- File.join(project_dir, "Godeps/Godeps.json")
57
+ def godeps
58
+ File.open(root_godeps_file) do |f|
59
+ FFI_Yajl::Parser.parse(f)
60
+ end
59
61
  end
60
62
 
61
- def gopath(pkg)
62
- "#{ENV["GOPATH"]}/src/#{pkg}"
63
+ def root_godeps_file
64
+ File.join(directory, "Godeps/Godeps.json")
63
65
  end
64
66
 
65
- def find_license_files_for_package_in_gopath(pkg)
66
- root_files = Dir["#{gopath(pkg)}/*"]
67
- root_files.select { |f| POSSIBLE_LICENSE_FILES.include?(File.basename(f)) }
67
+ def gopath(pkg)
68
+ "#{ENV['GOPATH']}/src/#{pkg}"
68
69
  end
69
70
  end
70
71
  end
@@ -0,0 +1,126 @@
1
+ #
2
+ # Copyright:: Copyright 2018, Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "license_scout/dependency_manager/base"
19
+
20
+ require "open-uri"
21
+ require "mixlib/shellout"
22
+
23
+ module LicenseScout
24
+ module DependencyManager
25
+ class Habitat < Base
26
+
27
+ def name
28
+ "habitat"
29
+ end
30
+
31
+ def type
32
+ "habitat"
33
+ end
34
+
35
+ def signature
36
+ File.exist?(habitat_plan_sh_path) ? "habitat/plan.sh file" : "plan.sh file"
37
+ end
38
+
39
+ def install_command
40
+ ""
41
+ end
42
+
43
+ def detected?
44
+ File.exist?(plan_sh_path) || File.exist?(habitat_plan_sh_path)
45
+ end
46
+
47
+ def dependencies
48
+ tdeps = Set.new(pkg_deps)
49
+
50
+ pkg_deps.each do |pkg_dep|
51
+ pkg_info(pkg_dep)["tdeps"].each { |dep| tdeps << to_ident(dep) }
52
+ end
53
+
54
+ tdeps.sort.map do |tdep|
55
+ o, n, v, r = tdep.split("/")
56
+ dep_name = "#{o}/#{n}"
57
+ dep_version = "#{v}-#{r}"
58
+
59
+ dependency = new_dependency(dep_name, dep_version, nil)
60
+
61
+ license_from_manifest(pkg_info(tdep)["manifest"]).each do |spdx|
62
+ dependency.add_license(spdx, "https://bldr.habitat.sh/v1/depot/channels/#{o}/stable/pkgs/#{n}/#{v}/#{r}")
63
+ end
64
+
65
+ dependency
66
+ end.compact
67
+ end
68
+
69
+ private
70
+
71
+ def license_from_manifest(manifest_content)
72
+ /^*\s+__License__:\s+(.+)$/.match(manifest_content)[1].strip.split("\s")
73
+ end
74
+
75
+ def pkg_deps
76
+ @pkg_deps ||= begin
77
+ plan_path = File.exist?(plan_sh_path) ? plan_sh_path : habitat_plan_sh_path
78
+
79
+ c = Mixlib::ShellOut.new("bash -ec 'export PLAN_CONTEXT=\"#{File.dirname(plan_path)}\"; source #{plan_path}; echo ${pkg_deps[*]}'", LicenseScout::Config.environment)
80
+ c.run_command
81
+ c.error!
82
+ pkg_deps = c.stdout.split("\s")
83
+
84
+ # Fetch the fully-qualified pkg_ident for each pkg
85
+ pkg_deps.map { |dep| to_ident(pkg_info(dep)["ident"]) }
86
+ end
87
+ end
88
+
89
+ def to_ident(ident_hash)
90
+ "#{ident_hash["origin"]}/#{ident_hash["name"]}/#{ident_hash["version"]}/#{ident_hash["release"]}"
91
+ end
92
+
93
+ def pkg_info(pkg_ident)
94
+ $habitat_pkg_info ||= {}
95
+ $habitat_pkg_info[pkg_ident] ||= begin
96
+ pkg_origin, pkg_name, pkg_version, pkg_release = pkg_ident.split("/")
97
+
98
+ base_api_uri = "https://bldr.habitat.sh/v1/depot/channels/#{pkg_origin}/stable/pkgs/#{pkg_name}"
99
+ if pkg_version.nil? && pkg_release.nil?
100
+ base_api_uri += "/latest"
101
+ elsif pkg_release.nil?
102
+ base_api_uri += "/#{pkg_version}/latest"
103
+ else
104
+ base_api_uri += "/#{pkg_version}/#{pkg_release}"
105
+ end
106
+
107
+ LicenseScout::Log.debug("[habitat] Fetching pkg_info from #{base_api_uri}")
108
+ FFI_Yajl::Parser.parse(open(base_api_uri).read)
109
+ rescue OpenURI::HTTPError
110
+ pkg_origin, pkg_name, = pkg_ident.split("/")
111
+
112
+ LicenseScout::Log.warn("[habitat] Could not find pkg_info for #{pkg_ident} - trying for the latest version of #{pkg_origin}/#{pkg_name}")
113
+ FFI_Yajl::Parser.parse(open("https://bldr.habitat.sh/v1/depot/channels/#{pkg_origin}/stable/pkgs/#{pkg_name}/latest").read)
114
+ end
115
+ end
116
+
117
+ def plan_sh_path
118
+ File.join(directory, "plan.sh")
119
+ end
120
+
121
+ def habitat_plan_sh_path
122
+ File.join(directory, "habitat", "plan.sh")
123
+ end
124
+ end
125
+ end
126
+ end