ruby-pwsh 0.10.2 → 0.11.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +16 -38
- data/README.md +29 -25
- data/lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb +23 -20
- data/lib/pwsh/version.rb +1 -1
- data/lib/pwsh/windows_powershell.rb +2 -2
- data/lib/pwsh.rb +31 -34
- data/spec/acceptance/dsc/basic.rb +209 -0
- data/spec/acceptance/dsc/cim_instances.rb +81 -0
- data/spec/acceptance/dsc/class.rb +129 -0
- data/spec/acceptance/dsc/complex.rb +139 -0
- data/spec/acceptance/support/setup_winrm.ps1 +6 -0
- data/spec/exit-27.ps1 +1 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/unit/puppet/provider/dsc_base_provider/dsc_base_provider_spec.rb +2084 -0
- data/spec/unit/pwsh/util_spec.rb +293 -0
- data/spec/unit/pwsh/version_spec.rb +10 -0
- data/spec/unit/pwsh/windows_powershell_spec.rb +121 -0
- data/spec/unit/pwsh_spec.rb +821 -0
- metadata +18 -22
- data/.gitattributes +0 -2
- data/.github/workflows/ci.yml +0 -109
- data/.gitignore +0 -23
- data/.pmtignore +0 -21
- data/.rspec +0 -3
- data/CHANGELOG.md +0 -204
- data/CODEOWNERS +0 -2
- data/CONTRIBUTING.md +0 -155
- data/DESIGN.md +0 -70
- data/Gemfile +0 -54
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -188
- data/design-comms.png +0 -0
- data/metadata.json +0 -82
- data/pwshlib.md +0 -92
- data/ruby-pwsh.gemspec +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3357d3c933ddf7b2ecce5d3be1958e9d6aaf6b9966f0b4e4d1f45c40f13963c
|
4
|
+
data.tar.gz: 59e415fc5c1804e09c481bfb6a3c8aa58a7b9767dd04f0a31b3f65c6986ab9c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd03304fb4b624296d2770b33296464ca7968e0d1f3b5ee31ddad4490892fcefeca55359447dad2389fb7123bb6b95f42d287f1dd29c50a4ccdd0244338d862e
|
7
|
+
data.tar.gz: 0a4c863c752858ee96975d0278220ca914ba1ea5e02bddd57ab8c798b23c775c2914e81a67482c3bfa5b54af64e0f55946e99af0c2d0fef2b7470c3aec379c86
|
data/.rubocop.yml
CHANGED
@@ -1,41 +1,19 @@
|
|
1
|
-
|
2
|
-
Enabled: false
|
3
|
-
Layout/EndOfLine:
|
4
|
-
Description: Don't enforce CRLF on Windows.
|
5
|
-
Enabled: false
|
6
|
-
Metrics/LineLength:
|
7
|
-
Description: People have wide screens, use them.
|
8
|
-
Max: 200
|
9
|
-
Metrics/BlockLength:
|
10
|
-
Enabled: false
|
11
|
-
Metrics/MethodLength:
|
12
|
-
Enabled: false
|
13
|
-
Metrics/ClassLength:
|
14
|
-
Enabled: false
|
15
|
-
Metrics/PerceivedComplexity:
|
16
|
-
Enabled: false
|
17
|
-
Metrics/CyclomaticComplexity:
|
18
|
-
Enabled: false
|
19
|
-
Metrics/AbcSize:
|
20
|
-
Enabled: false
|
1
|
+
inherit_from: .rubocop_todo.yml
|
21
2
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
Naming/FileName:
|
3
|
+
require:
|
4
|
+
- rubocop-performance
|
5
|
+
- rubocop-rspec
|
6
|
+
|
7
|
+
AllCops:
|
28
8
|
Exclude:
|
29
|
-
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
9
|
+
- Gemfile
|
10
|
+
- Rakefile
|
11
|
+
- spec/fixtures/**/*
|
12
|
+
- vendor/bundle/**/*
|
13
|
+
NewCops: enable
|
14
|
+
SuggestExtensions: false
|
15
|
+
TargetRubyVersion: '2.7'
|
16
|
+
|
17
|
+
# Disabled
|
36
18
|
Style/ClassAndModuleChildren:
|
37
|
-
|
38
|
-
- lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb
|
39
|
-
Style/ClassVars:
|
40
|
-
Exclude:
|
41
|
-
- lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb
|
19
|
+
Enabled: false
|
data/README.md
CHANGED
@@ -74,47 +74,51 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
74
74
|
|
75
75
|
Steps to release an update to the gem and module include:
|
76
76
|
|
77
|
-
1.
|
77
|
+
1. From main, checkout a new working branch for the release prep (where xyz is the appropriate version, sans periods):
|
78
78
|
```bash
|
79
|
-
git
|
79
|
+
git checkout -b maint-release_prep_xyz
|
80
80
|
```
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
1. Update the version in `lib/pwsh/version.rb` and `metadata.json` to the appropriate version for the new release.
|
86
|
-
1. Run the changelog update task (make sure to verify the changelog, correctly tagging PRs as needed):
|
81
|
+
|
82
|
+
2. Update the version in `lib/pwsh/version.rb` and `metadata.json` to the appropriate version for the new release.
|
83
|
+
|
84
|
+
3. Run the changelog update task (make sure to verify the changelog, correctly tagging PRs as needed):
|
87
85
|
```bash
|
88
86
|
bundle exec rake changelog
|
89
87
|
```
|
90
|
-
|
91
|
-
|
88
|
+
|
89
|
+
4. Commit your changes with a short, sensible commit message, like:
|
90
|
+
```bash
|
92
91
|
git add lib/pwsh/version.rb
|
93
92
|
git add metadata.json
|
94
93
|
git add CHANGELOG.md
|
95
94
|
git commit -m '(MAINT) Prep for x.y.z release'
|
96
95
|
```
|
97
|
-
|
98
|
-
|
99
|
-
git push -u origin maint/release/prep-xyz
|
100
|
-
```
|
101
|
-
1. Ensure tests pass and the code is merged to `release`.
|
102
|
-
1. Grab the commit hash from the merge commit on release, use that as the tag for the version (replacing `x.y.z` with the appropriate version and `commithash` with the relevant one), then push the tags to upstream:
|
96
|
+
|
97
|
+
5. Push your changes and submit a pull request for review _against main:
|
103
98
|
```bash
|
104
|
-
|
99
|
+
git push -u origin maint_release_prep_xyz
|
105
100
|
```
|
106
|
-
|
101
|
+
|
102
|
+
6. Ensure tests pass and the code is merged to `main`.
|
103
|
+
|
104
|
+
7. Once the release_prep PR has been merged, checkout main and pull down the latests changes.
|
107
105
|
```bash
|
108
|
-
|
109
|
-
|
106
|
+
git checkout main
|
107
|
+
git pull
|
110
108
|
```
|
111
|
-
|
112
|
-
|
109
|
+
|
110
|
+
8. Assuming that the release_prep merge commit is at the HEAD of main we can simply create and push a tag as follows (replacing xyz with the appropriate version).
|
113
111
|
```bash
|
114
|
-
|
112
|
+
git tag -a xyx -m "Release xyz"
|
113
|
+
git push --follow-tags
|
115
114
|
```
|
116
|
-
|
117
|
-
|
115
|
+
|
116
|
+
9. Execute the publish workflow. This will:
|
117
|
+
- Create a GitHub release
|
118
|
+
- Build and publish the Gem
|
119
|
+
- Build and publish the Puppet module
|
120
|
+
|
121
|
+
10. Finally check that the expected versions are present on rubygems.org and the Forge.
|
118
122
|
|
119
123
|
## Known Issues
|
120
124
|
|
@@ -79,13 +79,16 @@ class Puppet::Provider::DscBaseProvider
|
|
79
79
|
end
|
80
80
|
downcased_result = recursively_downcase(canonicalized)
|
81
81
|
downcased_resource = recursively_downcase(r)
|
82
|
+
# Ensure that metaparameters are preserved when we canonicalize the resource.
|
83
|
+
metaparams = downcased_resource.select { |key, _value| Puppet::Type.metaparam?(key) }
|
84
|
+
canonicalized.merge!(metaparams) unless metaparams.nil?
|
82
85
|
downcased_result.each do |key, value|
|
83
86
|
# Canonicalize to the manifest value unless the downcased strings match and the attribute is not an enum:
|
84
87
|
# - When the values don't match at all, the manifest value is desired;
|
85
88
|
# - When the values match case insensitively but the attribute is an enum, prefer the casing of the manifest enum.
|
86
89
|
# - When the values match case insensitively and the attribute is not an enum, prefer the casing from invoke_get_method
|
87
90
|
canonicalized[key] = r[key] unless same?(value, downcased_resource[key]) && !enum_attributes(context).include?(key)
|
88
|
-
canonicalized.delete(key) unless downcased_resource.
|
91
|
+
canonicalized.delete(key) unless downcased_resource.key?(key)
|
89
92
|
end
|
90
93
|
# Cache the actually canonicalized resource separately
|
91
94
|
@@cached_canonicalized_resource << canonicalized.dup
|
@@ -131,7 +134,7 @@ class Puppet::Provider::DscBaseProvider
|
|
131
134
|
(mandatory_get_attributes(context) - namevar_attributes(context)).include?(attribute)
|
132
135
|
end
|
133
136
|
# If dsc_psdscrunascredential was specified, re-add it here.
|
134
|
-
mandatory_properties[:dsc_psdscrunascredential] = canonicalized_resource[:dsc_psdscrunascredential] if canonicalized_resource.
|
137
|
+
mandatory_properties[:dsc_psdscrunascredential] = canonicalized_resource[:dsc_psdscrunascredential] if canonicalized_resource.key?(:dsc_psdscrunascredential)
|
135
138
|
end
|
136
139
|
names.collect do |name|
|
137
140
|
name = { name: name } if name.is_a? String
|
@@ -250,7 +253,7 @@ class Puppet::Provider::DscBaseProvider
|
|
250
253
|
|
251
254
|
begin
|
252
255
|
data = JSON.parse(output)
|
253
|
-
rescue => e
|
256
|
+
rescue StandardError => e
|
254
257
|
context.err(e)
|
255
258
|
return nil
|
256
259
|
end
|
@@ -260,7 +263,7 @@ class Puppet::Provider::DscBaseProvider
|
|
260
263
|
unless error.nil? || error.empty?
|
261
264
|
# NB: We should have a way to stop processing this resource *now* without blowing up the whole Puppet run
|
262
265
|
# Raising an error stops processing but blows things up while context.err alerts but continues to process
|
263
|
-
if error
|
266
|
+
if error.include?('Logon failure: the user has not been granted the requested logon type at this computer')
|
264
267
|
logon_error = "PSDscRunAsCredential account specified (#{name_hash[:dsc_psdscrunascredential]['user']}) does not have appropriate logon rights; are they an administrator?"
|
265
268
|
name_hash[:name].nil? ? context.err(logon_error) : context.err(name_hash[:name], logon_error)
|
266
269
|
@@logon_failures << name_hash[:dsc_psdscrunascredential].dup
|
@@ -340,19 +343,19 @@ class Puppet::Provider::DscBaseProvider
|
|
340
343
|
end
|
341
344
|
# PowerShell does not distinguish between a return of empty array/string
|
342
345
|
# and null but Puppet does; revert to those values if specified.
|
343
|
-
data[type_key] = [] if data[type_key].nil? && query_props.
|
346
|
+
data[type_key] = [] if data[type_key].nil? && query_props.key?(type_key) && query_props[type_key].is_a?(Array)
|
344
347
|
end
|
345
348
|
# If a resource is found, it's present, so refill this Puppet-only key
|
346
|
-
data
|
349
|
+
data[:name] = name_hash[:name]
|
347
350
|
|
348
351
|
# Have to check for this to avoid a weird canonicalization warning
|
349
352
|
# The Resource API calls canonicalize against the current state which
|
350
353
|
# will lead to dsc_ensure being set to absent in the name_hash even if
|
351
354
|
# it was set to present in the manifest
|
352
|
-
name_hash_has_nil_keys = name_hash.
|
355
|
+
name_hash_has_nil_keys = name_hash.count { |_k, v| v.nil? }.positive?
|
353
356
|
# We want to throw away all of the empty keys if and only if the manifest
|
354
357
|
# declaration is for an absent resource and the resource is actually absent
|
355
|
-
data.
|
358
|
+
data.compact! if data[:dsc_ensure] == 'Absent' && name_hash[:dsc_ensure] == 'Absent' && !name_hash_has_nil_keys
|
356
359
|
|
357
360
|
# Sort the return for order-insensitive nested enumerable comparison:
|
358
361
|
data = recursively_sort(data)
|
@@ -448,7 +451,7 @@ class Puppet::Provider::DscBaseProvider
|
|
448
451
|
# path to allow multiple modules to with shared dsc_resources to be installed side by side
|
449
452
|
# The old vendored_modules_path: puppet_x/dsc_resources
|
450
453
|
# The new vendored_modules_path: puppet_x/<module_name>/dsc_resources
|
451
|
-
root_module_path = load_path.
|
454
|
+
root_module_path = load_path.find { |path| path.match?(%r{#{puppetize_name(module_name)}/lib}) }
|
452
455
|
vendored_path = if root_module_path.nil?
|
453
456
|
File.expand_path(Pathname.new(__FILE__).dirname + '../../../' + "puppet_x/#{puppetize_name(module_name)}/dsc_resources") # rubocop:disable Style/StringConcatenation
|
454
457
|
else
|
@@ -498,7 +501,7 @@ class Puppet::Provider::DscBaseProvider
|
|
498
501
|
# @return [String] a uuid with underscores instead of dashes.
|
499
502
|
def random_variable_name
|
500
503
|
# PowerShell variables can't include dashes
|
501
|
-
SecureRandom.uuid.
|
504
|
+
SecureRandom.uuid.tr('-', '_')
|
502
505
|
end
|
503
506
|
|
504
507
|
# Return a Hash containing all of the variables defined for instantiation as well as the Ruby hash for their
|
@@ -637,7 +640,7 @@ class Puppet::Provider::DscBaseProvider
|
|
637
640
|
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
638
641
|
# @return [Array] returns an array of attribute names as symbols which are enums
|
639
642
|
def enum_attributes(context)
|
640
|
-
context.type.attributes.select { |_name, properties| properties[:type].
|
643
|
+
context.type.attributes.select { |_name, properties| properties[:type].include?('Enum[') }.keys
|
641
644
|
end
|
642
645
|
|
643
646
|
# Look through a fully formatted string, replacing all instances where a value matches the formatted properties
|
@@ -667,7 +670,7 @@ class Puppet::Provider::DscBaseProvider
|
|
667
670
|
def munge_psmodulepath(resource)
|
668
671
|
return unless resource[:dscmeta_resource_implementation] == 'Class'
|
669
672
|
|
670
|
-
vendor_path = resource[:vendored_modules_path].
|
673
|
+
vendor_path = resource[:vendored_modules_path].tr('/', '\\')
|
671
674
|
<<~MUNGE_PSMODULEPATH.strip
|
672
675
|
$UnmungedPSModulePath = [System.Environment]::GetEnvironmentVariable('PSModulePath','machine')
|
673
676
|
$MungedPSModulePath = $env:PSModulePath + ';#{vendor_path}'
|
@@ -728,7 +731,7 @@ class Puppet::Provider::DscBaseProvider
|
|
728
731
|
# name = property_name.to_s.gsub(/^dsc_/, '').to_sym
|
729
732
|
# Process nested CIM instances first as those neeed to be passed to higher-order
|
730
733
|
# instances and must therefore be declared before they must be referenced
|
731
|
-
cim_instance_hashes = nested_cim_instances(property_hash[:value]).flatten.
|
734
|
+
cim_instance_hashes = nested_cim_instances(property_hash[:value]).flatten.compact
|
732
735
|
# Sometimes the instances are an empty array
|
733
736
|
unless cim_instance_hashes.count.zero?
|
734
737
|
cim_instance_hashes.each do |instance|
|
@@ -740,7 +743,7 @@ class Puppet::Provider::DscBaseProvider
|
|
740
743
|
end
|
741
744
|
end
|
742
745
|
# We have to handle arrays of CIM instances slightly differently
|
743
|
-
if property_hash[:mof_type]
|
746
|
+
if /\[\]$/.match?(property_hash[:mof_type])
|
744
747
|
class_name = property_hash[:mof_type].gsub('[]', '')
|
745
748
|
property_hash[:value].each do |hash|
|
746
749
|
variable_name = random_variable_name
|
@@ -844,7 +847,7 @@ class Puppet::Provider::DscBaseProvider
|
|
844
847
|
params_block = params_block.gsub("#{param_block_name} = @()", "#{param_block_name} = [#{properties[:mof_type]}]@()")
|
845
848
|
end
|
846
849
|
# HACK: make CIM instances work:
|
847
|
-
resource[:parameters].select { |_key, hash| hash[:mof_is_embedded] && hash[:mof_type]
|
850
|
+
resource[:parameters].select { |_key, hash| hash[:mof_is_embedded] && hash[:mof_type].include?('[]') }.each do |_property_name, property_hash|
|
848
851
|
formatted_property_hash = interpolate_variables(format(property_hash[:value]))
|
849
852
|
params_block = params_block.gsub(formatted_property_hash, "[CimInstance[]]#{formatted_property_hash}")
|
850
853
|
end
|
@@ -858,7 +861,7 @@ class Puppet::Provider::DscBaseProvider
|
|
858
861
|
# @param resource [Hash] a hash with the information needed to run `Invoke-DscResource`
|
859
862
|
# @return [String] A string representing the PowerShell script which will invoke the DSC Resource.
|
860
863
|
def ps_script_content(resource)
|
861
|
-
template_path = File.expand_path(
|
864
|
+
template_path = File.expand_path(__dir__)
|
862
865
|
# Defines the helper functions
|
863
866
|
functions = File.new("#{template_path}/invoke_dsc_resource_functions.ps1").read
|
864
867
|
# Defines the response hash and the runtime settings
|
@@ -885,7 +888,7 @@ class Puppet::Provider::DscBaseProvider
|
|
885
888
|
def format(value)
|
886
889
|
Pwsh::Util.format_powershell_value(value)
|
887
890
|
rescue RuntimeError => e
|
888
|
-
raise unless e.message
|
891
|
+
raise unless e.message.include?('Sensitive [value redacted]')
|
889
892
|
|
890
893
|
Pwsh::Util.format_powershell_value(unwrap(value))
|
891
894
|
end
|
@@ -945,12 +948,12 @@ class Puppet::Provider::DscBaseProvider
|
|
945
948
|
def handle_secrets(text, replacement, error_message)
|
946
949
|
# Every secret unwrapped in this module will unwrap as "'secret#{SECRET_POSTFIX}'"
|
947
950
|
# Currently, no known resources specify a SecureString instead of a PSCredential object.
|
948
|
-
return text unless
|
951
|
+
return text unless /#{Regexp.quote(SECRET_POSTFIX)}/.match?(text)
|
949
952
|
|
950
953
|
# In order to reduce time-to-parse, look at each line individually and *only* attempt
|
951
954
|
# to substitute if a naive match for the secret postfix is found on the line.
|
952
955
|
modified_text = text.split("\n").map do |line|
|
953
|
-
if
|
956
|
+
if /#{Regexp.quote(SECRET_POSTFIX)}/.match?(line)
|
954
957
|
line.gsub(SECRET_DATA_REGEX, replacement)
|
955
958
|
else
|
956
959
|
line
|
@@ -960,7 +963,7 @@ class Puppet::Provider::DscBaseProvider
|
|
960
963
|
modified_text = modified_text.join("\n")
|
961
964
|
|
962
965
|
# Something has gone wrong, error loudly
|
963
|
-
raise error_message if
|
966
|
+
raise error_message if /#{Regexp.quote(SECRET_POSTFIX)}/.match?(modified_text)
|
964
967
|
|
965
968
|
modified_text
|
966
969
|
end
|
data/lib/pwsh/version.rb
CHANGED
@@ -82,7 +82,7 @@ if Pwsh::Util.on_windows?
|
|
82
82
|
HKLM.open(PS_ONE_REG_PATH, ACCESS_TYPE) do |reg|
|
83
83
|
version = reg[REG_KEY]
|
84
84
|
end
|
85
|
-
rescue
|
85
|
+
rescue StandardError
|
86
86
|
version = nil
|
87
87
|
end
|
88
88
|
version
|
@@ -98,7 +98,7 @@ if Pwsh::Util.on_windows?
|
|
98
98
|
HKLM.open(PS_THREE_REG_PATH, ACCESS_TYPE) do |reg|
|
99
99
|
version = reg[REG_KEY]
|
100
100
|
end
|
101
|
-
rescue
|
101
|
+
rescue StandardError
|
102
102
|
version = nil
|
103
103
|
end
|
104
104
|
version
|
data/lib/pwsh.rb
CHANGED
@@ -55,7 +55,7 @@ module Pwsh
|
|
55
55
|
# ignore any errors trying to tear down this unusable instance
|
56
56
|
begin
|
57
57
|
manager.exit unless manager.nil? # rubocop:disable Style/SafeNavigation
|
58
|
-
rescue
|
58
|
+
rescue StandardError
|
59
59
|
nil
|
60
60
|
end
|
61
61
|
@@instances[key] = Manager.new(cmd, args, options)
|
@@ -151,7 +151,7 @@ module Pwsh
|
|
151
151
|
UNIXSocket.new(pipe_path)
|
152
152
|
end
|
153
153
|
break
|
154
|
-
rescue
|
154
|
+
rescue StandardError
|
155
155
|
sleep sleep_interval
|
156
156
|
end
|
157
157
|
end
|
@@ -208,7 +208,6 @@ module Pwsh
|
|
208
208
|
out[:stderr] = out[:stderr].nil? ? [] : [out[:stderr]]
|
209
209
|
out[:stderr] += err unless err.nil?
|
210
210
|
out[:native_stdout] = native_stdout
|
211
|
-
|
212
211
|
out
|
213
212
|
end
|
214
213
|
|
@@ -224,7 +223,7 @@ module Pwsh
|
|
224
223
|
# rather than expecting the pipe.close to terminate it
|
225
224
|
begin
|
226
225
|
write_pipe(pipe_command(:exit)) unless @pipe.closed?
|
227
|
-
rescue
|
226
|
+
rescue StandardError
|
228
227
|
nil
|
229
228
|
end
|
230
229
|
|
@@ -245,8 +244,8 @@ module Pwsh
|
|
245
244
|
# @return [String] full path to the bootstrap template
|
246
245
|
def self.template_path
|
247
246
|
# A PowerShell -File compatible path to bootstrap the instance
|
248
|
-
path = File.expand_path('
|
249
|
-
path = File.join(path, 'init.ps1').
|
247
|
+
path = File.expand_path('templates', __dir__)
|
248
|
+
path = File.join(path, 'init.ps1').tr('/', '\\')
|
250
249
|
"\"#{path}\""
|
251
250
|
end
|
252
251
|
|
@@ -268,7 +267,7 @@ module Pwsh
|
|
268
267
|
|
269
268
|
# Lower bound protection. The polling resolution is only 50ms.
|
270
269
|
timeout_ms = 50 if timeout_ms < 50
|
271
|
-
rescue
|
270
|
+
rescue StandardError
|
272
271
|
timeout_ms = 300 * 1000
|
273
272
|
end
|
274
273
|
|
@@ -308,17 +307,17 @@ module Pwsh
|
|
308
307
|
|
309
308
|
# PS Side expects Invoke-PowerShellUserCode is always the return value here
|
310
309
|
# TODO: Refactor to use <<~ as soon as we can :sob:
|
311
|
-
|
312
|
-
$params = @{
|
313
|
-
|
314
|
-
#{powershell_code}
|
315
|
-
'@
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
}
|
320
|
-
|
321
|
-
Invoke-PowerShellUserCode @params
|
310
|
+
<<~CODE
|
311
|
+
$params = @{
|
312
|
+
Code = @'
|
313
|
+
#{powershell_code}
|
314
|
+
'@
|
315
|
+
TimeoutMilliseconds = #{timeout_ms}
|
316
|
+
WorkingDirectory = "#{working_dir}"
|
317
|
+
AdditionalEnvironmentVariables = #{additional_environment_variables}
|
318
|
+
}
|
319
|
+
|
320
|
+
Invoke-PowerShellUserCode @params
|
322
321
|
CODE
|
323
322
|
end
|
324
323
|
|
@@ -336,10 +335,10 @@ Invoke-PowerShellUserCode @params
|
|
336
335
|
#
|
337
336
|
# @return [String] the absolute path to the PowerShell executable. Returns 'powershell.exe' if no more specific path found.
|
338
337
|
def self.powershell_path
|
339
|
-
if File.exist?("#{ENV
|
340
|
-
"#{ENV
|
341
|
-
elsif File.exist?("#{ENV
|
342
|
-
"#{ENV
|
338
|
+
if File.exist?("#{ENV.fetch('SYSTEMROOT', nil)}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe")
|
339
|
+
"#{ENV.fetch('SYSTEMROOT', nil)}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe"
|
340
|
+
elsif File.exist?("#{ENV.fetch('SYSTEMROOT', nil)}\\system32\\WindowsPowershell\\v1.0\\powershell.exe")
|
341
|
+
"#{ENV.fetch('SYSTEMROOT', nil)}\\system32\\WindowsPowershell\\v1.0\\powershell.exe"
|
343
342
|
else
|
344
343
|
'powershell.exe'
|
345
344
|
end
|
@@ -353,7 +352,7 @@ Invoke-PowerShellUserCode @params
|
|
353
352
|
# Convert all the key names to upcase so we can be sure to find PATH etc.
|
354
353
|
# Also while ruby can have difficulty changing the case of some UTF8 characters, we're
|
355
354
|
# only going to use plain ASCII names so this is safe.
|
356
|
-
current_path = Pwsh::Util.on_windows? ? ENV.select { |k, _| k.
|
355
|
+
current_path = Pwsh::Util.on_windows? ? ENV.select { |k, _| k.casecmp('PATH').zero? }.values[0] : ENV.fetch('PATH', nil)
|
357
356
|
current_path = '' if current_path.nil?
|
358
357
|
|
359
358
|
# Prefer any additional paths
|
@@ -367,10 +366,10 @@ Invoke-PowerShellUserCode @params
|
|
367
366
|
# TODO: What about PS 8?
|
368
367
|
# TODO: Need to check on French/Turkish windows if ENV['PROGRAMFILES'] parses UTF8 names correctly
|
369
368
|
# TODO: Need to ensure ENV['PROGRAMFILES'] is case insensitive, i.e. ENV['PROGRAMFiles'] should also resolve on Windows
|
370
|
-
search_paths += ";#{ENV
|
371
|
-
";#{ENV
|
372
|
-
";#{ENV
|
373
|
-
";#{ENV
|
369
|
+
search_paths += ";#{ENV.fetch('PROGRAMFILES', nil)}\\PowerShell\\6" \
|
370
|
+
";#{ENV.fetch('PROGRAMFILES(X86)', nil)}\\PowerShell\\6" \
|
371
|
+
";#{ENV.fetch('PROGRAMFILES', nil)}\\PowerShell\\7" \
|
372
|
+
";#{ENV.fetch('PROGRAMFILES(X86)', nil)}\\PowerShell\\7"
|
374
373
|
end
|
375
374
|
raise 'No paths discovered to search for Powershell!' if search_paths.split(File::PATH_SEPARATOR).empty?
|
376
375
|
|
@@ -433,7 +432,7 @@ Invoke-PowerShellUserCode @params
|
|
433
432
|
# as this resolves to a HANDLE and then calls the Windows API
|
434
433
|
!stream.stat.nil?
|
435
434
|
# Any exceptions mean the stream is dead
|
436
|
-
rescue
|
435
|
+
rescue StandardError
|
437
436
|
false
|
438
437
|
end
|
439
438
|
|
@@ -497,9 +496,7 @@ Invoke-PowerShellUserCode @params
|
|
497
496
|
#
|
498
497
|
# @return nil
|
499
498
|
def write_pipe(input)
|
500
|
-
|
501
|
-
# not write - otherwise, the pipe breaks after writing 1024 bytes.
|
502
|
-
written = @pipe.syswrite(input)
|
499
|
+
written = @pipe.write(input)
|
503
500
|
@pipe.flush
|
504
501
|
|
505
502
|
if written != input.length # rubocop:disable Style/GuardClause
|
@@ -541,7 +538,7 @@ Invoke-PowerShellUserCode @params
|
|
541
538
|
read_from_pipe(pipe, 0) { |s| output << s } while self.class.readable?(pipe)
|
542
539
|
|
543
540
|
# String has been binary up to this point, so force UTF-8 now
|
544
|
-
output == [] ? [] : [output.join
|
541
|
+
output == [] ? [] : [output.join.force_encoding(Encoding::UTF_8)]
|
545
542
|
end
|
546
543
|
|
547
544
|
# Open threads and pipes to read stdout and stderr from the PowerShell manager,
|
@@ -592,8 +589,8 @@ Invoke-PowerShellUserCode @params
|
|
592
589
|
|
593
590
|
[
|
594
591
|
output,
|
595
|
-
stdout == [] ? nil : stdout.join
|
596
|
-
stderr_reader.value
|
592
|
+
stdout == [] ? nil : stdout.join, # native stdout
|
593
|
+
stderr_reader.value # native stderr
|
597
594
|
]
|
598
595
|
ensure
|
599
596
|
# Failsafe if the prior unlock was never reached / Mutex wasn't unlocked
|