licensed 2.7.0 → 2.8.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b2b964bfef1d9dd5d12c96a4586a4e1815d530a744c09484b548e1505a01a22
4
- data.tar.gz: a14d76ad21ab8fb742f698eb0ca90702fb154184b5e887313f8f7a7f894a6437
3
+ metadata.gz: 2613c80ad97c8d19cea10560588a08406388e1860b21c18bfbd322843db428cc
4
+ data.tar.gz: b34fc3f921feccd667898554166d69cb8d7b327e3756c30a3e418364d57cc4a6
5
5
  SHA512:
6
- metadata.gz: 46689f9c144234c03de03c3adc9ee8cf11042d51047bcd83321eac9e1fd9d03a974f0f1757777da831e105e784153d06a4f48ea57b4cbda8079c0221074212f5
7
- data.tar.gz: 5e1fe805637db57b6d16b0cc0f6ff8dfe3d56fd5aa1d70ffc19c06cc41764afe98b20917697de45ca338a22fe3aa1fcfa2491e9c9f90db393b4b35d38de34dc4
6
+ metadata.gz: 720754f1245f1043a2ff4721e0c2b7a403d020cb886277992d1d56a4c2145c588514fdcb18c3a1cd5aaf0d9409a5f839d73691ef0b167fbce69304c9aaf061ed
7
+ data.tar.gz: f33e96346dbb63b48f78de856094992fa4bf58acd5ff811e9193e8e65e71308d91f4a4dc54bfa2b5b80bba1c3fc5348d94454bc00ddb7d012c20ecbed26ba441
@@ -16,6 +16,8 @@ jobs:
16
16
  uses: actions/setup-node@v1
17
17
  with:
18
18
  node-version: 8
19
+ - name: Install Bower
20
+ run: npm install -g bower
19
21
  - name: Set up Ruby
20
22
  uses: actions/setup-ruby@v1
21
23
  with:
@@ -340,3 +342,35 @@ jobs:
340
342
  run: script/source-setup/pipenv
341
343
  - name: Run tests
342
344
  run: script/test pipenv
345
+
346
+ yarn:
347
+ runs-on: ubuntu-latest
348
+ strategy:
349
+ matrix:
350
+ # not using 1.0.0 because it doesn't support `yarn list --production`
351
+ yarn_version: [ 1.4.0, latest ]
352
+ steps:
353
+ - uses: actions/checkout@master
354
+ - name: Setup node
355
+ uses: actions/setup-node@v1
356
+ with:
357
+ node-version: 12
358
+ - name: Install Yarn
359
+ run: npm install -g yarn@${YARN_VERSION}
360
+ env:
361
+ YARN_VERSION: ${{ matrix.yarn_version }}
362
+ - name: Set up Ruby
363
+ uses: actions/setup-ruby@v1
364
+ with:
365
+ ruby-version: 2.6.x
366
+ - run: bundle lock
367
+ - uses: actions/cache@preview
368
+ with:
369
+ path: vendor/gems
370
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
371
+ - name: Bootstrap
372
+ run: script/bootstrap
373
+ - name: Set up fixtures
374
+ run: script/source-setup/yarn
375
+ - name: Run tests
376
+ run: script/test yarn
data/.gitignore CHANGED
@@ -12,26 +12,39 @@
12
12
  test/fixtures/bundler/.bundle/
13
13
  test/fixtures/bundler/vendor/
14
14
  test/fixtures/bundler/Gemfile.lock
15
+
15
16
  test/fixtures/bower/bower_components
17
+
16
18
  test/fixtures/npm/node_modules
17
19
  test/fixtures/npm/package-lock.json
20
+
18
21
  test/fixtures/go/src/*
19
22
  test/fixtures/go/pkg
20
23
  !test/fixtures/go/src/test
21
24
  !test/fixtures/go/src/modules_test
25
+
22
26
  test/fixtures/cabal/*
23
27
  !test/fixtures/cabal/app*
28
+
24
29
  test/fixtures/git_submodule/*
25
30
  !test/fixtures/git_submodule/README
31
+
26
32
  test/fixtures/pip/venv
33
+
27
34
  test/fixtures/pipenv/Pipfile.lock
35
+
28
36
  !test/fixtures/migrations/**/*
37
+
29
38
  test/fixtures/composer/**/*
30
39
  !test/fixtures/composer/composer.json
40
+
31
41
  test/fixtures/mix/_build
