diff --git a/.github/workflows/manual-publish-powershell-gallery.yml b/.github/workflows/manual-publish-powershell-gallery.yml new file mode 100644 index 0000000..82df521 --- /dev/null +++ b/.github/workflows/manual-publish-powershell-gallery.yml @@ -0,0 +1,238 @@ +name: Manual Publish to PowerShell Gallery + +on: + workflow_dispatch: + inputs: + mode: + description: 'Select mode' + required: true + type: choice + options: + - 'DRY_RUN' + - 'PUBLISH' + default: 'DRY_RUN' + confirm_publish: + description: 'If PUBLISH mode: Type "CONFIRM" to proceed' + required: false + type: string + +jobs: + publish: + runs-on: windows-latest + if: github.event.inputs.mode == 'DRY_RUN' || (github.event.inputs.mode == 'PUBLISH' && github.event.inputs.confirm_publish == 'CONFIRM') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install PowerShell Module Dependencies + shell: pwsh + run: | + Set-PSRepository PSGallery -InstallationPolicy Trusted + Install-Module -Name PowerShell-Yaml -Force -SkipPublisherCheck + + - name: Get Module Version + id: version + shell: pwsh + run: | + $manifestPath = "src/PSBlogger.psd1" + $manifest = Import-PowerShellDataFile -Path $manifestPath + $version = $manifest.ModuleVersion + Write-Host "Module version: $version" + echo "version=$version" >> $env:GITHUB_OUTPUT + + - name: Validate Module Manifest + shell: pwsh + run: | + # Test module manifest + $manifestPath = "./src/PSBlogger.psd1" + Write-Host "Testing module manifest at: $manifestPath" + + # Test manifest syntax + $manifest = Test-ModuleManifest -Path $manifestPath -Verbose + Write-Host "Module manifest is valid" + Write-Host "Module Name: $($manifest.Name)" + Write-Host "Module Version: $($manifest.Version)" + Write-Host "Module Author: $($manifest.Author)" + Write-Host "Module Description: $($manifest.Description)" + + # Check required fields for PowerShell Gallery + if (-not $manifest.Description) { + throw "Module description is required for PowerShell Gallery" + } + if (-not $manifest.Author) { + throw "Module author is required for PowerShell Gallery" + } + + # Test that the module can be imported successfully + Import-Module $manifestPath -Force + $loadedModule = Get-Module PSBlogger + Write-Host "Successfully imported module. Exported commands:" + $loadedModule.ExportedCommands.Keys | Sort-Object | ForEach-Object { Write-Host " - $_" } + + - name: Run Tests + shell: pwsh + run: | + # Install Pester for testing + Install-Module -Name Pester -Force -SkipPublisherCheck + + # Install pandoc for tests + choco install pandoc -y + + # Change to src directory and run tests + Set-Location "./src" + + # Configure Pester + $PesterConfig = @{ + Run = @{ + Path = './tests' + } + Output = @{ + Verbosity = 'Normal' + } + Should = @{ + ErrorAction = 'Stop' + } + } + + # Run tests and fail if any tests fail + $testResults = Invoke-Pester -Configuration $PesterConfig + if ($testResults.FailedCount -gt 0) { + throw "Tests failed. Cannot publish to PowerShell Gallery." + } + + - name: Publish to PowerShell Gallery + shell: pwsh + env: + NUGET_API_KEY: ${{ secrets.POWERSHELLGALLERY_API }} + run: | + $isDryRun = "${{ github.event.inputs.mode }}" -eq "DRY_RUN" + + if ($isDryRun) { + Write-Host "๐Ÿงช DRY RUN MODE - No actual publishing will occur" + } else { + # Validate that we have the API key + if (-not $env:NUGET_API_KEY) { + throw "POWERSHELLGALLERY_API secret is not set" + } + } + + Write-Host "Publishing PSBlogger version ${{ steps.version.outputs.version }} to PowerShell Gallery..." + + # Check if this version already exists + try { + $existingModule = Find-Module -Name PSBlogger -RequiredVersion ${{ steps.version.outputs.version }} -ErrorAction SilentlyContinue + if ($existingModule) { + if ($isDryRun) { + Write-Host "โš ๏ธ DRY RUN: Version ${{ steps.version.outputs.version }} already exists on PowerShell Gallery" + } else { + throw "Version ${{ steps.version.outputs.version }} already exists on PowerShell Gallery" + } + } + } catch { + if ($_.Exception.Message -notlike "*Version*already exists*") { + Write-Host "Could not check existing versions (this is normal for new modules): $($_.Exception.Message)" + } else { + throw + } + } + + # Publish the module + try { + if ($isDryRun) { + Publish-Module -Path "./src" -WhatIf -Verbose + Write-Host "๐Ÿงช DRY RUN: Would publish to PowerShell Gallery (no actual publishing)" + } else { + Publish-Module -Path "./src" -NuGetApiKey $env:NUGET_API_KEY -Verbose -Force + Write-Host "โœ… Successfully published to PowerShell Gallery" + } + } catch { + Write-Error "โŒ Failed to publish to PowerShell Gallery: $_" + throw + } + + - name: Create Git Tag and Release + shell: pwsh + run: | + $isDryRun = "${{ github.event.inputs.mode }}" -eq "DRY_RUN" + + if ($isDryRun) { + Write-Host "๐Ÿงช DRY RUN: Would create tag v${{ steps.version.outputs.version }} and GitHub release" + return + } + + # Configure git + git config user.name "github-actions" + git config user.email "github-actions@github.com" + + # Check if tag already exists + $tagExists = git tag -l "v${{ steps.version.outputs.version }}" + if ($tagExists) { + Write-Host "Tag v${{ steps.version.outputs.version }} already exists" + } else { + # Create and push tag + git tag -a "v${{ steps.version.outputs.version }}" -m "Release version ${{ steps.version.outputs.version }}" + git push origin "v${{ steps.version.outputs.version }}" + Write-Host "Created and pushed tag v${{ steps.version.outputs.version }}" + } + + - name: Get Commit Messages for Release Notes + id: release-notes + shell: pwsh + run: | + # Get the previous tag + $previousTag = git describe --tags --abbrev=0 "v${{ steps.version.outputs.version }}~1" 2>$null + if ($LASTEXITCODE -ne 0) { + # If no previous tag, get last 10 commits + $commits = git log --oneline --pretty=format:"- %s (%h)" -n 10 + } else { + # Get commits since the previous tag + $commits = git log --oneline --pretty=format:"- %s (%h)" "$previousTag..v${{ steps.version.outputs.version }}" + } + + $releaseNotes = @" + ## Changes in version ${{ steps.version.outputs.version }} + + $($commits -join "`n") + + ## Installation + ``````powershell + Install-Module -Name PSBlogger -RequiredVersion ${{ steps.version.outputs.version }} + `````` + "@ + + # Escape for GitHub Actions + $releaseNotes = $releaseNotes -replace "`r`n", "`n" -replace "`n", "%0A" + echo "release-notes=$releaseNotes" >> $env:GITHUB_OUTPUT + + - name: Create GitHub Release + if: github.event.inputs.mode == 'PUBLISH' + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ steps.version.outputs.version }} + release_name: Release v${{ steps.version.outputs.version }} + body: ${{ steps.release-notes.outputs.release-notes }} + draft: false + prerelease: false + + - name: Notify Success + if: success() + shell: pwsh + run: | + $isDryRun = "${{ github.event.inputs.mode }}" -eq "DRY_RUN" + + if ($isDryRun) { + Write-Host "๐Ÿงช DRY RUN COMPLETED SUCCESSFULLY!" + Write-Host "โœ… Module manifest validation passed" + Write-Host "โœ… All tests passed" + Write-Host "โœ… Module can be imported successfully" + Write-Host "" + Write-Host "Ready to publish PSBlogger v${{ steps.version.outputs.version }} to PowerShell Gallery" + Write-Host "To actually publish, run this workflow again with PUBLISH mode and type CONFIRM" + } else { + Write-Host "๐ŸŽ‰ Successfully published PSBlogger v${{ steps.version.outputs.version }} to PowerShell Gallery!" + Write-Host "๐Ÿ“ฆ Module is now available at: https://www.powershellgallery.com/packages/PSBlogger/${{ steps.version.outputs.version }}" + Write-Host "๐Ÿท๏ธ Created release: https://github.com/${{ github.repository }}/releases/tag/v${{ steps.version.outputs.version }}" + } diff --git a/.github/workflows/publish-powershell-gallery.yml b/.github/workflows/publish-powershell-gallery.yml new file mode 100644 index 0000000..a8a640f --- /dev/null +++ b/.github/workflows/publish-powershell-gallery.yml @@ -0,0 +1,220 @@ +name: Publish to PowerShell Gallery + +on: + push: + branches: [ main ] + paths: + - 'src/PSBlogger.psd1' + +jobs: + check-version: + runs-on: ubuntu-latest + outputs: + version-changed: ${{ steps.version-check.outputs.version-changed }} + new-version: ${{ steps.version-check.outputs.new-version }} + old-version: ${{ steps.version-check.outputs.old-version }} + steps: + - name: Checkout current commit + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Check if version changed + id: version-check + shell: pwsh + run: | + # Get the current version from the manifest + $manifestPath = "src/PSBlogger.psd1" + $manifest = Import-PowerShellDataFile -Path $manifestPath + $currentVersion = $manifest.ModuleVersion + Write-Host "Current version: $currentVersion" + + # Get the previous version from the previous commit + git checkout HEAD~1 -- $manifestPath 2>$null + if ($LASTEXITCODE -eq 0) { + $previousManifest = Import-PowerShellDataFile -Path $manifestPath + $previousVersion = $previousManifest.ModuleVersion + Write-Host "Previous version: $previousVersion" + } else { + Write-Host "No previous version found (first commit?)" + $previousVersion = "0.0.0" + } + + # Restore current version + git checkout HEAD -- $manifestPath + + # Check if version changed + if ($currentVersion -ne $previousVersion) { + Write-Host "Version changed from $previousVersion to $currentVersion" + echo "version-changed=true" >> $env:GITHUB_OUTPUT + echo "new-version=$currentVersion" >> $env:GITHUB_OUTPUT + echo "old-version=$previousVersion" >> $env:GITHUB_OUTPUT + } else { + Write-Host "Version unchanged: $currentVersion" + echo "version-changed=false" >> $env:GITHUB_OUTPUT + echo "new-version=$currentVersion" >> $env:GITHUB_OUTPUT + echo "old-version=$previousVersion" >> $env:GITHUB_OUTPUT + } + + publish: + needs: check-version + if: needs.check-version.outputs.version-changed == 'true' + runs-on: windows-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install PowerShell Module Dependencies + shell: pwsh + run: | + Set-PSRepository PSGallery -InstallationPolicy Trusted + Install-Module -Name PowerShell-Yaml -Force -SkipPublisherCheck + + - name: Validate Module Manifest + shell: pwsh + run: | + # Test module manifest + $manifestPath = "./src/PSBlogger.psd1" + Write-Host "Testing module manifest at: $manifestPath" + + # Test manifest syntax + $manifest = Test-ModuleManifest -Path $manifestPath -Verbose + Write-Host "Module manifest is valid" + Write-Host "Module Name: $($manifest.Name)" + Write-Host "Module Version: $($manifest.Version)" + Write-Host "Module Author: $($manifest.Author)" + Write-Host "Module Description: $($manifest.Description)" + + # Check required fields for PowerShell Gallery + if (-not $manifest.Description) { + throw "Module description is required for PowerShell Gallery" + } + if (-not $manifest.Author) { + throw "Module author is required for PowerShell Gallery" + } + + # Test that the module can be imported successfully + Import-Module $manifestPath -Force + $loadedModule = Get-Module PSBlogger + Write-Host "Successfully imported module. Exported commands:" + $loadedModule.ExportedCommands.Keys | Sort-Object | ForEach-Object { Write-Host " - $_" } + + - name: Run Tests + shell: pwsh + run: | + # Install Pester for testing + Install-Module -Name Pester -Force -SkipPublisherCheck + + # Install pandoc for tests + choco install pandoc -y + + # Change to src directory and run tests + Set-Location "./src" + + # Configure Pester + $PesterConfig = @{ + Run = @{ + Path = './tests' + } + Output = @{ + Verbosity = 'Normal' + } + Should = @{ + ErrorAction = 'Stop' + } + } + + # Run tests and fail if any tests fail + $testResults = Invoke-Pester -Configuration $PesterConfig + if ($testResults.FailedCount -gt 0) { + throw "Tests failed. Cannot publish to PowerShell Gallery." + } + + - name: Get Commit Messages for Release Notes + id: release-notes + shell: pwsh + run: | + # Get the previous tag + $previousTag = git describe --tags --abbrev=0 HEAD~1 2>$null + if ($LASTEXITCODE -ne 0) { + # If no previous tag, get all commits + $commits = git log --oneline --pretty=format:"- %s (%h)" + } else { + # Get commits since the previous tag + $commits = git log --oneline --pretty=format:"- %s (%h)" "$previousTag..HEAD" + } + + $releaseNotes = @" + ## Changes in version ${{ needs.check-version.outputs.new-version }} + + $($commits -join "`n") + "@ + + # Escape for GitHub Actions + $releaseNotes = $releaseNotes -replace "`r`n", "`n" -replace "`n", "%0A" + echo "release-notes=$releaseNotes" >> $env:GITHUB_OUTPUT + + - name: Create Git Tag + shell: pwsh + run: | + git config user.name "github-actions" + git config user.email "github-actions@github.com" + git tag -a "v${{ needs.check-version.outputs.new-version }}" -m "Release version ${{ needs.check-version.outputs.new-version }}" + git push origin "v${{ needs.check-version.outputs.new-version }}" + + - name: Create GitHub Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ needs.check-version.outputs.new-version }} + release_name: Release v${{ needs.check-version.outputs.new-version }} + body: ${{ steps.release-notes.outputs.release-notes }} + draft: false + prerelease: false + + - name: Publish to PowerShell Gallery + shell: pwsh + env: + NUGET_API_KEY: ${{ secrets.POWERSHELLGALLERY_API }} + run: | + # Validate that we have the API key + if (-not $env:NUGET_API_KEY) { + throw "POWERSHELLGALLERY_API secret is not set" + } + + Write-Host "Publishing PSBlogger version ${{ needs.check-version.outputs.new-version }} to PowerShell Gallery..." + + # Check if this version already exists + try { + $existingModule = Find-Module -Name PSBlogger -RequiredVersion ${{ needs.check-version.outputs.new-version }} -ErrorAction SilentlyContinue + if ($existingModule) { + throw "Version ${{ needs.check-version.outputs.new-version }} already exists on PowerShell Gallery" + } + } catch { + if ($_.Exception.Message -notlike "*Version*already exists*") { + Write-Host "Could not check existing versions (this is normal for new modules): $($_.Exception.Message)" + } else { + throw + } + } + + # Publish the module + try { + Publish-Module -Path "./src" -NuGetApiKey $env:NUGET_API_KEY -Verbose -Force + Write-Host "โœ… Successfully published to PowerShell Gallery" + } catch { + Write-Error "โŒ Failed to publish to PowerShell Gallery: $_" + throw + } + + - name: Notify Success + if: success() + shell: pwsh + run: | + Write-Host "๐ŸŽ‰ Successfully published PSBlogger v${{ needs.check-version.outputs.new-version }} to PowerShell Gallery!" + Write-Host "๐Ÿ“ฆ Module is now available at: https://www.powershellgallery.com/packages/PSBlogger/${{ needs.check-version.outputs.new-version }}" + Write-Host "๐Ÿท๏ธ Created release: https://github.com/${{ github.repository }}/releases/tag/v${{ needs.check-version.outputs.new-version }}"