Files
PSInfisicalAPI/.gitea/workflows/publish-psgallery.yml
T
GraceSolutions e94bb2c52d
Publish to PowerShell Gallery / build (pull_request) Successful in 27s
Publish to PowerShell Gallery / release (pull_request) Successful in 16s
Publish to PowerShell Gallery / publish (pull_request) Failing after 3s
ci: add diagnostics + strict mode to Create Gitea release step
2026-06-04 22:51:48 -04:00

314 lines
14 KiB
YAML

name: Publish to PowerShell Gallery
on:
pull_request:
types: [closed]
branches: [main]
jobs:
build:
if: github.event.pull_request.merged == true
runs-on: powershell-linux
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Verify host prerequisites (pwsh, dotnet)
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$missing = @()
if (-not (Get-Command pwsh -ErrorAction SilentlyContinue)) { $missing += 'pwsh' }
if (-not (Get-Command dotnet -ErrorAction SilentlyContinue)) { $missing += 'dotnet' }
if ($missing.Count -gt 0) {
throw "Host runner is missing required tool(s): $($missing -join ', '). Provision them on the runner host."
}
Write-Host ("pwsh: " + (pwsh -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'))
Write-Host ("dotnet: " + (dotnet --version))
Write-Host '--- dotnet --info ---'
dotnet --info
Write-Host '--- disk free ---'
df -h .
Write-Host '--- memory ---'
free -m
- name: Restore NuGet packages
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
Write-Host '==> dotnet restore src/PSInfisicalAPI/PSInfisicalAPI.csproj'
dotnet restore src/PSInfisicalAPI/PSInfisicalAPI.csproj --verbosity normal
if ($LASTEXITCODE -ne 0) { throw "Restore of PSInfisicalAPI.csproj failed with exit code $LASTEXITCODE" }
Write-Host '==> dotnet restore src/PSInfisicalAPI.Tests/PSInfisicalAPI.Tests.csproj'
dotnet restore src/PSInfisicalAPI.Tests/PSInfisicalAPI.Tests.csproj --verbosity normal
if ($LASTEXITCODE -ne 0) { throw "Restore of PSInfisicalAPI.Tests.csproj failed with exit code $LASTEXITCODE" }
- name: Build module
shell: pwsh
run: ./build.ps1
- name: Validate module manifest
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$manifestPath = Join-Path $PWD 'Module/PSInfisicalAPI/PSInfisicalAPI.psd1'
$manifest = Test-ModuleManifest -Path $manifestPath
Write-Host "Manifest OK: $($manifest.Name) $($manifest.Version)"
- name: Upload module artifact
uses: christopherhx/gitea-upload-artifact@v4
with:
name: PSInfisicalAPI-module
path: Module/PSInfisicalAPI
if-no-files-found: error
retention-days: 7
release:
needs: build
if: ${{ success() && github.event.pull_request.merged == true }}
runs-on: powershell-linux
outputs:
version: ${{ steps.meta.outputs.version }}
tag: ${{ steps.meta.outputs.tag }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Verify host prerequisites (pwsh)
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
if (-not (Get-Command pwsh -ErrorAction SilentlyContinue)) {
throw "Host runner is missing required tool: pwsh. Provision it on the runner host."
}
Write-Host ("pwsh: " + (pwsh -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'))
- name: Download module artifact
uses: christopherhx/gitea-download-artifact@v4
with:
name: PSInfisicalAPI-module
path: Module/PSInfisicalAPI
- name: Resolve module version and tag
id: meta
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$manifestPath = Join-Path $PWD 'Module/PSInfisicalAPI/PSInfisicalAPI.psd1'
$manifest = Test-ModuleManifest -Path $manifestPath
$version = $manifest.Version.ToString()
$tag = $version
Write-Host "Module version: $version"
Write-Host "Release tag: $tag"
"version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
"tag=$tag" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Package module as release asset
shell: pwsh
env:
VERSION: ${{ steps.meta.outputs.version }}
run: |
$ErrorActionPreference = 'Stop'
$zipPath = Join-Path $PWD "PSInfisicalAPI-$($env:VERSION).zip"
if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
Compress-Archive -Path 'Module/PSInfisicalAPI/*' -DestinationPath $zipPath -Force
Write-Host "Created: $zipPath ($([math]::Round((Get-Item $zipPath).Length / 1KB, 1)) KB)"
- name: Create Gitea release
shell: pwsh
env:
GITEA_TOKEN: ${{ github.token }}
API_URL: ${{ github.api_url }}
REPO: ${{ github.repository }}
TAG: ${{ steps.meta.outputs.tag }}
VERSION: ${{ steps.meta.outputs.version }}
COMMIT_SHA: ${{ github.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
SERVER_URL: ${{ github.server_url }}
RUN_ID: ${{ github.run_id }}
run: |
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
trap { Write-Host "==> RELEASE STEP FAILED: $($_ | Out-String)"; Write-Host ($_.ScriptStackTrace); exit 1 }
Write-Host "==> [1/8] Validating inputs"
Write-Host " TAG=$($env:TAG)"
Write-Host " VERSION=$($env:VERSION)"
Write-Host " REPO=$($env:REPO)"
Write-Host " API_URL=$($env:API_URL)"
Write-Host " SERVER_URL=$($env:SERVER_URL)"
Write-Host " PR_NUMBER=$($env:PR_NUMBER)"
Write-Host " RUN_ID=$($env:RUN_ID)"
if ([string]::IsNullOrWhiteSpace($env:GITEA_TOKEN)) { throw "github.token is empty." }
if ([string]::IsNullOrWhiteSpace($env:TAG)) { throw "TAG is empty." }
if ([string]::IsNullOrWhiteSpace($env:VERSION)) { throw "VERSION is empty." }
if ([string]::IsNullOrWhiteSpace($env:API_URL)) { throw "API_URL is empty." }
if ([string]::IsNullOrWhiteSpace($env:REPO)) { throw "REPO is empty." }
if ([string]::IsNullOrWhiteSpace($env:COMMIT_SHA)) { throw "COMMIT_SHA is empty." }
Write-Host "==> [2/8] Deriving metadata"
$shortSha = $env:COMMIT_SHA.Substring(0, [Math]::Min(12, $env:COMMIT_SHA.Length))
$buildUtc = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
$runUrl = "$($env:SERVER_URL)/$($env:REPO)/actions/runs/$($env:RUN_ID)"
$prUrl = "$($env:SERVER_URL)/$($env:REPO)/pulls/$($env:PR_NUMBER)"
Write-Host " shortSha=$shortSha"
Write-Host "==> [3/8] Extracting CHANGELOG section"
$changelogSection = ''
if (Test-Path 'CHANGELOG.md') {
$lines = [System.IO.File]::ReadAllLines('CHANGELOG.md')
$start = -1; $end = $lines.Length
for ($i = 0; $i -lt $lines.Length; $i++) {
if ($lines[$i] -match "^##\s+$([regex]::Escape($env:VERSION))\s*$") { $start = $i + 1; continue }
if ($start -ge 0 -and $lines[$i] -match '^##\s+') { $end = $i; break }
}
if ($start -ge 0) {
$changelogSection = ($lines[$start..($end - 1)] -join "`n").Trim()
}
}
Write-Host " CHANGELOG section length: $($changelogSection.Length) chars"
Write-Host "==> [4/8] Building release body"
$changelogText = if ($changelogSection) { $changelogSection } else { '_No CHANGELOG section found for this version._' }
$sb = New-Object System.Text.StringBuilder
[void]$sb.AppendLine("**PSInfisicalAPI $($env:VERSION)**")
[void]$sb.AppendLine('')
[void]$sb.AppendLine('| Field | Value |')
[void]$sb.AppendLine('| --- | --- |')
[void]$sb.AppendLine("| Version | ``$($env:VERSION)`` |")
[void]$sb.AppendLine("| Tag | ``$($env:TAG)`` |")
[void]$sb.AppendLine("| Commit | [``$shortSha``]($($env:SERVER_URL)/$($env:REPO)/commit/$($env:COMMIT_SHA)) |")
[void]$sb.AppendLine("| Built (UTC) | $buildUtc |")
[void]$sb.AppendLine("| Merged PR | [#$($env:PR_NUMBER) $($env:PR_TITLE)]($prUrl) by @$($env:PR_AUTHOR) |")
[void]$sb.AppendLine("| Workflow run | [$($env:RUN_ID)]($runUrl) |")
[void]$sb.AppendLine('')
[void]$sb.AppendLine('## Changes')
[void]$sb.AppendLine($changelogText)
[void]$sb.AppendLine('')
[void]$sb.AppendLine('## Install')
[void]$sb.AppendLine('```powershell')
[void]$sb.AppendLine("Install-Module -Name PSInfisicalAPI -RequiredVersion $($env:VERSION) -Scope CurrentUser")
[void]$sb.AppendLine('```')
$body = $sb.ToString()
Write-Host " body length: $($body.Length) chars"
$headers = @{
Authorization = "token $($env:GITEA_TOKEN)"
Accept = 'application/json'
}
$createUri = "$($env:API_URL)/repos/$($env:REPO)/releases"
Write-Host "==> [5/8] Checking for existing release tag: $createUri/tags/$($env:TAG)"
$existing = $null
try {
$existing = Invoke-RestMethod -Method Get -Headers $headers `
-Uri "$createUri/tags/$($env:TAG)" -ErrorAction Stop
} catch {
$status = $null
try { $status = $_.Exception.Response.StatusCode.value__ } catch { }
if ($status -ne 404) {
Write-Host " Lookup failed (status=$status): $($_.Exception.Message)"
throw
}
Write-Host " No existing release (404)."
}
if ($existing) {
Write-Host " Release tag '$($env:TAG)' already exists (id=$($existing.id)); skipping creation."
return
}
Write-Host "==> [6/8] Creating release"
$payload = @{
tag_name = $env:TAG
target_commitish = $env:COMMIT_SHA
name = "PSInfisicalAPI $($env:VERSION)"
body = $body
draft = $false
prerelease = $false
} | ConvertTo-Json -Depth 4
Write-Host " payload bytes: $([System.Text.Encoding]::UTF8.GetByteCount($payload))"
$release = Invoke-RestMethod -Method Post -Uri $createUri -Headers $headers `
-ContentType 'application/json' -Body $payload
Write-Host " Created release id=$($release.id) at $($release.html_url)"
Write-Host "==> [7/8] Locating release asset"
$assetPath = Join-Path $PWD "PSInfisicalAPI-$($env:VERSION).zip"
if (-not (Test-Path $assetPath)) { throw "Release asset not found at: $assetPath" }
$fileBytes = [System.IO.File]::ReadAllBytes($assetPath)
Write-Host " Asset: $assetPath ($([math]::Round($fileBytes.Length / 1KB, 1)) KB)"
Write-Host "==> [8/8] Uploading asset"
$uploadUri = "$createUri/$($release.id)/assets?name=PSInfisicalAPI-$($env:VERSION).zip"
Invoke-RestMethod -Method Post -Uri $uploadUri -Headers $headers `
-ContentType 'application/zip' -Body $fileBytes | Out-Null
Write-Host "==> Done: uploaded PSInfisicalAPI-$($env:VERSION).zip"
publish:
needs: release
if: ${{ success() && github.event.pull_request.merged == true }}
runs-on: powershell-linux
steps:
- name: Verify host prerequisites (pwsh)
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
if (-not (Get-Command pwsh -ErrorAction SilentlyContinue)) {
throw "Host runner is missing required tool: pwsh. Provision it on the runner host."
}
Write-Host ("pwsh: " + (pwsh -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'))
- name: Download module artifact
uses: christopherhx/gitea-download-artifact@v4
with:
name: PSInfisicalAPI-module
path: Module/PSInfisicalAPI
- name: Bootstrap Microsoft.PowerShell.PSResourceGet
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
if (-not (Get-Module -ListAvailable -Name Microsoft.PowerShell.PSResourceGet)) {
throw "Microsoft.PowerShell.PSResourceGet is not installed on the host runner. Provision it (Install-Module Microsoft.PowerShell.PSResourceGet -Scope AllUsers)."
}
Import-Module Microsoft.PowerShell.PSResourceGet -ErrorAction Stop
Set-PSResourceRepository -Name PSGallery -Trusted -ApiVersion v2
Get-PSResourceRepository -Name PSGallery | Format-Table Name,Uri,Trusted,ApiVersion
- name: Verify PowerShell Gallery API key is configured
shell: pwsh
env:
PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }}
run: |
if ([string]::IsNullOrWhiteSpace($env:PSGALLERY_API_KEY)) {
throw "Repository secret 'PSGALLERY_API_KEY' is not configured."
}
- name: Re-validate downloaded module manifest
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$manifestPath = Join-Path $PWD 'Module/PSInfisicalAPI/PSInfisicalAPI.psd1'
$manifest = Test-ModuleManifest -Path $manifestPath
Write-Host "Manifest OK: $($manifest.Name) $($manifest.Version)"
- name: Publish to PowerShell Gallery
shell: pwsh
env:
PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }}
run: |
$ErrorActionPreference = 'Stop'
$moduleDir = Join-Path $PWD 'Module/PSInfisicalAPI'
Write-Host "Publishing module from: $moduleDir"
Publish-PSResource `
-Path $moduleDir `
-Repository PSGallery `
-ApiKey $env:PSGALLERY_API_KEY `
-Verbose