licensed 1.5.2 → 2.0.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/.gitignore +2 -0
- data/CHANGELOG.md +22 -1
- data/CONTRIBUTING.md +2 -2
- data/README.md +17 -24
- data/Rakefile +2 -2
- data/docs/adding_a_new_source.md +93 -0
- data/docs/commands.md +81 -0
- data/docs/configuration.md +8 -8
- data/docs/migrating_to_newer_versions.md +3 -0
- data/docs/reporters.md +174 -0
- data/docs/sources/bundler.md +5 -5
- data/lib/licensed.rb +5 -14
- data/lib/licensed/cli.rb +23 -9
- data/lib/licensed/commands.rb +9 -0
- data/lib/licensed/commands/cache.rb +82 -0
- data/lib/licensed/commands/command.rb +112 -0
- data/lib/licensed/commands/list.rb +24 -0
- data/lib/licensed/commands/status.rb +49 -0
- data/lib/licensed/configuration.rb +3 -8
- data/lib/licensed/dependency.rb +116 -58
- data/lib/licensed/dependency_record.rb +76 -0
- data/lib/licensed/migrations.rb +7 -0
- data/lib/licensed/migrations/v2.rb +65 -0
- data/lib/licensed/reporters.rb +9 -0
- data/lib/licensed/reporters/cache_reporter.rb +76 -0
- data/lib/licensed/reporters/list_reporter.rb +69 -0
- data/lib/licensed/reporters/reporter.rb +119 -0
- data/lib/licensed/reporters/status_reporter.rb +67 -0
- data/lib/licensed/shell.rb +8 -10
- data/lib/licensed/sources.rb +15 -0
- data/lib/licensed/{source → sources}/bower.rb +14 -19
- data/lib/licensed/{source → sources}/bundler.rb +73 -48
- data/lib/licensed/{source → sources}/cabal.rb +40 -46
- data/lib/licensed/{source → sources}/dep.rb +15 -27
- data/lib/licensed/{source → sources}/git_submodule.rb +14 -19
- data/lib/licensed/{source → sources}/go.rb +28 -35
- data/lib/licensed/{source → sources}/manifest.rb +68 -90
- data/lib/licensed/{source → sources}/npm.rb +16 -25
- data/lib/licensed/{source → sources}/pip.rb +23 -25
- data/lib/licensed/sources/source.rb +69 -0
- data/lib/licensed/ui/shell.rb +4 -0
- data/lib/licensed/version.rb +6 -1
- data/licensed.gemspec +4 -4
- data/script/source-setup/bundler +1 -1
- metadata +32 -18
- data/lib/licensed/command/cache.rb +0 -82
- data/lib/licensed/command/list.rb +0 -43
- data/lib/licensed/command/status.rb +0 -79
- data/lib/licensed/command/version.rb +0 -18
- data/lib/licensed/license.rb +0 -68
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Licensed
|
4
|
+
module Sources
|
5
|
+
class Source
|
6
|
+
class DependencyEnumerationNotImplementedError < StandardError
|
7
|
+
def initialize(message = "Source classes must implemented `enumerate_dependencies`")
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_reader :sources
|
16
|
+
def inherited(klass)
|
17
|
+
# add child source classes are defined,
|
18
|
+
# add them to the known sources list
|
19
|
+
(@sources ||= []) << klass
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the source name as the snake cased class name
|
23
|
+
def type
|
24
|
+
self.name.split(/::/)
|
25
|
+
.last
|
26
|
+
.gsub(/([A-Z\d]+)([A-Z][a-z])/, "\\1_\\2".freeze)
|
27
|
+
.gsub(/([a-z\d])([A-Z])/, "\\1_\\2".freeze)
|
28
|
+
.downcase
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# all sources have a configuration
|
33
|
+
attr_accessor :config
|
34
|
+
|
35
|
+
def initialize(configuration)
|
36
|
+
@config = configuration
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns whether a source is enabled based on the environment in which licensed is run
|
40
|
+
# Defaults to false.
|
41
|
+
def enabled?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns all dependencies that should be evaluated.
|
46
|
+
# Excludes ignored dependencies.
|
47
|
+
def dependencies
|
48
|
+
cached_dependencies.reject { |d| ignored?(d) }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Enumerate all source dependencies. Must be implemented by each source class.
|
52
|
+
def enumerate_dependencies
|
53
|
+
raise DependencyEnumerationNotImplementedError
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns whether a dependency is ignored in the configuration.
|
57
|
+
def ignored?(dependency)
|
58
|
+
config.ignored?("type" => self.class.type, "name" => dependency.name)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Returns a cached list of dependencies
|
64
|
+
def cached_dependencies
|
65
|
+
@dependencies ||= enumerate_dependencies.compact
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/licensed/ui/shell.rb
CHANGED
data/lib/licensed/version.rb
CHANGED
data/licensed.gemspec
CHANGED
@@ -24,10 +24,10 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.required_ruby_version = ">= 2.3.0"
|
25
25
|
|
26
26
|
spec.add_dependency "licensee", "~> 9.0"
|
27
|
-
spec.add_dependency "thor", "~>0.19"
|
28
|
-
spec.add_dependency "pathname-common_prefix", "~>0.0.1"
|
29
|
-
spec.add_dependency "tomlrb", "~>1.2"
|
30
|
-
spec.add_dependency "bundler", "
|
27
|
+
spec.add_dependency "thor", "~> 0.19"
|
28
|
+
spec.add_dependency "pathname-common_prefix", "~> 0.0.1"
|
29
|
+
spec.add_dependency "tomlrb", "~> 1.2"
|
30
|
+
spec.add_dependency "bundler", ">= 1.10"
|
31
31
|
|
32
32
|
spec.add_development_dependency "rake", "~> 10.0"
|
33
33
|
spec.add_development_dependency "minitest", "~> 5.8"
|
data/script/source-setup/bundler
CHANGED
@@ -14,7 +14,7 @@ cd $BASE_PATH/test/fixtures/bundler
|
|
14
14
|
unset BUNDLE_GEMFILE
|
15
15
|
|
16
16
|
if [ "$1" == "-f" ]; then
|
17
|
-
find . -not -regex "\.*" -and -not -name "Gemfile" -print0 | xargs -0 rm -rf
|
17
|
+
find . -not -regex "\.*" -and -not -name "Gemfile" -and -not \( -path ./pathed-gem-fixture -prune \) -print0 | xargs -0 rm -rf
|
18
18
|
fi
|
19
19
|
|
20
20
|
bundle install --path vendor/gems --without ignore
|
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:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: licensee
|
@@ -70,14 +70,14 @@ dependencies:
|
|
70
70
|
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '1.10'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '1.10'
|
83
83
|
- !ruby/object:Gem::Dependency
|
@@ -170,8 +170,12 @@ files:
|
|
170
170
|
- README.md
|
171
171
|
- Rakefile
|
172
172
|
- docker/Dockerfile.build-linux
|
173
|
+
- docs/adding_a_new_source.md
|
174
|
+
- docs/commands.md
|
173
175
|
- docs/configuration.md
|
176
|
+
- docs/migrating_to_newer_versions.md
|
174
177
|
- docs/packaging.md
|
178
|
+
- docs/reporters.md
|
175
179
|
- docs/sources/bower.md
|
176
180
|
- docs/sources/bundler.md
|
177
181
|
- docs/sources/cabal.md
|
@@ -185,24 +189,34 @@ files:
|
|
185
189
|
- exe/licensed
|
186
190
|
- lib/licensed.rb
|
187
191
|
- lib/licensed/cli.rb
|
188
|
-
- lib/licensed/
|
189
|
-
- lib/licensed/
|
190
|
-
- lib/licensed/command
|
191
|
-
- lib/licensed/
|
192
|
+
- lib/licensed/commands.rb
|
193
|
+
- lib/licensed/commands/cache.rb
|
194
|
+
- lib/licensed/commands/command.rb
|
195
|
+
- lib/licensed/commands/list.rb
|
196
|
+
- lib/licensed/commands/status.rb
|
192
197
|
- lib/licensed/configuration.rb
|
193
198
|
- lib/licensed/dependency.rb
|
199
|
+
- lib/licensed/dependency_record.rb
|
194
200
|
- lib/licensed/git.rb
|
195
|
-
- lib/licensed/
|
201
|
+
- lib/licensed/migrations.rb
|
202
|
+
- lib/licensed/migrations/v2.rb
|
203
|
+
- lib/licensed/reporters.rb
|
204
|
+
- lib/licensed/reporters/cache_reporter.rb
|
205
|
+
- lib/licensed/reporters/list_reporter.rb
|
206
|
+
- lib/licensed/reporters/reporter.rb
|
207
|
+
- lib/licensed/reporters/status_reporter.rb
|
196
208
|
- lib/licensed/shell.rb
|
197
|
-
- lib/licensed/
|
198
|
-
- lib/licensed/
|
199
|
-
- lib/licensed/
|
200
|
-
- lib/licensed/
|
201
|
-
- lib/licensed/
|
202
|
-
- lib/licensed/
|
203
|
-
- lib/licensed/
|
204
|
-
- lib/licensed/
|
205
|
-
- lib/licensed/
|
209
|
+
- lib/licensed/sources.rb
|
210
|
+
- lib/licensed/sources/bower.rb
|
211
|
+
- lib/licensed/sources/bundler.rb
|
212
|
+
- lib/licensed/sources/cabal.rb
|
213
|
+
- lib/licensed/sources/dep.rb
|
214
|
+
- lib/licensed/sources/git_submodule.rb
|
215
|
+
- lib/licensed/sources/go.rb
|
216
|
+
- lib/licensed/sources/manifest.rb
|
217
|
+
- lib/licensed/sources/npm.rb
|
218
|
+
- lib/licensed/sources/pip.rb
|
219
|
+
- lib/licensed/sources/source.rb
|
206
220
|
- lib/licensed/ui/shell.rb
|
207
221
|
- lib/licensed/version.rb
|
208
222
|
- licensed.gemspec
|
@@ -1,82 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module Licensed
|
3
|
-
module Command
|
4
|
-
class Cache
|
5
|
-
attr_reader :config
|
6
|
-
|
7
|
-
def initialize(config)
|
8
|
-
@config = config
|
9
|
-
end
|
10
|
-
|
11
|
-
def run(force: false)
|
12
|
-
summary = @config.apps.flat_map do |app|
|
13
|
-
app_name = app["name"]
|
14
|
-
@config.ui.info "Caching licenses for #{app_name}:"
|
15
|
-
|
16
|
-
# load the app environment
|
17
|
-
Dir.chdir app.source_path do
|
18
|
-
|
19
|
-
# map each available app source to it's dependencies
|
20
|
-
app.sources.map do |source|
|
21
|
-
type = source.class.type
|
22
|
-
|
23
|
-
@config.ui.info " #{type} dependencies:"
|
24
|
-
|
25
|
-
names = []
|
26
|
-
cache_path = app.cache_path.join(type)
|
27
|
-
|
28
|
-
# exclude ignored dependencies
|
29
|
-
dependencies = source.dependencies.select { |d| !app.ignored?(d) }
|
30
|
-
|
31
|
-
# ensure each dependency is cached
|
32
|
-
dependencies.each do |dependency|
|
33
|
-
name = dependency.name
|
34
|
-
version = dependency["version"]
|
35
|
-
|
36
|
-
names << name
|
37
|
-
filename = cache_path.join("#{name}.txt")
|
38
|
-
|
39
|
-
# try to load existing license from disk
|
40
|
-
# or default to a blank license
|
41
|
-
license = Licensed::License.read(filename) || Licensed::License.new
|
42
|
-
|
43
|
-
# cached version string exists and did not change, no need to re-cache
|
44
|
-
has_version = !license["version"].nil? && !license["version"].empty?
|
45
|
-
if !force && has_version && version == license["version"]
|
46
|
-
@config.ui.info " Using #{name} (#{version})"
|
47
|
-
next
|
48
|
-
end
|
49
|
-
|
50
|
-
@config.ui.info " Caching #{name} (#{version})"
|
51
|
-
|
52
|
-
dependency.detect_license!
|
53
|
-
# use the cached license value if the license text wasn't updated
|
54
|
-
dependency["license"] = license["license"] if dependency.license_text_match?(license)
|
55
|
-
|
56
|
-
dependency.save(filename)
|
57
|
-
end
|
58
|
-
|
59
|
-
# Clean up cached files that dont match current dependencies
|
60
|
-
Dir.glob(cache_path.join("**/*.txt")).each do |file|
|
61
|
-
file_path = Pathname.new(file)
|
62
|
-
relative_path = file_path.relative_path_from(cache_path).to_s
|
63
|
-
FileUtils.rm(file) unless names.include?(relative_path.chomp(".txt"))
|
64
|
-
end
|
65
|
-
|
66
|
-
"* #{app_name} #{type} dependencies: #{dependencies.size}"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
@config.ui.confirm "License caching complete!"
|
72
|
-
summary.each do |message|
|
73
|
-
@config.ui.confirm message
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def success?
|
78
|
-
true
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module Licensed
|
3
|
-
module Command
|
4
|
-
class List
|
5
|
-
attr_reader :config
|
6
|
-
|
7
|
-
def initialize(config)
|
8
|
-
@config = config
|
9
|
-
end
|
10
|
-
|
11
|
-
def run
|
12
|
-
@config.apps.each do |app|
|
13
|
-
@config.ui.info "Displaying dependencies for #{app["name"]}"
|
14
|
-
Dir.chdir app.source_path do
|
15
|
-
app.sources.each do |source|
|
16
|
-
type = source.class.type
|
17
|
-
|
18
|
-
@config.ui.info " #{type} dependencies:"
|
19
|
-
|
20
|
-
source_dependencies = dependencies(app, source)
|
21
|
-
source_dependencies.each do |dependency|
|
22
|
-
@config.ui.info " Found #{dependency.name} (#{dependency["version"]})"
|
23
|
-
end
|
24
|
-
|
25
|
-
@config.ui.confirm " * #{type} dependencies: #{source_dependencies.size}"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Returns an apps non-ignored dependencies, sorted by name
|
32
|
-
def dependencies(app, source)
|
33
|
-
source.dependencies
|
34
|
-
.select { |d| !app.ignored?(d) }
|
35
|
-
.sort_by { |d| d.name }
|
36
|
-
end
|
37
|
-
|
38
|
-
def success?
|
39
|
-
true
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "yaml"
|
3
|
-
|
4
|
-
module Licensed
|
5
|
-
module Command
|
6
|
-
class Status
|
7
|
-
attr_reader :config
|
8
|
-
|
9
|
-
def initialize(config)
|
10
|
-
@config = config
|
11
|
-
end
|
12
|
-
|
13
|
-
def allowed_or_reviewed?(app, dependency)
|
14
|
-
app.allowed?(dependency) || app.reviewed?(dependency)
|
15
|
-
end
|
16
|
-
|
17
|
-
def app_dependencies(app)
|
18
|
-
app.sources.flat_map(&:dependencies).select { |d| !app.ignored?(d) }
|
19
|
-
end
|
20
|
-
|
21
|
-
def run
|
22
|
-
@results = @config.apps.flat_map do |app|
|
23
|
-
Dir.chdir app.source_path do
|
24
|
-
dependencies = app_dependencies(app)
|
25
|
-
@config.ui.info "Checking licenses for #{app['name']}: #{dependencies.size} dependencies"
|
26
|
-
|
27
|
-
results = dependencies.map do |dependency|
|
28
|
-
name = dependency.name
|
29
|
-
filename = app.cache_path.join(dependency["type"], "#{name}.txt")
|
30
|
-
|
31
|
-
warnings = []
|
32
|
-
|
33
|
-
# verify cached license data for dependency
|
34
|
-
if File.exist?(filename)
|
35
|
-
license = License.read(filename)
|
36
|
-
|
37
|
-
if license["version"] != dependency["version"]
|
38
|
-
warnings << "cached license data out of date"
|
39
|
-
end
|
40
|
-
warnings << "missing license text" if license.license_text.empty?
|
41
|
-
unless allowed_or_reviewed?(app, license)
|
42
|
-
warnings << "license needs reviewed: #{license["license"]}."
|
43
|
-
end
|
44
|
-
else
|
45
|
-
warnings << "cached license data missing"
|
46
|
-
end
|
47
|
-
|
48
|
-
if warnings.size > 0
|
49
|
-
@config.ui.error("F", false)
|
50
|
-
[filename, warnings]
|
51
|
-
else
|
52
|
-
@config.ui.confirm(".", false)
|
53
|
-
nil
|
54
|
-
end
|
55
|
-
end.compact
|
56
|
-
|
57
|
-
unless results.empty?
|
58
|
-
@config.ui.warn "\n\nWarnings:"
|
59
|
-
|
60
|
-
results.each do |filename, warnings|
|
61
|
-
@config.ui.info "\n#{filename}:"
|
62
|
-
warnings.each do |warning|
|
63
|
-
@config.ui.error " - #{warning}"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
puts "\n#{dependencies.size} dependencies checked, #{results.size} warnings found."
|
69
|
-
results
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def success?
|
75
|
-
@results.empty?
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
data/lib/licensed/license.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "yaml"
|
3
|
-
require "fileutils"
|
4
|
-
require "forwardable"
|
5
|
-
require "licensee"
|
6
|
-
|
7
|
-
module Licensed
|
8
|
-
class License
|
9
|
-
include Licensee::ContentHelper
|
10
|
-
extend Forwardable
|
11
|
-
|
12
|
-
YAML_FRONTMATTER_PATTERN = /\A---\s*\n(.*?\n?)^---\s*$\n?(.*)\z/m
|
13
|
-
TEXT_SEPARATOR = ("-" * 80).freeze
|
14
|
-
LICENSE_SEPARATOR = ("*" * 80).freeze
|
15
|
-
|
16
|
-
# Read an existing license file
|
17
|
-
#
|
18
|
-
# filename - A String path to the file
|
19
|
-
#
|
20
|
-
# Returns a Licensed::License
|
21
|
-
def self.read(filename)
|
22
|
-
return unless File.exist?(filename)
|
23
|
-
match = File.read(filename).scrub.match(YAML_FRONTMATTER_PATTERN)
|
24
|
-
new(YAML.load(match[1]), match[2])
|
25
|
-
end
|
26
|
-
|
27
|
-
def_delegators :@metadata, :[], :[]=, :delete
|
28
|
-
|
29
|
-
# The license text and other legal notices
|
30
|
-
attr_accessor :text
|
31
|
-
|
32
|
-
# Construct a new license
|
33
|
-
#
|
34
|
-
# filename - the String path of the file
|
35
|
-
# metadata - a Hash of the metadata for the package
|
36
|
-
# text - a String of the license text and other legal notices
|
37
|
-
def initialize(metadata = {}, text = nil)
|
38
|
-
@metadata = metadata
|
39
|
-
@text = text
|
40
|
-
end
|
41
|
-
|
42
|
-
# Save the metadata and license to a file
|
43
|
-
def save(filename)
|
44
|
-
FileUtils.mkdir_p(File.dirname(filename))
|
45
|
-
File.write(filename, YAML.dump(@metadata) + "---\n#{text}")
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns the license text without any notices
|
49
|
-
def license_text
|
50
|
-
return unless text
|
51
|
-
|
52
|
-
# if the text contains the separator, the first string in the array
|
53
|
-
# should always be the license text whether empty or not.
|
54
|
-
# if the text didn't contain the separator, the text itself is the entirety
|
55
|
-
# of the license text
|
56
|
-
split = text.split(TEXT_SEPARATOR)
|
57
|
-
split.length > 1 ? split.first.rstrip : text.rstrip
|
58
|
-
end
|
59
|
-
alias_method :content, :license_text # use license_text for content matching
|
60
|
-
|
61
|
-
# Returns whether the current license should be updated to `other`
|
62
|
-
# based on whether the normalized license content matches
|
63
|
-
def license_text_match?(other)
|
64
|
-
return false unless other.is_a?(License)
|
65
|
-
self.content_normalized == other.content_normalized
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|