mutant 0.11.34 → 0.12.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 +4 -4
- data/lib/mutant/cli/command/environment/run.rb +6 -9
- data/lib/mutant/cli/command/environment.rb +15 -0
- data/lib/mutant/cli/command/root.rb +1 -1
- data/lib/mutant/cli/command.rb +1 -9
- data/lib/mutant/config.rb +8 -2
- data/lib/mutant/reporter/cli/printer/config.rb +1 -0
- data/lib/mutant/repository/diff.rb +7 -9
- data/lib/mutant/usage.rb +109 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +14 -8
- data/lib/mutant.rb +3 -7
- metadata +4 -9
- data/lib/mutant/cli/command/subscription.rb +0 -54
- data/lib/mutant/license/subscription/commercial.rb +0 -88
- data/lib/mutant/license/subscription/opensource.rb +0 -30
- data/lib/mutant/license/subscription/repository.rb +0 -72
- data/lib/mutant/license/subscription.rb +0 -69
- data/lib/mutant/license.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e8ba758de42528dd4dd4fdb5686df439d9ea4e2b1635905e89d7a98b904b959
|
4
|
+
data.tar.gz: df2317b0cabc5964fec52e0fc1d7e76c0f368a01f713fa411f03cba4808b834a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b74495aa82f4489e22a95b5c25f3185d759fa616354af51c07544bb66be08f69becf8e5021db80008d169c2de12e677bf8a3a38becb954c32dc8287f1eea24d
|
7
|
+
data.tar.gz: 82e7bdc18591e89028a39a3489291452b0b73cc4bcc6ea7549e9fa376778de622ac53bd4a2dbbe7702ff875eb9d84f55b6fa1ad424e55bfb0e4c7bdaa7243d13
|
@@ -9,13 +9,6 @@ module Mutant
|
|
9
9
|
SHORT_DESCRIPTION = 'Run code analysis'
|
10
10
|
SUBCOMMANDS = EMPTY_ARRAY
|
11
11
|
|
12
|
-
UNLICENSED = <<~MESSAGE.lines.freeze
|
13
|
-
You are using mutant unlicensed.
|
14
|
-
|
15
|
-
See https://github.com/mbj/mutant#licensing to aquire a license.
|
16
|
-
Note: Its free for opensource use, which is recommended for trials.
|
17
|
-
MESSAGE
|
18
|
-
|
19
12
|
NO_TESTS_MESSAGE = <<~'MESSAGE'
|
20
13
|
===============
|
21
14
|
Mutant found no tests available for mutation testing.
|
@@ -34,8 +27,8 @@ module Mutant
|
|
34
27
|
private
|
35
28
|
|
36
29
|
def action
|
37
|
-
|
38
|
-
.bind
|
30
|
+
bootstrap
|
31
|
+
.bind(&method(:verify_usage))
|
39
32
|
.bind(&method(:validate_tests))
|
40
33
|
.bind(&Mutation::Runner.public_method(:call))
|
41
34
|
.bind(&method(:from_result))
|
@@ -56,6 +49,10 @@ module Mutant
|
|
56
49
|
Either::Left.new('Uncovered mutations detected, exiting nonzero!')
|
57
50
|
end
|
58
51
|
end
|
52
|
+
|
53
|
+
def verify_usage(environment)
|
54
|
+
environment.config.usage.verify.fmap { environment }
|
55
|
+
end
|
59
56
|
end # Run
|
60
57
|
end # Environment
|
61
58
|
end # Command
|
@@ -15,6 +15,7 @@ module Mutant
|
|
15
15
|
add_integration_options
|
16
16
|
add_matcher_options
|
17
17
|
add_reporter_options
|
18
|
+
add_usage_options
|
18
19
|
].freeze
|
19
20
|
|
20
21
|
private
|
@@ -136,6 +137,20 @@ module Mutant
|
|
136
137
|
set(reporter: @config.reporter.with(print_warnings: true))
|
137
138
|
end
|
138
139
|
end
|
140
|
+
|
141
|
+
def add_usage_options(parser)
|
142
|
+
parser.separator('Usage:')
|
143
|
+
|
144
|
+
parser.accept(Usage, Usage::CLI_REGEXP) do |value|
|
145
|
+
Usage.parse(value).from_right
|
146
|
+
end
|
147
|
+
|
148
|
+
parser.on(
|
149
|
+
'--usage USAGE_TYPE',
|
150
|
+
Usage,
|
151
|
+
'License usage: opensource|commercial'
|
152
|
+
) { |usage| set(usage: usage) }
|
153
|
+
end
|
139
154
|
end # Run
|
140
155
|
# rubocop:enable Metrics/ClassLength
|
141
156
|
end # Command
|
@@ -10,7 +10,7 @@ module Mutant
|
|
10
10
|
class Root < self
|
11
11
|
NAME = 'mutant'
|
12
12
|
SHORT_DESCRIPTION = 'mutation testing engine main command'
|
13
|
-
SUBCOMMANDS = [Environment::Run, Environment,
|
13
|
+
SUBCOMMANDS = [Environment::Run, Environment, Util].freeze
|
14
14
|
end # Root
|
15
15
|
end # Command
|
16
16
|
end # CLI
|
data/lib/mutant/cli/command.rb
CHANGED
@@ -122,7 +122,7 @@ module Mutant
|
|
122
122
|
|
123
123
|
def parse(arguments)
|
124
124
|
Either
|
125
|
-
.wrap_error(OptionParser::InvalidOption) { parser.order(arguments) }
|
125
|
+
.wrap_error(OptionParser::InvalidArgument, OptionParser::InvalidOption) { parser.order(arguments) }
|
126
126
|
.lmap(&method(:with_help))
|
127
127
|
.bind(&method(:parse_remaining))
|
128
128
|
end
|
@@ -176,14 +176,6 @@ module Mutant
|
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
179
|
-
def parse_remaining_arguments(remaining)
|
180
|
-
if remaining.any?
|
181
|
-
Either::Left.new("#{full_name}: Does not expect extra arguments")
|
182
|
-
else
|
183
|
-
Either::Right.new(self)
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
179
|
def parse_subcommand(arguments)
|
188
180
|
command_name, *arguments = arguments
|
189
181
|
|
data/lib/mutant/config.rb
CHANGED
@@ -21,7 +21,8 @@ module Mutant
|
|
21
21
|
:matcher,
|
22
22
|
:mutation,
|
23
23
|
:reporter,
|
24
|
-
:requires
|
24
|
+
:requires,
|
25
|
+
:usage
|
25
26
|
)
|
26
27
|
|
27
28
|
%i[fail_fast].each do |name|
|
@@ -87,7 +88,8 @@ module Mutant
|
|
87
88
|
jobs: other.jobs || jobs,
|
88
89
|
matcher: matcher.merge(other.matcher),
|
89
90
|
mutation: mutation.merge(other.mutation),
|
90
|
-
requires: requires + other.requires
|
91
|
+
requires: requires + other.requires,
|
92
|
+
usage: other.usage.merge(usage)
|
91
93
|
)
|
92
94
|
end
|
93
95
|
# rubocop:enable Metrics/AbcSize
|
@@ -247,6 +249,10 @@ module Mutant
|
|
247
249
|
Transform::Hash::Key.new(
|
248
250
|
transform: Transform::STRING_ARRAY,
|
249
251
|
value: 'requires'
|
252
|
+
),
|
253
|
+
Transform::Hash::Key.new(
|
254
|
+
transform: Usage::TRANSFORM,
|
255
|
+
value: 'usage'
|
250
256
|
)
|
251
257
|
],
|
252
258
|
required: []
|
@@ -36,9 +36,8 @@ module Mutant
|
|
36
36
|
|
37
37
|
def repository_root
|
38
38
|
world
|
39
|
-
.
|
40
|
-
.fmap(
|
41
|
-
.fmap(&world.pathname.public_method(:new))
|
39
|
+
.capture_command(%w[git rev-parse --show-toplevel])
|
40
|
+
.fmap { |status| world.pathname.new(status.stdout.chomp) }
|
42
41
|
end
|
43
42
|
|
44
43
|
def touched_path(path, &block)
|
@@ -52,11 +51,10 @@ module Mutant
|
|
52
51
|
|
53
52
|
def diff_index(root)
|
54
53
|
world
|
55
|
-
.
|
56
|
-
.
|
57
|
-
.bind do |lines|
|
54
|
+
.capture_command(%W[git diff-index #{to}])
|
55
|
+
.bind do |status|
|
58
56
|
Mutant
|
59
|
-
.traverse(->(line) { parse_line(root, line) }, lines)
|
57
|
+
.traverse(->(line) { parse_line(root, line) }, status.stdout.lines)
|
60
58
|
.fmap do |paths|
|
61
59
|
paths.to_h { |path| [path.path, path] }
|
62
60
|
end
|
@@ -105,8 +103,8 @@ module Mutant
|
|
105
103
|
|
106
104
|
def diff_ranges
|
107
105
|
world
|
108
|
-
.
|
109
|
-
.fmap
|
106
|
+
.capture_command(%W[git diff --unified=0 #{to} -- #{path}])
|
107
|
+
.fmap { |status| Ranges.parse(status.stdout) }
|
110
108
|
.from_right
|
111
109
|
end
|
112
110
|
memoize :diff_ranges
|
data/lib/mutant/usage.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Usage
|
5
|
+
include Adamantium, Equalizer.new
|
6
|
+
|
7
|
+
def value
|
8
|
+
self.class::VALUE
|
9
|
+
end
|
10
|
+
|
11
|
+
def verify
|
12
|
+
Either::Right.new(nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def message
|
16
|
+
self.class::MESSAGE
|
17
|
+
end
|
18
|
+
|
19
|
+
def merge(_other)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
class Commercial < self
|
24
|
+
VALUE = 'commercial'
|
25
|
+
|
26
|
+
MESSAGE = <<~'MESSAGE'
|
27
|
+
## Commercial use
|
28
|
+
|
29
|
+
`commercial` usage type requires [payment](https://github.com/mbj/mutant?tab=readme-ov-file#pricing),
|
30
|
+
If you are under an active payment plan you can use the commercial usage type on any
|
31
|
+
repository, including private ones.
|
32
|
+
|
33
|
+
To use `commercial` usage type either specify `--usage commercial` on the command
|
34
|
+
line or use the config file key `usage`:
|
35
|
+
|
36
|
+
```
|
37
|
+
# mutant.yml or config/mutant.yml
|
38
|
+
usage: commercial
|
39
|
+
```
|
40
|
+
MESSAGE
|
41
|
+
end
|
42
|
+
|
43
|
+
class Opensource < self
|
44
|
+
VALUE = 'opensource'
|
45
|
+
|
46
|
+
MESSAGE = <<~'MESSAGE'
|
47
|
+
## Opensource use
|
48
|
+
|
49
|
+
`opensource` usage is free while mutant is run on an opensource project.
|
50
|
+
Under that usage mutant does not require any kind of sign up or payment.
|
51
|
+
Set this usage type exclusively on public opensource projects. Any other
|
52
|
+
scenario requires payment.
|
53
|
+
Using the `opensource` usage type on private repotiories and or on commercial
|
54
|
+
code bases is not valid.
|
55
|
+
|
56
|
+
To use `opensource` usage type either specify `--usage opensource` on the command
|
57
|
+
line or use the config file key `usage`:
|
58
|
+
|
59
|
+
```
|
60
|
+
# mutant.yml or config/mutant.yml
|
61
|
+
usage: opensource
|
62
|
+
```
|
63
|
+
MESSAGE
|
64
|
+
end
|
65
|
+
|
66
|
+
class Unknown < self
|
67
|
+
VALUE = 'unknown'
|
68
|
+
|
69
|
+
MESSAGE = <<~"MESSAGE".freeze
|
70
|
+
# Unknown mutant usage type
|
71
|
+
|
72
|
+
Mutant license usage is unspecified. Valid usage types are `opensource` or `commercial`.
|
73
|
+
|
74
|
+
Usage can be specified via the `--usage` command line parameter or via the
|
75
|
+
config file under the `usage` key.
|
76
|
+
|
77
|
+
#{Commercial::MESSAGE}
|
78
|
+
#{Opensource::MESSAGE}
|
79
|
+
This is a breaking change for users of the 0.11.x / 0.10.x mutant releases.
|
80
|
+
Sorry for that but its going to make future adoption much easier.
|
81
|
+
License gem is gone entirely.
|
82
|
+
MESSAGE
|
83
|
+
|
84
|
+
def merge(other)
|
85
|
+
other
|
86
|
+
end
|
87
|
+
|
88
|
+
def verify
|
89
|
+
Either::Left.new(MESSAGE)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.parse(value)
|
94
|
+
{
|
95
|
+
'commercial' => Either::Right.new(Commercial.new),
|
96
|
+
'opensource' => Either::Right.new(Opensource.new)
|
97
|
+
}.fetch(value) { Either::Left.new("Unknown usage option: #{value.inspect}") }
|
98
|
+
end
|
99
|
+
|
100
|
+
CLI_REGEXP = /\A(?:commercial|opensource)\z/
|
101
|
+
|
102
|
+
TRANSFORM = Transform::Sequence.new(
|
103
|
+
steps: [
|
104
|
+
Transform::STRING,
|
105
|
+
Transform::Block.capture(:environment_variables, &method(:parse))
|
106
|
+
]
|
107
|
+
)
|
108
|
+
end # Usage
|
109
|
+
end # Mutant
|
data/lib/mutant/version.rb
CHANGED
data/lib/mutant/world.rb
CHANGED
@@ -39,19 +39,25 @@ module Mutant
|
|
39
39
|
INSPECT
|
40
40
|
end
|
41
41
|
|
42
|
+
class CommandStatus
|
43
|
+
include Adamantium, Anima.new(:process_status, :stderr, :stdout)
|
44
|
+
end # CommandStatus
|
45
|
+
|
42
46
|
# Capture stdout of a command
|
43
47
|
#
|
44
48
|
# @param [Array<String>] command
|
45
49
|
#
|
46
|
-
# @return [Either<
|
47
|
-
def
|
48
|
-
stdout,
|
50
|
+
# @return [Either<CommandStatus,CommandStatus>]
|
51
|
+
def capture_command(command)
|
52
|
+
stdout, stderr, process_status = open3.capture3(*command, binmode: true)
|
49
53
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
(process_status.success? ? Either::Right : Either::Left).new(
|
55
|
+
CommandStatus.new(
|
56
|
+
process_status: process_status,
|
57
|
+
stderr: stderr,
|
58
|
+
stdout: stdout
|
59
|
+
)
|
60
|
+
)
|
55
61
|
end
|
56
62
|
|
57
63
|
# Try const get
|
data/lib/mutant.rb
CHANGED
@@ -109,6 +109,7 @@ module Mutant
|
|
109
109
|
require 'mutant/require_highjack'
|
110
110
|
require 'mutant/mutation'
|
111
111
|
require 'mutant/mutation/operators'
|
112
|
+
require 'mutant/usage'
|
112
113
|
require 'mutant/mutation/config'
|
113
114
|
require 'mutant/mutator'
|
114
115
|
require 'mutant/mutator/util'
|
@@ -221,7 +222,6 @@ module Mutant
|
|
221
222
|
require 'mutant/config/coverage_criteria'
|
222
223
|
require 'mutant/cli'
|
223
224
|
require 'mutant/cli/command'
|
224
|
-
require 'mutant/cli/command/subscription'
|
225
225
|
require 'mutant/cli/command/environment'
|
226
226
|
require 'mutant/cli/command/environment/irb'
|
227
227
|
require 'mutant/cli/command/environment/run'
|
@@ -255,11 +255,6 @@ module Mutant
|
|
255
255
|
require 'mutant/repository/diff/ranges'
|
256
256
|
require 'mutant/zombifier'
|
257
257
|
require 'mutant/range'
|
258
|
-
require 'mutant/license'
|
259
|
-
require 'mutant/license/subscription'
|
260
|
-
require 'mutant/license/subscription/commercial'
|
261
|
-
require 'mutant/license/subscription/opensource'
|
262
|
-
require 'mutant/license/subscription/repository'
|
263
258
|
require 'mutant/segment'
|
264
259
|
require 'mutant/segment/recorder'
|
265
260
|
end
|
@@ -363,7 +358,8 @@ module Mutant
|
|
363
358
|
matcher: Matcher::Config::DEFAULT,
|
364
359
|
mutation: Mutation::Config::EMPTY,
|
365
360
|
reporter: Reporter::CLI.build(WORLD.stdout),
|
366
|
-
requires: EMPTY_ARRAY
|
361
|
+
requires: EMPTY_ARRAY,
|
362
|
+
usage: Usage::Unknown.new
|
367
363
|
)
|
368
364
|
end # Config
|
369
365
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mutant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Schirp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diff-lcs
|
@@ -177,7 +177,6 @@ files:
|
|
177
177
|
- lib/mutant/cli/command/environment/subject.rb
|
178
178
|
- lib/mutant/cli/command/environment/test.rb
|
179
179
|
- lib/mutant/cli/command/root.rb
|
180
|
-
- lib/mutant/cli/command/subscription.rb
|
181
180
|
- lib/mutant/cli/command/util.rb
|
182
181
|
- lib/mutant/config.rb
|
183
182
|
- lib/mutant/config/coverage_criteria.rb
|
@@ -197,11 +196,6 @@ files:
|
|
197
196
|
- lib/mutant/isolation/exception.rb
|
198
197
|
- lib/mutant/isolation/fork.rb
|
199
198
|
- lib/mutant/isolation/none.rb
|
200
|
-
- lib/mutant/license.rb
|
201
|
-
- lib/mutant/license/subscription.rb
|
202
|
-
- lib/mutant/license/subscription/commercial.rb
|
203
|
-
- lib/mutant/license/subscription/opensource.rb
|
204
|
-
- lib/mutant/license/subscription/repository.rb
|
205
199
|
- lib/mutant/loader.rb
|
206
200
|
- lib/mutant/matcher.rb
|
207
201
|
- lib/mutant/matcher/chain.rb
|
@@ -341,6 +335,7 @@ files:
|
|
341
335
|
- lib/mutant/test/runner/sink.rb
|
342
336
|
- lib/mutant/timer.rb
|
343
337
|
- lib/mutant/transform.rb
|
338
|
+
- lib/mutant/usage.rb
|
344
339
|
- lib/mutant/util.rb
|
345
340
|
- lib/mutant/variable.rb
|
346
341
|
- lib/mutant/version.rb
|
@@ -366,7 +361,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
366
361
|
- !ruby/object:Gem::Version
|
367
362
|
version: '0'
|
368
363
|
requirements: []
|
369
|
-
rubygems_version: 3.3
|
364
|
+
rubygems_version: 3.5.3
|
370
365
|
signing_key:
|
371
366
|
specification_version: 4
|
372
367
|
summary: ''
|
@@ -1,54 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mutant
|
4
|
-
module CLI
|
5
|
-
class Command
|
6
|
-
class Subscription < self
|
7
|
-
NAME = 'subscription'
|
8
|
-
SHORT_DESCRIPTION = 'Subscription subcommands'
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def license
|
13
|
-
License.call(world)
|
14
|
-
end
|
15
|
-
|
16
|
-
class Test < self
|
17
|
-
NAME = 'test'
|
18
|
-
SUBCOMMANDS = [].freeze
|
19
|
-
SHORT_DESCRIPTION = 'Silently validates subscription, exits accordingly'
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def execute
|
24
|
-
license.right?
|
25
|
-
end
|
26
|
-
end # Test
|
27
|
-
|
28
|
-
class Show < self
|
29
|
-
NAME = 'show'
|
30
|
-
SUBCOMMANDS = [].freeze
|
31
|
-
SHORT_DESCRIPTION = 'Show subscription status'
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def execute
|
36
|
-
license.either(method(:unlicensed), method(:licensed))
|
37
|
-
end
|
38
|
-
|
39
|
-
def licensed(subscription)
|
40
|
-
world.stdout.puts(subscription.description)
|
41
|
-
true
|
42
|
-
end
|
43
|
-
|
44
|
-
def unlicensed(message)
|
45
|
-
world.stderr.puts(message)
|
46
|
-
false
|
47
|
-
end
|
48
|
-
end # Show
|
49
|
-
|
50
|
-
SUBCOMMANDS = [Show, Test].freeze
|
51
|
-
end # Subscription
|
52
|
-
end # Command
|
53
|
-
end # CLI
|
54
|
-
end # Mutant
|
@@ -1,88 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mutant
|
4
|
-
module License
|
5
|
-
class Subscription
|
6
|
-
class Commercial < self
|
7
|
-
include AbstractType
|
8
|
-
|
9
|
-
def self.from_json(value)
|
10
|
-
{
|
11
|
-
'individual' => Individual,
|
12
|
-
'organization' => Organization
|
13
|
-
}.fetch(value.fetch('type', 'individual')).from_json(value)
|
14
|
-
end
|
15
|
-
|
16
|
-
class Organization < self
|
17
|
-
SUBSCRIPTION_NAME = 'commercial organization'
|
18
|
-
|
19
|
-
def self.from_json(value)
|
20
|
-
new(licensed: value.fetch('repositories').map(&Repository.public_method(:parse)).to_set)
|
21
|
-
end
|
22
|
-
|
23
|
-
def call(world)
|
24
|
-
Repository.load_from_git(world).bind(&method(:check_subscription))
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def check_subscription(actual)
|
30
|
-
if licensed.any? { |repository| actual.any? { |other| repository.allow?(other) } }
|
31
|
-
success
|
32
|
-
else
|
33
|
-
failure(licensed, actual)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class Individual < self
|
39
|
-
SUBSCRIPTION_NAME = 'commercial individual'
|
40
|
-
|
41
|
-
class Author
|
42
|
-
include Anima.new(:email)
|
43
|
-
|
44
|
-
alias_method :to_s, :email
|
45
|
-
public :to_s
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.from_json(value)
|
49
|
-
new(licensed: value.fetch('authors').to_set { |email| Author.new(email: email) })
|
50
|
-
end
|
51
|
-
|
52
|
-
def call(world)
|
53
|
-
candidates = candidates(world)
|
54
|
-
|
55
|
-
if (licensed & candidates).any?
|
56
|
-
success
|
57
|
-
else
|
58
|
-
failure(licensed, candidates)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
def candidates(world)
|
65
|
-
git_author(world).merge(commit_author(world))
|
66
|
-
end
|
67
|
-
|
68
|
-
def git_author(world)
|
69
|
-
capture(world, %w[git config --get user.email])
|
70
|
-
end
|
71
|
-
|
72
|
-
def commit_author(world)
|
73
|
-
capture(world, %w[git show --quiet --pretty=format:%ae])
|
74
|
-
end
|
75
|
-
|
76
|
-
def capture(world, command)
|
77
|
-
world
|
78
|
-
.capture_stdout(command)
|
79
|
-
.fmap(&:chomp)
|
80
|
-
.fmap { |email| Author.new(email: email) }
|
81
|
-
.fmap { |value| Set.new([value]) }
|
82
|
-
.from_right { Set.new }
|
83
|
-
end
|
84
|
-
end # Individual
|
85
|
-
end # Commercial
|
86
|
-
end # Subscription
|
87
|
-
end # License
|
88
|
-
end # Mutant
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mutant
|
4
|
-
module License
|
5
|
-
class Subscription
|
6
|
-
class Opensource < self
|
7
|
-
SUBSCRIPTION_NAME = 'opensource repository'
|
8
|
-
|
9
|
-
def self.from_json(value)
|
10
|
-
new(licensed: value.fetch('repositories').map(&Repository.public_method(:parse)).to_set)
|
11
|
-
end
|
12
|
-
|
13
|
-
def call(world)
|
14
|
-
Repository.load_from_git(world).bind(&method(:check_subscription))
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def check_subscription(actual)
|
20
|
-
if licensed.any? { |repository| actual.any? { |other| repository.allow?(other) } }
|
21
|
-
success
|
22
|
-
else
|
23
|
-
failure(licensed, actual)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end # Opensource
|
28
|
-
end # Subscription
|
29
|
-
end # License
|
30
|
-
end # Mutant
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mutant
|
4
|
-
module License
|
5
|
-
class Subscription
|
6
|
-
class Repository
|
7
|
-
include Anima.new(:host, :path)
|
8
|
-
|
9
|
-
REMOTE_REGEXP = /\A[^\t]+\t(?<url>[^ ]+) \((?:fetch|push)\)\n\z/
|
10
|
-
GIT_SSH_REGEXP = %r{\A[^@]+@(?<host>[^:/]+)[:/](?<path>.+?)(?:\.git)?\z}
|
11
|
-
GIT_HTTPS_REGEXP = %r{\Ahttps://(?<host>[^/]+)/(?<path>.+?)(?:\.git)?\z}
|
12
|
-
WILDCARD = '/*'
|
13
|
-
WILDCARD_RANGE = (..-WILDCARD.length)
|
14
|
-
|
15
|
-
private_constant(*constants(false))
|
16
|
-
|
17
|
-
def to_s
|
18
|
-
[host, path].join('/')
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.load_from_git(world)
|
22
|
-
world
|
23
|
-
.capture_stdout(%w[git remote --verbose])
|
24
|
-
.fmap(&method(:parse_remotes))
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.parse_remotes(input)
|
28
|
-
input.lines.map(&method(:parse_remote)).to_set
|
29
|
-
end
|
30
|
-
private_class_method :parse_remotes
|
31
|
-
|
32
|
-
def self.parse(input)
|
33
|
-
host, path = *input.split('/', 2).map(&:downcase)
|
34
|
-
new(host: host, path: path)
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.parse_remote(input)
|
38
|
-
match = REMOTE_REGEXP.match(input) or
|
39
|
-
fail "Unmatched remote line: #{input.inspect}"
|
40
|
-
|
41
|
-
parse_url(match[:url])
|
42
|
-
end
|
43
|
-
private_class_method :parse_remote
|
44
|
-
|
45
|
-
def self.parse_url(input)
|
46
|
-
match = GIT_SSH_REGEXP.match(input) || GIT_HTTPS_REGEXP.match(input)
|
47
|
-
|
48
|
-
unless match
|
49
|
-
fail "Unmatched git remote URL: #{input.inspect}"
|
50
|
-
end
|
51
|
-
|
52
|
-
new(host: match[:host], path: match[:path].downcase)
|
53
|
-
end
|
54
|
-
private_class_method :parse_url
|
55
|
-
|
56
|
-
def allow?(other)
|
57
|
-
other.host.eql?(host) && path_match?(other.path)
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def path_match?(other_path)
|
63
|
-
path.eql?(other_path) || wildcard_match?(path, other_path) || wildcard_match?(other_path, path)
|
64
|
-
end
|
65
|
-
|
66
|
-
def wildcard_match?(left, right)
|
67
|
-
left.end_with?(WILDCARD) && right.start_with?(left[WILDCARD_RANGE])
|
68
|
-
end
|
69
|
-
end # Repository
|
70
|
-
end # Subscription
|
71
|
-
end # License
|
72
|
-
end # Mutant
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mutant
|
4
|
-
module License
|
5
|
-
class Subscription
|
6
|
-
include Anima.new(:licensed)
|
7
|
-
|
8
|
-
FORMAT = <<~'MESSAGE'
|
9
|
-
%<subscription_name>s subscription:
|
10
|
-
Licensed:
|
11
|
-
%<licensed>s
|
12
|
-
MESSAGE
|
13
|
-
|
14
|
-
FAILURE_FORMAT = <<~'MESSAGE'
|
15
|
-
Can not validate %<subscription_name>s license.
|
16
|
-
Licensed:
|
17
|
-
%<expected>s
|
18
|
-
Present:
|
19
|
-
%<actual>s
|
20
|
-
MESSAGE
|
21
|
-
|
22
|
-
# Load value into subscription
|
23
|
-
#
|
24
|
-
# @param [Object] value
|
25
|
-
#
|
26
|
-
# @return [Subscription]
|
27
|
-
def self.load(world, value)
|
28
|
-
{
|
29
|
-
'com' => Commercial,
|
30
|
-
'oss' => Opensource
|
31
|
-
}.fetch(value.fetch('type'))
|
32
|
-
.from_json(value.fetch('contents'))
|
33
|
-
.call(world)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Subscription self description
|
37
|
-
#
|
38
|
-
# @return [String]
|
39
|
-
def description
|
40
|
-
FORMAT % {
|
41
|
-
licensed: licensed.to_a.join("\n"),
|
42
|
-
subscription_name: subscription_name
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def failure(expected, actual)
|
49
|
-
Either::Left.new(failure_message(expected, actual))
|
50
|
-
end
|
51
|
-
|
52
|
-
def success
|
53
|
-
Either::Right.new(self)
|
54
|
-
end
|
55
|
-
|
56
|
-
def subscription_name
|
57
|
-
self.class::SUBSCRIPTION_NAME
|
58
|
-
end
|
59
|
-
|
60
|
-
def failure_message(expected, actual)
|
61
|
-
FAILURE_FORMAT % {
|
62
|
-
actual: actual.any? ? actual.map(&:to_s).join("\n") : '[none]',
|
63
|
-
expected: expected.map(&:to_s).join("\n"),
|
64
|
-
subscription_name: subscription_name
|
65
|
-
}
|
66
|
-
end
|
67
|
-
end # Subscription
|
68
|
-
end # License
|
69
|
-
end # Mutant
|
data/lib/mutant/license.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mutant
|
4
|
-
module License
|
5
|
-
NAME = 'mutant-license'
|
6
|
-
VERSION = ['>= 0.1', '< 0.3'].freeze
|
7
|
-
|
8
|
-
# Load license
|
9
|
-
#
|
10
|
-
# @param [World] world
|
11
|
-
#
|
12
|
-
# @return [Either<String,Subscription>]
|
13
|
-
#
|
14
|
-
# @api private
|
15
|
-
def self.call(world)
|
16
|
-
load_mutant_license(world)
|
17
|
-
.fmap { license_path(world) }
|
18
|
-
.bind { |path| Subscription.load(world, world.json.load(path)) }
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.load_mutant_license(world)
|
22
|
-
Either
|
23
|
-
.wrap_error(LoadError) { world.gem_method.call(NAME, *VERSION) }
|
24
|
-
.lmap(&:message)
|
25
|
-
.lmap(&method(:check_for_rubygems_mutant_license))
|
26
|
-
end
|
27
|
-
private_class_method :load_mutant_license
|
28
|
-
|
29
|
-
def self.check_for_rubygems_mutant_license(message)
|
30
|
-
if message.include?('already activated mutant-license-0.0.0')
|
31
|
-
'mutant-license gem from rubygems.org is a dummy'
|
32
|
-
else
|
33
|
-
message
|
34
|
-
end
|
35
|
-
end
|
36
|
-
private_class_method :check_for_rubygems_mutant_license
|
37
|
-
|
38
|
-
def self.license_path(world)
|
39
|
-
world
|
40
|
-
.pathname
|
41
|
-
.new(world.gem.loaded_specs.fetch(NAME).full_gem_path)
|
42
|
-
.join('license.json')
|
43
|
-
end
|
44
|
-
private_class_method :license_path
|
45
|
-
end
|
46
|
-
end
|