license-acceptance 0.2.10 → 0.2.13
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/Gemfile.lock +2 -2
- data/lib/license_acceptance/acceptor.rb +24 -22
- data/lib/license_acceptance/strategy/argument.rb +44 -0
- data/lib/license_acceptance/strategy/base.rb +11 -0
- data/lib/license_acceptance/strategy/environment.rb +38 -0
- data/lib/license_acceptance/strategy/file.rb +102 -0
- data/lib/license_acceptance/strategy/prompt.rb +111 -0
- data/lib/license_acceptance/strategy/provided_value.rb +28 -0
- data/lib/license_acceptance/version.rb +1 -1
- data/spec/license_acceptance/acceptor_spec.rb +37 -73
- data/spec/license_acceptance/strategy/argument_spec.rb +82 -0
- data/spec/license_acceptance/strategy/environment_spec.rb +76 -0
- data/spec/license_acceptance/{file_acceptance_spec.rb → strategy/file_spec.rb} +3 -3
- data/spec/license_acceptance/{prompt_acceptance_spec.rb → strategy/prompt_spec.rb} +4 -4
- data/spec/license_acceptance/strategy/provided_value_spec.rb +55 -0
- metadata +14 -11
- data/lib/license_acceptance/arg_acceptance.rb +0 -32
- data/lib/license_acceptance/env_acceptance.rb +0 -26
- data/lib/license_acceptance/file_acceptance.rb +0 -97
- data/lib/license_acceptance/prompt_acceptance.rb +0 -106
- data/spec/license_acceptance/arg_acceptance_spec.rb +0 -57
- data/spec/license_acceptance/env_acceptance_spec.rb +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4e838f450a28b5543cbf2ebf0a620b4274567cc1f42067d2ebd859a5e122005
|
4
|
+
data.tar.gz: 6257b8dd5964e8058e7614c219ebbe60234f9f060dff4c52cdd4df3a295daecb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81e308d0664299c9797fa0b06470f550aac3087f9d154ee0f8b3737aff4cfc09fca669a80f81d4be726d01e4166124ea3747630720f32d8115bc1c5d5fb38722
|
7
|
+
data.tar.gz: 3677568ac06e369235ecba67c6ae38cd4f271a9ca19f495c8d2e789e32e18eea559fe40aea9a8f1b60cdef659828f5b892eacf1c70099ea83cae1b846bd27cf1
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
license-acceptance (0.2.
|
4
|
+
license-acceptance (0.2.13)
|
5
5
|
pastel (~> 0.7)
|
6
6
|
tomlrb (~> 1.2)
|
7
7
|
tty-box (~> 0.3)
|
@@ -40,7 +40,7 @@ GEM
|
|
40
40
|
rspec-mocks (~> 3.8.0)
|
41
41
|
rspec-core (3.8.0)
|
42
42
|
rspec-support (~> 3.8.0)
|
43
|
-
rspec-expectations (3.8.
|
43
|
+
rspec-expectations (3.8.3)
|
44
44
|
diff-lcs (>= 1.2.0, < 2.0)
|
45
45
|
rspec-support (~> 3.8.0)
|
46
46
|
rspec-mocks (3.8.0)
|
@@ -3,26 +3,28 @@ require "license_acceptance/config"
|
|
3
3
|
require "license_acceptance/logger"
|
4
4
|
require "license_acceptance/product_reader"
|
5
5
|
require "license_acceptance/product_relationship"
|
6
|
-
require "license_acceptance/
|
7
|
-
require "license_acceptance/
|
8
|
-
require "license_acceptance/
|
9
|
-
require "license_acceptance/
|
6
|
+
require "license_acceptance/strategy/environment"
|
7
|
+
require "license_acceptance/strategy/file"
|
8
|
+
require "license_acceptance/strategy/argument"
|
9
|
+
require "license_acceptance/strategy/prompt"
|
10
|
+
require "license_acceptance/strategy/provided_value"
|
10
11
|
|
11
12
|
module LicenseAcceptance
|
12
13
|
class Acceptor
|
13
14
|
extend Forwardable
|
14
15
|
include Logger
|
15
16
|
|
16
|
-
attr_reader :config, :product_reader, :
|
17
|
+
attr_reader :config, :product_reader, :env_strategy, :file_strategy, :arg_strategy, :prompt_strategy, :provided_strategy
|
17
18
|
|
18
19
|
def initialize(opts={})
|
19
20
|
@config = Config.new(opts)
|
20
21
|
Logger.initialize(config.logger)
|
21
22
|
@product_reader = ProductReader.new
|
22
|
-
@
|
23
|
-
@
|
24
|
-
@
|
25
|
-
@
|
23
|
+
@env_strategy = Strategy::Environment.new(ENV)
|
24
|
+
@file_strategy = Strategy::File.new(config)
|
25
|
+
@arg_strategy = Strategy::Argument.new(ARGV)
|
26
|
+
@prompt_strategy = Strategy::Prompt.new(config)
|
27
|
+
@provided_strategy = Strategy::ProvidedValue.new(opts.fetch(:provided, nil))
|
26
28
|
end
|
27
29
|
|
28
30
|
def_delegator :@config, :output
|
@@ -46,7 +48,7 @@ module LicenseAcceptance
|
|
46
48
|
product_reader.read
|
47
49
|
product_relationship = product_reader.lookup(product_name, version)
|
48
50
|
|
49
|
-
missing_licenses =
|
51
|
+
missing_licenses = file_strategy.accepted?(product_relationship)
|
50
52
|
|
51
53
|
# They have already accepted all licenses and stored their acceptance in the persistent files
|
52
54
|
if missing_licenses.empty?
|
@@ -56,7 +58,7 @@ module LicenseAcceptance
|
|
56
58
|
|
57
59
|
if accepted? || accepted_silent?
|
58
60
|
if config.persist
|
59
|
-
errs =
|
61
|
+
errs = file_strategy.persist(product_relationship, missing_licenses)
|
60
62
|
if errs.empty?
|
61
63
|
output_num_persisted(missing_licenses.size) unless accepted_silent?
|
62
64
|
else
|
@@ -64,9 +66,9 @@ module LicenseAcceptance
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
return true
|
67
|
-
elsif config.output.isatty &&
|
69
|
+
elsif config.output.isatty && prompt_strategy.request(missing_licenses) do
|
68
70
|
if config.persist
|
69
|
-
|
71
|
+
file_strategy.persist(product_relationship, missing_licenses)
|
70
72
|
else
|
71
73
|
[]
|
72
74
|
end
|
@@ -86,17 +88,17 @@ module LicenseAcceptance
|
|
86
88
|
end
|
87
89
|
|
88
90
|
def accepted?
|
89
|
-
|
91
|
+
provided_strategy.accepted? || env_strategy.accepted? || arg_strategy.accepted?
|
90
92
|
end
|
91
93
|
|
92
94
|
# no-persist is silent too
|
93
95
|
def accepted_no_persist?
|
94
|
-
|
96
|
+
provided_strategy.no_persist? || env_strategy.no_persist? || arg_strategy.no_persist?
|
95
97
|
end
|
96
98
|
|
97
99
|
# persist but be silent like no-persist
|
98
100
|
def accepted_silent?
|
99
|
-
|
101
|
+
provided_strategy.silent? || env_strategy.silent? || arg_strategy.silent?
|
100
102
|
end
|
101
103
|
|
102
104
|
# In the case where users accept with a command line argument or environment variable
|
@@ -104,18 +106,18 @@ module LicenseAcceptance
|
|
104
106
|
def output_num_persisted(count)
|
105
107
|
s = count > 1 ? "s": ""
|
106
108
|
output.puts <<~EOM
|
107
|
-
#{
|
108
|
-
#{
|
109
|
-
#{
|
109
|
+
#{Strategy::Prompt::BORDER}
|
110
|
+
#{Strategy::Prompt::CHECK} #{count} product license#{s} accepted.
|
111
|
+
#{Strategy::Prompt::BORDER}
|
110
112
|
EOM
|
111
113
|
end
|
112
114
|
|
113
115
|
def output_persist_failed(errs)
|
114
116
|
output.puts <<~EOM
|
115
|
-
#{
|
116
|
-
#{
|
117
|
+
#{Strategy::Prompt::BORDER}
|
118
|
+
#{Strategy::Prompt::CHECK} Product license accepted.
|
117
119
|
Could not persist acceptance:\n\t* #{errs.map(&:message).join("\n\t* ")}
|
118
|
-
#{
|
120
|
+
#{Strategy::Prompt::BORDER}
|
119
121
|
EOM
|
120
122
|
end
|
121
123
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "license_acceptance/strategy/base"
|
2
|
+
|
3
|
+
module LicenseAcceptance
|
4
|
+
module Strategy
|
5
|
+
|
6
|
+
# Look for acceptance values in the ARGV
|
7
|
+
class Argument < Base
|
8
|
+
|
9
|
+
attr_reader :argv
|
10
|
+
|
11
|
+
def initialize(argv)
|
12
|
+
@argv = argv
|
13
|
+
end
|
14
|
+
|
15
|
+
def accepted?
|
16
|
+
look_for_value(ACCEPT)
|
17
|
+
end
|
18
|
+
|
19
|
+
def silent?
|
20
|
+
look_for_value(ACCEPT_SILENT)
|
21
|
+
end
|
22
|
+
|
23
|
+
def no_persist?
|
24
|
+
look_for_value(ACCEPT_NO_PERSIST)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def look_for_value(sought)
|
30
|
+
if argv.include?("--chef-license=#{sought}")
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
i = argv.index("--chef-license")
|
34
|
+
unless i.nil?
|
35
|
+
val = argv[i+1]
|
36
|
+
if val != nil && val.downcase == sought
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "license_acceptance/strategy/base"
|
2
|
+
|
3
|
+
module LicenseAcceptance
|
4
|
+
module Strategy
|
5
|
+
|
6
|
+
# Look for acceptance values in the environment
|
7
|
+
class Environment < Base
|
8
|
+
|
9
|
+
attr_reader :env
|
10
|
+
|
11
|
+
def initialize(env)
|
12
|
+
@env = env
|
13
|
+
end
|
14
|
+
|
15
|
+
def accepted?
|
16
|
+
look_for_value(ACCEPT)
|
17
|
+
end
|
18
|
+
|
19
|
+
def silent?
|
20
|
+
look_for_value(ACCEPT_SILENT)
|
21
|
+
end
|
22
|
+
|
23
|
+
def no_persist?
|
24
|
+
look_for_value(ACCEPT_NO_PERSIST)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def look_for_value(sought)
|
30
|
+
if env['CHEF_LICENSE'] && env['CHEF_LICENSE'].downcase == sought
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'yaml'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'etc'
|
5
|
+
require "license_acceptance/logger"
|
6
|
+
require "license_acceptance/strategy/base"
|
7
|
+
|
8
|
+
module LicenseAcceptance
|
9
|
+
module Strategy
|
10
|
+
|
11
|
+
# Read and write marker files that show acceptance.
|
12
|
+
class File < Base
|
13
|
+
include Logger
|
14
|
+
|
15
|
+
attr_reader :config
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
@config = config
|
19
|
+
end
|
20
|
+
|
21
|
+
INVOCATION_TIME = DateTime.now.freeze
|
22
|
+
|
23
|
+
# For all the given products in the product set, search all possible locations for the
|
24
|
+
# license acceptance files.
|
25
|
+
def accepted?(product_relationship)
|
26
|
+
searching = [product_relationship.parent] + product_relationship.children
|
27
|
+
missing_licenses = searching.clone
|
28
|
+
logger.debug("Searching for the following licenses: #{missing_licenses.map(&:name)}")
|
29
|
+
|
30
|
+
searching.each do |product|
|
31
|
+
found = false
|
32
|
+
config.license_locations.each do |loc|
|
33
|
+
f = ::File.join(loc, product.filename)
|
34
|
+
if ::File.exist?(f)
|
35
|
+
found = true
|
36
|
+
logger.debug("Found license #{product.filename} at #{f}")
|
37
|
+
missing_licenses.delete(product)
|
38
|
+
break
|
39
|
+
end
|
40
|
+
end
|
41
|
+
break if missing_licenses.empty?
|
42
|
+
end
|
43
|
+
logger.debug("Missing licenses remaining: #{missing_licenses.map(&:name)}")
|
44
|
+
missing_licenses
|
45
|
+
end
|
46
|
+
|
47
|
+
def persist(product_relationship, missing_licenses)
|
48
|
+
parent = product_relationship.parent
|
49
|
+
parent_version = product_relationship.parent_version
|
50
|
+
root_dir = config.persist_location
|
51
|
+
|
52
|
+
if !Dir.exist?(root_dir)
|
53
|
+
begin
|
54
|
+
FileUtils.mkdir_p(root_dir)
|
55
|
+
rescue StandardError => e
|
56
|
+
msg = "Could not create license directory #{root_dir}"
|
57
|
+
logger.info "#{msg}\n\t#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
58
|
+
return [e]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
errs = []
|
63
|
+
if missing_licenses.include?(parent)
|
64
|
+
err = persist_license(root_dir, parent, parent, parent_version)
|
65
|
+
errs << err unless err.nil?
|
66
|
+
end
|
67
|
+
product_relationship.children.each do |child|
|
68
|
+
if missing_licenses.include?(child)
|
69
|
+
err = persist_license(root_dir, child, parent, parent_version)
|
70
|
+
errs << err unless err.nil?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return errs
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def persist_license(folder_path, product, parent, parent_version)
|
79
|
+
path = ::File.join(folder_path, product.filename)
|
80
|
+
logger.info("Persisting a license for #{product.name} at path #{path}")
|
81
|
+
::File.open(path, ::File::WRONLY | ::File::CREAT | ::File::EXCL) do |license_file|
|
82
|
+
contents = {
|
83
|
+
name: product.name,
|
84
|
+
date_accepted: INVOCATION_TIME.iso8601,
|
85
|
+
accepting_product: parent.name,
|
86
|
+
accepting_product_version: parent_version,
|
87
|
+
user: Etc.getlogin,
|
88
|
+
file_format: 1,
|
89
|
+
}
|
90
|
+
contents = Hash[contents.map { |k, v| [k.to_s, v] }]
|
91
|
+
license_file << YAML.dump(contents)
|
92
|
+
end
|
93
|
+
return nil
|
94
|
+
rescue StandardError => e
|
95
|
+
msg = "Could not persist license to #{path}"
|
96
|
+
logger.info "#{msg}\n\t#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
97
|
+
return e
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'tty-prompt'
|
2
|
+
require 'pastel'
|
3
|
+
require "license_acceptance/logger"
|
4
|
+
require "license_acceptance/strategy/base"
|
5
|
+
require "timeout"
|
6
|
+
|
7
|
+
module LicenseAcceptance
|
8
|
+
module Strategy
|
9
|
+
|
10
|
+
# Interactive prompt for accepting and persistnce license acceptance, or failing with custom exit code
|
11
|
+
class Prompt < Base
|
12
|
+
include Logger
|
13
|
+
|
14
|
+
attr_reader :output
|
15
|
+
|
16
|
+
def initialize(config)
|
17
|
+
@output = config.output
|
18
|
+
end
|
19
|
+
|
20
|
+
WIDTH = 50.freeze
|
21
|
+
PASTEL = Pastel.new
|
22
|
+
BORDER = "+---------------------------------------------+".freeze
|
23
|
+
YES = PASTEL.green.bold("yes")
|
24
|
+
CHECK = PASTEL.green("✔")
|
25
|
+
|
26
|
+
def request(missing_licenses, &persist_callback)
|
27
|
+
logger.debug("Requesting a license for #{missing_licenses.map(&:name)}")
|
28
|
+
c = missing_licenses.size
|
29
|
+
s = c > 1 ? "s": ""
|
30
|
+
|
31
|
+
acceptance_question = "Do you accept the #{c} product license#{s} (#{YES}/no)?"
|
32
|
+
output.puts <<~EOM
|
33
|
+
#{BORDER}
|
34
|
+
Chef License Acceptance
|
35
|
+
|
36
|
+
Before you can continue, #{c} product license#{s}
|
37
|
+
must be accepted. View the license at
|
38
|
+
https://www.chef.io/end-user-license-agreement/
|
39
|
+
|
40
|
+
License#{s} that need accepting:
|
41
|
+
* #{missing_licenses.map(&:pretty_name).join("\n * ")}
|
42
|
+
|
43
|
+
#{acceptance_question}
|
44
|
+
|
45
|
+
EOM
|
46
|
+
|
47
|
+
if ask(output, c, s, persist_callback)
|
48
|
+
output.puts BORDER
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
|
52
|
+
output.puts <<~EOM
|
53
|
+
|
54
|
+
If you do not accept this license you will
|
55
|
+
not be able to use Chef products.
|
56
|
+
|
57
|
+
#{acceptance_question}
|
58
|
+
|
59
|
+
EOM
|
60
|
+
|
61
|
+
answer = ask(output, c, s, persist_callback)
|
62
|
+
if answer != "yes"
|
63
|
+
output.puts BORDER
|
64
|
+
end
|
65
|
+
return answer
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def ask(output, c, s, persist_callback)
|
71
|
+
logger.debug("Attempting to request interactive prompt on TTY")
|
72
|
+
prompt = TTY::Prompt.new(track_history: false, active_color: :bold, interrupt: :exit, output: output)
|
73
|
+
|
74
|
+
answer = "no"
|
75
|
+
begin
|
76
|
+
Timeout::timeout(60, PromptTimeout) do
|
77
|
+
answer = prompt.ask(">") do |q|
|
78
|
+
q.modify :down, :trim
|
79
|
+
q.required true
|
80
|
+
q.messages[:required?] = "You must enter 'yes' or 'no'"
|
81
|
+
q.validate /^\s*(yes|no)\s*$/i
|
82
|
+
q.messages[:valid?] = "You must enter 'yes' or 'no'"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
rescue PromptTimeout
|
86
|
+
prompt.unsubscribe(prompt.reader)
|
87
|
+
output.puts "Prompt timed out. Use non-interactive flags or enter an answer within 60 seconds."
|
88
|
+
end
|
89
|
+
|
90
|
+
if answer == "yes"
|
91
|
+
output.puts
|
92
|
+
output.puts "Persisting #{c} product license#{s}..."
|
93
|
+
errs = persist_callback.call
|
94
|
+
if errs.empty?
|
95
|
+
output.puts "#{CHECK} #{c} product license#{s} persisted.\n\n"
|
96
|
+
else
|
97
|
+
output.puts <<~EOM
|
98
|
+
#{CHECK} #{c} product license#{s} accepted.
|
99
|
+
Could not persist acceptance:\n\t* #{errs.map(&:message).join("\n\t* ")}
|
100
|
+
EOM
|
101
|
+
end
|
102
|
+
return true
|
103
|
+
end
|
104
|
+
return false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class PromptTimeout < StandardError; end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|