licensed 0.10.0 → 0.11.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/README.md +42 -1
- data/bin/setup +22 -8
- data/lib/licensed.rb +4 -4
- data/lib/licensed/cli.rb +7 -0
- data/lib/licensed/command/cache.rb +7 -7
- data/lib/licensed/command/list.rb +33 -0
- data/lib/licensed/command/verify.rb +5 -4
- data/lib/licensed/configuration.rb +3 -2
- data/lib/licensed/dependency.rb +58 -26
- data/lib/licensed/source/cabal.rb +128 -0
- data/lib/licensed/source/go.rb +20 -28
- data/lib/licensed/source/manifest.rb +14 -7
- data/lib/licensed/version.rb +1 -1
- metadata +46 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3597f50e8d15e450f50fca7be0b50357cc26fa5
|
4
|
+
data.tar.gz: cd5174f2e14dc8a752441fae45311ce3aaa5a16d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed0c77a45225ab16a635bc1f8965523a5f0b140458b96b574cd69cb5621c4d5768e8917f827b3af433e0c9726de7628a7aae3dd6fb6ab99853c78f98578b5d4f
|
7
|
+
data.tar.gz: d1860f3c82f756a6de3f55fefef58385afeaefcec058cf50b3c30a67a627be3e63c44036709c4ed23f5fd8544794239c75bf984640cf662ca54c6dd9260299a5
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -73,10 +73,13 @@ reviewed:
|
|
73
73
|
### Sources
|
74
74
|
|
75
75
|
Dependencies will be automatically detected for
|
76
|
-
1. Bundler
|
76
|
+
1. Bundler (rubygem)
|
77
77
|
2. NPM
|
78
78
|
3. Bower
|
79
79
|
4. HaskellStack
|
80
|
+
5. Cabal
|
81
|
+
6. Go
|
82
|
+
7. Manifest lists
|
80
83
|
|
81
84
|
You can disable any of them in `vendor/licenses/config.yml`:
|
82
85
|
|
@@ -88,12 +91,50 @@ sources:
|
|
88
91
|
stack: false
|
89
92
|
```
|
90
93
|
|
94
|
+
#### Special Considerations for Sources
|
95
|
+
##### rubygem
|
96
|
+
The rubygem source will explicitly exclude gems in the `:development` and `:test` groups. Be aware that if you have a local
|
97
|
+
bundler configuration (e.g. `.bundle`), that configuration will be respected as well. For example, if you have a local
|
98
|
+
configuration set for `without: [':server']`, the rubygem source will exclude all gems in the `:server` group.
|
99
|
+
|
100
|
+
##### cabal
|
101
|
+
Cabal sourced dependencies are found exclusively through `ghc-pkg`. `licensed` makes no assumptions on where `ghc` package dbs are found.
|
102
|
+
As a result, it is up to the caller to set `GHC_PACKAGE_PATHS` to all package db directories prior to calling into `licensed`.
|
103
|
+
|
104
|
+
##### manifests
|
105
|
+
Manifests are intended to be a stopgap if no package managers are available. The manifest is a JSON file that should be placed in
|
106
|
+
the same directory as `config.yml` and should have the following format
|
107
|
+
```JSON
|
108
|
+
{
|
109
|
+
"file1": "package1",
|
110
|
+
"path/to/file2": "package1",
|
111
|
+
"other/file3": "package2"
|
112
|
+
}
|
113
|
+
```
|
114
|
+
Paths to files are expected to be relative to the git repository root. Package names will match 1:1 with metadata files at `<licenses directory>/manifest/*.txt`.
|
115
|
+
|
116
|
+
It is the responsibility of the repository owner to maintain the manifest file.
|
117
|
+
|
91
118
|
## Development
|
92
119
|
|
93
120
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
94
121
|
|
95
122
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
96
123
|
|
124
|
+
#### Adding sources
|
125
|
+
|
126
|
+
When adding new dependency sources, ensure that `bin/setup` scripting and tests are only run if the required tooling is available on the development machine.
|
127
|
+
|
128
|
+
* See `bin/setup` for examples of gating scripting based on whether tooling executables are found.
|
129
|
+
* Use `tool_available?` when writing test files to gate running a test suite when tooling executables aren't available.
|
130
|
+
```ruby
|
131
|
+
if tool_available?('bundle')
|
132
|
+
describe Licensed::Source::Bundler do
|
133
|
+
...
|
134
|
+
end
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
97
138
|
## Contributing
|
98
139
|
|
99
140
|
Bug reports and pull requests are welcome on GitHub at https://github.com/github/licensed. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org/) code of conduct.
|
data/bin/setup
CHANGED
@@ -2,23 +2,37 @@
|
|
2
2
|
set -euo pipefail
|
3
3
|
IFS=$'\n\t'
|
4
4
|
|
5
|
-
bundle
|
5
|
+
if [ -n "$(which bundle)" ]; then
|
6
|
+
bundle install
|
7
|
+
fi
|
6
8
|
|
7
9
|
# Install bower fixtures
|
8
|
-
bower
|
9
|
-
|
10
|
+
if [ -n "$(which bower)" ]; then
|
11
|
+
bower install
|
12
|
+
fi
|
10
13
|
|
11
14
|
cd test/fixtures
|
12
15
|
|
13
16
|
# Install npm fixtures
|
14
|
-
npm
|
17
|
+
if [ -n "$(which npm)" ]; then
|
18
|
+
npm install
|
19
|
+
fi
|
15
20
|
|
16
21
|
# Install stack fixtures
|
17
|
-
stack
|
22
|
+
if [ -n "$(which stack)" ]; then
|
23
|
+
stack build
|
24
|
+
fi
|
18
25
|
|
19
|
-
if [ -n $(which go) ]; then
|
26
|
+
if [ -n "$(which go)" ]; then
|
20
27
|
export GOPATH="`pwd`/go"
|
21
28
|
|
22
|
-
|
23
|
-
go get
|
29
|
+
pushd go/src/test
|
30
|
+
go get || true
|
31
|
+
popd
|
32
|
+
fi
|
33
|
+
|
34
|
+
if [ -n "$(which cabal)" ]; then
|
35
|
+
pushd haskell
|
36
|
+
cabal new-build
|
37
|
+
popd
|
24
38
|
fi
|
data/lib/licensed.rb
CHANGED
@@ -8,8 +8,10 @@ require "licensed/source/manifest"
|
|
8
8
|
require "licensed/source/npm"
|
9
9
|
require "licensed/source/stack"
|
10
10
|
require "licensed/source/go"
|
11
|
+
require "licensed/source/cabal"
|
11
12
|
require "licensed/command/cache"
|
12
13
|
require "licensed/command/verify"
|
14
|
+
require "licensed/command/list"
|
13
15
|
require "licensed/ui/shell"
|
14
16
|
require "octokit"
|
15
17
|
|
@@ -28,10 +30,8 @@ module Licensed
|
|
28
30
|
|
29
31
|
license_url = Octokit::Repository.path(match[1]) + "/license"
|
30
32
|
response = octokit.get license_url, :accept => LICENSE_CONTENT_TYPE
|
31
|
-
[
|
32
|
-
|
33
|
-
Base64.decode64(response["content"]).force_encoding('UTF-8')
|
34
|
-
]
|
33
|
+
content = Base64.decode64(response["content"]).force_encoding('UTF-8')
|
34
|
+
Licensee::ProjectFiles::LicenseFile.new(content, response["name"])
|
35
35
|
rescue Octokit::NotFound
|
36
36
|
nil
|
37
37
|
end
|
data/lib/licensed/cli.rb
CHANGED
@@ -23,6 +23,13 @@ module Licensed
|
|
23
23
|
run Licensed::Command::Verify.new(config)
|
24
24
|
end
|
25
25
|
|
26
|
+
desc "list", "List dependencies"
|
27
|
+
method_option "license-dir", :type => :string,
|
28
|
+
:desc => "Path to license storage directory (defaults to vendor/licenses)"
|
29
|
+
def list
|
30
|
+
run Licensed::Command::List.new(config)
|
31
|
+
end
|
32
|
+
|
26
33
|
private
|
27
34
|
|
28
35
|
def config
|
@@ -11,9 +11,7 @@ module Licensed
|
|
11
11
|
@config.sources.each do |source|
|
12
12
|
@config.ui.info "Caching licenses for #{source.type} dependencies:"
|
13
13
|
|
14
|
-
source.
|
15
|
-
next if @config.ignored?(dependency)
|
16
|
-
|
14
|
+
dependencies(source).each do |dependency|
|
17
15
|
filename = @config.path.join("#{source.type}/#{dependency["name"]}.txt")
|
18
16
|
|
19
17
|
if File.exist?(filename)
|
@@ -33,9 +31,7 @@ module Licensed
|
|
33
31
|
end
|
34
32
|
|
35
33
|
# Clean up dependencies that have been removed
|
36
|
-
names = source.
|
37
|
-
dependency["name"] unless @config.ignored?(dependency)
|
38
|
-
end.compact
|
34
|
+
names = dependencies(source).map { |d| d["name"] }
|
39
35
|
|
40
36
|
license_source_path = @config.path.join(source.type)
|
41
37
|
Dir.glob(license_source_path.join("**/*.txt")).each do |file|
|
@@ -47,10 +43,14 @@ module Licensed
|
|
47
43
|
|
48
44
|
@config.ui.confirm "License caching complete!"
|
49
45
|
@config.sources.each do |source|
|
50
|
-
@config.ui.confirm "* #{source.type} dependencies: #{source.
|
46
|
+
@config.ui.confirm "* #{source.type} dependencies: #{dependencies(source).size}"
|
51
47
|
end
|
52
48
|
end
|
53
49
|
|
50
|
+
def dependencies(source)
|
51
|
+
source.dependencies.select { |d| !@config.ignored?(d) }
|
52
|
+
end
|
53
|
+
|
54
54
|
def success?
|
55
55
|
true
|
56
56
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Licensed
|
2
|
+
module Command
|
3
|
+
class List
|
4
|
+
attr_reader :config
|
5
|
+
|
6
|
+
def initialize(config)
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
@config.sources.each do |source|
|
12
|
+
@config.ui.info "Displaying licenses for #{source.type} dependencies:"
|
13
|
+
|
14
|
+
dependencies(source).each do |dependency|
|
15
|
+
@config.ui.info " Found #{dependency['name']} (#{dependency['version']})"
|
16
|
+
end
|
17
|
+
|
18
|
+
@config.ui.confirm "* #{source.type} dependencies: #{dependencies(source).size}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def dependencies(source)
|
23
|
+
source.dependencies
|
24
|
+
.select { |d| !@config.ignored?(d) }
|
25
|
+
.sort_by { |d| d['name'] }
|
26
|
+
end
|
27
|
+
|
28
|
+
def success?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -10,19 +10,20 @@ module Licensed
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def approved?(dependency)
|
13
|
-
@config.whitelisted?(dependency) ||
|
14
|
-
@config.reviewed?(dependency)
|
13
|
+
@config.whitelisted?(dependency) || @config.reviewed?(dependency)
|
15
14
|
end
|
16
15
|
|
17
16
|
def dependencies
|
18
|
-
@dependencies ||= @config.sources
|
17
|
+
@dependencies ||= @config.sources
|
18
|
+
.map(&:dependencies)
|
19
|
+
.flatten
|
20
|
+
.select { |d| !@config.ignored?(d) }
|
19
21
|
end
|
20
22
|
|
21
23
|
def run
|
22
24
|
@config.ui.info "Verifying licenses for #{dependencies.size} dependencies"
|
23
25
|
|
24
26
|
@results = dependencies.map do |dependency|
|
25
|
-
next if @config.ignored?(dependency)
|
26
27
|
filename = @config.path.join("#{dependency["type"]}/#{dependency["name"]}.txt")
|
27
28
|
|
28
29
|
warnings = []
|
@@ -37,10 +37,11 @@ module Licensed
|
|
37
37
|
@sources ||= [
|
38
38
|
Source::Bundler.new(self),
|
39
39
|
Source::Bower.new(self),
|
40
|
+
Source::Cabal.new(self),
|
41
|
+
Source::Go.new(self),
|
40
42
|
Source::Manifest.new(self),
|
41
43
|
Source::NPM.new(self),
|
42
|
-
Source::Stack.new(self)
|
43
|
-
Source::Go.new(self)
|
44
|
+
Source::Stack.new(self)
|
44
45
|
].select(&:enabled?)
|
45
46
|
end
|
46
47
|
|
data/lib/licensed/dependency.rb
CHANGED
@@ -26,42 +26,74 @@ module Licensed
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def detect_license!
|
29
|
+
self["license"] = license_key
|
30
|
+
self.text = ([license_text] + self.notices).compact.join("\n" + "-" * 80 + "\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Extract legal notices from the dependency source
|
34
|
+
def notices
|
35
|
+
files = local_files
|
36
|
+
|
29
37
|
if project.license_file
|
30
|
-
|
31
|
-
else
|
32
|
-
# No license file detected, see if there is one in the GitHub repository
|
33
|
-
license, content = Licensed.from_github(self["homepage"])
|
34
|
-
|
35
|
-
# No license in the GitHub repository, see if there is one in the README
|
36
|
-
if !license && project.readme
|
37
|
-
license = project.license.key if project.license
|
38
|
-
content = project.readme.content
|
39
|
-
end
|
38
|
+
files.unshift File.join(project.license_file[:dir], project.license_file.filename)
|
40
39
|
end
|
41
40
|
|
42
|
-
|
43
|
-
self.text = ([content] + self.notices).compact.join("\n" + "-" * 80 + "\n")
|
41
|
+
files.uniq.map { |f| File.read(f) }
|
44
42
|
end
|
45
43
|
|
46
|
-
def
|
47
|
-
|
44
|
+
def local_files
|
45
|
+
return [] unless Dir.exist?(path)
|
46
|
+
|
47
|
+
Dir.foreach(path).map do |file|
|
48
|
+
next unless file.match(LEGAL_FILES)
|
49
|
+
|
50
|
+
file_path = File.join(path, file)
|
51
|
+
next unless File.file?(file_path)
|
52
|
+
|
53
|
+
file_path
|
54
|
+
end.compact
|
48
55
|
end
|
49
56
|
|
50
|
-
|
51
|
-
def notices
|
52
|
-
return [] unless Dir.exist? path
|
57
|
+
private
|
53
58
|
|
54
|
-
|
55
|
-
|
56
|
-
|
59
|
+
# Returns the Licensee::ProjectFile representing the matched_project_file
|
60
|
+
# or remote_license_file
|
61
|
+
def project_file
|
62
|
+
matched_project_file || remote_license_file
|
63
|
+
end
|
57
64
|
|
58
|
-
|
59
|
-
|
60
|
-
|
65
|
+
# Returns the Licensee::LicenseFile, Licensee::PackageManagerFile, or
|
66
|
+
# Licensee::ReadmeFile with a matched license, in that order or nil
|
67
|
+
# if no license file matched a known license
|
68
|
+
def matched_project_file
|
69
|
+
@matched_project_file ||= project.matched_files
|
70
|
+
.select { |f| f.license && !f.license.other? }
|
71
|
+
.first
|
72
|
+
end
|
61
73
|
|
62
|
-
|
63
|
-
|
64
|
-
|
74
|
+
# Returns a Licensee::LicenseFile with the content of the license in the
|
75
|
+
# dependency's repository to account for LICENSE files not being distributed
|
76
|
+
def remote_license_file
|
77
|
+
@remote_license_file ||= Licensed.from_github(self["homepage"])
|
78
|
+
end
|
79
|
+
|
80
|
+
# Regardless of the license detected, try to pull the license content
|
81
|
+
# from the local LICENSE, remote LICENSE, or the README, in that order
|
82
|
+
def license_text
|
83
|
+
content_file = project.license_file || remote_license_file || project.readme_file
|
84
|
+
content_file.content if content_file
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns a string representing the project's license
|
88
|
+
# Note, this will be "other" if a license file was found but the license
|
89
|
+
# could not be identified and "none" if no license file was found at all
|
90
|
+
def license_key
|
91
|
+
if project_file && project_file.license
|
92
|
+
project_file.license.key
|
93
|
+
elsif project.license_file || remote_license_file
|
94
|
+
"other"
|
95
|
+
else
|
96
|
+
"none"
|
65
97
|
end
|
66
98
|
end
|
67
99
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'English'
|
2
|
+
|
3
|
+
module Licensed
|
4
|
+
module Source
|
5
|
+
class Cabal
|
6
|
+
def initialize(config)
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def type
|
11
|
+
'cabal'
|
12
|
+
end
|
13
|
+
|
14
|
+
def enabled?
|
15
|
+
@config.enabled?(type) && cabal_packages.any? && ghc?
|
16
|
+
end
|
17
|
+
|
18
|
+
def dependencies
|
19
|
+
@dependencies ||= package_ids.map do |id|
|
20
|
+
package = package_info(id)
|
21
|
+
|
22
|
+
path, search_root = package_docs_dirs(package)
|
23
|
+
Dependency.new(path, {
|
24
|
+
'type' => type,
|
25
|
+
'name' => package['name'],
|
26
|
+
'version' => package['version'],
|
27
|
+
'summary' => package['synopsis'],
|
28
|
+
'homepage' => safe_homepage(package['homepage']),
|
29
|
+
'search_root' => search_root
|
30
|
+
})
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def package_docs_dirs(package)
|
35
|
+
unless package['haddock-html']
|
36
|
+
# default to a local vendor directory if haddock-html property
|
37
|
+
# isn't available
|
38
|
+
return [File.join(@config.pwd, 'vendor', package['name']), nil]
|
39
|
+
end
|
40
|
+
|
41
|
+
html_dir = package['haddock-html']
|
42
|
+
data_dir = package['data-dir']
|
43
|
+
return [html_dir, nil] unless data_dir
|
44
|
+
|
45
|
+
# don't use a search root that isn't an ancestor of the haddock-html dir
|
46
|
+
unless Pathname.new(html_dir).fnmatch?(File.join(data_dir, '**'))
|
47
|
+
data_dir = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
[html_dir, data_dir]
|
51
|
+
end
|
52
|
+
|
53
|
+
def safe_homepage(homepage)
|
54
|
+
return unless homepage
|
55
|
+
# use https and remove url fragment
|
56
|
+
homepage.gsub(/http:/, 'https:')
|
57
|
+
.gsub(/#[^?]*\z/, '')
|
58
|
+
end
|
59
|
+
|
60
|
+
def package_ids
|
61
|
+
deps = cabal_packages.flat_map { |n| package_dependencies(n, false) }
|
62
|
+
recursive_dependencies(deps)
|
63
|
+
end
|
64
|
+
|
65
|
+
def recursive_dependencies(package_names, results = Set.new)
|
66
|
+
return [] if package_names.nil? || package_names.empty?
|
67
|
+
|
68
|
+
new_packages = Set.new(package_names) - results.to_a
|
69
|
+
return [] if new_packages.empty?
|
70
|
+
|
71
|
+
results.merge new_packages
|
72
|
+
|
73
|
+
dependencies = new_packages.flat_map { |n| package_dependencies(n) }
|
74
|
+
.compact
|
75
|
+
|
76
|
+
return results if dependencies.empty?
|
77
|
+
|
78
|
+
results.merge recursive_dependencies(dependencies, results)
|
79
|
+
end
|
80
|
+
|
81
|
+
def package_dependencies(id, full_id = true)
|
82
|
+
package_dependencies_command(id, full_id).gsub('depends:', '')
|
83
|
+
.split
|
84
|
+
.map(&:strip)
|
85
|
+
end
|
86
|
+
|
87
|
+
def package_dependencies_command(id, full_id)
|
88
|
+
args = full_id ? '--ipid' : ''
|
89
|
+
fields = %w(depends)
|
90
|
+
|
91
|
+
ghc_pkg_field_command(id, fields, args)
|
92
|
+
end
|
93
|
+
|
94
|
+
def package_info(id)
|
95
|
+
package_info_command(id).lines.each_with_object({}) do |line, info|
|
96
|
+
key, value = line.split(':', 2).map(&:strip)
|
97
|
+
next unless key && value
|
98
|
+
|
99
|
+
info[key] = value
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def package_info_command(id)
|
104
|
+
fields = %w(name version synopsis homepage haddock-html data-dir)
|
105
|
+
ghc_pkg_field_command(id, fields, '--ipid')
|
106
|
+
end
|
107
|
+
|
108
|
+
def ghc_pkg_field_command(id, fields, *args)
|
109
|
+
`ghc-pkg field #{id} #{fields.join(',')} #{args.join(' ')}`
|
110
|
+
end
|
111
|
+
|
112
|
+
def cabal_packages
|
113
|
+
cabal_files.map do |f|
|
114
|
+
name_match = File.read(f).match(/^name:\s*(.*)$/)
|
115
|
+
name_match[1] if name_match
|
116
|
+
end.compact
|
117
|
+
end
|
118
|
+
|
119
|
+
def cabal_files
|
120
|
+
@cabal_files ||= Dir.glob(File.join(@config.pwd, '*.cabal'))
|
121
|
+
end
|
122
|
+
|
123
|
+
def ghc?
|
124
|
+
@ghc = `which ghc 2>/dev/null` && $CHILD_STATUS.success?
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
data/lib/licensed/source/go.rb
CHANGED
@@ -9,7 +9,7 @@ module Licensed
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def type
|
12
|
-
|
12
|
+
'go'
|
13
13
|
end
|
14
14
|
|
15
15
|
def enabled?
|
@@ -26,13 +26,14 @@ module Licensed
|
|
26
26
|
raise "couldn't find package for #{import_path}"
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
29
|
+
package_dir = package['Dir']
|
30
|
+
Dependency.new(package_dir, {
|
31
|
+
'type' => type,
|
32
|
+
'name' => import_path,
|
33
|
+
'summary' => package['Doc'],
|
34
|
+
'homepage' => homepage(import_path),
|
35
|
+
'search_root' => search_root(package_dir),
|
36
|
+
'version' => package_version(package_dir)
|
36
37
|
})
|
37
38
|
end.compact
|
38
39
|
end
|
@@ -49,26 +50,26 @@ module Licensed
|
|
49
50
|
|
50
51
|
# Returns an array of dependency package import paths
|
51
52
|
def packages
|
52
|
-
return [] unless root_package[
|
53
|
+
return [] unless root_package['Deps']
|
53
54
|
|
54
55
|
# don't include go std packages
|
55
56
|
# don't include packages under the root project that aren't vendored
|
56
|
-
root_package[
|
57
|
+
root_package['Deps']
|
57
58
|
.uniq
|
58
59
|
.select { |d| !go_std_packages.include?(d) }
|
59
|
-
.select { |d| !d.start_with?(root_package[
|
60
|
+
.select { |d| !d.start_with?(root_package['ImportPath']) || vendored_path?(d) }
|
60
61
|
end
|
61
62
|
|
62
63
|
# Returns the root directory to search for a package license
|
63
64
|
#
|
64
65
|
# package - package object obtained from package_info
|
65
|
-
def search_root(
|
66
|
+
def search_root(package_dir)
|
66
67
|
# search root choices:
|
67
68
|
# 1. vendor folder if package is vendored
|
68
69
|
# 2. GOPATH
|
69
70
|
# 3. nil (no search up directory hierarchy)
|
70
|
-
return
|
71
|
-
ENV.fetch(
|
71
|
+
return package_dir.match('^(.*/vendor)/.*$')[1] if vendored_path?(package_dir)
|
72
|
+
ENV.fetch('GOPATH', nil)
|
72
73
|
end
|
73
74
|
|
74
75
|
# Returns the most recent git SHA for a package, or nil if SHA is
|
@@ -84,9 +85,9 @@ module Licensed
|
|
84
85
|
# Returns whether a package is vendored or not based on the package
|
85
86
|
# import_path
|
86
87
|
#
|
87
|
-
#
|
88
|
-
def
|
89
|
-
|
88
|
+
# path - Package path to test
|
89
|
+
def vendored_path?(path)
|
90
|
+
path && path.include?('vendor/')
|
90
91
|
end
|
91
92
|
|
92
93
|
# Returns the import path parameter without the vendor component
|
@@ -94,7 +95,8 @@ module Licensed
|
|
94
95
|
# import_path - Package import path with vendor component
|
95
96
|
def non_vendored_import_path(import_path)
|
96
97
|
return unless import_path
|
97
|
-
import_path
|
98
|
+
return import_path unless vendored_path?(import_path)
|
99
|
+
import_path.split('vendor/')[1]
|
98
100
|
end
|
99
101
|
|
100
102
|
# Returns package information, or {} if package isn't found
|
@@ -113,16 +115,6 @@ module Licensed
|
|
113
115
|
`go list -json #{package}`
|
114
116
|
end
|
115
117
|
|
116
|
-
# Returns the root directory for vendored packages
|
117
|
-
def vendor_dir
|
118
|
-
"#{root_package["Dir"]}/vendor"
|
119
|
-
end
|
120
|
-
|
121
|
-
# Returns the import path for vendored packages
|
122
|
-
def vendor_import_path
|
123
|
-
"#{root_package["ImportPath"]}/vendor/"
|
124
|
-
end
|
125
|
-
|
126
118
|
# Returns the info for the package under test
|
127
119
|
def root_package
|
128
120
|
@root_package ||= package_info
|
@@ -17,7 +17,7 @@ module Licensed
|
|
17
17
|
|
18
18
|
def dependencies
|
19
19
|
@dependencies ||= packages.map do |package_name, sources|
|
20
|
-
Dependency.new(
|
20
|
+
Dependency.new(sources_license_path(sources), {
|
21
21
|
'type' => type,
|
22
22
|
'name' => package_name,
|
23
23
|
'version' => package_version(sources)
|
@@ -25,13 +25,16 @@ module Licensed
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
common_prefix = Pathname.common_prefix(*sources)
|
28
|
+
def sources_license_path(sources)
|
29
|
+
common_prefix = Pathname.common_prefix(*sources).to_path
|
30
30
|
|
31
|
-
#
|
32
|
-
|
31
|
+
# don't allow the repo root to be used as common prefix
|
32
|
+
# the project this is run for should be excluded from the manifest,
|
33
|
+
# or ignored in the config. any license in the root should be ignored.
|
34
|
+
return common_prefix if common_prefix != repository_root
|
33
35
|
|
34
|
-
|
36
|
+
# use the first source file as the license path.
|
37
|
+
sources.first
|
35
38
|
end
|
36
39
|
|
37
40
|
def package_version(sources)
|
@@ -54,7 +57,7 @@ module Licensed
|
|
54
57
|
manifest.each_with_object({}) do |(src, package_name), hsh|
|
55
58
|
next if src.nil? || src.empty?
|
56
59
|
hsh[package_name] ||= []
|
57
|
-
hsh[package_name] << src
|
60
|
+
hsh[package_name] << File.join(repository_root, src)
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
@@ -65,6 +68,10 @@ module Licensed
|
|
65
68
|
def manifest_path
|
66
69
|
@config.path.join('manifest.json')
|
67
70
|
end
|
71
|
+
|
72
|
+
def repository_root
|
73
|
+
@root ||= `git rev-parse --show-toplevel`.strip
|
74
|
+
end
|
68
75
|
end
|
69
76
|
end
|
70
77
|
end
|
data/lib/licensed/version.rb
CHANGED
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: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: licensee
|
@@ -212,10 +212,13 @@ description: |
|
|
212
212
|
### Sources
|
213
213
|
|
214
214
|
Dependencies will be automatically detected for
|
215
|
-
1. Bundler
|
215
|
+
1. Bundler (rubygem)
|
216
216
|
2. NPM
|
217
217
|
3. Bower
|
218
218
|
4. HaskellStack
|
219
|
+
5. Cabal
|
220
|
+
6. Go
|
221
|
+
7. Manifest lists
|
219
222
|
|
220
223
|
You can disable any of them in `vendor/licenses/config.yml`:
|
221
224
|
|
@@ -227,12 +230,50 @@ description: |
|
|
227
230
|
stack: false
|
228
231
|
```
|
229
232
|
|
233
|
+
#### Special Considerations for Sources
|
234
|
+
##### rubygem
|
235
|
+
The rubygem source will explicitly exclude gems in the `:development` and `:test` groups. Be aware that if you have a local
|
236
|
+
bundler configuration (e.g. `.bundle`), that configuration will be respected as well. For example, if you have a local
|
237
|
+
configuration set for `without: [':server']`, the rubygem source will exclude all gems in the `:server` group.
|
238
|
+
|
239
|
+
##### cabal
|
240
|
+
Cabal sourced dependencies are found exclusively through `ghc-pkg`. `licensed` makes no assumptions on where `ghc` package dbs are found.
|
241
|
+
As a result, it is up to the caller to set `GHC_PACKAGE_PATHS` to all package db directories prior to calling into `licensed`.
|
242
|
+
|
243
|
+
##### manifests
|
244
|
+
Manifests are intended to be a stopgap if no package managers are available. The manifest is a JSON file that should be placed in
|
245
|
+
the same directory as `config.yml` and should have the following format
|
246
|
+
```JSON
|
247
|
+
{
|
248
|
+
"file1": "package1",
|
249
|
+
"path/to/file2": "package1",
|
250
|
+
"other/file3": "package2"
|
251
|
+
}
|
252
|
+
```
|
253
|
+
Paths to files are expected to be relative to the git repository root. Package names will match 1:1 with metadata files at `<licenses directory>/manifest/*.txt`.
|
254
|
+
|
255
|
+
It is the responsibility of the repository owner to maintain the manifest file.
|
256
|
+
|
230
257
|
## Development
|
231
258
|
|
232
259
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
233
260
|
|
234
261
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
235
262
|
|
263
|
+
#### Adding sources
|
264
|
+
|
265
|
+
When adding new dependency sources, ensure that `bin/setup` scripting and tests are only run if the required tooling is available on the development machine.
|
266
|
+
|
267
|
+
* See `bin/setup` for examples of gating scripting based on whether tooling executables are found.
|
268
|
+
* Use `tool_available?` when writing test files to gate running a test suite when tooling executables aren't available.
|
269
|
+
```ruby
|
270
|
+
if tool_available?('bundle')
|
271
|
+
describe Licensed::Source::Bundler do
|
272
|
+
...
|
273
|
+
end
|
274
|
+
end
|
275
|
+
```
|
276
|
+
|
236
277
|
## Contributing
|
237
278
|
|
238
279
|
Bug reports and pull requests are welcome on GitHub at https://github.com/github/licensed. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org/) code of conduct.
|
@@ -263,12 +304,14 @@ files:
|
|
263
304
|
- lib/licensed.rb
|
264
305
|
- lib/licensed/cli.rb
|
265
306
|
- lib/licensed/command/cache.rb
|
307
|
+
- lib/licensed/command/list.rb
|
266
308
|
- lib/licensed/command/verify.rb
|
267
309
|
- lib/licensed/configuration.rb
|
268
310
|
- lib/licensed/dependency.rb
|
269
311
|
- lib/licensed/license.rb
|
270
312
|
- lib/licensed/source/bower.rb
|
271
313
|
- lib/licensed/source/bundler.rb
|
314
|
+
- lib/licensed/source/cabal.rb
|
272
315
|
- lib/licensed/source/go.rb
|
273
316
|
- lib/licensed/source/manifest.rb
|
274
317
|
- lib/licensed/source/npm.rb
|