32
42
  test/fixtures/mix/deps
33
43
  test/fixtures/mix/mix.lock
34
44
 
45
+ test/fixtures/yarn/*
46
+ !test/fixtures/yarn/package.json
47
+
35
48
  vendor/licenses
36
49
  .licenses
37
50
  *.gem
@@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## 2.8.0
10
+ 2020-01-03
11
+
12
+ ### Added
13
+ - Yarn source (https://github.com/github/licensed/pull/232, https://github.com/github/licensed/pull/233, https://github.com/github/licensed/pull/236)
14
+ - NPM source has a new option to include non-production dependencies (https://github.com/github/licensed/pull/231)
15
+
16
+ ### Fixed
17
+ - Cabal source will no longer crash if packages aren't found (https://github.com/github/licensed/pull/230)
18
+
9
19
  ## 2.7.0
10
20
  2019-11-10
11
21
 
@@ -255,4 +265,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
255
265
 
256
266
  Initial release :tada:
257
267
 
258
- [Unreleased]: https://github.com/github/licensed/compare/2.7.0...HEAD
268
+ [Unreleased]: https://github.com/github/licensed/compare/2.8.0...HEAD
data/README.md CHANGED
@@ -25,11 +25,11 @@ See the [migration documentation](./docs/migrating_to_newer_versions.md) for mor
25
25
 
26
26
  Licensed uses the `libgit2` bindings for Ruby provided by `rugged`. `rugged` requires `cmake` and `pkg-config` which you may need to install before you can install Licensed.
27
27
 
28
- > Ubuntu
28
+ > Ubuntu
29
29
 
30
30
  sudo apt-get install cmake pkg-config
31
31
 
32
- > OS X
32
+ > OS X
33
33
 
34
34
  brew install cmake pkg-config
35
35
 
@@ -110,6 +110,7 @@ Dependencies will be automatically detected for all of the following sources by
110
110
  1. [Pipenv](./docs/sources/pipenv.md)
111
111
  1. [Git Submodules (git_submodule)](./docs/sources/git_submodule.md)
112
112
  1. [Mix](./docs/sources/mix.md)
113
+ 1. [Yarn](./docs/sources/yarn.md)
113
114
 
114
115
  You can disable any of them in the configuration file:
115
116
 
@@ -64,7 +64,7 @@ name: 'My application'
64
64
  # If not set, defaults to a git repository root
65
65
  root: 'relative/path/from/configuration/file/directory'
66
66
 
67
- # Path is relative to configuration root
67
+ # Path is relative to configuration root and specifies where cached metadata will be stored.
68
68
  # If not set, defaults to '.licenses'
69
69
  cache_path: 'relative/path/to/cache'
70
70
 
@@ -78,16 +78,23 @@ sources:
78
78
  bower: true
79
79
  bundler: false
80
80
 
81
- # Dependencies with these licenses are allowed by default.
81
+ # Dependencies with these licenses are allowed and will not raise errors or warnings.
82
+ # This list does not have a default value and is required for `licensed status`
83
+ # to succeed.
82
84
  allowed:
83
85
  - mit
84
86
  - apache-2.0
85
87
  - bsd-2-clause
86
88
  - bsd-3-clause
87
89
  - cc0-1.0
88
-
89
- # These dependencies are explicitly ignored.
90
- # They shouldn't be cached at all, and will not have cached metadata written to the repo.
90
+ - isc
91
+
92
+ # These dependencies are ignored during enumeration.
93
+ # They will not be cached, and will not raise errors or warnings.
94
+ # This configuration is intended to be used for dependencies that don't need to
95
+ # be included for compliance purposes, such as other projects owned by the current
96
+ # project's owner, internal dependencies, and dependencies that aren't shipped with
97
+ # the project like test frameworks.
91
98
  ignored:
92
99
  bundler:
93
100
  - some-internal-gem
@@ -101,8 +108,10 @@ ignored:
101
108
  # comparisons use the FNM_CASEFOLD and FNM_PATHNAME flags
102
109
  - github.com/internal-package/**/*
103
110
 
104
- # These dependencies have been reviewed.
105
- # They need to be cached and checked, but do not have a license found that matches the allowed configured licenses.
111
+ # These dependencies have licenses not on the `allowed` list and have been reviewed.
112
+ # They will be cached and checked, but will not raise errors or warnings for a
113
+ # non-allowed license. Dependencies on this list will still raise errors if
114
+ # license text cannot be found for the dependency.
106
115
  reviewed:
