license_finder 3.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/CONTRIBUTING.md +17 -3
  4. data/Dockerfile +12 -15
  5. data/README.md +3 -1
  6. data/Rakefile +7 -0
  7. data/ci/pipelines/pipeline.yml.erb +2 -2
  8. data/features/features/cli/cli_spec.rb +4 -3
  9. data/features/support/testing_dsl.rb +9 -2
  10. data/lib/license_finder/cli/base.rb +1 -1
  11. data/lib/license_finder/cli/main.rb +7 -0
  12. data/lib/license_finder/configuration.rb +4 -0
  13. data/lib/license_finder/core.rb +2 -2
  14. data/lib/license_finder/decision_applier.rb +8 -4
  15. data/lib/license_finder/package_managers/cocoa_pods.rb +6 -10
  16. data/lib/license_finder/package_managers/go_vendor.rb +2 -0
  17. data/lib/license_finder/package_managers/go_workspace.rb +2 -0
  18. data/lib/license_finder/package_managers/maven.rb +2 -1
  19. data/lib/license_finder/package_managers/npm.rb +66 -58
  20. data/lib/license_finder/package_managers/npm_package.rb +0 -14
  21. data/lib/license_finder/package_managers/nuget.rb +6 -1
  22. data/lib/license_finder/reports/csv_report.rb +6 -2
  23. data/lib/license_finder/version.rb +1 -1
  24. data/license_finder.gemspec +1 -0
  25. data/spec/fixtures/npm-circular-licenses/npm-list.json +7597 -0
  26. data/spec/fixtures/npm-circular-licenses/package.json +23 -0
  27. data/spec/lib/license_finder/cli/main_spec.rb +31 -7
  28. data/spec/lib/license_finder/core_spec.rb +1 -0
  29. data/spec/lib/license_finder/decision_applier_spec.rb +8 -0
  30. data/spec/lib/license_finder/package_managers/go_dep_spec.rb +17 -16
  31. data/spec/lib/license_finder/package_managers/npm_spec.rb +37 -3
  32. data/spec/lib/license_finder/package_managers/nuget_spec.rb +12 -1
  33. metadata +21 -5
  34. data/spec/lib/license_finder/package_managers/npm_package_spec.rb +0 -56
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 966af7ae1414671e4b0993c96df55ae99b57993f
4
- data.tar.gz: '0319c59b0c751aae09e8822c30e6a27aa7a9d473'
3
+ metadata.gz: 499f2da3bda92418516368e51583453fc1c5225f
4
+ data.tar.gz: a8b35a1beb522d75a1cb573e68be86fe7bd0f921
5
5
  SHA512:
