licensed 2.9.1 → 2.12.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/workflows/release.yml +11 -12
- data/.github/workflows/test.yml +69 -50
- data/.gitignore +2 -0
- data/CHANGELOG.md +42 -1
- data/README.md +11 -7
- data/docs/commands.md +14 -0
- data/docs/configuration.md +20 -0
- data/docs/sources/nuget.md +14 -0
- data/lib/licensed/cli.rb +22 -3
- data/lib/licensed/commands.rb +1 -0
- data/lib/licensed/commands/cache.rb +51 -14
- data/lib/licensed/commands/command.rb +12 -4
- data/lib/licensed/commands/list.rb +19 -0
- data/lib/licensed/commands/notices.rb +54 -0
- data/lib/licensed/commands/status.rb +19 -0
- data/lib/licensed/configuration.rb +12 -2
- data/lib/licensed/dependency.rb +1 -1
- data/lib/licensed/reporters.rb +1 -0
- data/lib/licensed/reporters/cache_reporter.rb +10 -10
- data/lib/licensed/reporters/list_reporter.rb +21 -5
- data/lib/licensed/reporters/notices_reporter.rb +99 -0
- data/lib/licensed/reporters/reporter.rb +3 -0
- data/lib/licensed/reporters/status_reporter.rb +24 -7
- data/lib/licensed/sources.rb +1 -0
- data/lib/licensed/sources/cabal.rb +2 -2
- data/lib/licensed/sources/go.rb +2 -2
- data/lib/licensed/sources/npm.rb +1 -0
- data/lib/licensed/sources/nuget.rb +212 -0
- data/lib/licensed/version.rb +1 -1
- data/licensed.gemspec +2 -1
- data/script/source-setup/nuget +17 -0
- metadata +23 -4
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,47 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## 2.12.0
|
10
|
+
2020-06-19
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- `--sources` argument for cache, list, status and notices commands to filter running sources (https://github.com/github/licensed/pull/287)
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
- `cache` command will not remove files outside of enabled source cache paths (https://github.com/github/licensed/pull/287)
|
17
|
+
|
18
|
+
## 2.11.1
|
19
|
+
2020-06-09
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
- `notices` command properly reads cached dependency notices contents (https://github.com/github/licensed/pull/283)
|
23
|
+
|
24
|
+
## 2.11.0
|
25
|
+
2020-06-02
|
26
|
+
|
27
|
+
### Added
|
28
|
+
- `notices` command to create a `NOTICE` file for each configured app (https://github.com/github/licensed/pull/277)
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
- NuGet source no longer crashes on a non-existent dependency path (https://github.com/github/licensed/pull/280)
|
32
|
+
- Go source no longer crashes on a non-existent dependency package path (https://github.com/github/licensed/pull/274)
|
33
|
+
|
34
|
+
## 2.10.0
|
35
|
+
2020-05-15
|
36
|
+
|
37
|
+
### Changed
|
38
|
+
- NPM source ignores missing peer dependencies (https://github.com/github/licensed/pull/267)
|
39
|
+
|
40
|
+
### Added
|
41
|
+
- NuGet source (:tada: @zarenner https://github.com/github/licensed/pull/261)
|
42
|
+
- Multiple apps can share a single cache location (https://github.com/github/licensed/pull/263)
|
43
|
+
|
44
|
+
## 2.9.2
|
45
|
+
2020-04-28
|
46
|
+
|
47
|
+
### Changed
|
48
|
+
- `licensee` minimum version bumped to 9.13.2 (https://github.com/github/licensed/pull/256)
|
49
|
+
|
9
50
|
## 2.9.1
|
10
51
|
2020-03-24
|
11
52
|
|
@@ -286,4 +327,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
286
327
|
|
287
328
|
Initial release :tada:
|
288
329
|
|
289
|
-
[Unreleased]: https://github.com/github/licensed/compare/2.
|
330
|
+
[Unreleased]: https://github.com/github/licensed/compare/2.11.1...HEAD
|
data/README.md
CHANGED
@@ -24,13 +24,13 @@ See the [migration documentation](./docs/migrating_to_newer_versions.md) for mor
|
|
24
24
|
### Dependencies
|
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
28
|
> Ubuntu
|
29
|
-
|
29
|
+
|
30
30
|
sudo apt-get install cmake pkg-config
|
31
|
-
|
31
|
+
|
32
32
|
> OS X
|
33
|
-
|
33
|
+
|
34
34
|
brew install cmake pkg-config
|
35
35
|
|
36
36
|
### With a Gemfile
|
@@ -64,8 +64,10 @@ For system wide usage, install licensed to a location on `$PATH`, e.g. `/usr/loc
|
|
64
64
|
|
65
65
|
- `licensed list`: Output enumerated dependencies only.
|
66
66
|
- `licensed cache`: Cache licenses and metadata.
|
67
|
-
- `licensed status`: Check status of dependencies' cached licenses.
|
67
|
+
- `licensed status`: Check status of dependencies' cached licenses.
|
68
|
+
- `licensed notices`: Write a `NOTICE` file for each application configuration.
|
68
69
|
- `licensed version`: Show current installed version of Licensed. Aliases: `-v|--version`
|
70
|
+
- `licensed env`: Output environment information from the licensed configuration.
|
69
71
|
|
70
72
|
See the [commands documentation](./docs/commands.md) for additional documentation, or run `licensed -h` to see all of the current available commands.
|
71
73
|
|
@@ -102,14 +104,16 @@ Dependencies will be automatically detected for all of the following sources by
|
|
102
104
|
1. [Bundler](./docs/sources/bundler.md)
|
103
105
|
1. [Cabal](./docs/sources/cabal.md)
|
104
106
|
1. [Composer](./docs/sources/composer.md)
|
107
|
+
1. [Git Submodules (git_submodule)](./docs/sources/git_submodule.md)
|
105
108
|
1. [Go](./docs/sources/go.md)
|
106
109
|
1. [Go Dep (dep)](./docs/sources/dep.md)
|
110
|
+
1. [Gradle](./docs/sources/gradle.md)
|
107
111
|
1. [Manifest lists (manifests)](./docs/sources/manifests.md)
|
112
|
+
1. [Mix](./docs/sources/mix.md)
|
108
113
|
1. [NPM](./docs/sources/npm.md)
|
114
|
+
1. [NuGet](./docs/sources/nuget.md)
|
109
115
|
1. [Pip](./docs/sources/pip.md)
|
110
116
|
1. [Pipenv](./docs/sources/pipenv.md)
|
111
|
-
1. [Git Submodules (git_submodule)](./docs/sources/git_submodule.md)
|
112
|
-
1. [Mix](./docs/sources/mix.md)
|
113
117
|
1. [Yarn](./docs/sources/yarn.md)
|
114
118
|
|
115
119
|
You can disable any of them in the configuration file:
|
data/docs/commands.md
CHANGED
@@ -6,10 +6,14 @@ Run `licensed -h` to see help content for running licensed commands.
|
|
6
6
|
|
7
7
|
Running the list command finds the dependencies for all sources in all configured applications. No additional actions are taken on each dependency.
|
8
8
|
|
9
|
+
An optional `--sources` flag can be given to limit which dependency sources are run. This is a filter over sources that are enabled via the licensed configuration file and cannot be used to run licensed with a disabled source.
|
10
|
+
|
9
11
|
## `cache`
|
10
12
|
|
11
13
|
The cache command finds all dependencies and ensures that each dependency has an up-to-date cached record.
|
12
14
|
|
15
|
+
An optional `--sources` flag can be given to limit which dependency sources are run. This is a filter over sources that are enabled via the licensed configuration file and cannot be used to run licensed with a disabled source.
|
16
|
+
|
13
17
|
Dependency records will be saved if:
|
14
18
|
1. The `force` option is set
|
15
19
|
2. No cached record is found
|
@@ -22,6 +26,8 @@ After the cache command is run, any cached records that don't match up to a curr
|
|
22
26
|
|
23
27
|
The status command finds all dependencies and checks whether each dependency has a valid cached record.
|
24
28
|
|
29
|
+
An optional `--sources` flag can be given to limit which dependency sources are run. This is a filter over sources that are enabled via the licensed configuration file and cannot be used to run licensed with a disabled source.
|
30
|
+
|
25
31
|
A dependency will fail the status checks if:
|
26
32
|
1. No cached record is found
|
27
33
|
2. The cached record's version is different than the current dependency's version
|
@@ -31,6 +37,14 @@ A dependency will fail the status checks if:
|
|
31
37
|
5. The cached record is flagged for re-review.
|
32
38
|
- This occurs when the record's license text has changed since the record was reviewed.
|
33
39
|
|
40
|
+
## `notices`
|
41
|
+
|
42
|
+
Outputs license and notice text for all dependencies in each app into a `NOTICE` file in the app's `cache_path`. If an app uses a shared cache path, the file name will contain the app name as well, e.g. `NOTICE.my_app`.
|
43
|
+
|
44
|
+
An optional `--sources` flag can be given to limit which dependency sources are run. This is a filter over sources that are enabled via the licensed configuration file and cannot be used to run licensed with a disabled source.
|
45
|
+
|
46
|
+
The `NOTICE` file contents are retrieved from cached records, with the assumption that cached records have already been reviewed in a compliance workflow.
|
47
|
+
|
34
48
|
## `env`
|
35
49
|
|
36
50
|
Prints the runtime environment used by licensed after loading a configuration file. By default the output is in YAML format, but can be output in JSON using the `--json` flag.
|
data/docs/configuration.md
CHANGED
@@ -209,6 +209,26 @@ apps:
|
|
209
209
|
|
210
210
|
In this example, the root configuration will contain a default cache path of `.licenses`. `app1` will inherit this value and append it's name, resulting in a cache path of `.licenses/app1`.
|
211
211
|
|
212
|
+
### Sharing caches between apps
|
213
|
+
|
214
|
+
Dependency caches can be shared between apps by setting the same cache path on each app.
|
215
|
+
|
216
|
+
```yaml
|
217
|
+
apps:
|
218
|
+
- source_path: "path/to/app1"
|
219
|
+
cache_path: ".licenses/apps"
|
220
|
+
- source_path: "path/to/app2"
|
221
|
+
cache_path: ".licenses/apps"
|
222
|
+
```
|
223
|
+
|
224
|
+
When using a source path with a glob pattern, the apps created from the glob pattern can share a dependency by setting an explicit cache path and setting `shared_cache` to true.
|
225
|
+
|
226
|
+
```yaml
|
227
|
+
source_path: "path/to/apps/*"
|
228
|
+
cache_path: ".licenses/apps"
|
229
|
+
shared_cache: true
|
230
|
+
```
|
231
|
+
|
212
232
|
## Source specific configuration
|
213
233
|
|
214
234
|
See the [source documentation](./sources) for details on any source specific configuration.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# NuGet
|
2
|
+
|
3
|
+
The NuGet source will detect ProjectReference-style restored packages by inspecting `project.assets.json` files for dependencies. It requires that `dotnet restore` has already ran on the project.
|
4
|
+
|
5
|
+
The source currently expects that `source_path` is set to the `obj` directory containing the `project.assets.json`.
|
6
|
+
For example, if your project lives at `foo/foo.proj`, you likely want to set `source_path` to `foo/obj`.
|
7
|
+
If in MSBuild you have customized your `obj` paths (e.g. to live outside your source tree), you may need to set `source_path` to something different such as `../obj/foo`.
|
8
|
+
|
9
|
+
### Search strategy
|
10
|
+
This source looks for licenses:
|
11
|
+
1. Specified by SPDX expression via `<license type="expression">` in a package's `.nuspec` (via licensee)
|
12
|
+
2. In license files such as `LICENSE.txt`, even if not specified in the `.nuspec` (via licensee)
|
13
|
+
3. Specified by filepath via `<license type="file">` in a package's `.nuspec`, even if not a standard license filename.
|
14
|
+
4. By downloading and inspecting the contents of `<licenseUrl>` in a package's `.nuspec`, if not found otherwise.
|
data/lib/licensed/cli.rb
CHANGED
@@ -10,22 +10,41 @@ module Licensed
|
|
10
10
|
desc: "Overwrite licenses even if version has not changed."
|
11
11
|
method_option :config, aliases: "-c", type: :string,
|
12
12
|
desc: "Path to licensed configuration file"
|
13
|
+
method_option :sources, aliases: "-s", type: :array,
|
14
|
+
desc: "Individual source(s) to evaluate. Must also be enabled via configuration."
|
13
15
|
def cache
|
14
|
-
run Licensed::Commands::Cache.new(config: config),
|
16
|
+
run Licensed::Commands::Cache.new(config: config),
|
17
|
+
{ force: options[:force], sources: options[:sources] }
|
15
18
|
end
|
16
19
|
|
17
20
|
desc "status", "Check status of dependencies' cached licenses"
|
18
21
|
method_option :config, aliases: "-c", type: :string,
|
19
22
|
desc: "Path to licensed configuration file"
|
23
|
+
method_option :sources, aliases: "-s", type: :array,
|
24
|
+
desc: "Individual source(s) to evaluate. Must also be enabled via configuration."
|
20
25
|
def status
|
21
|
-
run Licensed::Commands::Status.new(config: config)
|
26
|
+
run Licensed::Commands::Status.new(config: config),
|
27
|
+
{ sources: options[:sources] }
|
22
28
|
end
|
23
29
|
|
24
30
|
desc "list", "List dependencies"
|
25
31
|
method_option :config, aliases: "-c", type: :string,
|
26
32
|
desc: "Path to licensed configuration file"
|
33
|
+
method_option :sources, aliases: "-s", type: :array,
|
34
|
+
desc: "Individual source(s) to evaluate. Must also be enabled via configuration."
|
27
35
|
def list
|
28
|
-
run Licensed::Commands::List.new(config: config)
|
36
|
+
run Licensed::Commands::List.new(config: config),
|
37
|
+
{ sources: options[:sources] }
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "notices", "Generate a NOTICE file from cached records"
|
41
|
+
method_option :config, aliases: "-c", type: :string,
|
42
|
+
desc: "Path to licensed configuration file"
|
43
|
+
method_option :sources, aliases: "-s", type: :array,
|
44
|
+
desc: "Individual source(s) to evaluate. Must also be enabled via configuration."
|
45
|
+
def notices
|
46
|
+
run Licensed::Commands::Notices.new(config: config),
|
47
|
+
{ sources: options[:sources] }
|
29
48
|
end
|
30
49
|
|
31
50
|
map "-v" => :version
|
data/lib/licensed/commands.rb
CHANGED
@@ -11,21 +11,47 @@ module Licensed
|
|
11
11
|
Licensed::Reporters::CacheReporter.new
|
12
12
|
end
|
13
13
|
|
14
|
+
# Run the command.
|
15
|
+
# Removes any cached records that don't match a current application
|
16
|
+
# dependency.
|
17
|
+
#
|
18
|
+
# options - Options to run the command with
|
19
|
+
#
|
20
|
+
# Returns whether the command was a success
|
21
|
+
def run(**options)
|
22
|
+
begin
|
23
|
+
result = super
|
24
|
+
clear_stale_cached_records if result
|
25
|
+
|
26
|
+
result
|
27
|
+
ensure
|
28
|
+
cache_paths.clear
|
29
|
+
files.clear
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
14
33
|
protected
|
15
34
|
|
16
35
|
# Run the command for all enumerated dependencies found in a dependency source,
|
17
36
|
# recording results in a report.
|
18
|
-
#
|
19
|
-
#
|
37
|
+
# Enumerating dependencies in the source is skipped if a :sources option
|
38
|
+
# is provided and the evaluated `source.class.type` is not in the :sources values
|
20
39
|
#
|
21
40
|
# app - The application configuration for the source
|
22
41
|
# source - A dependency source enumerator
|
23
42
|
#
|
24
43
|
# Returns whether the command succeeded for the dependency source enumerator
|
25
44
|
def run_source(app, source)
|
26
|
-
|
27
|
-
|
28
|
-
|
45
|
+
super do |report|
|
46
|
+
if Array(options[:sources]).any? && !options[:sources].include?(source.class.type)
|
47
|
+
report.warnings << "skipped source"
|
48
|
+
next :skip
|
49
|
+
end
|
50
|
+
|
51
|
+
# add the full cache path to the list of cache paths
|
52
|
+
# that should be cleaned up after the command run
|
53
|
+
cache_paths << app.cache_path.join(source.class.type)
|
54
|
+
end
|
29
55
|
end
|
30
56
|
|
31
57
|
# Cache dependency record data.
|
@@ -62,6 +88,9 @@ module Licensed
|
|
62
88
|
report.warnings << "expected dependency path #{dependency.path} does not exist"
|
63
89
|
end
|
64
90
|
|
91
|
+
# add the absolute dependency file path to the list of files seen during this licensed run
|
92
|
+
files << filename.to_s
|
93
|
+
|
65
94
|
true
|
66
95
|
end
|
67
96
|
|
@@ -86,18 +115,26 @@ module Licensed
|
|
86
115
|
|
87
116
|
# Clean up cached files that dont match current dependencies
|
88
117
|
#
|
89
|
-
# app - An application configuration
|
90
|
-
# source - A dependency source enumerator
|
91
|
-
#
|
92
118
|
# Returns nothing
|
93
|
-
def clear_stale_cached_records
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
119
|
+
def clear_stale_cached_records
|
120
|
+
cache_paths.each do |cache_path|
|
121
|
+
Dir.glob(cache_path.join("**/*.#{DependencyRecord::EXTENSION}")).each do |file|
|
122
|
+
next if files.include?(file)
|
123
|
+
|
124
|
+
FileUtils.rm(file)
|
125
|
+
end
|
99
126
|
end
|
100
127
|
end
|
128
|
+
|
129
|
+
# Set of unique cache paths that are evaluted during the run
|
130
|
+
def cache_paths
|
131
|
+
@cache_paths ||= Set.new
|
132
|
+
end
|
133
|
+
|
134
|
+
# Set of unique absolute file paths of cached records evaluted during the run
|
135
|
+
def files
|
136
|
+
@files ||= Set.new
|
137
|
+
end
|
101
138
|
end
|
102
139
|
end
|
103
140
|
end
|
@@ -21,7 +21,9 @@ module Licensed
|
|
21
21
|
begin
|
22
22
|
result = reporter.report_run(self) do |report|
|
23
23
|
# allow additional report data to be given by commands
|
24
|
-
|
24
|
+
if block_given?
|
25
|
+
next if (yield report) == :skip
|
26
|
+
end
|
25
27
|
|
26
28
|
config.apps.sort_by { |app| app["name"] }
|
27
29
|
.map { |app| run_app(app) }
|
@@ -57,7 +59,9 @@ module Licensed
|
|
57
59
|
Dir.chdir app.source_path do
|
58
60
|
begin
|
59
61
|
# allow additional report data to be given by commands
|
60
|
-
|
62
|
+
if block_given?
|
63
|
+
next if (yield report) == :skip
|
64
|
+
end
|
61
65
|
|
62
66
|
app.sources.select(&:enabled?)
|
63
67
|
.sort_by { |source| source.class.type }
|
@@ -81,7 +85,9 @@ module Licensed
|
|
81
85
|
reporter.report_source(source) do |report|
|
82
86
|
begin
|
83
87
|
# allow additional report data to be given by commands
|
84
|
-
|
88
|
+
if block_given?
|
89
|
+
next if (yield report) == :skip
|
90
|
+
end
|
85
91
|
|
86
92
|
source.dependencies.sort_by { |dependency| dependency.name }
|
87
93
|
.map { |dependency| run_dependency(app, source, dependency) }
|
@@ -114,7 +120,9 @@ module Licensed
|
|
114
120
|
|
115
121
|
begin
|
116
122
|
# allow additional report data to be given by commands
|
117
|
-
|
123
|
+
if block_given?
|
124
|
+
next if (yield report) == :skip
|
125
|
+
end
|
118
126
|
|
119
127
|
evaluate_dependency(app, source, dependency, report)
|
120
128
|
rescue Licensed::Shell::Error => err
|
@@ -13,6 +13,25 @@ module Licensed
|
|
13
13
|
|
14
14
|
protected
|
15
15
|
|
16
|
+
# Run the command for all enumerated dependencies found in a dependency source,
|
17
|
+
# recording results in a report.
|
18
|
+
# Enumerating dependencies in the source is skipped if a :sources option
|
19
|
+
# is provided and the evaluated `source.class.type` is not in the :sources values
|
20
|
+
#
|
21
|
+
# app - The application configuration for the source
|
22
|
+
# source - A dependency source enumerator
|
23
|
+
#
|
24
|
+
# Returns whether the command succeeded for the dependency source enumerator
|
25
|
+
def run_source(app, source)
|
26
|
+
super do |report|
|
27
|
+
next if Array(options[:sources]).empty?
|
28
|
+
next if options[:sources].include?(source.class.type)
|
29
|
+
|
30
|
+
report.warnings << "skipped source"
|
31
|
+
:skip
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
16
35
|
# Listing dependencies requires no extra work.
|
17
36
|
#
|
18
37
|
# app - The application configuration for the dependency
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Licensed
|
3
|
+
module Commands
|
4
|
+
class Notices < Command
|
5
|
+
# Create a reporter to use during a command run
|
6
|
+
#
|
7
|
+
# options - The options the command was run with
|
8
|
+
#
|
9
|
+
# Raises a Licensed::Reporters::CacheReporter
|
10
|
+
def create_reporter(options)
|
11
|
+
Licensed::Reporters::NoticesReporter.new
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
# Run the command for all enumerated dependencies found in a dependency source,
|
17
|
+
# recording results in a report.
|
18
|
+
# Enumerating dependencies in the source is skipped if a :sources option
|
19
|
+
# is provided and the evaluated `source.class.type` is not in the :sources values
|
20
|
+
#
|
21
|
+
# app - The application configuration for the source
|
22
|
+
# source - A dependency source enumerator
|
23
|
+
#
|
24
|
+
# Returns whether the command succeeded for the dependency source enumerator
|
25
|
+
def run_source(app, source)
|
26
|
+
super do |report|
|
27
|
+
next if Array(options[:sources]).empty?
|
28
|
+
next if options[:sources].include?(source.class.type)
|
29
|
+
|
30
|
+
report.warnings << "skipped source"
|
31
|
+
:skip
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Load stored dependency record data to add to the notices report.
|
36
|
+
#
|
37
|
+
# app - The application configuration for the dependency
|
38
|
+
# source - The dependency source enumerator for the dependency
|
39
|
+
# dependency - An application dependency
|
40
|
+
# report - A report hash for the command to provide extra data for the report output.
|
41
|
+
#
|
42
|
+
# Returns true.
|
43
|
+
def evaluate_dependency(app, source, dependency, report)
|
44
|
+
filename = app.cache_path.join(source.class.type, "#{dependency.name}.#{DependencyRecord::EXTENSION}")
|
45
|
+
report["cached_record"] = Licensed::DependencyRecord.read(filename)
|
46
|
+
if !report["cached_record"]
|
47
|
+
report.warnings << "expected cached record not found at #{filename}"
|
48
|
+
end
|
49
|
+
|
50
|
+
true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|