107
116
  bundler:
108
117
  - bcrypt-ruby
@@ -1,3 +1,12 @@
1
1
  # NPM
2
2
 
3
3
  The npm source will detect dependencies `package.json` is found at an apps `source_path`. It uses `npm list` to enumerate dependencies and metadata.
4
+
5
+ ### Including development dependencies
6
+
7
+ By default, the npm source will exclude all non-development dependencies. To include development or test dependencies, set `production_only: false` in the licensed configuration.
8
+
9
+ ```yml
10
+ npm:
11
+ production_only: false
12
+ ```
@@ -0,0 +1,16 @@
1
+ # Yarn
2
+
3
+ The yarn source will detect dependencies when `package.json` and `yarn.lock` are found at an app's `source_path`.
4
+
5
+ It uses `yarn list` to enumerate dependencies and `yarn info` to get metadata on each package.
6
+
7
+ ### Including development dependencies
8
+
9
+ Yarn versions < 1.3.0 will always include non-production dependencies due to a bug in those yarn versions.
10
+
11
+ Starting with yarn version >= 1.3.0, the yarn source excludes non-production dependencies by default. To include development and test dependencies, set `production_only: false` in `.licensed.yml`.
12
+
13
+ ```yml
14
+ yarn:
15
+ production_only: false
16
+ ```
@@ -15,5 +15,6 @@ module Licensed
15
15
  require "licensed/sources/pipenv"
16
16
  require "licensed/sources/gradle"
17
17
  require "licensed/sources/mix"
18
+ require "licensed/sources/yarn"
18
19
  end
19
20
  end
@@ -31,24 +31,25 @@ module Licensed
31
31
 
32
32
  # Returns a list of all detected packages
33
33
  def packages
34
- missing = []
35
34
  package_ids = Set.new
36
35
  cabal_file_dependencies.each do |target|
37
- name, version = target.split(/\s/, 2)
36
+ name = target.split(/\s/)[0]
38
37
  package_id = cabal_package_id(name)
39
38
  if package_id.nil?
40
- missing << { "name" => name, "version" => version, "error" => "package not found" }
39
+ package_ids << target
41
40
  else
42
41
  recursive_dependencies([package_id], package_ids)
43
42
  end
44
43
  end
45
44
 
46
- Parallel.map(package_ids) { |id| package_info(id) }.concat(missing)
45
+ Parallel.map(package_ids) { |id| package_info(id) }
47
46
  end
48
47
 
49
48
  # Returns the packages document directory and search root directory
50
49
  # as an array
51
50
  def package_docs_dirs(package)
51
+ return [nil, nil] if package.nil? || package.empty?
52
+
52
53
  unless package["haddock-html"]
53
54
  # default to a local vendor directory if haddock-html property
54
55
  # isn't available
@@ -78,18 +79,17 @@ module Licensed
78
79
  # Recursively finds the dependencies for each cabal package.
79
80
  # Returns a `Set` containing the package names for all dependencies
80
81
  def recursive_dependencies(package_names, results = Set.new)
81
- return [] if package_names.nil? || package_names.empty?
82
+ return results if package_names.nil? || package_names.empty?
82
83
 
83
84
  new_packages = Set.new(package_names) - results
84
- return [] if new_packages.empty?
85
+ return results if new_packages.empty?
85
86
 
86
87
  results.merge new_packages
87
88
 
88
89
  dependencies = Parallel.map(new_packages, &method(:package_dependencies)).flatten
89
90
 
90
- return results if dependencies.empty?
91
-
92
- results.merge recursive_dependencies(dependencies, results)
91
+ recursive_dependencies(dependencies, results)
92
+ results
93
93
  end
94
94
 
95
95
  # Returns an array of dependency package names for the cabal package
@@ -108,7 +108,10 @@ module Licensed
108
108
 
109
109
  # Returns package information as a hash for the given id
110
110
  def package_info(id)
111
- package_info_command(id).lines.each_with_object({}) do |line, info|
111
+ info = package_info_command(id).strip
112
+ return missing_package(id) if info.empty?
113
+
114
+ info.lines.each_with_object({}) do |line, info|
112
115
  key, value = line.split(":", 2).map(&:strip)
113
116
  next unless key && value
114
117
 
@@ -216,6 +219,17 @@ module Licensed
216
219
  def ghc?
