Skip to content

Commit

Permalink
PICARD-3002: Use Azure Trusted Signing for code signing
Browse files Browse the repository at this point in the history
  • Loading branch information
phw committed Dec 6, 2024
1 parent 442dd6a commit 906b2c1
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 253 deletions.
127 changes: 59 additions & 68 deletions .github/workflows/package-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ jobs:
runs-on: windows-2019
strategy:
matrix:
type:
- store-app
- signed-app
- installer
- portable
setup:
- type: installer
- type: portable
build-portable: 1
- type: store-app
disable-autoupdate: 1
fail-fast: false
env:
CODESIGN: 0
CODESIGN: ${{ !!secrets.AZURE_CERT_PROFILE_NAME }}
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -32,9 +33,6 @@ jobs:
-DiscidVersion $Env:DISCID_VERSION -DiscidSha256Sum $Env:DISCID_SHA256SUM `
-FpcalcVersion $Env:FPCALC_VERSION -FpcalcSha256Sum $Env:FPCALC_SHA256SUM
Add-Content $env:GITHUB_PATH "C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64"
$ReleaseTag = $(git describe --match "release-*" --abbrev=0 --always HEAD)
$BuildNumber = $(git rev-list --count "$ReleaseTag..HEAD")
Add-Content $env:GITHUB_ENV "BUILD_NUMBER=$BuildNumber"
New-Item -Name .\artifacts -ItemType Directory
env:
DISCID_VERSION: 0.6.4
Expand All @@ -57,80 +55,73 @@ jobs:
- name: Patch build version
if: startsWith(github.ref, 'refs/tags/') != true
run: |
$ReleaseTag = $(git describe --match "release-*" --abbrev=0 --always HEAD)
$BuildNumber = $(git rev-list --count "$ReleaseTag..HEAD")
python setup.py patch_version --platform=$Env:BUILD_NUMBER.$(git rev-parse --short HEAD)
- name: Run tests
timeout-minutes: 30
run: pytest --verbose
- name: Prepare code signing certificate
if: matrix.type != 'store-app'
- name: Prepare clean build environment
run: |
If ($Env:CODESIGN_P12_URL -And $Env:AWS_ACCESS_KEY_ID) {
pip install awscli
aws s3 cp "$Env:CODESIGN_P12_URL" .\codesign.pfx
Add-Content $env:GITHUB_ENV "CODESIGN=1"
} Else {
Write-Output "::warning::No code signing certificate available, skipping code signing."
}
env:
AWS_DEFAULT_REGION: eu-central-1
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
CODESIGN_P12_URL: ${{ secrets.CODESIGN_P12_URL }}
- name: Build Windows 10 store app package
if: matrix.type == 'store-app'
Remove-Item -Path build,dist/picard,locale -Recurse -ErrorAction Ignore
python setup.py clean
- name: Build
run: |
& .\scripts\package\win-package-appx.ps1 -BuildNumber $Env:BUILD_NUMBER
Move-Item .\dist\*.msix .\artifacts
python setup.py build --build-number=$BuildNumber
python setup.py build_ext
pyinstaller --noconfirm --clean picard.spec
If ($env:PICARD_BUILD_PORTABLE -ne "1") {
dist\picard\_internal\fpcalc -version
}
env:
PICARD_APPX_PUBLISHER: CN=0A9169B7-05A3-4ED9-8876-830F17846709
- name: Build Windows 10 signed app package
if: matrix.type == 'signed-app' && env.CODESIGN == '1'
run: |
$CertificateFile = ".\codesign.pfx"
$CertificatePassword = ConvertTo-SecureString -String $Env:CODESIGN_P12_PASSWORD -Force -AsPlainText
& .\scripts\package\win-package-appx.ps1 -BuildNumber $Env:BUILD_NUMBER `
-CertificateFile $CertificateFile -CertificatePassword $CertificatePassword
Move-Item .\dist\*.msix .\artifacts
env:
CODESIGN_P12_PASSWORD: ${{ secrets.CODESIGN_P12_PASSWORD }}
PICARD_BUILD_PORTABLE: ${{ matrix.setup.build-portable }}
PICARD_DISABLE_AUTOUPDATE: ${{ matrix.setup.disable-autoupdate }}
- name: Sign picard.exe
uses: azure/trusted-signing-action@v0.5.0
if: matrix.setup.type != 'portable' && env.CODESIGN == 'true'
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
endpoint: ${{ secrets.AZURE_ENDPOINT }}
trusted-signing-account-name: ${{ secrets.AZURE_CODE_SIGNING_NAME }}
certificate-profile-name: ${{ secrets.AZURE_CERT_PROFILE_NAME }}
files-folder: dist\picard
files-folder-filter: exe
files-folder-recurse: true
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Build Windows installer
if: matrix.type == 'installer'
if: matrix.setup.type == 'installer'
run: |
# choco install nsis
If ($Env:CODESIGN -eq "1") {
$CertificateFile = ".\codesign.pfx"
$CertificatePassword = ConvertTo-SecureString -String $Env:CODESIGN_P12_PASSWORD -Force -AsPlainText
} Else {
$CertificateFile = $null
$CertificatePassword = $null
}
& .\scripts\package\win-package-installer.ps1 -BuildNumber $Env:BUILD_NUMBER `
-CertificateFile $CertificateFile -CertificatePassword $CertificatePassword
makensis.exe /INPUTCHARSET UTF8 installer\picard-setup.nsi
Move-Item .\installer\*.exe .\artifacts
dist\picard\_internal\fpcalc -version
env:
CODESIGN_P12_PASSWORD: ${{ secrets.CODESIGN_P12_PASSWORD }}
- name: Build Windows portable app
if: matrix.type == 'portable'
if: matrix.setup.type == 'portable'
run: |
If ($Env:CODESIGN -eq "1") {
$CertificateFile = ".\codesign.pfx"
$CertificatePassword = ConvertTo-SecureString -String $Env:CODESIGN_P12_PASSWORD -Force -AsPlainText
} Else {
$CertificateFile = $null
$CertificatePassword = $null
}
& .\scripts\package\win-package-portable.ps1 -BuildNumber $Env:BUILD_NUMBER `
-CertificateFile $CertificateFile -CertificatePassword $CertificatePassword
Move-Item .\dist\*.exe .\artifacts
env:
CODESIGN_P12_PASSWORD: ${{ secrets.CODESIGN_P12_PASSWORD }}
- name: Cleanup
if: env.CODESIGN == '1'
run: Remove-Item .\codesign.pfx
- name: Build Windows 10 store app package
if: matrix.setup.type == 'store-app'
run: |
& .\scripts\package\win-package-appx.ps1 dist\picard
Move-Item .\dist\*.msix .\artifacts
- name: Sign final executable
uses: azure/trusted-signing-action@v0.5.0
if: matrix.setup.type != 'store-app' && env.CODESIGN == 'true'
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
endpoint: ${{ secrets.AZURE_ENDPOINT }}
trusted-signing-account-name: ${{ secrets.AZURE_CODE_SIGNING_NAME }}
certificate-profile-name: ${{ secrets.AZURE_CERT_PROFILE_NAME }}
files-folder: artifacts
files-folder-filter: exe
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Archive production artifacts
uses: actions/upload-artifact@v4
if: matrix.type != 'signed-app' || env.CODESIGN == '1'
with:
name: windows-${{ matrix.type }}
name: windows-${{ matrix.setup.type }}
path: artifacts/
19 changes: 19 additions & 0 deletions scripts/package/win-build-fixes.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Apply fixes to the build result

