licensed 2.9.2 → 2.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5d614629bc40a6943d7559bac1b1eb070ea69edb
4
- data.tar.gz: 4ee2fcae1f10b35ee656f8525318d22784ae7641
2
+ SHA256:
3
+ metadata.gz: 8b47685dba77cc47406dfa9aa2dd805c5f4a1746ea547a5b34c39a6d2ec47dcf
4
+ data.tar.gz: 0eef1add309449ee2f00a33831d780cf6250990bb74be08077cf786ab28996a2
5
5
  SHA512:
6
- metadata.gz: 62e67b0b4659935131e62f340ca85d922313b30683ef9ad4993d1ee27948429a136a12c2b3749969ea1cddcedcd7d1d9a071c64118f68f1bcbb349e05c4e5521
7
- data.tar.gz: 693472a314b0393705cb11da11ee801e48f705dbd3a58cd1f7900e662e1925882abdf4a2a7e6bca54076dcf409bf86d6e8f23e22e1dec4691a8b41bf3e36e003
6
+ metadata.gz: e01ca0150f1690ade72d0c95743d435af9796269d6b3a51496d39b5e98c186dc0cf78dd824f565c7070dc75e8a85e5af0aad21c8382b92b1578bd09edbe89dd3
7
+ data.tar.gz: 0d00ed4c4b0596f96cb2ec77972fe36352bc17a84ee7043b5c3d002db9d9b2f15c9ddf87a2b67ebf36b99eb8dc8b02b4a917e68f9d83d2d42457bb26ee3a5314
@@ -14,7 +14,7 @@ jobs:
14
14
  needs: tag_filter
15
15
 
16
16
  steps:
17
- - uses: actions/checkout@master
17
+ - uses: actions/checkout@v2
18
18
  - name: Set up Ruby 2.6
19
19
  uses: actions/setup-ruby@v1
20
20
  with:
@@ -25,7 +25,7 @@ jobs:
25
25
  env:
26
26
  VERSION: ${{github.event.ref}}
27
27
 
28
- - uses: actions/upload-artifact@master
28
+ - uses: actions/upload-artifact@v2
29
29
  with:
30
30
  name: ${{github.event.ref}}-linux
31
31
  path: pkg/${{github.event.ref}}/licensed-${{github.event.ref}}-linux-x64.tar.gz
@@ -35,7 +35,7 @@ jobs:
35
35
  needs: tag_filter
36
36
 
37
37
  steps:
38
- - uses: actions/checkout@master
38
+ - uses: actions/checkout@v2
39
39
  - name: Set up Ruby 2.6
40
40
  uses: actions/setup-ruby@v1
41
41
  with:
@@ -46,7 +46,7 @@ jobs:
46
46
  env:
47
47
  VERSION: ${{github.event.ref}}
48
48
 
49
- - uses: actions/upload-artifact@master
49
+ - uses: actions/upload-artifact@v2
50
50
  with:
51
51
  name: ${{github.event.ref}}-darwin
52
52
  path: pkg/${{github.event.ref}}/licensed-${{github.event.ref}}-darwin-x64.tar.gz
@@ -56,7 +56,7 @@ jobs:
56
56
  needs: tag_filter
57
57
 
58
58
  steps:
59
- - uses: actions/checkout@master
59
+ - uses: actions/checkout@v2
60
60
  - name: Set up Ruby 2.6
61
61
  uses: actions/setup-ruby@v1
62
62
  with:
@@ -65,7 +65,7 @@ jobs:
65
65
  - name: Build gem
66
66
  run: gem build *.gemspec
67
67
 
68
- - uses: actions/upload-artifact@master
68
+ - uses: actions/upload-artifact@v2
69
69
  with:
70
70
  name: ${{github.event.ref}}-gem
71
71
  path: licensed-${{github.event.ref}}.gem
@@ -74,7 +74,6 @@ jobs:
74
74
  runs-on: ubuntu-latest
75
75
  needs: [package_linux, package_mac, build_gem]
76
76
  steps:
77
- - uses: actions/checkout@master
78
77
  - uses: Roang-zero1/github-create-release-action@v1.0.2
79
78
  env:
80
79
  GITHUB_TOKEN: ${{ secrets.API_AUTH_TOKEN }}
@@ -91,24 +90,24 @@ jobs:
91
90
  ruby-version: 2.6.x
92
91
 
93
92
  - name: Download linux package
94
- uses: actions/download-artifact@master
93
+ uses: actions/download-artifact@v2
95
94
  with:
96
95
  name: ${{github.event.ref}}-linux
97
96
 
98
97
  - name: Download macOS package
