license_finder 3.0.0 → 3.0.1

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 (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