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 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