99
- uses: actions/download-artifact@master
98
+ uses: actions/download-artifact@v2
100
99
  with:
101
100
  name: ${{github.event.ref}}-darwin
102
101
 
103
102
  - name: Download gem
104
- uses: actions/download-artifact@master
103
+ uses: actions/download-artifact@v2
105
104
  with:
106
105
  name: ${{github.event.ref}}-gem
107
106
 
108
107
  - name: Publish packages to GitHub Release
109
108
  uses: Roang-zero1/github-upload-release-artifacts-action@v2.0.0
110
109
  with:
111
- args: ${{github.event.ref}}-linux ${{github.event.ref}}-darwin
110
+ args: licensed-${{github.event.ref}}-linux-x64.tar.gz licensed-${{github.event.ref}}-darwin-x64.tar.gz
112
111
  env:
113
112
  GITHUB_TOKEN: ${{secrets.API_AUTH_TOKEN}}
114
113
 
@@ -121,4 +120,4 @@ jobs:
121
120
  gem push $GEM
122
121
  env:
123
122
  GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
124
- GEM: ${{github.event.ref}}-gem/*.gem
123
+ GEM: licensed-${{github.event.ref}}.gem
@@ -1,17 +1,12 @@
1
1
  name: Test
2
2
 
3
- on:
4
- push:
5
- branches:
6
- - "*"
7
- tags:
8
- - "!*"
3
+ on: pull_request
9
4
 
10
5
  jobs:
11
6
  bower:
12
7
  runs-on: ubuntu-latest
13
8
  steps:
14
- - uses: actions/checkout@master
9
+ - uses: actions/checkout@v2
15
10
  - name: Setup node
16
11
  uses: actions/setup-node@v1
17
12
  with:
@@ -23,10 +18,10 @@ jobs:
23
18
  with:
24
19
  ruby-version: 2.6.x
25
20
  - run: bundle lock
26
- - uses: actions/cache@preview
21
+ - uses: actions/cache@v1
27
22
  with:
28
23
  path: vendor/gems
29
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
24
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
30
25
  - name: Bootstrap
31
26
  run: script/bootstrap
32
27
  - name: Set up fixtures
@@ -40,7 +35,7 @@ jobs:
40
35
  matrix:
41
36
  bundler: [ '~> 1.15.0', '~> 1.16.0', '~> 1.17.0', '~> 2.0.0' ]
42
37
  steps:
43
- - uses: actions/checkout@master
38
+ - uses: actions/checkout@v2
44
39
  - name: Set up Ruby
45
40
  uses: actions/setup-ruby@v1
46
41
  with:
@@ -50,10 +45,10 @@ jobs:
50
45
  yes | gem uninstall bundler --all
51
46
  gem install bundler -v "${{ matrix.bundler }}"
52
47
  - run: bundle lock
53
- - uses: actions/cache@preview
48
+ - uses: actions/cache@v1
54
49
  with:
55
50
  path: vendor/gems
56
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
51
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
57
52
  - name: Bootstrap
58
53
  run: script/bootstrap
59
54
  - name: Set up fixtures
@@ -66,9 +61,9 @@ jobs:
66
61
  strategy:
67
62
  matrix:
68
63
  ghc: [ '8.2.2', '8.6.5' ]
69
- cabal: [ '2.0', '3.0' ]
64
+ cabal: [ '2.2', '2.4', '3.0', 'latest' ]
70
65
  steps:
71
- - uses: actions/checkout@master
66
+ - uses: actions/checkout@v2
72
67
  - name: Set up Ruby
73
68
  uses: actions/setup-ruby@v1
74
69
  with:
@@ -79,10 +74,10 @@ jobs:
79
74
  ghc-version: ${{ matrix.ghc }}
80
75
  cabal-version: ${{ matrix.cabal }}
81
76
  - run: bundle lock
82
- - uses: actions/cache@preview
77
+ - uses: actions/cache@v1
83
78
  with:
84
79
  path: vendor/gems
85
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
80
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
86
81
  - name: Bootstrap
87
82
  run: script/bootstrap
88
83
  - name: Set up fixtures
@@ -96,7 +91,7 @@ jobs:
96
91
  matrix:
97
92
  php: [ '5.6', '7.1', '7.2', '7.3' ]
98
93
  steps:
99
- - uses: actions/checkout@master
94
+ - uses: actions/checkout@v2
100
95
  - name: Setup php
101
96
  uses: nanasess/setup-php@v1.0.2
102
97
  with:
@@ -106,10 +101,10 @@ jobs:
106
101
  with:
107
102
  ruby-version: 2.6.x
108
103
  - run: bundle lock
109
- - uses: actions/cache@preview
104
+ - uses: actions/cache@v1
110
105
  with:
111
106
  path: vendor/gems
112
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
107
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
113
108
  - name: Bootstrap
114
109
  run: script/bootstrap
115
110
  - name: Set up fixtures
@@ -123,7 +118,7 @@ jobs:
123
118
  matrix:
124
119
  ruby: [ 2.4.x, 2.5.x, 2.6.x ]
125
120
  steps:
126
- - uses: actions/checkout@master
121
+ - uses: actions/checkout@v2
127
122
  - name: Set up Ruby
128
123
  uses: actions/setup-ruby@v1
129
124
  with:
@@ -131,10 +126,10 @@ jobs:
131
126
  - name: Set up Bundler
132
127
  run: gem install bundler
133
128
  - run: bundle lock
134
- - uses: actions/cache@preview
129
+ - uses: actions/cache@v1
135
130
  with:
136
131
  path: vendor/gems
137
- key: ${{ runner.os }}-gem-${{ matrix.ruby }}-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
132
+ key: ${{ runner.os }}-gem-${{ matrix.ruby }}-${{ hashFiles('**/Gemfile.lock') }}
138
133
  - name: Bootstrap
