licensed 1.5.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|