license-acceptance 0.2.10 → 1.0.5

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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +2 -2
  3. data/config/product_info.toml +19 -10
  4. data/lib/license_acceptance/acceptor.rb +76 -36
  5. data/lib/license_acceptance/product.rb +10 -5
  6. data/lib/license_acceptance/product_reader.rb +22 -11
  7. data/lib/license_acceptance/strategy/argument.rb +44 -0
  8. data/lib/license_acceptance/strategy/base.rb +7 -0
  9. data/lib/license_acceptance/strategy/environment.rb +38 -0
  10. data/lib/license_acceptance/strategy/file.rb +103 -0
  11. data/lib/license_acceptance/strategy/prompt.rb +111 -0
  12. data/lib/license_acceptance/strategy/provided_value.rb +28 -0
  13. data/lib/license_acceptance/version.rb +1 -1
  14. data/spec/license_acceptance/acceptor_spec.rb +115 -94
  15. data/spec/license_acceptance/product_reader_spec.rb +21 -5
  16. data/spec/license_acceptance/product_spec.rb +4 -2
  17. data/spec/license_acceptance/strategy/argument_spec.rb +82 -0
  18. data/spec/license_acceptance/strategy/environment_spec.rb +76 -0
  19. data/spec/license_acceptance/{file_acceptance_spec.rb → strategy/file_spec.rb} +21 -15
  20. data/spec/license_acceptance/{prompt_acceptance_spec.rb → strategy/prompt_spec.rb} +6 -6
  21. data/spec/license_acceptance/strategy/provided_value_spec.rb +55 -0
  22. metadata +14 -11
  23. data/lib/license_acceptance/arg_acceptance.rb +0 -32
  24. data/lib/license_acceptance/env_acceptance.rb +0 -26
  25. data/lib/license_acceptance/file_acceptance.rb +0 -97
  26. data/lib/license_acceptance/prompt_acceptance.rb +0 -106
  27. data/spec/license_acceptance/arg_acceptance_spec.rb +0 -57
  28. 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: aa15a0ed3a76f4f1bd60469de0b9f836f73f7db824da061e876bd95fb78a919b
4
- data.tar.gz: d31a667d3f45ed4455e613d6091f3635aeb6f76cfd40589f0a35e6108d022847
3
+ metadata.gz: e892e857adb140ddab6c76d173116f248180ced44a2e2ceeeecd9919b1a91b09
4
+ data.tar.gz: ce171bdadb89755fa39e0bd9ce5c5c63901d7949958a0de9a4883a823a35ae9f
5
5
  SHA512:
6
- metadata.gz: 0a91aee2c874863a1bb217da59b9cf9f8345cc90f2f9e6e9420ca9bb792fbca3808a23131702b434a64321ae915c4973b640dd71d3b0da65002c456c56e14f1d
7
- data.tar.gz: 76433d31718bffa392e817f7a350aab605142dc5d2248c25f0349adde3ac23c0ce36f83130296900dd84b24a600049df04e4c3e8a325154446540dae0e7fbb0c
6
+ metadata.gz: 0a6c4bb643dc44b5ca0ca0e0f48f6ac22ef590bc7645eea104aa584259d29a911054030749689d2be4424daed5d5a6fd2ace9554a2c7ee9989502d7f63e69d79
7
+ data.tar.gz: c5508cb4655eb0a02ba5c1067b9c58a8c03a08d9ff9eeed7bf0c864df31749ab649901db64c5fc3db6da1fee22f66c81e0bc27d32b02a0fc4899648033c2f408
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- license-acceptance (0.2.10)
4
+ license-acceptance (1.0.5)
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.2)
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)
@@ -2,27 +2,36 @@
2
2
  title = "All known products that require license acceptance, and the mapping of parent to child relationships"
3
3
 
4
4
  [[products]]
5
- name = "chef-client"
6
- pretty_name = "Chef Client"
5
+ id = "infra-client"
6
+ pretty_name = "Chef Infra Client"
7
7
  hab_pkg_id = "chef/chef-client"
