mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-15 20:01:31 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6a5961366 | ||
|
|
9b57bd0167 | ||
|
|
da34704f5e | ||
|
|
9afa6233b4 | ||
|
|
298aa7f706 |
@@ -20,6 +20,8 @@ on:
|
||||
- 'scripts/test_build_win.ps1'
|
||||
- 'build_release_macos.sh'
|
||||
- 'scripts/build_preset_cache.*'
|
||||
- 'signpath/**'
|
||||
- 'scripts/*.ps1'
|
||||
- 'scripts/flatpak/**'
|
||||
- 'scripts/msix/**'
|
||||
- 'tests/**'
|
||||
@@ -36,11 +38,13 @@ on:
|
||||
- 'version.inc'
|
||||
- ".github/workflows/build_*.yml"
|
||||
- ".github/workflows/unit_tests*.yml"
|
||||
- 'signpath/**'
|
||||
- 'build_linux.sh'
|
||||
- 'build_win.bat'
|
||||
- 'scripts/test_build_win.ps1'
|
||||
- 'build_release_macos.sh'
|
||||
- 'scripts/build_preset_cache.*'
|
||||
- 'scripts/*.ps1'
|
||||
- 'scripts/flatpak/**'
|
||||
- 'scripts/msix/**'
|
||||
- 'tests/**'
|
||||
|
||||
@@ -504,6 +504,57 @@ jobs:
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload unsigned Windows portable artifact for SignPath
|
||||
id: upload-windows-portable
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable_unsigned
|
||||
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Submit Windows portable artifact to SignPath
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
uses: signpath/github-action-submit-signing-request@v2
|
||||
with:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
|
||||
project-slug: OrcaSlicer
|
||||
signing-policy-slug: test-signing
|
||||
artifact-configuration-slug: windows-portable-v1
|
||||
github-artifact-id: ${{ steps.upload-windows-portable.outputs.artifact-id }}
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/windows-portable
|
||||
|
||||
- name: Verify SignPath Windows portable signatures
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
shell: pwsh
|
||||
# -AllowUntrustedRoot is required while signing with the SignPath test
|
||||
# certificate (self-signed). Remove it once signing-policy-slug switches
|
||||
# to release-signing with a production CA-issued certificate.
|
||||
run: ./scripts/verify-authenticode.ps1 -ArtifactDirectory '${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/windows-portable' -AllowUntrustedRoot
|
||||
|
||||
- name: Replace Windows binaries with signed output
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
shell: pwsh
|
||||
run: |
|
||||
$source = Join-Path "${{ github.workspace }}" "${{ env.BUILD_DIR }}/signpath/windows-portable"
|
||||
$destination = Join-Path "${{ github.workspace }}" "${{ env.BUILD_DIR }}/OrcaSlicer"
|
||||
if (-not (Test-Path -LiteralPath $source -PathType Container)) {
|
||||
throw "SignPath output directory not found: $source"
|
||||
}
|
||||
Get-ChildItem -LiteralPath $source -Force | Copy-Item -Destination $destination -Recurse -Force
|
||||
|
||||
# cpack's install rules take the binaries from the build output, not from
|
||||
# the portable bundle, so the installer needs the signed copies there too.
|
||||
$buildOutput = Join-Path "${{ github.workspace }}" "${{ env.BUILD_DIR }}/src/Release"
|
||||
if (-not (Test-Path -LiteralPath $buildOutput -PathType Container)) {
|
||||
throw "Build output directory not found: $buildOutput"
|
||||
}
|
||||
foreach ($name in "orca-slicer.exe", "OrcaSlicer.dll") {
|
||||
Copy-Item -LiteralPath (Join-Path $source $name) -Destination $buildOutput -Force
|
||||
}
|
||||
|
||||
# NSIS is x86-only; it runs (and the installer it emits runs) under ARM64's
|
||||
# x86 emulation, packaging the native arm64 payload from build-arm64.
|
||||
- name: Create installer Win
|
||||
@@ -512,11 +563,53 @@ jobs:
|
||||
run: |
|
||||
cpack -G NSIS
|
||||
|
||||
- name: Upload unsigned Windows installer for SignPath
|
||||
id: upload-windows-installer
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}_unsigned
|
||||
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}.exe
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Submit Windows installer to SignPath
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
uses: signpath/github-action-submit-signing-request@v2
|
||||
with:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
|
||||
project-slug: OrcaSlicer
|
||||
signing-policy-slug: test-signing
|
||||
artifact-configuration-slug: windows-installer-v1
|
||||
github-artifact-id: ${{ steps.upload-windows-installer.outputs.artifact-id }}
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/windows-installer
|
||||
|
||||
- name: Verify SignPath Windows installer signature
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
shell: pwsh
|
||||
# -AllowUntrustedRoot: see "Verify SignPath Windows portable signatures".
|
||||
run: ./scripts/verify-authenticode.ps1 -ArtifactDirectory '${{ github.workspace }}/${{ env.BUILD_DIR }}/signpath/windows-installer' -Files 'OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}.exe' -AllowUntrustedRoot
|
||||
|
||||
- name: Replace Windows installer with signed output
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
shell: pwsh
|
||||
run: |
|
||||
$name = "OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}.exe"
|
||||
$source = Join-Path "${{ github.workspace }}" "${{ env.BUILD_DIR }}/signpath/windows-installer/$name"
|
||||
Copy-Item -LiteralPath $source -Destination (Join-Path "${{ github.workspace }}" "${{ env.BUILD_DIR }}/$name") -Force
|
||||
|
||||
- name: Pack app
|
||||
if: runner.os == 'Windows'
|
||||
working-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}
|
||||
shell: cmd
|
||||
run: '"C:/Program Files/7-Zip/7z.exe" a -tzip OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable.zip ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$zipPath = "OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable.zip"
|
||||
Remove-Item -LiteralPath $zipPath -Force -ErrorAction SilentlyContinue
|
||||
& "C:/Program Files/7-Zip/7z.exe" a -tzip $zipPath "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
|
||||
- name: Pack PDB
|
||||
if: runner.os == 'Windows' && inputs.arch != 'arm64' && !vars.SELF_HOSTED
|
||||
@@ -530,6 +623,7 @@ jobs:
|
||||
with:
|
||||
name: OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable
|
||||
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload artifacts Win installer
|
||||
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateScript({ Test-Path -LiteralPath $_ -PathType Container })]
|
||||
[string]$ArtifactDirectory
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Get-RelativePath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Root,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$rootWithSeparator = $Root.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar
|
||||
$rootUri = [Uri]$rootWithSeparator
|
||||
$pathUri = [Uri]$Path
|
||||
|
||||
[Uri]::UnescapeDataString($rootUri.MakeRelativeUri($pathUri).ToString()).Replace("/", [IO.Path]::DirectorySeparatorChar)
|
||||
}
|
||||
|
||||
$artifactRoot = (Resolve-Path -LiteralPath $ArtifactDirectory).Path
|
||||
$binaries = Get-ChildItem -LiteralPath $artifactRoot -Recurse -File |
|
||||
Where-Object { $_.Extension -in @(".exe", ".dll") } |
|
||||
Sort-Object -Property FullName
|
||||
|
||||
if (-not $binaries) {
|
||||
Write-Warning "No .exe or .dll files found under '$artifactRoot'."
|
||||
exit 0
|
||||
}
|
||||
|
||||
$binaries | ForEach-Object {
|
||||
$signature = Get-AuthenticodeSignature -LiteralPath $_.FullName
|
||||
|
||||
[pscustomobject]@{
|
||||
RelativePath = Get-RelativePath -Root $artifactRoot -Path $_.FullName
|
||||
Status = $signature.Status
|
||||
SignatureType = $signature.SignatureType
|
||||
Signer = if ($signature.SignerCertificate) { $signature.SignerCertificate.Subject } else { "" }
|
||||
SignerThumbprint = if ($signature.SignerCertificate) { $signature.SignerCertificate.Thumbprint } else { "" }
|
||||
Timestamped = [bool]$signature.TimeStamperCertificate
|
||||
TimeStamper = if ($signature.TimeStamperCertificate) { $signature.TimeStamperCertificate.Subject } else { "" }
|
||||
}
|
||||
} | Format-Table -AutoSize
|
||||
@@ -0,0 +1,115 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateScript({ Test-Path -LiteralPath $_ -PathType Container })]
|
||||
[string]$ArtifactDirectory,
|
||||
|
||||
[string[]]$Files = @(
|
||||
"orca-slicer.exe",
|
||||
"OrcaSlicer.dll"
|
||||
),
|
||||
|
||||
[string]$SignToolPath,
|
||||
|
||||
# Accept signatures whose certificate chain terminates in an untrusted root.
|
||||
# Required for the SignPath test certificate (self-signed). Do NOT pass this
|
||||
# once a production CA-issued certificate is in use, so release builds enforce
|
||||
# a fully trusted chain.
|
||||
[switch]$AllowUntrustedRoot
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Resolve-SignToolPath {
|
||||
param(
|
||||
[string]$ExplicitPath
|
||||
)
|
||||
|
||||
if ($ExplicitPath) {
|
||||
if (Test-Path -LiteralPath $ExplicitPath -PathType Leaf) {
|
||||
return (Resolve-Path -LiteralPath $ExplicitPath).Path
|
||||
}
|
||||
|
||||
throw "SignTool was not found at '$ExplicitPath'."
|
||||
}
|
||||
|
||||
$fromPath = Get-Command -Name "signtool.exe" -ErrorAction SilentlyContinue
|
||||
if ($fromPath) {
|
||||
return $fromPath.Source
|
||||
}
|
||||
|
||||
$candidateRoots = @(
|
||||
"${env:ProgramFiles(x86)}\Windows Kits\10\bin",
|
||||
"${env:ProgramFiles}\Windows Kits\10\bin"
|
||||
) | Where-Object { $_ -and (Test-Path -LiteralPath $_ -PathType Container) }
|
||||
|
||||
foreach ($root in $candidateRoots) {
|
||||
$candidate = Get-ChildItem -LiteralPath $root -Recurse -Filter "signtool.exe" -File -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.FullName -match "\\(x64|arm64)\\signtool\.exe$" } |
|
||||
Sort-Object -Property FullName -Descending |
|
||||
Select-Object -First 1
|
||||
|
||||
if ($candidate) {
|
||||
return $candidate.FullName
|
||||
}
|
||||
}
|
||||
|
||||
throw "signtool.exe was not found. Install the Windows SDK or pass -SignToolPath."
|
||||
}
|
||||
|
||||
$artifactRoot = (Resolve-Path -LiteralPath $ArtifactDirectory).Path
|
||||
$signtool = Resolve-SignToolPath -ExplicitPath $SignToolPath
|
||||
|
||||
Write-Host "Using SignTool: $signtool"
|
||||
Write-Host "Verifying Authenticode signatures in: $artifactRoot"
|
||||
|
||||
foreach ($relativePath in $Files) {
|
||||
$filePath = Join-Path $artifactRoot $relativePath
|
||||
if (-not (Test-Path -LiteralPath $filePath -PathType Leaf)) {
|
||||
throw "Expected signed file was not found: $filePath"
|
||||
}
|
||||
|
||||
Write-Host "Verifying $relativePath"
|
||||
|
||||
# Capture signtool output without letting native stderr (redirected via 2>&1)
|
||||
# raise a terminating NativeCommandError under $ErrorActionPreference = 'Stop'.
|
||||
$previousErrorActionPreference = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Continue"
|
||||
try {
|
||||
$output = & $signtool verify /pa /all /tw /v $filePath 2>&1
|
||||
$exitCode = $LASTEXITCODE
|
||||
}
|
||||
finally {
|
||||
$ErrorActionPreference = $previousErrorActionPreference
|
||||
}
|
||||
|
||||
$output | ForEach-Object { Write-Host $_ }
|
||||
|
||||
if ($exitCode -eq 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ($AllowUntrustedRoot) {
|
||||
# signtool interleaves its stdout (chain details) and stderr (the error)
|
||||
# unpredictably, so the error text cannot be matched reliably. Use the
|
||||
# structured Get-AuthenticodeSignature result instead: accept only when the
|
||||
# file is genuinely signed and the sole problem is that the chain terminates
|
||||
# in an untrusted root (i.e. the self-signed SignPath test certificate).
|
||||
$signature = Get-AuthenticodeSignature -LiteralPath $filePath
|
||||
$isSigned = ($signature.SignatureType -eq "Authenticode") -and ($null -ne $signature.SignerCertificate)
|
||||
$untrustedRootOnly = $signature.StatusMessage -match "terminated in a root certificate which is not trusted"
|
||||
if ($isSigned -and $untrustedRootOnly) {
|
||||
Write-Host " Accepted: '$relativePath' is signed but its certificate chains to an untrusted root (expected for the SignPath test certificate)."
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
throw "SignTool verification failed for '$relativePath' with exit code $exitCode."
|
||||
}
|
||||
|
||||
Write-Host "Authenticode verification passed."
|
||||
|
||||
# signtool exits non-zero for the untrusted test-cert root even when we accept
|
||||
# it above, leaving $LASTEXITCODE = 1. The GitHub Actions pwsh wrapper exits
|
||||
# with that lingering code, so reset it to report success explicitly.
|
||||
exit 0
|
||||
@@ -0,0 +1,22 @@
|
||||
# SignPath configurations
|
||||
|
||||
This directory contains SignPath artifact configurations used by GitHub Actions.
|
||||
|
||||
## `windows-portable-v1`
|
||||
|
||||
`windows-portable-v1.xml` is the initial conservative Windows portable-bundle signing configuration. It signs only the two first-party binaries:
|
||||
|
||||
- `orca-slicer.exe`
|
||||
- `OrcaSlicer.dll`
|
||||
|
||||
Do not broaden this to all DLLs without first confirming ownership, provenance, and whether upstream vendor signatures should be verified instead.
|
||||
|
||||
The Windows workflow uploads `${{ env.BUILD_DIR }}/OrcaSlicer` with `actions/upload-artifact`. GitHub stores that artifact as a ZIP, and the uploaded directory contents are rooted at the ZIP root. Because of that, the SignPath configuration uses `<zip-file>` with `orca-slicer.exe` and `OrcaSlicer.dll` directly beneath it.
|
||||
|
||||
The release portable ZIP is a separate archive created with 7-Zip from `${{ env.BUILD_DIR }}/OrcaSlicer`; that archive keeps the top-level `OrcaSlicer/` folder. After SignPath returns the signed artifact, the workflow copies the signed files back into `${{ env.BUILD_DIR }}/OrcaSlicer` and recreates the portable release ZIP so the public ZIP layout stays unchanged. It also copies them over the build output in `${{ env.BUILD_DIR }}/src/Release`, because `cpack` packages the installer from there rather than from the portable bundle.
|
||||
|
||||
## `windows-installer-v1`
|
||||
|
||||
`windows-installer-v1.xml` signs the NSIS installer that `cpack` produces, `OrcaSlicer_Windows_Installer_<version>_<arch>.exe`. The workflow uploads that single file, so it sits at the root of the artifact ZIP. The installer is built after the binaries are signed, so the files it installs carry the `windows-portable-v1` signatures.
|
||||
|
||||
Both configurations must also be created in the SignPath project under the same slugs; the workflow refers to them only by slug.
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<artifact-configuration xmlns="http://signpath.io/artifact-configuration/v1">
|
||||
<zip-file>
|
||||
<pe-file path="OrcaSlicer_Windows_Installer_*.exe">
|
||||
<authenticode-sign />
|
||||
</pe-file>
|
||||
</zip-file>
|
||||
</artifact-configuration>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<artifact-configuration xmlns="http://signpath.io/artifact-configuration/v1">
|
||||
<zip-file>
|
||||
<pe-file path="orca-slicer.exe">
|
||||
<authenticode-sign />
|
||||
</pe-file>
|
||||
|
||||
<pe-file path="OrcaSlicer.dll">
|
||||
<authenticode-sign />
|
||||
</pe-file>
|
||||
</zip-file>
|
||||
</artifact-configuration>
|
||||
Reference in New Issue
Block a user