license_patrol 1.0.0 → 1.2.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 +4 -4
- data/.github/CODEOWNERS +2 -0
- data/.github/workflows/stale.yml +35 -0
- data/CHANGELOG.md +16 -1
- data/README.md +17 -2
- data/VERSION +1 -1
- data/lib/license_patrol/cli/base.rb +1 -0
- data/lib/license_patrol/cli/main.rb +4 -0
- data/lib/license_patrol/configuration.rb +4 -0
- data/lib/license_patrol/core.rb +1 -0
- data/lib/license_patrol/package_managers/bundler.rb +77 -38
- data/lib/license_patrol/package_managers/yarn.rb +75 -60
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bfadea16931d270ad27f48e5db7d22d95751c39335299a0375b037b192d88925
|
|
4
|
+
data.tar.gz: acba13bf06366eb312bde648d1aefc653576ff2fa67cae75a2cbddf9d697a84b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bd3c0356c36aa7aae5ed0891ce8e4b4baff329fdbbd21f26316f87f13d6de4daf9f4d3b6ed1831f4b1cbac9a7763f0224ef371d9cc2a87abf8ea67e760ad8a10
|
|
7
|
+
data.tar.gz: ba987bba63a5b6161407ad3fb73c904d15e6d4d2438cf6ce8cfea6e867dfb7435e8cfab1be6c7f2b3c2bb34bd19ff955de05155356d0cff3519c58863f7ac89d
|
data/.github/CODEOWNERS
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Managed by https://github.com/powerhome/software. Do not make changes here, they will be reverted.
|
|
2
|
+
|
|
3
|
+
name: 'Close stale issues and PRs'
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: '37 21 * * *'
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
stale:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
|
|
14
|
+
with:
|
|
15
|
+
operations-per-run: 200
|
|
16
|
+
delete-branch: true
|
|
17
|
+
stale-pr-message: >
|
|
18
|
+
This issue has been automatically marked as stale because it has not had
|
|
19
|
+
recent activity. It will be closed in 24 hours if no further activity occurs.
|
|
20
|
+
|
|
21
|
+
If this change is desirable, please accelerate completing it. If it is not,
|
|
22
|
+
please close the PR. If you're blocked on something, please ensure there's
|
|
23
|
+
a reference to this PR in a story on your team's board so the team will
|
|
24
|
+
follow up, and consider closing the PR for now.
|
|
25
|
+
|
|
26
|
+
Please do not artificially extend the deadline with a dummy comment. If
|
|
27
|
+
necessary, provide a status update, such as "this change is being actively
|
|
28
|
+
tested".
|
|
29
|
+
|
|
30
|
+
Thank you for your contributions and your collaboration in reducing WIP
|
|
31
|
+
and cycle time.
|
|
32
|
+
days-before-stale: 14
|
|
33
|
+
days-before-close: 3
|
|
34
|
+
days-before-issue-stale: -1
|
|
35
|
+
exempt-pr-labels: dependencies
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
# [1.
|
|
1
|
+
# [1.2.0] / 2026-10-28
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
* Yarn 2, 3 and 4 projects scan without the third-party licenses plugin, reading packages from the built-in `yarn info` - [6a46a083](https://github.com/powerhome/license_patrol/commit/6a46a083) - Carlos Palhares
|
|
5
|
+
|
|
6
|
+
### Changed
|
|
7
|
+
* Yarn 2+ no longer reports workspaces as packages, and `--prepare` only runs `yarn install` - [6a46a083](https://github.com/powerhome/license_patrol/commit/6a46a083) - Carlos Palhares
|
|
8
|
+
|
|
9
|
+
# [1.1.0] / 2026-10-21
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
* `bundler_gemfiles` option to scan many gemfiles, such as Appraisal's `gemfiles/*.gemfile`, in one run - [5d7e1372](https://github.com/powerhome/license_patrol/commit/5d7e1372) - Carlos Palhares
|
|
13
|
+
|
|
14
|
+
# [1.0.0] / 2026-10-21
|
|
2
15
|
|
|
3
16
|
First release as `license_patrol`, forked from LicenseFinder 7.2.1. Earlier entries are LicenseFinder's history.
|
|
4
17
|
|
|
@@ -1068,3 +1081,5 @@ Bugfixes:
|
|
|
1068
1081
|
[7.1.0]: https://github.com/pivotal/LicenseFinder/compare/v7.0.1...v7.1.0
|
|
1069
1082
|
[7.2.0]: https://github.com/pivotal/LicenseFinder/compare/v7.1.0...v7.2.0
|
|
1070
1083
|
[7.2.1]: https://github.com/pivotal/LicenseFinder/compare/v7.2.0...v7.2.1
|
|
1084
|
+
[1.0.0]: https://github.com/powerhome/license_patrol/releases/tag/v1.0.0
|
|
1085
|
+
[1.1.0]: https://github.com/powerhome/license_patrol/compare/v1.0.0...v1.1.0
|
data/README.md
CHANGED
|
@@ -48,7 +48,7 @@ and give you an actionable exception report.
|
|
|
48
48
|
* Objective-C, Swift (via Carthage, CocoaPods \[0.39 and below. See [CocoaPods Specs Repo Sharding](http://blog.cocoapods.org/Sharding/)\]) and Swift Package Manager(SPM)
|
|
49
49
|
* Elixir (via `mix`)
|
|
50
50
|
* Golang (via `gvt`, `glide`,`dep`, `trash` and `govendor`)
|
|
51
|
-
* JavaScript (via `yarn`)
|
|
51
|
+
* JavaScript (via `yarn` 1.x through 4.x)
|
|
52
52
|
* C++/C (via `conan`)
|
|
53
53
|
* Scala (via `sbt`)
|
|
54
54
|
* Rust (via `cargo`)
|
|
@@ -328,7 +328,7 @@ be approved. The project name at the top of the report can be set with
|
|
|
328
328
|
`license_patrol project_name add`.
|
|
329
329
|
|
|
330
330
|
### Note:
|
|
331
|
-
When using
|
|
331
|
+
When using Yarn 1, when a node_module's package.json doesn't
|
|
332
332
|
explicitly declare a license, yarn indicates that it has inferred the license based
|
|
333
333
|
on some keywords in other files by appending an asterisk to the license name. If you
|
|
334
334
|
see a * at the end of the license name, this is intended.
|
|
@@ -466,6 +466,18 @@ If you store rebar dependencies in a custom directory (by setting `deps_dir` in
|
|
|
466
466
|
You can also invoke a custom Mix script `remix` with `--mix_command remix` and
|
|
467
467
|
set `--mix_deps_dir` to fetch Mix dependencies from a custom directory.
|
|
468
468
|
|
|
469
|
+
By default, `bundler` dependencies are read from the gemfile in `BUNDLE_GEMFILE`,
|
|
470
|
+
or from `Gemfile`. If your project locks dependencies in more than one gemfile
|
|
471
|
+
(e.g. when using [Appraisal](https://github.com/thoughtbot/appraisal)), pass all
|
|
472
|
+
of them to `--bundler_gemfiles`, relative to the project path. Glob patterns are
|
|
473
|
+
accepted, as long as they are quoted:
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
$ license_patrol --bundler_gemfiles Gemfile 'gemfiles/*.gemfile'
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Every gem locked by any of the gemfiles will be reported, once per version.
|
|
480
|
+
|
|
469
481
|
### Narrow down Package Manager
|
|
470
482
|
|
|
471
483
|
By default, license_patrol will check for all supported package managers,
|
|
@@ -492,6 +504,9 @@ rebar_command: './rebarw'
|
|
|
492
504
|
rebar_deps_dir: './rebar_deps'
|
|
493
505
|
mix_command: './mixw'
|
|
494
506
|
mix_deps_dir: './mix_deps'
|
|
507
|
+
bundler_gemfiles:
|
|
508
|
+
- Gemfile
|
|
509
|
+
- gemfiles/*.gemfile
|
|
495
510
|
enabled_package_managers:
|
|
496
511
|
- bundler
|
|
497
512
|
- gradle
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.2.0
|
|
@@ -24,6 +24,10 @@ module LicensePatrol
|
|
|
24
24
|
'junit' => JunitReport
|
|
25
25
|
}.freeze
|
|
26
26
|
|
|
27
|
+
class_option :bundler_gemfiles,
|
|
28
|
+
type: :array,
|
|
29
|
+
desc: "Gemfiles to be scanned, relative to the project path. Accepts glob patterns (e.g. 'gemfiles/*.gemfile' for Appraisal).
|
|
30
|
+
Only meaningful if used with a Ruby/bundler project. Defaults to BUNDLE_GEMFILE, or 'Gemfile'."
|
|
27
31
|
class_option :go_full_version, desc: 'Whether dependency version should include full version. Only meaningful if used with a Go project. Defaults to false.'
|
|
28
32
|
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.'
|
|
29
33
|
class_option :gradle_command,
|
data/lib/license_patrol/core.rb
CHANGED
|
@@ -95,6 +95,7 @@ module LicensePatrol
|
|
|
95
95
|
log_directory: File.join(config.log_directory, project_name),
|
|
96
96
|
ignored_groups: decisions.ignored_groups,
|
|
97
97
|
enabled_package_manager_ids: config.enabled_package_manager_ids,
|
|
98
|
+
bundler_gemfiles: config.bundler_gemfiles,
|
|
98
99
|
go_full_version: config.go_full_version,
|
|
99
100
|
gradle_command: config.gradle_command,
|
|
100
101
|
gradle_include_groups: config.gradle_include_groups,
|
|
@@ -2,23 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
require 'bundler'
|
|
4
4
|
require 'securerandom'
|
|
5
|
+
require 'shellwords'
|
|
5
6
|
|
|
6
7
|
module LicensePatrol
|
|
7
8
|
class Bundler < PackageManager
|
|
8
9
|
def initialize(options = {})
|
|
9
10
|
super
|
|
10
11
|
@ignored_groups = options[:ignored_groups]
|
|
12
|
+
@gemfiles = Array(options[:bundler_gemfiles]) unless options[:bundler_gemfiles].nil?
|
|
11
13
|
@definition = options[:definition] # dependency injection for tests
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
def current_packages
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
log_package_dependencies package
|
|
20
|
-
end
|
|
21
|
-
end
|
|
17
|
+
packages = existing_gemfile_paths.flat_map { |gemfile_path| current_packages_for(gemfile_path) }
|
|
18
|
+
|
|
19
|
+
packages.group_by { |package| [package.name, package.version] }.map do |_, same_packages|
|
|
20
|
+
merge_groups(same_packages)
|
|
22
21
|
end
|
|
23
22
|
end
|
|
24
23
|
|
|
@@ -32,68 +31,81 @@ module LicensePatrol
|
|
|
32
31
|
gem_path = "lf-bundler-gems-#{SecureRandom.uuid}"
|
|
33
32
|
logger.info self.class, "Running bundle install for #{Dir.pwd} with path #{gem_path}", color: :blue
|
|
34
33
|
|
|
35
|
-
"bundle install #{ignored_groups_argument} --path #{gem_path}".strip
|
|
34
|
+
return "bundle install #{ignored_groups_argument} --path #{gem_path}".strip if existing_gemfile_paths.size <= 1
|
|
35
|
+
|
|
36
|
+
existing_gemfile_paths.map do |gemfile_path|
|
|
37
|
+
gemfile_argument = "--gemfile #{Shellwords.escape(relative_to_project(gemfile_path))}"
|
|
38
|
+
"bundle install #{gemfile_argument} #{ignored_groups_argument} --path #{gem_path}".squeeze(' ')
|
|
39
|
+
end.join(' && ')
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
def possible_package_paths
|
|
39
|
-
|
|
43
|
+
gemfile_paths
|
|
40
44
|
end
|
|
41
45
|
|
|
42
46
|
private
|
|
43
47
|
|
|
44
48
|
attr_reader :ignored_groups
|
|
45
49
|
|
|
46
|
-
def
|
|
47
|
-
|
|
50
|
+
def current_packages_for(gemfile_path)
|
|
51
|
+
with_gemfile(gemfile_path) do
|
|
52
|
+
definition = definition_for(gemfile_path)
|
|
53
|
+
included_groups = definition.groups - ignored_groups.map(&:to_sym)
|
|
54
|
+
logger.debug self.class, "including groups #{included_groups.inspect} from #{gemfile_path}"
|
|
48
55
|
|
|
49
|
-
|
|
56
|
+
details(definition, included_groups, gemfile_path).map do |gem_detail, bundle_detail|
|
|
57
|
+
BundlerPackage.new(gem_detail, bundle_detail, logger: logger).tap do |package|
|
|
58
|
+
log_package_dependencies package
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
50
62
|
end
|
|
51
63
|
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
ENV['BUNDLE_GEMFILE'] = gemfile_path.to_s
|
|
55
|
-
yield
|
|
56
|
-
ensure
|
|
57
|
-
ENV['BUNDLE_GEMFILE'] = previous_gemfile
|
|
64
|
+
def definition_for(gemfile_path)
|
|
65
|
+
@definition || ::Bundler::Definition.build(gemfile_path, lockfile_path(gemfile_path), nil)
|
|
58
66
|
end
|
|
59
67
|
|
|
60
|
-
def details
|
|
61
|
-
|
|
68
|
+
def details(definition, included_groups, gemfile_path)
|
|
69
|
+
bundler_details = definition.dependencies
|
|
70
|
+
|
|
71
|
+
gem_details(definition, included_groups, gemfile_path).map do |gem_detail|
|
|
62
72
|
bundle_detail = bundler_details.detect { |bundler_detail| bundler_detail.name == gem_detail.name }
|
|
63
73
|
[gem_detail, bundle_detail]
|
|
64
74
|
end
|
|
65
75
|
end
|
|
66
76
|
|
|
67
|
-
def gem_details
|
|
68
|
-
return @gem_details if @gem_details
|
|
69
|
-
|
|
77
|
+
def gem_details(definition, included_groups, gemfile_path)
|
|
70
78
|
# clear gem paths before running specs_for
|
|
71
79
|
Gem.clear_paths
|
|
72
|
-
if bundler_config_path_found
|
|
80
|
+
if bundler_config_path_found(gemfile_path)
|
|
73
81
|
::Bundler.reset!
|
|
74
82
|
::Bundler.configure
|
|
75
83
|
end
|
|
76
|
-
|
|
84
|
+
definition.specs_for(included_groups)
|
|
77
85
|
end
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
# The same gem (name and version) may be locked by several gemfiles,
|
|
88
|
+
# in which case it should be reported once, but under all its groups.
|
|
89
|
+
def merge_groups(packages)
|
|
90
|
+
packages.first.tap do |package|
|
|
91
|
+
package.groups.concat(packages.drop(1).flat_map(&:groups)).uniq!
|
|
92
|
+
end
|
|
85
93
|
end
|
|
86
94
|
|
|
87
|
-
def
|
|
88
|
-
|
|
95
|
+
def with_gemfile(gemfile_path)
|
|
96
|
+
previous_gemfile = ENV['BUNDLE_GEMFILE']
|
|
97
|
+
ENV['BUNDLE_GEMFILE'] = gemfile_path.to_s
|
|
98
|
+
yield
|
|
99
|
+
ensure
|
|
100
|
+
ENV['BUNDLE_GEMFILE'] = previous_gemfile
|
|
89
101
|
end
|
|
90
102
|
|
|
91
|
-
def lockfile_path
|
|
103
|
+
def lockfile_path(gemfile_path)
|
|
92
104
|
Pathname("#{gemfile_path}.lock")
|
|
93
105
|
end
|
|
94
106
|
|
|
95
107
|
# Bundler keeps the config (e.g. BUNDLE_PATH for vendored gems) next to the gemfile
|
|
96
|
-
def bundler_config_path_found
|
|
108
|
+
def bundler_config_path_found(gemfile_path)
|
|
97
109
|
config_file = gemfile_path.dirname.join('.bundle/config')
|
|
98
110
|
|
|
99
111
|
return false unless File.exist?(config_file)
|
|
@@ -114,14 +126,41 @@ module LicensePatrol
|
|
|
114
126
|
end
|
|
115
127
|
end
|
|
116
128
|
|
|
117
|
-
|
|
118
|
-
|
|
129
|
+
# All the gemfiles to be scanned, from the `bundler_gemfiles` option
|
|
130
|
+
# (which accepts glob patterns, e.g. `gemfiles/*.gemfile` for Appraisal),
|
|
131
|
+
# or the one in BUNDLE_GEMFILE, or `Gemfile`, relative to the project path.
|
|
132
|
+
def gemfile_paths
|
|
133
|
+
@gemfile_paths ||= gemfiles.flat_map { |gemfile| expand_gemfile(gemfile) }.uniq
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def existing_gemfile_paths
|
|
137
|
+
gemfile_paths.select(&:exist?)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def gemfiles
|
|
141
|
+
@gemfiles || [default_gemfile]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def relative_to_project(gemfile_path)
|
|
145
|
+
gemfile_path.expand_path.relative_path_from(project_path.expand_path).to_s
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def expand_gemfile(gemfile)
|
|
149
|
+
return [project_path.join(gemfile)] unless glob?(gemfile)
|
|
150
|
+
|
|
151
|
+
Dir.glob(gemfile, base: project_path.to_s).sort
|
|
152
|
+
.reject { |path| path.end_with?('.lock') }
|
|
153
|
+
.map { |path| project_path.join(path) }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def glob?(gemfile)
|
|
157
|
+
gemfile.match?(/[*?\[{]/)
|
|
119
158
|
end
|
|
120
159
|
|
|
121
160
|
# BUNDLE_GEMFILE is taken as relative to the project path. Through `bundle exec`
|
|
122
161
|
# it gets expanded to an absolute path, which may point to another project
|
|
123
162
|
# (e.g. when using --project_path), in which case only its basename is kept.
|
|
124
|
-
def
|
|
163
|
+
def default_gemfile
|
|
125
164
|
gemfile = ENV['BUNDLE_GEMFILE']
|
|
126
165
|
return 'Gemfile' if gemfile.nil? || gemfile.empty?
|
|
127
166
|
return gemfile unless Pathname(gemfile).absolute?
|
|
@@ -8,30 +8,17 @@ module LicensePatrol
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
SHELL_COMMAND = 'yarn licenses list --recursive --json'
|
|
11
|
+
BERRY_SHELL_COMMAND = 'yarn info --all --recursive --json --extra manifest'
|
|
11
12
|
|
|
12
13
|
def possible_package_paths
|
|
13
14
|
[project_path.join('yarn.lock')]
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def current_packages
|
|
17
|
-
# the licenses plugin supports the classic production flag
|
|
18
|
-
cmd = "#{Yarn::SHELL_COMMAND}#{classic_yarn_production_flag}"
|
|
19
18
|
if yarn_version == 1
|
|
20
|
-
|
|
21
|
-
cmd += " --cwd #{project_path}" unless project_path.nil?
|
|
22
|
-
cmd += " #{@yarn_options}" unless @yarn_options.nil?
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(cmd) }
|
|
26
|
-
raise "Command '#{cmd}' failed to execute: #{stderr}" unless status.success?
|
|
27
|
-
|
|
28
|
-
json_strings = stdout.encode('ASCII', invalid: :replace, undef: :replace, replace: '?').split("\n")
|
|
29
|
-
json_objects = json_strings.map { |json_object| JSON.parse(json_object) }
|
|
30
|
-
|
|
31
|
-
if yarn_version == 1
|
|
32
|
-
get_yarn1_packages(json_objects)
|
|
19
|
+
get_yarn1_packages(run_json_command(classic_shell_command))
|
|
33
20
|
else
|
|
34
|
-
get_yarn_packages(
|
|
21
|
+
get_yarn_packages(run_json_command(BERRY_SHELL_COMMAND))
|
|
35
22
|
end
|
|
36
23
|
end
|
|
37
24
|
|
|
@@ -62,20 +49,28 @@ module LicensePatrol
|
|
|
62
49
|
|
|
63
50
|
private
|
|
64
51
|
|
|
52
|
+
# devDependencies are left out when reading the packages, so all of them are installed
|
|
65
53
|
def yarn_prepare_command
|
|
66
|
-
|
|
54
|
+
'yarn install'
|
|
67
55
|
end
|
|
68
56
|
|
|
69
57
|
def classic_yarn_prepare_command
|
|
70
58
|
"yarn install --ignore-engines --ignore-scripts#{classic_yarn_production_flag}"
|
|
71
59
|
end
|
|
72
60
|
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
61
|
+
def classic_shell_command
|
|
62
|
+
cmd = "#{Yarn::SHELL_COMMAND}#{classic_yarn_production_flag} --no-progress"
|
|
63
|
+
cmd += " --cwd #{project_path}" unless project_path.nil?
|
|
64
|
+
cmd += " #{@yarn_options}" unless @yarn_options.nil?
|
|
65
|
+
cmd
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def run_json_command(cmd)
|
|
69
|
+
stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(cmd) }
|
|
70
|
+
raise "Command '#{cmd}' failed to execute: #{stderr}" unless status.success?
|
|
71
|
+
|
|
72
|
+
json_strings = stdout.encode('ASCII', invalid: :replace, undef: :replace, replace: '?').split("\n")
|
|
73
|
+
json_strings.map { |json_object| JSON.parse(json_object) }
|
|
79
74
|
end
|
|
80
75
|
|
|
81
76
|
def yarn_version
|
|
@@ -88,40 +83,64 @@ module LicensePatrol
|
|
|
88
83
|
end
|
|
89
84
|
end
|
|
90
85
|
|
|
86
|
+
# Yarn 2+ prints one line per package in the lockfile, keyed by its locator
|
|
87
|
+
# (e.g. "@babel/core@npm:7.18.6"). Workspaces are the project's own code,
|
|
88
|
+
# so only the packages they depend on are reported.
|
|
91
89
|
def get_yarn_packages(json_objects)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
90
|
+
infos = json_objects.map { |json_object| [json_object['value'], json_object['children']] }.to_h
|
|
91
|
+
locators = ignore_dev_dependencies? ? production_locators(infos) : infos.keys
|
|
92
|
+
|
|
93
|
+
packages = locators.reject { |locator| workspace_locator?(locator) }.map do |locator|
|
|
94
|
+
name = locator_name(locator)
|
|
95
|
+
info = infos[locator]
|
|
96
|
+
manifest = info['Manifest'] || {}
|
|
97
|
+
YarnPackage.new(
|
|
98
|
+
name,
|
|
99
|
+
info['Version'],
|
|
100
|
+
spec_licenses: [manifest['License']].compact,
|
|
101
|
+
homepage: manifest['Homepage'],
|
|
102
|
+
install_path: project_path.join(modules_folder, name)
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# a patched package (e.g. typescript) is listed next to the package it patches
|
|
107
|
+
packages.uniq { |package| [package.name, package.version] }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Walks the dependencies of every workspace, skipping the ones they declare
|
|
111
|
+
# in devDependencies, like "yarn workspaces focus --all --production".
|
|
112
|
+
def production_locators(infos)
|
|
113
|
+
seen = Set.new
|
|
114
|
+
pending = infos.keys.select { |locator| workspace_locator?(locator) }
|
|
115
|
+
until pending.empty?
|
|
116
|
+
locator = devirtualize(pending.shift)
|
|
117
|
+
next unless infos.key?(locator) && seen.add?(locator)
|
|
118
|
+
|
|
119
|
+
dev_dependencies = workspace_locator?(locator) ? workspace_dev_dependencies(locator) : []
|
|
120
|
+
(infos[locator]['Dependencies'] || []).each do |dependency|
|
|
121
|
+
pending << dependency['locator'] unless dev_dependencies.include?(locator_name(dependency['descriptor']))
|
|
121
122
|
end
|
|
122
123
|
end
|
|
124
|
+
seen.to_a
|
|
125
|
+
end
|
|
123
126
|
|
|
124
|
-
|
|
127
|
+
def workspace_dev_dependencies(locator)
|
|
128
|
+
workspace_path = locator.delete_prefix("#{locator_name(locator)}@workspace:")
|
|
129
|
+
manifest = JSON.parse(File.read(project_path.join(workspace_path, 'package.json')))
|
|
130
|
+
(manifest['devDependencies'] || {}).keys - (manifest['dependencies'] || {}).keys
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def locator_name(locator)
|
|
134
|
+
locator[%r{\A(@[^/@]+/)?[^@]+}]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def workspace_locator?(locator)
|
|
138
|
+
locator.start_with?("#{locator_name(locator)}@workspace:")
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# "react-dom@virtual:c6e8...#npm:18.2.0" is "react-dom@npm:18.2.0" with its peer dependencies resolved
|
|
142
|
+
def devirtualize(locator)
|
|
143
|
+
locator.sub(/@virtual:[^#]*#/, '@')
|
|
125
144
|
end
|
|
126
145
|
|
|
127
146
|
def get_yarn1_packages(json_objects)
|
|
@@ -181,15 +200,11 @@ module LicensePatrol
|
|
|
181
200
|
end
|
|
182
201
|
|
|
183
202
|
def classic_yarn_production_flag
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
@ignored_groups.include?('devDependencies') ? ' --production' : ''
|
|
203
|
+
ignore_dev_dependencies? ? ' --production' : ''
|
|
187
204
|
end
|
|
188
205
|
|
|
189
|
-
def
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
@ignored_groups.include?('devDependencies') ? 'yarn plugin import workspace-tools && yarn workspaces focus --all --production && ' : ''
|
|
206
|
+
def ignore_dev_dependencies?
|
|
207
|
+
!@ignored_groups.nil? && @ignored_groups.include?('devDependencies')
|
|
193
208
|
end
|
|
194
209
|
end
|
|
195
210
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: license_patrol
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ryan Collins
|
|
@@ -396,9 +396,11 @@ extensions: []
|
|
|
396
396
|
extra_rdoc_files: []
|
|
397
397
|
files:
|
|
398
398
|
- ".force-build"
|
|
399
|
+
- ".github/CODEOWNERS"
|
|
399
400
|
- ".github/dependabot.yml"
|
|
400
401
|
- ".github/workflows/install-test-tools.sh"
|
|
401
402
|
- ".github/workflows/license_patrol.yml"
|
|
403
|
+
- ".github/workflows/stale.yml"
|
|
402
404
|
- ".gitignore"
|
|
403
405
|
- ".pre-commit-hooks.yaml"
|
|
404
406
|
- ".rspec"
|