Skip to content

Commit

Permalink
feat: add utils module
Browse files Browse the repository at this point in the history
  • Loading branch information
cuinixam authored and JuReMq committed Jun 21, 2024
1 parent c69c464 commit 6617fdc
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 135 deletions.
74 changes: 3 additions & 71 deletions bootstrap.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -22,77 +22,6 @@ function Convert-CustomObjectToHashtable {
return $hashtable
}

function Remove-Path {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$path
)
if (Test-Path -Path $path -PathType Container) {
Write-Output "Deleting directory '$path' ..."
Remove-Item $path -Force -Recurse
}
elseif (Test-Path -Path $path -PathType Leaf) {
Write-Output "Deleting file '$path' ..."
Remove-Item $path -Force
}
}

function New-Directory {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$dir
)
if (-Not (Test-Path -Path $dir)) {
Write-Output "Creating directory '$dir' ..."
New-Item -ItemType Directory $dir
}
}


# Update/Reload current environment variable PATH with settings from registry
function Initialize-EnvPath {
# workaround for system-wide installations (e.g. in GitHub Actions)
if ($Env:USER_PATH_FIRST) {
$Env:Path = [System.Environment]::GetEnvironmentVariable("Path", "User") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "Machine")
}
else {
$Env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
}
}

function Invoke-CommandLine {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Justification = 'Usually this statement must be avoided (https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/avoid-using-invoke-expression?view=powershell-7.3), here it is OK as it does not execute unknown code.')]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$CommandLine,
[Parameter(Mandatory = $false, Position = 1)]
[bool]$StopAtError = $true,
[Parameter(Mandatory = $false, Position = 2)]
[bool]$Silent = $false
)
if (-Not $Silent) {
Write-Output "Executing: $CommandLine"
}
$global:LASTEXITCODE = 0
if ($Silent) {
# Omit information stream (6) and stdout (1)
Invoke-Expression $CommandLine 6>&1 | Out-Null
}
else {
Invoke-Expression $CommandLine
}
if ($global:LASTEXITCODE -ne 0) {
if ($StopAtError) {
Write-Error "Command line call `"$CommandLine`" failed with exit code $global:LASTEXITCODE"
}
else {
Write-Output "Command line call `"$CommandLine`" failed with exit code $global:LASTEXITCODE, continuing ..."
}
}
}