8
- filename = "chef_client"
8
+ filename = "chef_infra_client"
9
+ mixlib_name = "chef"
10
+ license_required_version = "15"
9
11
 
10
12
  [[products]]
11
- name = "inspec"
12
- pretty_name = "InSpec"
13
+ id = "inspec"
14
+ pretty_name = "Chef InSpec"
13
15
  hab_pkg_id = "chef/inspec"
14
16
  filename = "inspec"
15
17
 
16
18
  [[products]]
17
- name = "supermarket"
19
+ id = "supermarket"
18
20
  pretty_name = "Supermarket"
19
21
  filename = "supermarket"
20
22
 
21
23
  [[products]]
22
- name = "chef-server"
23
- pretty_name = "Chef Server"
24
+ id = "infra-server"
25
+ pretty_name = "Chef Infra Server"
24
26
  hab_pkg_id = "chef/oc_erchef"
25
- filename = "chef_server"
27
+ filename = "chef_infra_server"
28
+
29
+ [[products]]
30
+ id = "push-jobs-server"
31
+ pretty_name = "Chef Push Jobs Server"
32
+ filename = "push_jobs_server"
26
33
 
27
34
  [relationships]
28
- "chef-client" = ["inspec"]
35
+ "infra-client" = ["inspec"]
36
+ "infra-server" = ["infra-client"]
37
+ "push-jobs-server" = ["infra-client", "infra-server"]
@@ -3,26 +3,35 @@ 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/file_acceptance"
7
- require "license_acceptance/arg_acceptance"
8
- require "license_acceptance/prompt_acceptance"
9
- require "license_acceptance/env_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
13
+
14
+ ACCEPT = "accept"
15
+ ACCEPT_SILENT = "accept-silent"
16
+ ACCEPT_NO_PERSIST = "accept-no-persist"
17
+
12
18
  class Acceptor
13
19
  extend Forwardable
14
20
  include Logger
15
21
 
16
- attr_reader :config, :product_reader, :env_acceptance, :file_acceptance, :arg_acceptance, :prompt_acceptance
22
+ attr_reader :config, :product_reader, :env_strategy, :file_strategy, :arg_strategy, :prompt_strategy, :provided_strategy
17
23
 
18
24
  def initialize(opts={})
19
25
  @config = Config.new(opts)
20
26
  Logger.initialize(config.logger)
21
27
  @product_reader = ProductReader.new
22
- @env_acceptance = EnvAcceptance.new
23
- @file_acceptance = FileAcceptance.new(config)
24
- @arg_acceptance = ArgAcceptance.new
25
- @prompt_acceptance = PromptAcceptance.new(config)
28
+ product_reader.read
29
+ @env_strategy = Strategy::Environment.new(ENV)
30
+ @file_strategy = Strategy::File.new(config)
31
+ @arg_strategy = Strategy::Argument.new(ARGV)
32
+ @prompt_strategy = Strategy::Prompt.new(config)
33
+ @provided_strategy = Strategy::ProvidedValue.new(opts.fetch(:provided, nil))
34
+ @acceptance_value = nil
26
35
  end
27
36
 
28
37
  def_delegator :@config, :output
@@ -30,73 +39,102 @@ module LicenseAcceptance
30
39
  # For applications that just need simple logic to handle a failed license acceptance flow we include this small
31
40
  # wrapper. Apps with more complex logic (like logging to a logging engine) should call the non-bang version and
32
41
  # handle the exception.
33
- def check_and_persist!(product_name, version)
34
- check_and_persist(product_name, version)
35
- rescue LicenseNotAcceptedError
36
- output.puts "#{product_name} cannot execute without accepting the license"
42
+ def check_and_persist!(product_id, version)
43
+ check_and_persist(product_id, version)
44
+ rescue LicenseNotAcceptedError => e
45
+ output.puts "#{e.product.pretty_name} cannot execute without accepting the license"
37
46
  exit 172
38
47
  end
39
48
 
