licensed 2.11.1 → 2.14.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
  SHA256:
3
- metadata.gz: 22d2a3480b9386c6041c7ab17c90ca28ffedfb58f802974c9e7a4aef6c017919
4
- data.tar.gz: c441684e72e0bdc85f55da6e34181758902226dd89f4abf844b6a8cb5c8c9907
3
+ metadata.gz: b246cf67fe29bfe3612770c81b44006cdc185e2875deba36aa339b7f8be6d654
4
+ data.tar.gz: 47e90e173cd914d214c270db673c1ffdff61f155853fec18da41837cebe7740b
5
5
  SHA512:
6
- metadata.gz: 11527ca53a55700ec9626fe8d625ada1539453e53482d4d75394ef8115d79f2ee697f93fbc200eed6dbb0f76dde2138b6874dfd5de7c01ff10fc78c669374b8b
7
- data.tar.gz: b24cedd6ab775e54c0fa503ca79e0ad93eef0f19ae15199aac3db66f891dbf8f71c98bc4fc58dff167e606be589ffee25ccf827da4ca5892b95ae25e356cdb5e
6
+ metadata.gz: 8e651c1ce72c6802b7f13e813ea4227c8dbeb53058deb8f68f1c8eabbcf4b9c4b9bd4a130b2d00b4238e14577f0a341d55fabac5aa72d0c3d0195a4b75bd1c86
7
+ data.tar.gz: b07832f775f62da3fef628bf3ca9769e6f33e9d32338ffe468dad212f27d65d2be455e57ac73786930017d55308cd5222f2280f2944c832a1f32fa5c8a3557bb
@@ -1,18 +1,12 @@
1
- name: Create release
1
+ name: Build and publish release assets
2
2
 
3
- on: create
3
+ on:
4
+ release:
5
+ types: [created]
4
6
 
5
7
  jobs:
6
- tag_filter:
7
- runs-on: ubuntu-latest
8
- if: startsWith(github.ref, 'refs/tags/')
9
- steps:
10
- - run: exit 0
11
-
12
8
  package_linux:
13
9
  runs-on: ubuntu-latest
14
- needs: tag_filter
15
-
16
10
  steps:
17
11
  - uses: actions/checkout@v2
18
12
  - name: Set up Ruby 2.6
@@ -23,17 +17,15 @@ jobs:
23
17
  - name: Build package
24
18
  run: script/packages/linux
25
19
  env:
26
- VERSION: ${{github.event.ref}}
20
+ VERSION: ${{github.event.release.tag_name}}
27
21
 
28
22
  - uses: actions/upload-artifact@v2
29
23
  with:
30
- name: ${{github.event.ref}}-linux
31
- path: pkg/${{github.event.ref}}/licensed-${{github.event.ref}}-linux-x64.tar.gz
24
+ name: ${{github.event.release.tag_name}}-linux
25
+ path: pkg/${{github.event.release.tag_name}}/licensed-${{github.event.release.tag_name}}-linux-x64.tar.gz
32
26
 
33
27
  package_mac:
34
28
  runs-on: macOS-latest
35
- needs: tag_filter
36
-
37
29
  steps:
38
30
  - uses: actions/checkout@v2
39
31
  - name: Set up Ruby 2.6
@@ -44,17 +36,15 @@ jobs:
44
36
  - name: Build package
45
37
  run: script/packages/mac
46
38
  env:
47
- VERSION: ${{github.event.ref}}
39
+ VERSION: ${{github.event.release.tag_name}}
48
40
 
49
41
  - uses: actions/upload-artifact@v2
50
42
  with:
51
- name: ${{github.event.ref}}-darwin
52
- path: pkg/${{github.event.ref}}/licensed-${{github.event.ref}}-darwin-x64.tar.gz
43
+ name: ${{github.event.release.tag_name}}-darwin
44
+ path: pkg/${{github.event.release.tag_name}}/licensed-${{github.event.release.tag_name}}-darwin-x64.tar.gz
53
45
 
54
46
  build_gem:
55
47
  runs-on: ubuntu-latest
56
- needs: tag_filter
57
-
58
48
  steps:
59
49
  - uses: actions/checkout@v2
60
50
  - name: Set up Ruby 2.6
@@ -63,25 +53,16 @@ jobs:
63
53
  ruby-version: 2.6.x
64
54
 
65
55
  - name: Build gem
66
- run: gem build *.gemspec
56
+ run: gem build licensed.gemspec -o licensed-${{github.event.release.tag_name}}.gem
67
57
 
68
58
  - uses: actions/upload-artifact@v2
69
59
  with:
70
- name: ${{github.event.ref}}-gem
71
- path: licensed-${{github.event.ref}}.gem
72
-
73
- create_release:
74
- runs-on: ubuntu-latest
75
- needs: [package_linux, package_mac, build_gem]
76
- steps:
77
- - uses: Roang-zero1/github-create-release-action@v1.0.2
78
- env:
79
- GITHUB_TOKEN: ${{ secrets.API_AUTH_TOKEN }}
80
- VERSION_REGEX: "^[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+"
60
+ name: ${{github.event.release.tag_name}}-gem
61
+ path: licensed-${{github.event.release.tag_name}}.gem
81
62
 
82
63
  upload_packages:
83
64
  runs-on: ubuntu-latest