139
134
  run: script/bootstrap
140
135
  - name: Build and lint
@@ -145,7 +140,7 @@ jobs:
145
140
  dep:
146
141
  runs-on: ubuntu-latest
147
142
  steps:
148
- - uses: actions/checkout@master
143
+ - uses: actions/checkout@v2
149
144
  - name: Setup go
150
145
  uses: actions/setup-go@v1
151
146
  with:
@@ -155,10 +150,10 @@ jobs:
155
150
  with:
156
151
  ruby-version: 2.6.x
157
152
  - run: bundle lock
158
- - uses: actions/cache@preview
153
+ - uses: actions/cache@v1
159
154
  with:
160
155
  path: vendor/gems
161
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
156
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
162
157
  - name: Bootstrap
163
158
  run: script/bootstrap
164
159
  - name: Set up fixtures
@@ -172,7 +167,7 @@ jobs:
172
167
  matrix:
173
168
  go: [ '1.7.x', '1.10.x', '1.11.x', '1.12.x', '1.13.x', '1.14.x' ]
174
169
  steps:
175
- - uses: actions/checkout@master
170
+ - uses: actions/checkout@v2
176
171
  - name: Setup go
177
172
  uses: actions/setup-go@v1
178
173
  with:
@@ -182,10 +177,10 @@ jobs:
182
177
  with:
183
178
  ruby-version: 2.6.x
184
179
  - run: bundle lock
185
- - uses: actions/cache@preview
180
+ - uses: actions/cache@v1
186
181
  with:
187
182
  path: vendor/gems
188
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
183
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
189
184
  - name: Bootstrap
190
185
  run: script/bootstrap
191
186
  - name: Set up fixtures
@@ -196,16 +191,16 @@ jobs:
196
191
  gradle:
197
192
  runs-on: ubuntu-latest
198
193
  steps:
199
- - uses: actions/checkout@master
194
+ - uses: actions/checkout@v2
200
195
  - name: Set up Ruby
201
196
  uses: actions/setup-ruby@v1
202
197
  with:
203
198
  ruby-version: 2.6.x
204
199
  - run: bundle lock
205
- - uses: actions/cache@preview
200
+ - uses: actions/cache@v1
206
201
  with:
207
202
  path: vendor/gems
208
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
203
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
209
204
  - name: Bootstrap
210
205
  run: script/bootstrap
211
206
  - name: Gradle version
@@ -216,16 +211,16 @@ jobs:
216
211
  manifest:
217
212
  runs-on: ubuntu-latest
218
213
  steps:
219
- - uses: actions/checkout@master
214
+ - uses: actions/checkout@v2
220
215
  - name: Set up Ruby
221
216
  uses: actions/setup-ruby@v1
222
217
  with:
223
218
  ruby-version: 2.6.x
224
219
  - run: bundle lock
225
- - uses: actions/cache@preview
220
+ - uses: actions/cache@v1
226
221
  with:
227
222
  path: vendor/gems
228
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
223
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
229
224
  - name: Bootstrap
230
225
  run: script/bootstrap
231
226
  - name: Run tests
@@ -238,7 +233,7 @@ jobs:
238
233
  otp: [21.x, 22.x]
239
234
  elixir: [1.8.x, 1.9.x]
240
235
  steps:
241
- - uses: actions/checkout@master
236
+ - uses: actions/checkout@v2
242
237
  - uses: actions/setup-elixir@v1.0.0
