Publish Netbird to Winget #12
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: Publish Netbird to Winget | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Netbird version to publish (e.g., 0.59.9)' | |
| required: true | |
| type: string | |
| jobs: | |
| publish-winget: | |
| runs-on: windows-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up WingetCreate | |
| shell: pwsh | |
| run: Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe | |
| - name: Determine version | |
| id: version | |
| shell: pwsh | |
| run: | | |
| if ("${{ github.event_name }}" -eq "workflow_dispatch") { | |
| $version = "${{ github.event.inputs.version }}" | |
| } else { | |
| $version = "${{ github.event.release.tag_name }}".TrimStart('v') | |
| } | |
| "VERSION=$version" >> $env:GITHUB_ENV | |
| "version=$version" >> $env:GITHUB_OUTPUT | |
| - name: Fetch release info & collect installer URLs | |
| id: release_info | |
| shell: pwsh | |
| env: | |
| GH_API: https://api.github.com | |
| WINGET_PAT: ${{ secrets.WINGET_PAT }} | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| $headers = @{ | |
| Authorization = "Bearer $env:WINGET_PAT" | |
| "User-Agent" = "netbird-winget-action" | |
| Accept = "application/vnd.github+json" | |
| "X-GitHub-Api-Version" = "2022-11-28" | |
| } | |
| if ("${{ github.event_name }}" -eq "workflow_dispatch") { | |
| $release = Invoke-RestMethod -Headers $headers -Uri "$env:GH_API/repos/netbirdio/netbird/releases/tags/v${{ env.VERSION }}" | |
| } else { | |
| $release = Invoke-RestMethod -Headers $headers -Uri "${{ github.event.release.url }}" | |
| } | |
| $assets = $release.assets | |
| if (-not $assets) { throw "Release v${{ env.VERSION }} has no assets." } | |
| $msi_x64 = $assets | Where-Object { $_.name -like "*windows_amd64.msi" } | Select-Object -ExpandProperty browser_download_url -First 1 | |
| $exe_x64 = $assets | Where-Object { $_.name -like "*windows_amd64.exe" } | Select-Object -ExpandProperty browser_download_url -First 1 | |
| $msi_arm = $assets | Where-Object { $_.name -like "*windows_arm64.msi" } | Select-Object -ExpandProperty browser_download_url -First 1 | |
| $exe_arm = $assets | Where-Object { $_.name -like "*windows_arm64.exe" } | Select-Object -ExpandProperty browser_download_url -First 1 | |
| $any = @($msi_x64, $exe_x64, $msi_arm, $exe_arm) | Where-Object { $_ } | |
| if (-not $any) { throw "No Windows installers (MSI/EXE) found in v${{ env.VERSION }}." } | |
| $qualified = @() | |
| if ($msi_x64) { $qualified += "$msi_x64|x64" } | |
| if ($exe_x64) { $qualified += "$exe_x64|x64" } | |
| if ($msi_arm) { $qualified += "$msi_arm|arm64" } | |
| if ($exe_arm) { $qualified += "$exe_arm|arm64" } | |
| Set-Content -Path installer_urls.txt -Value ($qualified -join "`n") | |
| "installer_urls_file=installer_urls.txt" >> $env:GITHUB_OUTPUT | |
| "installer_url=$($any[0])" >> $env:GITHUB_OUTPUT | |
| # ---------- Fork under PAT user, then sync ---------- | |
| - name: Install GitHub CLI | |
| shell: pwsh | |
| run: choco install gh -y | |
| - name: Resolve PAT user (who will own the fork) | |
| id: whoami | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.WINGET_PAT }} # <-- set for THIS step | |
| run: | | |
| $login = gh api /user --jq .login | |
| if (-not $login) { throw "Could not determine PAT owner via /user" } | |
| "login=$login" >> $env:GITHUB_OUTPUT | |
| Write-Host "PAT user: $login" | |
| - name: Ensure fork exists under PAT user (create if missing) | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.WINGET_PAT }} # <-- set for THIS step | |
| run: | | |
| $login = "${{ steps.whoami.outputs.login }}" | |
| $fork = "$login/winget-pkgs" | |
| Write-Host "Checking fork $fork ..." | |
| $exists = $false | |
| try { | |
| gh repo view $fork --json name -q .name | Out-Null | |
| if ($LASTEXITCODE -eq 0) { $exists = $true } | |
| } catch { $exists = $false } | |
| if (-not $exists) { | |
| Write-Host "Fork not found; creating under $login ..." | |
| gh api --method POST -H "Accept: application/vnd.github+json" /repos/microsoft/winget-pkgs/forks -F default_branch_only=true | |
| # forking is async: poll until it appears | |
| $deadline = (Get-Date).AddSeconds(90) | |
| do { | |
| Start-Sleep -Seconds 3 | |
| try { | |
| gh repo view $fork --json defaultBranchRef -q .defaultBranchRef.name | Out-Null | |
| if ($LASTEXITCODE -eq 0) { $exists = $true } | |
| } catch { $exists = $false } | |
| } until ($exists -or (Get-Date) -gt $deadline) | |
| if (-not $exists) { throw "Fork creation did not complete in time for $fork." } | |
| } else { | |
| Write-Host "Fork already exists." | |
| } | |
| - name: Sync fork with upstream | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.WINGET_PAT }} # <-- set for THIS step | |
| run: | | |
| $login = "${{ steps.whoami.outputs.login }}" | |
| gh repo sync "$login/winget-pkgs" -b master --force | |
| # ---------- Submit with wingetcreate ---------- | |
| - name: Update Winget Manifest | |
| shell: pwsh | |
| env: | |
| WINGET_PAT: ${{ secrets.WINGET_PAT }} | |
| run: | | |
| Write-Host "Updating Winget manifest for Netbird ${{ env.VERSION }}" | |
| $urls = Get-Content "${{ steps.release_info.outputs.installer_urls_file }}" | Where-Object { $_.Trim() } | |
| if (-not $urls) { throw "No installer URLs to submit." } | |
| .\wingetcreate.exe update Netbird.Netbird ` | |
| --version $env:VERSION ` | |
| --display-version $env:VERSION ` | |
| --urls $urls ` | |
| --submit ` | |
| --token $env:WINGET_PAT | |
| - name: Verify submission | |
| shell: pwsh | |
| run: | | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Host "✅ Successfully submitted Winget manifest update for Netbird ${{ env.VERSION }}" | |
| } else { | |
| Write-Error "❌ Failed to submit Winget manifest update" | |
| exit 1 | |
| } |