Public/Install-PCReleaseWorkflow.ps1
|
<# .SYNOPSIS Installs CI and Release workflow templates into a target repository. .DESCRIPTION Detects project technology (PowerShell, .NET, Node, Python), selects the appropriate workflow templates, and installs them with all tokens resolved from the project manifest. Token resolution: {{MODULE_NAME}} — Module/project name from manifest {{RUNNER}} — CI runner OS (default: ubuntu-latest) {{DEFAULT_BRANCH}} — Default branch name (default: main) {{SECRETS_BOOTSTRAP}} — Secrets step (auto-generated when PowerCraft.Secrets detected) Override defaults with an optional .powercraft/release.json config file. That file is never overwritten by this command. .PARAMETER Path Target repository root directory. Defaults to current directory. .PARAMETER Force Overwrite existing workflow files without prompting. .EXAMPLE Install-PCReleaseWorkflow # Installs workflows into current directory's .github/workflows/ .EXAMPLE Install-PCReleaseWorkflow -Path C:\repos\MyModule -Force # Overwrites any existing workflows .OUTPUTS [PSCustomObject[]] List of files created/updated. #> function Install-PCReleaseWorkflow { [CmdletBinding(SupportsShouldProcess)] [OutputType([PSCustomObject[]])] param( [Parameter()] [string]$Path = (Get-Location).Path, [switch]$Force ) $templateRoot = Join-Path $PSScriptRoot '..' 'Templates' if (-not (Test-Path $templateRoot)) { throw "Templates directory not found at '$templateRoot'. Module may be incorrectly installed." } # --- Technology detection --- $projectType = Get-ProjectType -Path $Path $techName = $projectType.Type.ToLower() Write-Verbose "Detected project type: $techName" $techDir = Join-Path $templateRoot $techName if (-not (Test-Path $techDir)) { throw "No templates found for project type '$techName' at '$techDir'." } # --- Detect module/project name --- switch ($techName) { 'powershell' { $psd1 = Get-ChildItem -Path $Path -Filter '*.psd1' -File | Select-Object -First 1 if (-not $psd1) { throw "No .psd1 file found in '$Path'." } $projectName = $psd1.BaseName $manifestData = Import-PowerShellDataFile $psd1.FullName } default { throw "Technology '$techName' template support is not yet implemented." } } Write-Verbose "Project name: $projectName" # --- Load config (optional .powercraft/release.json) --- $config = Read-PCReleaseConfig -Path $Path # --- Build secrets bootstrap block --- $secretsBlock = '' if ($techName -eq 'powershell' -and $psd1) { $secretsBlock = Get-SecretBootstrapBlock -ManifestPath $psd1.FullName } # --- Build token table --- $defaultBranch = if ($config.ci.branches) { $config.ci.branches[0] } else { 'main' } $tokens = @{ MODULE_NAME = $projectName RUNNER = $config.ci.runner DEFAULT_BRANCH = $defaultBranch SECRETS_BOOTSTRAP = $secretsBlock } # --- Ensure .github/workflows exists --- $workflowDir = Join-Path $Path '.github' 'workflows' if (-not (Test-Path $workflowDir)) { New-Item -ItemType Directory -Path $workflowDir -Force | Out-Null Write-Verbose "Created: $workflowDir" } $results = @() # --- Version stamp --- $pcReleasePsd1 = Join-Path $PSScriptRoot '..' 'PowerCraft.Release.psd1' $pcReleaseVersion = (Import-PowerShellDataFile $pcReleasePsd1).ModuleVersion $versionStamp = "# Generated by PowerCraft.Release v$pcReleaseVersion — do not edit manually" # --- Install technology-specific templates --- $techTemplates = Get-ChildItem -Path $techDir -Filter '*.yml' -File foreach ($template in $techTemplates) { if ($config.disabled -contains $template.BaseName) { Write-Verbose "Skipping disabled template: $($template.Name)" $results += [PSCustomObject]@{ File = $template.Name; Status = 'Skipped'; Reason = 'Disabled in config' } continue } $targetFile = Join-Path $workflowDir $template.Name if ((Test-Path $targetFile) -and -not $Force) { Write-Warning "$($template.Name) already exists. Use -Force to overwrite." $results += [PSCustomObject]@{ File = $template.Name; Status = 'Skipped'; Reason = 'Already exists' } continue } if ($PSCmdlet.ShouldProcess($targetFile, "Install $($template.Name) workflow")) { $content = Get-Content -Path $template.FullName -Raw $content = Resolve-TemplateTokens -Content $content -Tokens $tokens Set-Content -Path $targetFile -Value "$versionStamp`n$content" -NoNewline $results += [PSCustomObject]@{ File = $template.Name; Status = 'Installed'; Reason = '' } } } # --- Install shared templates --- $sharedDir = Join-Path $templateRoot 'shared' if (Test-Path $sharedDir) { $sharedTemplates = Get-ChildItem -Path $sharedDir -Filter '*.yml' -File foreach ($template in $sharedTemplates) { # Skip dependency-check if no dependencies if ($template.BaseName -eq 'dependency-check' -and -not $manifestData.RequiredModules) { Write-Verbose "Skipping dependency-check.yml — no RequiredModules" continue } if ($config.disabled -contains $template.BaseName) { Write-Verbose "Skipping disabled template: $($template.Name)" $results += [PSCustomObject]@{ File = $template.Name; Status = 'Skipped'; Reason = 'Disabled in config' } continue } $targetFile = Join-Path $workflowDir $template.Name if ((Test-Path $targetFile) -and -not $Force) { Write-Warning "$($template.Name) already exists. Use -Force to overwrite." $results += [PSCustomObject]@{ File = $template.Name; Status = 'Skipped'; Reason = 'Already exists' } continue } if ($PSCmdlet.ShouldProcess($targetFile, "Install $($template.Name) workflow")) { $content = Get-Content -Path $template.FullName -Raw $content = Resolve-TemplateTokens -Content $content -Tokens $tokens Set-Content -Path $targetFile -Value "$versionStamp`n$content" -NoNewline $results += [PSCustomObject]@{ File = $template.Name; Status = 'Installed'; Reason = '' } } } } # --- Clean up legacy files --- $publishYml = Join-Path $workflowDir 'publish.yml' if (Test-Path $publishYml) { Write-Host " Found legacy publish.yml — consider removing it (superseded by release.yml)" -ForegroundColor Yellow } # --- Summary --- Write-Host "`nWorkflow installation for '$projectName' ($techName):" -ForegroundColor Cyan foreach ($r in $results) { $color = if ($r.Status -eq 'Installed') { 'Green' } else { 'Yellow' } $detail = if ($r.Reason) { " ($($r.Reason))" } else { '' } Write-Host " $($r.File): $($r.Status)$detail" -ForegroundColor $color } Write-Host "`nReminder:" -ForegroundColor Cyan Write-Host " Ensure PSGALLERY_API_KEY secret is set:" -ForegroundColor White Write-Host " gh secret set PSGALLERY_API_KEY --repo <owner>/<repo>" -ForegroundColor Gray return $results } |