Skip to content

build package

build package #66

Workflow file for this run

name: build package
on:
workflow_dispatch:
inputs:
version:
description: 'release version'
default: "latest"
required: true
detours-git-commit:
description: 'Detours git commit'
default: '4b8c659'
required: true
sign-nuget:
description: 'Sign nuget package'
required: true
type: boolean
default: false
skip-publish:
description: 'Skip publishing'
required: true
type: boolean
default: false
dry-run:
description: 'Dry run (simulate)'
required: true
type: boolean
default: true
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 }}
detours-git-commit: ${{ steps.info.outputs.detours-git-commit }}
sign-nuget: ${{ steps.info.outputs.sign-nuget }}
skip-publish: ${{ steps.info.outputs.skip-publish }}
dry-run: ${{ steps.info.outputs.dry-run }}
steps:
- name: Package information
id: info
shell: pwsh
run: |
$IsMasterBranch = ('${{ github.ref_name }}' -eq 'master')
$IsScheduledJob = ('${{ github.event_name }}' -eq 'schedule')
if ('${{ github.event_name }}' -Eq 'schedule') {
}
try { $SignNuget = [System.Boolean]::Parse('${{ inputs.sign-nuget }}') } catch { $SignNuget = $false }
try { $SkipPublish = [System.Boolean]::Parse('${{ inputs.skip-publish }}') } catch { $SkipPublish = $false }
try { $DryRun = [System.Boolean]::Parse('${{ inputs.dry-run }}') } catch { $DryRun = $true }
$PackageEnv = if ($IsMasterBranch -And -Not $IsScheduledJob) {
"publish-prod"
} else {
"publish-test"
}
if (-Not $IsMasterBranch) {
$DryRun = $true # force dry run when not on master branch
}
if ($IsScheduledJob) {
$DryRun = $true # force dry run for scheduled runs
}
$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"
}
$DetoursGitCommit = '${{ inputs.detours-git-commit }}'
if ([string]::IsNullOrEmpty($DetoursGitCommit)) {
$DetoursGitCommit = '4b8c659'
}
echo "package-env=$PackageEnv" >> $Env:GITHUB_OUTPUT
echo "package-version=$PackageVersion" >> $Env:GITHUB_OUTPUT
echo "detours-git-commit=$DetoursGitCommit" >> $Env:GITHUB_OUTPUT
echo "sign-nuget=$($SignNuget.ToString().ToLower())" >> $Env:GITHUB_OUTPUT
echo "skip-publish=$($SkipPublish.ToString().ToLower())" >> $Env:GITHUB_OUTPUT
echo "dry-run=$($DryRun.ToString().ToLower())" >> $Env:GITHUB_OUTPUT
echo "::notice::Version: $PackageVersion"
echo "::notice::DryRun: $DryRun"
build-native:
name: Build native library
runs-on: windows-2022
needs: [preflight]
strategy:
fail-fast: false
matrix:
arch: [ x86, x64, arm64 ]
steps:
- name: Check out ${{ github.repository }}
uses: actions/checkout@v4
- name: Configure runner
shell: pwsh
run: |
Install-Module -Name VsDevShell -Force
New-Item .\package -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
New-Item .\symbols -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
- name: Set package version
shell: pwsh
run: |
$PackageVersion = '${{ needs.preflight.outputs.package-version }}'
$csprojPath = "dotnet\Devolutions.MsRdpEx\Devolutions.MsRdpEx.csproj"
$csprojContent = Get-Content $csprojPath -Raw
$csprojContent = $csprojContent -Replace '(<Version>).*?(</Version>)', "<Version>$PackageVersion</Version>"
Set-Content -Path $csprojPath -Value $csprojContent -Encoding UTF8
- name: Restore Detours Cache (${{matrix.arch}})
id: cache-detours
uses: actions/cache/restore@v4
with:
path: dependencies/detours
key: detours-${{ matrix.arch }}-${{ needs.preflight.outputs.detours-git-commit }}
- name: Build Detours (${{matrix.arch}})
if: steps.cache-detours.outputs.cache-hit != 'true'
shell: pwsh
run: |
Enter-VsDevShell ${{matrix.arch}}
$GitCommit = '${{ needs.preflight.outputs.detours-git-commit }}'
.\detours.ps1 -GitCommit $GitCommit
- name: Save Detours Cache (${{matrix.arch}})
if: steps.cache-detours.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: dependencies/detours
key: detours-${{ matrix.arch }}-${{ needs.preflight.outputs.detours-git-commit }}
- name: Build MsRdpEx (${{matrix.arch}})
shell: pwsh
run: |
$Arch = "${{matrix.arch}}"
$BuildDir = "build-$Arch"
$PackageVersion = '${{ needs.preflight.outputs.package-version }}'
$MsvcArch = @{"x86"="Win32";"x64"="x64";"arm64"="ARM64"}["${{matrix.arch}}"]
cmake -G "Visual Studio 17 2022" -A $MsvcArch -DWITH_DOTNET=OFF -B $BuildDir
cmake --build $BuildDir --config Release
New-Item -ItemType Directory -Path "dependencies/MsRdpEx/$Arch" | Out-Null
@('MsRdpEx.dll','MsRdpEx.pdb','mstscex.exe','msrdcex.exe') | % {
Copy-Item "$BuildDir/Release/$_" "dependencies/MsRdpEx/$Arch"
}
Compress-Archive "dependencies\MsRdpEx\$Arch\*.pdb" ".\symbols\MsRdpEx-$PackageVersion-$Arch.symbols.zip" -CompressionLevel Optimal
Remove-Item "dependencies\MsRdpEx\$Arch\*.pdb" | Out-Null
Compress-Archive "dependencies\MsRdpEx\$Arch\*" ".\package\MsRdpEx-$PackageVersion-$Arch.zip" -CompressionLevel Optimal
- name: Upload MsRdpEx (${{matrix.arch}})
uses: actions/upload-artifact@v4
with:
name: MsRdpEx-zip-${{matrix.arch}}
path: package/*.zip
- name: Upload MsRdpEx symbols (${{matrix.arch}})
uses: actions/upload-artifact@v4
with:
name: MsRdpEx-symbols-${{matrix.arch}}
path: symbols/*.zip
build-managed:
name: Build managed library
runs-on: windows-2022
needs: [preflight, build-native]
environment: ${{ needs.preflight.outputs.package-env }}
steps:
- name: Check out ${{ github.repository }}
uses: actions/checkout@v4
- name: Configure runner
shell: pwsh
run: |
New-Item .\package -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
New-Item .\symbols -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
New-Item ".\dependencies\MsRdpEx" -ItemType Directory | Out-Null
- name: Install code signing tools
run: |
dotnet tool install --global AzureSignTool
dotnet tool install --global NuGetKeyVaultSignTool
Install-Module -Name Devolutions.Authenticode -Force
# 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 }}'
$csprojPath = "dotnet\Devolutions.MsRdpEx\Devolutions.MsRdpEx.csproj"
$csprojContent = Get-Content $csprojPath -Raw
$csprojContent = $csprojContent -Replace '(<Version>).*?(</Version>)', "<Version>$PackageVersion</Version>"
Set-Content -Path $csprojPath -Value $csprojContent -Encoding UTF8
- name: Download native dependencies
uses: actions/download-artifact@v4
with:
pattern: MsRdpEx-zip-*
merge-multiple: true
path: package
- name: Download native symbols
uses: actions/download-artifact@v4
with:
pattern: MsRdpEx-symbols-*
merge-multiple: true
path: symbols
- name: Extract native dependencies
shell: pwsh
run: |
Get-Item .\package\*.zip | ForEach-Object {
($Name, $Version, $Arch) = $_.BaseName -Split '-'
Expand-Archive $_ "dependencies\$Name\$Arch\" -Force
}
- name: Code sign zip packages
shell: pwsh
run: |
$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-Item .\package\*.zip | ForEach-Object {
$ZipFile = $_.FullName
($Name, $Version, $Arch) = $_.BaseName -Split '-'
$BinDir = "dependencies\$Name\$Arch"
Get-ChildItem -Path "$BinDir/*" -Include @("*.exe","*.dll") | ForEach-Object {
AzureSignTool @Params $_.FullName
}
Remove-Item $ZipFile | Out-Null
Compress-Archive "$BinDir\*" $ZipFile -CompressionLevel Optimal
Get-ZipAuthenticodeDigest $ZipFile -Export
AzureSignTool @Params "${ZipFile}.sig.ps1"
Import-ZipAuthenticodeSignature $ZipFile -Remove
}
- name: Upload zip packages
uses: actions/upload-artifact@v4
with:
name: MsRdpEx-zip
path: package/*.zip
- name: Upload native symbols
uses: actions/upload-artifact@v4
with:
name: MsRdpEx-symbols
path: symbols/*.zip
- name: Build nuget package
shell: pwsh
run: |
Set-PSDebug -Trace 1
$BuildDir = "build-dotnet"
cmake -G "Visual Studio 17 2022" -A x64 -DWITH_DOTNET=ON -DWITH_NATIVE=OFF -B $BuildDir
cmake --build $BuildDir --config Release
& dotnet pack .\dotnet\Devolutions.MsRdpEx -o package
- name: Code sign nuget contents
shell: pwsh
run: |
Set-PSDebug -Trace 1
$NugetBaseName = $(Get-Item ./package/*.nupkg).BaseName
$PackedFile = "./package/${NugetBaseName}.nupkg"
$UnpackedDir = "./package/${NugetBaseName}"
$OutputDirectory = $(Get-Item $PackedFile).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: Code sign nuget package
if: ${{ fromJSON(needs.preflight.outputs.sign-nuget) == true }}
shell: pwsh
run: |
$NugetPackage = (Get-Item ".\package\*.nupkg" | Select-Object -First 1) | Resolve-Path -Relative
$Params = @('sign', $NugetPackage,
'-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')
& NuGetKeyVaultSignTool @Params
- name: Upload nuget package
uses: actions/upload-artifact@v4
with:
name: MsRdpEx-nupkg
path: package/*.nupkg
- name: Build MSI packages
shell: pwsh
run: |
$PackageVersion = '${{ needs.preflight.outputs.package-version }}'
$ShortVersion = $PackageVersion.Substring(2) # MSI short version
$WixVariables = Get-Content .\installer\Variables.wxi
$WixVariables = $WixVariables -Replace 'ProductVersion = "([^"]*)"', "ProductVersion = `"$ShortVersion`""
Set-Content .\installer\Variables.wxi $WixVariables
foreach ($Arch in @('x86','x64','arm64')) {
$MsvcArch = @{"x86"="Win32";"x64"="x64";"arm64"="ARM64"}[$Arch]
dotnet build /p:Configuration=Release /p:Platform=$MsvcArch installer/MsRdpEx.sln
Move-Item "installer\bin\$MsvcArch\Release\en-US\MsRdpEx.msi" "package\MsRdpEx-$PackageVersion-$Arch.msi"
}
- name: Code sign MSI packages
shell: pwsh
run: |
$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 .\package\*.msi | ForEach-Object {
AzureSignTool @Params $_.FullName
}
- name: Upload MSI packages
uses: actions/upload-artifact@v4
with:
name: MsRdpEx-msi
path: package/*.msi
publish:
name: Publish packages
runs-on: ubuntu-22.04
needs: [preflight, build-native, build-managed]
environment: ${{ needs.preflight.outputs.package-env }}
if: ${{ fromJSON(needs.preflight.outputs.skip-publish) == false }}
steps:
- name: Download zip package
uses: actions/download-artifact@v4
with:
name: MsRdpEx-zip
path: package
- name: Download nuget package
uses: actions/download-artifact@v4
with:
name: MsRdpEx-nupkg
path: package
- name: Download MSI package
uses: actions/download-artifact@v4
with:
name: MsRdpEx-msi
path: package
- name: Download symbols package
uses: actions/download-artifact@v4
with:
name: MsRdpEx-symbols
path: package
- name: Publish to nuget.org
shell: pwsh
run: |
$DryRun = [System.Boolean]::Parse('${{ needs.preflight.outputs.dry-run }}')
$NugetPackage = (Get-Item ./package/*.nupkg) | Resolve-Path -Relative
$PushArgs = @(
'nuget', 'push', "$NugetPackage",
'--api-key', '${{ secrets.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
}
- name: Create GitHub release
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
working-directory: package
run: |
$PackageVersion = '${{ needs.preflight.outputs.package-version }}'
$DryRun = [System.Boolean]::Parse('${{ needs.preflight.outputs.dry-run }}')
$HashPath = 'checksums'
$Files = Get-Item * | % { Get-FileHash -Algorithm SHA256 $_.FullName }
$Files | % { "$($_.Hash) $(Split-Path $_.Path -Leaf)" } | Out-File -FilePath $HashPath -Append -Encoding ASCII
echo "::group::checksums"
Get-Content $HashPath
echo "::endgroup::"
$ReleaseTag = "v$PackageVersion"
$ReleaseTitle = "MsRdpEx v${PackageVersion}"
$Repository = $Env:GITHUB_REPOSITORY
if ($DryRun) {
Write-Host "Dry Run: skipping GitHub release!"
} else {
& gh release create $ReleaseTag --repo $Repository --title $ReleaseTitle ./*
}