Publish-ToGallery.ps1
|
<#
.SYNOPSIS Complete workflow to test and publish iFacto.AICodeReview module to PowerShell Gallery .DESCRIPTION This script provides a complete manual workflow for publishing the module: 1. Validates module structure 2. Runs all tests 3. Checks version isn't already published 4. Optionally updates version 5. Publishes to PowerShell Gallery .PARAMETER NuGetApiKey NuGet API key for PowerShell Gallery. Get from https://www.powershellgallery.com/account/apikeys Can also be set via $env:NUGET_API_KEY .PARAMETER UpdateVersion Update the module version before publishing. Format: X.Y.Z (e.g., 0.2.0) .PARAMETER SkipTests Skip running Pester tests (not recommended) .PARAMETER SkipValidation Skip build validation (not recommended) .PARAMETER WhatIf Show what would be done without actually publishing .PARAMETER Force Skip confirmation prompts .EXAMPLE # First time setup - get your API key $apiKey = Read-Host "Enter your PowerShell Gallery API Key" -AsSecureString $env:NUGET_API_KEY = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($apiKey)) .EXAMPLE # Publish current version .\Publish-ToGallery.ps1 .EXAMPLE # Update version and publish .\Publish-ToGallery.ps1 -UpdateVersion "0.2.0" .EXAMPLE # Test run without publishing .\Publish-ToGallery.ps1 -WhatIf .EXAMPLE # Force publish without prompts .\Publish-ToGallery.ps1 -Force .NOTES Prerequisites: - PowerShell Gallery API key from https://www.powershellgallery.com/account/apikeys - Pester 5.x installed (Install-Module Pester -Force -SkipPublisherCheck -MinimumVersion 5.0) - Git repository with no uncommitted changes (recommended) #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter()] [string]$NuGetApiKey = $env:NUGET_API_KEY, [Parameter()] [ValidatePattern('^\d+\.\d+\.\d+$')] [string]$UpdateVersion, [Parameter()] [switch]$SkipTests, [Parameter()] [switch]$SkipValidation, [Parameter()] [switch]$Force ) $ErrorActionPreference = 'Stop' # Module details $moduleRoot = $PSScriptRoot $moduleName = 'iFacto.AICodeReview' $manifestPath = Join-Path $moduleRoot "$moduleName.psd1" Write-Host "" Write-Host "========================================" -ForegroundColor Cyan Write-Host " PowerShell Gallery Publishing Workflow" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" Write-Host "Module: $moduleName" -ForegroundColor White Write-Host "Path: $moduleRoot" -ForegroundColor Gray Write-Host "" # ============================================================================= # PRE-FLIGHT CHECKS # ============================================================================= Write-Host "Pre-flight Checks" -ForegroundColor Yellow Write-Host "----------------" -ForegroundColor Yellow # Check 1: API Key if ([string]::IsNullOrWhiteSpace($NuGetApiKey)) { Write-Host "❌ NuGet API key not found" -ForegroundColor Red Write-Host "" Write-Host "To get your API key:" -ForegroundColor Cyan Write-Host "1. Go to https://www.powershellgallery.com/" -ForegroundColor White Write-Host "2. Sign in with your account" -ForegroundColor White Write-Host "3. Go to https://www.powershellgallery.com/account/apikeys" -ForegroundColor White Write-Host "4. Create a new API key with 'Push' permission" -ForegroundColor White Write-Host "" Write-Host "Then set it:" -ForegroundColor Cyan Write-Host "`$env:NUGET_API_KEY = 'your-api-key-here'" -ForegroundColor White Write-Host "" throw "NuGet API key required" } Write-Host " ✓ NuGet API key found" -ForegroundColor Green # Check 2: Pester try { $pesterModule = Get-Module -ListAvailable -Name Pester | Sort-Object Version -Descending | Select-Object -First 1 if ($pesterModule.Version.Major -lt 5) { Write-Host " ⚠️ Pester version $($pesterModule.Version) found (5.x recommended)" -ForegroundColor Yellow Write-Host " Install with: Install-Module Pester -Force -SkipPublisherCheck -MinimumVersion 5.0" -ForegroundColor Gray } else { Write-Host " ✓ Pester $($pesterModule.Version) installed" -ForegroundColor Green } } catch { Write-Host " ⚠️ Pester not installed (required for tests)" -ForegroundColor Yellow } # Check 3: Git status try { $gitStatus = git status --porcelain 2>&1 if ($gitStatus -and -not $Force) { Write-Host " ⚠️ Git repository has uncommitted changes" -ForegroundColor Yellow Write-Host " Consider committing changes before publishing" -ForegroundColor Gray } else { Write-Host " ✓ Git repository clean" -ForegroundColor Green } } catch { Write-Host " ℹ️ Not a git repository" -ForegroundColor Cyan } # Check 4: Module manifest exists if (-not (Test-Path $manifestPath)) { throw "Module manifest not found: $manifestPath" } Write-Host " ✓ Module manifest found" -ForegroundColor Green Write-Host "" # ============================================================================= # VERSION MANAGEMENT # ============================================================================= Write-Host "Version Management" -ForegroundColor Yellow Write-Host "------------------" -ForegroundColor Yellow # Get current version $manifest = Import-PowerShellDataFile -Path $manifestPath $currentVersion = $manifest.ModuleVersion Write-Host " Current version: $currentVersion" -ForegroundColor White # Update version if requested if ($UpdateVersion) { Write-Host " New version: $UpdateVersion" -ForegroundColor Cyan if ($UpdateVersion -eq $currentVersion) { Write-Host " ⚠️ New version same as current version" -ForegroundColor Yellow } if (-not $Force) { $confirm = Read-Host "Update version from $currentVersion to $UpdateVersion? (y/N)" if ($confirm -ne 'y') { throw "Version update cancelled by user" } } # Update manifest file $manifestContent = Get-Content $manifestPath -Raw $manifestContent = $manifestContent -replace "ModuleVersion\s*=\s*'[\d\.]+?'", "ModuleVersion = '$UpdateVersion'" Set-Content -Path $manifestPath -Value $manifestContent -NoNewline Write-Host " ✓ Version updated to $UpdateVersion" -ForegroundColor Green $currentVersion = $UpdateVersion # Update changelog $changelogPath = Join-Path $moduleRoot 'CHANGELOG.md' if (Test-Path $changelogPath) { Write-Host " ℹ️ Don't forget to update CHANGELOG.md" -ForegroundColor Cyan } } # Check if version already published Write-Host "" Write-Host "Checking PowerShell Gallery..." -ForegroundColor Yellow try { $published = Find-Module -Name $moduleName -RequiredVersion $currentVersion -ErrorAction SilentlyContinue if ($published) { throw "Version $currentVersion is already published to PowerShell Gallery. Use -UpdateVersion to publish a new version." } Write-Host " ✓ Version $currentVersion not yet published" -ForegroundColor Green } catch { if ($_.Exception.Message -like "*already published*") { throw } # Module doesn't exist yet - that's fine for first publish Write-Host " ✓ Module not found in gallery (first publish)" -ForegroundColor Green } Write-Host "" # ============================================================================= # BUILD VALIDATION # ============================================================================= if (-not $SkipValidation) { Write-Host "Build Validation" -ForegroundColor Yellow Write-Host "----------------" -ForegroundColor Yellow $buildScript = Join-Path $moduleRoot 'Build\Build-Module.ps1' if (Test-Path $buildScript) { try { & $buildScript -SkipTests:$SkipTests Write-Host "" } catch { throw "Build validation failed: $_" } } else { Write-Host " ⚠️ Build script not found, performing basic validation..." -ForegroundColor Yellow # Basic validation $requiredFolders = @('Public', 'Private', 'Rules') foreach ($folder in $requiredFolders) { $folderPath = Join-Path $moduleRoot $folder if (-not (Test-Path $folderPath)) { throw "Required folder missing: $folder" } } Write-Host " ✓ Basic structure validation passed" -ForegroundColor Green Write-Host "" } } else { Write-Host "⚠️ Skipping build validation (not recommended)" -ForegroundColor Yellow Write-Host "" } # ============================================================================= # SUMMARY & CONFIRMATION # ============================================================================= Write-Host "========================================" -ForegroundColor Cyan Write-Host "Ready to Publish" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" Write-Host "Module: $moduleName" -ForegroundColor White Write-Host "Version: $currentVersion" -ForegroundColor White Write-Host "Destination: PowerShell Gallery" -ForegroundColor White Write-Host "" if ($WhatIfPreference) { Write-Host "WhatIf: Would publish module to PowerShell Gallery" -ForegroundColor Yellow Write-Host "" Write-Host "✓ All checks passed - ready to publish" -ForegroundColor Green exit 0 } if (-not $Force) { Write-Host "This will publish the module to PowerShell Gallery where it will be publicly available." -ForegroundColor Yellow Write-Host "" $confirm = Read-Host "Continue with publish? (y/N)" if ($confirm -ne 'y') { Write-Host "" Write-Host "Publish cancelled by user" -ForegroundColor Yellow exit 0 } } # ============================================================================= # PUBLISH # ============================================================================= Write-Host "" Write-Host "Publishing to PowerShell Gallery..." -ForegroundColor Cyan Write-Host "" try { # Publish module Publish-Module ` -Path $moduleRoot ` -NuGetApiKey $NuGetApiKey ` -Repository PSGallery ` -Verbose Write-Host "" Write-Host "========================================" -ForegroundColor Green Write-Host "✓ Successfully Published!" -ForegroundColor Green Write-Host "========================================" -ForegroundColor Green Write-Host "" Write-Host "Module: $moduleName" -ForegroundColor White Write-Host "Version: $currentVersion" -ForegroundColor White Write-Host "" Write-Host "View at: https://www.powershellgallery.com/packages/$moduleName/$currentVersion" -ForegroundColor Cyan Write-Host "" Write-Host "Install with:" -ForegroundColor Yellow Write-Host " Install-Module $moduleName" -ForegroundColor White Write-Host "" Write-Host "Note: It may take a few minutes for the module to be searchable in the gallery." -ForegroundColor Gray Write-Host "" # Tag in git if possible if (-not (git rev-parse --git-dir 2>$null)) { Write-Host "Next steps:" -ForegroundColor Yellow Write-Host "1. Commit the version change: git commit -am 'Release v$currentVersion'" -ForegroundColor White Write-Host "2. Create a tag: git tag v$currentVersion" -ForegroundColor White Write-Host "3. Push changes: git push && git push --tags" -ForegroundColor White Write-Host "" } } catch { Write-Host "" Write-Host "========================================" -ForegroundColor Red Write-Host "❌ Publish Failed" -ForegroundColor Red Write-Host "========================================" -ForegroundColor Red Write-Host "" Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red Write-Host "" if ($_.Exception.Message -like "*conflict*") { Write-Host "This usually means the version is already published." -ForegroundColor Yellow Write-Host "Use -UpdateVersion to publish a new version." -ForegroundColor Yellow } Write-Host "" throw } |