217
220
  @ghc ||= Licensed::Shell.tool_available?("ghc")
218
221
  end
222
+
223
+ # Returns a package info structure with an error set
224
+ def missing_package(id)
225
+ name, _, version = if id.index(/\s/).nil?
226
+ id.rpartition("-") # e.g. to match the right-most dash from ipid fused-effects-1.0.0.0
227
+ else
228
+ id.partition(/\s/) # e.g. to match the left-most space from constraint fused-effects > 1.0.0.0
229
+ end
230
+
231
+ { "name" => name, "version" => version, "error" => "package not found" }
232
+ end
219
233
  end
220
234
  end
221
235
  end
@@ -57,13 +57,20 @@ module Licensed
57
57
 
58
58
  # Returns the output from running `npm list` to get package metadata
59
59
  def package_metadata_command
60
- Licensed::Shell.execute("npm", "list", "--json", "--production", "--long", allow_failure: true)
60
+ args = %w(--json --long)
61
+ args << "--production" unless include_non_production?
62
+ Licensed::Shell.execute("npm", "list", *args, allow_failure: true)
61
63
  end
62
64
 
63
65
  # Returns true if a yarn.lock file exists in the current directory
64
66
  def yarn_lock_present
65
67
  @yarn_lock_present ||= File.exist?(config.pwd.join("yarn.lock"))
66
68
  end
69
+
70
+ # Returns whether to include non production dependencies based on the licensed configuration settings
71
+ def include_non_production?
72
+ config.dig("npm", "production_only") == false
73
+ end
67
74
  end
68
75
  end
69
76
  end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+ require "json"
3
+
4
+ module Licensed
5
+ module Sources
6
+ class Yarn < Source
7
+ # `yarn licenses list --json` returns data in a table format with header
8
+ # ordering specified in the output. Look for these specific headers and use
9
+ # their indices to get data from the table body
10
+ YARN_NAME_HEAD = "Name".freeze
11
+ YARN_VERSION_HEAD = "Version".freeze
12
+ YARN_URL_HEAD = "URL".freeze
13
+
14
+ def enabled?
15
+ return unless Licensed::Shell.tool_available?("yarn")
16
+
17
+ config.pwd.join("package.json").exist? && config.pwd.join("yarn.lock").exist?
18
+ end
19
+
20
+ def enumerate_dependencies
21
+ packages.map do |name, package|
22
+ Dependency.new(
23
+ name: name,
24
+ version: package["version"],
25
+ path: package["path"],
26
+ metadata: {
27
+ "type" => Yarn.type,
28
+ "name" => package["name"],
29
+ "homepage" => dependency_urls[package["id"]]
30
+ }
31
+ )
32
+ end
33
+ end
34
+
35
+ # Finds packages that the current project relies on
36
+ def packages
37
+ return [] if yarn_package_tree.nil?
38
+ all_dependencies = {}
39
+ recursive_dependencies(config.pwd, yarn_package_tree).each do |name, results|
40
+ results.uniq! { |package| package["version"] }
41
+ if results.size == 1
42
+ # if there is only one package for a name, reference it by name
43
+ all_dependencies[name] = results[0]
44
+ else
45
+ # if there is more than one package for a name, reference each by
46
+ # "<name>-<version>"
47
+ results.each do |package|
48
+ all_dependencies["#{name}-#{package["version"]}"] = package
49
+ end
50
+ end
51
+ end
52
+
53
+ all_dependencies
54
+ end
55
+
56
+ # Recursively parse dependency JSON data. Returns a hash mapping the
57
+ # package name to it's metadata
58
+ def recursive_dependencies(path, dependencies, result = {})
59
+ dependencies.each do |dependency|
60
+ # "shadow" indicate a dependency requirement only, not a
61
+ # resolved package identifier
62
+ next if dependency["shadow"]
63
+ name, _, version = dependency["name"].rpartition("@")
64
+
65
+ # the dependency should be found under the parent's "node_modules" path
66
+ dependency_path = path.join("node_modules", name)
67
+ (result[name] ||= []) << {
68
+ "id" => dependency["name"],
69
+ "name" => name,
70
+ "version" => version,
71
+ "path" => dependency_path
72
+ }
73
+ recursive_dependencies(dependency_path, dependency["children"], result)
74
+ end
75
+ result
76
+ end
77
+
78
+ # Finds and returns the yarn package tree listing from `yarn list` output
79
+ def yarn_package_tree
80
+ return @yarn_package_tree if defined?(@yarn_package_tree)
81
+ @yarn_package_tree = begin
82
+ # parse all lines of output to json and find one that is "type": "tree"
83
+ tree = yarn_list_command.lines
84
+ .map(&:strip)
85
+ .map(&JSON.method(:parse))
86
+ .find { |json| json["type"] == "tree" }
87
+ tree&.dig("data", "trees")
88
+ end
89
+ end
90
+
91
+ # Returns a mapping of unique dependency identifiers to urls
92
+ def dependency_urls
93
+ @dependency_urls ||= begin
94
+ table = yarn_licenses_command.lines
95
+ .map(&:strip)
96
+ .map(&JSON.method(:parse))
97
+ .find { |json| json["type"] == "table" }
98
+ return [] if table.nil?
99
+
100
+ head = table.dig("data", "head")
101
+ return [] if head.nil?
102
+
103
+ name_index = head.index YARN_NAME_HEAD
104
+ version_index = head.index YARN_VERSION_HEAD
105
+ url_index = head.index YARN_URL_HEAD
106
+ return [] if name_index.nil? || version_index.nil? || url_index.nil?
107
+
108
+ body = table.dig("data", "body")
109
+ return [] if body.nil?
110
+
111
+ body.each_with_object({}) do |row, hsh|
112
+ id = "#{row[name_index]}@#{row[version_index]}"
113
+ hsh[id] = row[url_index]
114
+ end
115
+ end
116
+ end
117
+
118
+ # Returns the output from running `yarn list` to get project dependencies
119
+ def yarn_list_command
120
+ args = %w(--json -s --no-progress)
121
+ args << "--production" unless include_non_production?
122
+ Licensed::Shell.execute("yarn", "list", *args, allow_failure: true)
123
+ end
124
+
125
+ # Returns the output from running `yarn licenses list` to get project urls
126
+ def yarn_licenses_command
127
+ args = %w(--json -s --no-progress)
128
+ args << "--production" unless include_non_production?
129
+ Licensed::Shell.execute("yarn", "licenses", "list", *args, allow_failure: true)
130
+ end
131
+
132
+ # Returns whether to include non production dependencies based on the licensed configuration settings
133
+ def include_non_production?
134
+ config.dig("yarn", "production_only") == false
135
+ end
136
+ end
137
+ end
138
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Licensed
3
- VERSION = "2.7.0".freeze
3
+ VERSION = "2.8.0".freeze
4
4
 