Param(
[ValidateScript({Test-Path $_ -PathType Container})]
[String]
$Path
)

$ErrorActionPreference = 'Stop'

$InternalPath = (Join-Path -Path $Path -ChildPath _internal)
# Move all Qt6 DLLs into the main folder to avoid conflicts with system wide
# versions of those dependencies. Since some version PyInstaller tries to
# maintain the file hierarchy of imported modules, but this easily breaks
# DLL loading on Windows.
# Workaround for https://tickets.metabrainz.org/browse/PICARD-2736
$Qt6Dir = (Join-Path -Path $InternalPath -ChildPath PyQt6\Qt6)
Move-Item -Path (Join-Path -Path $Qt6Dir -ChildPath bin\*.dll) -Destination $Path -Force
Remove-Item -Path (Join-Path -Path $Qt6Dir -ChildPath bin)
49 changes: 0 additions & 49 deletions scripts/package/win-common.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,6 @@ Param(
$CertificatePassword
)

# RFC 3161 timestamp server for code signing
$TimeStampServer = 'http://ts.ssl.com'

Function CodeSignBinary {
Param(
[ValidateScript({Test-Path $_ -PathType Leaf})]
[String]
$BinaryPath
)
If ($CertificateFile) {
SignTool sign /v /fd SHA256 /tr "$TimeStampServer" /td sha256 `
/f "$CertificateFile" /p (ConvertFrom-SecureString -AsPlainText $CertificatePassword) `
$BinaryPath
ThrowOnExeError "SignTool failed"
} Else {
Write-Output "Skip signing $BinaryPath"
}
}

Function ThrowOnExeError {
Param( [String]$Message )
If ($LastExitCode -ne 0) {
Throw $Message
}
}

