licensed 2.8.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +11 -12
- data/.github/workflows/test.yml +71 -52
- data/.gitignore +2 -0
- data/CHANGELOG.md +48 -1
- data/README.md +11 -7
- data/docs/commands.md +8 -0
- data/docs/configuration.md +36 -0
- data/docs/sources/nuget.md +14 -0
- data/lib/licensed/cli.rb +7 -0
- data/lib/licensed/commands.rb +1 -0
- data/lib/licensed/commands/cache.rb +56 -19
- data/lib/licensed/commands/environment.rb +2 -2
- data/lib/licensed/commands/notices.rb +35 -0
- data/lib/licensed/commands/status.rb +5 -1
- data/lib/licensed/configuration.rb +72 -37
- data/lib/licensed/dependency.rb +1 -1
- data/lib/licensed/git.rb +3 -1
- data/lib/licensed/reporters.rb +1 -0
- data/lib/licensed/reporters/cache_reporter.rb +10 -10
- data/lib/licensed/reporters/list_reporter.rb +5 -5
- data/lib/licensed/reporters/notices_reporter.rb +68 -0
- data/lib/licensed/reporters/reporter.rb +3 -0
- data/lib/licensed/reporters/status_reporter.rb +7 -7
- data/lib/licensed/sources.rb +1 -0
- data/lib/licensed/sources/cabal.rb +2 -2
- data/lib/licensed/sources/go.rb +27 -20
- data/lib/licensed/sources/mix.rb +5 -1
- data/lib/licensed/sources/npm.rb +1 -0
- data/lib/licensed/sources/nuget.rb +212 -0
- data/lib/licensed/version.rb +1 -1
- data/licensed.gemspec +4 -3
- data/script/bootstrap +2 -1
- data/script/source-setup/nuget +17 -0
- metadata +37 -12
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,53 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## 2.11.0
|
10
|
+
2020-06-02
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- `notices` command to create a `NOTICE` file for each configured app (https://github.com/github/licensed/pull/277)
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
- NuGet source no longer crashes on a non-existent dependency path (https://github.com/github/licensed/pull/280)
|
17
|
+
- Go source no longer crashes on a non-existent dependency package path (https://github.com/github/licensed/pull/274)
|
18
|
+
|
19
|
+
## 2.10.0
|
20
|
+
2020-05-15
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
- NPM source ignores missing peer dependencies (https://github.com/github/licensed/pull/267)
|
24
|
+
|
25
|
+
### Added
|
26
|
+
- NuGet source (:tada: @zarenner https://github.com/github/licensed/pull/261)
|
27
|
+
- Multiple apps can share a single cache location (https://github.com/github/licensed/pull/263)
|
28
|
+
|
29
|
+
## 2.9.2
|
30
|
+
2020-04-28
|
31
|
+
|
32
|
+
### Changed
|
33
|
+
- `licensee` minimum version bumped to 9.13.2 (https://github.com/github/licensed/pull/256)
|
34
|
+
|
35
|
+
## 2.9.1
|
36
|
+
2020-03-24
|
37
|
+
|
38
|
+
### Changed
|
39
|
+
- relaxed gem version restrictions on Thor (:tada: @eileencodes https://github.com/github/licensed/pull/254)
|
40
|
+
|
41
|
+
## 2.9.0
|
42
|
+
2020-03-19
|
43
|
+
|
44
|
+
### Added
|
45
|
+
- Source paths use glob pattern matching (https://github.com/github/licensed/pull/245)
|
46
|
+
|
47
|
+
### Fixed
|
48
|
+
- Mix source supports updates to mix.lock format (:tada: @bruce https://github.com/github/licensed/pull/242)
|
49
|
+
- Go source supports `go list` format changes in go 1.14 (https://github.com/github/licensed/pull/247)
|
50
|
+
|
51
|
+
### Changed
|
52
|
+
- `licensed cache` will flag dependencies for re-review when license text changes (https://github.com/github/licensed/pull/248)
|
53
|
+
- `licensed status` will raise errors on dependencies that need re-review (https://github.com/github/licensed/pull/248)
|
54
|
+
- `licensee` minimum version bumped to 9.13.1 (https://github.com/github/licensed/pull/251)
|
55
|
+
|
9
56
|
## 2.8.0
|
10
57
|
2020-01-03
|
11
58
|
|
@@ -265,4 +312,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
265
312
|
|
266
313
|
Initial release :tada:
|
267
314
|
|
268
|
-
[Unreleased]: https://github.com/github/licensed/compare/2.
|
315
|
+
[Unreleased]: https://github.com/github/licensed/compare/2.11.0...HEAD
|
data/README.md
CHANGED
@@ -24,13 +24,13 @@ See the [migration documentation](./docs/migrating_to_newer_versions.md) for mor
|
|
24
24
|
### Dependencies
|
25
25
|
|
26
26
|
Licensed uses the `libgit2` bindings for Ruby provided by `rugged`. `rugged` requires `cmake` and `pkg-config` which you may need to install before you can install Licensed.
|
27
|
-
|
27
|
+
|
28
28
|
> Ubuntu
|
29
|
-
|
29
|
+
|
30
30
|
sudo apt-get install cmake pkg-config
|
31
|
-
|
31
|
+
|
32
32
|
> OS X
|
33
|
-
|
33
|
+
|
34
34
|
brew install cmake pkg-config
|
35
35
|
|
36
36
|
### With a Gemfile
|
@@ -64,8 +64,10 @@ For system wide usage, install licensed to a location on `$PATH`, e.g. `/usr/loc
|
|
64
64
|
|
65
65
|
- `licensed list`: Output enumerated dependencies only.
|
66
66
|
- `licensed cache`: Cache licenses and metadata.
|
67
|
-
- `licensed status`: Check status of dependencies' cached licenses.
|
67
|
+
- `licensed status`: Check status of dependencies' cached licenses.
|
68
|
+
- `licensed notices`: Write a `NOTICE` file for each application configuration.
|
68
69
|
- `licensed version`: Show current installed version of Licensed. Aliases: `-v|--version`
|
70
|
+
- `licensed env`: Output environment information from the licensed configuration.
|
69
71
|
|
70
72
|
See the [commands documentation](./docs/commands.md) for additional documentation, or run `licensed -h` to see all of the current available commands.
|
71
73
|
|
@@ -102,14 +104,16 @@ Dependencies will be automatically detected for all of the following sources by
|
|
102
104
|
1. [Bundler](./docs/sources/bundler.md)
|
103
105
|
1. [Cabal](./docs/sources/cabal.md)
|
104
106
|
1. [Composer](./docs/sources/composer.md)
|
107
|
+
1. [Git Submodules (git_submodule)](./docs/sources/git_submodule.md)
|
105
108
|
1. [Go](./docs/sources/go.md)
|
106
109
|
1. [Go Dep (dep)](./docs/sources/dep.md)
|
110
|
+
1. [Gradle](./docs/sources/gradle.md)
|
107
111
|
1. [Manifest lists (manifests)](./docs/sources/manifests.md)
|
112
|
+
1. [Mix](./docs/sources/mix.md)
|
108
113
|
1. [NPM](./docs/sources/npm.md)
|
114
|
+
1. [NuGet](./docs/sources/nuget.md)
|
109
115
|
1. [Pip](./docs/sources/pip.md)
|
110
116
|
1. [Pipenv](./docs/sources/pipenv.md)
|
111
|
-
1. [Git Submodules (git_submodule)](./docs/sources/git_submodule.md)
|
112
|
-
1. [Mix](./docs/sources/mix.md)
|
113
117
|
1. [Yarn](./docs/sources/yarn.md)
|
114
118
|
|
115
119
|
You can disable any of them in the configuration file:
|
data/docs/commands.md
CHANGED
@@ -28,6 +28,14 @@ A dependency will fail the status checks if:
|
|
28
28
|
3. The cached record's `licenses` data is empty
|
29
29
|
4. The cached record's `license` metadata doesn't match an `allowed` license from the dependency's application configuration.
|
30
30
|
- If `license: other` is specified and all of the `licenses` entries match an `allowed` license a failure will not be logged
|
31
|
+
5. The cached record is flagged for re-review.
|
32
|
+
- This occurs when the record's license text has changed since the record was reviewed.
|
33
|
+
|
34
|
+
## `notices`
|
35
|
+
|
36
|
+
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
|
+
|
38
|
+
The `NOTICE` file contents are retrieved from cached records, with the assumption that cached records have already been reviewed in a compliance workflow.
|
31
39
|
|
32
40
|
## `env`
|
33
41
|
|
data/docs/configuration.md
CHANGED
@@ -19,6 +19,22 @@ If a root path is not specified, it will default to using the following, in orde
|
|
19
19
|
1. the root of the local git repository, if run inside a git repository
|
20
20
|
2. the current directory
|
21
21
|
|
22
|
+
### Source path glob patterns
|
23
|
+
|
24
|
+
The `source_path` property can use a glob path to share configuration properties across multiple application entrypoints.
|
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.
|
27
|
+
|
28
|
+
```yml
|
29
|
+
sources:
|
30
|
+
go: true
|
31
|
+
|
32
|
+
# treat all directories under `cmd` as separate apps
|
33
|
+
source_path: cmd/*
|
34
|
+
```
|
35
|
+
|
36
|
+
Glob patterns are syntactic sugar for, and provide the same functionality as, manually specifying multiple `source_path` values. See the instructions on [specifying multiple apps](./#specifying-multiple-apps) below for additional considerations when using multiple apps.
|
37
|
+
|
22
38
|
## Restricting sources
|
23
39
|
|
24
40
|
The `sources` configuration property specifies which sources `licensed` will use to enumerate dependencies.
|
@@ -193,6 +209,26 @@ apps:
|
|
193
209
|
|
194
210
|
In this example, the root configuration will contain a default cache path of `.licenses`. `app1` will inherit this value and append it's name, resulting in a cache path of `.licenses/app1`.
|
195
211
|
|
212
|
+
### Sharing caches between apps
|
213
|
+
|
214
|
+
Dependency caches can be shared between apps by setting the same cache path on each app.
|
215
|
+
|
216
|
+
```yaml
|
217
|
+
apps:
|
218
|
+
- source_path: "path/to/app1"
|
219
|
+
cache_path: ".licenses/apps"
|
220
|
+
- source_path: "path/to/app2"
|
221
|
+
cache_path: ".licenses/apps"
|
222
|
+
```
|
223
|
+
|
224
|
+
When using a source path with a glob pattern, the apps created from the glob pattern can share a dependency by setting an explicit cache path and setting `shared_cache` to true.
|
225
|
+
|
226
|
+
```yaml
|
227
|
+
source_path: "path/to/apps/*"
|
228
|
+
cache_path: ".licenses/apps"
|
229
|
+
shared_cache: true
|
230
|
+
```
|
231
|
+
|
196
232
|
## Source specific configuration
|
197
233
|
|
198
234
|
See the [source documentation](./sources) for details on any source specific configuration.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# NuGet
|
2
|
+
|
3
|
+
The NuGet source will detect ProjectReference-style restored packages by inspecting `project.assets.json` files for dependencies. It requires that `dotnet restore` has already ran on the project.
|
4
|
+
|
5
|
+
The source currently expects that `source_path` is set to the `obj` directory containing the `project.assets.json`.
|
6
|
+
For example, if your project lives at `foo/foo.proj`, you likely want to set `source_path` to `foo/obj`.
|
7
|
+
If in MSBuild you have customized your `obj` paths (e.g. to live outside your source tree), you may need to set `source_path` to something different such as `../obj/foo`.
|
8
|
+
|
9
|
+
### Search strategy
|
10
|
+
This source looks for licenses:
|
11
|
+
1. Specified by SPDX expression via `<license type="expression">` in a package's `.nuspec` (via licensee)
|
12
|
+
2. In license files such as `LICENSE.txt`, even if not specified in the `.nuspec` (via licensee)
|
13
|
+
3. Specified by filepath via `<license type="file">` in a package's `.nuspec`, even if not a standard license filename.
|
14
|
+
4. By downloading and inspecting the contents of `<licenseUrl>` in a package's `.nuspec`, if not found otherwise.
|
data/lib/licensed/cli.rb
CHANGED
@@ -28,6 +28,13 @@ module Licensed
|
|
28
28
|
run Licensed::Commands::List.new(config: config)
|
29
29
|
end
|
30
30
|
|
31
|
+
desc "notices", "Generate a NOTICE file from cached records"
|
32
|
+
method_option :config, aliases: "-c", type: :string,
|
33
|
+
desc: "Path to licensed configuration file"
|
34
|
+
def notices
|
35
|
+
run Licensed::Commands::Notices.new(config: config)
|
36
|
+
end
|
37
|
+
|
31
38
|
map "-v" => :version
|
32
39
|
map "--version" => :version
|
33
40
|
desc "version", "Show Installed Version of Licensed, [-v, --version]"
|
data/lib/licensed/commands.rb
CHANGED
@@ -11,20 +11,39 @@ module Licensed
|
|
11
11
|
Licensed::Reporters::CacheReporter.new
|
12
12
|
end
|
13
13
|
|
14
|
+
# Run the command.
|
15
|
+
# Removes any cached records that don't match a current application
|
16
|
+
# dependency.
|
17
|
+
#
|
18
|
+
# options - Options to run the command with
|
19
|
+
#
|
20
|
+
# Returns whether the command was a success
|
21
|
+
def run(**options)
|
22
|
+
begin
|
23
|
+
result = super
|
24
|
+
clear_stale_cached_records if result
|
25
|
+
|
26
|
+
result
|
27
|
+
ensure
|
28
|
+
cache_paths.clear
|
29
|
+
files.clear
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
14
33
|
protected
|
15
34
|
|
16
|
-
# Run the command for all
|
35
|
+
# Run the command for all enabled sources for an application configuration,
|
17
36
|
# recording results in a report.
|
18
|
-
# Removes any cached records that don't match a current application
|
19
|
-
# dependency.
|
20
37
|
#
|
21
|
-
# app -
|
22
|
-
# source - A dependency source enumerator
|
38
|
+
# app - An application configuration
|
23
39
|
#
|
24
|
-
# Returns whether the command succeeded for the
|
25
|
-
def
|
40
|
+
# Returns whether the command succeeded for the application.
|
41
|
+
def run_app(app)
|
26
42
|
result = super
|
27
|
-
|
43
|
+
|
44
|
+
# add the full cache path to the list of cache paths evaluted during this run
|
45
|
+
cache_paths << app.cache_path
|
46
|
+
|
28
47
|
result
|
29
48
|
end
|
30
49
|
|
@@ -45,8 +64,15 @@ module Licensed
|
|
45
64
|
filename = app.cache_path.join(source.class.type, "#{dependency.name}.#{DependencyRecord::EXTENSION}")
|
46
65
|
cached_record = Licensed::DependencyRecord.read(filename)
|
47
66
|
if options[:force] || save_dependency_record?(dependency, cached_record)
|
48
|
-
|
49
|
-
|
67
|
+
if dependency.record.matches?(cached_record)
|
68
|
+
# use the cached license value if the license text wasn't updated
|
69
|
+
dependency.record["license"] = cached_record["license"]
|
70
|
+
elsif cached_record && app.reviewed?(dependency.record)
|
71
|
+
# if the license text changed and the dependency is set as reviewed
|
72
|
+
# force a re-review of the dependency
|
73
|
+
dependency.record["review_changed_license"] = true
|
74
|
+
end
|
75
|
+
|
50
76
|
dependency.record.save(filename)
|
51
77
|
report["cached"] = true
|
52
78
|
end
|
@@ -55,6 +81,9 @@ module Licensed
|
|
55
81
|
report.warnings << "expected dependency path #{dependency.path} does not exist"
|
56
82
|
end
|
57
83
|
|
84
|
+
# add the absolute dependency file path to the list of files seen during this licensed run
|
85
|
+
files << filename.to_s
|
86
|
+
|
58
87
|
true
|
59
88
|
end
|
60
89
|
|
@@ -79,18 +108,26 @@ module Licensed
|
|
79
108
|
|
80
109
|
# Clean up cached files that dont match current dependencies
|
81
110
|
#
|
82
|
-
# app - An application configuration
|
83
|
-
# source - A dependency source enumerator
|
84
|
-
#
|
85
111
|
# Returns nothing
|
86
|
-
def clear_stale_cached_records
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
112
|
+
def clear_stale_cached_records
|
113
|
+
cache_paths.each do |cache_path|
|
114
|
+
Dir.glob(cache_path.join("**/*.#{DependencyRecord::EXTENSION}")).each do |file|
|
115
|
+
next if files.include?(file)
|
116
|
+
|
117
|
+
FileUtils.rm(file)
|
118
|
+
end
|
92
119
|
end
|
93
120
|
end
|
121
|
+
|
122
|
+
# Set of unique cache paths that are evaluted during the run
|
123
|
+
def cache_paths
|
124
|
+
@cache_paths ||= Set.new
|
125
|
+
end
|
126
|
+
|
127
|
+
# Set of unique absolute file paths of cached records evaluted during the run
|
128
|
+
def files
|
129
|
+
@files ||= Set.new
|
130
|
+
end
|
94
131
|
end
|
95
132
|
end
|
96
133
|
end
|
@@ -23,14 +23,14 @@ module Licensed
|
|
23
23
|
"allowed" => config["allowed"],
|
24
24
|
"ignored" => config["ignored"],
|
25
25
|
"reviewed" => config["reviewed"],
|
26
|
-
"version_strategy" => self.version_strategy
|
26
|
+
"version_strategy" => self.version_strategy,
|
27
|
+
"root" => config.root
|
27
28
|
}
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
32
|
def run(**options)
|
32
33
|
super do |report|
|
33
|
-
report["root"] = config.root
|
34
34
|
report["git_repo"] = Licensed::Git.git_repo?
|
35
35
|
end
|
36
36
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Licensed
|
3
|
+
module Commands
|
4
|
+
class Notices < Command
|
5
|
+
# Create a reporter to use during a command run
|
6
|
+
#
|
7
|
+
# options - The options the command was run with
|
8
|
+
#
|
9
|
+
# Raises a Licensed::Reporters::CacheReporter
|
10
|
+
def create_reporter(options)
|
11
|
+
Licensed::Reporters::NoticesReporter.new
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
# Load stored dependency record data to add to the notices report.
|
17
|
+
#
|
18
|
+
# app - The application configuration for the dependency
|
19
|
+
# source - The dependency source enumerator for the dependency
|
20
|
+
# dependency - An application dependency
|
21
|
+
# report - A report hash for the command to provide extra data for the report output.
|
22
|
+
#
|
23
|
+
# Returns true.
|
24
|
+
def evaluate_dependency(app, source, dependency, report)
|
25
|
+
filename = app.cache_path.join(source.class.type, "#{dependency.name}.#{DependencyRecord::EXTENSION}")
|
26
|
+
report["cached_record"] = Licensed::DependencyRecord.read(filename)
|
27
|
+
if !report["cached_record"]
|
28
|
+
report["warning"] = "expected cached record not found at #{filename}"
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -35,7 +35,11 @@ module Licensed
|
|
35
35
|
else
|
36
36
|
report.errors << "cached dependency record out of date" if cached_record["version"] != dependency.version
|
37
37
|
report.errors << "missing license text" if cached_record.licenses.empty?
|
38
|
-
|
38
|
+
if cached_record["review_changed_license"]
|
39
|
+
report.errors << "license text has changed and needs re-review. if the new text is ok, remove the `review_changed_license` flag from the cached record"
|
40
|
+
elsif license_needs_review?(app, cached_record)
|
41
|
+
report.errors << "license needs review: #{cached_record["license"]}"
|
42
|
+
end
|
39
43
|
end
|
40
44
|
|
41
45
|
report.errors.empty?
|
@@ -4,40 +4,39 @@ require "pathname"
|
|
4
4
|
module Licensed
|
5
5
|
class AppConfiguration < Hash
|
6
6
|
DEFAULT_CACHE_PATH = ".licenses".freeze
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
|
8
|
+
# Returns the root for a configuration in following order of precendence:
|
9
|
+
# 1. explicitly configured "root" property
|
10
|
+
# 2. a found git repository root
|
11
|
+
# 3. the current directory
|
12
|
+
def self.root_for(configuration)
|
13
|
+
configuration["root"] || Licensed::Git.repository_root || Dir.pwd
|
14
|
+
end
|
12
15
|
|
13
16
|
def initialize(options = {}, inherited_options = {})
|
14
17
|
super()
|
15
18
|
|
16
19
|
# update order:
|
17
20
|
# 1. anything inherited from root config
|
18
|
-
# 2. app
|
19
|
-
# 3. explicitly configured app settings
|
21
|
+
# 2. explicitly configured app settings
|
20
22
|
update(inherited_options)
|
21
|
-
update(defaults_for(options, inherited_options))
|
22
23
|
update(options)
|
24
|
+
verify_arg "source_path"
|
23
25
|
|
24
26
|
self["sources"] ||= {}
|
25
27
|
self["reviewed"] ||= {}
|
26
28
|
self["ignored"] ||= {}
|
27
29
|
self["allowed"] ||= []
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
verify_arg "source_path"
|
34
|
-
verify_arg "cache_path"
|
30
|
+
self["root"] = AppConfiguration.root_for(self)
|
31
|
+
# defaults to the directory name of the source path if not set
|
32
|
+
self["name"] ||= File.basename(self["source_path"])
|
33
|
+
# setting the cache path might need a valid app name
|
34
|
+
self["cache_path"] = detect_cache_path(options, inherited_options)
|
35
35
|
end
|
36
36
|
|
37
37
|
# Returns the path to the workspace root as a Pathname.
|
38
|
-
# Defaults to Licensed::Git.repository_root if not explicitly set
|
39
38
|
def root
|
40
|
-
Pathname.new(self["root"])
|
39
|
+
@root ||= Pathname.new(self["root"])
|
41
40
|
end
|
42
41
|
|
43
42
|
# Returns the path to the app cache directory as a Pathname
|
@@ -102,13 +101,20 @@ module Licensed
|
|
102
101
|
|
103
102
|
private
|
104
103
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
104
|
+
# Returns the cache path for the application based on:
|
105
|
+
# 1. An explicitly set cache path for the application, if set
|
106
|
+
# 2. An inherited root cache path joined with the app name
|
107
|
+
# 3. The default cache path joined with the app name
|
108
|
+
def detect_cache_path(options, inherited_options)
|
109
|
+
return options["cache_path"] unless options["cache_path"].to_s.empty?
|
110
|
+
|
111
|
+
# if cache_path and shared_cache are both set in inherited_options,
|
112
|
+
# don't append the app name to the cache path
|
113
|
+
cache_path = inherited_options["cache_path"]
|
114
|
+
return cache_path if cache_path && inherited_options["shared_cache"] == true
|
115
|
+
|
116
|
+
cache_path ||= DEFAULT_CACHE_PATH
|
117
|
+
File.join(cache_path, self["name"])
|
112
118
|
end
|
113
119
|
|
114
120
|
def verify_arg(property)
|
@@ -118,9 +124,18 @@ module Licensed
|
|
118
124
|
end
|
119
125
|
end
|
120
126
|
|
121
|
-
class Configuration
|
127
|
+
class Configuration
|
128
|
+
DEFAULT_CONFIG_FILES = [
|
129
|
+
".licensed.yml".freeze,
|
130
|
+
".licensed.yaml".freeze,
|
131
|
+
".licensed.json".freeze
|
132
|
+
].freeze
|
133
|
+
|
122
134
|
class LoadError < StandardError; end
|
123
135
|
|
136
|
+
# An array of the applications in this licensed configuration.
|
137
|
+
attr_reader :apps
|
138
|
+
|
124
139
|
# Loads and returns a Licensed::Configuration object from the given path.
|
125
140
|
# The path can be relative or absolute, and can point at a file or directory.
|
126
141
|
# If the path given is a directory, the directory will be searched for a
|
@@ -133,21 +148,41 @@ module Licensed
|
|
133
148
|
|
134
149
|
def initialize(options = {})
|
135
150
|
apps = options.delete("apps") || []
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
140
|
-
|
141
|
-
# Returns an array of the applications for this licensed configuration.
|
142
|
-
# If the configuration did not explicitly configure any applications,
|
143
|
-
# return self as an application configuration.
|
144
|
-
def apps
|
145
|
-
return [self] if self["apps"].empty?
|
146
|
-
self["apps"]
|
151
|
+
apps << default_options.merge(options) if apps.empty?
|
152
|
+
apps = apps.flat_map { |app| self.class.expand_app_source_path(app) }
|
153
|
+
@apps = apps.map { |app| AppConfiguration.new(app, options) }
|
147
154
|
end
|
148
155
|
|
149
156
|
private
|
150
157
|
|
158
|
+
def self.expand_app_source_path(app_config)
|
159
|
+
return app_config if app_config["source_path"].to_s.empty?
|
160
|
+
|
161
|
+
source_path = File.expand_path(app_config["source_path"], AppConfiguration.root_for(app_config))
|
162
|
+
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
|
165
|
+
|
166
|
+
# map the expanded paths to new application configurations
|
167
|
+
expanded_source_paths.map do |path|
|
168
|
+
config = app_config.merge("source_path" => path)
|
169
|
+
|
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
|
+
config["name"] = "#{config["name"]}-#{dir_name}" if config["name"]
|
175
|
+
|
176
|
+
# if a cache_path is set and is not marked as shared, append the app name
|
177
|
+
# to the end of the cache path to make a unique cache path for the app
|
178
|
+
if config["cache_path"] && config["shared_cache"] != true
|
179
|
+
config["cache_path"] = File.join(config["cache_path"], dir_name)
|
180
|
+
end
|
181
|
+
|
182
|
+
config
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
151
186
|
# Find a default configuration file in the given directory.
|
152
187
|
# File preference is given by the order of elements in DEFAULT_CONFIG_FILES
|
153
188
|
#
|
@@ -198,7 +233,7 @@ module Licensed
|
|
198
233
|
# manually set a cache path without additional name
|
199
234
|
{
|
200
235
|
"source_path" => Dir.pwd,
|
201
|
-
"cache_path" => DEFAULT_CACHE_PATH
|
236
|
+
"cache_path" => AppConfiguration::DEFAULT_CACHE_PATH
|
202
237
|
}
|
203
238
|
end
|
204
239
|
end
|