# 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 }