licensed 0.10.0 → 0.11.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/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
|