ruby-pwsh 0.10.2 → 0.11.0.rc.1
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/.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
|