diff --git a/.github/workflows/ModuleCI.yml b/.github/workflows/ModuleCI.yml index 700535b..5b10ae1 100644 --- a/.github/workflows/ModuleCI.yml +++ b/.github/workflows/ModuleCI.yml @@ -11,68 +11,44 @@ jobs: name: Run Linters runs-on: [ubuntu-latest] steps: - - uses: actions/checkout@v4 - - uses: streetsidesoftware/cspell-action@v6 - with: - strict: false - suggestions: true - incremental_files_only: true - - uses: reviewdog/action-alex@v1.16.0 - with: - github_token: ${{ secrets.github_token }} - # Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review]. - reporter: github-pr-review - level: warning - - shell: pwsh - name: Apply PSScriptAnalyzer Fixes - run: Invoke-ScriptAnalyzer . -Fix - - name: PSScriptAnalyzer Fixes - uses: reviewdog/action-suggester@v1.21.0 - with: - tool_name: 'PSScriptAnalyzer' + - uses: actions/checkout@v4 + - uses: streetsidesoftware/cspell-action@v6 + with: + strict: false + suggestions: true + incremental_files_only: true + - shell: pwsh + name: Apply PSScriptAnalyzer Fixes + run: Invoke-ScriptAnalyzer . -Fix + - name: PSScriptAnalyzer Fixes + uses: reviewdog/action-suggester@v1.21.0 + with: + tool_name: "PSScriptAnalyzer" test: - name: Run Tests + name: Run Pwsh Tests + # We still have the azure pipeline setup that's running windows runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macOS-latest] steps: - - uses: actions/checkout@v4 - - name: Install and cache PSDepend - id: psdepend - uses: potatoqualitee/psmodulecache@v6.2.1 - with: - modules-to-cache: PSDepend:0.3.8 - - name: Determine modules to cache - shell: pwsh - id: modules-to-cache - run: | - $dependancies = Get-Dependency - $f = $dependancies | ?{ $_.DependencyType -eq 'PSGalleryModule' } | %{ "{0}:{1}" -F $_.DependencyName, $_.Version} - Write-Output "::set-output name=ModulesToCache::$($f -join ', ')" - - name: Install and cache PowerShell modules - id: psmodulecache - uses: potatoqualitee/psmodulecache@v6.2.1 - with: - modules-to-cache: ${{ steps.modules-to-cache.outputs.ModulesToCache }} + - uses: actions/checkout@v4 + - name: Test shell: pwsh - - name: Test - shell: pwsh - run: ./build.ps1 -Task Test - - name: Upload Unit Test Results - if: always() - uses: actions/upload-artifact@v4 - with: - name: Unit Test Results (OS ${{ matrix.os }}) - path: ./tests/out/testResults.xml + run: ./build.ps1 -Task Test -Bootstrap + - name: Upload Unit Test Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: Unit Test Results (OS ${{ matrix.os }}) + path: ./tests/out/testResults.xml publish-test-results: name: "Publish Unit Tests Results" needs: test runs-on: ubuntu-latest # the test job might be skipped, we don't need to run this job then if: success() || failure() - steps: - name: Download Artifacts uses: actions/download-artifact@v4 diff --git a/.github/workflows/PublishModule.yml b/.github/workflows/PublishModule.yml index 0f8f924..54dab87 100644 --- a/.github/workflows/PublishModule.yml +++ b/.github/workflows/PublishModule.yml @@ -1,48 +1,153 @@ +# spell-checker:ignore BHPS PSGALLERY pwsh name: Publish Module + +concurrency: + group: publish-${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: write + on: workflow_call: + inputs: + version: + description: "The version to publish. Leave empty to use the version in the module manifest." + required: false + type: string + isPrerelease: + description: "Is this a prerelease version?" + required: false + default: false + type: boolean + dry_run: + type: boolean + description: "If true, skip actual publishing and just validate the workflow logic." + required: false + default: false + secrets: + PS_GALLERY_KEY: + description: "The API key for publishing to the PowerShell Gallery." + required: true + jobs: - publish: - name: Publish Module + check_version: + name: Check Version + timeout-minutes: 15 runs-on: windows-latest + outputs: + version_bumped: ${{ steps.check_if_versions_bumped.outputs.BUMPED }} + module_name: ${{ steps.check_if_versions_bumped.outputs.MODULE_NAME }} + new_version: ${{ steps.check_if_versions_bumped.outputs.NEW_VERSION }} steps: - # Get the current version - uses: actions/checkout@v4 - - name: Install and cache PSDepend - id: psdepend - uses: potatoqualitee/psmodulecache@v6.2.1 - with: - modules-to-cache: PSDepend:0.3.8, BuildHelpers - - shell: pwsh - name: Check if this version is already published - # Give an id to the step, so we can reference it later + - name: Check if version is already published id: check_if_versions_bumped - run: | - Set-BuildEnvironment - [version]$GalleryVersion = Get-NextNugetPackageVersion -Name $env:BHProjectName -ErrorAction Stop - [version]$GithubVersion = Get-MetaData -Path $env:BHPSModuleManifest -PropertyName ModuleVersion -ErrorAction Stop - $bumped = $GithubVersion -ge $GalleryVersion - - # Set the output named "version_bumped" - Write-Host "::set-output name=version_bumped::$bumped" - - name: Determine modules to cache shell: pwsh - id: modules-to-cache - if: steps.check_if_versions_bumped.outputs.version_bumped == 'True' run: | - $dependancies = Get-Dependency - $f = $dependancies | ?{ $_.DependencyType -eq 'PSGalleryModule' } | %{ "{0}:{1}" -F $_.DependencyName, $_.Version} - Write-Output "::set-output name=ModulesToCache::$($f -join ', ')" - - name: Install and cache PowerShell modules - id: psmodulecache - uses: potatoqualitee/psmodulecache@v6.2.1 - if: steps.check_if_versions_bumped.outputs.version_bumped == 'True' + Install-Module BuildHelpers + Import-Module BuildHelpers + Set-BuildEnvironment -Force + [version]$githubVersion = Get-MetaData -Path $env:BHPSModuleManifest -PropertyName 'ModuleVersion' -ErrorAction 'Stop' + Write-Host "Current Version: $githubVersion" + + # Override version if specified as input + if (-not [String]::IsNullOrEmpty('${{ inputs.version }}')) { + # Split version and prerelease suffix if present + if('${{ inputs.version }}' -match '^(?\d+\.\d+\.\d+)(-(?.+))?$') { + $githubVersion = [version]$matches['version'] + if ($matches['prerelease']) { + $prereleaseSuffix = $matches['prerelease'] + } + } else { + Write-Warning "Invalid version format: '${{ inputs.version }}'. Expected format: '1.2.3' or '1.2.3-beta'" + } + Write-Host "Module version was specified in workflow: $githubVersion" + Update-MetaData -Path $env:BHPSModuleManifest -PropertyName ModuleVersion -Value $githubVersion -ErrorAction 'Stop' + } + + $moduleSplat = @{ + Name = $env:BHProjectName + } + + # Handle pre-release versions + $isPrerelease = '${{ inputs.isPrerelease }}' -eq 'true' + Write-Host "Prerelease: $isPrerelease" + if ($isPrerelease) { + $PSData = Get-MetaData -Path $env:BHPSModuleManifest -PropertyName PrivateData.PSData -ErrorAction 'Stop' + $moduleSplat['AllowPrerelease'] = $true + $moduleSplat['RequiredVersion'] = "$githubVersion-$($PSData.Prerelease)" + } else { + $moduleSplat['AllowPrerelease'] = $false + $moduleSplat['RequiredVersion'] = $githubVersion + } + Write-Host "Version to Publish: $($moduleSplat.RequiredVersion)" + + # Check if exact version exists in PSGallery + try { + $existingModule = Find-Module @moduleSplat -ErrorAction Stop + $bumped = $false + Write-Host "Version $($moduleSplat.RequiredVersion) already exists in PSGallery - skipping publish" + } catch { + if ($_.CategoryInfo.Category -eq 'ObjectNotFound') { + $bumped = $true + Write-Host "Version $($moduleSplat.RequiredVersion) not found in PSGallery - will publish" + } else { + Write-Warning "Failed to check existing version: $($_.Exception.Message)" + $bumped = $false + } + } + + # Strip prerelease suffix for changelog lookup + $versionForChangelog = "$($moduleSplat.RequiredVersion)" -replace '-.*$', '' + + Add-Content -LiteralPath $env:GITHUB_OUTPUT -Value "BUMPED=$bumped" -Confirm:$false -Encoding UTF8 + Add-Content -LiteralPath $env:GITHUB_OUTPUT -Value "MODULE_NAME=$env:BHProjectName" -Confirm:$false -Encoding UTF8 + Add-Content -LiteralPath $env:GITHUB_OUTPUT -Value "NEW_VERSION=$($moduleSplat.RequiredVersion)" -Confirm:$false -Encoding UTF8 + + create_release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: + - check_version + steps: + - uses: actions/checkout@v4 + - name: Read and validate changelog (keepachangelog) + id: changelog + uses: mindsers/changelog-reader-action@v2 + with: + validation_level: warn + version: ${{ needs.check_version.outputs.new_version }} + path: ./CHANGELOG.md + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 with: - modules-to-cache: ${{ steps.modules-to-cache.outputs.ModulesToCache }} - shell: pwsh + tag_name: ${{ needs.check_version.outputs.new_version }} + name: Release ${{ steps.changelog.outputs.version }} + body: ${{ steps.changelog.outputs.changes }} + prerelease: ${{ steps.changelog.outputs.status == 'prereleased' }} + draft: ${{ steps.changelog.outputs.status == 'unreleased' }} + + publish: + name: Publish Module + runs-on: windows-latest + needs: + - check_version + if: needs.check_version.outputs.version_bumped == 'True' + steps: + - uses: actions/checkout@v4 - name: Publish to PSGallery shell: pwsh - if: steps.check_if_versions_bumped.outputs.version_bumped == 'True' env: PSGALLERY_API_KEY: ${{ secrets.PS_GALLERY_KEY }} - run: ./build.ps1 -Task Publish + PACKAGE: ${{ needs.check_version.outputs.module_name }} + VERSION: ${{ needs.check_version.outputs.new_version }} + DRY_RUN: ${{ inputs.dry_run || false }} + run: | + if ($env:DRY_RUN -eq 'true') { + Write-Host "DRY RUN: Would publish module to PSGallery" + Write-Host "Module: $env:PACKAGE" + Write-Host "Version: $env:VERSION" + exit 0 + } + ./build.ps1 -Task 'Publish' -Bootstrap