spandx 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -2
- data/lib/spandx/cli/command.rb +65 -0
- data/lib/spandx/cli/commands/index/build.rb +31 -0
- data/lib/spandx/cli/commands/index/update.rb +26 -0
- data/lib/spandx/cli/commands/index.rb +36 -0
- data/lib/spandx/cli/commands/scan.rb +28 -0
- data/lib/spandx/cli.rb +5 -18
- data/lib/spandx/core/content.rb +64 -0
- data/lib/spandx/core/database.rb +65 -0
- data/lib/spandx/core/dependency.rb +23 -0
- data/lib/spandx/core/guess.rb +51 -0
- data/lib/spandx/{parsers/base.rb → core/parser.rb} +16 -2
- data/lib/spandx/core/report.rb +23 -0
- data/lib/spandx/core/score.rb +32 -0
- data/lib/spandx/dotnet/index.rb +72 -0
- data/lib/spandx/{gateways/nuget.rb → dotnet/nuget_gateway.rb} +7 -29
- data/lib/spandx/dotnet/package_reference.rb +21 -0
- data/lib/spandx/dotnet/parsers/csproj.rb +40 -0
- data/lib/spandx/dotnet/parsers/packages_config.rb +39 -0
- data/lib/spandx/dotnet/parsers/sln.rb +47 -0
- data/lib/spandx/dotnet/project_file.rb +50 -0
- data/lib/spandx/java/metadata.rb +47 -0
- data/lib/spandx/java/parsers/maven.rb +44 -0
- data/lib/spandx/parsers/pipfile_lock.rb +2 -2
- data/lib/spandx/{gateways/rubygems.rb → rubygems/gateway.rb} +4 -4
- data/lib/spandx/rubygems/offline_index.rb +72 -0
- data/lib/spandx/rubygems/parsers/gemfile_lock.rb +45 -0
- data/lib/spandx/spdx/catalogue.rb +69 -0
- data/lib/spandx/{gateways/spdx.rb → spdx/gateway.rb} +2 -2
- data/lib/spandx/spdx/license.rb +81 -0
- data/lib/spandx/version.rb +1 -1
- data/lib/spandx.rb +27 -14
- data/spandx.gemspec +2 -2
- metadata +45 -40
- data/lib/spandx/catalogue.rb +0 -67
- data/lib/spandx/command.rb +0 -119
- data/lib/spandx/commands/build.rb +0 -33
- data/lib/spandx/commands/scan.rb +0 -26
- data/lib/spandx/content.rb +0 -62
- data/lib/spandx/database.rb +0 -49
- data/lib/spandx/dependency.rb +0 -21
- data/lib/spandx/guess.rb +0 -76
- data/lib/spandx/index.rb +0 -49
- data/lib/spandx/license.rb +0 -79
- data/lib/spandx/parsers/csproj/package_reference.rb +0 -23
- data/lib/spandx/parsers/csproj/project_file.rb +0 -52
- data/lib/spandx/parsers/csproj.rb +0 -40
- data/lib/spandx/parsers/gemfile_lock.rb +0 -43
- data/lib/spandx/parsers/maven.rb +0 -85
- data/lib/spandx/parsers/packages_config.rb +0 -37
- data/lib/spandx/parsers/sln.rb +0 -45
- data/lib/spandx/parsers.rb +0 -29
- data/lib/spandx/report.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b3a0275ae468012967376aa3bd813b3dc1efef7783eb11d0151d7babf4ac6a8
|
4
|
+
data.tar.gz: b86bd82707ad5afa0b83c59674e4f0d08c12733d5f1788038845f30079bb8ba6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f71dd4bf8438ec8857f622076ea345f3c48c8dce3354949459b564ace2c0c002bcc95d09d9d906ba9063bdeb092ade90160f51bdcfb9900a0225114d1aa5aa80
|
7
|
+
data.tar.gz: e0bbbdd0156924ac21d9730aa322b332f4d3c1ef43b530f58c56f411c57bfb95c154574bda77c61dda3fbd80b9c8fc9e8a2fea4c09053fa6bfda2c5a6ac86dc5
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Version 0.
|
1
|
+
Version 0.6.0
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
@@ -9,6 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
9
9
|
|
10
10
|
## [Unreleased]
|
11
11
|
|
12
|
+
## [0.6.0] - 2020-03-03
|
13
|
+
### Added
|
14
|
+
- Add `spandx index update` command to fetch the latest `spandx-rubygems` index.
|
15
|
+
|
16
|
+
### Removed
|
17
|
+
- Drop `spandx-rubygems` dependency.
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
- Pull latest `spandx-rubygems` index via git.
|
21
|
+
- Perform binary search on CSV index.
|
22
|
+
|
12
23
|
## [0.5.0] - 2020-02-13
|
13
24
|
### Added
|
14
25
|
- Add jaro winkler string similarity support.
|
@@ -73,7 +84,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
73
84
|
### Added
|
74
85
|
- Provide ruby API to the latest SPDX catalogue.
|
75
86
|
|
76
|
-
[Unreleased]: https://github.com/mokhan/spandx/compare/v0.
|
87
|
+
[Unreleased]: https://github.com/mokhan/spandx/compare/v0.6.0...HEAD
|
88
|
+
[0.6.0]: https://github.com/mokhan/spandx/compare/v0.5.0...v0.6.0
|
77
89
|
[0.5.0]: https://github.com/mokhan/spandx/compare/v0.4.1...v0.5.0
|
78
90
|
[0.4.1]: https://github.com/mokhan/spandx/compare/v0.4.0...v0.4.1
|
79
91
|
[0.4.0]: https://github.com/mokhan/spandx/compare/v0.3.0...v0.4.0
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Cli
|
5
|
+
class Command
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :command, :run
|
9
|
+
|
10
|
+
def execute(*)
|
11
|
+
raise(NotImplementedError, "#{self.class}##{__method__} must be implemented")
|
12
|
+
end
|
13
|
+
|
14
|
+
def command(**options)
|
15
|
+
require 'tty-command'
|
16
|
+
TTY::Command.new(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def cursor
|
20
|
+
require 'tty-cursor'
|
21
|
+
TTY::Cursor
|
22
|
+
end
|
23
|
+
|
24
|
+
def editor
|
25
|
+
require 'tty-editor'
|
26
|
+
TTY::Editor
|
27
|
+
end
|
28
|
+
|
29
|
+
def generator
|
30
|
+
require 'tty-file'
|
31
|
+
TTY::File
|
32
|
+
end
|
33
|
+
|
34
|
+
def pager(**options)
|
35
|
+
require 'tty-pager'
|
36
|
+
TTY::Pager.new(options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def platform
|
40
|
+
require 'tty-platform'
|
41
|
+
TTY::Platform.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def prompt(**options)
|
45
|
+
require 'tty-prompt'
|
46
|
+
TTY::Prompt.new(options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def screen
|
50
|
+
require 'tty-screen'
|
51
|
+
TTY::Screen
|
52
|
+
end
|
53
|
+
|
54
|
+
def which(*args)
|
55
|
+
require 'tty-which'
|
56
|
+
TTY::Which.which(*args)
|
57
|
+
end
|
58
|
+
|
59
|
+
def exec_exist?(*args)
|
60
|
+
require 'tty-which'
|
61
|
+
TTY::Which.exist?(*args)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Cli
|
5
|
+
module Commands
|
6
|
+
class Index
|
7
|
+
class Build < Spandx::Cli::Command
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(output: $stdout)
|
13
|
+
catalogue = Spandx::Spdx::Catalogue.from_git
|
14
|
+
indexes.each do |index|
|
15
|
+
index.update!(catalogue: catalogue)
|
16
|
+
end
|
17
|
+
output.puts 'OK'
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def indexes
|
23
|
+
[
|
24
|
+
Spandx::Dotnet::Index.new(directory: @options[:directory])
|
25
|
+
]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Cli
|
5
|
+
module Commands
|
6
|
+
class Index
|
7
|
+
class Update < Spandx::Cli::Command
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(output: $stdout)
|
13
|
+
[
|
14
|
+
'https://github.com/mokhan/spandx-rubygems.git',
|
15
|
+
'https://github.com/spdx/license-list-data.git',
|
16
|
+
].each do |url|
|
17
|
+
output.puts "Updating #{url}..."
|
18
|
+
Spandx::Core::Database.new(url: url).update!
|
19
|
+
end
|
20
|
+
output.puts 'OK'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Cli
|
5
|
+
module Commands
|
6
|
+
class Index < Thor
|
7
|
+
require 'spandx/cli/commands/index/build'
|
8
|
+
require 'spandx/cli/commands/index/update'
|
9
|
+
|
10
|
+
namespace :index
|
11
|
+
|
12
|
+
desc 'build', 'Build a package index'
|
13
|
+
method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
|
14
|
+
method_option :directory, aliases: '-d', type: :string, desc: 'Directory to build index in', default: '.index'
|
15
|
+
def build(*)
|
16
|
+
if options[:help]
|
17
|
+
invoke :help, ['build']
|
18
|
+
else
|
19
|
+
Spandx::Cli::Commands::Index::Build.new(options).execute
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'update', 'Update the offline indexes'
|
24
|
+
method_option :help, aliases: '-h', type: :boolean,
|
25
|
+
desc: 'Display usage information'
|
26
|
+
def update(*)
|
27
|
+
if options[:help]
|
28
|
+
invoke :help, ['update']
|
29
|
+
else
|
30
|
+
Spandx::Cli::Commands::Index::Update.new(options).execute
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Cli
|
5
|
+
module Commands
|
6
|
+
class Scan < Spandx::Cli::Command
|
7
|
+
attr_reader :lockfile
|
8
|
+
|
9
|
+
def initialize(lockfile, options)
|
10
|
+
@lockfile = lockfile ? ::Pathname.new(File.expand_path(lockfile)) : nil
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(output: $stdout)
|
15
|
+
if lockfile.nil?
|
16
|
+
output.puts 'OK'
|
17
|
+
else
|
18
|
+
report = ::Spandx::Core::Report.new
|
19
|
+
::Spandx::Core::Parser.for(lockfile).parse(lockfile).each do |dependency|
|
20
|
+
report.add(dependency)
|
21
|
+
end
|
22
|
+
output.puts report.to_json
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/spandx/cli.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'thor'
|
4
|
-
|
5
4
|
require 'spandx'
|
6
|
-
require 'spandx/command'
|
7
|
-
require 'spandx/commands/
|
8
|
-
require 'spandx/commands/scan'
|
5
|
+
require 'spandx/cli/command'
|
6
|
+
require 'spandx/cli/commands/index'
|
7
|
+
require 'spandx/cli/commands/scan'
|
9
8
|
|
10
9
|
module Spandx
|
11
10
|
class CLI < Thor
|
@@ -17,19 +16,7 @@ module Spandx
|
|
17
16
|
end
|
18
17
|
map %w[--version -v] => :version
|
19
18
|
|
20
|
-
|
21
|
-
method_option :help, aliases: '-h', type: :boolean,
|
22
|
-
desc: 'Display usage information'
|
23
|
-
method_option :directory, aliases: '-d', type: :string,
|
24
|
-
desc: 'Directory to build index in'
|
25
|
-
def build(*)
|
26
|
-
if options[:help]
|
27
|
-
invoke :help, ['build']
|
28
|
-
else
|
29
|
-
require_relative 'commands/build'
|
30
|
-
Spandx::Commands::Build.new(options).execute
|
31
|
-
end
|
32
|
-
end
|
19
|
+
register Spandx::Cli::Commands::Index, 'index', 'index [SUBCOMMAND]', 'Command description...'
|
33
20
|
|
34
21
|
desc 'scan LOCKFILE', 'Scan a lockfile and list dependencies/licenses'
|
35
22
|
method_option :help, aliases: '-h', type: :boolean,
|
@@ -38,7 +25,7 @@ module Spandx
|
|
38
25
|
if options[:help]
|
39
26
|
invoke :help, ['scan']
|
40
27
|
else
|
41
|
-
Spandx::Commands::Scan.new(lockfile, options).execute
|
28
|
+
Spandx::Cli::Commands::Scan.new(lockfile, options).execute
|
42
29
|
end
|
43
30
|
end
|
44
31
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Core
|
5
|
+
class Content
|
6
|
+
attr_reader :raw
|
7
|
+
|
8
|
+
def initialize(raw)
|
9
|
+
@raw = raw
|
10
|
+
end
|
11
|
+
|
12
|
+
def tokens
|
13
|
+
@tokens ||= tokenize(canonicalize(raw)).to_set
|
14
|
+
end
|
15
|
+
|
16
|
+
def similar?(other, algorithm: :dice_coefficient)
|
17
|
+
case algorithm
|
18
|
+
when :dice_coefficient
|
19
|
+
similarity_score(other, algorithm: algorithm) > 89.0
|
20
|
+
when :levenshtein
|
21
|
+
similarity_score(other, algorithm: algorithm) < 3
|
22
|
+
when :jaro_winkler
|
23
|
+
similarity_score(other, algorithm: algorithm) > 89.0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def similarity_score(other, algorithm: :dice_coefficient)
|
28
|
+
case algorithm
|
29
|
+
when :dice_coefficient
|
30
|
+
dice_coefficient(other)
|
31
|
+
when :levenshtein
|
32
|
+
require 'text'
|
33
|
+
|
34
|
+
Text::Levenshtein.distance(raw, other.raw, 100)
|
35
|
+
when :jaro_winkler
|
36
|
+
require 'jaro_winkler'
|
37
|
+
|
38
|
+
JaroWinkler.distance(raw, other.raw) * 100.0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def canonicalize(content)
|
45
|
+
content&.downcase
|
46
|
+
end
|
47
|
+
|
48
|
+
def tokenize(content)
|
49
|
+
content.to_s.scan(/[a-zA-Z]+/)
|
50
|
+
end
|
51
|
+
|
52
|
+
def blank?(content)
|
53
|
+
content.nil? || content.chomp.strip.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
# https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Dice%27s_coefficient#Ruby
|
57
|
+
def dice_coefficient(other)
|
58
|
+
overlap = (tokens & other.tokens).size
|
59
|
+
total = tokens.size + other.tokens.size
|
60
|
+
100.0 * (overlap * 2.0 / total)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Core
|
5
|
+
class Database
|
6
|
+
attr_reader :path, :url
|
7
|
+
|
8
|
+
def initialize(url:)
|
9
|
+
@url = url
|
10
|
+
@path = path_for(url)
|
11
|
+
end
|
12
|
+
|
13
|
+
def update!
|
14
|
+
dotgit? ? pull! : clone!
|
15
|
+
end
|
16
|
+
|
17
|
+
def expand_path(relative_path)
|
18
|
+
File.join(path, relative_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def read(path)
|
22
|
+
update! unless dotgit?
|
23
|
+
|
24
|
+
full_path = expand_path(path)
|
25
|
+
IO.read(full_path) if File.exist?(full_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def open(path, mode: 'r')
|
29
|
+
update! unless dotgit?
|
30
|
+
|
31
|
+
File.open(expand_path(path), mode) do |io|
|
32
|
+
yield io
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def path_for(url)
|
39
|
+
uri = URI.parse(url)
|
40
|
+
name = uri.path.gsub(/\.git$/, '')
|
41
|
+
File.expand_path(File.join(Dir.home, '.local', 'share', name))
|
42
|
+
end
|
43
|
+
|
44
|
+
def dotgit?
|
45
|
+
File.directory?(File.join(path, '.git'))
|
46
|
+
end
|
47
|
+
|
48
|
+
def clone!
|
49
|
+
system('git', 'clone', '--quiet', url, path)
|
50
|
+
end
|
51
|
+
|
52
|
+
def pull!
|
53
|
+
within do
|
54
|
+
system('git', 'pull', '--no-rebase', '--quiet', 'origin', 'master')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def within
|
59
|
+
Dir.chdir(path) do
|
60
|
+
yield
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Core
|
5
|
+
class Dependency
|
6
|
+
attr_reader :name, :version, :licenses
|
7
|
+
|
8
|
+
def initialize(name:, version:, licenses: [])
|
9
|
+
@name = name
|
10
|
+
@version = version
|
11
|
+
@licenses = licenses
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_h
|
15
|
+
{
|
16
|
+
name: name,
|
17
|
+
version: version,
|
18
|
+
licenses: licenses.compact.map(&:id)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Core
|
5
|
+
class Guess
|
6
|
+
attr_reader :catalogue
|
7
|
+
|
8
|
+
def initialize(catalogue)
|
9
|
+
@catalogue = catalogue
|
10
|
+
end
|
11
|
+
|
12
|
+
def license_for(raw_content, algorithm: :dice_coefficient)
|
13
|
+
content = Content.new(raw_content)
|
14
|
+
score = Score.new(nil, nil)
|
15
|
+
threshold = threshold_for(algorithm)
|
16
|
+
direction = algorithm == :levenshtein ? method(:min) : method(:max)
|
17
|
+
|
18
|
+
catalogue.each do |license|
|
19
|
+
direction.call(content, license, score, threshold, algorithm) unless license.deprecated_license_id?
|
20
|
+
end
|
21
|
+
score&.item&.id
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def threshold_for(algorithm)
|
27
|
+
{
|
28
|
+
dice_coefficient: 89.0,
|
29
|
+
jaro_winkler: 80.0,
|
30
|
+
levenshtein: 80.0,
|
31
|
+
}[algorithm.to_sym]
|
32
|
+
end
|
33
|
+
|
34
|
+
def min(target, other, score, threshold, algorithm)
|
35
|
+
percentage = target.similarity_score(other.content, algorithm: algorithm)
|
36
|
+
return if percentage > threshold
|
37
|
+
return if score.score > 0.0 && score.score < percentage
|
38
|
+
|
39
|
+
score.update(percentage, other)
|
40
|
+
end
|
41
|
+
|
42
|
+
def max(target, other, score, threshold, algorithm)
|
43
|
+
percentage = target.similarity_score(other.content, algorithm: algorithm)
|
44
|
+
return if percentage < threshold
|
45
|
+
return if score.score >= percentage
|
46
|
+
|
47
|
+
score.update(percentage, other)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,8 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Spandx
|
4
|
-
module
|
5
|
-
class
|
4
|
+
module Core
|
5
|
+
class Parser
|
6
|
+
UNKNOWN = Class.new do
|
7
|
+
def self.parse(*_args)
|
8
|
+
[]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
6
12
|
attr_reader :catalogue
|
7
13
|
|
8
14
|
def initialize(catalogue:)
|
@@ -25,6 +31,14 @@ module Spandx
|
|
25
31
|
def registry
|
26
32
|
@registry ||= []
|
27
33
|
end
|
34
|
+
|
35
|
+
def for(path, catalogue: Spandx::Spdx::Catalogue.from_git)
|
36
|
+
result = ::Spandx::Core::Parser.find do |x|
|
37
|
+
x.matches?(File.basename(path))
|
38
|
+
end
|
39
|
+
|
40
|
+
result&.new(catalogue: catalogue) || UNKNOWN
|
41
|
+
end
|
28
42
|
end
|
29
43
|
end
|
30
44
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Core
|
5
|
+
class Report
|
6
|
+
def initialize(report: { version: '1.0', packages: [] })
|
7
|
+
@report = report
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(dependency)
|
11
|
+
@report[:packages].push(dependency.to_h)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_h
|
15
|
+
@report
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_json(*_args)
|
19
|
+
JSON.pretty_generate(to_h)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Core
|
5
|
+
class Score
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
attr_reader :score, :item
|
9
|
+
|
10
|
+
def initialize(score, item)
|
11
|
+
update(score || 0.0, item)
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(score, item)
|
15
|
+
@score = score
|
16
|
+
@item = item
|
17
|
+
end
|
18
|
+
|
19
|
+
def empty?
|
20
|
+
score.nil? || item.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def <=>(other)
|
24
|
+
score <=> other.score
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"#{score}: #{item}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spandx
|
4
|
+
module Dotnet
|
5
|
+
class Index
|
6
|
+
DEFAULT_DIR = File.expand_path(File.join(Dir.home, '.local', 'share', 'spandx'))
|
7
|
+
attr_reader :directory
|
8
|
+
|
9
|
+
def initialize(directory: DEFAULT_DIR)
|
10
|
+
@directory = directory ? File.expand_path(directory) : DEFAULT_DIR
|
11
|
+
end
|
12
|
+
|
13
|
+
def update!(catalogue:, limit: nil)
|
14
|
+
counter = 0
|
15
|
+
gateway = Spandx::Dotnet::NugetGateway.new(catalogue: catalogue)
|
16
|
+
gateway.each do |spec|
|
17
|
+
next unless spec['licenseExpression']
|
18
|
+
|
19
|
+
write([gateway.host, spec['id'], spec['version']], spec['licenseExpression'])
|
20
|
+
|
21
|
+
if limit
|
22
|
+
counter += 1
|
23
|
+
break if counter > limit
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def indexed?(key)
|
29
|
+
File.exist?(data_file_for(digest_for(key)))
|
30
|
+
end
|
31
|
+
|
32
|
+
def read(key)
|
33
|
+
open_data(digest_for(key), mode: 'r', &:read)
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(key, data)
|
37
|
+
return if data.nil? || data.empty?
|
38
|
+
|
39
|
+
open_data(digest_for(key)) do |x|
|
40
|
+
x.write(data)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def digest_for(components)
|
47
|
+
Digest::SHA1.hexdigest(Array(components).join('/'))
|
48
|
+
end
|
49
|
+
|
50
|
+
def open_data(key, mode: 'w')
|
51
|
+
FileUtils.mkdir_p(data_dir_for(key))
|
52
|
+
File.open(data_file_for(key), mode) do |file|
|
53
|
+
yield file
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def data_dir_for(index_key)
|
58
|
+
File.join(directory, *index_key.scan(/../)).downcase
|
59
|
+
end
|
60
|
+
|
61
|
+
def data_file_for(key)
|
62
|
+
File.join(data_dir_for(key), 'data')
|
63
|
+
end
|
64
|
+
|
65
|
+
def upsert!(spec)
|
66
|
+
return unless spec['licenseExpression']
|
67
|
+
|
68
|
+
write([host, spec['id'], spec['version']], spec['licenseExpression'])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|