243
238
  with:
244
239
  otp-version: ${{matrix.otp}}
@@ -248,10 +243,10 @@ jobs:
248
243
  with:
249
244
  ruby-version: 2.6.x
250
245
  - run: bundle lock
251
- - uses: actions/cache@preview
246
+ - uses: actions/cache@v1
252
247
  with:
253
248
  path: vendor/gems
254
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
249
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
255
250
  - name: Bootstrap
256
251
  run: script/bootstrap
257
252
  - name: Set up fixtures
@@ -265,7 +260,7 @@ jobs:
265
260
  matrix:
266
261
  node_version: [ 8, 10, 12 ]
267
262
  steps:
268
- - uses: actions/checkout@master
263
+ - uses: actions/checkout@v2
269
264
  - name: Setup node
270
265
  uses: actions/setup-node@v1
271
266
  with:
@@ -275,10 +270,10 @@ jobs:
275
270
  with:
276
271
  ruby-version: 2.6.x
277
272
  - run: bundle lock
278
- - uses: actions/cache@preview
273
+ - uses: actions/cache@v1
279
274
  with:
280
275
  path: vendor/gems
281
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
276
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
282
277
  - name: Bootstrap
283
278
  run: script/bootstrap
284
279
  - name: Set up fixtures
@@ -286,13 +281,37 @@ jobs:
286
281
  - name: Run tests
287
282
  run: script/test npm
288
283
 
284
+ nuget:
285
+ runs-on: ubuntu-latest
286
+ steps:
287
+ - uses: actions/checkout@v2
288
+ - name: Setup dotnet
289
+ uses: actions/setup-dotnet@v1
290
+ with:
291
+ dotnet-version: 3.1.202
292
+ - name: Set up Ruby
293
+ uses: actions/setup-ruby@v1
294
+ with:
295
+ ruby-version: 2.6.x
296
+ - run: bundle lock
297
+ - uses: actions/cache@v1
298
+ with:
299
+ path: vendor/gems
300
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
301
+ - name: Bootstrap
302
+ run: script/bootstrap
303
+ - name: Set up fixtures
304
+ run: script/source-setup/nuget
305
+ - name: Run tests
306
+ run: script/test nuget
307
+
289
308
  pip:
290
309
  runs-on: ubuntu-latest
291
310
  strategy:
292
311
  matrix:
293
312
  python: [ '2.x', '3.x' ]
294
313
  steps:
295
- - uses: actions/checkout@master
314
+ - uses: actions/checkout@v2
296
315
  - name: Setup python
297
316
  uses: actions/setup-python@v1
298
317
  with:
@@ -303,10 +322,10 @@ jobs:
303
322
  with:
304
323
  ruby-version: 2.6.x
305
324
  - run: bundle lock
306
- - uses: actions/cache@preview
325
+ - uses: actions/cache@v1
307
326
  with:
308
327
  path: vendor/gems
309
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
328
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
310
329
  - name: Bootstrap
311
330
  run: script/bootstrap
312
331
  - name: Install virtualenv
@@ -319,7 +338,7 @@ jobs:
319
338
  pipenv:
320
339
  runs-on: ubuntu-latest
321
340
  steps:
322
- - uses: actions/checkout@master
341
+ - uses: actions/checkout@v2
323
342
  - name: Setup python
324
343
  uses: actions/setup-python@v1
325
344
  with:
@@ -330,10 +349,10 @@ jobs:
330
349
  with:
331
350
  ruby-version: 2.6.x
332
351
  - run: bundle lock
333
- - uses: actions/cache@preview
352
+ - uses: actions/cache@v1
334
353
  with:
335
354
  path: vendor/gems
336
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
355
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
337
356
  - name: Bootstrap
338
357
  run: script/bootstrap
339
358
  - name: Install pipenv
@@ -350,7 +369,7 @@ jobs:
350
369
  # not using 1.0.0 because it doesn't support `yarn list --production`
351
370
  yarn_version: [ 1.4.0, latest ]
352
371
  steps:
353
- - uses: actions/checkout@master
372
+ - uses: actions/checkout@v2
354
373
  - name: Setup node
355
374
  uses: actions/setup-node@v1
356
375
  with:
@@ -364,10 +383,10 @@ jobs:
364
383
  with:
365
384
  ruby-version: 2.6.x
366
385
  - run: bundle lock
367
- - uses: actions/cache@preview
386
+ - uses: actions/cache@v1
368
387
  with:
369
388
  path: vendor/gems
