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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8e24e2f900732197c5d4b91212a7477dd7ab68a61892e0ea56ae1a99f4c29e6
4
- data.tar.gz: 19c679898ccab8f60d219d739c18a14b849cdd87ad04608b24dcf1ea67613f82
3
+ metadata.gz: 9411b608edb8210d926f1c927bcaa65e396eac39dbf6300b946e842a33071a23
4
+ data.tar.gz: a4dd2527919e79c111107482233945f476df72d9f282431abf99f40a3516b221
5
5
  SHA512:
6
- metadata.gz: 171905b06dca655364341c888c5e8845e6dff8d163ffb009f28de624e4d14d399c7add250d80ebb7391b4c94316c8a3c9bbae7bdfb7b219996a8415f463b7a5c
7
- data.tar.gz: 054fc3dd7ccb7e3a92b6585e757b85efd0880b1b9c3fa93855ba50ba79d0c7727d707a76d30f588a94b9f7bb1dd0d6bc6f3959e40bf96685f5dda5ea1a7d127f
6
+ metadata.gz: 07f9153972ac85375a1cb8273d9990052a8d13bc3918c0cd7697c7a0686ef31ed45045065b10091b569a53ad0f2494ae32f0cf2392ae937d242b2954af92c0f6
7
+ data.tar.gz: 90364cc7be14673627b0280b018498a0feffc962b1c48a89094b9503d5743362f99ee0c36d601478b1818b10ee48d5b1ef97bdd120d6fbc7299b55efa9c5278c
@@ -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.1...HEAD
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:
@@ -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 path glob patterns
22
+ ### Source paths
23
23
 
24
- The `source_path` property can use a glob path to share configuration properties across multiple application entrypoints.
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
@@ -0,0 +1,4 @@
1
+ # Swift
2
+
3
+ The Swift source uses `swift package` subcommands
4
+ to enumerate dependencies and properties.
@@ -158,18 +158,41 @@ module Licensed
158
158
  private
159
159
 
160
160
  def self.expand_app_source_path(app_config)
161
- return app_config if app_config["source_path"].to_s.empty?
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
- source_path = File.expand_path(app_config["source_path"], AppConfiguration.root_for(app_config))
165
- return app_config if Dir.exist?(source_path)
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 = Dir.glob(source_path).select { |p| File.directory?(p) }
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
- return app_config if configs.size == 0
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
@@ -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
- # reset all bundler configuration
148
- ::Bundler.reset!
149
- # and re-configure with settings for current directory
150
- ::Bundler.configure
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
- ENV["BUNDLE_GEMFILE"] = original_bundle_gemfile
155
- ::Bundler.ui = bundler_ui
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
- # restore bundler configuration
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:: MissingSpecification.new(name: name, version: version, platform: platform, source: source)
51
+ Licensed::Bundler::MissingSpecification.new(name: name, version: version, platform: platform, source: source)
52
52
  end
53
53
  end
54
54
  end
@@ -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
- json = JSON.parse(project_assets_file)
184
- nuget_packages_dir = json["project"]["restore"]["packagesPath"]
185
- json["targets"].each_with_object({}) do |(_, target), dependencies|
186
- target.each do |reference_key, reference|
187
- # Ignore project references
188
- next unless reference["type"] == "package"
189
- package_id_parts = reference_key.partition("/")
190
- name = package_id_parts[0]
191
- version = package_id_parts[-1]
192
- id = "#{name}-#{version}"
193
-
194
- # Already know this package from another target
195
- next if dependencies.key?(id)
196
-
197
- path = File.join(nuget_packages_dir, json["libraries"][reference_key]["path"])
198
- dependencies[id] = NuGetDependency.new(
199
- name: id,
200
- version: version,
201
- path: path,
202
- metadata: {
203
- "type" => NuGet.type,
204
- "name" => name
205
- }
206
- )
207
- end
208
- end.values
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Licensed
3
- VERSION = "3.0.1".freeze
3
+ VERSION = "3.1.0".freeze
4
4
 
5
5
  def self.previous_major_versions
6
6
  major_version = Gem::Version.new(Licensed::VERSION).segments.first
@@ -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.1
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-05-18 00:00:00.000000000 Z
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