licensed 2.9.2 → 2.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- 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 +24 -6
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.1
|
10
|
+
2020-06-30
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
- `licensed` no longer exits an error code when using the `--sources` CLI argument(https://github.com/github/licensed/pull/290)
|
14
|
+
|
15
|
+
## 2.12.0
|
16
|
+
2020-06-19
|
17
|
+
|
18
|
+
### Added
|
19
|
+
- `--sources` argument for cache, list, status and notices commands to filter running sources (https://github.com/github/licensed/pull/287)
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
- `cache` command will not remove files outside of enabled source cache paths (https://github.com/github/licensed/pull/287)
|
23
|
+
|
24
|
+
## 2.11.1
|
25
|
+
2020-06-09
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
- `notices` command properly reads cached dependency notices contents (https://github.com/github/licensed/pull/283)
|
29
|
+
|
30
|
+
## 2.11.0
|
31
|
+
2020-06-02
|
32
|
+
|
33
|
+
### Added
|
34
|
+
- `notices` command to create a `NOTICE` file for each configured app (https://github.com/github/licensed/pull/277)
|
35
|
+
|
36
|
+
### Fixed
|
37
|
+
- NuGet source no longer crashes on a non-existent dependency path (https://github.com/github/licensed/pull/280)
|
38
|
+
- Go source no longer crashes on a non-existent dependency package path (https://github.com/github/licensed/pull/274)
|
39
|
+
|
40
|
+
## 2.10.0
|
41
|
+
2020-05-15
|
42
|
+
|
43
|
+
### Changed
|
44
|
+
- NPM source ignores missing peer dependencies (https://github.com/github/licensed/pull/267)
|
45
|
+
|
46
|
+
### Added
|
47
|
+
- NuGet source (:tada: @zarenner https://github.com/github/licensed/pull/261)
|
48
|
+
- Multiple apps can share a single cache location (https://github.com/github/licensed/pull/263)
|
49
|
+
|
9
50
|
## 2.9.2
|
10
51
|
2020-04-28
|
11
52
|
|
@@ -292,4 +333,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
292
333
|
|
293
334
|
Initial release :tada:
|
294
335
|
|
295
|
-
[Unreleased]: https://github.com/github/licensed/compare/2.
|
336
|
+
[Unreleased]: https://github.com/github/licensed/compare/2.12.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 true 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 true 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 true 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 true 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
|