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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d5a1817146cc5f614b0e2a105173d7576822350
4
- data.tar.gz: 9943ff25b44537ea166f43d7bcf129ac936bb0f2
3
+ metadata.gz: c3597f50e8d15e450f50fca7be0b50357cc26fa5
4
+ data.tar.gz: cd5174f2e14dc8a752441fae45311ce3aaa5a16d
5
5
  SHA512:
6
- metadata.gz: 4ae78971d00c63a2fb9a623ea1fd80dfb16bcf9937f58d34c874c698f76af676729f6ce6dc11baaacd3542f8fc17d4c392f6ea87d2b3b2ec95ecd46dae51f869
7
- data.tar.gz: fb06c9f7c7c1b367b836bbf1b122f964eca15138d356a4baf1dc6ff05aa8c98ead899f9283dc274b5f1fb75698e68c9ebaff51ea1dd5f17b3a999ff179ce9ec9
6
+ metadata.gz: ed0c77a45225ab16a635bc1f8965523a5f0b140458b96b574cd69cb5621c4d5768e8917f827b3af433e0c9726de7628a7aae3dd6fb6ab99853c78f98578b5d4f
7
+ data.tar.gz: d1860f3c82f756a6de3f55fefef58385afeaefcec058cf50b3c30a67a627be3e63c44036709c4ed23f5fd8544794239c75bf984640cf662ca54c6dd9260299a5
data/.gitignore CHANGED
@@ -12,4 +12,6 @@ test/fixtures/node_modules
12
12
  test/fixtures/go/src/*
13
13
  !test/fixtures/go/src/test
14
14
  test/**/.stack-work
15
+ test/**/haskell/dist*
15
16
  vendor/licenses
17
+ *.gem
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 install
5
+ if [ -n "$(which bundle)" ]; then
6
+ bundle install
7
+ fi
6
8
 
7
9
  # Install bower fixtures
8
- bower install
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 install
17
+ if [ -n "$(which npm)" ]; then
18
+ npm install
19
+ fi
15
20
 
16
21
  # Install stack fixtures
17
- stack build
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
- cd go/src/test
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
@@ -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
- response["license"]["key"],
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
@@ -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.dependencies.each do |dependency|
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.dependencies.map do |dependency|
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.dependencies.size}"
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.map(&:dependencies).flatten
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
 
@@ -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
- license = project.license.key if project.license
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
- self["license"] = license || package_manager_license || "other"
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 package_manager_license
47
- project.license.key if project.license
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
- # Extract legal notices from the dependency source
51
- def notices
52
- return [] unless Dir.exist? path
57
+ private
53
58
 
54
- files = Dir.foreach(path).select do |file|
55
- File.file?(File.join(path, file)) && file.match(LEGAL_FILES)
56
- end
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
- if license_file = project.license_file
59
- files.unshift File.join(license_file[:dir], license_file.filename)
60
- end
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
- files.uniq.map do |f|
63
- absolute_path = Pathname.new(f).absolute? ? f : File.join(path, f)
64
- File.read(absolute_path)
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
@@ -9,7 +9,7 @@ module Licensed
9
9
  end
10
10
 
11
11
  def type
12
- "go"
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
- Dependency.new(package["Dir"], {
30
- "type" => type,
31
- "name" => import_path,
32
- "summary" => package["Doc"],
33
- "homepage" => homepage(import_path),
34
- "search_root" => search_root(package),
35
- "version" => package_version(package["Dir"])
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["Deps"]
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["Deps"]
57
+ root_package['Deps']
57
58
  .uniq
58
59
  .select { |d| !go_std_packages.include?(d) }
59
- .select { |d| !d.start_with?(root_package["ImportPath"]) || vendored_package?(d) }
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(package)
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 vendor_dir if vendored_package?(package["ImportPath"])
71
- ENV.fetch("GOPATH", nil)
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
- # import_path - Package import path to test
88
- def vendored_package?(import_path)
89
- import_path && import_path.start_with?(vendor_import_path)
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.gsub(vendor_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(common_dir(sources), {
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 common_dir(sources)
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
- # if there's no common prefix, use the directory of the first source
32
- common_prefix = Pathname.new(sources[0]).dirname unless common_prefix
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
- Pathname.new(@config.path).join(common_prefix).expand_path.to_path
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
@@ -1,3 +1,3 @@
1
1
  module Licensed
2
- VERSION = "0.10.0"
2
+ VERSION = "0.11.0"
3
3
  end
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.10.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-09-12 00:00:00.000000000 Z
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