licensed 2.9.2 → 2.10.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
- 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.