40
- def check_and_persist(product_name, version)
49
+ def check_and_persist(product_id, version)
41
50
  if accepted_no_persist?
42
51
  logger.debug("Chef License accepted with no persistence")
52
+ @acceptance_value = ACCEPT_NO_PERSIST
43
53
  return true
44
54
  end
45
55
 
46
- product_reader.read
47
- product_relationship = product_reader.lookup(product_name, version)
56
+ product_relationship = product_reader.lookup(product_id, version)
48
57
 
49
- missing_licenses = file_acceptance.accepted?(product_relationship)
58
+ missing_licenses = file_strategy.accepted?(product_relationship)
50
59
 
51
60
  # They have already accepted all licenses and stored their acceptance in the persistent files
52
61
  if missing_licenses.empty?
53
62
  logger.debug("All licenses present")
63
+ @acceptance_value = ACCEPT
54
64
  return true
55
65
  end
56
66
 
57
67
  if accepted? || accepted_silent?
58
68
  if config.persist
59
- errs = file_acceptance.persist(product_relationship, missing_licenses)
69
+ errs = file_strategy.persist(product_relationship, missing_licenses)
60
70
  if errs.empty?
61
71
  output_num_persisted(missing_licenses.size) unless accepted_silent?
62
72
  else
63
73
  output_persist_failed(errs)
64
74
  end
65
75
  end
76
+ @acceptance_value = accepted_silent? ? ACCEPT_SILENT : ACCEPT
66
77
  return true
67
- elsif config.output.isatty && prompt_acceptance.request(missing_licenses) do
78
+ elsif config.output.isatty && prompt_strategy.request(missing_licenses) do
79
+ # We have to infer the acceptance value if they use the prompt to accept
68
80
  if config.persist
69
- file_acceptance.persist(product_relationship, missing_licenses)
81
+ @acceptance_value = ACCEPT
82
+ file_strategy.persist(product_relationship, missing_licenses)
70
83
  else
84
+ @acceptance_value = ACCEPT_NO_PERSIST
71
85
  []
72
86
  end
73
87
  end
74
88
  return true
75
89
  else
76
- raise LicenseNotAcceptedError.new(missing_licenses)
90
+ raise LicenseNotAcceptedError.new(product_relationship.parent, missing_licenses)
77
91
  end
78
92
  end
79
93
 
80
- def self.check_and_persist!(product_name, version, opts={})
81
- new(opts).check_and_persist!(product_name, version)
94
+ def self.check_and_persist!(product_id, version, opts={})
95
+ new(opts).check_and_persist!(product_id, version)
96
+ end
97
+
98
+ def self.check_and_persist(product_id, version, opts={})
99
+ new(opts).check_and_persist(product_id, version)
100
+ end
101
+
102
+ # Check whether the specified product requires license acceptance for the given version.
103
+ def license_required?(mixlib_name, version)
104
+ product = product_reader.lookup_by_mixlib(mixlib_name)
105
+ return false if product.nil?
106
+ # If they don't pass a version we assume they want latest
107
+ return true if version == :latest || version.nil?
108
+ Gem::Version.new(version) >= Gem::Version.new(product.license_required_version)
109
+ end
110
+
111
+ # Some callers only know about mixlib names so we need a way for them to get the product
112
+ # id as this library knows it.
113
+ def id_from_mixlib(mixlib_name)
114
+ product = product_reader.lookup_by_mixlib(mixlib_name)
115
+ return nil if product.nil?
116
+ product.id
82
117
  end
83
118
 
84
- def self.check_and_persist(product_name, version, opts={})
85
- new(opts).check_and_persist(product_name, version)
119
+ # Return the value that was matched ("accept", "accept-no-persist", etc.). Used by callers so they do not
120
+ # have to know the precedence order between provided/environment/argument. Can just get back the value
121
+ # that was used. Value is only guaranteed to be set after calling check_and_persist.
122
+ def acceptance_value
123
+ @acceptance_value
86
124
  end
87
125
 
88
126
  def accepted?
