diff --git a/.github/workflows/UpdateModuleTag.yml b/.github/workflows/UpdateModuleTag.yml new file mode 100644 index 00000000..d921303b --- /dev/null +++ b/.github/workflows/UpdateModuleTag.yml @@ -0,0 +1,92 @@ +name: UpdateModuleTag +on: + workflow_dispatch: + pull_request: + branches: + - main + - master + paths-ignore: + - 'docs/**' + - '*.help.txt' + - '*.md' + +jobs: + UpdateModuleTag: + if: ${{github.event.action == 'closed' && github.event.merged == true}} + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: TagModuleVersion + id: TagModuleVersion + shell: pwsh + run: | + $Parameters = @{} + $Parameters.ModulePath = ${env:ModulePath} + $Parameters.UserEmail = ${env:UserEmail} + $Parameters.UserName = ${env:UserName} + $Parameters.TagVersionFormat = ${env:TagVersionFormat} + $Parameters.TagAnnotationFormat = ${env:TagAnnotationFormat} + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: TagModuleVersion $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {param( + [string] + $ModulePath, + + # The user email associated with a git commit. + [string] + $UserEmail, + + # The user name associated with a git commit. + [string] + $UserName, + + # The tag version format (default value: 'v$(imported.Version)') + # This can expand variables. $imported will contain the imported module. + [string] + $TagVersionFormat = 'v$($imported.Version)', + + # The tag version format (default value: '$($imported.Name) $(imported.Version)') + # This can expand variables. $imported will contain the imported module. + [string] + $TagAnnotationFormat = '$($imported.Name) $($imported.Version)' + ) + + $imported = + if (-not $ModulePath) { + $orgName, $moduleName = $env:BUILD_REPOSITORY_ID -split "/" + Import-Module ".\$moduleName.psd1" -Force -PassThru -Global + } else { + Import-Module $modulePath -Force -PassThru -Global + } + + if (-not $imported) { return } + + $targetVersion =$ExecutionContext.InvokeCommand.ExpandString($TagVersionFormat) + + $versionTagExists = git tag --list | Where-Object { $_ -eq $targetVersion } + + if ($versionTagExists) { + "::warning::Version $($versionTagExists)" + return + } + + if (-not $UserName) { $UserName = $env:GITHUB_ACTOR } + if (-not $UserEmail) { $UserEmail = "$UserName@github.com" } + git config --global user.email $UserEmail + git config --global user.name $UserName + + git tag -a $targetVersion -m $ExecutionContext.InvokeCommand.ExpandString($TagAnnotationFormat) + git push --tags + + if ($env:GITHUB_ACTOR) { + exit 0 + } + + + } @Parameters + diff --git a/Convert-BuildStep.ps1 b/Convert-BuildStep.ps1 index f2054771..c505d802 100644 --- a/Convert-BuildStep.ps1 +++ b/Convert-BuildStep.ps1 @@ -422,6 +422,20 @@ $collectParameters Import-Module `$($modulePathVariable) -Force -PassThru $logParameters $Name `@Parameters +"@) + $innerScript = $sb + } elseif ($BuildOption.RootDirectory -and $ScriptBlock.File -and + $ScriptBlock.File -like "$($BuildOption.RootDirectory)*") { + $relativeScriptPath = + $ScriptBlock.File.Substring( + $BuildOption.RootDirectory.Length + ).TrimStart( + [IO.Path]::DirectorySeparatorChar + ).Replace('\','/') + $sb = [ScriptBlock]::Create(@" +$CollectParameters +$logParameters +& './$relativeScriptPath' `@Parameters "@) $innerScript = $sb } else { diff --git a/Expand-BuildStep.ps1 b/Expand-BuildStep.ps1 index 57a561c6..aeb5dcf6 100644 --- a/Expand-BuildStep.ps1 +++ b/Expand-BuildStep.ps1 @@ -108,7 +108,8 @@ } $splatMe.Remove('StepMap') - :nextKey foreach ($kv in $stepMap.GetEnumerator()) { + + :nextKey foreach ($kv in $stepMap.GetEnumerator()) { if ($kv.Key.EndsWith('s') -and -not $singleton) { # Already pluralized $thingType = $kv.Key.Substring(0,$kv.Key.Length -1) $propName = $kv.Key @@ -117,8 +118,9 @@ $propName = $kv.Key } else { $thingType = $kv.Key + $thingTypePlural = $kv.Key + 's' $propName = - if ($SingleItemName -notcontains $thingType -and + if ($SingleItemName -notcontains $thingType -and $thingType -notmatch '\W$' -and $theComponentNames.Keys -contains $thingType) { $kv.Key.Substring(0,1).ToLower() + $kv.Key.Substring(1) + 's' @@ -312,6 +314,26 @@ $outObject[$propName] -isnot [Collections.IList]) { $outObject[$propName] = @($outObject[$propName]) } + + if ($SingleItemName -contains $propName -and + $outObject[$propName] -is [Collections.IList]) { + $newOut = [Ordered]@{} + foreach ($obj in $outObject[$propName]) { + if ($obj -is [Collections.IDictionary]) { + $k = @($obj.Keys)[0] + if ($obj.Count -eq 1 -and + $obj.Keys -contains $k) { + $newOut[$k] = $obj.$k + } else { + $newOut[$k] = $obj + } + } + elseif ($obj -is [string]) { + $newOut[$obj] = [Ordered]@{} + } + } + $outObject[$propName] = $newOut + } } } $outObject diff --git a/GitHub/Jobs/PublishToGallery.psd1 b/GitHub/Jobs/PublishToGallery.psd1 new file mode 100644 index 00000000..bbf7f565 --- /dev/null +++ b/GitHub/Jobs/PublishToGallery.psd1 @@ -0,0 +1,12 @@ +@{ + "runs-on" = "ubuntu-latest" + if = '${{ success() }}' + steps = @( + @{ + name = 'Check out repository' + uses = 'actions/checkout@v2' + }, 'PublishPowerShellGallery' + ) +} + + diff --git a/GitHub/Jobs/UpdateModuleTag.psd1 b/GitHub/Jobs/UpdateModuleTag.psd1 new file mode 100644 index 00000000..f110cc07 --- /dev/null +++ b/GitHub/Jobs/UpdateModuleTag.psd1 @@ -0,0 +1,11 @@ +@{ + "runs-on" = "ubuntu-latest" + if = '${{ success() }}' + steps = @( + @{ + name = 'Check out repository' + uses = 'actions/checkout@v2' + }, 'TagModuleVersion' + ) +} + diff --git a/GitHub/On/Demand.psd1 b/GitHub/On/Demand.psd1 index 68afe641..2a6c0431 100644 --- a/GitHub/On/Demand.psd1 +++ b/GitHub/On/Demand.psd1 @@ -1 +1 @@ -'workflow_dispatch' \ No newline at end of file +@{'workflow_dispatch'=@{}} \ No newline at end of file diff --git a/GitHub/On/PullRequestMerged.psd1 b/GitHub/On/PullRequestMerged.psd1 new file mode 100644 index 00000000..f1553916 --- /dev/null +++ b/GitHub/On/PullRequestMerged.psd1 @@ -0,0 +1,10 @@ +@{ + pull_request = @{ + branches =@('main','master') + if = @' +${{github.event.action == 'closed' && github.event.merged == true}} +'@ + "paths-ignore" = @("docs/**","*.help.txt", "*.md") + } +} + diff --git a/GitHub/On/PullToMain.psd1 b/GitHub/On/PullToMain.psd1 new file mode 100644 index 00000000..fae38d86 --- /dev/null +++ b/GitHub/On/PullToMain.psd1 @@ -0,0 +1,8 @@ +@{ + pull_request = @{ + branches =@('main','master') + "paths-ignore" = @("docs/**","*.help.txt", "*.md") + } +} + + diff --git a/GitHub/On/Push.psd1 b/GitHub/On/Push.psd1 index 54041420..9a30db79 100644 --- a/GitHub/On/Push.psd1 +++ b/GitHub/On/Push.psd1 @@ -1,2 +1,2 @@ -'push' +@{'push'=@{}} diff --git a/GitHub/Steps/PublishPowerShellGallery.ps1 b/GitHub/Steps/PublishPowerShellGallery.ps1 new file mode 100644 index 00000000..f70f877b --- /dev/null +++ b/GitHub/Steps/PublishPowerShellGallery.ps1 @@ -0,0 +1,68 @@ +param( +[string] +$ModulePath +) +$gitHubEvent = if ($env:GITHUB_EVENT_PATH) { + [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json +} else { $null } + + +@" +::group::GitHubEvent +$($gitHubEvent | ConvertTo-Json -Depth 100) +::endgroup:: +"@ | Out-Host + +if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and + (-not $gitHubEvent.psobject.properties['inputs'])) { + "::warning::Pull Request has not merged, skipping" | Out-Host + return +} + + +$imported = +if (-not $ModulePath) { + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + Import-Module ".\$moduleName.psd1" -Force -PassThru -Global +} else { + Import-Module $modulePath -Force -PassThru -Global +} + +if (-not $imported) { return } + +$foundModule = try { Find-Module -Name $imported.Name -ErrorAction SilentlyContinue } catch {} + +if ($foundModule -and $foundModule.Version -ge $imported.Version) { + "::warning::Gallery Version of $moduleName is more recent ($($foundModule.Version) >= $($imported.Version))" | Out-Host +} else { + + $gk = '${{secrets.GALLERYKEY}}' + + $rn = Get-Random + $moduleTempFolder = Join-Path $pwd "$rn" + $moduleTempPath = Join-Path $moduleTempFolder $moduleName + New-Item -ItemType Directory -Path $moduleTempPath -Force | Out-Host + + Write-Host "Staging Directory: $ModuleTempPath" + + $imported | Split-Path | + Get-ChildItem -Force | + Where-Object Name -NE $rn | + Copy-Item -Destination $moduleTempPath -Recurse + + $moduleGitPath = Join-Path $moduleTempPath '.git' + Write-Host "Removing .git directory" + if (Test-Path $moduleGitPath) { + Remove-Item -Recurse -Force $moduleGitPath + } + Write-Host "Module Files:" + Get-ChildItem $moduleTempPath -Recurse + Write-Host "Publishing $moduleName [$($imported.Version)] to Gallery" + Publish-Module -Path $moduleTempPath -NuGetApiKey $gk + if ($?) { + Write-Host "Published to Gallery" + } else { + Write-Host "Gallery Publish Failed" + exit 1 + } +} diff --git a/GitHub/Steps/RunScriptCop.ps1 b/GitHub/Steps/RunScriptCop.ps1 index 5f485f1d..a002fbfc 100644 --- a/GitHub/Steps/RunScriptCop.ps1 +++ b/GitHub/Steps/RunScriptCop.ps1 @@ -20,5 +20,5 @@ $importedModule | Out-Host foreach ($issue in $scriptCopIssues) { - Write-GitWarning -Message "$($issue.ItemWithProblem): $($issue.Problem)" + Write-GitHubWarning -Message "$($issue.ItemWithProblem): $($issue.Problem)" } diff --git a/GitHub/Steps/TagModuleVersion.ps1 b/GitHub/Steps/TagModuleVersion.ps1 new file mode 100644 index 00000000..f60a41ad --- /dev/null +++ b/GitHub/Steps/TagModuleVersion.ps1 @@ -0,0 +1,81 @@ +param( +[string] +$ModulePath, + +# The user email associated with a git commit. +[string] +$UserEmail, + +# The user name associated with a git commit. +[string] +$UserName, + +# The tag version format (default value: 'v$(imported.Version)') +# This can expand variables. $imported will contain the imported module. +[string] +$TagVersionFormat = 'v$($imported.Version)', + +# The tag version format (default value: '$($imported.Name) $(imported.Version)') +# This can expand variables. $imported will contain the imported module. +[string] +$TagAnnotationFormat = '$($imported.Name) $($imported.Version)' +) + + +$gitHubEvent = if ($env:GITHUB_EVENT_PATH) { + [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json +} else { $null } + + +@" +::group::GitHubEvent +$($gitHubEvent | ConvertTo-Json -Depth 100) +::endgroup:: +"@ | Out-Host + +if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and + (-not $gitHubEvent.psobject.properties['inputs'])) { + "::warning::Pull Request has not merged, skipping" | Out-Host + return +} + + + +$imported = +if (-not $ModulePath) { + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + Import-Module ".\$moduleName.psd1" -Force -PassThru -Global +} else { + Import-Module $modulePath -Force -PassThru -Global +} + +if (-not $imported) { return } + +$targetVersion =$ExecutionContext.InvokeCommand.ExpandString($TagVersionFormat) +$existingTags = git tag --list + +@" +Target Version: $targetVersion + +Existing Tags: +$($existingTags -join [Environment]::NewLine) +"@ | Out-Host + +$versionTagExists = $existingTags | Where-Object { $_ -match $targetVersion } + +if ($versionTagExists) { + "::warning::Version $($versionTagExists)" + return +} + +if (-not $UserName) { $UserName = $env:GITHUB_ACTOR } +if (-not $UserEmail) { $UserEmail = "$UserName@github.com" } +git config --global user.email $UserEmail +git config --global user.name $UserName + +git tag -a $targetVersion -m $ExecutionContext.InvokeCommand.ExpandString($TagAnnotationFormat) +git push origin --tags + +if ($env:GITHUB_ACTOR) { + exit 0 +} \ No newline at end of file diff --git a/New-GitHubWorkflow.ps1 b/New-GitHubWorkflow.ps1 index d16b6b36..339208ea 100644 --- a/New-GitHubWorkflow.ps1 +++ b/New-GitHubWorkflow.ps1 @@ -96,7 +96,7 @@ function New-GitHubWorkflow { $workflowOptions = @{} $expandGitHubBuildStep = @{ BuildSystem = $mynoun - SingleItemName = 'On','Name' + SingleItemName = 'Name','On' DictionaryItemName = 'Jobs', 'Inputs','Outputs' BuildOption = $workflowOptions } @@ -133,6 +133,8 @@ function New-GitHubWorkflow { #endregion Map Dynamic Input + if ($RootDirectory) { $workflowOptions.RootDirectory = $RootDirectory} + #region Expand Input $expandSplat = @{} + $PSBoundParameters foreach ($k in @($expandSplat.Keys)) {