ruby-pwsh 0.5.1 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37a20d5c79da4ea0b065cb85afc8e99933128576ef73c759a9f8113b7f738eed
4
- data.tar.gz: 9da59c07a8037eee9bb03591726ed83e4909b95009579b362fb49a4d2e4ac171
3
+ metadata.gz: 164dc89d750a5544dd4c100bd2166c0fe9d26447d5636bb18b0eec7d8dfc142f
4
+ data.tar.gz: bdf317f119bd2e5a7261a711a2a5c4721feda199a955feb5265af07c6f94d8d8
5
5
  SHA512:
6
- metadata.gz: 760bbbdd326042ff79e4e0f28bbfe48447ddc17fb6968f569ce1c5d75848a83e3b3042022250fb309d98124d97580c9ad1a7a9a8a629aadbbdc57b7de7718f60
7
- data.tar.gz: 1c57dec6a7bd3db1e4b32adfd0e61abb996157317fa2f74f20110f246a3daf8f6302aa0569aef07e863299e9a8ba126631b89f9df3055f5b013a8c720ff308e0
6
+ metadata.gz: 8430d204e301ce7577148a95d37a8581b3e0d37da1c3e97fcb5dce3c3a2b66601b651dd48ef3a2810d802d09cf28579dfc986712bf407e54ecb3ca8f23ab9283
7
+ data.tar.gz: ed31dd3ec848dc4c7fd108ee040c5b39e532ea50fe406ba8f98dfb6bb2cf5c804b0d758c393ead8b560785d34717a629380e346f24baae6963e34094889c6fdc
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org).
4
4
 