89
- env_acceptance.accepted?(ENV) || arg_acceptance.accepted?(ARGV)
127
+ provided_strategy.accepted? || env_strategy.accepted? || arg_strategy.accepted?
90
128
  end
91
129
 
92
130
  # no-persist is silent too
93
131
  def accepted_no_persist?
94
- env_acceptance.no_persist?(ENV) || arg_acceptance.no_persist?(ARGV)
132
+ provided_strategy.no_persist? || env_strategy.no_persist? || arg_strategy.no_persist?
95
133
  end
96
134
 
97
135
  # persist but be silent like no-persist
98
136
  def accepted_silent?
99
- env_acceptance.silent?(ENV) || arg_acceptance.silent?(ARGV)
137
+ provided_strategy.silent? || env_strategy.silent? || arg_strategy.silent?
100
138
  end
101
139
 
102
140
  # In the case where users accept with a command line argument or environment variable
@@ -104,26 +142,28 @@ module LicenseAcceptance
104
142
  def output_num_persisted(count)
105
143
  s = count > 1 ? "s": ""
106
144
  output.puts <<~EOM
107
- #{PromptAcceptance::BORDER}
108
- #{PromptAcceptance::CHECK} #{count} product license#{s} accepted.
109
- #{PromptAcceptance::BORDER}
145
+ #{Strategy::Prompt::BORDER}
146
+ #{Strategy::Prompt::CHECK} #{count} product license#{s} accepted.
147
+ #{Strategy::Prompt::BORDER}
110
148
  EOM
111
149
  end
112
150
 
113
151
  def output_persist_failed(errs)
114
152
  output.puts <<~EOM
115
- #{PromptAcceptance::BORDER}
116
- #{PromptAcceptance::CHECK} Product license accepted.
153
+ #{Strategy::Prompt::BORDER}
154
+ #{Strategy::Prompt::CHECK} Product license accepted.
117
155
  Could not persist acceptance:\n\t* #{errs.map(&:message).join("\n\t* ")}
118
- #{PromptAcceptance::BORDER}
156
+ #{Strategy::Prompt::BORDER}
119
157
  EOM
120
158
  end
121
159
 
122
160
  end
123
161
 
124
162
  class LicenseNotAcceptedError < RuntimeError
125
- def initialize(missing_licenses)
126
- msg = "Missing licenses for the following:\n* " + missing_licenses.join("\n* ")
163
+ attr_reader :product
164
+ def initialize(product, missing_licenses)
165
+ @product = product
166
+ msg = "Missing licenses for the following:\n* " + missing_licenses.map(&:id).join("\n* ")
127
167
  super(msg)
128
168
  end
129
169
  end
@@ -1,19 +1,24 @@
1
1
  module LicenseAcceptance
2
2
  class Product
3
3
 
4
- attr_reader :name, :pretty_name, :filename
4
+ attr_reader :id, :pretty_name, :filename, :mixlib_name, :license_required_version
5
5
 
6
- def initialize(name, pretty_name, filename)
7
- @name = name
6
+ def initialize(id, pretty_name, filename, mixlib_name, license_required_version)
7
+ # id is the internal representation of this product as license-acceptance knows it
8
+ @id = id
8
9
  @pretty_name = pretty_name
9
10
  @filename = filename
11
+ @mixlib_name = mixlib_name
12
+ @license_required_version = license_required_version
10
13
  end
11
14
 
12
15
  def ==(other)
13
16
  return false if other.class != Product
14
- if other.name == name &&
17
+ if other.id == id &&
15
18
  other.pretty_name == pretty_name &&
16
- other.filename == filename
19
+ other.filename == filename &&
20
+ other.mixlib_name == mixlib_name
21
+ other.license_required_version == license_required_version
17
22
  return true
18
23
  end
19
24
  return false
@@ -19,20 +19,24 @@ module LicenseAcceptance
19
19
  raise InvalidProductInfo.new(location) if toml.empty? || toml["products"].nil? || toml["relationships"].nil?