5
5
  def self.previous_major_versions
6
6
  major_version = Gem::Version.new(Licensed::VERSION).segments.first
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ if [ -z "$(which yarn)" ]; then
5
+ echo "A local yarn installation is required for yarn development." >&2
6
+ exit 127
7
+ fi
8
+
9
+ # setup test fixtures
10
+ BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
11
+ cd $BASE_PATH/test/fixtures/yarn
12
+
13
+ if [ "$1" == "-f" ]; then
14
+ find . -not -regex "\.*" -and -not -name "package\.json" -print0 | xargs -0 rm -rf
15
+ fi
16
+
17
+ yarn install
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: licensed
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.0
4
+ version: 2.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-10 00:00:00.000000000 Z
11
+ date: 2020-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: licensee
@@ -254,6 +254,7 @@ files:
254
254
  - docs/sources/pip.md
255
255
  - docs/sources/pipenv.md
256
256
  - docs/sources/stack.md
257
+ - docs/sources/yarn.md
257
258
  - exe/licensed
258
259
  - lib/licensed.rb
259
260
  - lib/licensed/cli.rb
@@ -293,6 +294,7 @@ files:
293
294
  - lib/licensed/sources/pip.rb
294
295
  - lib/licensed/sources/pipenv.rb
295
296
  - lib/licensed/sources/source.rb
297
+ - lib/licensed/sources/yarn.rb
296
298
  - lib/licensed/ui/shell.rb
297
299
  - lib/licensed/version.rb
298
300
  - licensed.gemspec
@@ -314,6 +316,7 @@ files:
314
316
  - script/source-setup/npm
315
317
  - script/source-setup/pip
316
318
  - script/source-setup/pipenv
319
+ - script/source-setup/yarn
317
320
  - script/test
318
321
  homepage: https://github.com/github/licensed
319
322
  licenses: