Sign executables and uninstaller
Some checks failed
Build / Initialization (push) Has been cancelled
Build / Code Format Validation (push) Has been cancelled
Build / Linux (Qt5) (push) Has been cancelled
Build / Linux (Qt6) (push) Has been cancelled
Build / Linux (Qt6 + 7zip) (push) Has been cancelled
Build / macOS (Qt6 Universal) (push) Has been cancelled
Build / macOS (Qt5) (push) Has been cancelled
Build / Windows x64 (Qt5) (push) Has been cancelled
Build / Windows x64 (Qt6) (push) Has been cancelled
Build / Windows x86 (Qt5) (push) Has been cancelled
Build / Docker amd64 Image (push) Has been cancelled
Build / Docker arm64 Image (push) Has been cancelled
Build / Publish Dev Builds (push) Has been cancelled
Build / Publish Release (push) Has been cancelled

This commit is contained in:
luisangelsm
2025-10-15 23:50:29 +02:00
parent aa5371c05f
commit d4a438a4e3
5 changed files with 309 additions and 5 deletions

View File

@ -295,6 +295,45 @@ jobs:
set PATH=C:\Qt\5.15.2\msvc2019_64\bin;%PATH%
nmake check TESTARGS="-maxwarnings 100000"
- name: Upload executables for signing
if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
uses: actions/upload-artifact@v4
id: upload_executables
with:
name: windows-x64-executables-unsigned-${{ needs.initialization.outputs.build_number }}
path: |
release64/YACReader.exe
release64/YACReaderLibrary.exe
release64/YACReaderLibraryServer.exe
- name: Sign executables with SignPath
if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: 'yacreader'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: 'zipped-files'
github-artifact-id: ${{ steps.upload_executables.outputs.artifact-id }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: "3600"
output-artifact-directory: release64/signed
- name: Replace with signed executables
if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
shell: pwsh
run: |
Write-Host "=== Replacing executables with signed versions ==="
Get-ChildItem -Path "release64/signed" -Filter "*.exe" | ForEach-Object {
$destPath = "release64/$($_.Name)"
Write-Host "Moving signed: $($_.Name) -> $destPath"
Move-Item -Path $_.FullName -Destination $destPath -Force
Write-Host " Moved successfully"
}
Remove-Item -Path "release64/signed" -Recurse -Force -ErrorAction SilentlyContinue
Write-Host "Signed executables are ready for installer creation"
- name: Create installer
shell: cmd
working-directory: ci/win
@ -328,9 +367,10 @@ jobs:
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: 'yacreader'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: 'windows-installer'
artifact-configuration-slug: 'zipped-files'
github-artifact-id: ${{ steps.upload_unsigned.outputs.artifact-id }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: "3600"
output-artifact-directory: ci/win/Output/signed
- name: Replace with signed installer
@ -421,6 +461,45 @@ jobs:
set PATH=C:\Qt\6.3.1\msvc2019_64\bin;%PATH%
nmake check TESTARGS="-maxwarnings 100000"
- name: Upload executables for signing
if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
uses: actions/upload-artifact@v4
id: upload_executables
with:
name: windows-x64-qt6-executables-unsigned-${{ needs.initialization.outputs.build_number }}
path: |
release64/YACReader.exe
release64/YACReaderLibrary.exe
release64/YACReaderLibraryServer.exe
- name: Sign executables with SignPath
if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: 'yacreader'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: 'zipped-files'
github-artifact-id: ${{ steps.upload_executables.outputs.artifact-id }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: "3600"
output-artifact-directory: release64/signed
- name: Replace with signed executables
if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
shell: pwsh
run: |
Write-Host "=== Replacing executables with signed versions ==="
Get-ChildItem -Path "release64/signed" -Filter "*.exe" | ForEach-Object {
$destPath = "release64/$($_.Name)"
Write-Host "Moving signed: $($_.Name) -> $destPath"
Move-Item -Path $_.FullName -Destination $destPath -Force
Write-Host " Moved successfully"
}
Remove-Item -Path "release64/signed" -Recurse -Force -ErrorAction SilentlyContinue
Write-Host "Signed executables are ready for installer creation"
- name: Create installer
shell: cmd
working-directory: ci/win
@ -454,9 +533,10 @@ jobs:
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: 'yacreader'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: 'windows-installer-qt6'
artifact-configuration-slug: 'zipped-files'
github-artifact-id: ${{ steps.upload_unsigned.outputs.artifact-id }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: "3600"
output-artifact-directory: ci/win/Output/signed
- name: Replace with signed installer
@ -531,6 +611,45 @@ jobs:
set PATH=C:\Qt\5.15.2\msvc2019\bin;%PATH%
nmake check TESTARGS="-maxwarnings 100000"
- name: Upload executables for signing
if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
uses: actions/upload-artifact@v4
id: upload_executables
with:
name: windows-x86-executables-unsigned-${{ needs.initialization.outputs.build_number }}
path: |
release/YACReader.exe
release/YACReaderLibrary.exe
release/YACReaderLibraryServer.exe
- name: Sign executables with SignPath
if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: 'yacreader'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: 'zipped-files'
github-artifact-id: ${{ steps.upload_executables.outputs.artifact-id }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: "3600"
output-artifact-directory: release/signed
- name: Replace with signed executables
if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
shell: pwsh
run: |
Write-Host "=== Replacing executables with signed versions ==="
Get-ChildItem -Path "release/signed" -Filter "*.exe" | ForEach-Object {
$destPath = "release/$($_.Name)"
Write-Host "Moving signed: $($_.Name) -> $destPath"
Move-Item -Path $_.FullName -Destination $destPath -Force
Write-Host " Moved successfully"
}
Remove-Item -Path "release/signed" -Recurse -Force -ErrorAction SilentlyContinue
Write-Host "Signed executables are ready for installer creation"
- name: Create installer
shell: cmd
working-directory: ci/win
@ -564,9 +683,10 @@ jobs:
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: 'yacreader'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: 'windows-installer-x86'
artifact-configuration-slug: 'zipped-files'
github-artifact-id: ${{ steps.upload_unsigned.outputs.artifact-id }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: "3600"
output-artifact-directory: ci/win/Output/signed
- name: Replace with signed installer

View File

@ -15,6 +15,7 @@ SetupIconFile=setup.ico
UninstallDisplayIcon=uninstall.ico
ArchitecturesInstallIn64BitMode=x64
ArchitecturesAllowed=x64
SignTool=custom_signtool
[Registry]
Root: HKCR; SubKey: .cbz; ValueType: string; ValueData: Comic Book (zip); Flags: uninsdeletekey; Tasks: File_association

View File

@ -15,6 +15,7 @@ SetupIconFile=setup.ico
UninstallDisplayIcon=uninstall.ico
ArchitecturesInstallIn64BitMode=x64
ArchitecturesAllowed=x64
SignTool=custom_signtool
[Registry]
Root: HKCR; SubKey: .cbz; ValueType: string; ValueData: Comic Book (zip); Flags: uninsdeletekey; Tasks: File_association

View File

@ -58,10 +58,11 @@ if "%1"=="x86" (
)
echo "iscc start"
set "SCRIPT_PATH=%cd%\sign_tool.ps1"
if "%4"=="qt6" (
iscc /DVERSION=%VERSION% /DPLATFORM=%1 /DCOMPRESSED_ARCHIVE_BACKEND=%2 /DBUILD_NUMBER=%3 build_installer_qt6.iss || exit /b
iscc /DVERSION=%VERSION% /DPLATFORM=%1 /DCOMPRESSED_ARCHIVE_BACKEND=%2 /DBUILD_NUMBER=%3 /Scustom_signtool="powershell.exe -ExecutionPolicy Bypass -File %SCRIPT_PATH% $f" build_installer_qt6.iss || exit /b
) else (
iscc /DVERSION=%VERSION% /DPLATFORM=%1 /DCOMPRESSED_ARCHIVE_BACKEND=%2 /DBUILD_NUMBER=%3 build_installer.iss || exit /b
iscc /DVERSION=%VERSION% /DPLATFORM=%1 /DCOMPRESSED_ARCHIVE_BACKEND=%2 /DBUILD_NUMBER=%3 /Scustom_signtool="powershell.exe -ExecutionPolicy Bypass -File %SCRIPT_PATH% $f" build_installer.iss || exit /b
)
echo "iscc done!"

181
ci/win/sign_tool.ps1 Normal file
View File

@ -0,0 +1,181 @@
# PowerShell script for signing uninstaller during Inno Setup process
param(
[Parameter(Mandatory=$true)]
[string]$FilePath
)
# Get the filename from the full path
$fileName = Split-Path $FilePath -Leaf
Write-Host "SignTool called for file: $fileName"
Write-Host "Full file path: $FilePath"
# Validate the file exists
if (-not (Test-Path $FilePath)) {
Write-Error "File not found: $FilePath"
exit 1
}
# Only sign the uninstaller - all other executables are pre-signed
if ($fileName -eq "unins000.exe") {
Write-Host "Signing uninstaller: $fileName"
# Check if we're in a CI environment (GitHub Actions)
$isCI = $env:GITHUB_ACTIONS -eq "true"
if ($isCI) {
Write-Host "Running in CI environment - calling SignPath API"
Write-Host "File path: $FilePath"
Write-Host "File size: $((Get-Item $FilePath).Length) bytes"
# Get required environment variables (these should be set as GitHub secrets)
$organizationId = $env:SIGNPATH_ORGANIZATION_ID
$projectSlug = $env:SIGNPATH_PROJECT_SLUG
$signingPolicySlug = $env:SIGNPATH_SIGNING_POLICY_SLUG
$apiToken = $env:SIGNPATH_API_TOKEN
# Validate required environment variables are set
if (-not $organizationId) {
Write-Error "SIGNPATH_ORGANIZATION_ID environment variable not set"
exit 1
}
if (-not $projectSlug) {
Write-Error "SIGNPATH_PROJECT_SLUG environment variable not set"
exit 1
}
if (-not $signingPolicySlug) {
Write-Error "SIGNPATH_SIGNING_POLICY_SLUG environment variable not set"
exit 1
}
if (-not $apiToken) {
Write-Error "SIGNPATH_API_TOKEN environment variable not set"
exit 1
}
# Initialize temp directory variable for proper cleanup
$tempDir = $null
try {
# Create a temporary directory for the signing process
$tempDir = New-Item -ItemType Directory -Path (Join-Path $env:TEMP "signpath_$(Get-Random)") -Force
$tempFile = Join-Path $tempDir "unins000.exe"
Write-Host "Created temp directory: $tempDir"
# Copy file to temp location with error handling
Copy-Item $FilePath $tempFile -Force
Write-Host "Copied file to temp location: $tempFile"
Write-Host "Copied file to temp location: $tempFile"
Write-Host "Uploading uninstaller to SignPath for signing..."
# Create the API request
$uri = "https://app.signpath.io/api/v1/$organizationId/SigningRequests"
# Prepare the form data
$form = @{
'ProjectSlug' = $projectSlug
'SigningPolicySlug' = $signingPolicySlug
'ArtifactConfigurationSlug' = 'executable-file'
'Description' = "Signing uninstaller for YACReader build $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
}
# Prepare headers (API token should never be logged)
$headers = @{
'Authorization' = "Bearer $apiToken"
}
# Upload and sign with error handling
Write-Host "Submitting signing request..."
$response = Invoke-RestMethod -Uri $uri -Method Post -Form $form -InFile $tempFile -Headers $headers
# Validate response has required fields
if (-not $response.signingRequestId) {
Write-Error "SignPath API response missing signingRequestId field"
exit 1
}
$signingRequestId = $response.signingRequestId
Write-Host "Signing request submitted with ID: $signingRequestId"
# Poll for completion
$statusUri = "https://app.signpath.io/api/v1/$organizationId/SigningRequests/$signingRequestId"
$maxWaitTime = 3600 # 1 hour max (3600 seconds)
$waitTime = 0
$pollInterval = 10
do {
Start-Sleep $pollInterval
$waitTime += $pollInterval
Write-Host "Checking signing status... ($waitTime/$maxWaitTime seconds)"
$status = Invoke-RestMethod -Uri $statusUri -Headers $headers
# Validate status response
if (-not $status.status) {
Write-Error "SignPath status response missing status field"
exit 1
}
if ($status.status -eq "Completed") {
Write-Host "Signing completed successfully!"
break
} elseif ($status.status -eq "Failed") {
$description = if ($status.description) { $status.description } else { "Unknown error" }
Write-Error "Signing failed: $description"
exit 1
} elseif ($status.status -eq "Denied") {
$description = if ($status.description) { $status.description } else { "Request denied" }
Write-Error "Signing was denied: $description"
exit 1
}
} while ($waitTime -lt $maxWaitTime)
if ($waitTime -ge $maxWaitTime) {
Write-Error "Signing timed out after $maxWaitTime seconds"
exit 1
}
# Download the signed file
Write-Host "Downloading signed uninstaller..."
$downloadUri = "https://app.signpath.io/api/v1/$organizationId/SigningRequests/$signingRequestId/SignedArtifact"
# Download to temp location first, then replace original
$signedTempFile = Join-Path $tempDir "unins000_signed.exe"
Invoke-RestMethod -Uri $downloadUri -Headers $headers -OutFile $signedTempFile
# Verify the signed file exists and has content
if (-not (Test-Path $signedTempFile) -or (Get-Item $signedTempFile).Length -eq 0) {
Write-Error "Downloaded signed file is missing or empty"
exit 1
}
# Replace the original file with the signed version
Copy-Item $signedTempFile $FilePath -Force
Write-Host "Uninstaller signed successfully and replaced at: $FilePath"
# Clean up temp directory
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
Write-Host "Cleaned up temporary files"
} catch {
Write-Error "SignPath API call failed: $($_.Exception.Message)"
Write-Host "Error details: $($_.Exception.ToString())"
# Clean up temp directory on error
if ($tempDir -and (Test-Path $tempDir)) {
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
Write-Host "Cleaned up temp directory after error"
}
exit 1
}
} else {
Write-Host "Running locally - skipping uninstaller signing"
Write-Host "Set GITHUB_ACTIONS=true to enable SignPath integration"
exit 0
}
} else {
Write-Host "Skipping file: $fileName (not the uninstaller)"
exit 0
}