20
20
 
21
21
  for product in toml["products"]
22
- products[product["name"]] = Product.new(product["name"], product["pretty_name"], product["filename"])
22
+ products[product["id"]] = Product.new(
23
+ product["id"], product["pretty_name"],
24
+ product["filename"], product["mixlib_name"],
25
+ product["license_required_version"]
26
+ )
23
27
  end
24
28
 
25
- for parent_name, children in toml["relationships"]
26
- parent = products[parent_name]
27
- raise UnknownParent.new(parent_name) if parent.nil?
29
+ for parent_id, children in toml["relationships"]
30
+ parent = products[parent_id]
31
+ raise UnknownParent.new(parent_id) if parent.nil?
28
32
  # Its fine to not have a relationship entry, but not fine to have
29
33
  # a relationship where the children are nil or empty.
30
34
  if children.nil? || children.empty? || !children.is_a?(Array)
31
35
  raise NoChildRelationships.new(parent)
32
36
  end
33
- children.map! do |child_name|
34
- child = products[child_name]
35
- raise UnknownChild.new(child_name) if child.nil?
37
+ children.map! do |child_id|
38
+ child = products[child_id]
39
+ raise UnknownChild.new(child_id) if child.nil?
36
40
  child
37
41
  end
38
42
  relationships[parent] = children
@@ -51,9 +55,9 @@ module LicenseAcceptance
51
55
  File.absolute_path(File.join(__FILE__, "../../../config/product_info.toml"))
52
56
  end
53
57
 
54
- def lookup(parent_name, parent_version)
55
- parent_product = products.fetch(parent_name) do
56
- raise UnknownProduct.new(parent_name)
58
+ def lookup(parent_id, parent_version)
59
+ parent_product = products.fetch(parent_id) do
60
+ raise UnknownProduct.new(parent_id)
57
61
  end
58
62
  children = relationships.fetch(parent_product, [])
59
63
  if !parent_version.is_a? String
@@ -62,6 +66,13 @@ module LicenseAcceptance
62
66
  ProductRelationship.new(parent_product, children, parent_version)
63
67
  end
64
68
 
69
+ def lookup_by_mixlib(mixlib_name)
70
+ found_product = products.values.find(nil) do |p|
71
+ p.mixlib_name == mixlib_name
72
+ end
73
+ found_product
74
+ end
75
+
65
76
  end
66
77
 
67
78
  class UnknownProduct < RuntimeError
@@ -94,7 +105,7 @@ module LicenseAcceptance
94
105
 
95
106
  class NoChildRelationships < RuntimeError
96
107
  def initialize(product)
97
- msg = "No child relationships for #{product.name}, should be removed from product info or fixed"
108
+ msg = "No child relationships for #{product.id}, should be removed from product info or fixed"
98
109
  super(msg)
99
110
  end
100
111
  end
@@ -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,7 @@
1
+ module LicenseAcceptance
2
+ module Strategy
3
+ class Base
4
+
5
+ end
6
+ end
7
+ 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,103 @@
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(&:id)}")
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(&:id)}")
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.pretty_name} at path #{path}")
81
+ ::File.open(path, ::File::WRONLY | ::File::CREAT | ::File::EXCL) do |license_file|
82
+ contents = {
83
+ id: product.id,
84
+ name: product.pretty_name,
85
+ date_accepted: INVOCATION_TIME.iso8601,
86
+ accepting_product: parent.id,
87
+ accepting_product_version: parent_version,
88
+ user: Etc.getlogin,
89
+ file_format: 1,
90
+ }
91
+ contents = Hash[contents.map { |k, v| [k.to_s, v] }]
92
+ license_file << YAML.dump(contents)
93
+ end
94
+ return nil
95
+ rescue StandardError => e
96
+ msg = "Could not persist license to #{path}"
97
+ logger.info "#{msg}\n\t#{e.message}\n\t#{e.backtrace.join("\n\t")}"
98
+ return e
99
+ end
100
+
101
+ end
102
+ end
103
+ end