5
+ ## [0.6.0](https://github.com/puppetlabs/ruby-pwsh/tree/0.6.0) (2020-11-24)
6
+
7
+ [Full Changelog](https://github.com/puppetlabs/ruby-pwsh/compare/0.5.1...0.6.0)
8
+
9
+ ### Added
10
+
11
+ - \(GH-81\) Handle parameters in the dsc base provider [\#62](https://github.com/puppetlabs/ruby-pwsh/pull/62) ([michaeltlombardi](https://github.com/michaeltlombardi))
12
+ - \(GH-74\) Remove special handling for ensure in the dsc base provider [\#61](https://github.com/puppetlabs/ruby-pwsh/pull/61) ([michaeltlombardi](https://github.com/michaeltlombardi))
13
+ - \(GH-59\) Refactor away from Simple Provider [\#60](https://github.com/puppetlabs/ruby-pwsh/pull/60) ([michaeltlombardi](https://github.com/michaeltlombardi))
14
+
15
+ ### Fixed
16
+
17
+ - \(GH-57\) Handle datetimes in dsc [\#58](https://github.com/puppetlabs/ruby-pwsh/pull/58) ([michaeltlombardi](https://github.com/michaeltlombardi))
18
+ - \(GH-55\) Handle intentionally empty arrays [\#56](https://github.com/puppetlabs/ruby-pwsh/pull/56) ([michaeltlombardi](https://github.com/michaeltlombardi))
19
+
5
20
  ## [0.5.1](https://github.com/puppetlabs/ruby-pwsh/tree/0.5.1) (2020-09-25)
6
21
 
7
22
  [Full Changelog](https://github.com/puppetlabs/ruby-pwsh/compare/0.5.0...0.5.1)
@@ -2,7 +2,7 @@
2
2
  version: 1.1.x.{build}
3
3
  branches:
4
4
  only:
5
- - master
5
+ - main
6
6
  - release
7
7
  clone_depth: 10
8
8
  environment:
@@ -1,12 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puppet/resource_api/simple_provider'
4
3
  require 'securerandom'
5
4
  require 'ruby-pwsh'
6
5
  require 'pathname'
7
6
  require 'json'
8
7
 
9
- class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
8
+ class Puppet::Provider::DscBaseProvider
10
9
  # Initializes the provider, preparing the class variables which cache:
11
10
  # - the canonicalized resources across calls
12
11
  # - query results
@@ -15,6 +14,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
15
14
  @@cached_canonicalized_resource = []
16
15
  @@cached_query_results = []
17
16
  @@logon_failures = []
17
+ super
18
18
  end
19
19
 
20
20
  # Look through a cache to retrieve the hashes specified, if they have been cached.
@@ -109,6 +109,77 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
109
109
  end
110
110
  end
111
111
 
112
+ # Determines whether a resource is ensurable and which message to write (create, update, or delete),
113
+ # then passes the appropriate values along to the various sub-methods which themselves call the Set
114
+ # method of Invoke-DscResource. Implementation borrowed directly from the Resource API Simple Provider
115
+ #
116
+ # @param context [Object] the Puppet runtime context to operate in and send feedback to
117
+ # @param changes [Hash] the hash of whose key is the name_hash and value is the is and should hashes
118
+ def set(context, changes)
119
+ changes.each do |name, change|
120
+ is = change.key?(:is) ? change[:is] : (get(context, [name]) || []).find { |r| r[:name] == name }
121
+ context.type.check_schema(is) unless change.key?(:is)
122
+
123
+ should = change[:should]
124
+
125
+ name_hash = if context.type.namevars.length > 1
126
+ # pass a name_hash containing the values of all namevars
127
+ name_hash = {}
128
+ context.type.namevars.each do |namevar|
129
+ name_hash[namevar] = change[:should][namevar]
130
+ end
131
+ name_hash
132
+ else
133
+ name
134
+ end
135
+
136
+ # for compatibility sake, we use dsc_ensure instead of ensure, so context.type.ensurable? does not work
137
+ if !context.type.attributes.key?(:dsc_ensure)
138
+ is = create_absent(:name, name) if is.nil?
139
+ should = create_absent(:name, name) if should.nil?
140
+
141
+ # HACK: If the DSC Resource is ensurable but doesn't report a default value
142
+ # for ensure, we assume it to be `Present` - this is the most common pattern.
143
+ should_ensure = should[:dsc_ensure].nil? ? 'Present' : should[:dsc_ensure].to_s
144
+ is_ensure = is[:dsc_ensure].to_s
145
+
146
+ if is_ensure == 'Absent' && should_ensure == 'Present'
147
+ context.creating(name) do
148
+ create(context, name_hash, should)
149
+ end
150
+ elsif is_ensure == 'Present' && should_ensure == 'Present'
151
+ context.updating(name) do
152
+ update(context, name_hash, should)
153
+ end
154
+ elsif is_ensure == 'Present' && should_ensure == 'Absent'
155
+ context.deleting(name) do
156
+ delete(context, name_hash)
157
+ end
158
+ end
159
+ else
160
+ context.updating(name) do
161
+ update(context, name_hash, should)
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ # Creates a hash with the name / name_hash and sets dsc_ensure to absent for comparison
168
+ # purposes; this handles cases where the resource isn't found on the node.
169
+ #
170
+ # @param namevar [Object] the name of the variable being used for the resource name
171
+ # @param title [Hash] the hash of namevar properties and their values
172
+ # @return [Hash] returns a hash representing the absent state of the resource
173
+ def create_absent(namevar, title)
174
+ result = if title.is_a? Hash
175
+ title.dup
176
+ else
177
+ { namevar => title }
178
+ end
179
+ result[:dsc_ensure] = 'Absent'
180
+ result
181
+ end
182
+
112
183
  # Attempts to set an instance of the DSC resource, invoking the `Set` method and thinly wrapping
113
184
  # the `invoke_set_method` method; whether this method, `update`, or `delete` is called is entirely
114
185
  # up to the Resource API based on the results
@@ -158,9 +229,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
158
229
  context.debug("retrieving #{name_hash.inspect}")
159
230
 
160
231
  # Do not bother running if the logon credentials won't work
161
- unless name_hash[:dsc_psdscrunascredential].nil?
162
- return name_hash if logon_failed_already?(name_hash[:dsc_psdscrunascredential])
163
- end
232
+ return name_hash if !name_hash[:dsc_psdscrunascredential].nil? && logon_failed_already?(name_hash[:dsc_psdscrunascredential])
164
233
 
165
234
  query_props = name_hash.select { |k, v| mandatory_get_attributes(context).include?(k) || (k == :dsc_psdscrunascredential && !v.nil?) }
166
235
  resource = should_to_resource(query_props, context, 'get')
@@ -190,28 +259,27 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
190
259
  # DSC gives back information we don't care about; filter down to only
191
260
  # those properties exposed in the type definition.
192
261
  valid_attributes = context.type.attributes.keys.collect(&:to_s)
262
+ parameters = context.type.attributes.select { |_name, properties| [properties[:behaviour]].collect.include?(:parameter) }.keys.collect(&:to_s)
193
263
  data.select! { |key, _value| valid_attributes.include?("dsc_#{key.downcase}") }
264
+ data.reject! { |key, _value| parameters.include?("dsc_#{key.downcase}") }
194
265
  # Canonicalize the results to match the type definition representation;
195
266
  # failure to do so will prevent the resource_api from comparing the result
196
267
  # to the should hash retrieved from the resource definition in the manifest.
197
- data.keys.each do |key|
268
+ data.keys.each do |key| # rubocop:disable Style/HashEachMethods
198
269
  type_key = "dsc_#{key.downcase}".to_sym
199
270
  data[type_key] = data.delete(key)
200
271
  camelcase_hash_keys!(data[type_key]) if data[type_key].is_a?(Enumerable)
272
+ # Convert DateTime back to appropriate type
273
+ data[type_key] = Puppet::Pops::Time::Timestamp.parse(data[type_key]) if context.type.attributes[type_key][:mof_type] =~ /DateTime/i
274
+ # PowerShell does not distinguish between a return of empty array/string
275
+ # and null but Puppet does; revert to those values if specified.
276
+ if data[type_key].nil? && query_props.keys.include?(type_key) && query_props[type_key].is_a?(Array)
277
+ data[type_key] = query_props[type_key].empty? ? query_props[type_key] : []
278
+ end
201
279
  end
202
- # If a resource is found, it's present, so refill these two Puppet-only keys
280
+ # If a resource is found, it's present, so refill this Puppet-only key
203
281
  data.merge!({ name: name_hash[:name] })
204
- if ensurable?(context)
205
- ensure_value = data.key?(:dsc_ensure) ? data[:dsc_ensure].downcase : 'present'
206
- data.merge!({ ensure: ensure_value })
207
- end
208
- # TODO: Handle PSDscRunAsCredential flapping
209
- # Resources do not return the account under which they were discovered, so re-add that
210
- if name_hash[:dsc_psdscrunascredential].nil?
211
- data.delete(:dsc_psdscrunascredential)
212
- else
213
- data.merge!({ dsc_psdscrunascredential: name_hash[:dsc_psdscrunascredential] })
214
- end
282
+
215
283
  # Cache the query to prevent a second lookup
216
284
  @@cached_query_results << data.dup if fetch_cached_hashes(@@cached_query_results, [data]).empty?
217
285
  context.debug("Returned to Puppet as #{data}")
@@ -229,9 +297,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
229
297
  context.debug("Invoking Set Method for '#{name}' with #{should.inspect}")
230
298
 
231
299
  # Do not bother running if the logon credentials won't work
232
- unless should[:dsc_psdscrunascredential].nil?
233
- return nil if logon_failed_already?(should[:dsc_psdscrunascredential])
234
- end
300
+ return nil if !should[:dsc_psdscrunascredential].nil? && logon_failed_already?(should[:dsc_psdscrunascredential])
235
301
 
236
302
  apply_props = should.select { |k, _v| k.to_s =~ /^dsc_/ }
237
303
  resource = should_to_resource(apply_props, context, 'set')
@@ -265,7 +331,6 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
265
331
  resource[k] = context.type.definition[k]
266
332
  end
267
333
  should.each do |k, v|
268
- next if k == :ensure
269
334
  # PSDscRunAsCredential is considered a namevar and will always be passed, even if nil
270
335
  # To prevent flapping during runs, remove it from the resource definition unless specified
271
336
  next if k == :dsc_psdscrunascredential && v.nil?
@@ -283,9 +348,9 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
283
348
  # During a Puppet agent run, the code lives in the cache so we can use the file expansion to discover the correct folder.
284
349
  root_module_path = $LOAD_PATH.select { |path| path.match?(%r{#{resource[:dscmeta_module_name].downcase}/lib}) }.first
285
350
  resource[:vendored_modules_path] = if root_module_path.nil?
286
- File.expand_path(Pathname.new(__FILE__).dirname + '../../../' + 'puppet_x/dsc_resources')
351
+ File.expand_path(Pathname.new(__FILE__).dirname + '../../../' + 'puppet_x/dsc_resources') # rubocop:disable Style/StringConcatenation
287
352
  else
288
- File.expand_path(root_module_path + '/puppet_x/dsc_resources')
353
+ File.expand_path("#{root_module_path}/puppet_x/dsc_resources")
289
354
  end
290
355
  resource[:attributes] = nil
291
356
  context.debug("should_to_resource: #{resource.inspect}")
@@ -331,7 +396,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
331
396
  # @return [Enumerable] returns the input object with hash keys recursively camelCased
332
397
  def camelcase_hash_keys!(enumerable)
333
398
  if enumerable.is_a?(Hash)
334
- enumerable.keys.each do |key|
399
+ enumerable.keys.each do |key| # rubocop:disable Style/HashEachMethods
335
400
  name = key.dup
336
401
  name[0] = name[0].downcase
337
402
  enumerable[name] = enumerable.delete(key)
@@ -363,14 +428,6 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
363
428
  end
364
429
  end
365
430
 
366
- # Checks to see whether the DSC resource being managed is defined as ensurable
367
- #
368
- # @param context [Object] the Puppet runtime context to operate in and send feedback to
369
- # @return [Bool] returns true if the DSC Resource is ensurable, otherwise false.
370
- def ensurable?(context)
371
- context.type.attributes.keys.include?(:ensure)
372
- end
373
-
374
431
  # Parses the DSC resource type definition to retrieve the names of any attributes which are specified as mandatory for get operations
375
432
  #
376
433
  # @param context [Object] the Puppet runtime context to operate in and send feedback to
@@ -441,8 +498,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
441
498
  # @param credential_hash [Hash] the Properties which define the PSCredential Object
442
499
  # @return [String] A line of PowerShell which defines the PSCredential object and stores it to a variable
443
500
  def format_pscredential(variable_name, credential_hash)
444
- definition = "$#{variable_name} = New-PSCredential -User #{credential_hash['user']} -Password '#{credential_hash['password']}' # PuppetSensitive"
445
- definition
501
+ "$#{variable_name} = New-PSCredential -User #{credential_hash['user']} -Password '#{credential_hash['password']}' # PuppetSensitive"
446
502
  end
447
503
 
448
504
  # Parses a resource definition (as from `should_to_resource`) for any properties which are CIM instances
@@ -522,8 +578,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
522
578
  # EVEN WORSE HACK - this one we can't even be sure it's a cim instance...
523
579
  # but I don't _think_ anything but nested cim instances show up as hashes inside an array
524
580
  definition = definition.gsub('@(@{', '[CimInstance[]]@(@{')
525
- definition = interpolate_variables(definition)
526
- definition
581
+ interpolate_variables(definition)
527
582
  end
528
583
 
529
584
  # Munge a resource definition (as from `should_to_resource`) into valid PowerShell which represents
@@ -548,18 +603,32 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
548
603
  resource[:parameters].each do |property_name, property_hash|
549
604
  # strip dsc_ from the beginning of the property name declaration
550
605
  name = property_name.to_s.gsub(/^dsc_/, '').to_sym
551
- params[:Property][name] = if property_hash[:mof_type] == 'PSCredential'
606
+ params[:Property][name] = case property_hash[:mof_type]
607
+ when 'PSCredential'
552
608
  # format can't unwrap Sensitive strings nested in arbitrary hashes/etc, so make
553
609
  # the Credential hash interpolable as it will be replaced by a variable reference.
554
610
  {
555
611
  'user' => property_hash[:value]['user'],
556
612
  'password' => escape_quotes(property_hash[:value]['password'].unwrap)
557
613
  }
614
+ when 'DateTime'
615
+ # These have to be handled specifically because they rely on the *Puppet* DateTime,
616
+ # not a generic ruby data type (and so can't go in the shared util in ruby-pwsh)
617
+ "[DateTime]#{property_hash[:value].format('%FT%T%z')}"
558
618
  else
559
619
  property_hash[:value]
560
620
  end
561
621
  end
562
622
  params_block = interpolate_variables("$InvokeParams = #{format(params)}")
623
+ # Move the Apostrophe for DateTime declarations
624
+ params_block = params_block.gsub("'[DateTime]", "[DateTime]'")
625
+ # HACK: Handle intentionally empty arrays - need to strongly type them because
626
+ # CIM instances do not do a consistent job of casting an empty array properly.
627
+ empty_array_parameters = resource[:parameters].select { |_k, v| v[:value].empty? }
628
+ empty_array_parameters.each do |name, properties|
629
+ param_block_name = name.to_s.gsub(/^dsc_/, '')
630
+ params_block = params_block.gsub("#{param_block_name} = @()", "#{param_block_name} = [#{properties[:mof_type]}]@()")
631
+ end
563
632
  # HACK: make CIM instances work:
564
633
  resource[:parameters].select { |_key, hash| hash[:mof_is_embedded] && hash[:mof_type] =~ /\[\]/ }.each do |_property_name, property_hash|
565
634
  formatted_property_hash = interpolate_variables(format(property_hash[:value]))
@@ -577,11 +646,11 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
577
646
  def ps_script_content(resource)
578
647
  template_path = File.expand_path('../', __FILE__)
579
648
  # Defines the helper functions
580
- functions = File.new(template_path + '/invoke_dsc_resource_functions.ps1').read
649
+ functions = File.new("#{template_path}/invoke_dsc_resource_functions.ps1").read
581
650
  # Defines the response hash and the runtime settings
582
- preamble = File.new(template_path + '/invoke_dsc_resource_preamble.ps1').read
651
+ preamble = File.new("#{template_path}/invoke_dsc_resource_preamble.ps1").read
583
652
  # The postscript defines the invocation error and result handling; expects `$InvokeParams` to be defined
584
- postscript = File.new(template_path + '/invoke_dsc_resource_postscript.ps1').read
653
+ postscript = File.new("#{template_path}/invoke_dsc_resource_postscript.ps1").read
585
654
  # The blocks define the variables to define for the postscript.
586
655
  credential_block = prepare_credentials(resource)
587
656
  cim_instances_block = prepare_cim_instances(resource)
@@ -589,8 +658,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
589
658
  # clean them out of the temporary cache now that they're not needed; failure to do so can goof up future executions in this run
590
659
  clear_instantiated_variables!
591
660
 
592
- content = [functions, preamble, credential_block, cim_instances_block, parameters_block, postscript].join("\n")
593
- content
661
+ [functions, preamble, credential_block, cim_instances_block, parameters_block, postscript].join("\n")
594
662
  end
595
663
 
596
664
  # Convert a Puppet/Ruby value into a PowerShell representation. Requires some slight additional
@@ -614,15 +682,16 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
614
682
  # @param value [Object] The object to unwrap sensitive data inside of
615
683
  # @return [Object] The object with any sensitive strings unwrapped and annotated
616
684
  def unwrap(value)
617
- if value.class.name == 'Puppet::Pops::Types::PSensitiveType::Sensitive'
685
+ case value
686
+ when Puppet::Pops::Types::PSensitiveType::Sensitive
618
687
  "#{value.unwrap}#PuppetSensitive"
619
- elsif value.class.name == 'Hash'
688
+ when Hash
620
689
  unwrapped = {}
621
690
  value.each do |k, v|
622
691
  unwrapped[k] = unwrap(v)
623
692
  end
624
693
  unwrapped
625
- elsif value.class.name == 'Array'
694
+ when Array
626
695
  unwrapped = []
627
696
  value.each do |v|
628
697
  unwrapped << unwrap(v)
@@ -68,6 +68,10 @@ Function ConvertTo-CanonicalResult {
68
68
  $Value = $null
69
69
  }
70
70
  }
71
+ elseif ($Result.$PropertyName.GetType().Name -match 'DateTime') {
72
+ # Handle DateTimes especially since they're an edge case
73
+ $Value = Get-Date $Result.$PropertyName -UFormat "%Y-%m-%dT%H:%M:%S%Z"
74
+ }
71
75
  else {
72
76
  # Looks like a nested CIM instance, recurse if we're not too deep in already.
73
77
  $RecursionLevel++
@@ -14,10 +14,10 @@ require 'logger'
14
14
  module Pwsh
15
15
  # Standard errors
16
16
  class Error < StandardError; end
17
+
17
18
  # Create an instance of a PowerShell host and manage execution of PowerShell code inside that host.
18
19
  class Manager
19
- attr_reader :powershell_command
20
- attr_reader :powershell_arguments
20
+ attr_reader :powershell_command, :powershell_arguments
21
21
 
22
22
  # We actually want this to be a class variable.
23
23
  @@instances = {} # rubocop:disable Style/ClassVars
@@ -70,7 +70,7 @@ module Pwsh
70
70
  def self.win32console_enabled?
71
71
  @win32console_enabled ||= defined?(Win32) &&
72
72
  defined?(Win32::Console) &&
73
- Win32::Console.class == Class
73
+ Win32::Console.instance_of?(Class)
74
74
  end
75
75
 
76
76
  # TODO: This thing isn't called anywhere and the variable it sets is never referenced...
@@ -117,16 +117,16 @@ module Pwsh
117
117
  #
118
118
  # @return [String] representation of the value for interpolation
119
119
  def format_powershell_value(object)
120
- if %i[true false].include?(object) || %w[trueclass falseclass].include?(object.class.name.downcase) # rubocop:disable Lint/BooleanSymbol
120
+ if %i[true false].include?(object) || %w[trueclass falseclass].include?(object.class.name.downcase)
121
121
  "$#{object}"
122
- elsif object.class.name == 'Symbol' || object.class.ancestors.include?(Numeric)
122
+ elsif object.instance_of?(Symbol) || object.class.ancestors.include?(Numeric)
123
123
  object.to_s
124
- elsif object.class.name == 'String'
124
+ elsif object.instance_of?(String)
125
125
  "'#{escape_quotes(object)}'"
126
- elsif object.class.name == 'Array'
127
- '@(' + object.collect { |item| format_powershell_value(item) }.join(', ') + ')'
128
- elsif object.class.name == 'Hash'
129
- '@{' + object.collect { |k, v| format_powershell_value(k) + ' = ' + format_powershell_value(v) }.join('; ') + '}'
126
+ elsif object.instance_of?(Array)
127
+ "@(#{object.collect { |item| format_powershell_value(item) }.join(', ')})"
128
+ elsif object.instance_of?(Hash)
129
+ "@{#{object.collect { |k, v| "#{format_powershell_value(k)} = #{format_powershell_value(v)}" }.join('; ')}}"
130
130
  else
131
131
  raise "unsupported type #{object.class} of value '#{object}'"
132
132
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Pwsh
4
4
  # The version of the ruby-pwsh gem
5
- VERSION = '0.5.1'
5
+ VERSION = '0.6.0'
6
6
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "puppetlabs-pwshlib",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "author": "puppetlabs",
5
5
  "summary": "Provide library code for interoperating with PowerShell.",
6
6
  "license": "MIT",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-pwsh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-25 00:00:00.000000000 Z
11
+ date: 2020-11-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: PowerShell code manager for ruby.
14
14
  email: