diff --git a/.github/copilot-commit-message-instructions.md b/.github/copilot-commit-message-instructions.md new file mode 100644 index 0000000..4007169 --- /dev/null +++ b/.github/copilot-commit-message-instructions.md @@ -0,0 +1,34 @@ +# Semantic Commit Messages + +See how a minor change to your commit message style can make you a better programmer. + +Format: `(): ` + +`` is optional + +## Example + +``` +feat: add hat wobble +^--^ ^------------^ +| | +| +-> Summary in present tense. +| ++-------> Type: chore, docs, feat, fix, refactor, style, or test. +``` + +More Examples: + +- `feat`: (new feature for the user, not a new feature for build script) +- `fix`: (bug fix for the user, not a fix to a build script) +- `docs`: (changes to the documentation) +- `style`: (formatting, missing semi colons, etc; no production code change) +- `refactor`: (refactoring production code, eg. renaming a variable) +- `test`: (adding missing tests, refactoring tests; no production code change) +- `chore`: (updating grunt tasks etc; no production code change) + +References: + +- https://www.conventionalcommits.org/ +- https://seesparkbox.com/foundry/semantic_commit_messages +- http://karma-runner.github.io/1.0/dev/git-commit-msg.html \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..9c4a524 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,44 @@ +# Copilot Instructions + +## Powershell Modules Code Instructions + +### PowerShell Functions Instructions + +Every powershell function will contain the `CmdletBinding` attribute and the `parm`iven if there are no parameters. +If the function is on the public folder of the module, we will add the Èxport-ModuleFunction` statement in the same line as the closing `}` of the function + +Sample of function will be: + +```powershell + +function Get-UserName{ + [CmdletBinding()] + param() + + #Logic of the function +} Export-ModuleFunction -FunctionName 'Get-UserName' +``` + +### PowerShell Test Instructions + +Every public function on the Test module will have the following format + +- Name will start with `Test_` will follow the name of the function that we are testing with no '-'. It will follow the intention of the test splited with a '_' +- Every time we create a new function with no body we will add the `Assert-NotImplemented` statement at the end +- We will add the 3 sections as with comments `Arrange`, `Act` and `Assert` to make the test more readable. +- Sample of a test function to test `Get-UserName` function will be `Test_GetUserName_UserNotFound` + +Full sample will be as follows + +```powershell +function Test_GetUserName_UserNotFound{ + + # Arrange + + # Act + + # Assert + + Assert-NotImplemented +} +``` \ No newline at end of file diff --git a/.github/workflows/powershell.yml b/.github/workflows/powershell.yml index 60bdec1..419192b 100644 --- a/.github/workflows/powershell.yml +++ b/.github/workflows/powershell.yml @@ -41,6 +41,6 @@ jobs: # Upload the SARIF file generated in the previous step - name: Upload SARIF results file - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: results.sarif diff --git a/CsvHelper.psd1 b/CsvHelper.psd1 index ba9f75c..31dd55b 100644 --- a/CsvHelper.psd1 +++ b/CsvHelper.psd1 @@ -51,7 +51,7 @@ Copyright = '(c) rulasg. All rights reserved.' # ProcessorArchitecture = '' # Modules that must be imported into the global environment prior to importing this module -# RequiredModules = @() +RequiredModules = @(@{ModuleName="InvokeHelper"; ModuleVersion="1.2.2"}) # Assemblies that must be loaded prior to importing this module # RequiredAssemblies = @() diff --git a/CsvHelper.psm1 b/CsvHelper.psm1 index 026beb2..d95aded 100644 --- a/CsvHelper.psm1 +++ b/CsvHelper.psm1 @@ -3,25 +3,11 @@ Write-Information -Message ("Loading {0} ..." -f ($PSCommandPath | Split-Path -L #Module path is where resides the RootModule file. This file. :) $MODULE_PATH = $PSScriptRoot -#Get public and private function definition files. -$Public = @( Get-ChildItem -Path $MODULE_PATH\public\*.ps1 -ErrorAction SilentlyContinue ) -$Private = @( Get-ChildItem -Path $MODULE_PATH\private\*.ps1 -ErrorAction SilentlyContinue ) +# Load ps1 files on code folders in order +"config","helper","include","private","public" | ForEach-Object { -#Dot source the files -Foreach($import in @($Public + $Private)) -{ - Try - { - . $import.fullname + Get-ChildItem -Path $MODULE_PATH\$_\*.ps1 -Recurse -ErrorAction SilentlyContinue | ForEach-Object { + try { . $_.fullname } + catch { Write-Error -Message "Failed to import function $($import.fullname): $_" } } - Catch - { - Write-Error -Message "Failed to import function $($import.fullname): $_" - } -} - -# Here I might... -# Read in or create an initial config file and variable -# Export Public functions ($Public.BaseName) for WIP modules -# Set variables visible to the module and its functions only - +} \ No newline at end of file diff --git a/Test/Test.psd1 b/Test/Test.psd1 index fba5b98..3f4a44e 100644 --- a/Test/Test.psd1 +++ b/Test/Test.psd1 @@ -3,7 +3,7 @@ # # Generated by: rulasg # -# Generated on: 19/3/2025 +# Generated on: 9/3/2025 # @{ @@ -18,7 +18,7 @@ ModuleVersion = '0.0.1' # CompatiblePSEditions = @() # ID used to uniquely identify this module -GUID = '170ed18a-e725-462b-8236-3eadd3adcb79' +GUID = '6b1a63eb-2d81-4424-8b7f-9f72e3979319' # Author of this module Author = 'rulasg' @@ -30,7 +30,7 @@ CompanyName = 'Unknown' Copyright = '(c) rulasg. All rights reserved.' # Description of the functionality provided by this module -# Description = '' +Description = 'Module for testing and hosting shared functionality between modules' # Minimum version of the PowerShell engine required by this module # PowerShellVersion = '' diff --git a/Test/Test.psm1 b/Test/Test.psm1 index 026beb2..844d032 100644 --- a/Test/Test.psm1 +++ b/Test/Test.psm1 @@ -1,27 +1,16 @@ -Write-Information -Message ("Loading {0} ..." -f ($PSCommandPath | Split-Path -LeafBase)) -InformationAction continue +Write-Information -Message ("Loading {0} ..." -f ($PSScriptRoot | Split-Path -Leaf)) -InformationAction continue #Module path is where resides the RootModule file. This file. :) $MODULE_PATH = $PSScriptRoot -#Get public and private function definition files. -$Public = @( Get-ChildItem -Path $MODULE_PATH\public\*.ps1 -ErrorAction SilentlyContinue ) -$Private = @( Get-ChildItem -Path $MODULE_PATH\private\*.ps1 -ErrorAction SilentlyContinue ) +# Load ps1 files on code folders in order +"config","helper","include","private","public" | ForEach-Object { -#Dot source the files -Foreach($import in @($Public + $Private)) -{ - Try - { - . $import.fullname - } - Catch - { - Write-Error -Message "Failed to import function $($import.fullname): $_" + Get-ChildItem -Path $MODULE_PATH\$_\*.ps1 -Recurse -ErrorAction SilentlyContinue | ForEach-Object { + try { . $_.fullname } + catch { Write-Error -Message "Failed to import function $($import.fullname): $_" } } } -# Here I might... -# Read in or create an initial config file and variable -# Export Public functions ($Public.BaseName) for WIP modules -# Set variables visible to the module and its functions only +Export-ModuleMember -Function Test_* diff --git a/en-US/about_CsvHelper.help.txt b/en-US/about_CsvHelper.help.txt deleted file mode 100644 index 867867a..0000000 --- a/en-US/about_CsvHelper.help.txt +++ /dev/null @@ -1,20 +0,0 @@ -TOPIC - about_CsvHelper - -AUTHOR - rulasg - -COPYRIGHT - (c) rulasg. All rights reserved. - -SHORT DESCRIPTION - - -LONG DESCRIPTION - - -KEYWORDS - Powershell Testing UnitTest Module TestingHelper - -SEE ALSO - https://github.com/rulasg/TestingHelper/ diff --git a/helper/mySetInvokeCommandAlias.config.ps1 b/helper/mySetInvokeCommandAlias.config.ps1 new file mode 100644 index 0000000..f30a7a2 --- /dev/null +++ b/helper/mySetInvokeCommandAlias.config.ps1 @@ -0,0 +1,6 @@ + +# This file is required for include mySetInvokeCommandAlias to work +# The value need to be unique to avoid collisions with other modules +# + +$MODULE_INVOKATION_TAG = "CsvHelperModule" diff --git a/helper/mySetInvokeCommandAlias.ps1 b/helper/mySetInvokeCommandAlias.ps1 new file mode 100644 index 0000000..7a4a232 --- /dev/null +++ b/helper/mySetInvokeCommandAlias.ps1 @@ -0,0 +1,25 @@ + +# SET MY INVOKE COMMAND ALIAS +# +# Allows calling constitely InvokeHelper with the module tag +# Need to define a variable called $MODULE_INVOKATION_TAG +# +# Sample: +# $MODULE_INVOKATION_TAG = "SfHelperModule" + +function Set-MyInvokeCommandAlias{ + [CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory,Position=0)][string]$Alias, + [Parameter(Mandatory,Position=1)][string]$Command + ) + + # throw if MODULE_INVOKATION_TAG is not set or is "MyModuleModule" + if (-not $MODULE_INVOKATION_TAG -or $MODULE_INVOKATION_TAG -eq "MyModuleModule") { + throw "MODULE_INVOKATION_TAG is not set or has an invalid value ('MyModuleModule'). Please set it to a unique value before calling Set-MyInvokeCommandAlias." + } + + if ($PSCmdlet.ShouldProcess("InvokeCommandAliasList", ("Add Command Alias [{0}] = [{1}]" -f $Alias, $Command))) { + InvokeHelper\Set-InvokeCommandAlias -Alias $Alias -Command $Command -Tag $MODULE_INVOKATION_TAG + } +} \ No newline at end of file diff --git a/include/config.config.ps1 b/include/config.config.ps1 new file mode 100644 index 0000000..c608cac --- /dev/null +++ b/include/config.config.ps1 @@ -0,0 +1,91 @@ +# CONFIG - PUBLIC +# +# This script defines aliases and functions for configuration management specific to "MyModule". +# It is intended to be included in public-facing scripts. + +# Define unique aliases for "MyModule" +$CONFIG_INVOKE_GET_ROOT_PATH_ALIAS = "CsvGetConfigRootPath" +$CONFIG_INVOKE_GET_ROOT_PATH_CMD = "Invoke-CsvGetConfigRootPath" + +# Set the alias for the root path command +Set-MyInvokeCommandAlias -Alias $CONFIG_INVOKE_GET_ROOT_PATH_ALIAS -Command $CONFIG_INVOKE_GET_ROOT_PATH_CMD + +# Define the function to get the configuration root path +function Invoke-CsvHelperGetConfigRootPath { + [CmdletBinding()] + param() + + $configRoot = GetConfigRootPath + return $configRoot +} + +Export-ModuleMember -Function Invoke-CsvHelperGetConfigRootPath + +# Extra functions not needed by INCLUDE CONFIG + +function Get-CsvHelperConfig{ + [CmdletBinding()] + param() + + $config = Get-Configuration + + return $config +} Export-ModuleMember -Function Get-CsvHelperConfig + +function Save-CsvHelperConfig{ + [CmdletBinding()] + param( + [Parameter(Mandatory, ValueFromPipeline, Position = 0)][Object]$Config + ) + + return Save-Configuration -Config $Config +} Export-ModuleMember -Function Save-CsvHelperConfig + +function Open-CsvHelperConfig{ + [CmdletBinding()] + param() + + $path = GetConfigFile -Key "config" + + code $path + +} Export-ModuleMember -Function Open-CsvHelperConfig + +function Add-CsvHelperConfigAttribute{ + [CmdletBinding()] + param( + [Parameter(Mandatory,Position=0)][ValidateSet("Account", "User", "Opportunity")][string]$objectType, + + [Parameter(Mandatory, ValueFromPipeline, Position = 1)][string]$Attribute + + ) + + begin{ + $config = Get-Configuration + $configAttribute = ($objectType + "_attributes").ToLower() + + if(-Not $config){ + $config = @{} + } + + if(-Not $config.$configAttribute){ + $config.$configAttribute = @() + } + } + + process{ + $config.$configAttribute += $Attribute + } + + End{ + $ret = Save-Configuration -Config $config + if(-Not $ret){ + throw "Error saving configuration" + } + + $config = Get-CsvHelperConfig + Write-Output $config.$configAttribute + + } + +} Export-ModuleMember -Function Add-CsvHelperConfigAttribute diff --git a/include/config.ps1 b/include/config.ps1 new file mode 100644 index 0000000..07b249e --- /dev/null +++ b/include/config.ps1 @@ -0,0 +1,121 @@ +# CONFIG +# +# Configuration management module +# +# Include design description +# This is the function ps1. This file is the same for all modules. +# Create a public psq with variables, Set-MyInvokeCommandAlias call and Invoke public function. +# Invoke function will call back `GetConfigRootPath` to use production root path +# Mock this Invoke function with Set-MyInvokeCommandAlias to set the Store elsewhere +# This ps1 has function `GetConfigFile` that will call `Invoke-MyCommand -Command $CONFIG_INVOKE_GET_ROOT_PATH_ALIAS` +# to use the store path, mocked or not, to create the final store file name. +# All functions of this ps1 will depend on `GetConfigFile` for functionality. +# +# TODO : Create a related public ps1 +# +# Create a related public ps1 +# 1. define $CONFIG_INVOKE_GET_ROOT_PATH_ALIAS. Make it unique. +# 2. define $CONFIG_INVOKE_GET_ROOT_PATH_CMD. Point to the invoke function that calls GetConfigRootPath to get the store path +# +# Sample code (replace "MyModule" with a unique module prefix): +# $CONFIG_INVOKE_GET_ROOT_PATH_ALIAS = "MyModuleGetConfigRootPath" +# $CONFIG_INVOKE_GET_ROOT_PATH_CMD = "Invoke-MyModuleGetConfigRootPath" +# +# Set-MyInvokeCommandAlias -Alias $CONFIG_INVOKE_GET_ROOT_PATH_ALIAS -Command $CONFIG_INVOKE_GET_ROOT_PATH_CMD +# +# function Invoke-MyModuleGetConfigRootPath{ +# $configRoot = GetConfigRootPath +# return $configRoot +# } Export-ModuleMember -Function Invoke-MyModuleGetConfigRootPath + + +$moduleRootPath = $PSScriptRoot | Split-Path -Parent +$moduleName = (Get-ChildItem -Path $moduleRootPath -Filter *.psd1 | Select-Object -First 1).BaseName +$CONFIG_ROOT = [System.Environment]::GetFolderPath('UserProfile') | Join-Path -ChildPath ".helpers" -AdditionalChildPath $moduleName, "config" + +# Create the config root if it does not exist +if(-Not (Test-Path $CONFIG_ROOT)){ + New-Item -Path $CONFIG_ROOT -ItemType Directory +} + +function GetConfigRootPath { + [CmdletBinding()] + param() + + $configRoot = $CONFIG_ROOT + return $configRoot +} + +function GetConfigFile { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, Position = 0)][string]$Key + ) + + $configRoot = Invoke-MyCommand -Command $CONFIG_INVOKE_GET_ROOT_PATH_ALIAS + $path = Join-Path -Path $configRoot -ChildPath "$Key.json" + return $path +} + +function Test-Configuration { + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key = "config" + ) + + $path = GetConfigFile -Key $Key + + return Test-Path $path +} + +function Get-Configuration { + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key = "config" + ) + + $path = GetConfigFile -Key $Key + + if(-Not (Test-Configuration -Key $Key)){ + return $null + } + + try{ + $ret = Get-Content $path | ConvertFrom-Json -AsHashtable -ErrorAction Stop + return $ret + } + catch{ + Write-Warning "Error reading configuration ($Key) file: $($path). $($_.Exception.Message)" + return $null + } +} + +function Save-Configuration { + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key = "config", + [Parameter(Mandatory = $true, Position = 1)][Object]$Config + ) + + $path = GetConfigFile -Key $Key + + try { + $Config | ConvertTo-Json -Depth 10 | Set-Content $path -ErrorAction Stop + } + catch { + Write-Warning "Error saving configuration ($Key) to file: $($path). $($_.Exception.Message)" + return $false + } + + return $true +} + + + + + + + + + + diff --git a/include/databaseV2.ps1 b/include/databaseV2.ps1 new file mode 100644 index 0000000..3c9b48a --- /dev/null +++ b/include/databaseV2.ps1 @@ -0,0 +1,160 @@ +# DATABASE V2 +# +# Database driver to store the cache +# +# Include design description +# This is the function ps1. This file is the same for all modules. +# Create a public psq with variables, Set-MyInvokeCommandAlias call and Invoke public function. +# Invoke function will call back `GetDatabaseRootPath` to use production root path +# Mock this Invoke function with Set-MyInvokeCommandAlias to set the Store elsewhere +# This ps1 has function `GetDatabaseFile` that will call `Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_ALIAS` +# to use the store path, mocked or not, to create the final store file name. +# All functions of this ps1 will depend on `GetDatabaseFile` for functionality. +# +# TODO : Create a related public ps1 +# 1. define $DB_INVOKE_GET_ROOT_PATH_ALIAS. Make it unique. +# 2. define $DB_INVOKE_GET_ROOT_PATH_CMD. Point to the invoke function that calls GetConfigRootPath to get the store path +# +# Sample code (replace "MyModule" with a unique module prefix): +# $DB_INVOKE_GET_ROOT_PATH_ALIAS = "SfGetDbRootPath" +# $DB_INVOKE_GET_ROOT_PATH_CMD = "Invoke-SfGetDbRootPath" +# +# Set-MyInvokeCommandAlias -Alias $DB_INVOKE_GET_ROOT_PATH_ALIAS -Command $DB_INVOKE_GET_ROOT_PATH_CMD +# +# function Invoke-SfGetDbRootPath{ +# [CmdletBinding()] +# param() +# +# $databaseRoot = GetDatabaseRootPath +# return $databaseRoot +# +# } Export-ModuleMember -Function Invoke-SfGetDbRootPath + +$moduleRootPath = $PSScriptRoot | Split-Path -Parent +$moduleName = (Get-ChildItem -Path $moduleRootPath -Filter *.psd1 | Select-Object -First 1).BaseName +$DATABASE_ROOT = [System.Environment]::GetFolderPath('UserProfile') | Join-Path -ChildPath ".helpers" -AdditionalChildPath $moduleName, "databaseCache" + +# Create the database root if it does not exist +if(-Not (Test-Path $DATABASE_ROOT)){ + New-Item -Path $DATABASE_ROOT -ItemType Directory +} + +Set-MyInvokeCommandAlias -Alias $DB_INVOKE_GET_ROOT_PATH_ALIAS -Command "Invoke-$DB_INVOKE_GET_ROOT_PATH_ALIAS" + +if(-not (Test-Path -Path function:"Invoke-$DB_INVOKE_GET_ROOT_PATH_ALIAS")){ + + # PUBLIC FUNCTION + function Invoke-MyModuleGetDbRootPath{ + [CmdletBinding()] + param() + + $databaseRoot = GetDatabaseRootPath + return $databaseRoot + + } + Rename-Item -path Function:Invoke-MyModuleGetDbRootPath -NewName "Invoke-$DB_INVOKE_GET_ROOT_PATH_ALIAS" + Export-ModuleMember -Function "Invoke-$DB_INVOKE_GET_ROOT_PATH_ALIAS" +} + +# Extra functions not needed by INCLUDE DATABASE V2 + +if(-not (Test-Path -Path function:"Reset-$DB_INVOKE_GET_ROOT_PATH_ALIAS")){ + function Reset-MyModuleDatabaseStore{ + [CmdletBinding()] + param() + + $databaseRoot = Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_ALIAS + + Remove-Item -Path $databaseRoot -Recurse -Force -ErrorAction SilentlyContinue + + New-Item -Path $databaseRoot -ItemType Directory + + } + Rename-Item -path Function:Reset-MyModuleDatabaseStore -NewName "Reset-$DB_INVOKE_GET_ROOT_PATH_ALIAS" + Export-ModuleMember -Function "Reset-$DB_INVOKE_GET_ROOT_PATH_ALIAS" +} + +# PRIVATE FUNCTIONS +function GetDatabaseRootPath { + [CmdletBinding()] + param() + + $databaseRoot = $DATABASE_ROOT + return $databaseRoot +} + +function GetDatabaseFile{ + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key + ) + + # throw if DB_INVOKE_GET_ROOT_PATH_ALIAS is not set + if (-not $DB_INVOKE_GET_ROOT_PATH_ALIAS) { + throw "DB_INVOKE_GET_ROOT_PATH_ALIAS is not set. Please set it before calling GetDatabaseFile." + } + + $databaseRoot = Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_ALIAS + + $path = $databaseRoot | Join-Path -ChildPath "$Key.json" + + return $path +} + +function Get-DatabaseKey{ + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key + ) + + if(-Not (Test-DatabaseKey $Key)){ + return $null + } + + $path = GetDatabaseFile $Key + + $ret = Get-Content $path | ConvertFrom-Json -Depth 10 -AsHashtable + + return $ret +} + +function Reset-DatabaseKey{ + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key + ) + $path = GetDatabaseFile -Key $Key + Remove-Item -Path $path -Force -ErrorAction SilentlyContinue + return +} + +function Save-DatabaseKey{ + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key, + [Parameter(Position = 2)][Object]$Value + ) + + $path = GetDatabaseFile -Key $Key + + $Value | ConvertTo-Json -Depth 10 | Set-Content $path +} + +function Test-DatabaseKey{ + [CmdletBinding()] + param( + [Parameter(Position = 0)][string]$Key + ) + + $path = GetDatabaseFile -Key $Key + + # Key file not exists + if(-Not (Test-Path $path)){ + return $false + } + + # TODO: Return $false if cache has expired + + return $true +} + diff --git a/include/databasev2.config.ps1 b/include/databasev2.config.ps1 new file mode 100644 index 0000000..dbf39c0 --- /dev/null +++ b/include/databasev2.config.ps1 @@ -0,0 +1,33 @@ +# This file is required for INCLUDE DATABASE V2 + +$DB_INVOKE_GET_ROOT_PATH_ALIAS = "CsvHelperGetDbRootPath" +# $DB_INVOKE_GET_ROOT_PATH_CMD = "Invoke-$MyModuleGetDbRootPath" + +# Set-MyInvokeCommandAlias -Alias $DB_INVOKE_GET_ROOT_PATH_ALIAS -Command $DB_INVOKE_GET_ROOT_PATH_CMD + +# function Invoke-MyModuleGetDbRootPath{ +# [CmdletBinding()] +# param() + +# $databaseRoot = GetDatabaseRootPath +# return $databaseRoot + +# } +# Rename-Item -path Function:Invoke-MyModuleGetDbRootPath -NewName $DB_INVOKE_GET_ROOT_PATH_CMD +# Export-ModuleMember -Function $DB_INVOKE_GET_ROOT_PATH_CMD + +# # Extra functions not needed by INCLUDE DATABASE V2 + +# function Reset-MyModuleDatabaseStore{ +# [CmdletBinding()] +# param() + +# $databaseRoot = Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_ALIAS + +# Remove-Item -Path $databaseRoot -Recurse -Force -ErrorAction SilentlyContinue + +# New-Item -Path $databaseRoot -ItemType Directory + +# } +# Rename-Item -path Function:Reset-MyModuleDatabaseStore -NewName "Reset-$DB_INVOKE_GET_ROOT_PATH_ALIAS" +# Export-ModuleMember -Function "Reset-$DB_INVOKE_GET_ROOT_PATH_ALIAS" \ No newline at end of file diff --git a/include/getHashCode.ps1 b/include/getHashCode.ps1 new file mode 100644 index 0000000..830a869 --- /dev/null +++ b/include/getHashCode.ps1 @@ -0,0 +1,26 @@ + +# Get HASH CODE +# +# This script defines a function to compute the MD5 hash of a given string. +# The hash is returned as a hexadecimal string without dashes. +# The function is useful for generating unique keys based on input strings, +# such as for caching purposes in a database or other storage. +# The hash generated is equal on all environments making it usedfull across computers. + +function Get-HashCode { + param ( + [Parameter(Mandatory,ValueFromPipeline,Position=0)] + [string]$InputString + ) + + process{ + # Generate MD5 hash + $md5 = [System.Security.Cryptography.MD5]::Create() + $bytes = [System.Text.Encoding]::UTF8.GetBytes($InputString) + $hashBytes = $md5.ComputeHash($bytes) + $hashString = [BitConverter]::ToString($hashBytes) -replace '-', '' + + return $hashString + } + +} \ No newline at end of file diff --git a/test.ps1 b/test.ps1 index 33bc5ac..d34447b 100644 --- a/test.ps1 +++ b/test.ps1 @@ -7,44 +7,138 @@ Using TestingHelper this script will search for a Test module and run the tests This script will be referenced from launch.json to run the tests on VSCode .LINK - https://raw.githubusercontent.com/rulasg/DemoPsModule/main/test.ps1 + https://raw.githubusercontent.com/rulasg/StagingModule/main/test.ps1 .EXAMPLE > ./test.ps1 #> [CmdletBinding()] param ( - #Switch ShowTestErrors [Parameter()][switch]$ShowTestErrors ) -function Import-TestingHelper{ +function Set-TestName{ [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Scope='Function')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Scope='Function')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Scope='Function')] + [Alias("st")] param ( - [Parameter()][string]$Version, - [Parameter()][switch]$AllowPrerelease, - [Parameter()][switch]$PassThru + [Parameter(Position=0,ValueFromPipeline)][string]$TestName ) - - if ($Version) { - $V = $Version.Split('-') - $semVer = $V[0] - $AllowPrerelease = ($AllowPrerelease -or ($null -ne $V[1])) + + process{ + $global:TestName = $TestName } - - $module = Import-Module TestingHelper -PassThru -ErrorAction SilentlyContinue -RequiredVersion:$semVer +} + +function Get-TestName{ + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Scope='Function')] + [Alias("gt")] + param ( + ) + + process{ + $global:TestName + } +} + +function Clear-TestName{ + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Scope='Function')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Scope='Function')] + [Alias("ct")] + param ( + ) + + $global:TestName = $null +} + +function Import-RequiredModule{ + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Scope='Function')] + param ( + [Parameter(ParameterSetName = "HT", ValueFromPipeline)][hashtable]$RequiredModule, + [Parameter(ParameterSetName = "RM",Position = 0)][string]$ModuleName, + [Parameter(ParameterSetName = "RM")][string]$ModuleVersion, + [Parameter(ParameterSetName = "HT")] + [Parameter(ParameterSetName = "RM")] + [switch]$AllowPrerelease, + [Parameter(ParameterSetName = "HT")] + [Parameter(ParameterSetName = "RM")] + [switch]$PassThru + ) + + process{ + # Powershell module manifest does not allow versions with prerelease tags on them. + # Powershell modle manifest does not allow to add a arbitrary field to specify prerelease versions. + # Valid value (ModuleName, ModuleVersion, RequiredVersion, GUID) + # There is no way to specify a prerelease required module. + + if($RequiredModule){ + $ModuleName = $RequiredModule.ModuleName + $ModuleVersion = [string]::IsNullOrWhiteSpace($RequiredModule.RequiredVersion) ? $RequiredModule.ModuleVersion : $RequiredModule.RequiredVersion + } + + "Importing module Name[{0}] Version[{1}] AllowPrerelease[{2}]" -f $ModuleName, $ModuleVersion, $AllowPrerelease | Write-Verbose + + # Following semVer we can manually specidy a taged version to specify that is prerelease + # Extract the semVer from it and set AllowPrerelease to true + if ($ModuleVersion) { + $V = $ModuleVersion.Split('-') + $semVer = $V[0] + $AllowPrerelease = ($AllowPrerelease -or ($null -ne $V[1])) + } + + $module = Import-Module $ModuleName -PassThru -ErrorAction SilentlyContinue -MinimumVersion:$semVer + + if ($null -eq $module) { + "Installing module Name[{0}] Version[{1}] AllowPrerelease[{2}]" -f $ModuleName, $ModuleVersion, $AllowPrerelease | Write-Host -ForegroundColor DarkGray + $installed = Install-Module -Name $ModuleName -Force -AllowPrerelease:$AllowPrerelease -passThru -RequiredVersion:$ModuleVersion + $module = $installed | ForEach-Object {Import-Module -Name $_.Name -RequiredVersion ($_.Version.Split('-')[0]) -Force -PassThru} + } + + "Imported module Name[{0}] Version[{1}] PreRelease[{2}]" -f $module.Name, $module.Version, $module.privatedata.psdata.prerelease | Write-Host -ForegroundColor DarkGray - if ($null -eq $module) { - $installed = Install-Module -Name TestingHelper -Force -AllowPrerelease:$AllowPrerelease -passThru -RequiredVersion:$Version - $module = Import-Module -Name $installed.Name -RequiredVersion ($installed.Version.Split('-')[0]) -Force -PassThru + if ($PassThru) { + $module + } } +} + +<# +. SYNOPSIS + Extracts the required modules from the module manifest +#> +function Get-RequiredModule{ + [CmdletBinding()] + [OutputType([Object[]])] + param() + + # Required Modules + $localPath = $PSScriptRoot + $manifest = $localPath | Join-Path -child "*.psd1" | Get-Item | Import-PowerShellDataFile + $requiredModule = $null -eq $manifest.RequiredModules ? @() : $manifest.RequiredModules - if ($PassThru) { - $module + # Convert to hashtable + $ret = @() + $requiredModule | ForEach-Object{ + $ret += $_ -is [string] ? @{ ModuleName = $_ } : $_ } + + return $ret } -Import-TestingHelper -AllowPrerelease +# Install and load TestingHelper +# Import-RequiredModule -Name TestingHelper -AllowPrerelease +Import-RequiredModule "TestingHelper" -AllowPrerelease + +# Install and Load Module dependencies +Get-RequiredModule | Import-RequiredModule -AllowPrerelease -# Run test by PSD1 file -Invoke-TestingHelper -ShowTestErrors:$ShowTestErrors +if($TestName){ + Invoke-TestingHelper -TestName $TestName -ShowTestErrors:$ShowTestErrors +} else { + Invoke-TestingHelper -ShowTestErrors:$ShowTestErrors +} \ No newline at end of file