Release.ps1

<#
.SYNOPSIS
    Automates version bumping and release tagging for ExoAliasManagement module.

.DESCRIPTION
    This script helps maintain consistent versioning by:
    - Reading current version from module manifest
    - Prompting for version bump type (major/minor/patch) or custom version
    - Updating the module manifest with new version
    - Prompting for release notes
    - Creating a Git commit
    - Creating a matching Git tag
    - Optionally pushing to remote

.PARAMETER BumpType
    Type of version bump: Major, Minor, or Patch. If not specified, prompts interactively.

.PARAMETER CustomVersion
    Specify a custom version number instead of bumping. Format: x.y.z

.PARAMETER SkipPush
    Don't push changes and tags to remote repository.

.EXAMPLE
    .\Release.ps1
    # Interactive mode - prompts for all inputs

.EXAMPLE
    .\Release.ps1 -BumpType Patch
    # Bump patch version (1.0.0 -> 1.0.1)

.EXAMPLE
    .\Release.ps1 -BumpType Minor
    # Bump minor version (1.0.0 -> 1.1.0)

.EXAMPLE
    .\Release.ps1 -CustomVersion "2.0.0"
    # Set specific version
#>


[CmdletBinding()]
param(
    [Parameter()]
    [ValidateSet('Major', 'Minor', 'Patch')]
    [string]$BumpType,
    
    [Parameter()]
    [ValidatePattern('^\d+\.\d+\.\d+$')]
    [string]$CustomVersion,
    
    [Parameter()]
    [switch]$SkipPush
)

$ErrorActionPreference = 'Stop'

# Get script directory
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$manifestPath = Join-Path $scriptPath "ExoAliasManagement.psd1"

# Verify we're in a git repository
try {
    git rev-parse --git-dir 2>&1 | Out-Null
} catch {
    Write-Host "Error: Not in a Git repository. Please initialize Git first." -ForegroundColor Red
    exit 1
}

# Check for uncommitted changes
$gitStatus = git status --porcelain
if ($gitStatus -and $gitStatus -notmatch '^M.*ExoAliasManagement\.psd1$') {
    Write-Host "Warning: You have uncommitted changes besides the manifest:" -ForegroundColor Yellow
    git status --short
    $continue = Read-Host "`nContinue anyway? (y/N)"
    if ($continue -ne 'y' -and $continue -ne 'Y') {
        Write-Host "Release cancelled." -ForegroundColor Yellow
        exit 0
    }
}

# Read current version from manifest
Write-Host "`nReading current version from manifest..." -ForegroundColor Cyan
$manifest = Test-ModuleManifest -Path $manifestPath -ErrorAction Stop
$currentVersion = $manifest.Version
Write-Host "Current version: $currentVersion" -ForegroundColor Green

# Determine new version
if ($CustomVersion) {
    $newVersion = [Version]$CustomVersion
    Write-Host "Using custom version: $newVersion" -ForegroundColor Cyan
} elseif ($BumpType) {
    switch ($BumpType) {
        'Major' { $newVersion = [Version]::new($currentVersion.Major + 1, 0, 0) }
        'Minor' { $newVersion = [Version]::new($currentVersion.Major, $currentVersion.Minor + 1, 0) }
        'Patch' { $newVersion = [Version]::new($currentVersion.Major, $currentVersion.Minor, $currentVersion.Build + 1) }
    }
    Write-Host "Bumping $BumpType version: $currentVersion -> $newVersion" -ForegroundColor Cyan
} else {
    # Interactive mode
    Write-Host "`nSelect version bump type:" -ForegroundColor Cyan
    Write-Host " 1) Patch ($currentVersion -> $($currentVersion.Major).$($currentVersion.Minor).$($currentVersion.Build + 1))" -ForegroundColor White
    Write-Host " 2) Minor ($currentVersion -> $($currentVersion.Major).$($currentVersion.Minor + 1).0)" -ForegroundColor White
    Write-Host " 3) Major ($currentVersion -> $($currentVersion.Major + 1).0.0)" -ForegroundColor White
    Write-Host " 4) Custom version" -ForegroundColor White
    
    $choice = Read-Host "`nEnter choice (1-4)"
    
    switch ($choice) {
        '1' { $newVersion = [Version]::new($currentVersion.Major, $currentVersion.Minor, $currentVersion.Build + 1) }
        '2' { $newVersion = [Version]::new($currentVersion.Major, $currentVersion.Minor + 1, 0) }
        '3' { $newVersion = [Version]::new($currentVersion.Major + 1, 0, 0) }
        '4' { 
            $customInput = Read-Host "Enter custom version (x.y.z)"
            if ($customInput -match '^\d+\.\d+\.\d+$') {
                $newVersion = [Version]$customInput
            } else {
                Write-Host "Error: Invalid version format. Must be x.y.z" -ForegroundColor Red
                exit 1
            }
        }
        default {
            Write-Host "Error: Invalid choice" -ForegroundColor Red
            exit 1
        }
    }
}

Write-Host "`nNew version will be: $newVersion" -ForegroundColor Green

# Prompt for release notes
Write-Host "`nEnter release notes (press Enter twice when done):" -ForegroundColor Cyan
$releaseNotes = @()
do {
    $line = Read-Host
    if ($line) { $releaseNotes += $line }
} while ($line)

if ($releaseNotes.Count -eq 0) {
    $releaseNotes = @("Version $newVersion release")
}

$releaseNotesText = $releaseNotes -join "`n"

# Read current manifest content
$manifestContent = Get-Content $manifestPath -Raw

# Update ModuleVersion
$manifestContent = $manifestContent -replace "(ModuleVersion\s*=\s*['""])\d+\.\d+\.\d+(['""])", "`${1}$newVersion`${2}"

# Get current date
$currentDate = Get-Date -Format "yyyy-MM-dd"

# Build new release notes section
$newReleaseNotesSection = @"
v$newVersion ($currentDate)
$releaseNotesText

"@


# Update ReleaseNotes - insert at the beginning of existing notes
if ($manifestContent -match "ReleaseNotes\s*=\s*@'[\r\n]+(.*?)[\r\n]+'@") {
    $existingNotes = $Matches[1]
    $updatedNotes = $newReleaseNotesSection + $existingNotes
    $manifestContent = $manifestContent -replace "(ReleaseNotes\s*=\s*@'[\r\n]+).*?([\r\n]+'@)", "`${1}$updatedNotes`${2}"
} else {
    Write-Host "Warning: Could not find ReleaseNotes section in manifest" -ForegroundColor Yellow
}

# Write updated manifest
Set-Content -Path $manifestPath -Value $manifestContent -NoNewline

Write-Host "`nManifest updated successfully!" -ForegroundColor Green

# Verify the manifest is valid
try {
    Test-ModuleManifest -Path $manifestPath -ErrorAction Stop | Out-Null
    Write-Host "Manifest validation: PASSED" -ForegroundColor Green
} catch {
    Write-Host "Error: Updated manifest is invalid!" -ForegroundColor Red
    Write-Host $_.Exception.Message -ForegroundColor Red
    exit 1
}

# Git operations
$commitMessage = "Bump version to $newVersion"
$tagName = "v$newVersion"
$tagMessage = "Release v$newVersion`n`n$releaseNotesText"

Write-Host "`nCreating Git commit and tag..." -ForegroundColor Cyan

try {
    # Stage the manifest file
    git add $manifestPath
    
    # Create commit
    git commit -m $commitMessage
    Write-Host "Commit created: $commitMessage" -ForegroundColor Green
    
    # Create annotated tag
    git tag -a $tagName -m $tagMessage
    Write-Host "Tag created: $tagName" -ForegroundColor Green
    
    # Show what was done
    Write-Host "`n--- Commit ---" -ForegroundColor Cyan
    git log -1 --oneline
    
    Write-Host "`n--- Tag ---" -ForegroundColor Cyan
    git tag -l $tagName -n9
    
    # Push to remote (unless SkipPush)
    if (-not $SkipPush) {
        Write-Host "`nPush changes to remote? (y/N): " -ForegroundColor Yellow -NoNewline
        $pushConfirm = Read-Host
        
        if ($pushConfirm -eq 'y' -or $pushConfirm -eq 'Y') {
            $branch = git rev-parse --abbrev-ref HEAD
            git push origin $branch
            git push origin $tagName
            Write-Host "`nChanges and tag pushed to remote!" -ForegroundColor Green
        } else {
            Write-Host "`nSkipped push. To push later, run:" -ForegroundColor Yellow
            Write-Host " git push origin $branch" -ForegroundColor White
            Write-Host " git push origin $tagName" -ForegroundColor White
        }
    } else {
        Write-Host "`nPush skipped (use -SkipPush parameter)" -ForegroundColor Yellow
        $branch = git rev-parse --abbrev-ref HEAD
        Write-Host "To push later, run:" -ForegroundColor Yellow
        Write-Host " git push origin $branch" -ForegroundColor White
        Write-Host " git push origin $tagName" -ForegroundColor White
    }
    
    Write-Host "`n✓ Release $newVersion completed successfully!" -ForegroundColor Green
    
} catch {
    Write-Host "`nError during Git operations:" -ForegroundColor Red
    Write-Host $_.Exception.Message -ForegroundColor Red
    Write-Host "`nThe manifest has been updated but Git operations failed." -ForegroundColor Yellow
    Write-Host "You may need to commit and tag manually." -ForegroundColor Yellow
    exit 1
}