license_scout 1.3.11 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
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 -1120
  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