84
- needs: [create_release]
65
+ needs: [package_linux, package_mac, build_gem]
85
66
 
86
67
  steps:
87
68
  - name: Set up Ruby 2.6
@@ -92,32 +73,45 @@ jobs:
92
73
  - name: Download linux package
93
74
  uses: actions/download-artifact@v2
94
75
  with:
95
- name: ${{github.event.ref}}-linux
76
+ name: ${{github.event.release.tag_name}}-linux
96
77
 
97
78
  - name: Download macOS package
98
79
  uses: actions/download-artifact@v2
99
80
  with:
100
- name: ${{github.event.ref}}-darwin
81
+ name: ${{github.event.release.tag_name}}-darwin
101
82
 
102
83
  - name: Download gem
103
84
  uses: actions/download-artifact@v2
104
85
  with:
105
- name: ${{github.event.ref}}-gem
86
+ name: ${{github.event.release.tag_name}}-gem
106
87
 
107
- - name: Publish packages to GitHub Release
108
- uses: Roang-zero1/github-upload-release-artifacts-action@v2.0.0
88
+ - name: Publish linux package
89
+ uses: actions/upload-release-asset@v1
90
+ env:
91
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
109
92
  with:
110
- args: licensed-${{github.event.ref}}-linux-x64.tar.gz licensed-${{github.event.ref}}-darwin-x64.tar.gz
93
+ upload_url: ${{ github.event.release.upload_url }}
94
+ asset_path: ./licensed-${{github.event.release.tag_name}}-linux-x64.tar.gz
95
+ asset_name: licensed-${{github.event.release.tag_name}}-linux-x64.tar.gz
96
+ asset_content_type: application/gzip
97
+
98
+ - name: Publish mac package
99
+ uses: actions/upload-release-asset@v1
111
100
  env:
112
- GITHUB_TOKEN: ${{secrets.API_AUTH_TOKEN}}
101
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
102
+ with:
103
+ upload_url: ${{ github.event.release.upload_url }}
104
+ asset_path: ./licensed-${{github.event.release.tag_name}}-darwin-x64.tar.gz
105
+ asset_name: licensed-${{github.event.release.tag_name}}-darwin-x64.tar.gz
106
+ asset_content_type: application/gzip
113
107
 
114
108
  - name: Publish gem to RubyGems
115
109
  run: |
116
110
  mkdir -p $HOME/.gem
117
111
  touch $HOME/.gem/credentials
118
112
  chmod 0600 $HOME/.gem/credentials
119
- printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
113
+ printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials
120
114
  gem push $GEM
121
115
  env:
122
- GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
123
- GEM: licensed-${{github.event.ref}}.gem
116
+ RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
117
+ GEM: licensed-${{github.event.release.tag_name}}.gem
@@ -116,7 +116,7 @@ jobs:
116
116
  runs-on: ubuntu-latest
117
117
  strategy:
118
118
  matrix:
119
- ruby: [ 2.4.x, 2.5.x, 2.6.x ]
119
+ ruby: [ 2.4.x, 2.5.x, 2.6.x, 2.7.x ]
120
120
  steps:
121
121
  - uses: actions/checkout@v2
122
122
  - name: Set up Ruby
@@ -165,7 +165,7 @@ jobs:
165
165
  runs-on: ubuntu-latest
166
166
  strategy:
167
167
  matrix:
168
- go: [ '1.7.x', '1.10.x', '1.11.x', '1.12.x', '1.13.x', '1.14.x' ]
168
+ go: [ '1.10.x', '1.11.x', '1.12.x', '1.13.x', '1.14.x', '1.15.x' ]
169
169
  steps:
170
170
  - uses: actions/checkout@v2
171
171
  - name: Setup go