6
- metadata.gz: 7fa60bd67436b53b6de9ee386c6301bcc485a8076a5cea9c464a3f7e581ce23820246f0ca4a603875fcda88409632b7bb29973c4c4d8590b8a0bd1c100cf699b
7
- data.tar.gz: 5317b4995bbfb33a9d5aaa9364e138eb9723d90aee8bd2cd102380fe078b91d32ee25c565ac4c356d572f9cfcec089c84a46c6a5b7da916c66a78e9b2c919602
6
+ metadata.gz: f1986e42151dfa364cfe7e10500b5fda295f957ae1fd62f1605067aac46bdfca0c8b596d9ded82b3192ddfecea804a312cb0cf7d6314e6713ea8f03a2ddf6bdd
7
+ data.tar.gz: fb51163eeb86d7e19fd77de0f26f5930048c8a7a8441a50db4b852f90539ce0402109acb375c87324054ed1be99a51d77a71ca108ba8d019e13fb8f189014f47
@@ -1,3 +1,18 @@
1
+ # Not Yet Released:
2
+
3
+ Features:
4
+
5
+ * Add --maven-options to allow options for maven scans (#305, thanks @jgielstra!)
6
+
7
+ Bugfixes:
8
+
9
+ * Restore the original GOPATH after modifying it (#287, thanks @sschuberth!)
10
+ * LF doesn't recognize .NET projects using 'packages' directory (#290, #292, thanks @bspeck!)
11
+ * Use glob for finding acknowledgements path for CocoaPods (#177, #288, thanks @aditya87!)
12
+ * Fix some failing tests on Windows (#294, thanks @sschuberth!)
13
+ * Add warning message if no dependencies are recognized (#293, thanks @bspeck!)
14
+ * Switch to YAJL for parsing the json output from npm using a tmp file rather than an in-memory string (#301, #304)
15
+
1
16
  # 3.0.0 / 2016-03-02
2
17
 
3
18
  Features:
@@ -8,6 +8,18 @@
8
8
  * Rebase on top of master.
9
9
  * Send a pull request.
10
10
 
11
+ ## Running Tests
12
+
13
+ You can use the [LicenseFinder docker image](https://hub.docker.com/r/licensefinder/license_finder/) to run the tests.
14
+
15
+ ```
16
+ $ docker run -it licensefinder/license_finder /bin/bash --login
17
+
18
+ # inside the container...
19
+
20
+ $ cd /LicenseFinder
21
+ $ rake
22
+ ```
11
23
 
12
24
  ## Adding Package Managers
13
25
 
@@ -44,7 +56,8 @@ If you come up with something useful, consider posting it to the Google Group
44
56
 
45
57
 
46
58
  ## Development Dependencies
47
- To successfully run the test suite, you will need the following installed:
59
+
60
+ To successfully run the test suite, you will need the following installed:
48
61
  - NPM (requires Node)
49
62
  - Bower (requires Node and NPM)
50
63
  - Maven (requires Java)
@@ -55,8 +68,9 @@ To successfully run the test suite, you will need the following installed:
55
68
  - CocoaPods (requires ruby)
56
69
  - Bundler (requires ruby)
57
70
 
58
- If you run `rake check_dependencies`, you'll see exactly which package managers
59
- you're missing.
71
+ The [LicenseFinder docker image](https://hub.docker.com/r/licensefinder/license_finder/) already contains these dependencies.
72
+
73
+ If you run `rake check_dependencies`, you'll see exactly which package managers you're missing.
60
74
 
61
75
  ### Python
62
76
 
data/Dockerfile CHANGED
@@ -1,32 +1,21 @@
1
1
  FROM ubuntu:trusty
2
- RUN apt-get update && apt-get install -y curl git-core
3
-
4
- #install rvm
5
- RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 && \
6
- curl -sSL https://get.rvm.io | bash -s stable --ruby
7
- ENV PATH=/usr/local/rvm/bin:$PATH
8
-
9
- # install build-essential wget unzip
10
- RUN apt-get install -y build-essential wget unzip
2
+ RUN apt-get update && apt-get install -y curl git-core wget unzip
11
3
 
12
4
  # nodejs seems to be required for the one of the gems
13
- RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - && \
5
+ RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
14
6
  apt-get -y install nodejs
15
7
 
16
8
  # install bower
17
9
  RUN npm install -g bower && \
18
10
  echo '{ "allow_root": true }' > /root/.bowerrc
19
11
 
20
- # install bundler
21
- RUN bash -lc "rvm install 2.3.3 && rvm use 2.3.3 && gem install bundler"
22
-
23
12
  #install java 8
24
13
  #http://askubuntu.com/questions/521145/how-to-install-oracle-java-on-ubuntu-14-04
25
14
  RUN cd /tmp && \
26
- wget --quiet --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-linux-x64.tar.gz -O jdk-8.tgz && \
15
+ wget --quiet --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u131-linux-x64.tar.gz -O jdk-8.tgz && \
27
16
  tar xf /tmp/jdk-8.tgz && \
28
17
  mkdir -p /usr/lib/jvm && \
29
- mv jdk1.8.0_66 /usr/lib/jvm/oracle_jdk8 && \
18
+ mv jdk1.8.0_131 /usr/lib/jvm/oracle_jdk8 && \
30
19
  rm /tmp/jdk-8.tgz
31
20
 
32
21
  ENV J2SDKDIR=/usr/lib/jvm/oracle_jdk8
@@ -76,6 +65,14 @@ ENV LANG=en_US.UTF-8
76
65
  ENV LANGUAGE=en_US:en
77
66
  ENV LC_ALL=en_US.UTF-8
78
67
 
68
+ #install rvm
69
+ RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 && \
70
+ curl -sSL https://raw.githubusercontent.com/wayneeseguin/rvm/stable/binscripts/rvm-installer | sudo bash -s stable --ruby=2.4.1
71
+ ENV PATH=/usr/local/rvm/bin:$PATH
72
+
73
+ # install bundler
74
+ RUN bash -lc "rvm install 2.4.1 --default && gem install bundler"
75
+
79
76
  # install license_finder
80
77
  RUN bash -lc "git clone https://github.com/pivotal/LicenseFinder /LicenseFinder && cd /LicenseFinder && bundle install -j4 && rake install"
81
78
 
data/README.md CHANGED
@@ -6,7 +6,7 @@ Build status
6
6
  * Ruby 2.1.5 [![Ruby 2.1.5 build status](https://osl.ci.cf-app.com/api/v1/teams/main/pipelines/LicenseFinder/jobs/ruby-2.1.5/badge)](https://osl.ci.cf-app.com/teams/main/pipelines/LicenseFinder)
7
7
  * Ruby 2.2.0 [![Ruby 2.2.0 build status](https://osl.ci.cf-app.com/api/v1/teams/main/pipelines/LicenseFinder/jobs/ruby-2.2.0/badge)](https://osl.ci.cf-app.com/teams/main/pipelines/LicenseFinder)
8
8
  * Ruby 2.3.0 [![Ruby 2.3.0 build status](https://osl.ci.cf-app.com/api/v1/teams/main/pipelines/LicenseFinder/jobs/ruby-2.3.0/badge)](https://osl.ci.cf-app.com/teams/main/pipelines/LicenseFinder)
9
- * Ruby 2.4.0 [![Ruby 2.4.0 build status](https://osl.ci.cf-app.com/api/v1/teams/main/pipelines/LicenseFinder/jobs/ruby-2.4.0/badge)](https://osl.ci.cf-app.com/teams/main/pipelines/LicenseFinder)
9
+ * Ruby 2.4.1 [![Ruby 2.4.1 build status](https://osl.ci.cf-app.com/api/v1/teams/main/pipelines/LicenseFinder/jobs/ruby-2.4.1/badge)](https://osl.ci.cf-app.com/teams/main/pipelines/LicenseFinder)
10
10
  * JRuby 9.0.4.0 [![JRuby 9.0.4.0 build status](https://osl.ci.cf-app.com/api/v1/teams/main/pipelines/LicenseFinder/jobs/ruby-jruby-9.0.4.0/badge)](https://osl.ci.cf-app.com/teams/main/pipelines/LicenseFinder)
11
11
 
12
12
 
@@ -17,6 +17,8 @@ report.
17
17
 
18
18
  * code: https://github.com/pivotal/LicenseFinder
19
19
  * ci: https://osl.ci.cf-app.com/teams/main/pipelines/LicenseFinder
20
+ * docker: [licensefinder/license_finder](https://hub.docker.com/r/licensefinder/license_finder/)
21
+ * the docker image contains all the package managers needed to run `license_finder`
20
22
  * support:
21
23
  * license-finder@googlegroups.com
22
24
  * https://groups.google.com/forum/#!forum/license-finder
data/Rakefile CHANGED
@@ -13,6 +13,13 @@ task :spec do
13
13
  end
14
14
  end
15
15
 
16
+ desc "Only run cocoapods specs"
17
+ RSpec::Core::RakeTask.new("spec:cocoapods") do |t|
18
+ t.fail_on_error = true
19
+ t.pattern = "./spec/lib/license_finder/package_managers/cocoa_pods_*spec.rb"
20
+ t.rspec_opts = %w[--color]
21
+ end
22
+
16
23
  desc "Run all specs in features/"
17
24
  task :features do
18
25
  RSpec::Core::RakeTask.new(:features) do |t|
@@ -35,7 +35,7 @@ resources:
35
35
  <% end %>
36
36
 
37
37
  jobs:
38
- <% ['2.4.0', '2.3.0', '2.2.0', '2.1.5', 'jruby-9.0.4.0'].each do |ruby_version| %>
38
+ <% ['2.4.1', '2.3.0', '2.2.0', '2.1.5', 'jruby-9.0.4.0'].each do |ruby_version| %>
39
39
  - name: ruby-<%= ruby_version %>
40
40
  public: true
41
41
  plan:
@@ -55,7 +55,7 @@ jobs:
55
55
  <% end %>
56
56
  <% end %>
57
57
 
58
- <% ['2.4.0', '2.3.0', '2.2.0', '2.1.5', 'jruby-9.0.4.0'].each do |ruby_version| %>
58
+ <% ['2.4.1', '2.3.0', '2.2.0', '2.1.5', 'jruby-9.0.4.0'].each do |ruby_version| %>
59
59
  - name: PR-ruby-<%= ruby_version %>
60
60
  public: true
61
61
  plan:
@@ -36,14 +36,15 @@ describe "License Finder command line executable" do
36
36
 
37
37
  developer.run_license_finder
38
38
  expect(developer).to be_receiving_exit_code(0)
39
- expect(developer).to be_seeing 'All dependencies are approved for use'
39
+ expect(developer).to be_seeing 'No dependencies recognized!'
40
40
  end
41
41
 
42
42
  specify "displays an error if project_path does not exist" do
43
43
  developer.create_empty_project
44
44
 
45
- developer.execute_command('license_finder report --project-path=/path/that/does/not/exist')
46
- expect(developer).to be_seeing("Project path '/path/that/does/not/exist' does not exist!")
45
+ path = '/path/that/does/not/exist'
46
+ developer.execute_command("license_finder report --project-path=#{path}")
47
+ expect(developer).to be_seeing("Project path '#{File.absolute_path(path)}' does not exist!")
47
48
  expect(developer).to be_receiving_exit_code(1)
48
49
  end
49
50
  end
@@ -181,7 +181,10 @@ module LicenseFinder
181
181
  end
182
182
 
183
183
  def install
184
- shell_out("GOPATH=#{project_dir}/gopath godep restore")
184
+ orig_gopath = ENV['GOPATH']
185
+ ENV['GOPATH'] = "#{project_dir}/gopath"
186
+ shell_out("godep restore")
187
+ ENV['GOPATH'] = orig_gopath
185
188
  end
186
189
 
187
190
  def shell_out(command)
@@ -348,7 +351,11 @@ module LicenseFinder
348
351
  end
349
352
 
350
353
  def install_fixture(fixture_name)
351
- join(fixture_name).make_symlink Paths.fixtures.join(fixture_name)
354
+ if Platform.windows?
355
+ FileUtils.cp(Paths.fixtures.join(fixture_name), join(fixture_name))
356
+ else
357
+ join(fixture_name).make_symlink Paths.fixtures.join(fixture_name)
358
+ end
352
359
  end
353
360
 
354
361
  def make
@@ -32,6 +32,7 @@ module LicenseFinder
32
32
  :gradle_command,
33
33
  :gradle_include_groups,
34
34
  :maven_include_groups,
35
+ :maven_options,
35
36
  :rebar_command,
36
37
  :rebar_deps_dir,
37
38
  :save
@@ -70,4 +71,3 @@ module LicenseFinder
70
71
  end
71
72
  end
72
73
  end
73
-
@@ -24,6 +24,7 @@ module LicenseFinder
24
24
  class_option :gradle_include_groups, desc: "Whether dependency name should include group id. Only meaningful if used with a Java/gradle project. Defaults to false."
25
25
  class_option :gradle_command, desc: "Command to use when fetching gradle packages. Only meaningful if used with a Java/gradle project. Defaults to 'gradlew' / 'gradlew.bat' if the wrapper is present, otherwise to 'gradle'."
26
26
  class_option :maven_include_groups, desc: "Whether dependency name should include group id. Only meaningful if used with a Java/maven project. Defaults to false."
27
+ class_option :maven_options, desc: "Maven options to append to command. Defaults to empty."
27
28
  class_option :rebar_command, desc: "Command to use when fetching rebar packages. Only meaningful if used with a Erlang/rebar project. Defaults to 'rebar'."
28
29
  class_option :rebar_deps_dir, desc: "Path to rebar dependencies directory. Only meaningful if used with a Erlang/rebar project. Defaults to 'deps'."
29
30
  class_option :subprojects, type: :array, desc: "Generate a single report for multiple sub-projects. Ex: --subprojects='path/to/project1', 'path/to/project2'"
@@ -34,12 +35,18 @@ module LicenseFinder
34
35
  desc "action_items", "List unapproved dependencies (the default action for `license_finder`)"
35
36
 
36
37
  def action_items
38
+ any_packages = license_finder.any_packages?
37
39
  unapproved = license_finder.unapproved
38
40
  blacklisted = license_finder.blacklisted
39
41
 
40
42
  # Ensure to start output on a new line even with dot progress indicators.
41
43
  say "\n"
42
44
 
45
+ unless any_packages
46
+ say "No dependencies recognized!", :red
47
+ exit 0
48
+ end
49
+
43
50
  if unapproved.empty?
44
51
  say "All dependencies are approved for use", :green
45
52
  else
@@ -37,6 +37,10 @@ module LicenseFinder
37
37
  get(:maven_include_groups)
38
38
  end
39
39
 
40
+ def maven_options
41
+ get(:maven_options)
42
+ end
43
+
40
44
  def rebar_command
41
45
  get(:rebar_command)
42
46
  end
@@ -37,7 +37,7 @@ module LicenseFinder
37
37
  end
38
38
 
39
39
  extend Forwardable
40
- def_delegators :decision_applier, :acknowledged, :unapproved, :blacklisted
40
+ def_delegators :decision_applier, :acknowledged, :unapproved, :blacklisted, :any_packages?
41
41
 
42
42
  def project_name
43
43
  decisions.project_name || config.project_path.basename.to_s
@@ -68,10 +68,10 @@ module LicenseFinder
68
68
  gradle_command: config.gradle_command,
69
69
  gradle_include_groups: config.gradle_include_groups,
70
70
  maven_include_groups: config.maven_include_groups,
71
+ maven_options: config.maven_options,
71
72
  rebar_command: config.rebar_command,
72
73
  rebar_deps_dir: config.rebar_deps_dir,
73
74
  )
74
75
  end
75
76
  end
76
77
  end
77
-
@@ -2,7 +2,8 @@ module LicenseFinder
2
2
  class DecisionApplier
3
3
  def initialize(options)
4
4
  @decisions = options.fetch(:decisions)
5
- @acknowledged = apply_decisions(options.fetch(:packages))
5
+ @all_packages = decisions.packages + options.fetch(:packages)
6
+ @acknowledged = apply_decisions
6
7
  end
7
8
 
8
9
  attr_reader :acknowledged
@@ -15,12 +16,15 @@ module LicenseFinder
15
16
  acknowledged.select(&:blacklisted?)
16
17
  end
17
18
 
19
+ def any_packages?
20
+ all_packages.any?
21
+ end
22
+
18
23
  private
19
24
 
20
- attr_reader :decisions
25
+ attr_reader :decisions, :all_packages
21
26
 
22
- def apply_decisions(system_packages)
23
- all_packages = decisions.packages + system_packages
27
+ def apply_decisions
24
28
  all_packages
25
29
  .map { |package| with_decided_licenses(package) }
26
30
  .map { |package| with_approval(package) }
@@ -40,19 +40,15 @@ module LicenseFinder
40
40
  end
41
41
 
42
42
  def acknowledgements_path
43
- filename = 'Pods-acknowledgements.plist'
44
- directories = [
45
- 'Pods', # cocoapods < 0.34
46
- 'Pods/Target Support Files/Pods' # cocoapods >= 0.34
47
- ]
48
-
49
- directories
50
- .map { |dir| project_path.join(dir, filename) }
51
- .find(&:exist?)
43
+ search_paths = [ "Pods/Pods-acknowledgements.plist",
44
+ "Pods/Target Support Files/Pods/Pods-acknowledgements.plist",
45
+ "Pods/Target Support Files/Pods-*/Pods-*-acknowledgements.plist" ]
46
+
47
+ Dir[*search_paths.map {|path| File.join(project_path, path) }].first
52
48
  end
53
49
 
54
50
  def read_plist pathname
55
- JSON.parse(`plutil -convert json -o - '#{pathname.expand_path}'`)
51
+ JSON.parse(`plutil -convert json -o - '#{pathname}'`)
56
52
  end
57
53
  end
58
54
  end
@@ -55,8 +55,10 @@ module LicenseFinder
55
55
  # checked in. Canonical paths are only checked by `go get'. We
56
56
  # discovered that `go list' will print a warning and unfortunately exit
57
57
  # with status code 1. Setting GOPATH to nil removes those warnings.
58
+ orig_gopath = ENV['GOPATH']
58
59
  ENV['GOPATH'] = nil
59
60
  val = capture('go list -f "{{join .Deps \"\n\"}}" ./...')
61
+ ENV['GOPATH'] = orig_gopath
60
62
  return [] unless val.last
61
63
  # Select non-standard packages. `go list std` returns the list of standard
62
64
  # dependencies. We then filter those dependencies out of the full list of
@@ -70,8 +70,10 @@ module LicenseFinder
70
70
  # checked in. Canonical paths are only checked by `go get'. We
71
71
  # discovered that `go list' will print a warning and unfortunately exit
72
72
  # with status code 1. Setting GOPATH to nil removes those warnings.
73
+ orig_gopath = ENV['GOPATH']
73
74
  ENV['GOPATH'] = nil
74
75
  val = capture('go list -f "{{join .Deps \"\n\"}}" ./...')
76
+ ENV['GOPATH'] = orig_gopath
75
77
  raise 'go list failed' unless val.last
76
78
  # Select non-standard packages. `go list std` returns the list of standard
77
79
  # dependencies. We then filter those dependencies out of the full list of
@@ -7,12 +7,13 @@ module LicenseFinder
7
7
  super
8
8
  @ignored_groups = options[:ignored_groups]
9
9
  @include_groups = options[:maven_include_groups]
10
+ @maven_options = options[:maven_options]
10
11
  end
11
12
 
12
13
  def current_packages
13
14
  command = "#{package_management_command} org.codehaus.mojo:license-maven-plugin:download-licenses"
14
15
  command += " -Dlicense.excludedScopes=#{@ignored_groups.to_a.join(',')}" if @ignored_groups and !@ignored_groups.empty?
15
-
16
+ command += " #{@maven_options}" if !@maven_options.nil?
16
17
  output, success = Dir.chdir(project_path) { capture(command) }
17
18
  raise "Command '#{command}' failed to execute: #{output}" unless success
18
19
 
@@ -1,88 +1,96 @@
1
- require 'json'
1
+ require 'yajl'
2
+ require 'tempfile'
2
3
 
3
4
  module LicenseFinder
4
5
  class NPM < PackageManager
5
6
  DEPENDENCY_GROUPS = ["dependencies", "devDependencies"]
6
7
 
7
8
  def current_packages
8
- packages = {}
9
- direct_dependencies.each do |dep|
10
- group_name = dep[:group]
11
- walk_dependency_tree(dep[:name]) do |dependency|
12
- package_id = dependency["name"]
13
- if packages[package_id] && packages[package_id].version.nil? && dependency["version"]
14
- old_package = packages[package_id]
15
- packages[package_id] = NpmPackage.new(dependency, logger: logger, groups: old_package.groups)
16
- else
17
- packages[package_id] ||= NpmPackage.new(dependency, logger: logger)
18
- end
19
- packages[package_id].groups << group_name unless packages[package_id].groups.include?(group_name)
9
+ top_level_deps = npm_json['dependencies']&.values || []
10
+ package_json = JSON.parse(File.read(package_path), :max_nesting => false)
11
+ top_level_deps.each do |dep|
12
+ dep['groups'] = DEPENDENCY_GROUPS.select do |group|
13
+ package_json[group]&.keys&.include? dep['name']
20
14
  end
21
15
  end
22
- packages.values
23
- end
24
-
25
- def self.package_management_command
26
- "npm"
16
+ dependency_matches = flatten(top_level_deps)
17
+ dependency_matches = dependency_matches.group_by {|dep| [dep['name'], dep['version']]}
18
+ dependency_matches.map {|id, dep_matches| construct_npm_package(dep_matches, id)}
27
19
  end
28
20
 
29
21
  private
30
22
 
31
- def direct_dependencies
32
- package_json = JSON.parse(File.read(package_path), :max_nesting => false)
33
- DEPENDENCY_GROUPS.map do |group|
34
- package_json.fetch(group, {}).keys.map do |dependency|
35
- {
36
- group: group,
37
- name: dependency
38
- }
39
- end
40
- end.flatten
23
+ def self.package_management_command
24
+ "npm"
41
25
  end
42
26
 
43
- def walk_dependency_tree(dependency, &block)
44
- @json ||= npm_json
45
- deps = @json.fetch("dependencies", {}).reject { |_,d| d.is_a?(String) }
46
- current_dep = deps[dependency]
47
- block.call(current_dep) if current_dep
48
- recursive_dependencies(current_dep) do |d|
49
- block.call(d)
50
- end
27
+ def package_path
28
+ project_path.join('package.json')
51
29
  end
52
30
 
53
31
  def npm_json
54
- command = "#{NPM::package_management_command} list --json --long"
55
- output, success = Dir.chdir(project_path) { capture(command) }
32
+ tempfile = Tempfile.new
33
+ begin
34
+ command = "#{NPM::package_management_command} list --json --long > #{tempfile.path}"
35
+ output, success = Dir.chdir(project_path) { capture(command) }
56
36
 
57
- if success
58
- json = JSON(output, :max_nesting => false)
59
- else
60
- json = begin
61
- JSON(output, :max_nesting => false)
62
- rescue JSON::ParserError
63
- nil
64
- end
65
- if json
66
- $stderr.puts "Command '#{command}' returned an error but parsing succeeded."
37
+ if success
38
+ json = Yajl::Parser.parse(File.open(tempfile.path))
67
39
  else
68
- raise "Command '#{command}' failed to execute: #{output}"
40
+ json = begin
41
+ Yajl::Parser.parse(File.open(tempfile.path))
42
+ rescue Yajl::ParseError
43
+ nil
44
+ end
45
+ if json
46
+ $stderr.puts "Command '#{command}' returned an error but parsing succeeded."
47
+ else
48
+ raise "Command '#{command}' failed to execute: #{output}"
49
+ end
69
50
  end
51
+ ensure
52
+ tempfile.close
53
+ tempfile.unlink
70
54
  end
71
-
72
55
  json
73
56
  end
74
57
 
75
- def package_path
76
- project_path.join('package.json')
58
+ def flatten(list)
59
+ list.inject [] {|acc, dep| acc + [dep] + flatten(dep['dependencies']&.values&.map {|inner_dep| inner_dep['groups'] = dep['groups']; inner_dep} || [])}
77
60
  end
78
61
 
79
- def recursive_dependencies(node_module, &block)
80
- return unless node_module # node_module can be empty hash if it is included elsewhere
81
- block.call(node_module)
82
- node_module.fetch('dependencies', {}).each do |dep_key, data|
83
- data['name'] ||= dep_key
84
- recursive_dependencies(data, &block)
62
+ def licenses(dep_matches)
63
+ licenses_lists = dep_matches.map do |match|
64
+ match['licenses']
85
65
  end
66
+
67
+ licenses_lists.reject! {|licenses| licenses.nil? || licenses == '[Circular]'}
68
+ licenses_lists = licenses_lists.uniq
69
+ case licenses_lists.count
70
+ when 0
71
+ dep_matches.map {|dep_match| dep_match['license']}.reject(&:nil?).uniq
72
+ when 1
73
+ licenses_lists.first.map {|license| license['type']}
74
+ else
75
+ raise "Varying lists of licenses provided for #{dep_matches.first['name']} (#{dep_matches.first['version']})"
76
+ end
77
+ end
78
+
79
+ def construct_npm_package(dep_matches, id)
80
+ name, version = id
81
+ dependencies = value_from_matches(dep_matches, 'dependencies')&.values&.map {|d| d['name']} || []
82
+ NpmPackage.new(name, version,
83
+ homepage: value_from_matches(dep_matches, 'homepage'),
84
+ description: value_from_matches(dep_matches, 'description'),
85
+ spec_licenses: licenses(dep_matches),
86
+ install_path: value_from_matches(dep_matches, 'path'),
87
+ dependencies: dependencies,
88
+ groups: dep_matches.map {|match| match['groups']}.flatten.uniq,
89
+ logger: logger)
90
+ end
91
+
92
+ def value_from_matches(dep_matches, key)
93
+ dep_matches.map {|m| m[key]}.reject(&:nil?).uniq&.first
86
94
  end
87
95
  end
88
96
  end