Repository navigation
Release #6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version_bump: | |
| description: "Version bump type (use 'none' to re-publish an existing release)" | |
| required: true | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| - none | |
| default: patch | |
| publish_to_gallery: | |
| description: "Publish to PowerShell Gallery" | |
| required: true | |
| type: boolean | |
| default: true | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| environment: CopilotCmdletsEnv | |
| env: | |
| # Supported runtimes — used by both the build and staging steps. | |
| # Keep in sync with SupportedRids in src/ModuleState.cs and $runtimes in build.ps1. | |
| RUNTIMES: 'win-x64 osx-arm64' | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.0.x" | |
| - name: Determine version | |
| id: version | |
| shell: pwsh | |
| run: | | |
| # Find latest release tag (used to compute next version or identify the version to re-publish) | |
| $tag = git describe --tags --abbrev=0 2>$null | |
| $hasTag = ($LASTEXITCODE -eq 0 -and $tag) | |
| if (-not $hasTag) { | |
| $major = 0; $minor = 0; $patch = 0 | |
| } elseif ($tag -match '^v?(\d+)\.(\d+)\.(\d+)$') { | |
| $major = [int]$Matches[1]; $minor = [int]$Matches[2]; $patch = [int]$Matches[3] | |
| } else { | |
| Write-Error "Could not parse version from tag '$tag'" | |
| exit 1 | |
| } | |
| $bump = '${{ inputs.version_bump }}' | |
| if ($bump -eq 'none') { | |
| if (-not $hasTag) { | |
| Write-Error "No existing release tag found — cannot re-publish" | |
| exit 1 | |
| } | |
| $version = "$major.$minor.$patch" | |
| Write-Host "Re-publishing existing version: $version (tag: $tag)" | |
| "version=$version" >> $env:GITHUB_OUTPUT | |
| "tag=$tag" >> $env:GITHUB_OUTPUT | |
| "is_new=false" >> $env:GITHUB_OUTPUT | |
| } else { | |
| switch ($bump) { | |
| 'major' { $newVersion = "$($major + 1).0.0" } | |
| 'minor' { $newVersion = "$major.$($minor + 1).0" } | |
| default { $newVersion = "$major.$minor.$($patch + 1)" } | |
| } | |
| Write-Host "Version: $major.$minor.$patch -> $newVersion" | |
| "version=$newVersion" >> $env:GITHUB_OUTPUT | |
| "tag=v$newVersion" >> $env:GITHUB_OUTPUT | |
| "is_new=true" >> $env:GITHUB_OUTPUT | |
| } | |
| - name: Checkout release tag | |
| if: steps.version.outputs.is_new == 'false' | |
| env: | |
| RELEASE_TAG: ${{ steps.version.outputs.tag }} | |
| run: git checkout "$RELEASE_TAG" | |
| - name: Update module manifest version | |
| if: steps.version.outputs.is_new == 'true' | |
| shell: pwsh | |
| run: | | |
| $manifest = Join-Path $env:GITHUB_WORKSPACE 'CopilotCmdlets.psd1' | |
| $content = Get-Content $manifest -Raw | |
| $updated = $content -replace "ModuleVersion\s*=\s*'\d+\.\d+\.\d+'", "ModuleVersion = '${{ steps.version.outputs.version }}'" | |
| Set-Content -Path $manifest -Value $updated -NoNewline | |
| Write-Host "Updated manifest to ${{ steps.version.outputs.version }}" | |
| - name: Run unit tests | |
| run: dotnet test tests/CopilotCmdlets.Tests.csproj --filter "Category=Unit" | |
| - name: Build all platforms | |
| shell: pwsh | |
| run: | | |
| $project = Join-Path $env:GITHUB_WORKSPACE 'src/CopilotCmdlets.csproj' | |
| $outDir = Join-Path $env:GITHUB_WORKSPACE 'out' | |
| $runtimes = $env:RUNTIMES -split ' ' | |
| foreach ($rid in $runtimes) { | |
| $ridOut = Join-Path $outDir $rid | |
| Write-Host "Publishing $rid -> $ridOut" | |
| dotnet publish $project -c Release -r $rid --self-contained false -o $ridOut | |
| if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed for $rid" } | |
| } | |
| # RID-neutral gallery build | |
| $galleryDir = Join-Path $outDir 'gallery' | |
| Write-Host "Publishing RID-neutral gallery build -> $galleryDir" | |
| dotnet publish $project -c Release -o $galleryDir | |
| if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed for gallery build" } | |
| - name: Stage module files and create release zips | |
| id: artifacts | |
| shell: pwsh | |
| run: | | |
| $root = $env:GITHUB_WORKSPACE | |
| $outDir = Join-Path $root 'out' | |
| $manifest = Join-Path $root 'CopilotCmdlets.psd1' | |
| $runtimes = $env:RUNTIMES -split ' ' | |
| $version = '${{ steps.version.outputs.version }}' | |
| $galleryDir = Join-Path $outDir 'gallery' | |
| # Stage per-RID builds and create zips | |
| $zipPaths = @() | |
| foreach ($rid in $runtimes) { | |
| $ridOut = Join-Path $outDir $rid | |
| $ridStage = Join-Path $ridOut 'CopilotCmdlets' | |
| if (Test-Path $ridStage) { Remove-Item $ridStage -Recurse -Force } | |
| New-Item -ItemType Directory -Force -Path $ridStage | Out-Null | |
| Get-ChildItem -Path $ridOut -Force | | |
| Where-Object { $_.FullName -ne $ridStage } | | |
| Copy-Item -Destination $ridStage -Recurse -Force | |
| Copy-Item $manifest -Destination $ridStage -Force | |
| $zipPath = Join-Path $root "CopilotCmdlets-$version-$rid.zip" | |
| Compress-Archive -Path (Join-Path $ridStage '*') -DestinationPath $zipPath -Force | |
| Write-Host "Created $zipPath" | |
| $zipPaths += $zipPath | |
| } | |
| # Stage gallery build | |
| $galleryStage = Join-Path $galleryDir 'CopilotCmdlets' | |
| if (Test-Path $galleryStage) { Remove-Item $galleryStage -Recurse -Force } | |
| New-Item -ItemType Directory -Force -Path $galleryStage | Out-Null | |
| Get-ChildItem -Path $galleryDir -Force | | |
| Where-Object { $_.FullName -ne $galleryStage } | | |
| Copy-Item -Destination $galleryStage -Recurse -Force | |
| # Merge per-RID native payloads into gallery stage so Install-PSResource | |
| # ships the Copilot CLI for every supported platform in a single package. | |
| $galleryRuntimes = Join-Path $galleryStage 'runtimes' | |
| New-Item -ItemType Directory -Force -Path $galleryRuntimes | Out-Null | |
| foreach ($rid in $runtimes) { | |
| $ridRuntimes = Join-Path $outDir $rid 'runtimes' | |
| if (Test-Path $ridRuntimes) { | |
| Get-ChildItem -Path $ridRuntimes -Force | | |
| Copy-Item -Destination $galleryRuntimes -Recurse -Force | |
| } | |
| } | |
| Copy-Item $manifest -Destination $galleryStage -Force | |
| # Ensure native CLI binaries have execute permission. | |
| # NuGet/zip packaging strips the Unix execute bit; without this, | |
| # Connect-Copilot fails with 'Permission denied' on macOS/Linux. | |
| # Skip Windows RIDs — .exe files don't need a Unix execute bit. | |
| foreach ($rid in ($runtimes | Where-Object { -not $_.StartsWith('win-') })) { | |
| $nativeDir = Join-Path $galleryRuntimes $rid 'native' | |
| if (Test-Path $nativeDir) { | |
| Get-ChildItem -Path $nativeDir -File | ForEach-Object { | |
| chmod +x $_.FullName | |
| } | |
| } | |
| } | |
| # Output zip paths (newline-separated) for the release step | |
| $joined = $zipPaths -join "`n" | |
| # Use a delimiter for multi-line output | |
| $delimiter = "EOF_$(Get-Random)" | |
| "zip_paths<<$delimiter" >> $env:GITHUB_OUTPUT | |
| $joined >> $env:GITHUB_OUTPUT | |
| $delimiter >> $env:GITHUB_OUTPUT | |
| - name: Commit version bump | |
| if: steps.version.outputs.is_new == 'true' | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add CopilotCmdlets.psd1 | |
| git diff --cached --quiet || git commit -m "Release ${{ steps.version.outputs.tag }}" | |
| if ! git tag --list "${{ steps.version.outputs.tag }}" | grep -q .; then | |
| git tag "${{ steps.version.outputs.tag }}" | |
| fi | |
| git push origin HEAD "${{ steps.version.outputs.tag }}" | |
| - name: Create GitHub Release | |
| if: steps.version.outputs.is_new == 'true' | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| $tag = '${{ steps.version.outputs.tag }}' | |
| $zipPaths = '${{ steps.artifacts.outputs.zip_paths }}' -split "`n" | Where-Object { $_ } | |
| Write-Host "Creating release $tag with assets:" | |
| $zipPaths | ForEach-Object { Write-Host " $_" } | |
| $existing = gh release view $tag --json tagName 2>$null | |
| if ($existing) { | |
| Write-Host "Release $tag already exists — uploading assets" | |
| gh release upload $tag @zipPaths --clobber | |
| } else { | |
| gh release create $tag @zipPaths --title $tag --notes "Release $tag" | |
| } | |
| - name: Publish to PowerShell Gallery | |
| if: inputs.publish_to_gallery | |
| shell: pwsh | |
| env: | |
| POWERSHELL_GALLERY_API_KEY: ${{ secrets.POWERSHELL_GALLERY_API_KEY }} | |
| run: | | |
| $galleryStage = Join-Path $env:GITHUB_WORKSPACE 'out/gallery/CopilotCmdlets' | |
| Write-Host "Publishing CopilotCmdlets ${{ steps.version.outputs.version }} to PowerShell Gallery" | |
| Publish-Module -Path $galleryStage -NuGetApiKey $env:POWERSHELL_GALLERY_API_KEY | |
| Write-Host "Successfully published to PowerShell Gallery" |