licensed 3.3.1 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 204627468559ebbf7283c41b74374a244f5aed5c885c4b9dcfc6fe59a8443c4a
4
- data.tar.gz: f6db3198bf7bd592e8e1512803e408091dfe6230984c9aa166bbdf7d16edb1df
3
+ metadata.gz: a3cb5bf8c20bf1177466536fbc30bc29135a9e76933e225af8353ba8eb89205d
4
+ data.tar.gz: 26acda6f4b9d90c2457725ecda8f0b00bc7fc793a4993a1e6aa9a6810a995d04
5
5
  SHA512:
6
- metadata.gz: 5669804505cd5277a292eb51b6d46beda9f9a16e9ede968855ab9df95cdd45bd43564c15fdc45e80a9fe109c190584b93078f45588de78c689f0a93c86278bec
7
- data.tar.gz: 7c57aa1d0a8bbe39860464a5efcec2d72bf06bd61b6b018c3b856547e7e0a6900e3dba4a440a41fed211b71b368a300d453a811f6fd11b65d82aa612bb7b203a
6
+ metadata.gz: 5d71df74f6fca5c309230b18a7423f608cdf61ca0a5ddf9f45116b75681196a15ecee70d4d56877841b82c1d75d43fa573182ec300f1976a1e815f169de1598f
7
+ data.tar.gz: 645c4bf3cbb162e46061b730f947f0299336506fa425640f215e0e96ccb89124ef0e8a9725e6b9044fcd1c8e38c8809b7f5a911479d6583cc097b15d1f4a48c3
data/CHANGELOG.md CHANGED
@@ -6,6 +6,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## 3.4.0
10
+
11
+ 2021-12-14
12
+
13
+ ### Added
14
+
15
+ - New Yarn enumerator with support for berry versions (https://github.com/github/licensed/pull/423)
16
+
17
+ ### Fixed
18
+
19
+ - Error handling cases return correct values in the Yarn enumerator (https://github.com/github/licensed/pull/425)
20
+ - Fixed link in command documentation (:tada: @chibicco https://github.com/github/licensed/pull/416)
21
+ - Fixed minor backwards compatibility issue for Ruby 2.3 support (:tada: @dzunk https://github.com/github/licensed/pull/414)
22
+
23
+ ### Changed
24
+
25
+ - Licensed's own dependencies are cached in the repository and kept up to date with GitHub Actions (https://github.com/github/licensed/pull/421)
26
+
9
27
  ## 3.3.1
10
28
 
11
29
  2021-10-07
@@ -521,4 +539,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
521
539
 
522
540
  Initial release :tada:
523
541
 
524
- [Unreleased]: https://github.com/github/licensed/compare/3.3.1...HEAD
542
+ [Unreleased]: https://github.com/github/licensed/compare/3.4.0...HEAD
data/Rakefile CHANGED
@@ -2,13 +2,16 @@
2
2
  require "bundler/gem_tasks"
3
3
  require "rake/testtask"
4
4
  require "rubocop/rake_task"
5
+ require "licensed"
5
6
 
6
7
  desc "Run source setup scripts"
7
8
  task :setup, [:arguments] do |task, args|
8
9
  arguments = args[:arguments].to_s.split
9
10
  force = arguments.include?("-f") ? "-f" : ""
10
11
 
11
- Dir["script/source-setup/*"].each do |script|
12
+ Dir["script/source-setup/**/*"].each do |script|
13
+ next if File.directory?(script)
14
+
12
15
  # green
13
16
  puts "\033[32mRunning #{script}.\e[0m"
14
17
 
@@ -27,8 +30,7 @@ task :setup, [:arguments] do |task, args|
27
30
  end
28
31
  end
29
32
 
30
- sources_search = File.expand_path("lib/licensed/sources/*.rb", __dir__)
31
- sources = Dir[sources_search].map { |f| File.basename(f, ".*") }
33
+ sources = Licensed::Sources::Source.sources.map { |source| source.full_type }
32
34
 
33
35
  namespace :test do
34
36
  sources.each do |source|
@@ -13,8 +13,37 @@ Dependency enumerators inherit and override the [`Licensed::Sources::Source`](..
13
13
 
14
14
  ### Optional method overrides
15
15
 
16
- 1. `Licensed::Sources::Source.type`
17
- - Returns the name of the current dependency enumerator as it is found in a licensed configuration file.
16
+ 1. `Licensed::Sources::Source.type_and_version`
17
+ - Returns the name, and optionally a version, of the current dependency enumerator as it is found in a licensed configuration file. See [the method description](../lib/licensed/sources/source.rb#L38-L41) for more details
18
+
19
+ ### Implementing an enumerator for a new version of an existing source
20
+
21
+ If a package manager introduces breaking changes, it can be easier to build a new implementation rather than making a single class work for all cases. To enable seamless migration between source versions, the implementation for each version of the source enumerator should return the same `.type` and determine whether the version implementation should run in `#enabled?`.
22
+
23
+ The sections below describe what was done when adding a new version for the `yarn` source. Following these steps will make sure that the new version implementation follows the expected patterns for local development and test scenarios.
24
+
25
+ #### Migrating the file structure for a single source enumerator to enable multiple source enumerator versions
26
+
27
+ The following steps will migrate the source to the pattern expected for multi-version source enumerators.
28
+
29
+ The enumerators source code file is likely named to closely match the source enumerator, e.g. `lib/licensed/sources/yarn.rb`
30
+
31
+ 1. Create a new directory matching the name of the source and move the existing enumerator into the new folder with a version descriptive name, e.g. `lib/licensed/sources/yarn/v1.rb`
32
+ 1. Update the source enumerator class name to include a version identifier, e.g. `Licensed::Sources::Yarn::V1`
33
+ 1. Make similar changes for the source's [unit test fixtures](../test/fixtures), [unit test file](../test/sources) and [setup script](../scripts/source-setup), moving these files into subfolders and renaming the files to match the change in (1)
34
+ - Also be sure to update any references to old paths or class names
35
+ 1. If needed, update the source's `#type_and_version` to include a version value as a second array value
36
+ - If this isn't already set, the default implementation will return the type and version as the last two part names of the class name, snake cased and with a `/` delimeter, e.g. `yarn/v1`
37
+ 1. Update the source's `#enabled?` method, adding a version check to ensure that the source only runs in the expected scenario
38
+ 1. Add a new generic source file in `lib/licensed/sources` that `require`s the new file, e.g. `lib/licensed/sources/yarn.rb`
39
+ - This is also an ideal spot to put shared code in a module that can be included in one or more versions of the source enumerator
40
+ 1. Update any references to the source in scripting and GitHub Actions automation to use the new versioned identifier, e.g. `yarn/v1` instead of the unversioned identifier.
41
+
42
+ #### Adding a new implementation for the new version of the source
43
+
44
+ 1. Add the new implementation to the source's `lib/licensed/sources` subfolder.
45
+ 1. If there is shared code that can be reused between multiple source enumerator versions, put it in a module in the source's base file, e.g. `lib/licensed/sources/yarn.rb`. Include the module in the version implementations.
46
+ 1. Ensure that the new version implementation checks for the expected source enumerator version in `#enabled?`
18
47
 
19
48
  ## Determining if dependencies should be enumerated
20
49
 
@@ -24,7 +53,7 @@ whether `Licensed::Source::Sources#enumerate_dependencies` should be called on t
24
53
  Determining whether dependencies should be enumerated depends on whether all the tools or files needed to find dependencies are present.
25
54
  For example, to enumerate `npm` dependencies the `npm` CLI tool must be found with `Licensed::Shell.tool_available?` and a `package.json` file needs to exist in the licensed app's configured [`source_path`](./configuration.md#configuration-paths).
26
55
 
27
- ### Gating functionality when required tools are not available.
56
+ ### Gating functionality when required tools are not available
28
57
 
29
58
  When adding new dependency sources, ensure that `script/bootstrap` scripting and tests are only run if the required tooling is available on the development machine.
30
59
 
@@ -8,7 +8,7 @@ Run `licensed -h` to see help content for running licensed commands.
8
8
  - [migrate](migrate.md)
9
9
  - [notices](notices.md)
10
10
  - [status](status.md)
11
- - [version](verison.md)
11
+ - [version](version.md)
12
12
 
13
13
  Most commands accept a `-c`/`--config` option to specify a path to a configuration file or directory. If a directory is specified, `licensed` will look in that directory for a file named (in order of preference):
14
14
 
data/docs/sources/yarn.md CHANGED
@@ -2,13 +2,14 @@
2
2
 
3
3
  The yarn source will detect dependencies when `package.json` and `yarn.lock` are found at an app's `source_path`.
4
4
 
5
- It uses `yarn list` to enumerate dependencies and `yarn info` to get metadata on each package.
5
+ It uses the `yarn` CLI commands to enumerate dependencies and gather metadata on each package.
6
6
 
7
- ### Including development dependencies
7
+ ## Including development dependencies
8
8
 
9
- Yarn versions < 1.3.0 will always include non-production dependencies due to a bug in those yarn versions.
9
+ **Note** Yarn versions < 1.3.0 will always include non-production dependencies due to a bug in those versions of yarn.
10
+ **Note** Yarn versions > 2.0 will always include non-production dependencies due to lack of filtering of production vs non-production dependencies in the yarn CLI.
10
11
 
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
+ For yarn versions between 1.3.0 and 2.0, the yarn source excludes non-production dependencies by default. To include development and test dependencies in these versions, set `production_only: false` in `.licensed.yml`.
12
13
 
13
14
  ```yml
14
15
  yarn:
@@ -49,7 +49,7 @@ module Licensed
49
49
  errored_reports = all_reports.select { |r| r.errors.any? }.to_a
50
50
 
51
51
  dependency_count = all_reports.count { |r| r.target.is_a?(Licensed::Dependency) }
52
- error_count = errored_reports.sum { |r| r.errors.size }
52
+ error_count = errored_reports.reduce(0) { |count, r| count + r.errors.size }
53
53
 
54
54
  if error_count > 0
55
55
  shell.newline
@@ -23,10 +23,6 @@ module Licensed
23
23
  end
24
24
  end
25
25
 
26
- def self.type
27
- "npm"
28
- end
29
-
30
26
  def enabled?
31
27
  Licensed::Shell.tool_available?("npm") && File.exist?(config.pwd.join("package.json"))
32
28
  end
@@ -7,8 +7,8 @@ module Licensed
7
7
  # Only supports ProjectReference (project.assets.json) style restore used in .NET Core.
8
8
  # Does not currently support packages.config style restore.
9
9
  class NuGet < Source
10
- def self.type
11
- "nuget"
10
+ def self.type_and_version
11
+ ["nuget"]
12
12
  end
13
13
 
14
14
  class NuGetDependency < Licensed::Dependency
@@ -19,13 +19,32 @@ module Licensed
19
19
  (@sources ||= []) << klass
20
20
  end
21
21
 
22
- # Returns the source name as the snake cased class name
22
+ # Returns the source name as the first snake cased class or module name
23
+ # following "Licensed::Sources::". This is the type that is included
24
+ # in metadata files and cache paths.
25
+ # e.g. for `Licensed::Sources::Yarn::V1`, this returns "yarn"
23
26
  def type
24
- self.name.split(/::/)
25
- .last
27
+ type_and_version[0]
28
+ end
29
+
30
+ # Returns the source name as a "/" delimited string of all the module and
31
+ # class names following "Licensed::Sources::". This is the type that is
32
+ # used to distinguish multiple versions of a sources from each other.
33
+ # e.g. for `Licensed::Sources::Yarn::V1`, this returns `yarn/v1`
34
+ def full_type
35
+ type_and_version.join("/")
36
+ end
37
+
38
+ # Returns an array that includes the source's type name at the first index, and
39
+ # optionally a version string for the source as the second index.
40
+ # Callers should override this function and not `type` or `full_type` when
41
+ # needing to adjust the default type and version parsing logic
42
+ def type_and_version
43
+ self.name.gsub("#{Licensed::Sources.name}::", "")
26
44
  .gsub(/([A-Z\d]+)([A-Z][a-z])/, "\\1_\\2".freeze)
27
45
  .gsub(/([a-z\d])([A-Z])/, "\\1_\\2".freeze)
28
46
  .downcase
47
+ .split("::")
29
48
  end
30
49
  end
31
50
 
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+ require "json"
3
+
4
+ module Licensed
5
+ module Sources
6
+ class Yarn::Berry < Source
7
+ include Licensed::Sources::Yarn
8
+
9
+ def self.version_requirement
10
+ Gem::Requirement.new(">= 2.0")
11
+ end
12
+
13
+ def enumerate_dependencies
14
+ packages.map do |name, package|
15
+ Dependency.new(
16
+ name: name,
17
+ version: package["version"],
18
+ path: package["path"],
19
+ metadata: {
20
+ "type" => self.class.type,
21
+ "name" => package["name"],
22
+ "homepage" => package["homepage"]
23
+ }
24
+ )
25
+ end
26
+ end
27
+
28
+ # Finds packages that the current project relies on based on the output from `yarn info`
29
+ def packages
30
+ # parse all lines of output to json and find one that is "type": "tree"
31
+ yarn_info = JSON.parse("[#{yarn_info_command.lines.join(",")}]")
32
+ mapped_packages = yarn_info.reduce({}) do |accum, package|
33
+ name, _ = package["value"].rpartition("@")
34
+ version = package.dig("children", "Version")
35
+ id = "#{name}-#{version}"
36
+
37
+ accum[name] ||= []
38
+ accum[name] << {
39
+ "id" => id,
40
+ "name" => name,
41
+ "version" => version,
42
+ "homepage" => package.dig("children", "Manifest", "Homepage"),
43
+ "path" => dependency_paths[id]
44
+ }
45
+ accum
46
+ end
47
+
48
+ mapped_packages.each_with_object({}) do |(name, results), hsh|
49
+ results.uniq! { |package| package["version"] }
50
+ if results.size == 1
51
+ # if there is only one package for a name, reference it by name
52
+ hsh[name] = results[0]
53
+ else
54
+ # if there is more than one package for a name, reference each by id
55
+ results.each do |package|
56
+ hsh[package["id"]] = package
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ # Returns a hash that maps all dependency names to their location on disk
63
+ # by parsing every package.json file under node_modules.
64
+ def dependency_paths
65
+ @dependency_paths ||= Dir.glob(config.pwd.join("node_modules/**/package.json")).each_with_object({}) do |file, hsh|
66
+ dirname = File.dirname(file)
67
+ json = JSON.parse(File.read(file))
68
+ hsh["#{json["name"]}-#{json["version"]}"] = dirname
69
+ end
70
+ end
71
+
72
+ # Returns the output from running `yarn list` to get project dependencies
73
+ def yarn_info_command
74
+ args = %w(--json --manifest --recursive --all)
75
+ Licensed::Shell.execute("yarn", "info", *args)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+ require "json"
3
+
4
+ module Licensed
5
+ module Sources
6
+ class Yarn::V1 < Source
7
+ include Licensed::Sources::Yarn
8
+
9
+ # `yarn licenses list --json` returns data in a table format with header
10
+ # ordering specified in the output. Look for these specific headers and use
11
+ # their indices to get data from the table body
12
+ YARN_NAME_HEAD = "Name".freeze
13
+ YARN_VERSION_HEAD = "Version".freeze
14
+ YARN_URL_HEAD = "URL".freeze
15
+
16
+ def self.version_requirement
17
+ Gem::Requirement.new("< 2.0")
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" => self.class.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(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(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
+ (result[name] ||= []) << {
66
+ "id" => dependency["name"],
67
+ "name" => name,
68
+ "version" => version,
69
+ "path" => dependency_paths[dependency["name"]]
70
+ }
71
+ recursive_dependencies(dependency["children"], result)
72
+ end
73
+ result
74
+ end
75
+
76
+ # Returns a hash that maps all dependency names to their location on disk
77
+ # by parsing every package.json file under node_modules.
78
+ def dependency_paths
79
+ @dependency_paths ||= Dir.glob(config.pwd.join("node_modules/**/package.json")).each_with_object({}) do |file, hsh|
80
+ dirname = File.dirname(file)
81
+ json = JSON.parse(File.read(file))
82
+ hsh["#{json["name"]}@#{json["version"]}"] = dirname
83
+ end
84
+ end
85
+
86
+ # Finds and returns the yarn package tree listing from `yarn list` output
87
+ def yarn_package_tree
88
+ return @yarn_package_tree if defined?(@yarn_package_tree)
89
+ @yarn_package_tree = begin
90
+ # parse all lines of output to json and find one that is "type": "tree"
91
+ tree = yarn_list_command.lines
92
+ .map(&:strip)
93
+ .map(&JSON.method(:parse))
94
+ .find { |json| json["type"] == "tree" }
95
+ tree&.dig("data", "trees")
96
+ end
97
+ end
98
+
99
+ # Returns a mapping of unique dependency identifiers to urls
100
+ def dependency_urls
101
+ @dependency_urls ||= begin
102
+ table = yarn_licenses_command.lines
103
+ .map(&:strip)
104
+ .map(&JSON.method(:parse))
105
+ .find { |json| json["type"] == "table" }
106
+ return {} if table.nil?
107
+
108
+ head = table.dig("data", "head")
109
+ return {} if head.nil?
110
+
111
+ name_index = head.index YARN_NAME_HEAD
112
+ version_index = head.index YARN_VERSION_HEAD
113
+ url_index = head.index YARN_URL_HEAD
114
+ return {} if name_index.nil? || version_index.nil? || url_index.nil?
115
+
116
+ body = table.dig("data", "body")
117
+ return {} if body.nil?
118
+
119
+ body.each_with_object({}) do |row, hsh|
120
+ id = "#{row[name_index]}@#{row[version_index]}"
121
+ hsh[id] = row[url_index]
122
+ end
123
+ end
124
+ end
125
+
126
+ # Returns the output from running `yarn list` to get project dependencies
127
+ def yarn_list_command
128
+ args = %w(--json -s --no-progress)
129
+ args << "--production" unless include_non_production?
130
+ Licensed::Shell.execute("yarn", "list", *args, allow_failure: true)
131
+ end
132
+
133
+ # Returns the output from running `yarn licenses list` to get project urls
134
+ def yarn_licenses_command
135
+ args = %w(--json -s --no-progress)
136
+ args << "--production" unless include_non_production?
137
+ Licensed::Shell.execute("yarn", "licenses", "list", *args, allow_failure: true)
138
+ end
139
+
140
+ # Returns whether to include non production dependencies based on the licensed configuration settings
141
+ def include_non_production?
142
+ config.dig("yarn", "production_only") == false
143
+ end
144
+ end
145
+ end
146
+ end
@@ -1,146 +1,31 @@
1
1
  # frozen_string_literal: true
2
- require "json"
3
2
 
4
3
  module Licensed
5
4
  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(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(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
- (result[name] ||= []) << {
66
- "id" => dependency["name"],
67
- "name" => name,
68
- "version" => version,
69
- "path" => dependency_paths[dependency["name"]]
70
- }
71
- recursive_dependencies(dependency["children"], result)
5
+ module Yarn
6
+ module ClassMethods
7
+ def type
8
+ "yarn"
72
9
  end
73
- result
74
10
  end
75
11
 
76
- # Returns a hash that maps all dependency names to their location on disk
77
- # by parsing every package.json file under node_modules.
78
- def dependency_paths
79
- @dependency_paths ||= Dir.glob(config.pwd.join("node_modules/**/package.json")).each_with_object({}) do |file, hsh|
80
- dirname = File.dirname(file)
81
- json = JSON.parse(File.read(file))
82
- hsh["#{json["name"]}@#{json["version"]}"] = dirname
83
- end
12
+ def self.included(klass)
13
+ klass.extend ClassMethods
84
14
  end
85
15
 
86
- # Finds and returns the yarn package tree listing from `yarn list` output
87
- def yarn_package_tree
88
- return @yarn_package_tree if defined?(@yarn_package_tree)
89
- @yarn_package_tree = begin
90
- # parse all lines of output to json and find one that is "type": "tree"
91
- tree = yarn_list_command.lines
92
- .map(&:strip)
93
- .map(&JSON.method(:parse))
94
- .find { |json| json["type"] == "tree" }
95
- tree&.dig("data", "trees")
96
- end
97
- end
98
-
99
- # Returns a mapping of unique dependency identifiers to urls
100
- def dependency_urls
101
- @dependency_urls ||= begin
102
- table = yarn_licenses_command.lines
103
- .map(&:strip)
104
- .map(&JSON.method(:parse))
105
- .find { |json| json["type"] == "table" }
106
- return [] if table.nil?
107
-
108
- head = table.dig("data", "head")
109
- return [] if head.nil?
110
-
111
- name_index = head.index YARN_NAME_HEAD
112
- version_index = head.index YARN_VERSION_HEAD
113
- url_index = head.index YARN_URL_HEAD
114
- return [] if name_index.nil? || version_index.nil? || url_index.nil?
115
-
116
- body = table.dig("data", "body")
117
- return [] if body.nil?
118
-
119
- body.each_with_object({}) do |row, hsh|
120
- id = "#{row[name_index]}@#{row[version_index]}"
121
- hsh[id] = row[url_index]
122
- end
123
- end
124
- end
125
-
126
- # Returns the output from running `yarn list` to get project dependencies
127
- def yarn_list_command
128
- args = %w(--json -s --no-progress)
129
- args << "--production" unless include_non_production?
130
- Licensed::Shell.execute("yarn", "list", *args, allow_failure: true)
131
- end
16
+ def enabled?
17
+ return unless Licensed::Shell.tool_available?("yarn")
18
+ return unless self.class.version_requirement.satisfied_by?(yarn_version)
132
19
 
133
- # Returns the output from running `yarn licenses list` to get project urls
134
- def yarn_licenses_command
135
- args = %w(--json -s --no-progress)
136
- args << "--production" unless include_non_production?
137
- Licensed::Shell.execute("yarn", "licenses", "list", *args, allow_failure: true)
20
+ config.pwd.join("package.json").exist? && config.pwd.join("yarn.lock").exist?
138
21
  end
139
22
 
140
- # Returns whether to include non production dependencies based on the licensed configuration settings
141
- def include_non_production?
142
- config.dig("yarn", "production_only") == false
23
+ def yarn_version
24
+ Gem::Version.new(Licensed::Shell.execute("yarn", "-v"))
143
25
  end
144
26
  end
145
27
  end
146
28
  end
29
+
30
+ require "licensed/sources/yarn/v1"
31
+ require "licensed/sources/yarn/berry"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Licensed
3
- VERSION = "3.3.1".freeze
3
+ VERSION = "3.4.0".freeze
4
4
 
5
5
  def self.previous_major_versions
6
6
  major_version = Gem::Version.new(Licensed::VERSION).segments.first
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: 3.3.1
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-07 00:00:00.000000000 Z
11
+ date: 2021-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: licensee
@@ -335,6 +335,8 @@ files:
335
335
  - lib/licensed/sources/source.rb
336
336
  - lib/licensed/sources/swift.rb
337
337
  - lib/licensed/sources/yarn.rb
338
+ - lib/licensed/sources/yarn/berry.rb
339
+ - lib/licensed/sources/yarn/v1.rb
338
340
  - lib/licensed/ui/shell.rb
339
341
  - lib/licensed/version.rb
340
342
  - licensed.gemspec