Function FinalizePackage {
Param(
[ValidateScript({Test-Path $_ -PathType Container})]
[String]
$Path
)

$InternalPath = (Join-Path -Path $Path -ChildPath _internal)

CodeSignBinary -BinaryPath (Join-Path -Path $Path -ChildPath picard.exe) -ErrorAction Stop
CodeSignBinary -BinaryPath (Join-Path -Path $InternalPath -ChildPath fpcalc.exe) -ErrorAction Stop
CodeSignBinary -BinaryPath (Join-Path -Path $InternalPath -ChildPath discid.dll) -ErrorAction Stop

# Move all Qt6 DLLs into the main folder to avoid conflicts with system wide
# versions of those dependencies. Since some version PyInstaller tries to
# maintain the file hierarchy of imported modules, but this easily breaks
# DLL loading on Windows.
# Workaround for https://tickets.metabrainz.org/browse/PICARD-2736
$Qt6Dir = (Join-Path -Path $InternalPath -ChildPath PyQt6\Qt6)
Move-Item -Path (Join-Path -Path $Qt6Dir -ChildPath bin\*.dll) -Destination $Path -Force
Remove-Item -Path (Join-Path -Path $Qt6Dir -ChildPath bin)
}

Function DownloadFile {
Param(
[Parameter(Mandatory = $true)]
Expand Down
60 changes: 6 additions & 54 deletions scripts/package/win-package-appx.ps1
Original file line number Diff line number Diff line change
@@ -1,72 +1,24 @@
# Build a MSIX app package for Windows 10

Param(
[ValidateScript({ (Test-Path $_ -PathType Leaf) -or (-not $_) })]
[ValidateScript({ Test-Path $_ -PathType Container })]
[String]
$CertificateFile,
[SecureString]
$CertificatePassword,
[Int]
$BuildNumber
$PackageDir
)

# Errors are handled explicitly. Otherwise any output to stderr when
# calling classic Windows exes causes a script error.
# TODO: For PowerShell >= 7.3 use $PSNativeCommandUseErrorActionPreference = $true
$ErrorActionPreference = 'Continue'
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true

If (-Not $BuildNumber) {
$BuildNumber = 0
}

If (-Not $Certificate -And $CertificateFile) {
$Certificate = Get-PfxCertificate -FilePath $CertificateFile -Password $CertificatePassword
}

$ScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
. $ScriptDirectory\win-common.ps1 -CertificateFile $CertificateFile -CertificatePassword $CertificatePassword

Write-Output "Building Windows 10 app package..."

# Set the publisher based on the certificate subject
if ($Certificate) {
$env:PICARD_APPX_PUBLISHER = $Certificate.Subject
Write-Output "Publisher: $env:PICARD_APPX_PUBLISHER"
}

# Build
Remove-Item -Path build,dist/picard,locale -Recurse -ErrorAction Ignore
python setup.py clean 2>&1 | %{ "$_" }
ThrowOnExeError "setup.py clean failed"
python setup.py build --build-number=$BuildNumber --disable-autoupdate 2>&1 | %{ "$_" }
ThrowOnExeError "setup.py build failed"
python setup.py build_ext -i 2>&1 | %{ "$_" }
ThrowOnExeError "setup.py build_ext -i failed"

# Package application
pyinstaller --noconfirm --clean picard.spec 2>&1 | %{ "$_" }
ThrowOnExeError "PyInstaller failed"
$PackageDir = (Resolve-Path dist\picard)
FinalizePackage $PackageDir
$PackageDir = (Resolve-Path $PackageDir)

# Generate resource files
Copy-Item appxmanifest.xml $PackageDir
$PriConfigFile = (Join-Path (Resolve-Path .\build) priconfig.xml)
Push-Location $PackageDir
MakePri createconfig /ConfigXml $PriConfigFile /Default en-US /Overwrite
ThrowOnExeError "MakePri createconfig failed"
MakePri new /ProjectRoot $PackageDir /ConfigXml $PriConfigFile
ThrowOnExeError "MakePri new failed"
Pop-Location

# Generate msix package
$PicardVersion = (python -c "import picard; print(picard.__version__)")
If ($CertificateFile -or $Certificate) {
$PackageFile = "dist\MusicBrainz-Picard-$PicardVersion.msix"
} Else {
$PackageFile = "dist\MusicBrainz-Picard-$PicardVersion-unsigned.msix"
}
$PackageFile = "dist\MusicBrainz-Picard-${PicardVersion}_unsigned.msix"
MakeAppx pack /o /h SHA256 /d $PackageDir /p $PackageFile
ThrowOnExeError "MakeAppx failed"

CodeSignBinary -BinaryPath $PackageFile -ErrorAction Stop
43 changes: 0 additions & 43 deletions scripts/package/win-package-installer.ps1

This file was deleted.

Loading

0 comments on commit 906b2c1

Please sign in to comment.