InvokeAbilityHelpers.psm1
# Implement your module commands in this script. if (-not $DependenciesPath ) { $DependenciesPath = (Join-Path $PSScriptRoot Dependencies) } $defaultDependencyTags = @("dev") $defaultPsDependVersion = '0.1.62' $defaultPsDependConfiguration = @{ PSDependOptions = @{ AddToPath = $true } InvokeBuild = @{ Version = '4.1.0' Tags = 'dev', 'prod' DependencyType = 'PSGalleryModule' } } Write-Verbose ("DependenciesPath [{0}]" -f $DependenciesPath) -Verbose if (-not(Get-InstalledModule PSDepend -RequiredVersion $defaultPsDependVersion -ErrorAction SilentlyContinue)) { Install-Module PSDepend -RequiredVersion $defaultPsDependVersion -Force -Scope CurrentUser } # -ProjectPath (Get-Location).Path Write-Verbose ("Import or Install Dependencies") -Verbose Import-Module PSDepend -RequiredVersion $defaultPsDependVersion Invoke-PSDepend -InputObject $defaultPsDependConfiguration -Install -Import -Force ` -Target $DependenciesPath -Tags $defaultDependencyTags -ErrorAction Stop -Verbose # Export only the functions using PowerShell standard verb-noun naming. # Be sure to list each exported functions in the FunctionsToExport field of the module manifest file. # This improves performance of command discovery in PowerShell. function Invoke-BuildStep { param ( [Parameter(Mandatory = $false)] $Task = "default", [Parameter(Mandatory = $false)] $OutputPath, [Parameter(Mandatory = $false)] $ProjectPath ) if (-not $ProjectPath ) { $ProjectPath = Resolve-Path (Get-Location).Path } if (-not $OutputPath ) { $OutputPath = (Join-Path $ProjectPath) } $PSBoundParameters.Add("File", (Join-Path $PSScriptRoot Invoke-BuildStep.task.ps1)) Write-Verbose ("ProjectPath [{0}]" -f $ProjectPath) -Verbose Write-Verbose ("OutputPath [{0}]" -f $OutputPath) -Verbose Invoke-Build @PSBoundParameters -Result result # Invoke-Build $Task { # Task default UpdateConfiguration # Task UpdateConfiguration{ # echo "test" # } # } -Result result if ($Result.Error) {exit 1} else {exit 0} } Export-ModuleMember -Function Invoke-BuildStep function Update-Project { param ( [Parameter(Mandatory = $false)] $ProjectPath, [Parameter(Mandatory = $false)] $OutputPath ) if (-not $ProjectPath ) { $ProjectPath = Resolve-Path (Get-Location).Path } if (-not $OutputPath ) { $OutputPath = $ProjectPath } $MetadataObjectAsJson = @{} $CurrentTime = Get-Date -Format "MM-dd-yyyy-hh-mm-ss" Set-Location $ProjectPath $GitVersionExe = (Join-Path $PSScriptRoot "\Dependencies\GitVersion_3.6.5\GitVersion.exe") $ConventionalChangelogExe = (Join-Path $PSScriptRoot "\Dependencies\conventional-changelog\conventional-changelog.cmd") $MetadataFile = (Join-Path $ProjectPath metadata.json) $GitVersionFile = (Join-Path $ProjectPath GitVersion.yml) # NOTE: Generate GitVersion file Write-Verbose "Generate GitVersion file" -Verbose Set-Content $GitVersionFile @" branches: master:` mode: ContinuousDeployment tag: stable increment: None prevent-increment-of-merged-branch-version: true track-merge-target: false release: mode: ContinuousDeployment tag: release increment: None prevent-increment-of-merged-branch-version: true track-merge-target: false (pull|pull\-requests|pr)[/-]: mode: ContinuousDeployment tag: pr increment: None prevent-increment-of-merged-branch-version: false tag-number-pattern: '[/-](?<number>\d+)[-/]' track-merge-target: false hotfix(es)?[/-]: mode: ContinuousDeployment tag: hotfix increment: None prevent-increment-of-merged-branch-version: false track-merge-target: false support[/-]: mode: ContinuousDeployment tag: support increment: None prevent-increment-of-merged-branch-version: true track-merge-target: false develop: mode: ContinuousDeployment tag: develop increment: None prevent-increment-of-merged-branch-version: false track-merge-target: true "@ # NOTE: Generate metadata Write-Verbose "Generate metadata" -Verbose $MetadataFileObject = @{} $GitVersionObject = @{} #Metadata try { (ConvertFrom-Json ((Get-Content $MetadataFile -ErrorAction Stop ) | Out-String)).psobject.properties | ForEach-Object { $MetadataFileObject[$_.Name] = $_.Value } } catch { Write-Warning ("Not Found metadata file [{0}]... will be created" -f $MetadataFile) } #GitVersion try { (ConvertFrom-Json ( & $GitVersionExe | Out-String)).psobject.properties | ForEach-Object { $GitVersionObject[$_.Name] = $_.Value } } catch { Write-Error "Cannot load information about version" return } $MetadataObject = @{ Version = if ($GitVersionObject.FullSemVer) {$GitVersionObject.FullSemVer} else {"none"}; Build = @{ Time = $CurrentTime BuildId = if ($env:BUILD_BUILDID) {$env:BUILD_BUILDID} else {"none"}; DefinitionName = if ($env:BUILD_DEFINITIONNAME) {$env:BUILD_DEFINITIONNAME} else {"none"}; BuildNumber = if ($env:BUILD_BUILDNUMBER) {$env:BUILD_BUILDNUMBER} else {"none"}; DefinitionVersion = if ($env:BUILD_DEFINITIONVERSION) {$env:BUILD_DEFINITIONVERSION} else {"none"}; QueuedBy = if ($env:BUILD_QUEUEDBY) {$env:BUILD_QUEUEDBY} else {"none"}; RepositoryUri = if ($env:BUILD_REPOSITORY_URI) {$env:BUILD_REPOSITORY_URI} else {"none"}; TeamProject = if ($env:SYSTEM_TEAMPROJECT) {$env:SYSTEM_TEAMPROJECT} else {"none"}; BuildReason = if ($env:RELEASE_ENVIRONMENTNAME) {$env:RELEASE_ENVIRONMENTNAME} else {"none"}; AgentName = if ($env:AGENT_NAME) {$env:AGENT_NAME} else {"none"}; } SemVer = $GitVersionObject UpdatedAt = $CurrentTime } $MetadataObjectAsJson = (Merge-Hashtables -primary $MetadataObject -secondary $MetadataFileObject ) ` | ConvertTo-Json -depth 100 $MetadataObjectAsJson | Out-File $MetadataFile -Force # NOTE: Generate changelog file $ChangelogFile = (Join-Path $ProjectPath CHANGELOG.md) & $ConventionalChangelogExe -p angular -i $ChangelogFile -s -r 0 # NOTE: Update Version element in each csproj Write-Verbose "Update Version element in each csproj" -Verbose Get-ChildItem -Path $ProjectPath -Recurse -Filter *.csproj -File | ForEach-Object { Write-Verbose ("Update [{0}]" -f $_.FullName) -Verbose [xml]$CsProjectName = Get-Content $_.FullName $Version = $CsProjectName.SelectSingleNode('//Version[last()]') if ($Version) { $Version.InnerText = $MetadataObject.Version $CsProjectName.Save($_.FullName) } } #VSTS variables Write-Host "##vso[build.updatebuildnumber]$($GitVersionObject.FullSemVer)" Write-Host "##vso[task.setvariable variable=ChangelogFile;]$ChangelogFile" Write-Host "##vso[task.setvariable variable=MetadataFile;]$MetadataFile" Write-Verbose ("MetadataPath: {0}" -f $MetadataFile) -Verbose Write-Verbose ("Metadata saved to: [{0}]" -f $MetadataFile) -Verbose Write-Verbose ("ChangelogFile saved to: [{0}]" -f $ChangelogFile) -Verbose Write-Verbose ("GitVersionExe: [{0}]" -f $GitVersionExe) -Verbose Write-Verbose ("ConventionalChangelogExe: [{0}]" -f $ConventionalChangelogExe) -Verbose } Export-ModuleMember -Function Update-Project Function ConvertTo-FlattenObject { # Version 00.02.12, by iRon [CmdletBinding()]Param ( [Parameter(ValueFromPipeLine = $True)][Object[]]$Objects, [String]$Separator = ".", [ValidateSet("", 0, 1)]$Base = 1, [Int]$Depth = 5, [Int]$Uncut = 1, [String[]]$ToString = ([String], [DateTime], [TimeSpan]), [String[]]$Path = @() ) $PipeLine = $Input | ForEach {$_}; If ($PipeLine) {$Objects = $PipeLine} If (@(Get-PSCallStack)[1].Command -eq $MyInvocation.MyCommand.Name -or @(Get-PSCallStack)[1].Command -eq "<position>") { $Object = @($Objects)[0]; $Iterate = New-Object System.Collections.Specialized.OrderedDictionary If ($ToString | Where {$Object -is $_}) {$Object = $Object.ToString()} ElseIf ($Depth) { $Depth-- If ($Object.GetEnumerator.OverloadDefinitions -match "[\W]IDictionaryEnumerator[\W]") { $Iterate = $Object } ElseIf ($Object.GetEnumerator.OverloadDefinitions -match "[\W]IEnumerator[\W]") { $Object.GetEnumerator() | ForEach -Begin {$i = $Base} {$Iterate.($i) = $_; $i += 1} } Else { $Names = If ($Uncut) {$Uncut--} Else {$Object.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames} If (!$Names) {$Names = $Object.PSObject.Properties | Where {$_.IsGettable} | Select -Expand Name} If ($Names) {$Names | ForEach {$Iterate.$_ = $Object.$_}} } } If (@($Iterate.Keys).Count) { $Iterate.Keys | ForEach { Flatten-Object @(, $Iterate.$_) $Separator $Base $Depth $Uncut $ToString ($Path + $_) } } Else {$Property.(($Path | Where {$_}) -Join $Separator) = $Object} } ElseIf ($Objects -ne $Null) { @($Objects) | ForEach -Begin {$Output = @(); $Names = @()} { New-Variable -Force -Option AllScope -Name Property -Value (New-Object System.Collections.Specialized.OrderedDictionary) Flatten-Object @(, $_) $Separator $Base $Depth $Uncut $ToString $Path $Output += New-Object PSObject -Property $Property $Names += $Output[-1].PSObject.Properties | Select -Expand Name } $Output | Select ([String[]]($Names | Select -Unique)) } }; Export-ModuleMember -Function ConvertTo-FlattenObject function Merge-Hashtables { [cmdletbinding()] Param ( [hashtable] # First hashtable to merge, this will have priority $primary, [hashtable] # second hashtable to merge $secondary ) # If in debug mode, show the function currently in #Write-Log -IfDebug -Message $("***** {0} *****" -f $MyInvocation.MyCommand) # Craete an array of types that can be merged. # Hashtables and Dictionaries can be merged $types = @( "Hashtable" "Dictionary``2" ) #check for any duplicate keys $duplicates = $primary.keys | where {$secondary.ContainsKey($_)} if ($duplicates) { foreach ($key in $duplicates) { # if the item is a hashtable then call this function again if ($types -contains $primary.$key.gettype().name -and $types -contains $secondary.$key.gettype().name) { # set the argument hashtable $splat = @{ primary = $primary.$key secondary = $secondary.$key } $Primary.$key = Merge-Hashtables @splat } # if the key is an array merge the two items if ($primary.$key.GetType().Name -eq "Object[]" -and $secondary.$key.GetType().name -eq "Object[]") { $result = @() # Because an array can contain many different types, need to be careful how this information is merged # This means that the normal additional functions and the Unique parameter of Select will not work properly # so iterate around each of the two arrays and add to a result array foreach ($arr in @($primary.$key, $secondary.$key)) { # analyse each item in the arr foreach ($item in $arr) { # Switch on the type of the item to determine how to add the information switch ($item.GetType().Name) { "Object[]" { $result += , $item } # If the type is a string make sure that the array does not already # contain the same string "String" { if ($result -notcontains $item) { $result += $item } } # For everything else add it in default { $result += $item } } } } # Now assign the result back to the primary array $primary.$key = $result } #force primary key, so remove secondary conflict $Secondary.Remove($key) } } #join the two hash tables and return to the calling function $Primary + $Secondary } Export-ModuleMember -Function Merge-Hashtables function ConvertTo-PsCustomObjectFromHashtable { param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [object[]]$hashtable ); begin { $i = 0; } process { foreach ($myHashtable in $hashtable) { if ($myHashtable.GetType().Name -eq 'hashtable') { $output = New-Object -TypeName PsObject; Add-Member -InputObject $output -MemberType ScriptMethod -Name AddNote -Value { Add-Member -InputObject $this -MemberType NoteProperty -Name $args[0] -Value $args[1]; }; $myHashtable.Keys | Sort-Object | % { $output.AddNote($_, $myHashtable.$_); } $output; } else { Write-Warning "Index $i is not of type [hashtable]"; } $i += 1; } } } Export-ModuleMember -Function ConvertTo-PsCustomObjectFromHashtable function ConvertTo-HashtableFromPsCustomObject { param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [object[]]$psCustomObject ); process { foreach ($myPsObject in $psObject) { $output = @{}; $myPsObject | Get-Member -MemberType *Property | % { $output.($_.name) = $myPsObject.($_.name); } $output; } } } Export-ModuleMember -Function ConvertTo-HashtableFromPsCustomObject |