Build-ModuleManifest.ps1

# Build-ModuleManifest.ps1
[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [string]$ModulePath = $PSScriptRoot
)

$psd1Path = Join-Path $ModulePath "cloudstack-ps.psd1"
$psm1Path = Join-Path $ModulePath "cloudstack-ps.psm1"
$publicPath = Join-Path $ModulePath "Public"
$allFunctions = @()
$allAliases = @()

# Function to extract function names from a file
function Get-FunctionsFromFile {
    param([string]$FilePath)

    $ast = [System.Management.Automation.Language.Parser]::ParseFile(
        $FilePath,
        [ref]$null,
        [ref]$null
    )

    $functions = $ast.FindAll({
        $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst]
    }, $true) | ForEach-Object { $_.Name }

    return $functions
}

# Extract names from backwards-compatibility 'Set-Alias -Name <old> -Value <new>' calls
function Get-AliasesFromFile {
    param([string]$FilePath)

    $ast = [System.Management.Automation.Language.Parser]::ParseFile(
        $FilePath,
        [ref]$null,
        [ref]$null
    )

    $commands = $ast.FindAll({
        $args[0] -is [System.Management.Automation.Language.CommandAst]
    }, $true) | Where-Object { $_.GetCommandName() -eq 'Set-Alias' }

    $aliases = foreach ($command in $commands) {
        $elements = $command.CommandElements
        for ($i = 0; $i -lt $elements.Count - 1; $i++) {
            if ($elements[$i] -is [System.Management.Automation.Language.CommandParameterAst] -and
                $elements[$i].ParameterName -eq 'Name') {
                $elements[$i + 1].Value
            }
        }
    }

    return $aliases
}

# Check .psm1 file
if (Test-Path $psm1Path) {
    Write-Host "Scanning cloudstack.psm1..." -ForegroundColor Cyan
    $psm1Functions = Get-FunctionsFromFile -FilePath $psm1Path
    if ($psm1Functions) {
        Write-Host " Found $($psm1Functions.Count) functions in .psm1" -ForegroundColor Gray
        $allFunctions += $psm1Functions
    }
    $allAliases += Get-AliasesFromFile -FilePath $psm1Path
}

# Check Public folder
if (Test-Path $publicPath) {
    Write-Host "Scanning Public folder..." -ForegroundColor Cyan
    $publicFiles = Get-ChildItem -Path $publicPath -Filter "*.ps1" -File
    
    foreach ($file in $publicFiles) {
        Write-Host " Scanning $($file.Name)..." -ForegroundColor Gray
        $fileFunctions = Get-FunctionsFromFile -FilePath $file.FullName
        if ($fileFunctions) {
            Write-Host " Found: $($fileFunctions -join ', ')" -ForegroundColor DarkGray
            $allFunctions += $fileFunctions
        }

        $fileAliases = Get-AliasesFromFile -FilePath $file.FullName
        if ($fileAliases) {
            Write-Host " Aliases: $($fileAliases -join ', ')" -ForegroundColor DarkGray
            $allAliases += $fileAliases
        }
    }
}

# Remove duplicates and sort
$allFunctions = $allFunctions | Select-Object -Unique | Sort-Object
$allAliases = $allAliases | Select-Object -Unique | Sort-Object

Write-Host "`nTotal functions found: $($allFunctions.Count)" -ForegroundColor Cyan
$allFunctions | ForEach-Object { Write-Host " - $_" -ForegroundColor White }

Write-Host "`nTotal aliases found: $($allAliases.Count)" -ForegroundColor Cyan
$allAliases | ForEach-Object { Write-Host " - $_" -ForegroundColor White }

if ($allFunctions.Count -eq 0) {
    Write-Warning "No functions found! Check your module structure."
    exit 1
}

# BUILD THE MANIFEST FROM SCRATCH
Write-Host "`nBuilding module manifest..." -ForegroundColor Cyan

# Create the functions and aliases array strings
$functionsString = ($allFunctions | ForEach-Object { " '$_'" }) -join ",`n"
$aliasesString = ($allAliases | ForEach-Object { " '$_'" }) -join ",`n"

# Build the complete manifest content - USE LOWERCASE cloudstack.psm1
$manifestContent = @"
@{
    RootModule = 'cloudstack-ps.psm1'
    ModuleVersion = '0.0.1'
    GUID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
    Author = 'Tyler Verkade'
    CompanyName = 'US Signal Company'
    Copyright = '(c) 2026. All rights reserved.'
    Description = 'PowerShell module for interacting with Apache CloudStack API'
    PowerShellVersion = '5.1'
    
    FunctionsToExport = @(
$functionsString
    )
    
    CmdletsToExport = @()
    VariablesToExport = @()

    # Deprecated pre-1.1.0 names, kept so existing scripts keep working.
    AliasesToExport = @(
$aliasesString
    )

    PrivateData = @{
        PSData = @{
            Tags = @('CloudStack', 'API', 'Cloud')
            ReleaseNotes = 'Renamed functions that used unapproved PowerShell verbs. Old names remain available as aliases.'
        }
    }
}
"@


# Write the manifest. 'Out-File -Encoding UTF8' means UTF-8 *with* BOM on
# Windows PowerShell 5.1 and *without* on PowerShell 6+, and a BOM-less file
# containing non-ASCII is read as the ANSI code page by 5.1, which corrupts it.
# Go through .NET so the encoding is the same on every host.
$utf8WithBom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($psd1Path, $manifestContent, $utf8WithBom)

Write-Host "✓ Module manifest created!" -ForegroundColor Green

# Test the manifest
Write-Host "`nTesting module manifest..." -ForegroundColor Cyan
try {
    $null = Test-ModuleManifest -Path $psd1Path -ErrorAction Stop
    Write-Host "✓ Manifest is valid!" -ForegroundColor Green
    Write-Host " Functions exported: $($allFunctions.Count)" -ForegroundColor Cyan
}
catch {
    Write-Error "Manifest validation failed: $_"
    exit 1
}