build package #370
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: build package | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| build-branch: | |
| description: "Branch to build from (must be 'master' or 'release/*')" | |
| type: string | |
| default: 'master' | |
| required: true | |
| build-mac: | |
| description: 'AvaloniaTheme.MacOS' | |
| type: boolean | |
| default: false | |
| build-devexpress: | |
| description: 'AvaloniaTheme.DevExpress' | |
| type: boolean | |
| default: false | |
| build-linux: | |
| description: 'AvaloniaTheme.Linux' | |
| type: boolean | |
| default: false | |
| build-controls: | |
| description: 'AvaloniaControls' | |
| type: boolean | |
| default: false | |
| version: | |
| description: 'release version' | |
| default: "latest" | |
| required: true | |
| prerelease-suffix: | |
| description: 'Prerelease suffix (e.g., avalonia12.1). Leave empty for stable release.' | |
| default: "" | |
| required: false | |
| publish-mode: | |
| description: 'Publishing Mode' | |
| type: choice | |
| options: | |
| - 'Skip Publishing' | |
| - 'Dry Run' | |
| - 'Publish' | |
| default: 'Skip Publishing' | |
| schedule: | |
| - cron: '32 5 * * 1' # 05:32 AM UTC every Monday | |
| jobs: | |
| preflight: | |
| name: Preflight | |
| runs-on: ubuntu-22.04 | |
| outputs: | |
| package-env: ${{ steps.info.outputs.package-env }} | |
| package-version: ${{ steps.info.outputs.package-version }} | |
| package-list: ${{ steps.info.outputs.package-list }} | |
| skip-publish: ${{ steps.info.outputs.skip-publish }} | |
| dry-run: ${{ steps.info.outputs.dry-run }} | |
| build-branch: ${{ steps.info.outputs.build-branch }} | |
| steps: | |
| - name: Package information | |
| id: info | |
| shell: pwsh | |
| run: | | |
| $IsScheduledJob = ('${{ github.event_name }}' -eq 'schedule') | |
| # Branch to build from. Scheduled jobs have no inputs, so default to 'master'. | |
| $BuildBranch = '${{ inputs.build-branch }}' | |
| if ([string]::IsNullOrEmpty($BuildBranch)) { $BuildBranch = 'master' } | |
| if ($BuildBranch -ne 'master' -And $BuildBranch -NotMatch '^release/.+') { | |
| throw "invalid build branch: $BuildBranch, must be 'master' or 'release/*'" | |
| } | |
| $PublishMode = '${{ inputs.publish-mode }}' | |
| if ([string]::IsNullOrEmpty($PublishMode)) { $PublishMode = 'Skip Publishing' } | |
| $SkipPublish = ($PublishMode -eq 'Skip Publishing') | |
| $DryRun = ($PublishMode -eq 'Dry Run') | |
| # Manual dispatch can publish from any branch selected in the Actions UI ref picker. | |
| # Scheduled jobs always skip publishing. | |
| $PackageEnv = if ((-Not $SkipPublish) -And (-Not $IsScheduledJob)) { | |
| "publish-prod" | |
| } else { | |
| "publish-test" | |
| } | |
| if ($IsScheduledJob) { | |
| $SkipPublish = $true # scheduled jobs always skip publishing | |
| $DryRun = $false | |
| } | |
| $PackageVersion = '${{ inputs.version }}' | |
| if ([string]::IsNullOrEmpty($PackageVersion) -or $PackageVersion -eq 'latest') { | |
| $PackageVersion = (Get-Date -Format "yyyy.MM.dd") + ".0" | |
| } | |
| if ($PackageVersion -NotMatch '^\d+\.\d+\.\d+\.\d+$') { | |
| throw "invalid version format: $PackageVersion, expected: 1.2.3.4" | |
| } | |
| $PrereleaseSuffix = '${{ inputs.prerelease-suffix }}' | |
| if (-Not [string]::IsNullOrEmpty($PrereleaseSuffix)) { | |
| if ($PrereleaseSuffix -NotMatch '^[a-zA-Z0-9][a-zA-Z0-9.\-]*$') { | |
| throw "invalid prerelease suffix: $PrereleaseSuffix, must be alphanumeric (dots/hyphens allowed)" | |
| } | |
| $PackageVersion = "$PackageVersion-$PrereleaseSuffix" | |
| } | |
| $Packages = @() | |
| if ('${{ inputs.build-mac }}' -eq 'true') { | |
| $Packages += "AvaloniaTheme.MacOS" | |
| } | |
| if ('${{ inputs.build-devexpress }}' -eq 'true') { | |
| $Packages += "AvaloniaTheme.DevExpress" | |
| } | |
| if ('${{ inputs.build-linux }}' -eq 'true') { | |
| $Packages += "AvaloniaTheme.Linux" | |
| } | |
| if ('${{ inputs.build-controls }}' -eq 'true') { | |
| $Packages += "AvaloniaControls" | |
| } | |
| if ($Packages.Count -eq 0) { | |
| $Packages = @( | |
| "AvaloniaTheme.MacOS", | |
| "AvaloniaTheme.DevExpress", | |
| "AvaloniaTheme.Linux", | |
| "AvaloniaControls" | |
| ) | |
| } | |
| $PackageList = $Packages -join "," | |
| echo "package-env=$PackageEnv" >> $Env:GITHUB_OUTPUT | |
| echo "package-version=$PackageVersion" >> $Env:GITHUB_OUTPUT | |
| echo "package-list=$PackageList" >> $Env:GITHUB_OUTPUT | |
| echo "skip-publish=$($SkipPublish.ToString().ToLower())" >> $Env:GITHUB_OUTPUT | |
| echo "dry-run=$($DryRun.ToString().ToLower())" >> $Env:GITHUB_OUTPUT | |
| echo "build-branch=$BuildBranch" >> $Env:GITHUB_OUTPUT | |
| echo "::notice::Branch: $BuildBranch" | |
| echo "::notice::Building Packages: $PackageList" | |
| echo "::notice::Version: $PackageVersion" | |
| echo "::notice::SkipPublish: $SkipPublish" | |
| echo "::notice::DryRun: $DryRun" | |
| build-nuget: | |
| name: Build nuget | |
| runs-on: windows-2022 | |
| needs: [preflight] | |
| environment: ${{ needs.preflight.outputs.package-env }} | |
| steps: | |
| - name: Check out ${{ github.repository }} | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.preflight.outputs.build-branch }} | |
| - name: Configure runner | |
| shell: pwsh | |
| run: | | |
| New-Item .\package -ItemType Directory -ErrorAction SilentlyContinue | Out-Null | |
| - name: Install code signing tools | |
| run: | | |
| dotnet tool install --global AzureSignTool | |
| # trust test code signing CA | |
| $TestCertsUrl = "https://raw.githubusercontent.com/Devolutions/devolutions-authenticode/master/data/certs" | |
| Invoke-WebRequest -Uri "$TestCertsUrl/authenticode-test-ca.crt" -OutFile ".\authenticode-test-ca.crt" | |
| Import-Certificate -FilePath ".\authenticode-test-ca.crt" -CertStoreLocation "cert:\LocalMachine\Root" | |
| Remove-Item ".\authenticode-test-ca.crt" -ErrorAction SilentlyContinue | Out-Null | |
| - name: Set package version | |
| shell: pwsh | |
| run: | | |
| $PackageVersion = '${{ needs.preflight.outputs.package-version }}' | |
| $PackageList = '${{ needs.preflight.outputs.package-list }}' | |
| $ProjectDirs = @($PackageList -Split ',' | ForEach-Object { ".\src\Devolutions.$_" }) | |
| foreach ($ProjectDir in $ProjectDirs) { | |
| $csprojPath = (Get-Item "$ProjectDir\*.csproj" | Select-Object -First 1).FullName | |
| $csprojContent = Get-Content $csprojPath -Raw | |
| $csprojContent = $csprojContent -Replace '(<Version>).*?(</Version>)', "<Version>$PackageVersion</Version>" | |
| Set-Content -Path $csprojPath -Value $csprojContent -Encoding UTF8 | |
| } | |
| - name: Build nuget packages | |
| shell: pwsh | |
| env: | |
| AVALONIA_LICENSE_KEY: ${{ secrets.AVALONIA_LICENSE_KEY }} | |
| run: | | |
| $PackageList = '${{ needs.preflight.outputs.package-list }}' | |
| $ProjectDirs = @($PackageList -Split ',' | ForEach-Object { ".\src\Devolutions.$_" }) | |
| foreach ($ProjectDir in $ProjectDirs) { | |
| & dotnet pack $ProjectDir -o package | |
| } | |
| - name: Code sign nuget contents | |
| shell: pwsh | |
| run: | | |
| $NugetPackages = Get-Item ./package/*.nupkg | |
| foreach ($Package in $NugetPackages) { | |
| $NugetBaseName = $Package.BaseName | |
| $PackedFile = $Package.FullName | |
| $UnpackedDir = "./package/${NugetBaseName}" | |
| $OutputDirectory = $Package.Directory.FullName | |
| Expand-Archive -Path $PackedFile -Destination $UnpackedDir -Force | |
| $Params = @('sign', | |
| '-kvt', '${{ secrets.AZURE_TENANT_ID }}', | |
| '-kvu', '${{ secrets.CODE_SIGNING_KEYVAULT_URL }}', | |
| '-kvi', '${{ secrets.CODE_SIGNING_CLIENT_ID }}', | |
| '-kvs', '${{ secrets.CODE_SIGNING_CLIENT_SECRET }}', | |
| '-kvc', '${{ secrets.CODE_SIGNING_CERTIFICATE_NAME }}', | |
| '-tr', '${{ vars.CODE_SIGNING_TIMESTAMP_SERVER }}', | |
| '-v') | |
| Get-ChildItem "$UnpackedDir\lib" -Include @("*.dll") -Recurse | ForEach-Object { | |
| AzureSignTool @Params $_.FullName | |
| } | |
| Remove-Item $PackedFile -ErrorAction SilentlyContinue | Out-Null | |
| Compress-Archive -Path "$UnpackedDir\*" -Destination $PackedFile -CompressionLevel Optimal | |
| } | |
| - name: Upload nuget packages | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: package-nupkg | |
| path: package/*.nupkg | |
| publish: | |
| name: Publish packages | |
| runs-on: ubuntu-22.04 | |
| needs: [preflight, build-nuget] | |
| environment: ${{ needs.preflight.outputs.package-env }} | |
| if: ${{ fromJSON(needs.preflight.outputs.skip-publish) == false }} | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download nuget package | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: package-nupkg | |
| path: package | |
| - name: NuGet login (OIDC) | |
| uses: NuGet/login@v1 | |
| id: nuget-login | |
| with: | |
| user: ${{ secrets.NUGET_BOT_USERNAME }} | |
| - name: Publish to nuget.org | |
| shell: pwsh | |
| run: | | |
| $DryRun = [System.Boolean]::Parse('${{ needs.preflight.outputs.dry-run }}') | |
| $NugetPackages = (Get-Item ./package/*.nupkg) | Resolve-Path -Relative | |
| foreach ($NugetPackage in $NugetPackages) { | |
| $PushArgs = @( | |
| 'nuget', 'push', "$NugetPackage", | |
| '--api-key', '${{ steps.nuget-login.outputs.NUGET_API_KEY }}', | |
| '--source', 'https://api.nuget.org/v3/index.json', | |
| '--skip-duplicate', '--no-symbols' | |
| ) | |
| Write-Host "dotnet $($PushArgs -Join ' ')" | |
| if ($DryRun) { | |
| Write-Host "Dry Run: skipping nuget.org publishing!" | |
| } else { | |
| & 'dotnet' $PushArgs | |
| } | |
| } |