# Load configuration from bootstrap.json or use default values
function Get-BootstrapConfig {
$bootstrapConfig = @{
Expand Down Expand Up @@ -242,6 +171,9 @@ $InformationPreference = "Continue"
# Stop on first error
$ErrorActionPreference = "Stop"

# Load functions from utils.ps1
. "$PSScriptRoot\utils.ps1"

# Load config
$config = Get-BootstrapConfig

Expand Down
64 changes: 0 additions & 64 deletions tests/bootstrap.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -61,70 +61,6 @@ Describe "Convert-CustomObjectToHashtable" {
}
}

Describe "Invoke-CommandLine" {
BeforeEach {
Mock -CommandName Write-Output -MockWith {}
Mock -CommandName Write-Error -MockWith {}
}

It "shall not write the executed command to console if silent" {
Invoke-CommandLine "dir" -Silent $true

Should -Invoke -CommandName Write-Output -Exactly 0
Should -Invoke -CommandName Write-Error -Exactly 0
$global:LASTEXITCODE | Should -Be 0
}

It "shall write the executed command to console (default)" {
Invoke-CommandLine "dir"

Should -Invoke -CommandName Write-Output -Exactly 1
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Executing: dir" }
Should -Invoke -CommandName Write-Error -Exactly 0
}

It "shall write the executed command to console (default)" {
Invoke-CommandLine "git --version"

Should -Invoke -CommandName Write-Output -Exactly 1
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Executing: git --version" }
Should -Invoke -CommandName Write-Error -Exactly 0
}

It "shall write and create an error when existing command fails (default)" {
Invoke-CommandLine "git fanatic"

Should -Invoke -CommandName Write-Output -Exactly 1
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Executing: git fanatic" }
Should -Invoke -CommandName Write-Error -Exactly 1
Should -Invoke -CommandName Write-Error -Exactly 1 -ParameterFilter { $Message -eq "Command line call `"git fanatic`" failed with exit code 1" }
}

It "shall write the command but not create an error when existing command fails" {
Invoke-CommandLine "git fanatic" -StopAtError $false

Should -Invoke -CommandName Write-Output -Exactly 2
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Executing: git fanatic" }
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Command line call `"git fanatic`" failed with exit code 1, continuing ..." }
Should -Invoke -CommandName Write-Error -Exactly 0
}

It "shall not write the command but create and write an error when existing command fails" {
Invoke-CommandLine "git fanatic" -Silent $true

Should -Invoke -CommandName Write-Output -Exactly 0
Should -Invoke -CommandName Write-Error -Exactly 1
Should -Invoke -CommandName Write-Error -Exactly 1 -ParameterFilter { $Message -eq "Command line call `"git fanatic`" failed with exit code 1" }
}
It "shall not write the command nor create and write an error when existing command fails" {
Invoke-CommandLine "git fanatic" -Silent $true -StopAtError $false

Should -Invoke -CommandName Write-Output -Exactly 1
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Command line call `"git fanatic`" failed with exit code 1, continuing ..." }
Should -Invoke -CommandName Write-Error -Exactly 0
}
}

Describe "Get-BootstrapConfig" {
It "should return the default configuration" {
# Arrange
Expand Down
79 changes: 79 additions & 0 deletions tests/utils.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
BeforeAll {
# Load SUT
$sut = (Split-Path -Leaf $PSCommandPath).Replace('.Tests.ps1', '.ps1')
# Inhibit execution of Main function of SUT
Set-Alias Main out-null
. ".\$sut"
Remove-Item Alias:Main

class ComparableHashTable : Hashtable {
ComparableHashTable($obj) : base($obj) {}
[string] ToString() {
return ($this | ConvertTo-Json)
}
}
}

Describe "Invoke-CommandLine" {
BeforeEach {
Mock -CommandName Write-Output -MockWith {}
Mock -CommandName Write-Error -MockWith {}
}

It "shall not write the executed command to console if silent" {
Invoke-CommandLine "dir" -PrintCommand $false

Should -Invoke -CommandName Write-Output -Exactly 0
Should -Invoke -CommandName Write-Error -Exactly 0
$global:LASTEXITCODE | Should -Be 0
}

It "shall write the executed command to console (default)" {
Invoke-CommandLine "dir"

Should -Invoke -CommandName Write-Output -Exactly 1
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Executing: dir" }
Should -Invoke -CommandName Write-Error -Exactly 0
}

It "shall write the executed command to console (default)" {
Invoke-CommandLine "git --version"

Should -Invoke -CommandName Write-Output -Exactly 1
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Executing: git --version" }
Should -Invoke -CommandName Write-Error -Exactly 0
}

It "shall write and create an error when existing command fails (default)" {
Invoke-CommandLine "git fanatic"

Should -Invoke -CommandName Write-Output -Exactly 1
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Executing: git fanatic" }
Should -Invoke -CommandName Write-Error -Exactly 1
Should -Invoke -CommandName Write-Error -Exactly 1 -ParameterFilter { $Message -eq "Command line call `"git fanatic`" failed with exit code 1" }
}

It "shall write the command but not create an error when existing command fails" {
Invoke-CommandLine "git fanatic" -StopAtError $false

Should -Invoke -CommandName Write-Output -Exactly 2
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Executing: git fanatic" }
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Command line call `"git fanatic`" failed with exit code 1, continuing ..." }
Should -Invoke -CommandName Write-Error -Exactly 0
}

It "shall not write the command but create and write an error when existing command fails" {
Invoke-CommandLine "git fanatic" -Silent $true -PrintCommand $false

Should -Invoke -CommandName Write-Output -Exactly 0
Should -Invoke -CommandName Write-Error -Exactly 1
Should -Invoke -CommandName Write-Error -Exactly 1 -ParameterFilter { $Message -eq "Command line call `"git fanatic`" failed with exit code 1" }
}
It "shall not write the command nor create and write an error when existing command fails" {
Invoke-CommandLine "git fanatic" -Silent $true -PrintCommand $false -StopAtError $false

Should -Invoke -CommandName Write-Output -Exactly 1
Should -Invoke -CommandName Write-Output -Exactly 1 -ParameterFilter { $InputObject -eq "Command line call `"git fanatic`" failed with exit code 1, continuing ..." }
Should -Invoke -CommandName Write-Error -Exactly 0
}
}
134 changes: 134 additions & 0 deletions utils.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<#
.DESCRIPTION
Utility methods for common tasks.
#>

function Invoke-CommandLine {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Justification = 'Usually this statement must be avoided (https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/avoid-using-invoke-expression?view=powershell-7.3), here it is OK as it does not execute unknown code.')]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$CommandLine,
[Parameter(Mandatory = $false, Position = 1)]
[bool]$StopAtError = $true,
[Parameter(Mandatory = $false, Position = 2)]
[bool]$PrintCommand = $true,
[Parameter(Mandatory = $false, Position = 3)]
[bool]$Silent = $false
)
if ($PrintCommand) {
Write-Output "Executing: $CommandLine"
}
$global:LASTEXITCODE = 0
if ($Silent) {
# Omit information stream (6) and stdout (1)
Invoke-Expression $CommandLine 6>&1 | Out-Null
}
else {
Invoke-Expression $CommandLine
}
if ($global:LASTEXITCODE -ne 0) {
if ($StopAtError) {
Write-Error "Command line call `"$CommandLine`" failed with exit code $global:LASTEXITCODE"
}
else {
Write-Output "Command line call `"$CommandLine`" failed with exit code $global:LASTEXITCODE, continuing ..."
}
}
}

# Update/Reload current environment variable PATH with settings from registry
function Initialize-EnvPath {
# workaround for system-wide installations (e.g. in GitHub Actions)
if ($Env:USER_PATH_FIRST) {
$Env:Path = [System.Environment]::GetEnvironmentVariable("Path", "User") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "Machine")
}
else {
$Env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
}
}

function Remove-Path {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$path
)
if (Test-Path -Path $path -PathType Container) {
Write-Output "Deleting directory '$path' ..."
Remove-Item $path -Force -Recurse
}
elseif (Test-Path -Path $path -PathType Leaf) {
Write-Output "Deleting file '$path' ..."
Remove-Item $path -Force
}
}

function New-Directory {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$dir
)
if (-Not (Test-Path -Path $dir)) {
Write-Output "Creating directory '$dir' ..."
New-Item -ItemType Directory $dir
}
}

function CloneOrPullGitRepo {
param (
[Parameter(Mandatory = $true)]
[string]$RepoUrl,
[Parameter(Mandatory = $true)]
[string]$TargetDirectory,
[Parameter(Mandatory = $false)]
[string]$Tag,
[Parameter(Mandatory = $false)]
[string]$Branch = "develop"
)

$baselineName = "branch"
$baseline = $Branch
if ($Tag) {
$baselineName = "tag"
$baseline = $Tag
}

if (Test-Path -Path "$TargetDirectory\.git" -PathType Container) {
# When the repository directory exists, fetch the latest changes and checkout the baseline
try {
Push-Location $TargetDirectory
Invoke-CommandLine "git fetch --tags" -Silent $true
if ($Tag) {
Invoke-CommandLine "git checkout $Tag --quiet" -Silent $true
}
else {
Invoke-CommandLine "git checkout -B $Branch origin/$Branch --quiet" -Silent $true
}
Invoke-CommandLine "git reset --hard"
return
}
catch {
Write-Output "Failed to checkout $baselineName '$baseline' in repository '$RepoUrl' at directory '$TargetDirectory'."
}
finally {
Pop-Location
}
}

# Repo directory does not exist, remove any possible leftovers and get a fresh clone
try {
Remove-Path $TargetDirectory
New-Directory $TargetDirectory
Push-Location $TargetDirectory
Invoke-CommandLine "git -c advice.detachedHead=false clone --branch $baseline $RepoUrl ." -Silent $true
Invoke-CommandLine "git config pull.rebase true" -Silent $true -PrintCommand $false
Invoke-CommandLine "git log -1 --pretty='format:%h %B'" -PrintCommand $false
}
catch {
Write-Output "Failed to clone repository '$RepoUrl' at directory '$TargetDirectory'."
}
finally {
Pop-Location
}
}

0 comments on commit 6617fdc

Please sign in to comment.