kitchen-pester 0.11.0 → 1.1.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 +4 -4
- data/Gemfile +3 -3
- data/Rakefile +1 -3
- data/kitchen-pester.gemspec +1 -2
- data/lib/kitchen/verifier/pester.rb +509 -163
- data/lib/kitchen/verifier/pester_version.rb +1 -1
- data/lib/support/modules/PesterUtil/PesterUtil.psm1 +158 -0
- metadata +7 -7
- data/lib/support/powershell/PesterUtil/PesterUtil.psm1 +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f323e411287a34d2008dc58aba57ced9995e39511e77207031606c01a5b258d
|
|
4
|
+
data.tar.gz: 45031fdce5ecef091be46aa44e3934afe21e4233d3d1ba469ba2c026105be145
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f1bfc59bc045bd3fe2c3313d6f0fb202f7a8f5887017251610546df15b0b1a644fbd75b4222bc7b9fdad80cd9a97faf4c0abd65eee36c8b54c3aeccc14ed43fd
|
|
7
|
+
data.tar.gz: 64f70c980a546c5d930f26bbea48e449f4da064feac617bffddd3fbef77649324e7204754af8fdacb41d79594e03bfcbcc779cf90f12b0766f519758070f36f1
|
data/Gemfile
CHANGED
|
@@ -11,12 +11,12 @@ group :integration do
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
group :changelog do
|
|
14
|
-
gem "github_changelog_generator", "1.
|
|
14
|
+
gem "github_changelog_generator", "1.16.4"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
group :debug do
|
|
18
|
-
gem "pry"
|
|
19
|
-
gem "pry-byebug"
|
|
18
|
+
gem "pry", "~>0.13.1"
|
|
19
|
+
gem "pry-byebug", "~>3.9.0"
|
|
20
20
|
gem "pry-stack_explorer"
|
|
21
21
|
end
|
|
22
22
|
|
data/Rakefile
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
|
-
|
|
3
1
|
require "bundler/gem_tasks"
|
|
4
2
|
|
|
5
3
|
require "rake/testtask"
|
|
@@ -25,7 +23,7 @@ desc "Run all quality tasks"
|
|
|
25
23
|
task quality: :style
|
|
26
24
|
|
|
27
25
|
begin
|
|
28
|
-
require "yard"
|
|
26
|
+
require "yard" unless defined?(YARD)
|
|
29
27
|
YARD::Rake::YardocTask.new
|
|
30
28
|
rescue LoadError
|
|
31
29
|
puts "yard is not available. (sudo) gem install yard to generate yard documentation."
|
data/kitchen-pester.gemspec
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
|
-
#
|
|
3
1
|
# Author:: Steven Murawski (<steven.murawski@gmail.com>)
|
|
4
2
|
#
|
|
5
3
|
# Copyright (C) 2015, Steven Murawski
|
|
@@ -16,9 +14,12 @@
|
|
|
16
14
|
# See the License for the specific language governing permissions and
|
|
17
15
|
# limitations under the License.
|
|
18
16
|
|
|
19
|
-
require "
|
|
17
|
+
require "fileutils" unless defined?(FileUtils)
|
|
18
|
+
require "pathname" unless defined?(Pathname)
|
|
19
|
+
require "kitchen/util"
|
|
20
20
|
require "kitchen/verifier/base"
|
|
21
21
|
require "kitchen/version"
|
|
22
|
+
require "base64" unless defined?(Base64)
|
|
22
23
|
require_relative "pester_version"
|
|
23
24
|
|
|
24
25
|
module Kitchen
|
|
@@ -32,9 +33,39 @@ module Kitchen
|
|
|
32
33
|
plugin_version Kitchen::Verifier::PESTER_VERSION
|
|
33
34
|
|
|
34
35
|
default_config :restart_winrm, false
|
|
35
|
-
default_config :test_folder
|
|
36
|
-
default_config :
|
|
37
|
-
default_config :
|
|
36
|
+
default_config :test_folder, "tests"
|
|
37
|
+
default_config :remove_builtin_powershellget, true
|
|
38
|
+
default_config :remove_builtin_pester, true
|
|
39
|
+
default_config :skip_pester_install, false
|
|
40
|
+
default_config :bootstrap, {
|
|
41
|
+
repository_url: "https://www.powershellgallery.com/api/v2",
|
|
42
|
+
modules: [],
|
|
43
|
+
}
|
|
44
|
+
default_config :register_repository, []
|
|
45
|
+
default_config :pester_install, {
|
|
46
|
+
SkipPublisherCheck: true,
|
|
47
|
+
Force: true,
|
|
48
|
+
ErrorAction: "Stop",
|
|
49
|
+
}
|
|
50
|
+
default_config :pester_configuration, {
|
|
51
|
+
run: {
|
|
52
|
+
path: ".",
|
|
53
|
+
PassThru: true,
|
|
54
|
+
},
|
|
55
|
+
TestResult: {
|
|
56
|
+
Enabled: true,
|
|
57
|
+
OutputPath: "PesterTestResults.xml",
|
|
58
|
+
TestSuiteName: "",
|
|
59
|
+
},
|
|
60
|
+
Output: {
|
|
61
|
+
Verbosity: "Detailed",
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
default_config :install_modules, []
|
|
65
|
+
default_config :downloads, { "./PesterTestResults.xml" => "./testresults/" }
|
|
66
|
+
default_config :copy_folders, []
|
|
67
|
+
default_config :sudo, false
|
|
68
|
+
default_config :shell, nil
|
|
38
69
|
|
|
39
70
|
# Creates a new Verifier object using the provided configuration data
|
|
40
71
|
# which will be merged with any default configuration.
|
|
@@ -64,21 +95,56 @@ module Kitchen
|
|
|
64
95
|
# end
|
|
65
96
|
def create_sandbox
|
|
66
97
|
super
|
|
67
|
-
|
|
98
|
+
prepare_supporting_psmodules
|
|
99
|
+
prepare_copy_folders
|
|
68
100
|
prepare_pester_tests
|
|
69
101
|
prepare_helpers
|
|
102
|
+
|
|
103
|
+
debug("\n\n")
|
|
104
|
+
debug("Sandbox content:\n")
|
|
105
|
+
list_files(sandbox_path).each do |f|
|
|
106
|
+
debug(" #{f}")
|
|
107
|
+
end
|
|
70
108
|
end
|
|
71
109
|
|
|
72
110
|
# Generates a command string which will install and configure the
|
|
73
111
|
# verifier software on an instance. If no work is required, then `nil`
|
|
74
112
|
# will be returned.
|
|
113
|
+
# PowerShellGet & Pester Bootstrap are done in prepare_command (after sandbox is transferred)
|
|
114
|
+
# so that we can use the PesterUtil.psm1
|
|
75
115
|
#
|
|
76
116
|
# @return [String] a command string
|
|
77
117
|
def install_command
|
|
78
|
-
|
|
79
|
-
|
|
118
|
+
# the sandbox has not yet been copied to the SUT.
|
|
119
|
+
install_command_string = <<-PS1
|
|
120
|
+
Write-Verbose 'Running Install Command...'
|
|
121
|
+
$modulesToRemove = @(
|
|
122
|
+
if ($#{config[:remove_builtin_powershellget]}) {
|
|
123
|
+
Get-module -ListAvailable -FullyQualifiedName @{ModuleName = 'PackageManagement'; RequiredVersion = '1.0.0.1'}
|
|
124
|
+
Get-module -ListAvailable -FullyQualifiedName @{ModuleName = 'PowerShellGet'; RequiredVersion = '1.0.0.1'}
|
|
125
|
+
}
|
|
80
126
|
|
|
81
|
-
|
|
127
|
+
if ($#{config[:remove_builtin_pester]}) {
|
|
128
|
+
Get-module -ListAvailable -FullyQualifiedName @{ModuleName = 'Pester'; RequiredVersion = '3.4.0'}
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if ($modulesToRemove.ModuleBase.Count -eq 0) {
|
|
133
|
+
# for PS7 on linux
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
$modulesToRemove.ModuleBase | Foreach-Object {
|
|
138
|
+
$ModuleBaseLeaf = Split-Path -Path $_ -Leaf
|
|
139
|
+
if ($ModuleBaseLeaf -as [System.version]) {
|
|
140
|
+
Remove-Item -force -Recurse (Split-Path -Parent -Path $_) -ErrorAction SilentlyContinue
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
Remove-Item -force -Recurse $_ -ErrorAction SilentlyContinue
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
PS1
|
|
147
|
+
really_wrap_shell_code(Util.outdent!(install_command_string))
|
|
82
148
|
end
|
|
83
149
|
|
|
84
150
|
# Generates a command string which will perform any data initialization
|
|
@@ -97,7 +163,11 @@ module Kitchen
|
|
|
97
163
|
# required, then `nil` will be returned.
|
|
98
164
|
#
|
|
99
165
|
# @return [String] a command string
|
|
100
|
-
def prepare_command
|
|
166
|
+
def prepare_command
|
|
167
|
+
info("Preparing the SUT and Pester dependencies...")
|
|
168
|
+
resolve_downloads_paths!
|
|
169
|
+
really_wrap_shell_code(install_command_script)
|
|
170
|
+
end
|
|
101
171
|
|
|
102
172
|
# Generates a command string which will invoke the main verifier
|
|
103
173
|
# command on the prepared instance. If no work is required, then `nil`
|
|
@@ -105,9 +175,38 @@ module Kitchen
|
|
|
105
175
|
#
|
|
106
176
|
# @return [String] a command string
|
|
107
177
|
def run_command
|
|
108
|
-
|
|
178
|
+
really_wrap_shell_code(invoke_pester_scriptblock)
|
|
179
|
+
end
|
|
109
180
|
|
|
110
|
-
|
|
181
|
+
# Resolves the remote Downloads path from the verifier root path,
|
|
182
|
+
# unless they're absolute path (starts with / or C:\)
|
|
183
|
+
# This updates the config[:downloads], nothing (nil) is returned.
|
|
184
|
+
#
|
|
185
|
+
# @return [nil] updates config downloads
|
|
186
|
+
def resolve_downloads_paths!
|
|
187
|
+
info("Resolving Downloads path from config.")
|
|
188
|
+
config[:downloads] = config[:downloads]
|
|
189
|
+
.map do |source, destination|
|
|
190
|
+
source = source.to_s
|
|
191
|
+
info(" resolving remote source's absolute path.")
|
|
192
|
+
unless source.match?('^/|^[a-zA-Z]:[\\/]') # is Absolute?
|
|
193
|
+
info(" '#{source}' is a relative path, resolving to: #{File.join(config[:root_path], source)}")
|
|
194
|
+
source = File.join(config[:root_path], source.to_s).to_s
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
if destination.match?('\\$|/$') # is Folder (ends with / or \)
|
|
198
|
+
destination = File.join(destination, File.basename(source)).to_s
|
|
199
|
+
end
|
|
200
|
+
info(" Destination: #{destination}")
|
|
201
|
+
if !File.directory?(File.dirname(destination))
|
|
202
|
+
FileUtils.mkdir_p(File.dirname(destination))
|
|
203
|
+
else
|
|
204
|
+
info(" Directory #{File.dirname(destination)} seem to exist.")
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
[ source, destination ]
|
|
208
|
+
end
|
|
209
|
+
nil # make sure we do not return anything
|
|
111
210
|
end
|
|
112
211
|
|
|
113
212
|
# Download functionality was added to the base verifier behavior after
|
|
@@ -115,128 +214,304 @@ module Kitchen
|
|
|
115
214
|
if Gem::Version.new(Kitchen::VERSION) <= Gem::Version.new("2.3.4")
|
|
116
215
|
def call(state)
|
|
117
216
|
super
|
|
118
|
-
|
|
119
|
-
|
|
217
|
+
ensure
|
|
218
|
+
info("Ensure download test files.")
|
|
219
|
+
download_test_files(state) unless config[:downloads].nil?
|
|
220
|
+
info("Download complete.")
|
|
221
|
+
end
|
|
222
|
+
else
|
|
223
|
+
def call(state)
|
|
224
|
+
super
|
|
225
|
+
rescue
|
|
226
|
+
# If the verifier reports failure, we need to download the files ourselves.
|
|
227
|
+
# Test Kitchen's base verifier doesn't have the download in an `ensure` block.
|
|
228
|
+
info("Rescue to download test files.")
|
|
229
|
+
download_test_files(state) unless config[:downloads].nil?
|
|
230
|
+
# Rethrow original exception, we still want to register the failure.
|
|
231
|
+
raise
|
|
120
232
|
end
|
|
121
233
|
end
|
|
122
234
|
|
|
123
235
|
# private
|
|
124
|
-
def
|
|
125
|
-
<<-
|
|
126
|
-
Import-Module Pester -Force
|
|
127
|
-
|
|
128
|
-
$
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
236
|
+
def invoke_pester_scriptblock
|
|
237
|
+
<<-PS1
|
|
238
|
+
$PesterModule = Import-Module -Name Pester -Force -ErrorAction Stop -PassThru
|
|
239
|
+
|
|
240
|
+
$TestPath = Join-Path "#{config[:root_path]}" -ChildPath "suites"
|
|
241
|
+
$OutputFilePath = Join-Path "#{config[:root_path]}" -ChildPath 'PesterTestResults.xml'
|
|
242
|
+
|
|
243
|
+
if ($PesterModule.Version.Major -le 4)
|
|
244
|
+
{
|
|
245
|
+
Write-Host -Object "Invoke Pester with v$($PesterModule.Version) Options"
|
|
246
|
+
$options = New-PesterOption -TestSuiteName "Pester - #{instance.to_str}"
|
|
247
|
+
$defaultPesterParameters = @{
|
|
248
|
+
Script = $TestPath
|
|
249
|
+
OutputFile = $OutputFilePath
|
|
250
|
+
OutputFormat = 'NUnitXml'
|
|
251
|
+
PassThru = $true
|
|
252
|
+
PesterOption = $options
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
$pesterCmd = Get-Command -Name 'Invoke-Pester'
|
|
256
|
+
$pesterConfig = #{ps_hash(config[:pester_configuration])}
|
|
257
|
+
$invokePesterParams = @{}
|
|
258
|
+
|
|
259
|
+
foreach ($paramName in $pesterCmd.Parameters.Keys)
|
|
260
|
+
{
|
|
261
|
+
$paramValue = $pesterConfig.($paramName)
|
|
262
|
+
|
|
263
|
+
if ($paramValue) {
|
|
264
|
+
Write-Host -Object "Using $paramName from Yaml config."
|
|
265
|
+
$invokePesterParams[$paramName] = $paramValue
|
|
266
|
+
}
|
|
267
|
+
elseif ($defaultPesterParameters.ContainsKey($paramName))
|
|
268
|
+
{
|
|
269
|
+
Write-Host -Object "Using $paramName from Defaults: $($defaultPesterParameters[$paramName])."
|
|
270
|
+
$invokePesterParams[$paramName] = $defaultPesterParameters[$paramName]
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
$result = Invoke-Pester @invokePesterParams
|
|
275
|
+
}
|
|
276
|
+
else
|
|
277
|
+
{
|
|
278
|
+
Write-Host -Object "Invoke Pester with v$($PesterModule.Version) Configuration."
|
|
279
|
+
$pesterConfigHash = #{ps_hash(config[:pester_configuration])}
|
|
280
|
+
|
|
281
|
+
if (-not $pesterConfigHash.ContainsKey('run')) {
|
|
282
|
+
$pesterConfigHash['run'] = @{}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (-not $pesterConfigHash.ContainsKey('TestResult')) {
|
|
286
|
+
$pesterConfigHash['TestResult'] = @{}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (-not $pesterConfigHash.run.path) {
|
|
290
|
+
$pesterConfigHash['run']['path'] = $TestPath
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (-not $pesterConfigHash.TestResult.TestSuiteName) {
|
|
294
|
+
$pesterConfigHash['TestResult']['TestSuiteName'] = 'Pester - #{instance.to_str}'
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (-not $pesterConfigHash.TestResult.OutputPath) {
|
|
298
|
+
$pesterConfigHash['TestResult']['OutputPath'] = $OutputFilePath
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
$PesterConfig = New-PesterConfiguration -Hashtable $pesterConfigHash
|
|
302
|
+
$result = Invoke-Pester -Configuration $PesterConfig
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
$resultXmlPath = (Join-Path -Path $TestPath -ChildPath 'result.xml')
|
|
306
|
+
if (Test-Path -Path $resultXmlPath) {
|
|
307
|
+
$result | Export-CliXml -Path
|
|
308
|
+
}
|
|
309
|
+
|
|
132
310
|
$LASTEXITCODE = $result.FailedCount
|
|
133
311
|
$host.SetShouldExit($LASTEXITCODE)
|
|
312
|
+
|
|
134
313
|
exit $LASTEXITCODE
|
|
135
|
-
|
|
314
|
+
PS1
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def get_powershell_modules_from_nugetapi
|
|
318
|
+
# don't return anything is the modules subkey or bootstrap is null
|
|
319
|
+
return if config.dig(:bootstrap, :modules).nil?
|
|
320
|
+
|
|
321
|
+
bootstrap = config[:bootstrap]
|
|
322
|
+
# if the repository url is set, use that as parameter to Install-ModuleFromNuget. Default is the PSGallery url
|
|
323
|
+
gallery_url_param = bootstrap[:repository_url] ? "-GalleryUrl '#{bootstrap[:repository_url]}'" : ""
|
|
324
|
+
|
|
325
|
+
info("Bootstrapping environment without PowerShellGet Provider...")
|
|
326
|
+
Array(bootstrap[:modules]).map do |powershell_module|
|
|
327
|
+
if powershell_module.is_a? Hash
|
|
328
|
+
<<-PS1
|
|
329
|
+
${#{powershell_module[:Name]}} = #{ps_hash(powershell_module)}
|
|
330
|
+
|
|
331
|
+
Install-ModuleFromNuget -Module ${#{powershell_module[:Name]}} #{gallery_url_param}
|
|
332
|
+
PS1
|
|
333
|
+
else
|
|
334
|
+
<<-PS1
|
|
335
|
+
Install-ModuleFromNuget -Module @{Name = '#{powershell_module}'} #{gallery_url_param}
|
|
336
|
+
PS1
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Returns the string command to set a PS Repository
|
|
342
|
+
# for each PSRepo configured.
|
|
343
|
+
#
|
|
344
|
+
# @return [Array<String>] array of suite files
|
|
345
|
+
# @api private
|
|
346
|
+
def register_psrepository_scriptblock
|
|
347
|
+
return if config[:register_repository].nil?
|
|
348
|
+
|
|
349
|
+
info("Registering a new PowerShellGet Repository")
|
|
350
|
+
Array(config[:register_repository]).map do |psrepo|
|
|
351
|
+
# Using Set-PSRepo from ../../*/*/*/PesterUtil.psm1
|
|
352
|
+
debug("Command to set PSRepo #{psrepo[:Name]}.")
|
|
353
|
+
<<-PS1
|
|
354
|
+
Write-Host 'Registering psrepo #{psrepo[:Name]}...'
|
|
355
|
+
${#{psrepo[:Name]}} = #{ps_hash(psrepo)}
|
|
356
|
+
Set-PSRepo -Repository ${#{psrepo[:Name]}}
|
|
357
|
+
PS1
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Returns the string command set the PSGallery as trusted, and
|
|
362
|
+
# Install Pester from gallery based on the params from Pester_install_params config
|
|
363
|
+
#
|
|
364
|
+
# @return <String> command to install Pester Module
|
|
365
|
+
# @api private
|
|
366
|
+
def install_pester
|
|
367
|
+
return if config[:skip_pester_install]
|
|
368
|
+
|
|
369
|
+
pester_install_params = config[:pester_install] || {}
|
|
370
|
+
<<-PS1
|
|
371
|
+
if ((Get-PSRepository -Name PSGallery).InstallationPolicy -ne 'Trusted') {
|
|
372
|
+
Write-Host -Object "Trusting the PSGallery to install Pester without -Force"
|
|
373
|
+
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -ErrorAction SilentlyContinue
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
Write-Host "Installing Pester..."
|
|
377
|
+
$installPesterParams = #{ps_hash(pester_install_params)}
|
|
378
|
+
$installPesterParams['Name'] = 'Pester'
|
|
379
|
+
Install-module @installPesterParams
|
|
380
|
+
Write-Host 'Pester Installed.'
|
|
381
|
+
PS1
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# returns a piece of PS scriptblock for each Module to install
|
|
385
|
+
# from gallery that has been sepcified in install_modules config.
|
|
386
|
+
#
|
|
387
|
+
# @return [Array<String>] array of PS commands.
|
|
388
|
+
# @api private
|
|
389
|
+
def install_modules_from_gallery
|
|
390
|
+
return if config[:install_modules].nil?
|
|
391
|
+
|
|
392
|
+
Array(config[:install_modules]).map do |powershell_module|
|
|
393
|
+
if powershell_module.is_a? Hash
|
|
394
|
+
# Sanitize variable name so that $powershell-yaml becomes $powershell_yaml
|
|
395
|
+
module_name = powershell_module[:Name].gsub(/[\W]/, "_")
|
|
396
|
+
# so we can splat that variable to install module
|
|
397
|
+
<<-PS1
|
|
398
|
+
$#{module_name} = #{ps_hash(powershell_module)}
|
|
399
|
+
Write-Host -NoNewline 'Installing #{module_name}'
|
|
400
|
+
Install-Module @#{module_name}
|
|
401
|
+
Write-host '... done.'
|
|
402
|
+
PS1
|
|
403
|
+
else
|
|
404
|
+
<<-PS1
|
|
405
|
+
Write-host -NoNewline 'Installing #{powershell_module} ...'
|
|
406
|
+
Install-Module -Name '#{powershell_module}'
|
|
407
|
+
Write-host '... done.'
|
|
408
|
+
PS1
|
|
409
|
+
end
|
|
410
|
+
end
|
|
136
411
|
end
|
|
137
412
|
|
|
138
413
|
def really_wrap_shell_code(code)
|
|
139
|
-
|
|
414
|
+
windows_os? ? really_wrap_windows_shell_code(code) : really_wrap_posix_shell_code(code)
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Get the defined shell or fall back to pwsh, unless we're on windows where we use powershell
|
|
418
|
+
# call via sudo if sudo is true.
|
|
419
|
+
# This allows to use pwsh-preview instead of pwsh, or a full path to a specific binary.
|
|
420
|
+
def shell_cmd
|
|
421
|
+
if !config[:shell].nil?
|
|
422
|
+
config[:sudo] ? "sudo #{config[:shell]}" : "#{config[:shell]}"
|
|
423
|
+
elsif windows_os?
|
|
424
|
+
"powershell"
|
|
425
|
+
else
|
|
426
|
+
config[:sudo] ? "sudo pwsh" : "pwsh"
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def really_wrap_windows_shell_code(code)
|
|
431
|
+
my_command = <<-PWSH
|
|
432
|
+
echo "Running as '$(whoami)'..."
|
|
433
|
+
New-Item -ItemType Directory -Path '#{config[:root_path]}/modules' -Force -ErrorAction SilentlyContinue
|
|
434
|
+
Set-Location -Path "#{config[:root_path]}"
|
|
435
|
+
# Send the pwsh here string to the file kitchen_cmd.ps1
|
|
436
|
+
@'
|
|
437
|
+
try {
|
|
438
|
+
Set-ExecutionPolicy Unrestricted -force
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
$_ | Out-String | Write-Warning
|
|
442
|
+
}
|
|
443
|
+
#{Util.outdent!(use_local_powershell_modules(code))}
|
|
444
|
+
'@ | Set-Content -Path kitchen_cmd.ps1 -Encoding utf8 -Force -ErrorAction 'Stop'
|
|
445
|
+
# create the modules folder, making sure it's done as current user (not root)
|
|
446
|
+
#
|
|
447
|
+
# Invoke the created kitchen_cmd.ps1 file using pwsh
|
|
448
|
+
#{shell_cmd} ./kitchen_cmd.ps1
|
|
449
|
+
PWSH
|
|
450
|
+
wrap_shell_code(Util.outdent!(my_command))
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
# Writing the command to a ps1 file, adding the pwsh shebang
|
|
454
|
+
# invoke the file
|
|
455
|
+
def really_wrap_posix_shell_code(code)
|
|
456
|
+
my_command = <<-BASH
|
|
457
|
+
echo "Running as '$(whoami)'"
|
|
458
|
+
# create the modules folder, making sure it's done as current user (not root)
|
|
459
|
+
mkdir -p #{config[:root_path]}/modules
|
|
460
|
+
cd #{config[:root_path]}
|
|
461
|
+
# Send the bash heredoc 'EOF' to the file kitchen_cmd.ps1 using the tool cat
|
|
462
|
+
cat << 'EOF' > kitchen_cmd.ps1
|
|
463
|
+
#!/usr/bin/env pwsh
|
|
464
|
+
#{Util.outdent!(use_local_powershell_modules(code))}
|
|
465
|
+
EOF
|
|
466
|
+
chmod +x kitchen_cmd.ps1
|
|
467
|
+
# Invoke the created kitchen_cmd.ps1 file using pwsh
|
|
468
|
+
#{shell_cmd} ./kitchen_cmd.ps1
|
|
469
|
+
BASH
|
|
470
|
+
|
|
471
|
+
debug(Util.outdent!(my_command))
|
|
472
|
+
Util.outdent!(my_command)
|
|
140
473
|
end
|
|
141
474
|
|
|
142
475
|
def use_local_powershell_modules(script)
|
|
143
|
-
<<-
|
|
144
|
-
|
|
476
|
+
<<-PS1
|
|
477
|
+
Write-Host -Object ("{0} - PowerShell {1}" -f $PSVersionTable.OS,$PSVersionTable.PSVersion)
|
|
145
478
|
$global:ProgressPreference = 'SilentlyContinue'
|
|
146
|
-
$
|
|
479
|
+
$PSModPathToPrepend = Join-Path "#{config[:root_path]}" -ChildPath 'modules'
|
|
480
|
+
Write-Verbose "Adding '$PSModPathToPrepend' to `$Env:PSModulePath."
|
|
481
|
+
if (!$isLinux -and -not (Test-Path -Path $PSModPathToPrepend)) {
|
|
482
|
+
# if you create this folder now in Linux, it may run as root (via sudo).
|
|
483
|
+
$null = New-Item -Path $PSModPathToPrepend -Force -ItemType Directory
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if ($Env:PSModulePath.Split([io.path]::PathSeparator) -notcontains $PSModPathToPrepend) {
|
|
487
|
+
$env:PSModulePath = @($PSModPathToPrepend, $env:PSModulePath) -Join [io.path]::PathSeparator
|
|
488
|
+
}
|
|
489
|
+
|
|
147
490
|
#{script}
|
|
148
|
-
|
|
491
|
+
PS1
|
|
149
492
|
end
|
|
150
493
|
|
|
151
494
|
def install_command_script
|
|
152
|
-
<<-
|
|
153
|
-
|
|
154
|
-
function directory($path){
|
|
155
|
-
if (test-path $path) {(resolve-path $path).providerpath}
|
|
156
|
-
else {(resolve-path (mkdir $path)).providerpath}
|
|
157
|
-
}
|
|
158
|
-
$VerifierModulePath = directory $env:temp/verifier/modules
|
|
159
|
-
$VerifierTestsPath = directory $env:temp/verifier/pester
|
|
495
|
+
<<-PS1
|
|
496
|
+
$PSModPathToPrepend = "#{config[:root_path]}"
|
|
160
497
|
|
|
161
|
-
|
|
162
|
-
function test-module($module){
|
|
163
|
-
(get-module $module -list) -ne $null
|
|
164
|
-
}
|
|
165
|
-
if (-not (test-module pester)) {
|
|
166
|
-
if (test-module PowerShellGet){
|
|
167
|
-
import-module PowerShellGet -force
|
|
168
|
-
import-module PackageManagement -force
|
|
169
|
-
get-packageprovider -name NuGet -force | out-null
|
|
170
|
-
install-module Pester -force
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
if (-not (test-module PsGet)){
|
|
174
|
-
$wc = New-Object -TypeName Net.WebClient
|
|
175
|
-
|
|
176
|
-
if($env:http_Proxy){
|
|
177
|
-
if($env:no_proxy){
|
|
178
|
-
Write-Output "Creating WebProxy with 'http_proxy' and 'no_proxy' environment variables.
|
|
179
|
-
$webproxy = New-Object System.Net.WebProxy($env:http_Proxy,$true,$env:no_proxy)
|
|
180
|
-
}else{
|
|
181
|
-
Write-Output "Creating WebProxy with 'http_proxy' environment variable.
|
|
182
|
-
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList ($env:http_Proxy)
|
|
183
|
-
}
|
|
498
|
+
Import-Module -ErrorAction Stop PesterUtil
|
|
184
499
|
|
|
185
|
-
|
|
186
|
-
}
|
|
500
|
+
#{get_powershell_modules_from_nugetapi.join("\n") unless config.dig(:bootstrap, :modules).nil?}
|
|
187
501
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
catch {
|
|
195
|
-
Write-Output "Installing from Github"
|
|
196
|
-
$zipfile = join-path(resolve-path "$env:temp/verifier") "pester.zip"
|
|
197
|
-
if (-not (test-path $zipfile)){
|
|
198
|
-
$source = 'https://github.com/pester/Pester/archive/3.3.14.zip'
|
|
199
|
-
$wc = New-Object -TypeName Net.WebClient
|
|
200
|
-
|
|
201
|
-
if($env:http_Proxy){
|
|
202
|
-
if($env:no_proxy){
|
|
203
|
-
Write-Output "Creating WebProxy with 'http_proxy' and 'no_proxy' environment variables."
|
|
204
|
-
$webproxy = New-Object System.Net.WebProxy($env:http_Proxy,$true,$env:no_proxy)
|
|
205
|
-
}else{
|
|
206
|
-
Write-Output "Creating WebProxy with 'http_proxy' environment variable."
|
|
207
|
-
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList ($env:http_Proxy)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
$wc.proxy = $webproxy
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
[byte[]]$bytes = $wc.DownloadData($source)
|
|
214
|
-
[IO.File]::WriteAllBytes($zipfile, $bytes)
|
|
215
|
-
$bytes = $null
|
|
216
|
-
[gc]::collect()
|
|
217
|
-
write-output "Downloaded Pester.zip"
|
|
218
|
-
}
|
|
219
|
-
write-output "Creating Shell.Application COM object"
|
|
220
|
-
$shellcom = new-object -com shell.application
|
|
221
|
-
Write-Output "Creating COM object for zip file."
|
|
222
|
-
$zipcomobject = $shellcom.namespace($zipfile)
|
|
223
|
-
Write-Output "Creating COM object for module destination."
|
|
224
|
-
$destination = $shellcom.namespace($VerifierModulePath)
|
|
225
|
-
Write-Output "Unpacking zip file."
|
|
226
|
-
$destination.CopyHere($zipcomobject.Items(), 0x610)
|
|
227
|
-
rename-item (join-path $VerifierModulePath "Pester-3.3.14") -newname 'Pester' -force
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
if (-not (test-module Pester)) {
|
|
232
|
-
throw "Unable to install Pester. Please include Pester in your base image or install during your converge."
|
|
233
|
-
}
|
|
234
|
-
EOH
|
|
502
|
+
#{register_psrepository_scriptblock.join("\n") unless config[:register_repository].nil?}
|
|
503
|
+
|
|
504
|
+
#{install_pester}
|
|
505
|
+
|
|
506
|
+
#{install_modules_from_gallery.join("\n") unless config[:install_modules].nil?}
|
|
507
|
+
PS1
|
|
235
508
|
end
|
|
236
509
|
|
|
237
510
|
def restart_winrm_service
|
|
511
|
+
return unless verifier.windows_os?
|
|
512
|
+
|
|
238
513
|
cmd = "schtasks /Create /TN restart_winrm /TR " \
|
|
239
|
-
'"powershell -
|
|
514
|
+
'"powershell -Command Restart-Service winrm" ' \
|
|
240
515
|
"/SC ONCE /ST 00:00 "
|
|
241
516
|
wrap_shell_code(Util.outdent!(<<-CMD
|
|
242
517
|
#{cmd}
|
|
@@ -246,19 +521,16 @@ module Kitchen
|
|
|
246
521
|
end
|
|
247
522
|
|
|
248
523
|
def download_test_files(state)
|
|
249
|
-
|
|
524
|
+
if config[:downloads].nil?
|
|
525
|
+
info("Skipped downloading test result file from #{instance.to_str}; 'downloads' hash is empty.")
|
|
526
|
+
return
|
|
527
|
+
end
|
|
250
528
|
|
|
529
|
+
info("Downloading test result files from #{instance.to_str}")
|
|
251
530
|
instance.transport.connection(state) do |conn|
|
|
252
|
-
config[:downloads].
|
|
253
|
-
debug("
|
|
254
|
-
|
|
255
|
-
remotes.each do |file|
|
|
256
|
-
safe_name = instance.name.gsub(/[^0-9A-Z-]/i, "_")
|
|
257
|
-
local_path = File.join(local, safe_name, file)
|
|
258
|
-
remote_path = File.join(config[:root_path], file)
|
|
259
|
-
|
|
260
|
-
conn.download(remote_path, local_path)
|
|
261
|
-
end
|
|
531
|
+
config[:downloads].each do |remotes, local|
|
|
532
|
+
debug("downloading #{Array(remotes).join(", ")} to #{local}")
|
|
533
|
+
conn.download(remotes, local)
|
|
262
534
|
end
|
|
263
535
|
end
|
|
264
536
|
|
|
@@ -271,29 +543,25 @@ module Kitchen
|
|
|
271
543
|
#
|
|
272
544
|
# @return [Array<String>] array of suite files
|
|
273
545
|
# @api private
|
|
274
|
-
|
|
275
546
|
def suite_test_folder
|
|
276
547
|
@suite_test_folder ||= File.join(test_folder, config[:suite_name])
|
|
277
548
|
end
|
|
278
549
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
def
|
|
284
|
-
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
def local_suite_files
|
|
288
|
-
suite = suite_level_glob
|
|
289
|
-
suite_verifier = suite_verifier_level_glob
|
|
290
|
-
(suite << suite_verifier).flatten!.reject do |f|
|
|
291
|
-
File.directory?(f)
|
|
292
|
-
end
|
|
550
|
+
# Returns the current file's parent folder's full path.
|
|
551
|
+
#
|
|
552
|
+
# @return [string]
|
|
553
|
+
# @api private
|
|
554
|
+
def script_root
|
|
555
|
+
@script_root ||= File.dirname(__FILE__)
|
|
293
556
|
end
|
|
294
557
|
|
|
295
|
-
|
|
296
|
-
|
|
558
|
+
# Returns the absolute path of the Supporting PS module to
|
|
559
|
+
# be copied to the SUT via the Sandbox.
|
|
560
|
+
#
|
|
561
|
+
# @return [string]
|
|
562
|
+
# @api private
|
|
563
|
+
def support_psmodule_folder
|
|
564
|
+
@support_psmodule_folder ||= Pathname.new(File.join(script_root, "../../support/modules/PesterUtil")).cleanpath
|
|
297
565
|
end
|
|
298
566
|
|
|
299
567
|
# Returns an Array of common helper filenames currently residing on the
|
|
@@ -321,50 +589,128 @@ module Kitchen
|
|
|
321
589
|
end
|
|
322
590
|
end
|
|
323
591
|
|
|
324
|
-
#
|
|
592
|
+
# Creates a PowerShell hashtable from a ruby map.
|
|
593
|
+
# The only types supported for now are hash, array, string and Boolean.
|
|
325
594
|
#
|
|
326
595
|
# @api private
|
|
327
|
-
def
|
|
328
|
-
|
|
596
|
+
def ps_hash(obj, depth = 0)
|
|
597
|
+
if [true, false].include? obj
|
|
598
|
+
%{$#{obj}} # Return $true or $false when value is a bool
|
|
599
|
+
elsif obj.is_a?(Hash)
|
|
600
|
+
obj.map do |k, v|
|
|
601
|
+
# Format "Key = Value" enabling recursion
|
|
602
|
+
%{#{pad(depth + 2)}#{ps_hash(k)} = #{ps_hash(v, depth + 2)}}
|
|
603
|
+
end
|
|
604
|
+
.join("\n") # append \n to the key/value definitions
|
|
605
|
+
.insert(0, "@{\n") # prepend @{\n
|
|
606
|
+
.insert(-1, "\n#{pad(depth)}}\n") # append \n}\n
|
|
607
|
+
|
|
608
|
+
elsif obj.is_a?(Array)
|
|
609
|
+
array_string = obj.map { |v| ps_hash(v, depth + 4) }.join(",")
|
|
610
|
+
"#{pad(depth)}@(\n#{array_string}\n)"
|
|
611
|
+
else
|
|
612
|
+
# When the object is not a string nor a hash or array, it will be quoted as a string.
|
|
613
|
+
# In most cases, PS is smart enough to convert back to the type it needs.
|
|
614
|
+
"'" + obj.to_s + "'"
|
|
615
|
+
end
|
|
616
|
+
end
|
|
329
617
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
618
|
+
# returns the path of the modules subfolder
|
|
619
|
+
# in the sandbox, where PS Modules and folders will be copied to.
|
|
620
|
+
#
|
|
621
|
+
# @api private
|
|
622
|
+
def sandbox_module_path
|
|
623
|
+
File.join(sandbox_path, "modules")
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
# copy files into the 'modules' folder of the sandbox,
|
|
627
|
+
# so that copied folders can be discovered with the updated $Env:PSModulePath.
|
|
628
|
+
#
|
|
629
|
+
# @api private
|
|
630
|
+
def prepare_copy_folders
|
|
631
|
+
return if config[:copy_folders].nil?
|
|
632
|
+
|
|
633
|
+
info("Preparing to copy specified folders to #{sandbox_module_path}.")
|
|
634
|
+
kitchen_root_path = config[:kitchen_root]
|
|
635
|
+
config[:copy_folders].each do |folder|
|
|
636
|
+
debug("copying #{folder}")
|
|
637
|
+
folder_to_copy = File.join(kitchen_root_path, folder)
|
|
638
|
+
copy_if_src_exists(folder_to_copy, sandbox_module_path)
|
|
335
639
|
end
|
|
336
640
|
end
|
|
337
641
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
642
|
+
# returns an array of string
|
|
643
|
+
# Creates a flat list of files contained in a folder.
|
|
644
|
+
# This is useful when trying to debug what has been copied to
|
|
645
|
+
# the sandbox.
|
|
646
|
+
#
|
|
647
|
+
# @return [Array<String>] array of files in a folder
|
|
648
|
+
# @api private
|
|
649
|
+
def list_files(path)
|
|
650
|
+
base_directory_content = Dir.glob(File.join(path, "*"))
|
|
651
|
+
nested_directory_content = Dir.glob(File.join(path, "*/**/*"))
|
|
652
|
+
[base_directory_content, nested_directory_content].flatten
|
|
345
653
|
end
|
|
346
654
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
655
|
+
# Copies all test suite files into the suites directory in the sandbox.
|
|
656
|
+
#
|
|
657
|
+
# @api private
|
|
658
|
+
def prepare_pester_tests
|
|
659
|
+
info("Preparing to copy files from '#{suite_test_folder}' to the SUT.")
|
|
660
|
+
sandboxed_suites_path = File.join(sandbox_path, "suites")
|
|
661
|
+
copy_if_src_exists(suite_test_folder, sandboxed_suites_path)
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def prepare_supporting_psmodules
|
|
665
|
+
info("Preparing to copy files from '#{support_psmodule_folder}' to the SUT.")
|
|
666
|
+
sandbox_module_path = File.join(sandbox_path, "modules")
|
|
667
|
+
copy_if_src_exists(support_psmodule_folder, sandbox_module_path)
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
# Copies a folder recursively preserving its layers,
|
|
671
|
+
# mostly used to copy to the sandbox.
|
|
672
|
+
#
|
|
673
|
+
# @api private
|
|
674
|
+
def copy_if_src_exists(src_to_validate, destination)
|
|
675
|
+
unless Dir.exist?(src_to_validate)
|
|
676
|
+
info("The path #{src_to_validate} was not found. Not copying to #{destination}.")
|
|
677
|
+
return
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
info("Moving #{src_to_validate} to #{destination}")
|
|
681
|
+
unless Dir.exist?(destination)
|
|
682
|
+
FileUtils.mkdir_p(destination)
|
|
683
|
+
debug("Folder '#{destination}' created.")
|
|
351
684
|
end
|
|
685
|
+
FileUtils.mkdir_p(File.join(destination, "__bugfix"))
|
|
686
|
+
FileUtils.cp_r(src_to_validate, destination, preserve: true)
|
|
352
687
|
end
|
|
353
688
|
|
|
689
|
+
# returns the absolute path of the folders containing the
|
|
690
|
+
# test suites, use default if not set.
|
|
691
|
+
#
|
|
692
|
+
# @api private
|
|
354
693
|
def test_folder
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
absolute_test_folder
|
|
694
|
+
config[:test_folder].nil? ? config[:test_base_path] : absolute_test_folder
|
|
358
695
|
end
|
|
359
696
|
|
|
697
|
+
# returns the absolute path of the relative folders containing the
|
|
698
|
+
# test suites, use default i not set.
|
|
699
|
+
#
|
|
700
|
+
# @api private
|
|
360
701
|
def absolute_test_folder
|
|
361
702
|
path = (Pathname.new config[:test_folder]).realpath
|
|
362
703
|
integration_path = File.join(path, "integration")
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
integration_path
|
|
704
|
+
Dir.exist?(integration_path) ? integration_path : path
|
|
366
705
|
end
|
|
367
706
|
|
|
707
|
+
# returns a string of space of the specified depth.
|
|
708
|
+
# This is used to pad messages or when building PS hashtables.
|
|
709
|
+
#
|
|
710
|
+
# @api private
|
|
711
|
+
def pad(depth = 0)
|
|
712
|
+
" " * depth
|
|
713
|
+
end
|
|
368
714
|
end
|
|
369
715
|
end
|
|
370
716
|
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
|
|
2
|
+
|
|
3
|
+
function Install-ModuleFromNuget {
|
|
4
|
+
[CmdletBinding()]
|
|
5
|
+
param (
|
|
6
|
+
[hashtable]
|
|
7
|
+
$Module,
|
|
8
|
+
|
|
9
|
+
[string]
|
|
10
|
+
$GalleryUrl = 'https://www.powershellgallery.com/api/v2'
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
$tempPath = [System.IO.Path]::GetTempPath()
|
|
14
|
+
$zipFileName = "{0}.{1}.{2}" -f $Module.Name, $Module.Version, 'zip'
|
|
15
|
+
$downloadedZip = Join-Path -Path $tempPath $zipFileName
|
|
16
|
+
$ModulePath = Join-Path -Path $PSHome -ChildPath 'Modules'
|
|
17
|
+
$ModuleFolder = Join-Path -Path $ModulePath -ChildPath $Module.Name
|
|
18
|
+
|
|
19
|
+
if ((Test-Path $ModuleFolder) -and ($PSVersionTable.PSVersion.Major -lt 5 -or $module.Force)) {
|
|
20
|
+
# Check if available version is correct
|
|
21
|
+
$ModuleManifest = Join-Path -Path $ModuleFolder -ChildPath "$($Module.Name).psd1"
|
|
22
|
+
if ((Test-Path -Path $ModuleManifest) -and -not $Module.Force) {
|
|
23
|
+
# Import-PowerShellDataFile only exists since 5.1
|
|
24
|
+
$ManifestInfo = Import-LocalizedData -BaseDirectory (Split-Path -Parent -Path $ModuleManifest) -FileName $Module.Name
|
|
25
|
+
$ModuleVersionNoPreRelease = $Module.Version -replace '-.*$'
|
|
26
|
+
# Compare the version in manifest with version required without Pre-release
|
|
27
|
+
if ($ManifestInfo.ModuleVersion -eq $ModuleVersionNoPreRelease) {
|
|
28
|
+
Write-Host "Module $($Module.Name) already installed, skipping."
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
Write-Host "Module $($Module.Name) found with version '$($ManifestInfo.ModuleVersion)', expecting '$ModuleVersionNoPreRelease'."
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
# if incorrect, remove it before install
|
|
37
|
+
Remove-Item -Recurse -Force -Path $ModuleFolder
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
elseif ($PSVersionTable.PSVersion.Major -gt 5) {
|
|
41
|
+
# skip if the version already exists or if force is enabled
|
|
42
|
+
$ModuleVersionNoPreRelease = $Module.Version -replace '-.*$'
|
|
43
|
+
$ModuleFolder = Join-Path -Path $ModuleFolder -ChildPath $ModuleVersionNoPreRelease
|
|
44
|
+
if (-not $Module.Force -and (Test-Path -Path $ModuleFolder)) {
|
|
45
|
+
Write-Verbose -Message "Module already installed."
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (-not (Test-Path $ModuleFolder)) {
|
|
51
|
+
$null = New-Item -Path $ModuleFolder -force -ItemType Directory
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
$urlSuffix = "/package/$($Module.Name)/$($Module.Version)".TrimEnd('/')
|
|
55
|
+
$nupkgUrl = $GalleryUrl.TrimEnd('/') + '/' + $urlSuffix.Trim('/')
|
|
56
|
+
$webclient = New-Object 'system.net.webclient'
|
|
57
|
+
|
|
58
|
+
if ($env:HTTP_PROXY){
|
|
59
|
+
if ($env:NO_PROXY){
|
|
60
|
+
Write-Host "Creating WebProxy with 'HTTP_PROXY' and 'NO_PROXY' environment variables."
|
|
61
|
+
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY, $true, $env:NO_PROXY
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
Write-Host "Creating WebProxy with 'HTTP_PROXY' environment variable."
|
|
65
|
+
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
$webclient.Proxy = $webproxy
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Write-Verbose -Message "Downloading Package from $nupkgUrl"
|
|
72
|
+
try {
|
|
73
|
+
if (Test-Path $downloadedZip) {
|
|
74
|
+
Remove-Item -Force -ErrorAction SilentlyContinue -Path $downloadedZip
|
|
75
|
+
}
|
|
76
|
+
$webclient.DownloadFile($nupkgUrl, $downloadedZip)
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
Write-Error "Error trying to download nupkg '$nupkgUrl' to '$downloadedZip'."
|
|
80
|
+
throw $_
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (-not (Test-Path -Path $downloadedZip)) {
|
|
84
|
+
throw "Error trying to download nupkg '$nupkgUrl' to '$downloadedZip'."
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Test to see if Expand-Archive is available first
|
|
88
|
+
if (Get-Command Expand-Archive) {
|
|
89
|
+
Expand-Archive -Path $downloadedZip -DestinationPath $ModuleFolder -Force
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
# Fall back to COM object for Shell.Application zip extraction
|
|
93
|
+
Write-Host "Creating COM object for zip file '$downloadedZip'."
|
|
94
|
+
$shellcom = New-Object -ComObject Shell.Application
|
|
95
|
+
$zipcomobject = $shellcom.Namespace($downloadedZip)
|
|
96
|
+
$destination = $shellcom.Namespace($ModuleFolder)
|
|
97
|
+
$destination.CopyHere($zipcomobject.Items(), 0x610)
|
|
98
|
+
Write-Host "Nupkg installed at $ModuleFolder"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
[GC]::Collect()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function Set-PSRepo {
|
|
105
|
+
param(
|
|
106
|
+
[Parameter(Mandatory)]
|
|
107
|
+
$Repository
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if (-not (Get-Command Get-PSRepository) -and (Get-Command Get-PackageSource)) {
|
|
111
|
+
# Old versions of PSGet do not have a *-PSrepository but have *-PackageSource instead.
|
|
112
|
+
if (Get-PackageSource -Name $Repository.Name) {
|
|
113
|
+
Set-PackageSource @Repository
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
Register-PackageSource @Repository
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
elseif (Get-Command Get-PSRepository) {
|
|
120
|
+
if (Get-PSRepository -Name $Repository.Name -ErrorAction SilentlyContinue) {
|
|
121
|
+
# The repo exists, we should use Set-PSRepository and splat parameters
|
|
122
|
+
Set-PSRepository @Repository
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
# The repo does not exist, use Register-PSRepository and splat
|
|
126
|
+
Register-PSRepository @Repository
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
throw "Cannot Set PS Repository, command Set or Register for PSRepository or PackageSource not found."
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function ConvertFrom-PesterOutputObject {
|
|
135
|
+
param (
|
|
136
|
+
[parameter(ValueFromPipeline=$true)]
|
|
137
|
+
[object]
|
|
138
|
+
$InputObject
|
|
139
|
+
)
|
|
140
|
+
begin {
|
|
141
|
+
$PesterModule = Import-Module Pester -Passthru
|
|
142
|
+
}
|
|
143
|
+
process {
|
|
144
|
+
$DescribeGroup = $InputObject.testresult | Group-Object Describe
|
|
145
|
+
foreach ($DescribeBlock in $DescribeGroup) {
|
|
146
|
+
$PesterModule.Invoke({Write-Screen $args[0]}, "Describing $($DescribeBlock.Name)")
|
|
147
|
+
$ContextGroup = $DescribeBlock.group | Group-Object Context
|
|
148
|
+
foreach ($ContextBlock in $ContextGroup) {
|
|
149
|
+
$PesterModule.Invoke({Write-Screen $args[0]}, "`tContext $($subheader.name)")
|
|
150
|
+
foreach ($TestResult in $ContextBlock.group) {
|
|
151
|
+
$PesterModule.Invoke({Write-PesterResult $args[0]}, $TestResult)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
$PesterModule.Invoke({Write-PesterReport $args[0]}, $InputObject)
|
|
157
|
+
}
|
|
158
|
+
}
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-pester
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steven Murawski
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-07-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -105,12 +105,12 @@ files:
|
|
|
105
105
|
- kitchen-pester.gemspec
|
|
106
106
|
- lib/kitchen/verifier/pester.rb
|
|
107
107
|
- lib/kitchen/verifier/pester_version.rb
|
|
108
|
-
- lib/support/
|
|
108
|
+
- lib/support/modules/PesterUtil/PesterUtil.psm1
|
|
109
109
|
homepage: https://github.com/test-kitchen/kitchen-pester
|
|
110
110
|
licenses:
|
|
111
111
|
- Apache-2.0
|
|
112
112
|
metadata: {}
|
|
113
|
-
post_install_message:
|
|
113
|
+
post_install_message:
|
|
114
114
|
rdoc_options: []
|
|
115
115
|
require_paths:
|
|
116
116
|
- lib
|
|
@@ -125,8 +125,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
125
125
|
- !ruby/object:Gem::Version
|
|
126
126
|
version: '0'
|
|
127
127
|
requirements: []
|
|
128
|
-
rubygems_version: 3.0.
|
|
129
|
-
signing_key:
|
|
128
|
+
rubygems_version: 3.0.6
|
|
129
|
+
signing_key:
|
|
130
130
|
specification_version: 4
|
|
131
131
|
summary: Test-Kitchen verifier for Pester.
|
|
132
132
|
test_files: []
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
function ConvertFrom-PesterOutputObject {
|
|
2
|
-
param (
|
|
3
|
-
[parameter(ValueFromPipeline=$true)]
|
|
4
|
-
[object]
|
|
5
|
-
$InputObject
|
|
6
|
-
)
|
|
7
|
-
begin {
|
|
8
|
-
$PesterModule = Import-Module Pester -Passthru
|
|
9
|
-
}
|
|
10
|
-
process {
|
|
11
|
-
$DescribeGroup = $InputObject.testresult | Group-Object Describe
|
|
12
|
-
foreach ($DescribeBlock in $DescribeGroup) {
|
|
13
|
-
$PesterModule.Invoke({Write-Screen $args[0]}, "Describing $($DescribeBlock.Name)")
|
|
14
|
-
$ContextGroup = $DescribeBlock.group | Group-Object Context
|
|
15
|
-
foreach ($ContextBlock in $ContextGroup) {
|
|
16
|
-
$PesterModule.Invoke({Write-Screen $args[0]}, "`tContext $($subheader.name)")
|
|
17
|
-
foreach ($TestResult in $ContextBlock.group) {
|
|
18
|
-
$PesterModule.Invoke({Write-PesterResult $args[0]}, $TestResult)
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
$PesterModule.Invoke({Write-PesterReport $args[0]}, $InputObject)
|
|
23
|
-
}
|
|
24
|
-
}
|