From 9018aa588551bd94267832962129505863dc0eb6 Mon Sep 17 00:00:00 2001 From: wild-devops <33394863+wild-devops@users.noreply.github.com> Date: Sun, 27 Oct 2019 19:09:31 +0300 Subject: [PATCH 1/6] Rework template concepts --- examples/hello_pwshake.yaml | 7 ++++ examples/msbuild_pwshake.yaml | 16 ++++--- examples/templates/templates.yaml | 63 +++++++++++++--------------- pwshake.ps1 | 2 +- pwshake/pwshake.psd1 | 2 +- pwshake/pwshake.psm1 | 6 +++ pwshake/scripts/Execute-Step.ps1 | 46 -------------------- pwshake/scripts/Invoke-Step.ps1 | 4 +- pwshake/scripts/Invoke-pwshake.ps1 | 2 +- pwshake/scripts/Normalize-Config.ps1 | 4 +- pwshake/scripts/Normalize-Step.ps1 | 44 +++++++++++-------- pwshake/templates/cmd.yaml | 8 ++++ pwshake/templates/invoke_tasks.yaml | 7 ++++ pwshake/templates/script.yaml | 10 +++++ pwshake/templates/shell.yaml | 4 ++ 15 files changed, 112 insertions(+), 113 deletions(-) delete mode 100644 pwshake/scripts/Execute-Step.ps1 create mode 100644 pwshake/templates/cmd.yaml create mode 100644 pwshake/templates/invoke_tasks.yaml create mode 100644 pwshake/templates/script.yaml create mode 100644 pwshake/templates/shell.yaml diff --git a/examples/hello_pwshake.yaml b/examples/hello_pwshake.yaml index d987a59..7e1aba3 100644 --- a/examples/hello_pwshake.yaml +++ b/examples/hello_pwshake.yaml @@ -7,6 +7,7 @@ attributes: # List of directories relative to this file location where executable scripts are looking for (can be omitted) scripts_directories: - . + - pwshake_scripts # Declaration of run lists that compose and determine order of executing for scripts tasks: @@ -14,6 +15,12 @@ tasks: - pwsh: | Write-Host "{{pwsh1}}" Write-Host "{{pwsh2}}" + - cmd: | + ls . + - create_windows + - script: create_linux + - shell: | + ls . # Run lists to current execute invoke_tasks: diff --git a/examples/msbuild_pwshake.yaml b/examples/msbuild_pwshake.yaml index a219892..c3cb401 100644 --- a/examples/msbuild_pwshake.yaml +++ b/examples/msbuild_pwshake.yaml @@ -6,16 +6,14 @@ attributes: tasks: msbuild: - - template: msbuild - parameters: - project: '{{pwshake_path}}/example.msbuild.proj' - - template: msbuild + - msbuild: + - msbuild: '{{pwshake_path}}/example.msbuild.proj' + - msbuild: on_error: continue - parameters: - project: '{{pwshake_path}}/example.msbuild.proj' - targets: Build - properties: Configuration={{configuration}} - - template: dotnet + project: '{{pwshake_path}}/example.msbuild.proj' + targets: Build + properties: Configuration={{configuration}} + - dotnet: invoke_tasks: - msbuild diff --git a/examples/templates/templates.yaml b/examples/templates/templates.yaml index 19d4e61..a7f35bd 100644 --- a/examples/templates/templates.yaml +++ b/examples/templates/templates.yaml @@ -1,55 +1,48 @@ templates: - python: - parameters: - file: - options: - cmd: | - python [[file]] [[options]] dotnet: - parameters: - command: - options: --version - cmd: | - dotnet [[command]] [[options]] + command: + options: --version + powershell: | + "dotnet $($step.command) $($step.options)" | Cmd-Shell msbuild: - parameters: - project: /version - targets: - properties: - options: - pwsh: | + project: /version + targets: + properties: + options: + powershell: | $cmd = if (${is-Linux}) {'dotnet msbuild'} else {'msbuild'} - if ($step.parameters.project) { - $cmd += " $($step.parameters.project)" + if ($step.msbuild) { + $cmd += " $($step.msbuild)" + } elseif ($step.project) { + $cmd += " $($step.project)" } - if ($step.parameters.targets) { - foreach ($target in $step.parameters.targets) { + if ($step.targets) { + foreach ($target in $step.targets) { $cmd += " /t:$target" } } - if ($step.parameters.properties) { - foreach ($property in $step.parameters.properties) { + if ($step.properties) { + foreach ($property in $step.properties) { $cmd += " /p:$property" } } - if ($step.parameters.options) { - foreach ($option in $step.parameters.options) { + if ($step.options) { + foreach ($option in $step.options) { $cmd += " $option" } } $cmd | Cmd-Shell transform_xml_file: - parameters: - path: - appends: - updates: - removes: - pwsh: | - $path = $step.parameters.path - $appends = $step.parameters.appends - $updates = $step.parameters.updates - $removes = $step.parameters.removes + path: + appends: + updates: + removes: + powershell: | + $path = $step.path + $appends = $step.appends + $updates = $step.updates + $removes = $step.removes Write-Host "Transforming file '$(Resolve-Path $path)'" $xml = [xml](Get-Content (Resolve-Path $path) -Raw) if ($appends) { diff --git a/pwshake.ps1 b/pwshake.ps1 index 4787559..303f8fc 100644 --- a/pwshake.ps1 +++ b/pwshake.ps1 @@ -22,7 +22,7 @@ param( [switch]$DryRun, [Parameter(Mandatory = $false)] - [string]$Version = "1.1.0" + [string]$Version = "1.2.0" ) $ErrorActionPreference = "Stop" diff --git a/pwshake/pwshake.psd1 b/pwshake/pwshake.psd1 index 13f8b8a..babd8a3 100644 --- a/pwshake/pwshake.psd1 +++ b/pwshake/pwshake.psd1 @@ -12,7 +12,7 @@ RootModule = 'pwshake.psm1' # Version number of this module. -ModuleVersion = '1.1.0' +ModuleVersion = '1.2.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/pwshake/pwshake.psm1 b/pwshake/pwshake.psm1 index 7fa7e33..43362fe 100644 --- a/pwshake/pwshake.psm1 +++ b/pwshake/pwshake.psm1 @@ -14,6 +14,12 @@ foreach ($script in $scripts) { # Shared variables [bool]${is-Windows} = ([System.Environment]::OSVersion.Platform -match 'Win') [bool]${is-Linux} = (-not ${is-Windows}) +[hashtable]${pwshake-context} = @{} +${pwshake-context}.templates = @{} +foreach ($template in (Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'templates/*.yaml') -Recurse)) { + $step = Get-Content $template -Raw | ConvertFrom-Yaml + ${pwshake-context}.templates = Merge-Hashtables ${pwshake-context}.templates $step.templates +} New-Alias -Name pwshake -Value Invoke-pwshake -Force diff --git a/pwshake/scripts/Execute-Step.ps1 b/pwshake/scripts/Execute-Step.ps1 deleted file mode 100644 index 4ac587a..0000000 --- a/pwshake/scripts/Execute-Step.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -function Execute-Step { - [CmdletBinding()] - param ( - [Parameter(Position = 0, Mandatory = $true)] - [hashtable]$config, - - [Parameter(Position = 1, Mandatory = $true)] - [hashtable]$step - ) - process { - $ErrorActionPreference = "Continue" - - if (-not (Invoke-Expression $step.when)) { - Write-Host "`t`tBypassed because of when: [$($step.when)] = $(Invoke-Expression $step.when)" - return; - } - - if ($step.template) { - Execute-Step $config (Normalize-Template $step $config) - } elseif ($step.script) { - $paths = $config.scripts_directories | ForEach-Object { Join-Path $config.attributes.pwshake_path -ChildPath $_ } - $script_path = Get-ChildItem $paths -File ` - | Where-Object BaseName -eq $step.script ` - | Select-Object -ExpandProperty FullName - Write-Host "Script file: $script_path" - if (-not $script_path) { throw "Script file: $($step.script).ps1 not found." } - & $script_path -attributes $config.attributes - } elseif ($step.powershell) { - Write-Host "powershell: $($step.powershell)" - Invoke-Expression $step.powershell - } elseif ($step.cmd) { - $cmd = "" - foreach ($item in ($step['cmd'] -split '\^\s*\n')) { - $cmd += $item - } - Cmd-Shell $cmd -errorMessage "$($step.name) failed." - } elseif ($step.invoke_tasks) { - $tasks = Arrange-Tasks $config $step.invoke_tasks - foreach ($task in $tasks) { - Invoke-Task $task $config - } - } - - if (-not $?) { throw "$($step.name) failed." } - } -} diff --git a/pwshake/scripts/Invoke-Step.ps1 b/pwshake/scripts/Invoke-Step.ps1 index d18aff1..7fcff4e 100644 --- a/pwshake/scripts/Invoke-Step.ps1 +++ b/pwshake/scripts/Invoke-Step.ps1 @@ -29,8 +29,8 @@ function Invoke-Step { Log-Output "Execute step: $($step.name)" $config $logOut = @() $global:LASTEXITCODE = 0 - Execute-Step $config $step *>&1 | Tee-Object -Variable logOut | Log-Output -config $config - if (($LASTEXITCODE -ne 0) -and ($throwOn)) { + Invoke-Expression $step.powershell *>&1 | Tee-Object -Variable logOut | Log-Output -config $config + if ((($LASTEXITCODE -ne 0) -or (-not $?)) -and ($throwOn)) { $lastErr = $logOut | Where-Object {$_ -is [Management.Automation.ErrorRecord]} | Select-Object -Last 1 if (-not $lastErr) { $lastErr = "$($step.name) failed." diff --git a/pwshake/scripts/Invoke-pwshake.ps1 b/pwshake/scripts/Invoke-pwshake.ps1 index 260d450..5200443 100644 --- a/pwshake/scripts/Invoke-pwshake.ps1 +++ b/pwshake/scripts/Invoke-pwshake.ps1 @@ -2,7 +2,7 @@ function Invoke-pwshake { [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory = $false)] - [string]$ConfigPath = ".\pwshake.yaml", + [string]$ConfigPath = "./pwshake.yaml", [Parameter(Position = 1, Mandatory = $false)] [Alias("RunLists", "Roles")] diff --git a/pwshake/scripts/Normalize-Config.ps1 b/pwshake/scripts/Normalize-Config.ps1 index 1ea6c19..9c2298c 100644 --- a/pwshake/scripts/Normalize-Config.ps1 +++ b/pwshake/scripts/Normalize-Config.ps1 @@ -5,13 +5,15 @@ function Normalize-Config { [hashtable]$config ) process { + $templates = Coalesce $config.templates, @{} + ${pwshake-context}.templates = Merge-Hashtables ${pwshake-context}.templates $templates return @{ includes = @() + (Coalesce $config.includes, @()); attributes = Coalesce $config.attributes, @{}; attributes_overrides = @() + (Coalesce $config.attributes_overrides, @()); scripts_directories = @() + (Coalesce $config.scripts_directories, @()); tasks = Coalesce $config.tasks, $config.tasks, $config.roles, @{}; - templates = Coalesce $config.templates, @{}; + templates = $templates; invoke_tasks = @() + (Coalesce $config.invoke_tasks, $config.invoke_run_lists, $config.apply_roles, @()); } } diff --git a/pwshake/scripts/Normalize-Step.ps1 b/pwshake/scripts/Normalize-Step.ps1 index 3048a1d..6d16c38 100644 --- a/pwshake/scripts/Normalize-Step.ps1 +++ b/pwshake/scripts/Normalize-Step.ps1 @@ -12,32 +12,42 @@ function Normalize-Step { if (-not $item) { return $null } + $step = @{ + name = Coalesce $item.name, "step_$([Math]::Abs($item.GetHashCode()))"; + when = (Normalize-When $item); + work_dir = Coalesce $item.work_dir, $item.in; + on_error = Coalesce $item.on_error, 'throw'; + powershell = Coalesce $item.powershell, $item.pwsh; + } + if ($item -is [string]) { - $item = @{ name = $item; script = $item } + $step = Merge-Hashtables $step @{ name = $item; script = $item } } elseif ($item -is [Hashtable]) { - if ($item.Keys.Length) { + $step = Merge-Hashtables $step $item +<# + $reserved_keys = $step.Keys + $config.templates.Keys + ${pwshake-context}.templates.Keys + if ($item.Keys.Length -eq 1) { $key = $item.Keys | Select-Object -First 1 - $reserved_keys = @('name','script','powershell','cmd','template', 'parameters','when','invoke_tasks','work_dir','on_error') - if ((-not ($key -in $reserved_keys)) -and ($item[$key] -is [hashtable])) { - $item = $item[$key] - $item.name = Coalesce $item.name, $key + if (-not ($key -in $reserved_keys)) { + $step = Normalize-Step (Merge-Hashtables $step $item) $config } + } else { + } +#> } else { throw "Unknown Task item type: $($item.GetType().Name)" } - return @{ - name = Coalesce $item.name, "step_$([Math]::Abs($item.GetHashCode()))"; - when = (Normalize-When $item); - work_dir = Coalesce $item.work_dir, $item.in; - on_error = Coalesce $item.on_error, 'throw'; - script = $item.script; - powershell = Coalesce $item.powershell, $item.pwsh; - cmd = Coalesce $item.cmd, $item.shell; - invoke_tasks = Coalesce $item.invoke_tasks, $item.apply_roles, $item.invoke_run_lists, @(); - template = $item.template; - parameters = Coalesce $item.parameters, @{}; + foreach ($key in ${pwshake-context}.templates.Keys) { + if ($step.Keys -contains $key) { + $step = Merge-Hashtables ${pwshake-context}.templates[$key] $step + $step.powershell = ${pwshake-context}.templates[$key].powershell + #Write-Host "<>:`n$(cty $step)" + break; + } } + + return $step } } diff --git a/pwshake/templates/cmd.yaml b/pwshake/templates/cmd.yaml new file mode 100644 index 0000000..f8c45f3 --- /dev/null +++ b/pwshake/templates/cmd.yaml @@ -0,0 +1,8 @@ +templates: + cmd: + powershell: | + $cmd = "" + foreach ($line in ($step.cmd -split '\^\s*\n')) { + $cmd += $line + } + Cmd-Shell $cmd \ No newline at end of file diff --git a/pwshake/templates/invoke_tasks.yaml b/pwshake/templates/invoke_tasks.yaml new file mode 100644 index 0000000..ac8fd49 --- /dev/null +++ b/pwshake/templates/invoke_tasks.yaml @@ -0,0 +1,7 @@ +templates: + invoke_tasks: + powershell: | + $tasks = Arrange-Tasks $config $step.invoke_tasks + foreach ($task in $tasks) { + Invoke-Task $task $config + } diff --git a/pwshake/templates/script.yaml b/pwshake/templates/script.yaml new file mode 100644 index 0000000..ad14a07 --- /dev/null +++ b/pwshake/templates/script.yaml @@ -0,0 +1,10 @@ +templates: + script: + powershell: | + $paths = $config.scripts_directories | ForEach-Object { Join-Path $config.attributes.pwshake_path -ChildPath $_ } + $script_path = Get-ChildItem $paths -File ` + | Where-Object BaseName -eq $step.script ` + | Select-Object -ExpandProperty FullName + Write-Host "Script file: $script_path" + if (-not $script_path) { throw "Script file: $($step.script).ps1 not found." } + & $script_path -attributes $config.attributes diff --git a/pwshake/templates/shell.yaml b/pwshake/templates/shell.yaml new file mode 100644 index 0000000..daeb857 --- /dev/null +++ b/pwshake/templates/shell.yaml @@ -0,0 +1,4 @@ +templates: + shell: + powershell: | + Cmd-Shell $step.shell \ No newline at end of file From 88457a8e7fc7cfa741b7b2e90cf9f2ea3969a329 Mon Sep 17 00:00:00 2001 From: wild-devops <33394863+wild-devops@users.noreply.github.com> Date: Sun, 27 Oct 2019 21:59:35 +0300 Subject: [PATCH 2/6] Rework steps normalizing --- examples/msbuild_pwshake.yaml | 13 +++++++---- examples/templates/templates.yaml | 8 +++---- .../templates/transform_xml_file_test.yaml | 10 ++++---- pwshake/scripts/Normalize-Step.ps1 | 23 +++++++++++-------- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/examples/msbuild_pwshake.yaml b/examples/msbuild_pwshake.yaml index c3cb401..ef868e5 100644 --- a/examples/msbuild_pwshake.yaml +++ b/examples/msbuild_pwshake.yaml @@ -9,11 +9,16 @@ tasks: - msbuild: - msbuild: '{{pwshake_path}}/example.msbuild.proj' - msbuild: - on_error: continue - project: '{{pwshake_path}}/example.msbuild.proj' - targets: Build - properties: Configuration={{configuration}} + name: Build all + on_error: continue + project: '{{pwshake_path}}/example.msbuild.proj' + targets: Build + properties: Configuration={{configuration}} - dotnet: + - dotnet: + name: Build again + command: msbuild + options: '{{pwshake_path}}/example.msbuild.proj' invoke_tasks: - msbuild diff --git a/examples/templates/templates.yaml b/examples/templates/templates.yaml index a7f35bd..5746d84 100644 --- a/examples/templates/templates.yaml +++ b/examples/templates/templates.yaml @@ -39,12 +39,12 @@ templates: updates: removes: powershell: | - $path = $step.path + $path = Normalize-Path (Coalesce $step.transform_xml_file, $step.path) $config $appends = $step.appends $updates = $step.updates $removes = $step.removes - Write-Host "Transforming file '$(Resolve-Path $path)'" - $xml = [xml](Get-Content (Resolve-Path $path) -Raw) + Write-Host "Transforming file '$path'" + $xml = [xml](Get-Content $path -Raw) if ($appends) { foreach ($key in $appends.Keys) { foreach ($node in $xml.SelectNodes($key)) { @@ -73,4 +73,4 @@ templates: } } } - $xml.Save((Resolve-Path $path)) + $xml.Save($path) diff --git a/examples/templates/transform_xml_file_test.yaml b/examples/templates/transform_xml_file_test.yaml index d808708..0dfe30e 100644 --- a/examples/templates/transform_xml_file_test.yaml +++ b/examples/templates/transform_xml_file_test.yaml @@ -3,9 +3,7 @@ includes: tasks: transform_xml_file: - - template: transform_xml_file - in: examples/templates - parameters: + - transform_xml_file: path: test.xml appends: '/xml': '' @@ -14,9 +12,9 @@ tasks: '/xml/two/@three': 'four' removes: '/xml/six[@seven="wrong"]': - - pwsh: - Get-Content test.xml -Raw - in: examples/templates + - pwsh: | + Get-Content {{pwshake_path}}/test.xml -Raw + - transform_xml_file: test.xml invoke_tasks: - transform_xml_file \ No newline at end of file diff --git a/pwshake/scripts/Normalize-Step.ps1 b/pwshake/scripts/Normalize-Step.ps1 index 6d16c38..06c9d04 100644 --- a/pwshake/scripts/Normalize-Step.ps1 +++ b/pwshake/scripts/Normalize-Step.ps1 @@ -23,18 +23,22 @@ function Normalize-Step { if ($item -is [string]) { $step = Merge-Hashtables $step @{ name = $item; script = $item } } elseif ($item -is [Hashtable]) { - $step = Merge-Hashtables $step $item -<# - $reserved_keys = $step.Keys + $config.templates.Keys + ${pwshake-context}.templates.Keys - if ($item.Keys.Length -eq 1) { - $key = $item.Keys | Select-Object -First 1 - if (-not ($key -in $reserved_keys)) { - $step = Normalize-Step (Merge-Hashtables $step $item) $config + if ($item.Keys.Count -eq 1) { + $key = $item.Keys[0] + $content = $item[$key][0] + if ($content -is [string]) { + $step = Merge-Hashtables $step @{ $($key) = $content } + } elseif ($content -is [Hashtable]) { + $step = Merge-Hashtables $step $content + $step.$($key) = $null + } elseif (-not ($content)) { + $step.$($key) = $null + } else { + throw "Unknown Task item: $(ConvertTo-Yaml $content)" } } else { - + $step = Merge-Hashtables $step $item } -#> } else { throw "Unknown Task item type: $($item.GetType().Name)" } @@ -43,7 +47,6 @@ function Normalize-Step { if ($step.Keys -contains $key) { $step = Merge-Hashtables ${pwshake-context}.templates[$key] $step $step.powershell = ${pwshake-context}.templates[$key].powershell - #Write-Host "<>:`n$(cty $step)" break; } } From 83346e96a4f6bbb42c3e2022eeb636241574c260 Mon Sep 17 00:00:00 2001 From: wild-devops <33394863+wild-devops@users.noreply.github.com> Date: Sun, 27 Oct 2019 22:26:05 +0300 Subject: [PATCH 3/6] Fix some tests --- tests/Normalize-Step.Context.ps1 | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/tests/Normalize-Step.Context.ps1 b/tests/Normalize-Step.Context.ps1 index faedd70..5d0d020 100644 --- a/tests/Normalize-Step.Context.ps1 +++ b/tests/Normalize-Step.Context.ps1 @@ -12,16 +12,15 @@ Context "Normalize-Step" { $actual | Should -BeOfType System.Collections.Hashtable $actual.Keys | Should -Contain "name" - $actual.Keys | Should -Contain "script" + $actual.Keys | Should -Contain "when" + $actual.Keys | Should -Contain "work_dir" + $actual.Keys | Should -Contain "on_error" $actual.Keys | Should -Contain "powershell" - $actual.Keys | Should -Contain "cmd" - $actual.Keys | Should -Contain "template" - $actual.Keys | Should -Contain "parameters" $actual.name | Should -BeLike "step_*" - $actual.script | Should -BeNullOrEmpty + $actual.when | Should -Be '$true' + $actual.work_dir | Should -BeNullOrEmpty + $actual.on_error | Should -Be 'throw' $actual.powershell | Should -BeNullOrEmpty - $actual.cmd | Should -BeNullOrEmpty - $actual.template | Should -BeNullOrEmpty } It "Should return $scriptPath in 'script' key" { @@ -55,16 +54,12 @@ Context "Normalize-Step" { It "Should normalize a full structure" { $mock = @" name: Mock -script: $scriptPath powershell: pwsh -cmd: python.exe "@ | ConvertFrom-Yaml $actual = Normalize-Step $mock $actual.name | Should -Be "Mock" - $actual.script | Should -Be $scriptPath $actual.powershell | Should -Be "pwsh" - $actual.cmd | Should -Be "python.exe" } It "Should normalize a single string as 'script' and 'name' keys" { @@ -76,8 +71,7 @@ run_list: $actual.name | Should -Be "Mock" $actual.script | Should -Be "Mock" - $actual.powershell | Should -BeNullOrEmpty - $actual.cmd | Should -BeNullOrEmpty + $actual.powershell | Should -Not -BeNullOrEmpty } It "Should normalize a 'powershell' key with default 'name'" { @@ -88,9 +82,7 @@ run_list: $actual = Normalize-Step $mock $actual.name | Should -BeLike "step_*" - $actual.script | Should -BeNullOrEmpty $actual.powershell | Should -Be "Mock" - $actual.cmd | Should -BeNullOrEmpty } It "Should normalize a 'cmd' key with default 'name'" { @@ -101,8 +93,7 @@ run_list: $actual = Normalize-Step $mock $actual.name | Should -BeLike "step_*" - $actual.script | Should -BeNullOrEmpty - $actual.powershell | Should -BeNullOrEmpty + $actual.powershell | Should -Not -BeNullOrEmpty $actual.cmd | Should -Be "Mock" } From fabcced25ff691b670a877fff86d17234e5de5ce Mon Sep 17 00:00:00 2001 From: wild-devops <33394863+wild-devops@users.noreply.github.com> Date: Mon, 28 Oct 2019 12:07:02 +0300 Subject: [PATCH 4/6] Fix all tests --- examples/templates/templates.yaml | 28 ---------------------------- pwshake/scripts/Invoke-Step.ps1 | 5 +++++ pwshake/scripts/Invoke-Task.ps1 | 2 +- pwshake/scripts/Normalize-Step.ps1 | 6 +++++- pwshake/templates/cmd.yaml | 5 ++++- pwshake/templates/invoke_tasks.yaml | 12 ++++++++++++ pwshake/templates/msbuild.yaml | 29 +++++++++++++++++++++++++++++ pwshake/templates/shell.yaml | 4 ---- tests/Normalize-Step.Context.ps1 | 6 ------ 9 files changed, 56 insertions(+), 41 deletions(-) create mode 100644 pwshake/templates/msbuild.yaml delete mode 100644 pwshake/templates/shell.yaml diff --git a/examples/templates/templates.yaml b/examples/templates/templates.yaml index 5746d84..f79cca8 100644 --- a/examples/templates/templates.yaml +++ b/examples/templates/templates.yaml @@ -4,34 +4,6 @@ templates: options: --version powershell: | "dotnet $($step.command) $($step.options)" | Cmd-Shell - msbuild: - project: /version - targets: - properties: - options: - powershell: | - $cmd = if (${is-Linux}) {'dotnet msbuild'} else {'msbuild'} - if ($step.msbuild) { - $cmd += " $($step.msbuild)" - } elseif ($step.project) { - $cmd += " $($step.project)" - } - if ($step.targets) { - foreach ($target in $step.targets) { - $cmd += " /t:$target" - } - } - if ($step.properties) { - foreach ($property in $step.properties) { - $cmd += " /p:$property" - } - } - if ($step.options) { - foreach ($option in $step.options) { - $cmd += " $option" - } - } - $cmd | Cmd-Shell transform_xml_file: path: diff --git a/pwshake/scripts/Invoke-Step.ps1 b/pwshake/scripts/Invoke-Step.ps1 index 7fcff4e..847793f 100644 --- a/pwshake/scripts/Invoke-Step.ps1 +++ b/pwshake/scripts/Invoke-Step.ps1 @@ -16,6 +16,11 @@ function Invoke-Step { $step = Normalize-Step $step $config $throwOn = ($step.on_error -eq 'throw') + if (-not (Invoke-Expression $step.when)) { + Log-Output "`t`tBypassed because of: [$($step.when)] = $(Invoke-Expression $step.when)" $config + continue; + } + try { if ($work_dir) { # Since actual execution is performed in the $step that can contain it's own .work_dir property diff --git a/pwshake/scripts/Invoke-Task.ps1 b/pwshake/scripts/Invoke-Task.ps1 index 7441c87..a85318f 100644 --- a/pwshake/scripts/Invoke-Task.ps1 +++ b/pwshake/scripts/Invoke-Task.ps1 @@ -14,7 +14,7 @@ function global:Invoke-Task { Log-Output "Invoke task: $($task.name)" $config if (-not (Invoke-Expression $task.when)) { - Log-Output "`t`tBypassed because of when: [$($task.when)] = $(Invoke-Expression $task.when)" $config + Log-Output "`t`tBypassed because of: [$($task.when)] = $(Invoke-Expression $task.when)" $config continue; } try { diff --git a/pwshake/scripts/Normalize-Step.ps1 b/pwshake/scripts/Normalize-Step.ps1 index 06c9d04..e9a612b 100644 --- a/pwshake/scripts/Normalize-Step.ps1 +++ b/pwshake/scripts/Normalize-Step.ps1 @@ -26,6 +26,10 @@ function Normalize-Step { if ($item.Keys.Count -eq 1) { $key = $item.Keys[0] $content = $item[$key][0] + $reserved_keys = $step.Keys + ${pwshake-context}.templates.Keys + if (-not ($reserved_keys -contains $key)) { + $step.name = $key + } if ($content -is [string]) { $step = Merge-Hashtables $step @{ $($key) = $content } } elseif ($content -is [Hashtable]) { @@ -34,7 +38,7 @@ function Normalize-Step { } elseif (-not ($content)) { $step.$($key) = $null } else { - throw "Unknown Task item: $(ConvertTo-Yaml $content)" + $step.$($key) = $content } } else { $step = Merge-Hashtables $step $item diff --git a/pwshake/templates/cmd.yaml b/pwshake/templates/cmd.yaml index f8c45f3..fb79174 100644 --- a/pwshake/templates/cmd.yaml +++ b/pwshake/templates/cmd.yaml @@ -5,4 +5,7 @@ templates: foreach ($line in ($step.cmd -split '\^\s*\n')) { $cmd += $line } - Cmd-Shell $cmd \ No newline at end of file + Cmd-Shell $cmd + shell: + powershell: | + Cmd-Shell $step.shell diff --git a/pwshake/templates/invoke_tasks.yaml b/pwshake/templates/invoke_tasks.yaml index ac8fd49..061efe3 100644 --- a/pwshake/templates/invoke_tasks.yaml +++ b/pwshake/templates/invoke_tasks.yaml @@ -5,3 +5,15 @@ templates: foreach ($task in $tasks) { Invoke-Task $task $config } + apply_roles: + powershell: | + $tasks = Arrange-Tasks $config $step.apply_roles + foreach ($task in $tasks) { + Invoke-Task $task $config + } + invoke_run_lists: + powershell: | + $tasks = Arrange-Tasks $config $step.invoke_run_lists + foreach ($task in $tasks) { + Invoke-Task $task $config + } diff --git a/pwshake/templates/msbuild.yaml b/pwshake/templates/msbuild.yaml new file mode 100644 index 0000000..214368b --- /dev/null +++ b/pwshake/templates/msbuild.yaml @@ -0,0 +1,29 @@ +templates: + msbuild: + project: /version + targets: + properties: + options: + powershell: | + $cmd = if (${is-Linux}) {'dotnet msbuild'} else {'msbuild'} + if ($step.msbuild) { + $cmd += " $($step.msbuild)" + } elseif ($step.project) { + $cmd += " $($step.project)" + } + if ($step.targets) { + foreach ($target in $step.targets) { + $cmd += " /t:$target" + } + } + if ($step.properties) { + foreach ($property in $step.properties) { + $cmd += " /p:$property" + } + } + if ($step.options) { + foreach ($option in $step.options) { + $cmd += " $option" + } + } + $cmd | Cmd-Shell diff --git a/pwshake/templates/shell.yaml b/pwshake/templates/shell.yaml deleted file mode 100644 index daeb857..0000000 --- a/pwshake/templates/shell.yaml +++ /dev/null @@ -1,4 +0,0 @@ -templates: - shell: - powershell: | - Cmd-Shell $step.shell \ No newline at end of file diff --git a/tests/Normalize-Step.Context.ps1 b/tests/Normalize-Step.Context.ps1 index 5d0d020..fbb0005 100644 --- a/tests/Normalize-Step.Context.ps1 +++ b/tests/Normalize-Step.Context.ps1 @@ -106,9 +106,7 @@ run_list: $actual = Normalize-Step $mock $actual.name | Should -Be "mock" - $actual.script | Should -BeNullOrEmpty $actual.powershell | Should -Be "Mock" - $actual.cmd | Should -BeNullOrEmpty } It "Should normalize full step with an explicit 'name'" { @@ -121,9 +119,7 @@ run_list: $actual = Normalize-Step $mock $actual.name | Should -Be "mock" - $actual.script | Should -BeNullOrEmpty $actual.powershell | Should -Be "Mock" - $actual.cmd | Should -BeNullOrEmpty } It "Should normalize implicit step with an explicit 'name'" { @@ -135,8 +131,6 @@ run_list: $actual = Normalize-Step $mock $actual.name | Should -Be "mock" - $actual.script | Should -BeNullOrEmpty $actual.powershell | Should -Be "Mock" - $actual.cmd | Should -BeNullOrEmpty } } From 1031fb7054ce87cc46728b939a5530af041d390b Mon Sep 17 00:00:00 2001 From: wild-devops <33394863+wild-devops@users.noreply.github.com> Date: Mon, 28 Oct 2019 13:59:02 +0300 Subject: [PATCH 5/6] Adjust pester module version --- pwshake.ps1 | 8 ++++---- pwshake/scripts/Invoke-Task.ps1 | 2 +- pwshake/scripts/Log-Output.ps1 | 2 +- tests/pwshake.Tests.ps1 | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pwshake.ps1 b/pwshake.ps1 index 303f8fc..0824c19 100644 --- a/pwshake.ps1 +++ b/pwshake.ps1 @@ -1,14 +1,14 @@ # Bootstrapper script for those who wants to run pwshake without prior importing the module. -# examples run from PowerShell: -# .\pwshake.ps1 ./examples/pwshake_config.yaml @("create_linux_istance","deploy_shake") @{override_to="local";artifact_id="42"} +# Example run from PowerShell: +# PS>./pwshake.ps1 ./examples/pwshake_config.yaml @("create_linux_istance","deploy_shake") @{override_to="local";artifact_id="42"} -# Must match parameter definitions for pwshake.psm1/invoke-pwshake +# Must match parameter definitions for pwshake.psm1/Invoke-pwshake function # otherwise named parameter binding fails [CmdletBinding()] param( [Alias("Path")] [Parameter(Position = 0, Mandatory = $false)] - [string]$ConfigPath = "$PSScriptRoot\pwshake.yaml", + [string]$ConfigPath = "$PSScriptRoot/pwshake.yaml", [Alias("RunLists", "Roles")] [Parameter(Position = 1, Mandatory = $false)] diff --git a/pwshake/scripts/Invoke-Task.ps1 b/pwshake/scripts/Invoke-Task.ps1 index a85318f..3728a67 100644 --- a/pwshake/scripts/Invoke-Task.ps1 +++ b/pwshake/scripts/Invoke-Task.ps1 @@ -1,4 +1,4 @@ -function global:Invoke-Task { +function Invoke-Task { [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] diff --git a/pwshake/scripts/Log-Output.ps1 b/pwshake/scripts/Log-Output.ps1 index 9dbf3ec..210048a 100644 --- a/pwshake/scripts/Log-Output.ps1 +++ b/pwshake/scripts/Log-Output.ps1 @@ -1,4 +1,4 @@ -function global:Log-Output { +function Log-Output { [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)] diff --git a/tests/pwshake.Tests.ps1 b/tests/pwshake.Tests.ps1 index 53833ba..68c6d30 100644 --- a/tests/pwshake.Tests.ps1 +++ b/tests/pwshake.Tests.ps1 @@ -2,9 +2,9 @@ $ErrorActionPreference = "Stop" [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Tls12' -if (-not (Get-Module -Name Pester | Where-Object Version -eq 4.6.0)) { - Install-Module -Name Pester -Repository PSGallery -Force -SkipPublisherCheck - Import-Module -Name Pester -Force +if (-not (Get-Module -Name Pester | Where-Object Version -eq 4.9.0)) { + Install-Module -Name Pester -Repository PSGallery -RequiredVersion 4.9.0 -Force -SkipPublisherCheck -Scope CurrentUser + Import-Module -Name Pester -Force -Global } $result = Invoke-Pester -Script $PSScriptRoot\module.Scope.ps1 -PassThru From f9d5a800a3882332a070f6427ac6fb67f5c5ebfa Mon Sep 17 00:00:00 2001 From: wild-devops <33394863+wild-devops@users.noreply.github.com> Date: Mon, 28 Oct 2019 18:32:26 +0300 Subject: [PATCH 6/6] Adjust template concept to initial state --- README.md | 3 +- doc/step.md | 45 +++++++------ doc/tasks.md | 17 ++++- doc/templates.md | 87 ++++++++++++++++++++++++++ examples/templates/hello.py | 2 + examples/templates/python.yaml | 14 +++++ examples/templates/python_pwshake.yaml | 16 +++++ tools/TODO.md | 4 -- 8 files changed, 161 insertions(+), 27 deletions(-) create mode 100644 doc/templates.md create mode 100644 examples/templates/hello.py create mode 100644 examples/templates/python.yaml create mode 100644 examples/templates/python_pwshake.yaml diff --git a/README.md b/README.md index 0eda0b1..92d1b5b 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ The result looks like the followed output including information about: * `PWSHAKE config:` - loaded config file content (may be rearranged due to overriding, interpolation, and merging metadata) * `Arranged tasks:` - tasks to be executed in actual order * `Invoke task:` - invoked tasks name -* `Execute :` - invoked steps caption +* `Execute step:` - invoked steps caption * Everything else - invoked scripts output ``` @@ -128,6 +128,7 @@ And it runs various tests and examples included in this repo. * [`includes:` element](/doc/includes.md) * [`invoke_tasks:` element](/doc/invoke_tasks.md) * [`tasks:` element](/doc/tasks.md) + * [`templates:` element](/doc/templates.md) * [implicit `[step]:` element](/doc/step.md) * [`scripts_directories:` element](/doc/scripts_directories.md) diff --git a/doc/step.md b/doc/step.md index 5416f46..f503109 100644 --- a/doc/step.md +++ b/doc/step.md @@ -9,12 +9,7 @@ The internal representation of a single **step** looks like a following **Powers when = "`$true"; work_dir = $null; on_error = "throw"; - script = $null; powershell = $null; - cmd = $null; - invoke_tasks = $null; - template = $null; - parameters = @{}; } ``` @@ -29,6 +24,10 @@ will be transformed into the following structure: ``` @{ name = "step name"; + when = "`$true"; + work_dir = $null; + on_error = "throw"; + powershell = $null; script = "script_name"; } ``` @@ -40,19 +39,7 @@ Pay attention that the `step1` identifier itself does not bring any actual value ``` In this case the `-` sign means that subsequent items in `yaml` hierarchy are keys of the same **Powershell** `[hashtable]`. -So, the full allowed form of the `- [step]:` element could be the following: -``` -- name: step name - script: script_name - powershell: "some inline powershell code" - cmd: "other inline cmd.exe commands" - template: my_template - when: $true -... -``` -But **PWSHAKE** engine take pecedence over the given structure items and executes only the first non empty item, in this case `script:` item with '`script_name`' value, all others (`powershell:`, `cmd:`, `template:`) are ignored. - -* ### - `[step]:` element implicit shortenings +* ## - `[step]:` element implicit shortenings Since the actual payload in the executed structure have only the two elements: * `name:` * first non empty of `[script: | powershell: | cmd:]` @@ -105,10 +92,28 @@ But **PWSHAKE** engine take pecedence over the given structure items and execute ``` This is the same as: ``` - - name: pwshake_2122574676 + - name: step_2122574676 powershell: rm ./ -recurse -force ``` +* ### - `[msbuild]:` element implicit shortenings + All things described above are eligible for the `msbuild:` element. + + + Since the actual payload of this element is the **MSBuild** project file name, so the shortening syntax use this value as a `project:` element value. + + Example: + ``` + - name: Build + msbuild: some_project_file_name + ``` + This is actually the same as: + ``` + - name: Build + msbuild: + project: some_project_file_name + ``` + * ### - `[when|only|except|skip_on]:` element implicit shortenings Since the `[when|only|except|skip_on]:` elements contain inline code that evaluated by **PWSHAKE** engine to make a decision whether or not to execute a particular **step** they can be omitted in general because of the default value is always set to `$true` (`-not ($true) ` for negation aliases `except:`, `skip_on:`). @@ -157,7 +162,7 @@ But **PWSHAKE** engine take pecedence over the given structure items and execute - step: name: Do all stuff if solution file is present scripts: - - powershell: $skip_it_all = -not (Test-Path MySolution.sln) + - powershell: $script:skip_it_all = -not (Test-Path MySolution.sln) - skip: $skip_it_all invoke_tasks: - clean diff --git a/doc/tasks.md b/doc/tasks.md index a411bfb..8d9894c 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -121,6 +121,20 @@ Every named element of the `tasks:` can contain some definitions to provide more -list -of -fake -parameters ``` + * `- msbuild:` - element to run **MSBuild** with particular settings + + Example: + ``` + tasks: + build: + - msbuild: .\MySolution.sln + - msbuild: + project: .\MyProject.csproj + targets: TransformConfigs + properties: Configuration=Debug + ``` + The above are 2 calls to **MSBuild**: the first is in shortened form and just uses default target (`Build`) and default options, the second uses given parameters `targets:` and `properties:` passed from `pwshake.yaml` config. + * `- [step]:` - an implicit element to fulfill the particular step settings in explicit way Example: @@ -134,8 +148,7 @@ Every named element of the `tasks:` can contain some definitions to provide more cmd: echo 'step2' - step3: name: Do msbuild task - template: msbuild - parameters: + msbuild: project: .\MyProject.csproj targets: TransformConfigs properties: Configuration=Debug diff --git a/doc/templates.md b/doc/templates.md new file mode 100644 index 0000000..679a374 --- /dev/null +++ b/doc/templates.md @@ -0,0 +1,87 @@ +## `templates:` **element** +Contains definitions of `step:` elements structure for reusing and syntax shortenings in the whole `pwshake.yaml` config. + +This tells to **PWSHAKE** engine how to substitute any structured `yaml` input in step definitions into an executable **Powershell** command. + +* ## `templates:` example + ``` + PS>cat python.yaml + templates: + python: + file: + inline: + powershell: | + if ($step.python) { + "python $($step.python)" | Cmd-Shell + } elseif ($step.file) { + "python $($step.file)" | Cmd-Shell + } elseif ($step.inline) { + python -c $step.inline + } else { + python --version + } + ``` + The given example can be used with regular `pwshake.yaml` config by including the template file `python.yaml` and using a new `python:` element as a regular step in tasks definition. + ``` + PS>cat hello.py + import sys + print("Hello " + sys.argv[1] + "!", file=sys.stdout, flush=True) + + PS>cat python_pwshake.yaml + includes: + - python.yaml + + tasks: + test_python_template: + - python: + - python: --version + - python: + inline: print('Hello pwshake!'); + - python: + file: | + {{pwshake_path}}/hello.py again + - python: '{{pwshake_path}}/hello.py twice' + + invoke_tasks: + - test_python_template + ``` + Output should look like the following: + ``` + PS>Invoke-pwshake ./python_pwshake.yaml + ... + Invoke task: test_python_template + Execute step: step_25540982 + Python 3.6.8 + Execute step: step_8493528 + bash: python --version + Python 3.6.8 + Execute step: step_46664441 + Hello pwshake! + Execute step: step_6213368 + bash: python /workdir/examples/templates/hello.py again + + Hello again! + Execute step: step_32712664 + bash: python /workdir/examples/templates/hello.py twice + Hello twice! + ``` + +* ## Implicit built-in `templates:` + + Some built-in `[step:]` template definitions are already included into the **PWSHAKE** module and loaded during the **PWSHAKE** engine initialization. + + So, they can be used in regular `pwshake.yaml` config without including either as external files or `templates:` element items. + + Examples: + ``` + PS>cat some_pwshake.yaml + ... + tasks: + - cmd: + - shell: + - msbuild: + - script: + - invoke_tasks: + ``` + All these steps above are actually substituted templates which are loaded from [this location](/pwshake/templates). + diff --git a/examples/templates/hello.py b/examples/templates/hello.py new file mode 100644 index 0000000..6a499ac --- /dev/null +++ b/examples/templates/hello.py @@ -0,0 +1,2 @@ +import sys +print("Hello " + sys.argv[1] + "!", file=sys.stdout, flush=True) diff --git a/examples/templates/python.yaml b/examples/templates/python.yaml new file mode 100644 index 0000000..1d7c8a5 --- /dev/null +++ b/examples/templates/python.yaml @@ -0,0 +1,14 @@ +templates: + python: + file: + inline: + powershell: | + if ($step.python) { + "python $($step.python)" | Cmd-Shell + } elseif ($step.file) { + "python $($step.file)" | Cmd-Shell + } elseif ($step.inline) { + python -c $step.inline + } else { + python --version + } diff --git a/examples/templates/python_pwshake.yaml b/examples/templates/python_pwshake.yaml new file mode 100644 index 0000000..972cb01 --- /dev/null +++ b/examples/templates/python_pwshake.yaml @@ -0,0 +1,16 @@ +includes: +- python.yaml + +tasks: + test_python_template: + - python: + - python: --version + - python: + inline: print('Hello pwshake!'); + - python: + file: | + {{pwshake_path}}/hello.py again + - python: '{{pwshake_path}}/hello.py twice' + +invoke_tasks: +- test_python_template diff --git a/tools/TODO.md b/tools/TODO.md index d69ceb2..975668a 100644 --- a/tools/TODO.md +++ b/tools/TODO.md @@ -1,7 +1,3 @@ -* adjust docs/examples according to new templates features - -* try to include default templates into module source - * implement verbosity levels for log\output * implement outer resources loading \ No newline at end of file