370
- key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
389
+ key: ${{ runner.os }}-gem-2.6.x-${{ hashFiles('**/Gemfile.lock') }}
371
390
  - name: Bootstrap
372
391
  run: script/bootstrap
373
392
  - name: Set up fixtures
@@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## 2.10.0
10
+ 2020-05-15
11
+
12
+ ### Changed
13
+ - NPM source ignores missing peer dependencies (https://github.com/github/licensed/pull/267)
14
+
15
+ ### Added
16
+ - Nuget source (:tada: @zarenner https://github.com/github/licensed/pull/261)
17
+ - Multiple apps can share a single cache location (https://github.com/github/licensed/pull/263)
18
+
9
19
  ## 2.9.2
10
20
  2020-04-28
11
21
 
@@ -292,4 +302,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
292
302
 
293
303
  Initial release :tada:
294
304
 
295
- [Unreleased]: https://github.com/github/licensed/compare/2.9.2...HEAD
305
+ [Unreleased]: https://github.com/github/licensed/compare/2.10.0...HEAD
data/README.md CHANGED
@@ -102,14 +102,16 @@ Dependencies will be automatically detected for all of the following sources by
102
102
  1. [Bundler](./docs/sources/bundler.md)
103
103
  1. [Cabal](./docs/sources/cabal.md)
104
104
  1. [Composer](./docs/sources/composer.md)
105
+ 1. [Git Submodules (git_submodule)](./docs/sources/git_submodule.md)
105
106
  1. [Go](./docs/sources/go.md)
106
107
  1. [Go Dep (dep)](./docs/sources/dep.md)
108
+ 1. [Gradle](./docs/sources/gradle.md)
107
109
  1. [Manifest lists (manifests)](./docs/sources/manifests.md)
110
+ 1. [Mix](./docs/sources/mix.md)
108
111
  1. [NPM](./docs/sources/npm.md)
112
+ 1. [NuGet](./docs/sources/nuget.md)
109
113
  1. [Pip](./docs/sources/pip.md)
110
114
  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
115
  1. [Yarn](./docs/sources/yarn.md)
114
116
 
115
117
  You can disable any of them in the configuration file:
@@ -209,6 +209,26 @@ apps:
209
209
 
210
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`.
211
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
+
212
232
  ## Source specific configuration
213
233
 
214
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.
@@ -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 enumerated dependencies found in a dependency source,
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 - The application configuration for the source
22
- # source - A dependency source enumerator
38
+ # app - An application configuration
23
39
  #
24
- # Returns whether the command succeeded for the dependency source enumerator
25
- def run_source(app, source)
40
+ # Returns whether the command succeeded for the application.
41
+ def run_app(app)
26
42
  result = super
27
- clear_stale_cached_records(app, source) if result
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
 
@@ -62,6 +81,9 @@ module Licensed
62
81
  report.warnings << "expected dependency path #{dependency.path} does not exist"
63
82
  end
64
83
 
84
+ # add the absolute dependency file path to the list of files seen during this licensed run
85
+ files << filename.to_s
86
+
65
87
  true
66
88
  end
67
89
 
@@ -86,18 +108,26 @@ module Licensed
86
108
 
87
109
  # Clean up cached files that dont match current dependencies
88
110
  #
89
- # app - An application configuration
90
- # source - A dependency source enumerator
91
- #
92
111
  # Returns nothing
93
- def clear_stale_cached_records(app, source)
94
- names = source.dependencies.map { |dependency| File.join(source.class.type, dependency.name) }
95
- Dir.glob(app.cache_path.join(source.class.type, "**/*.#{DependencyRecord::EXTENSION}")).each do |file|
96
- file_path = Pathname.new(file)
97
- relative_path = file_path.relative_path_from(app.cache_path).to_s
98
- FileUtils.rm(file) unless names.include?(relative_path.chomp(".#{DependencyRecord::EXTENSION}"))
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
99
119
  end
100
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
101
131
  end
102
132
  end
103
133
  end
@@ -108,7 +108,12 @@ module Licensed
108
108
  def detect_cache_path(options, inherited_options)
109
109
  return options["cache_path"] unless options["cache_path"].to_s.empty?
110
110
 
111
- cache_path = inherited_options["cache_path"] || DEFAULT_CACHE_PATH
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
112
117
  File.join(cache_path, self["name"])
113
118
  end
114
119
 
@@ -167,7 +172,12 @@ module Licensed
167
172
  # will handle configurations that don't have these explicitly set
168
173
  dir_name = File.basename(path)
169
174
  config["name"] = "#{config["name"]}-#{dir_name}" if config["name"]
170
- config["cache_path"] = File.join(config["cache_path"], dir_name) if config["cache_path"]
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
171
181
 
172
182
  config
173
183
  end
@@ -47,6 +47,9 @@ module Licensed
47
47
 
48
48
  def initialize(shell = Licensed::UI::Shell.new)
49
49
  @shell = shell
50
+ @run_report = nil
51
+ @app_report = nil
52
+ @source_report = nil
50
53
  end
51
54
 
52
55
  # Generate a report for a licensed command execution
@@ -11,6 +11,7 @@ module Licensed
11
11
  require "licensed/sources/go"
12
12
  require "licensed/sources/manifest"
13
13
  require "licensed/sources/npm"
14
+ require "licensed/sources/nuget"
14
15
  require "licensed/sources/pip"
15
16
  require "licensed/sources/pipenv"
16
17
  require "licensed/sources/gradle"
@@ -125,7 +125,7 @@ module Licensed
125
125
  # find most recent git SHA for a package, or nil if SHA is
126
126
  # not available
127
127
  Dir.chdir package_directory do
128
- contents_version *contents_version_arguments
128
+ contents_version(*contents_version_arguments)
129
129
  end
130
130
  end
131
131
 
@@ -48,6 +48,7 @@ module Licensed
48
48
  # package name to it's metadata
49
49
  def recursive_dependencies(dependencies, result = {})
50
50
  dependencies.each do |name, dependency|
51
+ next if dependency["peerMissing"]
51
52
  next if yarn_lock_present && dependency["missing"]
52
53
  (result[name] ||= []) << dependency
53
54
  recursive_dependencies(dependency["dependencies"] || {}, result)
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+ require "json"
3
+ require "reverse_markdown"
4
+
5
+ module Licensed
6
+ module Sources
7
+ # Only supports ProjectReference (project.assets.json) style restore used in .NET Core.
8
+ # Does not currently support packages.config style restore.
9
+ class NuGet < Source
10
+ def self.type
11
+ "nuget"
12
+ end
13
+
14
+ class NuGetDependency < Licensed::Dependency
15
+ LICENSE_FILE_REGEX = /<license\s*type\s*=\s*\"\s*file\s*\"\s*>\s*(.*)\s*<\/license>/ix.freeze
16
+ LICENSE_URL_REGEX = /<licenseUrl>\s*(.*)\s*<\/licenseUrl>/ix.freeze
17
+ PROJECT_URL_REGEX = /<projectUrl>\s*(.*)\s*<\/projectUrl>/ix.freeze
18
+ PROJECT_DESC_REGEX = /<description>\s*(.*)\s*<\/description>/ix.freeze
19
+
20
+ def initialize(name:, version:, path:, search_root: nil, metadata: {}, errors: [])
21
+ super(name: name, version: version, path: path, search_root: search_root, metadata: metadata, errors: errors)
22
+ @metadata["homepage"] = project_url if project_url
23
+ @metadata["summary"] = description if description
24
+ end
25
+
26
+ def nuspec_path
27
+ name = @metadata["name"]
28
+ File.join(self.path, "#{name.downcase}.nuspec")
29
+ end
30
+
31
+ def nuspec_contents
32
+ return unless nuspec_path
33
+ @nuspec_contents ||= File.read(nuspec_path)
34
+ end
35
+
36
+ def project_url
37
+ return @project_url if defined?(@project_url)
38
+ return unless nuspec_contents
39
+ @project_url = begin
40
+ match = nuspec_contents.match PROJECT_URL_REGEX
41
+ match[1] if match && match[1]
42
+ end
43
+ end
44
+
45
+ def description
46
+ return @description if defined?(@description)
47
+ return unless nuspec_contents
48
+ @description = begin
49
+ match = nuspec_contents.match PROJECT_DESC_REGEX
50
+ match[1] if match && match[1]
51
+ end
52
+ end
53
+
54
+ def project_files
55
+ @nuget_project_files ||= begin
56
+ files = super().flatten.compact
57
+
58
+ # Only include the local file if it's a file licensee didn't already detect
59
+ nuspec_license_filename = File.basename(nuspec_local_license_file.filename) if nuspec_local_license_file
60
+ if nuspec_license_filename && files.none? { |file| File.basename(file.filename) == nuspec_license_filename }
61
+ files.push(nuspec_local_license_file)
62
+ end
63
+
64
+ # Only download licenseUrl if no recognized license was found locally
65
+ if files.none? { |file| file.license && file.license.key != "other" }
66
+ files.push(nuspec_remote_license_file)
67
+ end
68
+
69
+ files.compact
70
+ end
71
+ end
72
+
73
+ # Look for a <license type="file"> element in the nuspec that points to an
74
+ # on-disk license file (which licensee may not find due to a non-standard filename)
75
+ def nuspec_local_license_file
76
+ return @nuspec_local_license_file if defined?(@nuspec_local_license_file)
77
+ return unless nuspec_contents
78
+
79
+ match = nuspec_contents.match LICENSE_FILE_REGEX
80
+ return unless match && match[1]
81
+
82
+ license_path = File.join(File.dirname(nuspec_path), match[1])
83
+ return unless File.exist?(license_path)
84
+
85
+ license_data = File.read(license_path)
86
+ @nuspec_local_license_file = Licensee::ProjectFiles::LicenseFile.new(license_data, license_path)
87
+ end
88
+
89
+ # Look for a <licenseUrl> element in the nuspec that either is known to contain a license identifier
90
+ # in the URL, or points to license text on the internet that can be downloaded.
91
+ def nuspec_remote_license_file
92
+ return @nuspec_remote_license_file if defined?(@nuspec_remote_license_file)
93
+ return unless nuspec_contents
94
+
95
+ match = nuspec_contents.match LICENSE_URL_REGEX
96
+ return unless match && match[1]
97
+
98
+ # Attempt to fetch the license content
99
+ license_content = self.class.retrieve_license(match[1])
100
+ @nuspec_remote_license_file = Licensee::ProjectFiles::LicenseFile.new(license_content, { uri: match[1] }) if license_content
101
+ end
102
+
103
+ class << self
104
+ def strip_html(html)
105
+ return unless html
106
+
107
+ return html unless html.downcase.include?("<html")
108
+ ReverseMarkdown.convert(html, unknown_tags: :bypass)
109
+ end
110
+
111
+ def ignored_url?(url)
112
+ # Many Microsoft packages that now use <license> use this for <licenseUrl>
113
+ # No need to fetch this page - it just contains NuGet documentation
114
+ url == "https://aka.ms/deprecateLicenseUrl"
115
+ end
116
+
117
+ def text_content_url(url)
118
+ # Convert github file URLs to raw URLs
119
+ return url unless match = url.match(/https?:\/\/(?:www\.)?github.com\/([^\/]+)\/([^\/]+)\/blob\/(.*)/i)
120
+ "https://github.com/#{match[1]}/#{match[2]}/raw/#{match[3]}"
121
+ end
122
+
123
+ def retrieve_license(url)
124
+ return unless url
125
+ return if ignored_url?(url)
126
+
127
+ # Transform URLs that are known to return HTML but have a corresponding text-based URL
128
+ text_url = text_content_url(url)
129
+
130
+ raw_content = fetch_content(text_url)
131
+ strip_html(raw_content)
132
+ end
133
+
134
+ def fetch_content(url, redirect_limit = 5)
135
+ url = URI.parse(url) if url.instance_of? String
136
+ return @response_by_url[url] if (@response_by_url ||= {}).key?(url)
137
+ return if redirect_limit == 0
138
+
139
+ begin
140
+ response = Net::HTTP.get_response(url)
141
+ case response
142
+ when Net::HTTPSuccess then
143
+ @response_by_url[url] = response.body
144
+ when Net::HTTPRedirection then
145
+ redirect_url = URI.parse(response["location"])
146
+ if redirect_url.relative?
147
+ redirect_url = url + redirect_url
148
+ end
149
+ # The redirect might be to a URL that requires transformation, i.e. a github file
150
+ redirect_url = text_content_url(redirect_url.to_s)
151
+ @response_by_url[url] = fetch_content(redirect_url, redirect_limit - 1)
152
+ end
153
+ rescue
154
+ # Host might no longer exist or some other error, ignore
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ def project_assets_file_path
161
+ File.join(config.pwd, "project.assets.json")
162
+ end
163
+
164
+ def project_assets_file
165
+ return @project_assets_file if defined?(@project_assets_file)
166
+ @project_assets_file = File.read(project_assets_file_path)
167
+ end
168
+
169
+ def enabled?
170
+ File.exist?(project_assets_file_path)
171
+ end
172
+
173
+ # Inspect project.assets.json files for package references.
174
+ # Ideally we'd use `dotnet list package` instead, but its output isn't
175
+ # easily machine readable and doesn't contain everything we need.
176
+ def enumerate_dependencies
177
+ json = JSON.parse(project_assets_file)
178
+ nuget_packages_dir = json["project"]["restore"]["packagesPath"]
179
+ json["targets"].each_with_object({}) do |(_, target), dependencies|
180
+ target.each do |reference_key, reference|
181
+ # Ignore project references
182
+ next unless reference["type"] == "package"
183
+ package_id_parts = reference_key.partition("/")
184
+ name = package_id_parts[0]
185
+ version = package_id_parts[-1]
186
+ id = "#{name}-#{version}"
187
+
188
+ # Already know this package from another target
189
+ next if dependencies.key?(id)
190
+
191
+ path = File.join(nuget_packages_dir, json["libraries"][reference_key]["path"])
192
+ dependencies[id] = NuGetDependency.new(
193
+ name: id,
194
+ version: version,
195
+ path: path,
196
+ metadata: {
197
+ "type" => NuGet.type,
198
+ "name" => name
199
+ }
200
+ )
201
+ end
202
+ end.values
203
+ end
204
+ end
205
+ end
206
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Licensed
3
- VERSION = "2.9.2".freeze
3
+ VERSION = "2.10.0".freeze
4
4
 
5
5
  def self.previous_major_versions
6
6
  major_version = Gem::Version.new(Licensed::VERSION).segments.first
@@ -23,13 +23,14 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.required_ruby_version = ">= 2.3.0"
25
25
 
26
- spec.add_dependency "licensee", ">= 9.13.2", "< 10.0.0"
26
+ spec.add_dependency "licensee", ">= 9.14.0", "< 10.0.0"
27
27
  spec.add_dependency "thor", ">= 0.19"
28
28
  spec.add_dependency "pathname-common_prefix", "~> 0.0.1"
29
29
  spec.add_dependency "tomlrb", "~> 1.2"
30
30
  spec.add_dependency "bundler", ">= 1.10"
31
31
  spec.add_dependency "ruby-xxHash", "~> 0.4"
32
32
  spec.add_dependency "parallel", ">= 0.18.0"
33
+ spec.add_dependency "reverse_markdown", "~> 1.0"
33
34
 
34
35
  spec.add_development_dependency "rake", ">= 12.3.3"
35
36
  spec.add_development_dependency "minitest", "~> 5.8"
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ if [ -z "$(which dotnet)" ]; then
5
+ echo "A local dotnet installation is required for dotnet/nuget development." >&2
6
+ exit 127
7
+ fi
8
+
9
+ # setup test fixtures
10
+ BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
11
+ cd $BASE_PATH/test/fixtures/nuget
12
+
13
+ if [ "$1" == "-f" ]; then
14
+ dotnet clean
15
+ fi
16
+
17
+ dotnet restore
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.9.2
4
+ version: 2.10.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-04-29 00:00:00.000000000 Z
11
+ date: 2020-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: licensee
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 9.13.2
19
+ version: 9.14.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: 10.0.0
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 9.13.2
29
+ version: 9.14.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: 10.0.0
@@ -114,6 +114,20 @@ dependencies:
114
114
  - - ">="
115
115
  - !ruby/object:Gem::Version
116
116
  version: 0.18.0
117
+ - !ruby/object:Gem::Dependency
118
+ name: reverse_markdown
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.0'
124
+ type: :runtime
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '1.0'
117
131
  - !ruby/object:Gem::Dependency
118
132
  name: rake
119
133
  requirement: !ruby/object:Gem::Requirement
@@ -257,6 +271,7 @@ files:
257
271
  - docs/sources/manifests.md
258
272
  - docs/sources/mix.md
259
273
  - docs/sources/npm.md
274
+ - docs/sources/nuget.md
260
275
  - docs/sources/pip.md
261
276
  - docs/sources/pipenv.md
262
277
  - docs/sources/stack.md
@@ -297,6 +312,7 @@ files:
297
312
  - lib/licensed/sources/manifest.rb
298
313
  - lib/licensed/sources/mix.rb
299
314
  - lib/licensed/sources/npm.rb
315
+ - lib/licensed/sources/nuget.rb
300
316
  - lib/licensed/sources/pip.rb
301
317
  - lib/licensed/sources/pipenv.rb
302
318
  - lib/licensed/sources/source.rb
@@ -320,6 +336,7 @@ files:
320
336
  - script/source-setup/go
321
337
  - script/source-setup/mix
322
338
  - script/source-setup/npm
339
+ - script/source-setup/nuget
323
340
  - script/source-setup/pip
324
341
  - script/source-setup/pipenv
325
342
  - script/source-setup/yarn
@@ -343,8 +360,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
343
360
  - !ruby/object:Gem::Version
344
361
  version: '0'
345
362
  requirements: []
346
- rubyforge_project:
347
- rubygems_version: 2.6.8
363
+ rubygems_version: 3.0.3
348
364
  signing_key:
349
365
  specification_version: 4
350
366
  summary: Extract and validate the licenses of dependencies.