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 and test module shell: pwsh run: ./build.ps1 -RunTests - 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' if ([string]::IsNullOrWhiteSpace($env:GITEA_TOKEN)) { throw "github.token is not available; cannot call the Gitea release API." } $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)" $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() } } $body = @" **PSInfisicalAPI $($env:VERSION)** | Field | Value | | --- | --- | | Version | ``$($env:VERSION)`` | | Tag | ``$($env:TAG)`` | | Commit | [``$shortSha``]($($env:SERVER_URL)/$($env:REPO)/commit/$($env:COMMIT_SHA)) | | Built (UTC) | $buildUtc | | Merged PR | [#$($env:PR_NUMBER) $($env:PR_TITLE)]($prUrl) by @$($env:PR_AUTHOR) | | Workflow run | [$($env:RUN_ID)]($runUrl) | ## Changes $(if ($changelogSection) { $changelogSection } else { '_No CHANGELOG section found for this version._' }) ## Install ``````powershell Install-Module -Name PSInfisicalAPI -RequiredVersion $($env:VERSION) -Scope CurrentUser `````` "@ $headers = @{ Authorization = "token $($env:GITEA_TOKEN)" Accept = 'application/json' } $createUri = "$($env:API_URL)/repos/$($env:REPO)/releases" $existing = $null try { $existing = Invoke-RestMethod -Method Get -Headers $headers ` -Uri "$createUri/tags/$($env:TAG)" -ErrorAction Stop } catch { if ($_.Exception.Response.StatusCode.value__ -ne 404) { throw } } if ($existing) { Write-Host "Release tag '$($env:TAG)' already exists (id=$($existing.id)); skipping release creation." return } $payload = @{ tag_name = $env:TAG target_commitish = $env:COMMIT_SHA name = "PSInfisicalAPI $($env:VERSION)" body = $body draft = $false prerelease = $false } | ConvertTo-Json -Depth 4 $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)" $assetPath = Join-Path $PWD "PSInfisicalAPI-$($env:VERSION).zip" $uploadUri = "$createUri/$($release.id)/assets?name=PSInfisicalAPI-$($env:VERSION).zip" $fileBytes = [System.IO.File]::ReadAllBytes($assetPath) Invoke-RestMethod -Method Post -Uri $uploadUri -Headers $headers ` -ContentType 'application/zip' -Body $fileBytes | Out-Null Write-Host "Uploaded asset: 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