Files
Ryan 8b8a42fa35 Windows Binary Signing through DigiCert (#4906)
# Description of Changes

Add EV code signing for Windows CLI binaries using DigiCert KeyLocker.
The workflow now signs `spacetimedb-update.exe`, `spacetimedb-cli.exe`,
and `spacetimedb-standalone.exe` on tag pushes using `smctl sign` with a
cloud HSM-backed certificate.

These changes reflect the updated DigiCert guidance for code signing
through GitHub found here:
https://github.com/marketplace/actions/digicert-binary-signing

# API and ABI breaking changes

No API or ABI changes. This change only affects the CI/CD packaging
workflow.

# Expected complexity level and risk

1 - This PR only adds code signing to existing CI packaging. Risk is
limited to the Windows packaging step failing on tags; Linux and macOS
builds are unaffected.

# Testing

- [X] Tested via workflow dispatch on tag `test-signing-v0.0.1`
- [X] All three executables signed and verified successfully
- [X] Signature verification confirms certificate chain
- [X] Signed artifacts uploaded successfully
2026-04-30 15:43:59 +00:00

154 lines
6.5 KiB
YAML

name: Package SpacetimeDB CLI
on:
push:
tags:
- '**'
workflow_dispatch:
permissions:
contents: read
jobs:
build-cli:
strategy:
fail-fast: false
matrix:
include:
# WARNING - do not upgrade this runner to 24.04 or the self hosted runners because it will break downloads for
# anyone who uses a linux distro that doesn't have glibc >= GLIBC_2.38
- { name: x86_64 Linux, target: x86_64-unknown-linux-gnu, runner: ubuntu-22.04 }
- { name: aarch64 Linux, target: aarch64-unknown-linux-gnu, runner: arm-runner }
# Disabled because musl builds weren't working and we didn't want to investigate. See https://github.com/clockworklabs/SpacetimeDB/pull/2964.
# - { name: x86_64 Linux musl, target: x86_64-unknown-linux-musl, runner: bare-metal, container: alpine }
# FIXME: arm musl build. "JavaScript Actions in Alpine containers are only supported on x64 Linux runners"
# - { name: aarch64 Linux musl, target: aarch64-unknown-linux-musl, runner: arm-runner }
- { name: aarch64 macOS, target: aarch64-apple-darwin, runner: macos-latest }
- { name: x86_64 macOS, target: x86_64-apple-darwin, runner: macos-latest }
- { name: x86_64 Windows, target: x86_64-pc-windows-msvc, runner: spacetimedb-windows-runner }
name: Build CLI for ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Show arch
run: uname -a
- name: Install musl dependencies
# TODO: Should we use `matrix.container == 'alpine'` instead of the `endsWith` check?
if: endsWith(matrix.target, '-musl')
run: apk add gcc g++ bash curl linux-headers perl git make
- name: Install Rust
uses: dsherret/rust-toolchain-file@v1
- name: Set default rust toolchain
run: rustup default $(rustup show active-toolchain | cut -d' ' -f1)
- name: Install rust target
run: rustup target add ${{ matrix.target }}
- name: Add signtool.exe to PATH
if: ${{ runner.os == 'Windows' }}
shell: pwsh
run: |
$root = "${env:ProgramFiles(x86)}\Windows Kits\10\bin"
$signtool = Get-ChildItem $root -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match '\\x64\\signtool\.exe$' } |
Sort-Object FullName -Descending |
Select-Object -First 1
if (-not $signtool) { throw "signtool.exe not found under $root" }
"Found: $($signtool.FullName)"
$dir = Split-Path $signtool.FullName
Add-Content -Path $env:GITHUB_PATH -Value $dir
- name: Decode DigiCert client auth certificate
if: ${{ runner.os == 'Windows' && startsWith(github.ref, 'refs/tags/') }}
shell: bash
env:
SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64 }}
run: |
echo "$SM_CLIENT_CERT_FILE_B64" | base64 --decode > "$RUNNER_TEMP/Certificate_pkcs12.p12"
- name: Setup DigiCert Software Trust Manager
if: ${{ runner.os == 'Windows' && startsWith(github.ref, 'refs/tags/') }}
uses: digicert/code-signing-software-trust-action@v1
env:
SM_HOST: ${{ vars.SM_HOST }}
SM_API_KEY: ${{ secrets.SM_API_KEY }}
SM_CLIENT_CERT_FILE: ${{ runner.temp }}\Certificate_pkcs12.p12
SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }}
- name: Compile
run: |
cargo build --release --target ${{ matrix.target }} -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update
- name: Sign binaries for Windows
if: ${{ runner.os == 'Windows' && startsWith(github.ref, 'refs/tags/') }}
shell: pwsh
env:
SM_HOST: ${{ vars.SM_HOST }}
SM_API_KEY: ${{ secrets.SM_API_KEY }}
SM_CLIENT_CERT_FILE: ${{ runner.temp }}\Certificate_pkcs12.p12
SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }}
DIGICERT_KEYPAIR_ALIAS: ${{ secrets.DIGICERT_KEYPAIR_ALIAS }}
run: |
$ErrorActionPreference = 'Stop'
$targetDir = Join-Path $env:GITHUB_WORKSPACE 'target\x86_64-pc-windows-msvc\release'
foreach ($exe in @('spacetimedb-update.exe','spacetimedb-cli.exe','spacetimedb-standalone.exe')) {
$path = Join-Path $targetDir $exe
Write-Host "Signing $exe..."
& smctl sign --keypair-alias $env:DIGICERT_KEYPAIR_ALIAS --input $path
if ($LASTEXITCODE -ne 0) { throw "Signing failed for $exe (exit code $LASTEXITCODE)" }
Write-Host "$exe signed successfully"
}
- name: Verify signatures
if: ${{ runner.os == 'Windows' && startsWith(github.ref, 'refs/tags/') }}
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$targetDir = Join-Path $env:GITHUB_WORKSPACE 'target\x86_64-pc-windows-msvc\release'
foreach ($exe in @('spacetimedb-update.exe','spacetimedb-cli.exe','spacetimedb-standalone.exe')) {
$path = Join-Path $targetDir $exe
& signtool.exe verify /v /pa $path
if ($LASTEXITCODE -ne 0) { throw "Signature verification failed for $exe" }
}
- name: Package (unix)
if: ${{ runner.os != 'Windows' }}
shell: bash
run: |
mkdir build
cd target/${{matrix.target}}/release
cp spacetimedb-update ../../../build/spacetimedb-update-${{matrix.target}}
tar -czf ../../../build/spacetime-${{matrix.target}}.tar.gz spacetimedb-{cli,standalone}
- name: Package (windows)
if: ${{ runner.os == 'Windows' }}
shell: bash
run: |
mkdir build
cd target/${{matrix.target}}/release
cp spacetimedb-update.exe ../../../build/spacetimedb-update-${{matrix.target}}.exe
7z a ../../../build/spacetime-${{matrix.target}}.zip spacetimedb-cli.exe spacetimedb-standalone.exe
- name: Extract branch name
shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
id: extract_branch
- name: Upload to DO Spaces
uses: shallwefootball/s3-upload-action@master
with:
aws_key_id: ${{ secrets.AWS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
aws_bucket: ${{ vars.AWS_BUCKET }}
source_dir: build
endpoint: https://nyc3.digitaloceanspaces.com
destination_dir: ${{ steps.extract_branch.outputs.branch }}