From d6a59613662138a8394fefe406eac1670c5f201a Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 15 Oct 2026 01:11:00 +0800 Subject: [PATCH] Sign the Windows installer and the binaries it installs --- .github/workflows/build_orca.yml | 48 ++++++++++++++++++++++++++++++- signpath/README.md | 10 +++++-- signpath/windows-installer-v1.xml | 8 ++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 signpath/windows-installer-v1.xml diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index 6756160064..1948653473 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -534,7 +534,7 @@ jobs: # 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 portable bundle with signed output + - 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: | @@ -545,6 +545,16 @@ jobs: } 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 @@ -553,6 +563,42 @@ 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 }} diff --git a/signpath/README.md b/signpath/README.md index 6adac07dd2..91def052ee 100644 --- a/signpath/README.md +++ b/signpath/README.md @@ -11,6 +11,12 @@ This directory contains SignPath artifact configurations used by GitHub Actions. 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 `${{ github.workspace }}/build/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 `` with `orca-slicer.exe` and `OrcaSlicer.dll` directly beneath it. +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 `` with `orca-slicer.exe` and `OrcaSlicer.dll` directly beneath it. -The release portable ZIP is a separate archive created with 7-Zip from `${{ github.workspace }}/build/OrcaSlicer`; that archive keeps the top-level `OrcaSlicer/` folder. After SignPath returns the signed artifact, the workflow copies the signed files back into `build/OrcaSlicer` and recreates the portable release ZIP so the public ZIP layout stays unchanged. +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__.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. diff --git a/signpath/windows-installer-v1.xml b/signpath/windows-installer-v1.xml new file mode 100644 index 0000000000..ec70556116 --- /dev/null +++ b/signpath/windows-installer-v1.xml @@ -0,0 +1,8 @@ + + + + + + + +