licensed 3.0.1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +27 -0
- data/CHANGELOG.md +19 -1
- data/README.md +1 -0
- data/docs/configuration.md +14 -2
- data/docs/sources/swift.md +4 -0
- data/lib/licensed/configuration.rb +28 -5
- data/lib/licensed/sources.rb +1 -0
- data/lib/licensed/sources/bundler.rb +18 -13
- data/lib/licensed/sources/bundler/missing_specification.rb +1 -1
- data/lib/licensed/sources/go.rb +1 -1
- data/lib/licensed/sources/nuget.rb +57 -27
- data/lib/licensed/sources/swift.rb +69 -0
- data/lib/licensed/version.rb +1 -1
- data/script/source-setup/swift +22 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9411b608edb8210d926f1c927bcaa65e396eac39dbf6300b946e842a33071a23
|
4
|
+
data.tar.gz: a4dd2527919e79c111107482233945f476df72d9f282431abf99f40a3516b221
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07f9153972ac85375a1cb8273d9990052a8d13bc3918c0cd7697c7a0686ef31ed45045065b10091b569a53ad0f2494ae32f0cf2392ae937d242b2954af92c0f6
|
7
|
+
data.tar.gz: 90364cc7be14673627b0280b018498a0feffc962b1c48a89094b9503d5743362f99ee0c36d601478b1818b10ee48d5b1ef97bdd120d6fbc7299b55efa9c5278c
|
data/.github/workflows/test.yml
CHANGED
@@ -362,6 +362,33 @@ jobs:
|
|
362
362
|
- name: Run tests
|
363
363
|
run: script/test pipenv
|
364
364
|
|
365
|
+
swift:
|
366
|
+
runs-on: ubuntu-latest
|
367
|
+
strategy:
|
368
|
+
matrix:
|
369
|
+
swift: [ "5.4", "5.3" ]
|
370
|
+
steps:
|
371
|
+
- uses: actions/checkout@v2
|
372
|
+
- name: Setup Swift
|
373
|
+
uses: fwal/setup-swift@v1
|
374
|
+
with:
|
375
|
+
swift-version: ${{ matrix.swift }}
|
376
|
+
- name: Set up Ruby
|
377
|
+
uses: ruby/setup-ruby@v1
|
378
|
+
with:
|
379
|
+
ruby-version: 2.6
|
380
|
+
- run: bundle lock
|
381
|
+
- uses: actions/cache@v1
|
382
|
+
with:
|
383
|
+
path: vendor/gems
|
384
|
+
key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
|
385
|
+
- name: Bootstrap
|
386
|
+
run: script/bootstrap
|
387
|
+
- name: Set up fixtures
|
388
|
+
run: script/source-setup/swift
|
389
|
+
- name: Run tests
|
390
|
+
run: script/test swift
|
391
|
+
|
365
392
|
yarn:
|
366
393
|
runs-on: ubuntu-latest
|
367
394
|
strategy:
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## 3.1.0
|
10
|
+
|
11
|
+
2021-06-16
|
12
|
+
|
13
|
+
### Added
|
14
|
+
|
15
|
+
- Licensed supports Swift/Swift package manager as a dependency source (:tada: @mattt https://github.com/github/licensed/pull/363)'
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
|
19
|
+
- The `source_path` configuration property accepts arrays of inclusion and exclusion glob patterns (https://github.com/github/licensed/pull/368)
|
20
|
+
- The Nuget source now uses configured fallback folders to find dependencies that are not in found in the project folder (https://github.com/github/licensed/pull/366)
|
21
|
+
- The Nuget source supports a configurable property for the path from the project source path to the project's `obj` folder (https://github.com/github/licensed/pull/365)
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
- The Go source's checks for local packages will correctly find paths in case-insensitive file systems (https://github.com/github/licensed/pull/370)
|
25
|
+
- The Bundler source will no longer unnecessarily reset the local Bundler environment configuration (https://github.com/github/licensed/pull/372)
|
26
|
+
|
9
27
|
## 3.0.1
|
10
28
|
|
11
29
|
2021-05-17
|
@@ -429,4 +447,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
429
447
|
|
430
448
|
Initial release :tada:
|
431
449
|
|
432
|
-
[Unreleased]: https://github.com/github/licensed/compare/3.0
|
450
|
+
[Unreleased]: https://github.com/github/licensed/compare/3.1.0...HEAD
|
data/README.md
CHANGED
@@ -125,6 +125,7 @@ Dependencies will be automatically detected for all of the following sources by
|
|
125
125
|
1. [NuGet](./docs/sources/nuget.md)
|
126
126
|
1. [Pip](./docs/sources/pip.md)
|
127
127
|
1. [Pipenv](./docs/sources/pipenv.md)
|
128
|
+
1. [Swift](./docs/sources/swift.md)
|
128
129
|
1. [Yarn](./docs/sources/yarn.md)
|
129
130
|
|
130
131
|
You can disable any of them in the configuration file:
|
data/docs/configuration.md
CHANGED
@@ -19,9 +19,13 @@ 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
|
22
|
+
### Source paths
|
23
23
|
|
24
|
-
|
24
|
+
A source path is the directory in which licensed should run to enumerate dependencies. This is often dependent on the project type, for example the bundler source should be run from the directory containing a `Gemfile` or `gems.rb` while the go source should be run from the directory containing an entrypoint function.
|
25
|
+
|
26
|
+
#### Using glob patterns
|
27
|
+
|
28
|
+
The `source_path` property can use one or more glob patterns to share configuration properties across multiple application entrypoints.
|
25
29
|
|
26
30
|
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
31
|
|
@@ -33,6 +37,14 @@ sources:
|
|
33
37
|
source_path: cmd/*
|
34
38
|
```
|
35
39
|
|
40
|
+
In order to better filter the results from glob patterns, the `source_path` property also accepts an array of inclusion and exclusion glob patterns similar to gitignore files. Inclusion patterns will add matching directory paths to resulting set of source paths, while exclusion patterns will remove matching directory paths.
|
41
|
+
|
42
|
+
```yml
|
43
|
+
source_path:
|
44
|
+
- "projects/*" # include by default all directories under "projects"
|
45
|
+
- "!projects/*Test" # exclude all projects ending in "Test"
|
46
|
+
```
|
47
|
+
|
36
48
|
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
49
|
|
38
50
|
## Restricting sources
|
@@ -158,18 +158,41 @@ module Licensed
|
|
158
158
|
private
|
159
159
|
|
160
160
|
def self.expand_app_source_path(app_config)
|
161
|
-
|
161
|
+
# map a source_path configuration value to an array of non-empty values
|
162
|
+
source_path_array = Array(app_config["source_path"])
|
163
|
+
.reject { |path| path.to_s.empty? }
|
164
|
+
.compact
|
165
|
+
app_root = AppConfiguration.root_for(app_config)
|
166
|
+
return app_config.merge("source_path" => app_root) if source_path_array.empty?
|
162
167
|
|
163
168
|
# check if the source path maps to an existing directory
|
164
|
-
|
165
|
-
|
169
|
+
if source_path_array.length == 1
|
170
|
+
source_path = File.expand_path(source_path_array[0], app_root)
|
171
|
+
return app_config.merge("source_path" => source_path) if Dir.exist?(source_path)
|
172
|
+
end
|
166
173
|
|
167
174
|
# try to expand the source path for glob patterns
|
168
|
-
expanded_source_paths =
|
175
|
+
expanded_source_paths = source_path_array.reduce(Set.new) do |matched_paths, pattern|
|
176
|
+
current_matched_paths = if pattern.start_with?("!")
|
177
|
+
# if the pattern is an exclusion, remove all matching files
|
178
|
+
# from the result
|
179
|
+
matched_paths - Dir.glob(pattern[1..-1])
|
180
|
+
else
|
181
|
+
# if the pattern is an inclusion, add all matching files
|
182
|
+
# to the result
|
183
|
+
matched_paths + Dir.glob(pattern)
|
184
|
+
end
|
185
|
+
|
186
|
+
current_matched_paths.select { |p| File.directory?(p) }
|
187
|
+
end
|
188
|
+
|
169
189
|
configs = expanded_source_paths.map { |path| app_config.merge("source_path" => path) }
|
170
190
|
|
171
191
|
# if no directories are found for the source path, return the original config
|
172
|
-
|
192
|
+
if configs.size == 0
|
193
|
+
app_config["source_path"] = app_root if app_config["source_path"].is_a?(Array)
|
194
|
+
return app_config
|
195
|
+
end
|
173
196
|
|
174
197
|
# update configured values for name and cache_path for uniqueness.
|
175
198
|
# this is only needed when values are explicitly set, AppConfiguration
|
data/lib/licensed/sources.rb
CHANGED
@@ -14,6 +14,7 @@ module Licensed
|
|
14
14
|
require "licensed/sources/nuget"
|
15
15
|
require "licensed/sources/pip"
|
16
16
|
require "licensed/sources/pipenv"
|
17
|
+
require "licensed/sources/swift"
|
17
18
|
require "licensed/sources/gradle"
|
18
19
|
require "licensed/sources/mix"
|
19
20
|
require "licensed/sources/yarn"
|
@@ -134,28 +134,33 @@ module Licensed
|
|
134
134
|
@lockfile_path ||= gemfile_path.dirname.join(GEMFILES[gemfile_path.basename.to_s])
|
135
135
|
end
|
136
136
|
|
137
|
-
private
|
138
|
-
|
139
137
|
# helper to clear all bundler environment around a yielded block
|
140
138
|
def with_local_configuration
|
141
|
-
# force bundler to use the local gem file
|
142
|
-
original_bundle_gemfile, ENV["BUNDLE_GEMFILE"] = ENV["BUNDLE_GEMFILE"], gemfile_path.to_s
|
143
|
-
|
144
139
|
# silence any bundler warnings while running licensed
|
145
140
|
bundler_ui, ::Bundler.ui = ::Bundler.ui, ::Bundler::UI::Silent.new
|
146
141
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
142
|
+
original_bundle_gemfile = nil
|
143
|
+
if gemfile_path.to_s != ENV["BUNDLE_GEMFILE"]
|
144
|
+
# force bundler to use the local gem file
|
145
|
+
original_bundle_gemfile, ENV["BUNDLE_GEMFILE"] = ENV["BUNDLE_GEMFILE"], gemfile_path.to_s
|
146
|
+
|
147
|
+
# reset all bundler configuration
|
148
|
+
::Bundler.reset!
|
149
|
+
# and re-configure with settings for current directory
|
150
|
+
::Bundler.configure
|
151
|
+
end
|
151
152
|
|
152
153
|
yield
|
153
154
|
ensure
|
154
|
-
|
155
|
-
|
155
|
+
if original_bundle_gemfile
|
156
|
+
ENV["BUNDLE_GEMFILE"] = original_bundle_gemfile
|
157
|
+
|
158
|
+
# restore bundler configuration
|
159
|
+
::Bundler.reset!
|
160
|
+
::Bundler.configure
|
161
|
+
end
|
156
162
|
|
157
|
-
|
158
|
-
::Bundler.configure
|
163
|
+
::Bundler.ui = bundler_ui
|
159
164
|
end
|
160
165
|
|
161
166
|
# Returns whether the current licensed execution is running ruby-packer
|
@@ -48,7 +48,7 @@ module Bundler
|
|
48
48
|
spec = orig_materialize
|
49
49
|
return spec if spec
|
50
50
|
|
51
|
-
Licensed::Bundler::
|
51
|
+
Licensed::Bundler::MissingSpecification.new(name: name, version: version, platform: platform, source: source)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
data/lib/licensed/sources/go.rb
CHANGED
@@ -98,7 +98,7 @@ module Licensed
|
|
98
98
|
# Returns whether the package is local to the current project
|
99
99
|
def local_package?(package)
|
100
100
|
return false unless package && package["Dir"]
|
101
|
-
return false unless File.fnmatch?("#{config.root.to_s}*", package["Dir"])
|
101
|
+
return false unless File.fnmatch?("#{config.root.to_s}*", package["Dir"], File::FNM_CASEFOLD)
|
102
102
|
vendored_path_parts(package).nil?
|
103
103
|
end
|
104
104
|
|
@@ -164,7 +164,7 @@ module Licensed
|
|
164
164
|
end
|
165
165
|
|
166
166
|
def project_assets_file_path
|
167
|
-
File.join(config.pwd, "project.assets.json")
|
167
|
+
File.join(config.pwd, nuget_obj_path, "project.assets.json")
|
168
168
|
end
|
169
169
|
|
170
170
|
def project_assets_file
|
@@ -172,6 +172,17 @@ module Licensed
|
|
172
172
|
@project_assets_file = File.read(project_assets_file_path)
|
173
173
|
end
|
174
174
|
|
175
|
+
def project_assets_json
|
176
|
+
@project_assets_json ||= JSON.parse(project_assets_file)
|
177
|
+
rescue JSON::ParserError => e
|
178
|
+
message = "Licensed was unable to read the project.assets.json file. Error: #{e.message}"
|
179
|
+
raise Licensed::Sources::Source::Error, message
|
180
|
+
end
|
181
|
+
|
182
|
+
def nuget_obj_path
|
183
|
+
config.dig("nuget", "obj_path") || ""
|
184
|
+
end
|
185
|
+
|
175
186
|
def enabled?
|
176
187
|
File.exist?(project_assets_file_path)
|
177
188
|
end
|
@@ -180,32 +191,51 @@ module Licensed
|
|
180
191
|
# Ideally we'd use `dotnet list package` instead, but its output isn't
|
181
192
|
# easily machine readable and doesn't contain everything we need.
|
182
193
|
def enumerate_dependencies
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
name
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
194
|
+
reference_keys.map do |reference_key|
|
195
|
+
package_id_parts = reference_key.partition("/")
|
196
|
+
name = package_id_parts[0]
|
197
|
+
version = package_id_parts[-1]
|
198
|
+
id = "#{name}-#{version}"
|
199
|
+
|
200
|
+
path = full_dependency_path(reference_key)
|
201
|
+
error = "Package #{id} path was not found in project.assets.json, or does not exist on disk at any project package folder" if path.nil?
|
202
|
+
|
203
|
+
NuGetDependency.new(
|
204
|
+
name: id,
|
205
|
+
version: version,
|
206
|
+
path: path,
|
207
|
+
errors: Array(error),
|
208
|
+
metadata: {
|
209
|
+
"type" => NuGet.type,
|
210
|
+
"name" => name
|
211
|
+
}
|
212
|
+
)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns a unique set of the package reference keys used across all target groups
|
217
|
+
def reference_keys
|
218
|
+
all_reference_keys = project_assets_json["targets"].flat_map do |_, references|
|
219
|
+
references.select { |key, reference| reference["type"] == "package" }
|
220
|
+
.keys
|
221
|
+
end
|
222
|
+
|
223
|
+
Set.new(all_reference_keys)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Returns a dependency's path, if it exists, in one of the project's global or fallback package folders
|
227
|
+
def full_dependency_path(reference_key)
|
228
|
+
dependency_path = project_assets_json.dig("libraries", reference_key, "path")
|
229
|
+
return unless dependency_path
|
230
|
+
|
231
|
+
nuget_package_dirs = [
|
232
|
+
project_assets_json.dig("project", "restore", "packagesPath"),
|
233
|
+
*Array(project_assets_json.dig("project", "restore", "fallbackFolders"))
|
234
|
+
].compact
|
235
|
+
|
236
|
+
nuget_package_dirs.map { |dir| File.join(dir, dependency_path) }
|
237
|
+
.select { |path| File.directory?(path) }
|
238
|
+
.first
|
209
239
|
end
|
210
240
|
end
|
211
241
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "json"
|
3
|
+
require "pathname"
|
4
|
+
require "uri"
|
5
|
+
|
6
|
+
module Licensed
|
7
|
+
module Sources
|
8
|
+
class Swift < Source
|
9
|
+
def enabled?
|
10
|
+
return unless Licensed::Shell.tool_available?("swift") && swift_package?
|
11
|
+
File.exist?(package_resolved_file_path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def enumerate_dependencies
|
15
|
+
pins.map { |pin|
|
16
|
+
name = pin["package"]
|
17
|
+
version = pin.dig("state", "version")
|
18
|
+
path = dependency_path_for_url(pin["repositoryURL"])
|
19
|
+
error = "Unable to determine project path from #{url}" unless path
|
20
|
+
|
21
|
+
Dependency.new(
|
22
|
+
name: name,
|
23
|
+
path: path,
|
24
|
+
version: version,
|
25
|
+
errors: Array(error),
|
26
|
+
metadata: {
|
27
|
+
"type" => Swift.type,
|
28
|
+
"homepage" => homepage_for_url(pin["repositoryURL"])
|
29
|
+
}
|
30
|
+
)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def pins
|
37
|
+
return @pins if defined?(@pins)
|
38
|
+
|
39
|
+
@pins = begin
|
40
|
+
json = JSON.parse(File.read(package_resolved_file_path))
|
41
|
+
json.dig("object", "pins")
|
42
|
+
rescue => e
|
43
|
+
message = "Licensed was unable to read the Package.resolved file. Error: #{e.message}"
|
44
|
+
raise Licensed::Sources::Source::Error, message
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def dependency_path_for_url(url)
|
49
|
+
last_path_component = URI(url).path.split("/").last.sub(/\.git$/, "")
|
50
|
+
File.join(config.pwd, ".build", "checkouts", last_path_component)
|
51
|
+
rescue URI::InvalidURIError
|
52
|
+
end
|
53
|
+
|
54
|
+
def homepage_for_url(url)
|
55
|
+
return unless %w{http https}.include?(URI(url).scheme)
|
56
|
+
url.sub(/\.git$/, "")
|
57
|
+
rescue URI::InvalidURIError
|
58
|
+
end
|
59
|
+
|
60
|
+
def package_resolved_file_path
|
61
|
+
File.join(config.pwd, "Package.resolved")
|
62
|
+
end
|
63
|
+
|
64
|
+
def swift_package?
|
65
|
+
Licensed::Shell.success?("swift", "package", "describe")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/licensed/version.rb
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
if [ -z "$(which swift)" ]; then
|
5
|
+
echo "A local swift installation is required for swift development." >&2
|
6
|
+
exit 127
|
7
|
+
fi
|
8
|
+
|
9
|
+
swift --version
|
10
|
+
|
11
|
+
BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
12
|
+
cd $BASE_PATH/test/fixtures/swift
|
13
|
+
|
14
|
+
if [ "$1" == "-f" ]; then
|
15
|
+
find . -not -regex "\.*" \
|
16
|
+
-and -not -path "*/Package.swift" \
|
17
|
+
-and -not -path "*/Sources*" \
|
18
|
+
-and -not -path "*/Tests*" \
|
19
|
+
-print0 | xargs -0 rm -rf
|
20
|
+
fi
|
21
|
+
|
22
|
+
swift package resolve
|
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: 3.0
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: licensee
|
@@ -262,6 +262,7 @@ files:
|
|
262
262
|
- docs/sources/pip.md
|
263
263
|
- docs/sources/pipenv.md
|
264
264
|
- docs/sources/stack.md
|
265
|
+
- docs/sources/swift.md
|
265
266
|
- docs/sources/yarn.md
|
266
267
|
- exe/licensed
|
267
268
|
- lib/licensed.rb
|
@@ -306,6 +307,7 @@ files:
|
|
306
307
|
- lib/licensed/sources/pip.rb
|
307
308
|
- lib/licensed/sources/pipenv.rb
|
308
309
|
- lib/licensed/sources/source.rb
|
310
|
+
- lib/licensed/sources/swift.rb
|
309
311
|
- lib/licensed/sources/yarn.rb
|
310
312
|
- lib/licensed/ui/shell.rb
|
311
313
|
- lib/licensed/version.rb
|
@@ -329,6 +331,7 @@ files:
|
|
329
331
|
- script/source-setup/nuget
|
330
332
|
- script/source-setup/pip
|
331
333
|
- script/source-setup/pipenv
|
334
|
+
- script/source-setup/swift
|
332
335
|
- script/source-setup/yarn
|
333
336
|
- script/test
|
334
337
|
homepage: https://github.com/github/licensed
|