diff --git a/CHANGELOG.md b/CHANGELOG.md index ddf6b6d..e905a48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,21 +5,32 @@ All notable changes to the [PSTK](https://github.com/Akaizoku/PSTK) project will The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased](https://github.com/Akaizoku/PSTK/compare/1.2.3...HEAD) +## [1.2.4](https://github.com/Akaizoku/PSTK/releases/tag/1.2.4) - 2021-09-21 -Work in progress +Standardisation effort ### Added The following functions have been added: +- ConvertTo-RegularExpression +- Copy-Object +- Get-Object - New-RandomPassword +- New-SelfContainedPackage ### Changed +Date formats have been updated to follow ISO 8601 standard. + The following functions have been updated: +- Copy-Object - Copy-OrderedHashtable +- Get-KeyValue +- Get-KeyValue +- Remove-Object +- Write-Log ## [1.2.3](https://github.com/Akaizoku/PSTK/releases/tag/1.2.3) - 2020-03-26 diff --git a/PSTK.psd1 b/PSTK.psd1 index 63fa784..3474da2 100644 --- a/PSTK.psd1 +++ b/PSTK.psd1 @@ -3,7 +3,7 @@ # # Generated by: Florian Carrier # -# Generated on: 08/07/2021 +# Generated on: 02/09/2021 # @{ diff --git a/Public/Copy-Object.ps1 b/Public/Copy-Object.ps1 index e38b9c6..5c9da65 100644 --- a/Public/Copy-Object.ps1 +++ b/Public/Copy-Object.ps1 @@ -11,6 +11,7 @@ function Copy-Object { .PARAMETER Destination The destination parameter corresponds to the target copy location. + /!\ This path must be a directory. .PARAMETER Filter The filter parameter corresponds to the filter to apply to the name of objects to copy. @@ -42,6 +43,7 @@ function Copy-Object { HelpMessage = "Target location" )] [ValidateNotNullOrEmpty ()] + [Alias ("Target")] [System.String] $Destination, [Parameter ( @@ -84,23 +86,27 @@ function Copy-Object { $Destination = Join-Path -Path $Destination -ChildPath (Split-Path -Path $Path -Leaf) if (Test-Object -Path $Destination -NotFound) { # Ensure destination exists to prevent error "Copy-Item : Container cannot be copied onto existing leaf item." - Write-Log -Type "DEBUG" -Message "Create destination directory $Destination" - New-Item -Path $Destination -ItemType "Directory" + Write-Log -Type "DEBUG" -Message "Create destination directory ""$Destination""" + New-Item -Path $Destination -ItemType "Directory" | Out-Null } else { - if ($Force -eq $true) { - Write-Log -Type "DEBUG" -Message "Destination already exists - files will be overwritten" - } else { - Write-Log -Type "ERROR" -Message "Destination path already exists $Destination" + if ($Force -eq $false) { + Write-Log -Type "ERROR" -Message "Destination path already exists ""$Destination""" Write-Log -Type "WARN" -Message "Use the -Force switch to overwrite" Stop-Script -ExitCode 1 } } # Copy directory content - Write-Log -Type "DEBUG" -Message "Copy directory and content $Path to $Destination" + Write-Log -Type "DEBUG" -Message "Copy directory and content ""$Path"" to ""$Destination""" Copy-Item -Path "$Path\*" -Destination $Destination -Filter $Filter -Exclude $Exclude -Recurse -Force:$Force } elseif ($Target -is [System.IO.FileInfo]) { # If target is a single file - Write-Log -Type "DEBUG" -Message "Copy file $Path to $Destination" + Write-Log -Type "DEBUG" -Message "Copy file ""$Path"" to ""$Destination""" + # Check target destination type + if (Test-Object -Path $Destination -NotFound) { + # Ensure destination exists to prevent contents to be added to a dummy file + Write-Log -Type "DEBUG" -Message "Create destination directory ""$Destination""" + New-Item -Path $Destination -ItemType "Directory" | Out-Null + } Copy-Item -Path $Path -Destination $Destination -Container -Force:$Force } } diff --git a/Public/Get-KeyValue.ps1 b/Public/Get-KeyValue.ps1 index 26ea383..df5fa09 100644 --- a/Public/Get-KeyValue.ps1 +++ b/Public/Get-KeyValue.ps1 @@ -17,6 +17,9 @@ function Get-KeyValue { .PARAMETER CaseSensitive The case sensitive switch defines is the search should be case sensitive. + .PARAMETER Silent + The silent switch defines if output messages should be suppressed. + .OUTPUTS [System.Boolean] The function returns a boolean. @@ -34,7 +37,7 @@ function Get-KeyValue { File name: Get-KeyValue.ps1 Author: Florian Carrier Creation date: 2018-12-08 - Last modified: 2018-12-08 + Last modified: 2021-08-27 #> [CmdletBinding ()] Param ( @@ -58,7 +61,12 @@ function Get-KeyValue { HelpMessage = "Define if match should be case sensitive" )] [Switch] - $CaseSensitive + $CaseSensitive, + [Parameter ( + HelpMessage = "Switch to suppress output messages" + )] + [Switch] + $Silent ) Process { if (Find-Key -Hashtable $Hashtable -Key $Key -CaseSensitive:$CaseSensitive) { @@ -76,7 +84,9 @@ function Get-KeyValue { return $Value } else { # If key does not exists, returns null - Write-Log -Type "WARN" -Message """$Key"" was not found" + if ($Silent -eq $false) { + Write-Log -Type "WARN" -Message "Key ""$Key"" was not found in the hashtable" + } return $null } } diff --git a/Public/New-SelfContainedPackage.ps1 b/Public/New-SelfContainedPackage.ps1 index 05011b0..9b33ed1 100644 --- a/Public/New-SelfContainedPackage.ps1 +++ b/Public/New-SelfContainedPackage.ps1 @@ -13,7 +13,7 @@ function New-SelfContainedPackage { File name: New-SelfContainedPackage.ps1 Author: Florian Carrier Creation date: 2021-07-06 - Last modified: 2021-07-06 + Last modified: 2021-07-28 #> [CmdletBinding ( SupportsShouldProcess = $true @@ -25,9 +25,9 @@ function New-SelfContainedPackage { HelpMessage = "Path to the package to bundle" )] [ValidateNotNullOrEmpty ()] - [Alias ("Path")] + [Alias ("Package")] [System.String] - $Package, + $Path, [Parameter ( Position = 2, Mandatory = $false, @@ -57,9 +57,8 @@ function New-SelfContainedPackage { # Get global preference vrariables Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState # Variables - $Properties = New-Object -TypeName "System.Collections.Specialized.OrderedDictionary" - $Properties.PackageName = Split-Path -Path $Package -Leaf - $Properties.PackagePath = Resolve-Path -Path $Package + $PackageName = Split-Path -Path $Path -Leaf + $PackagePath = Resolve-Path -Path $Path if ($PSBoundParameters.ContainsKey('Destination')) { if (Test-Path -Path $Destination -PathType "Leaf") { $Extension = [System.IO.Path]::GetExtension($Destination) @@ -67,46 +66,44 @@ function New-SelfContainedPackage { Write-Log -Type "WARN" -Message "$Extension is not a supported archive file format. .zip is the only supported archive file format." $Destination = $Destination.Replace($Extension, ".zip") } - $Properties.StagingPath = Join-Path -Path (Split-Path -Path $Destination -Parent) -ChildPath $Properties.PackageName - $Properties.CompressedPackage = $Destination + $CompressedPackage = $Destination + $Destination = Split-Path -Path $Destination -Parent + $StagingPath = Join-Path -Path $Destination -ChildPath $PackageName } else { - $Properties.StagingPath = Join-Path -Path $Destination -ChildPath $Properties.PackageName - $Properties.CompressedPackage = [System.String]::Concat($Properties.StagingPath, ".zip") + $StagingPath = Join-Path -Path $Destination -ChildPath $PackageName + $CompressedPackage = [System.String]::Concat($StagingPath, ".zip") } } else { - $Properties.StagingPath = Join-Path -Path (Split-Path -Path $Properties.PackagePath -Parent) -ChildPath $Properties.PackageName - $Properties.CompressedPackage = [System.String]::Concat($Properties.PackagePath, ".zip") - } - # Debug - foreach ($Property in $Properties.GetEnumerator()) { - Write-Log -Type "DEBUG" -Message "$($Property.Name)=$($Property.Value)" + $Destination = Split-Path -Path $PackagePath -Parent + $ISOTimeStamp = Get-Date -Format "yyyyMMdd_HHmmss" + $UniquePackageName = [System.String]::Concat($PackageName, "_", $ISOTimeStamp) + $StagingPath = Join-Path -Path $Destination -ChildPath $UniquePackageName + $CompressedPackage = [System.String]::Concat($PackagePath, ".zip") } } Process { - Write-Log -Type "CHECK" -Message "Creating self-contained $($Properties.PackageName) package" + Write-Log -Type "INFO" -Message "Creating self-contained ""$PackageName"" package" # Stage package - if (Test-Path -Path $Properties.PackagePath) { - if (Test-Path -Path $Properties.StagingPath) { - Write-Log -Type "WARN" -Message "Overwriting existing package folder" - Remove-Object -Path $Properties.StagingPath + if (Test-Path -Path $PackagePath) { + if (Test-Path -Path $StagingPath) { + Write-Log -Type "WARN" -Message "Removing existing staging directory ""$StagingPath""" + Remove-Object -Path $StagingPath } - Copy-Item -Path "$($Properties.PackagePath)\*" -Destination $Properties.StagingPath -Recurse - Write-Log -Type "DEBUG" -Message $Properties.StagingPath + Copy-Object -Path $PackagePath -Destination $Destination -Exclude $Ignore -Force } else { - Write-Log -Type "ERROR" -Message "Path not found $Properties.PackagePath" -ErrorCode 1 + Write-Log -Type "ERROR" -Message "Path not found $PackagePath" -ErrorCode 1 } - # List all required modules + # # List all required modules foreach ($Module in $Modules) { - Write-Log -Type "INFO" -Message "Retrieving module $Module" + $ModuleDirectory = Join-Path -Path $StagingPath -ChildPath "lib" + Write-Log -Type "INFO" -Message "Retrieving module ""$Module""" $Load = $false :loop foreach ($Repository in ($env:PSModulePath -split ";")) { Write-Log -Type "DEBUG" -Message $Repository $ModulePath = Join-Path -Path $Repository -ChildPath $Module Write-Log -Type "DEBUG" -Message $ModulePath if (Test-Path -Path $ModulePath) { - $ModuleDirectory = Join-Path -Path $Properties.StagingPath -ChildPath "lib\$Module" - Copy-Item -Path $ModulePath -Destination $ModuleDirectory -Recurse -Exclude $Ignore - Get-Object -Path $ModuleDirectory + Copy-Object -Path $ModulePath -Destination $ModuleDirectory -Exclude $Ignore -Force $Load = $true break loop } @@ -116,29 +113,22 @@ function New-SelfContainedPackage { } } # # Compress package - # if (Test-Path -Path $Properties.StagingPath) { - # # Remove unwanted files - # if ($PSBoundParameters.ContainsKey('Ignore')) { - # Write-Log -Type "INFO" -Message "Removing unwanted files" - # foreach ($Filter in $Ignore) { - # Remove-Object -Path $Properties.StagingPath -Filter $Filter - # } - # } - # Write-Log -Type "INFO" -Message "Compressing package" - # Write-Log -Type "DEBUG" -Message $Properties.CompressedPackage - # if (Test-Path -Path $Properties.CompressedPackage) { - # Write-Log -Type "WARN" -Message "Overwriting existing package" - # } - # Compress-Archive -Path "$Properties.StagingPath\*" -DestinationPath $Properties.CompressedPackage -CompressionLevel "Fastest" -Force - # } else { - # Write-Log -Type "ERROR" -Message "Path not found $Properties.StagingPath" - # Write-Log -Type "ERROR" -Message "Script packaging failed" -ErrorCode 1 - # } - # # Delete staging package - # Write-Log -Type "INFO" -Message "Deleting staged package folder" - # Write-Log -Type "DEBUG" -Message $Properties.StagingPath - # # Remove-Object -Path $Properties.StagingPath + if (Test-Path -Path $StagingPath) { + Write-Log -Type "INFO" -Message "Compressing package" + Write-Log -Type "DEBUG" -Message $CompressedPackage + if (Test-Path -Path $CompressedPackage) { + Write-Log -Type "WARN" -Message "Overwriting existing package" + } + Compress-Archive -Path "$StagingPath\*" -DestinationPath $CompressedPackage -CompressionLevel "Fastest" -Force + } else { + Write-Log -Type "ERROR" -Message "Path not found $StagingPath" + Write-Log -Type "ERROR" -Message "Script packaging failed" -ErrorCode 1 + } + # Delete staging package + Write-Log -Type "INFO" -Message "Deleting staged package folder" + Write-Log -Type "DEBUG" -Message $StagingPath + Remove-Object -Path $StagingPath # # End - # Write-Log -Type "CHECK" -Message "$($Properties.PackageName) self-contained package complete" + Write-Log -Type "CHECK" -Message """$PackageName"" self-contained package complete" } } \ No newline at end of file diff --git a/Public/Remove-Object.ps1 b/Public/Remove-Object.ps1 index 6343243..5bf1e4d 100644 --- a/Public/Remove-Object.ps1 +++ b/Public/Remove-Object.ps1 @@ -24,7 +24,7 @@ function Remove-Object { File name: Remove-Object.ps1 Author: Florian Carrier Creation date: 2019-06-14 - Last modified: 2021-07-06 + Last modified: 2021-07-08 #> [CmdletBinding ()] Param ( @@ -63,7 +63,12 @@ function Remove-Object { )] [ValidateNotNullOrEmpty ()] [System.String[]] - $Exclude = $null + $Exclude = $null, + [Parameter ( + HelpMessage = "Suppress debug messages" + )] + [Switch] + $Silent ) Begin { # Get global preference vrariables @@ -75,19 +80,19 @@ function Remove-Object { } } Process { - $Objects = New-Object -TypeName "System.Collections.ArrayList" - # Check PowerShell version to prevent issue - $PSVersion = $PSVersionTable.PSVersion | Select-Object -ExpandProperty "Major" - if ($PSVersion -lt 6) { - $Objects = Get-Object -Path $Path -Type $Type -Filter $Filter - } else { + # Check path content + if ($PSBoundParameters.ContainsKey("Exclude")) { $Objects = Get-Object -Path $Path -Type $Type -Filter $Filter -Exclude $Exclude + } else { + $Objects = Get-Object -Path $Path -Type $Type -Filter $Filter } # If objects are found if ($Objects.Count -ge 1) { foreach ($Object in $Objects) { if ($null -ne $Object) { - Write-Log -Type "DEBUG" -Object $Object + if ($Silent -eq $false) { + Write-Log -Type "DEBUG" -Object $Object.FullName + } try { Remove-Item -Path $Object.FullName -Recurse -Force } catch { diff --git a/Public/Write-Log.ps1 b/Public/Write-Log.ps1 index ed19eb0..3d65358 100644 --- a/Public/Write-Log.ps1 +++ b/Public/Write-Log.ps1 @@ -1,21 +1,17 @@ -# ------------------------------------------------------------------------------ -# Logging function -# ------------------------------------------------------------------------------ function Write-Log { <# .SYNOPSIS Formats output message as a log .DESCRIPTION - The Write-Log function outputs the time and type of a message in a formatt- - ed manner with respective colour code. + The Write-Log function outputs the time and type of a message in a formatted manner with respective colour code. It takes two parameters: - Type of output: information, warning, error, debug, or checkpoint. - Message: output content. .PARAMETER Type - The Type parameter defines the level of importance of the message and will + The type parameter defines the level of importance of the message and will influence the colour of the output. There are five available message types: @@ -25,16 +21,14 @@ function Write-Log { - INFO: information, used to convey a message; - WARN: warning, used to highlight a non-blocking issue. - .PARAMETER Message - The Message parameter corresponds to the desired output to be logged. + .PARAMETER Object + The object parameter corresponds to the desired output to be logged. .PARAMETER ExitCode - The optional exit code parameter acts as a switch. If specified, the script exe- - cution is terminated and the value corresponds to the error code to throw - when terminating the script. + The optional exit code parameter acts as a switch. If specified, the script execution is terminated and the value corresponds to the error code to throw when terminating the script. - .PARAMETER File - The optional file parameter corresponds to an output file in which to save the message. + .PARAMETER FilePath + The optional file path parameter corresponds to the path to an output file in which to save the message. .PARAMETER Obfuscate The optional obfuscate parameter corresponds to specific text to obfuscate from the output. Multiple values can be passed. @@ -48,63 +42,51 @@ function Write-Log { .EXAMPLE Write-Log -Type "INFO" -Message "This is an informational message." - This example outputs an informational message with the timestamp, the "INFO" - tag, and the specified message itself. It uses the defaut color scheme. + This example outputs an informational message with the timestamp, the "INFO" tag, and the specified message itself. It uses the defaut color scheme. .EXAMPLE Write-Log -Type "WARN" -Message "This is a warning message." - This example outputs a warning message with the timestamp, the "WARN" tag, - and the specified message itself. The message will be displayed in yellow in - the host. + This example outputs a warning message with the timestamp, the "WARN" tag, and the specified message itself. The message will be displayed in yellow in the host. .EXAMPLE Write-Log -Type "ERROR" -Message "This is an error message." - This example outputs an error message with the timestamp, the "ERROR" tag, - and the specified message itself. The message will be displayed in red in - the host. + This example outputs an error message with the timestamp, the "ERROR" tag, and the specified message itself. The message will be displayed in red in the host. .EXAMPLE Write-Log -Type "ERROR" -Message "This is an error message." -ExitCode 1 - This example outputs an error message with the timestamp, the "ERROR" tag, - and the specified message itself. The script will terminate with the exit - code 1. + This example outputs an error message with the timestamp, the "ERROR" tag, and the specified message itself. The script will terminate with the exit code 1. .EXAMPLE Write-Log -Type "CHECK" -Message "This is a checkpoint message." - This example outputs a checkpoint message with the timestamp, the "CHECK" - tag, and the specified message itself. The message will be displayed in - green in the host. + This example outputs a checkpoint message with the timestamp, the "CHECK" tag, and the specified message itself. The message will be displayed in green in the host. .EXAMPLE Write-Log -Type "DEBUG" -Message "This is a debug message." - This example outputs a message through the default DEBUG PowerShell chanel, - if the -Debug flag is enabled. + This example outputs a message through the default DEBUG PowerShell chanel, if the -Debug flag is enabled. .EXAMPLE Write-Log -Type "INFO" -Message "This is a password." -Obfuscate "password" - This example outputs a message with the timestamp, the "INFO" - tag, and the specified message itself with the "password" word obfuscated. + This example outputs a message with the timestamp, the "INFO" tag, and the specified message itself with the "password" word obfuscated. - Output: "This is a *******." + Output: "This is a *******." .NOTES File name: Write-Log.ps1 Author: Florian Carrier Creation date: 2018-10-15 - Last modified: 2019-12-17 + Last modified: 2021-09-02 TODO Add locale variable .LINK https://www.powershellgallery.com/packages/PSTK #> [CmdletBinding ()] - # Inputs Param ( [Parameter ( Position = 1, @@ -118,16 +100,16 @@ function Write-Log { "INFO", "WARN" )] - [String] + [System.String] $Type, [Parameter ( Position = 2, Mandatory = $true, HelpMessage = "Message to output" )] - [ValidateNotNull ()] + [AllowNull()] [Alias ("Message", "Output", "Log")] - [Object] + [System.Object] $Object, [Parameter ( Position = 3, @@ -148,8 +130,8 @@ function Write-Log { )] [ValidateNotNullOrEmpty ()] [Alias ("Path")] - [String] - $File, + [System.String] + $FilePath, [Parameter ( Position = 5, Mandatory = $false, @@ -169,32 +151,40 @@ function Write-Log { "INFO" = "White" "WARN" = "Yellow" } - # Ensure message is a string - if ($Object.GetType() -ne "String") { + # Message object check + if ($null -eq $Object) { + # Output explicit null value + $Message = '$null' + } elseif ($Object.GetType() -ne "String") { + # Serialise object $Message = ($Object | Out-String).Trim() } else { + # Trim message $Message = $Object.Trim() } # Obfuscate text if ($PSBoundParameters.ContainsKey("Obfuscate")) { foreach ($SensitiveText in $Obfuscate) { - $Message = $Message.Replace($SensitiveText, '*******') + $Message = $Message.Replace($SensitiveText, "*******") } } } Process { + # Check output type if ($Type -eq "DEBUG") { Write-Debug -Message $Message } else { - # Format log + # Format log message $Log = "$Time`t$Type`t$Message" # Output Write-Host -Object $Log -ForegroundColor $Colour.$Type } - if ($PSBoundParameters.ContainsKey("File")) { - Write-Log -Type "DEBUG" -Message $Path - $Message | Out-File -FilePath $Path -Append -Force + # If output file is specified + if ($PSBoundParameters.ContainsKey("FilePath")) { + Write-Log -Type "DEBUG" -Object $FilePath + $Message | Out-File -FilePath $FilePath -Append -Force } + # Stop script if exit code is specified if ($PSBoundParameters.ContainsKey("ExitCode")) { Stop-Script -ExitCode $ExitCode }