licensed 3.3.1 → 3.4.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: 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