@@ -6,6 +6,48 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## 2.14.0
10
+ 2020-10-04
11
+
12
+ ### Addded
13
+ - `reviewed` dependencies can use glob pattern matching (https://github.com/github/licensed/pull/313)
14
+
15
+ ### Fixed
16
+ - Fix configuring source path globs that expand into a single directory (https://github.com/github/licensed/pull/312)
17
+
18
+ ## 2.13.0
19
+ 2020-09-23
20
+
21
+ ### Added
22
+ - `status` command results can be output in YAML and JSON formats (:tada: @julianvilas https://github.com/github/licensed/pull/303)
23
+
24
+ ### Fixed
25
+ - `licensed` no longer crashes when parsing invalid YAML from cached records (https://github.com/github/licensed/pull/306)
26
+ - NPM source will no longer crash when invalid JSON is returned from npm CLI calls (https://github.com/github/licensed/pull/300)
27
+ - Bundler source is fixed to work properly with `gems.rb` lockfiles (https://github.com/github/licensed/pull/299)
28
+
29
+ ## 2.12.2
30
+ 2020-07-07
31
+
32
+ ### Changed
33
+ - Cleaned up ruby 2.7 warnings (:tada: @jurre https://github.com/github/licensed/pull/292)
34
+ - Cleaned up additional warnings in tests (https://github.com/github/licensed/pull/293)
35
+
36
+ ## 2.12.1
37
+ 2020-06-30
38
+
39
+ ### Fixed
40
+ - `licensed` no longer exits an error code when using the `--sources` CLI argument (https://github.com/github/licensed/pull/290)
41
+
42
+ ## 2.12.0
43
+ 2020-06-19
44
+
45
+ ### Added
46
+ - `--sources` argument for cache, list, status and notices commands to filter running sources (https://github.com/github/licensed/pull/287)
47
+
48
+ ### Fixed
49
+ - `cache` command will not remove files outside of enabled source cache paths (https://github.com/github/licensed/pull/287)
50
+
9
51
  ## 2.11.1
10
52
  2020-06-09
11
53
 
@@ -318,4 +360,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
318
360
 
319
361
  Initial release :tada:
320
362
 
321
- [Unreleased]: https://github.com/github/licensed/compare/2.11.1...HEAD
363
+ [Unreleased]: https://github.com/github/licensed/compare/2.14.0...HEAD
@@ -39,7 +39,7 @@ Pull requests that include a new dependency source must also
39
39
  ## Releasing
40
40
  If you are the current maintainer of this gem:
41
41
 
42
- 1. Create a branch for the release: git checkout -b cut-release-vxx.xx.xx
42
+ 1. Create a branch for the release: git checkout -b cut-release-xx.xx.xx
43
43
  2. Make sure your local dependencies are up to date: `script/bootstrap`
44
44
  3. Ensure that tests are green: `bundle exec rake test`
45
45
  4. Bump gem version in lib/licensed/version.rb.
@@ -51,15 +51,16 @@ If you are the current maintainer of this gem:
51
51
  2. Install the new gem locally
52
52
  3. Test behavior locally, branch deploy, whatever needs to happen
53
53
  9. Merge github/licensed PR
54
- 10. Tag and push: `git tag x.xx.xx; git push --tags`
54
+ 10. Create a new [github/licensed release](https://github.com/github/licensed/releases)
55
+ - Set the release name and tag to the release version - `x.xx.x`
56
+ - Set the release body to the changelog entries for the release
55
57
 
56
58
  The following steps will happen automatically from a GitHub Actions workflow
57
- after pushing a new tag. In case that fails, the following steps can be performed manually
59
+ after creating the release. In case that fails, the following steps can be performed manually
58
60
 
59
- 11. Push to rubygems.org -- `gem push licensed-x.xx.xx.gem`
61
+ 11. Push the gem from (7) to rubygems.org -- `gem push licensed-x.xx.xx.gem`
60
62
  12. Build packages for new tag: `VERSION=x.xx.xx bundle exec rake package`
61
- 13. Create release for new tag at github/licensed.
62
- 14. Add built packages to new release
63
+ 13. Upload packages from (12) to release from (10)
63
64
 
64
65
  ## Resources
65
66
 
@@ -6,10 +6,14 @@ Run `licensed -h` to see help content for running licensed commands.
6
6
 
7
7
  Running the list command finds the dependencies for all sources in all configured applications. No additional actions are taken on each dependency.
8
8
 
9
+ An optional `--sources` flag can be given to limit which dependency sources are run. This is a filter over sources that are enabled via the licensed configuration file and cannot be used to run licensed with a disabled source.
10
+
9
11
  ## `cache`
10
12
 
11
13
  The cache command finds all dependencies and ensures that each dependency has an up-to-date cached record.
12
14
 
15
+ An optional `--sources` flag can be given to limit which dependency sources are run. This is a filter over sources that are enabled via the licensed configuration file and cannot be used to run licensed with a disabled source.
16
+
13
17
  Dependency records will be saved if:
14
18
  1. The `force` option is set
15
19
  2. No cached record is found
@@ -22,6 +26,8 @@ After the cache command is run, any cached records that don't match up to a curr
22
26
 
23
27
  The status command finds all dependencies and checks whether each dependency has a valid cached record.
24
28
 
29
+ An optional `--sources` flag can be given to limit which dependency sources are run. This is a filter over sources that are enabled via the licensed configuration file and cannot be used to run licensed with a disabled source.
30
+
25
31
  A dependency will fail the status checks if:
26
32
  1. No cached record is found
27
33
  2. The cached record's version is different than the current dependency's version
@@ -35,6 +41,8 @@ A dependency will fail the status checks if:
35
41
 
36
42
  Outputs license and notice text for all dependencies in each app into a `NOTICE` file in the app's `cache_path`. If an app uses a shared cache path, the file name will contain the app name as well, e.g. `NOTICE.my_app`.
37
43
 
44
+ An optional `--sources` flag can be given to limit which dependency sources are run. This is a filter over sources that are enabled via the licensed configuration file and cannot be used to run licensed with a disabled source.
45
+
38
46
  The `NOTICE` file contents are retrieved from cached records, with the assumption that cached records have already been reviewed in a compliance workflow.
39
47
 
40
48
  ## `env`
@@ -23,7 +23,7 @@ If a root path is not specified, it will default to using the following, in orde
23
23
 
24
24
  The `source_path` property can use a glob path to share configuration properties across multiple application entrypoints.
25
25
 
26
- For example, there is a common pattern in go projects to include multiple executable entrypoints under folders in `cmd`. Using a glob pattern allows users to avoid manually configuring and maintaining multiple licensed application `source_path`s. Using a glob pattern will also ensure that any new entrypoints matching the pattern are automatically picked up by licensed commands as they are added.
26
+ For example, there is a common pattern in Go projects to include multiple executable entrypoints under folders in `cmd`. Using a glob pattern allows users to avoid manually configuring and maintaining multiple licensed application `source_path`s. Using a glob pattern will also ensure that any new entrypoints matching the pattern are automatically picked up by licensed commands as they are added.
27
27
 
28
28
  ```yml
29
29
  sources:
@@ -118,12 +118,6 @@ ignored:
118
118
  bower:
119
119
  - some-internal-package
120
120
 
121
- go:
122
- # ignore all go packages from import paths starting with github.com/internal-package
123
- # see the `File.fnmatch?` documentation for details on how patterns are matched.
124
- # comparisons use the FNM_CASEFOLD and FNM_PATHNAME flags
125
- - github.com/internal-package/**/*
126
-
127
121
  # These dependencies have licenses not on the `allowed` list and have been reviewed.
128
122
  # They will be cached and checked, but will not raise errors or warnings for a
129
123
  # non-allowed license. Dependencies on this list will still raise errors if
@@ -24,6 +24,26 @@ The setting supports absolute, relative and expandable (e.g. "~") paths. Relati
24
24
 
25
25
  Non-empty `GOPATH` configuration settings will override the `GOPATH` environment variable while enumerating `go` dependencies. The `GOPATH` environment variable is restored once dependencies have been enumerated.
26
26
 
27
+ #### Reviewing and ignoring all packages from a Go module
28
+
29
+ Go's package and module structure has common conventions that documentation and metadata for all packages in a module live in the module root. In this scenario all packages share the same LICENSE information and can be reviewed or ignored at the module level rather than per-package using glob patterns.
30
+
31
+ ```yaml
32
+ reviewed:
33
+ go:
34
+ # review all Go packages from import paths starting with github.com/external-package
35
+ # see the `File.fnmatch?` documentation for details on how patterns are matched.
36
+ # comparisons use the FNM_CASEFOLD and FNM_PATHNAME flags
37
+ - github.com/external-package/**/*
38
+
39
+ ignored:
40
+ go:
41
+ # ignore all Go packages from import paths starting with github.com/internal-package
42
+ # see the `File.fnmatch?` documentation for details on how patterns are matched.
43
+ # comparisons use the FNM_CASEFOLD and FNM_PATHNAME flags
44
+ - github.com/internal-package/**/*
45
+ ```
46
+
27
47
  #### Versioning
28
48
 
29
49
  The go source supports multiple versioning strategies to determine if cached dependency metadata is stale. A version strategy is chosen based on the availability of go module information along with the current app configuration.
@@ -10,29 +10,40 @@ module Licensed
10
10
  desc: "Overwrite licenses even if version has not changed."
11
11
  method_option :config, aliases: "-c", type: :string,
12
12
  desc: "Path to licensed configuration file"
13
+ method_option :sources, aliases: "-s", type: :array,
14
+ desc: "Individual source(s) to evaluate. Must also be enabled via configuration."
13
15
  def cache
14
- run Licensed::Commands::Cache.new(config: config), force: options[:force]
16
+ run Licensed::Commands::Cache.new(config: config),
17
+ force: options[:force], sources: options[:sources]
15
18
  end
16
19
 
17
20
  desc "status", "Check status of dependencies' cached licenses"
21
+ method_option :format, enum: ["yaml", "json"],
22
+ desc: "Output format"
18
23
  method_option :config, aliases: "-c", type: :string,
19
24
  desc: "Path to licensed configuration file"
25
+ method_option :sources, aliases: "-s", type: :array,
26
+ desc: "Individual source(s) to evaluate. Must also be enabled via configuration."
20
27
  def status
21
- run Licensed::Commands::Status.new(config: config)
28
+ run Licensed::Commands::Status.new(config: config), sources: options[:sources], reporter: options[:format]
22
29
  end
23
30
 
24
31
  desc "list", "List dependencies"
25
32
  method_option :config, aliases: "-c", type: :string,
26
33
  desc: "Path to licensed configuration file"
34
+ method_option :sources, aliases: "-s", type: :array,
35
+ desc: "Individual source(s) to evaluate. Must also be enabled via configuration."
27
36
  def list
28
- run Licensed::Commands::List.new(config: config)
37
+ run Licensed::Commands::List.new(config: config), sources: options[:sources]
29
38
  end
30
39
 
31
40
  desc "notices", "Generate a NOTICE file from cached records"
32
41
  method_option :config, aliases: "-c", type: :string,
33
42
  desc: "Path to licensed configuration file"
43
+ method_option :sources, aliases: "-s", type: :array,
44
+ desc: "Individual source(s) to evaluate. Must also be enabled via configuration."
34
45
  def notices
35
- run Licensed::Commands::Notices.new(config: config)
46
+ run Licensed::Commands::Notices.new(config: config), sources: options[:sources]
36
47
  end
37
48
 
38
49
  map "-v" => :version
@@ -48,7 +59,7 @@ module Licensed
48
59
  method_option :config, aliases: "-c", type: :string,
49
60
  desc: "Path to licensed configuration file"
50
61
  def env
51
- run Licensed::Commands::Environment.new(config: config), format: options[:format]
62
+ run Licensed::Commands::Environment.new(config: config), reporter: options[:format]
52
63
  end
53
64
 
54
65
  desc "migrate", "Migrate from a previous version of licensed"
@@ -87,7 +98,7 @@ module Licensed
87
98
  end
88
99
 
89
100
  def run(command, **args)
90
- exit command.run(args)
101
+ exit command.run(**args)
91
102
  end
92
103
  end
93
104
  end
@@ -2,12 +2,12 @@
2
2
  module Licensed
3
3
  module Commands
4
4
  class Cache < Command
5
- # Create a reporter to use during a command run
5
+ # Returns the default reporter to use during the command run
6
6
  #
7
7
  # options - The options the command was run with
8
8
  #
9
- # Raises a Licensed::Reporters::CacheReporter
10
- def create_reporter(options)
9
+ # Returns a Licensed::Reporters::CacheReporter
10
+ def default_reporter(options)
11
11
  Licensed::Reporters::CacheReporter.new
12
12
  end
13
13
 
@@ -32,19 +32,26 @@ module Licensed
32
32
 
33
33
  protected
34
34
 
35
- # Run the command for all enabled sources for an application configuration,
35
+ # Run the command for all enumerated dependencies found in a dependency source,
36
36
  # recording results in a report.
37
+ # Enumerating dependencies in the source is skipped if a :sources option
38
+ # is provided and the evaluated `source.class.type` is not in the :sources values
37
39
  #
38
- # app - An application configuration
40
+ # app - The application configuration for the source
41
+ # source - A dependency source enumerator
39
42
  #
40
- # Returns whether the command succeeded for the application.
41
- def run_app(app)
42
- result = super
43
-
44
- # add the full cache path to the list of cache paths evaluted during this run
45
- cache_paths << app.cache_path
43
+ # Returns whether the command succeeded for the dependency source enumerator
44
+ def run_source(app, source)
45
+ super do |report|
46
+ if Array(options[:sources]).any? && !options[:sources].include?(source.class.type)
47
+ report.warnings << "skipped source"
48
+ next :skip
49
+ end
46
50
 
47
- result
51
+ # add the full cache path to the list of cache paths
52
+ # that should be cleaned up after the command run
53
+ cache_paths << app.cache_path.join(source.class.type)
54
+ end
48
55
  end
49
56
 
50
57
  # Cache dependency record data.
@@ -21,7 +21,9 @@ module Licensed
21
21
  begin
22
22
  result = reporter.report_run(self) do |report|
23
23
  # allow additional report data to be given by commands
24
- yield report if block_given?
24
+ if block_given?
25
+ next true if (yield report) == :skip
26
+ end
25
27
 
26
28
  config.apps.sort_by { |app| app["name"] }
27
29
  .map { |app| run_app(app) }
@@ -35,13 +37,29 @@ module Licensed
35
37
  result
36
38
  end
37
39
 
38
- # Create a reporter to use during a command run
40
+ # Creates a reporter to use during a command run
39
41
  #
40
42
  # options - The options the command was run with
41
43
  #
42
- # Raises an error
44
+ # Returns the reporter to use during the command run
43
45
  def create_reporter(options)
44
- raise "`create_reporter` must be implemented by commands"
46
+ return options[:reporter] if options[:reporter].is_a?(Licensed::Reporters::Reporter)
47
+
48
+ if options[:reporter].is_a?(String)
49
+ klass = "#{options[:reporter].capitalize}Reporter"
50
+ return Licensed::Reporters.const_get(klass).new if Licensed::Reporters.const_defined?(klass)
51
+ end
52
+
53
+ default_reporter(options)
54
+ end
55
+
56
+ # Returns the default reporter to use during the command run
57
+ #
58
+ # options - The options the command was run with
59
+ #
60
+ # Raises an error
61
+ def default_reporter(options)
62
+ raise "`default_reporter` must be implemented by commands"
45
63
  end
46
64
 
47
65
  protected
@@ -54,10 +72,18 @@ module Licensed
54
72
  # Returns whether the command succeeded for the application.
55
73
  def run_app(app)
56
74
  reporter.report_app(app) do |report|
75
+ # ensure the app source path exists before evaluation
76
+ if !Dir.exist?(app.source_path)
77
+ report.errors << "No such directory #{app.source_path}"
78
+ next false
79
+ end
80
+
57
81
  Dir.chdir app.source_path do
58
82
  begin
59
83
  # allow additional report data to be given by commands
60
- yield report if block_given?
84
+ if block_given?
85
+ next true if (yield report) == :skip
86
+ end
61
87
 
62
88
  app.sources.select(&:enabled?)
63
89
  .sort_by { |source| source.class.type }
@@ -81,7 +107,9 @@ module Licensed
81
107
  reporter.report_source(source) do |report|
82
108
  begin
83
109
  # allow additional report data to be given by commands
84
- yield report if block_given?
110
+ if block_given?
111
+ next true if (yield report) == :skip
112
+ end
85
113
 
86
114
  source.dependencies.sort_by { |dependency| dependency.name }
87
115
  .map { |dependency| run_dependency(app, source, dependency) }
@@ -114,10 +142,12 @@ module Licensed
114
142
 
115
143
  begin
116
144
  # allow additional report data to be given by commands
117
- yield report if block_given?
145
+ if block_given?
146
+ next true if (yield report) == :skip
147
+ end
118
148
 
119
149
  evaluate_dependency(app, source, dependency, report)
120
- rescue Licensed::Shell::Error => err
150
+ rescue Licensed::DependencyRecord::Error, Licensed::Shell::Error => err
121
151
  report.errors << err.message
122
152
  false
123
153
  end
@@ -35,13 +35,13 @@ module Licensed
35
35
  end
36
36
  end
37
37
 
38
- def create_reporter(options)
39
- case options[:format]
40
- when "json"
41
- Licensed::Reporters::JsonReporter.new
42
- else
43
- Licensed::Reporters::YamlReporter.new
44
- end
38
+ # Returns the default reporter to use during the command run
39
+ #
40
+ # options - The options the command was run with
41
+ #
42
+ # Returns a Licensed::Reporters::StatusReporter
43
+ def default_reporter(options)
44
+ Licensed::Reporters::YamlReporter.new
45
45
  end
46
46
 
47
47
  protected
@@ -2,17 +2,36 @@
2
2
  module Licensed
3
3
  module Commands
4
4
  class List < Command
5
- # Create a reporter to use during a command run
5
+ # Returns the default reporter to use during the command run
6
6
  #
7
7
  # options - The options the command was run with
8
8
  #
9
9
  # Returns a Licensed::Reporters::ListReporter
10
- def create_reporter(options)
10
+ def default_reporter(options)
11
11
  Licensed::Reporters::ListReporter.new
12
12
  end
13
13
 
14
14
  protected
15
15
 
16
+ # Run the command for all enumerated dependencies found in a dependency source,
17
+ # recording results in a report.
18
+ # Enumerating dependencies in the source is skipped if a :sources option
19
+ # is provided and the evaluated `source.class.type` is not in the :sources values
20
+ #
21
+ # app - The application configuration for the source
22
+ # source - A dependency source enumerator
23
+ #
24
+ # Returns whether the command succeeded for the dependency source enumerator
25
+ def run_source(app, source)
26
+ super do |report|
27
+ next if Array(options[:sources]).empty?
28
+ next if options[:sources].include?(source.class.type)
29
+
30
+ report.warnings << "skipped source"
31
+ :skip
32
+ end
33
+ end
34
+
16
35
  # Listing dependencies requires no extra work.
17
36
  #
18
37
  # app - The application configuration for the dependency
@@ -2,17 +2,36 @@
2
2
  module Licensed
3
3
  module Commands
4
4
  class Notices < Command
5
- # Create a reporter to use during a command run
5
+ # Returns the default reporter to use during the command run
6
6
  #
7
7
  # options - The options the command was run with
8
8
  #
9
- # Raises a Licensed::Reporters::CacheReporter
10
- def create_reporter(options)
9
+ # Returns a Licensed::Reporters::CacheReporter
10
+ def default_reporter(options)
11
11
  Licensed::Reporters::NoticesReporter.new
12
12
  end
13
13
 
14
14
  protected
15
15
 
16
+ # Run the command for all enumerated dependencies found in a dependency source,
17
+ # recording results in a report.
18
+ # Enumerating dependencies in the source is skipped if a :sources option
19
+ # is provided and the evaluated `source.class.type` is not in the :sources values
20
+ #
21
+ # app - The application configuration for the source
22
+ # source - A dependency source enumerator
23
+ #
24
+ # Returns whether the command succeeded for the dependency source enumerator
25
+ def run_source(app, source)
26
+ super do |report|
27
+ next if Array(options[:sources]).empty?
28
+ next if options[:sources].include?(source.class.type)
29
+
30
+ report.warnings << "skipped source"
31
+ :skip
32
+ end
33
+ end
34
+
16
35
  # Load stored dependency record data to add to the notices report.
17
36
  #
18
37
  # app - The application configuration for the dependency
@@ -25,7 +44,7 @@ module Licensed
25
44
  filename = app.cache_path.join(source.class.type, "#{dependency.name}.#{DependencyRecord::EXTENSION}")
26
45
  report["cached_record"] = Licensed::DependencyRecord.read(filename)
27
46
  if !report["cached_record"]
28
- report["warning"] = "expected cached record not found at #{filename}"
47
+ report.warnings << "expected cached record not found at #{filename}"
29
48
  end
30
49
 
31
50
  true
@@ -4,17 +4,36 @@ require "yaml"
4
4
  module Licensed
5
5
  module Commands
6
6
  class Status < Command
7
- # Create a reporter to use during a command run
7
+ # Returns the default reporter to use during the command run
8
8
  #
9
9
  # options - The options the command was run with
10
10
  #
11
11
  # Returns a Licensed::Reporters::StatusReporter
12
- def create_reporter(options)
12
+ def default_reporter(options)
13
13
  Licensed::Reporters::StatusReporter.new
14
14
  end
15
15
 
16
16
  protected
17
17
 
18
+ # Run the command for all enumerated dependencies found in a dependency source,
19
+ # recording results in a report.
20
+ # Enumerating dependencies in the source is skipped if a :sources option
21
+ # is provided and the evaluated `source.class.type` is not in the :sources values
22
+ #
23
+ # app - The application configuration for the source
24
+ # source - A dependency source enumerator
25
+ #
26
+ # Returns whether the command succeeded for the dependency source enumerator
27
+ def run_source(app, source)
28
+ super do |report|
29
+ next if Array(options[:sources]).empty?
30
+ next if options[:sources].include?(source.class.type)
31
+
32
+ report.warnings << "skipped source"
33
+ :skip
34
+ end
35
+ end
36
+
18
37
  # Verifies that a cached record exists, is up to date and
19
38
  # has license data that complies with the licensed configuration.
20
39
  #
@@ -69,7 +69,9 @@ module Licensed
69
69
 
70
70
  # Is the given dependency reviewed?
71
71
  def reviewed?(dependency)
72
- Array(self["reviewed"][dependency["type"]]).include?(dependency["name"])
72
+ Array(self["reviewed"][dependency["type"]]).any? do |pattern|
73
+ File.fnmatch?(pattern, dependency["name"], File::FNM_PATHNAME | File::FNM_CASEFOLD)
74
+ end
73
75
  end
74
76
 
75
77
  # Is the given dependency ignored?
@@ -158,19 +160,22 @@ module Licensed
158
160
  def self.expand_app_source_path(app_config)
159
161
  return app_config if app_config["source_path"].to_s.empty?
160
162
 
163
+ # check if the source path maps to an existing directory
161
164
  source_path = File.expand_path(app_config["source_path"], AppConfiguration.root_for(app_config))
165
+ return app_config if Dir.exist?(source_path)
166
+
167
+ # try to expand the source path for glob patterns
162
168
  expanded_source_paths = Dir.glob(source_path).select { |p| File.directory?(p) }
163
- # return the original configuration if glob didn't result in multiple paths
164
- return app_config if expanded_source_paths.size <= 1
169
+ configs = expanded_source_paths.map { |path| app_config.merge("source_path" => path) }
165
170
 
166
- # map the expanded paths to new application configurations
167
- expanded_source_paths.map do |path|
168
- config = app_config.merge("source_path" => path)
171
+ # if no directories are found for the source path, return the original config
172
+ return app_config if configs.size == 0
169
173
 
170
- # update configured values for name and cache_path for uniqueness.
171
- # this is only needed when values are explicitly set, AppConfiguration
172
- # will handle configurations that don't have these explicitly set
173
- dir_name = File.basename(path)
174
+ # update configured values for name and cache_path for uniqueness.
175
+ # this is only needed when values are explicitly set, AppConfiguration
176
+ # will handle configurations that don't have these explicitly set
177
+ configs.each do |config|
178
+ dir_name = File.basename(config["source_path"])
174
179
  config["name"] = "#{config["name"]}-#{dir_name}" if config["name"]
175
180
 
176
181
  # if a cache_path is set and is not marked as shared, append the app name
@@ -178,9 +183,9 @@ module Licensed
178
183
  if config["cache_path"] && config["shared_cache"] != true
179
184
  config["cache_path"] = File.join(config["cache_path"], dir_name)
180
185
  end
181
-
182
- config
183
186
  end
187
+
188
+ configs
184
189
  end
185
190
 
186
191
  # Find a default configuration file in the given directory.
@@ -5,6 +5,8 @@ require "licensee"
5
5
 
6
6
  module Licensed
7
7
  class DependencyRecord
8
+ class Error < StandardError; end
9
+
8
10
  class License
9
11
  attr_reader :text, :sources
10
12
  def initialize(content)
@@ -46,6 +48,8 @@ module Licensed
46
48
  notices: data.delete("notices"),
47
49
  metadata: data
48
50
  )
51
+ rescue Psych::SyntaxError => e
52
+ raise Licensed::DependencyRecord::Error.new(e.message)
49
53
  end
50
54
 
51
55
  def_delegators :@metadata, :[], :[]=
@@ -28,6 +28,22 @@ module Licensed
28
28
  shell.info " #{source.class.type}"
29
29
  result = yield report
30
30
 
31
+ warning_reports = report.all_reports.select { |r| r.warnings.any? }.to_a
32
+ if warning_reports.any?
33
+ shell.newline
34
+ shell.warn " * Warnings:"
35
+ warning_reports.each do |r|
36
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
37
+
38
+ shell.warn " * #{r.name}"
39
+ shell.warn " #{display_metadata}" unless display_metadata.empty?
40
+ r.warnings.each do |warning|
41
+ shell.warn " - #{warning}"
42
+ end
43
+ shell.newline
44
+ end
45
+ end
46
+
31
47
  errored_reports = report.all_reports.select { |r| r.errors.any? }.to_a
32
48
  if errored_reports.any?
33
49
  shell.newline
@@ -33,6 +33,26 @@ module Licensed
33
33
  end
34
34
  end
35
35
 
36
+
37
+ # Reports on a dependency source enumerator in a notices command run.
38
+ # Shows warnings encountered during the run.
39
+ #
40
+ # source - A dependency source enumerator
41
+ #
42
+ # Returns the result of the yielded method
43
+ # Note - must be called from inside the `report_run` scope
44
+ def report_source(source)
45
+ super do |report|
46
+ result = yield report
47
+
48
+ report.warnings.each do |warning|
49
+ shell.warn "* #{report.name}: #{warning}"
50
+ end
51
+
52
+ result
53
+ end
54
+ end
55
+
36
56
  # Reports on a dependency in a notices command run.
37
57
  #
38
58
  # dependency - An application dependency
@@ -42,7 +62,9 @@ module Licensed
42
62
  def report_dependency(dependency)
43
63
  super do |report|
44
64
  result = yield report
45
- shell.warn "* #{report["warning"]}" if report["warning"]
65
+ report.warnings.each do |warning|
66
+ shell.warn "* #{report.name}: #{warning}"
67
+ end
46
68
  result
47
69
  end
48
70
  end
@@ -15,6 +15,23 @@ module Licensed
15
15
  result = yield report
16
16
 
17
17
  all_reports = report.all_reports
18
+
19
+ warning_reports = all_reports.select { |r| r.warnings.any? }.to_a
20
+ if warning_reports.any?
21
+ shell.newline
22
+ shell.warn "Warnings:"
23
+ warning_reports.each do |r|
24
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
25
+
26
+ shell.warn "* #{r.name}"
27
+ shell.warn " #{display_metadata}" unless display_metadata.empty?
28
+ r.warnings.each do |warning|
29
+ shell.warn " - #{warning}"
30
+ end
31
+ shell.newline
32
+ end
33
+ end
34
+
18
35
  errored_reports = all_reports.select { |r| r.errors.any? }.to_a
19
36
 
20
37
  dependency_count = all_reports.select { |r| r.target.is_a?(Licensed::Dependency) }.size
@@ -74,7 +74,7 @@ module Licensed
74
74
  end
75
75
  end
76
76
 
77
- GEMFILES = %w{Gemfile gems.rb}.freeze
77
+ GEMFILES = { "Gemfile" => "Gemfile.lock", "gems.rb" => "gems.locked" }
78
78
  DEFAULT_WITHOUT_GROUPS = %i{development test}
79
79
 
80
80
  def enabled?
@@ -272,14 +272,15 @@ module Licensed
272
272
 
273
273
  # Returns the path to the Bundler Gemfile
274
274
  def gemfile_path
275
- @gemfile_path ||= GEMFILES.map { |g| config.pwd.join g }
275
+ @gemfile_path ||= GEMFILES.keys
276
+ .map { |g| config.pwd.join g }
276
277
  .find { |f| f.exist? }
277
278
  end
278
279
 
279
280
  # Returns the path to the Bundler Gemfile.lock
280
281
  def lockfile_path
281
282
  return unless gemfile_path
282
- @lockfile_path ||= gemfile_path.dirname.join("#{gemfile_path.basename}.lock")
283
+ @lockfile_path ||= gemfile_path.dirname.join(GEMFILES[gemfile_path.basename.to_s])
283
284
  end
284
285
 
285
286
  # Returns the configured bundler executable to use, or "bundle" by default.
@@ -30,7 +30,7 @@ module Licensed
30
30
  end
31
31
 
32
32
  def packages
33
- root_dependencies = JSON.parse(package_metadata_command)["dependencies"]
33
+ root_dependencies = package_metadata["dependencies"]
34
34
  recursive_dependencies(root_dependencies).each_with_object({}) do |(name, results), hsh|
35
35
  results.uniq! { |package| package["version"] }
36
36
  if results.size == 1
@@ -56,6 +56,18 @@ module Licensed
56
56
  result
57
57
  end
58
58
 
59
+ # Returns parsed package metadata returned from `npm list`
60
+ def package_metadata
61
+ return @package_metadata if defined?(@package_metadata)
62
+
63
+ @package_metadata = begin
64
+ JSON.parse(package_metadata_command)
65
+ rescue JSON::ParserError => e
66
+ raise Licensed::Sources::Source::Error,
67
+ "Licensed was unable to parse the output from 'npm list'. Please run 'npm list --json --long' and check for errors. Error: #{e.message}"
68
+ end
69
+ end
70
+
59
71
  # Returns the output from running `npm list` to get package metadata
60
72
  def package_metadata_command
61
73
  args = %w(--json --long)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Licensed
3
- VERSION = "2.11.1".freeze
3
+ VERSION = "2.14.0".freeze
4
4
 
5
5
  def self.previous_major_versions
6
6
  major_version = Gem::Version.new(Licensed::VERSION).segments.first
@@ -38,5 +38,4 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency "rubocop", "~> 0.49", "< 0.67"
39
39
  spec.add_development_dependency "rubocop-github", "~> 0.6"
40
40
  spec.add_development_dependency "byebug", "~> 10.0.0"
41
- spec.add_development_dependency "spy", "~> 1.0.0"
42
41
  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: 2.11.1
4
+ version: 2.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-09 00:00:00.000000000 Z
11
+ date: 2020-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: licensee
@@ -218,20 +218,6 @@ dependencies:
218
218
  - - "~>"
219
219
  - !ruby/object:Gem::Version
220
220
  version: 10.0.0
221
- - !ruby/object:Gem::Dependency
222
- name: spy
223
- requirement: !ruby/object:Gem::Requirement
224
- requirements:
225
- - - "~>"
226
- - !ruby/object:Gem::Version
227
- version: 1.0.0
228
- type: :development
229
- prerelease: false
230
- version_requirements: !ruby/object:Gem::Requirement
231
- requirements:
232
- - - "~>"
233
- - !ruby/object:Gem::Version
234
- version: 1.0.0
235
221
  description: Licensed automates extracting and validating the licenses of dependencies.
236
222
  email:
237
223
  - opensource+licensed@github.com