Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/build-release-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ on:
plan:
required: false
type: string
environment:
description: "GitHub environment for secrets (e.g., code signing certificates)"
required: false
type: string

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -86,6 +90,7 @@ jobs:
macos-x86_64:
name: x86_64-apple-darwin
runs-on: depot-macos-14
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -110,6 +115,10 @@ jobs:
args: --release --locked --out dist --features self-update --compatibility pypi
env:
CARGO: ${{ github.workspace }}/scripts/cargo.sh
CODE_SIGN_IDENTITY: ${{ secrets.CODE_SIGN_IDENTITY }}
CODE_SIGN_CERTIFICATE: ${{ secrets.CODE_SIGN_CERTIFICATE }}
CODE_SIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODE_SIGN_CERTIFICATE_PASSWORD }}
CODE_SIGN_ALLOW_UNTRUSTED: ${{ vars.CODE_SIGN_ALLOW_UNTRUSTED }}
- name: "Upload wheels"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
Expand Down Expand Up @@ -143,6 +152,10 @@ jobs:
args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi
env:
CARGO: ${{ github.workspace }}/scripts/cargo.sh
CODE_SIGN_IDENTITY: ${{ secrets.CODE_SIGN_IDENTITY }}
CODE_SIGN_CERTIFICATE: ${{ secrets.CODE_SIGN_CERTIFICATE }}
CODE_SIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODE_SIGN_CERTIFICATE_PASSWORD }}
CODE_SIGN_ALLOW_UNTRUSTED: ${{ vars.CODE_SIGN_ALLOW_UNTRUSTED }}
- name: "Upload wheels uv-build"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
Expand All @@ -152,6 +165,7 @@ jobs:
macos-aarch64:
name: aarch64-apple-darwin
runs-on: depot-macos-14
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -177,6 +191,10 @@ jobs:
args: --release --locked --out dist --features self-update --compatibility pypi
env:
CARGO: ${{ github.workspace }}/scripts/cargo.sh
CODE_SIGN_IDENTITY: ${{ secrets.CODE_SIGN_IDENTITY }}
CODE_SIGN_CERTIFICATE: ${{ secrets.CODE_SIGN_CERTIFICATE }}
CODE_SIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODE_SIGN_CERTIFICATE_PASSWORD }}
CODE_SIGN_ALLOW_UNTRUSTED: ${{ vars.CODE_SIGN_ALLOW_UNTRUSTED }}
- name: "Test wheel - aarch64"
run: |
pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall
Expand Down Expand Up @@ -216,6 +234,10 @@ jobs:
args: --profile minimal-size --locked --out crates/uv-build/dist -m crates/uv-build/Cargo.toml --compatibility pypi
env:
CARGO: ${{ github.workspace }}/scripts/cargo.sh
CODE_SIGN_IDENTITY: ${{ secrets.CODE_SIGN_IDENTITY }}
CODE_SIGN_CERTIFICATE: ${{ secrets.CODE_SIGN_CERTIFICATE }}
CODE_SIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODE_SIGN_CERTIFICATE_PASSWORD }}
CODE_SIGN_ALLOW_UNTRUSTED: ${{ vars.CODE_SIGN_ALLOW_UNTRUSTED }}
- name: "Test wheel - aarch64"
run: |
pip install ${PACKAGE_NAME}_build --no-index --find-links crates/uv-build/dist --force-reinstall
Expand All @@ -230,6 +252,7 @@ jobs:
windows:
name: ${{ matrix.platform.target }}
runs-on: ${{ matrix.platform.runner }}
environment: ${{ inputs.environment }}
strategy:
matrix:
platform:
Expand All @@ -256,6 +279,33 @@ jobs:
- name: "Install cargo extensions"
shell: bash
run: scripts/install-cargo-extensions.sh
- name: "Prepare Windows signing certificate"
shell: pwsh
run: |
if (-not $env:CODE_SIGN_CERTIFICATE_BASE64) {
Write-Host "No signing certificate configured, skipping."
return
}

$certBytes = [Convert]::FromBase64String($env:CODE_SIGN_CERTIFICATE_BASE64)
$pfxPath = Join-Path $env:RUNNER_TEMP "codesign.pfx"
[IO.File]::WriteAllBytes($pfxPath, $certBytes)

# Find signtool.exe — it's not on PATH on GitHub Actions runners.
$signtool = Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin" -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue |
Sort-Object FullName -Descending |
Select-Object -First 1 -ExpandProperty FullName

if (-not $signtool) {
throw "signtool.exe not found in Windows SDK"
}

"CODE_SIGN_TOOL_PATH=$signtool" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"CODE_SIGN_CERTIFICATE_PATH=$pfxPath" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"CODE_SIGN_CERTIFICATE_PASSWORD=$($env:CODE_SIGN_CERTIFICATE_PASSWORD)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
env:
CODE_SIGN_CERTIFICATE_BASE64: ${{ secrets.CODE_SIGN_CERTIFICATE_BASE64 }}
CODE_SIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODE_SIGN_CERTIFICATE_PASSWORD }}

# uv
- name: "Build wheels"
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ jobs:
needs: plan
if: ${{ needs.plan.outputs.build-release-binaries == 'true' }}
uses: ./.github/workflows/build-release-binaries.yml
with:
environment: release-test
secrets: inherit

build-docker:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ jobs:
uses: ./.github/workflows/build-release-binaries.yml
with:
plan: ${{ needs.plan.outputs.val }}
environment: release
secrets: inherit

custom-build-docker:
Expand Down
5 changes: 5 additions & 0 deletions scripts/cargo-auditable.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@echo off
REM Cargo wrapper that runs `cargo auditable` to embed SBOM metadata.
REM See cargo-auditable.sh for the full explanation.

cargo.exe auditable %*
12 changes: 12 additions & 0 deletions scripts/cargo-auditable.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env sh
## Cargo wrapper that runs `cargo auditable` to embed SBOM metadata.
##
## Used as the inner build command for `cargo-code-sign`.
##
## Usage:
##
## CARGO_CODE_SIGN_CARGO="$PWD/scripts/cargo-auditable.sh" cargo-code-sign code-sign ...

set -eu

exec cargo auditable "$@"
5 changes: 5 additions & 0 deletions scripts/cargo-code-sign.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@echo off
REM Cargo wrapper that signs binaries after building via `cargo-code-sign`.
REM See cargo-code-sign.sh for the full explanation.

cargo-code-sign.exe code-sign %*
14 changes: 14 additions & 0 deletions scripts/cargo-code-sign.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env sh
## Cargo wrapper that signs binaries after building via `cargo-code-sign`.
##
## Uses `CARGO_CODE_SIGN_CARGO` to determine the inner cargo command.
## If unset, falls back to plain `cargo`.
##
## Usage:
##
## CARGO_CODE_SIGN_CARGO="$PWD/scripts/cargo-auditable.sh" \
## scripts/cargo-code-sign.sh build --release

set -eu

exec cargo-code-sign code-sign "$@"
17 changes: 5 additions & 12 deletions scripts/cargo.cmd
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
@echo off
REM Wrapper script that invokes `cargo auditable` instead of plain `cargo`.
REM Top-level cargo wrapper for release builds.
REM
REM Use `scripts/install-cargo-extensions.sh` to install the dependencies.
REM
REM Usage:
REM
REM set CARGO=%CD%\scripts\cargo.cmd
REM cargo build --release
REM Chains `cargo-code-sign` (post-build binary signing) with `cargo-auditable`
REM (SBOM embedding). See cargo.sh for the full explanation.

if defined REAL_CARGO (
"%REAL_CARGO%" auditable %*
) else (
cargo.exe auditable %*
)
set CARGO_CODE_SIGN_CARGO=%~dp0cargo-auditable.cmd
%~dp0cargo-code-sign.cmd %*
20 changes: 13 additions & 7 deletions scripts/cargo.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
#!/usr/bin/env sh
## Wrapper script that invokes `cargo auditable` instead of plain `cargo`.
## Top-level cargo wrapper for release builds.
##
## Chains `cargo-code-sign` (post-build binary signing) with `cargo-auditable`
## (SBOM embedding):
##
## maturin -> cargo.sh -> cargo-code-sign -> cargo-auditable -> cargo
##
## Use `scripts/install-cargo-extensions.sh` to install the dependencies.
##
## Usage:
##
## CARGO="$PWD/scripts/cargo.sh" cargo build --release
## CARGO="$PWD/scripts/cargo.sh" maturin build --release

set -eu

if [ -n "${REAL_CARGO:-}" ]; then
exec "$REAL_CARGO" auditable "$@"
else
exec cargo auditable "$@"
fi
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

# Tell cargo-code-sign to use cargo-auditable as the inner build command.
export CARGO_CODE_SIGN_CARGO="${SCRIPT_DIR}/cargo-auditable.sh"

exec "${SCRIPT_DIR}/cargo-code-sign.sh" "$@"
127 changes: 127 additions & 0 deletions scripts/check-release-artifacts-signed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env bash
## Check that release artifacts from a CI run are code-signed.
##
## Downloads macOS and Windows artifacts and wheels from the given GitHub
## Actions run, extracts binaries, and verifies:
## - macOS: codesign identity signature (not ad-hoc)
## - Windows: Authenticode signature present
##
## Usage:
## scripts/check-release-artifacts-signed.sh <run-id>

set -euo pipefail

if [ $# -lt 1 ]; then
echo "Usage: $0 <run-id>" >&2
exit 1
fi

missing=()
command -v gh >/dev/null 2>&1 || missing+=(gh)
command -v codesign >/dev/null 2>&1 || missing+=(codesign)
command -v osslsigncode >/dev/null 2>&1 || missing+=(osslsigncode)

if [ ${#missing[@]} -gt 0 ]; then
echo "error: missing required tools: ${missing[*]}" >&2
echo "" >&2
echo "Install with:" >&2
for tool in "${missing[@]}"; do
case "$tool" in
gh) echo " brew install gh" >&2 ;;
codesign) echo " (requires macOS)" >&2 ;;
osslsigncode) echo " brew install osslsigncode" >&2 ;;
esac
done
exit 1
fi

RUN_ID="$1"
WORK_DIR="$(mktemp -d)"
trap 'rm -rf "$WORK_DIR"' EXIT

PASS=0
FAIL=0

pass() { echo "PASS $1"; PASS=$((PASS + 1)); }
fail() { echo "FAIL $1"; FAIL=$((FAIL + 1)); }

check_macos() {
local binary="$1"
local label="$2"
local info
info=$(codesign -dv "$binary" 2>&1) || true
if echo "$info" | grep -q "Signature=adhoc"; then
fail "$label (ad-hoc, not identity-signed)"
elif sig_size=$(echo "$info" | grep "Signature size=" | sed 's/.*Signature size=//'); then
pass "$label (identity-signed, size=$sig_size)"
else
fail "$label (not signed)"
fi
}

check_windows() {
local binary="$1"
local label="$2"
local output
output=$(osslsigncode verify -in "$binary" 2>&1) || true
if echo "$output" | grep -q "Signer's certificate:"; then
local subject
subject=$(echo "$output" | grep "Subject:" | head -1 | sed 's/.*Subject: //')
pass "$label (Authenticode, $subject)"
else
fail "$label (not Authenticode signed)"
fi
}

echo "Fetching artifacts for run $RUN_ID..."
ALL_ARTIFACTS=$(gh api "repos/{owner}/{repo}/actions/runs/$RUN_ID/artifacts" \
--paginate --jq '.artifacts[].name')

echo ""

for artifact in $ALL_ARTIFACTS; do
# Only check macOS and Windows archives and wheels.
case "$artifact" in
artifacts-*apple-darwin*|artifacts-macos-*) check=check_macos ;;
artifacts-*windows*|artifacts-*win*) check=check_windows ;;
wheels_uv-*apple-darwin*|wheels_uv-macos-*) check=check_macos ;;
wheels_uv-*windows*|wheels_uv-*win*) check=check_windows ;;
*) continue ;;
esac

dest="$WORK_DIR/$artifact"
mkdir -p "$dest"
if ! gh run download "$RUN_ID" -n "$artifact" -D "$dest"; then
fail "$artifact (download failed)"
continue
fi

# Extract everything: tar.gz archives, zip archives, and wheels.
for tarball in "$dest"/*.tar.gz; do
[ -f "$tarball" ] || continue
tar xzf "$tarball" -C "$dest"
done
for zip in "$dest"/*.zip "$dest"/*.whl; do
[ -f "$zip" ] || continue
unzip -qo "$zip" -d "$dest"
done

# Check each binary. The label shows the archive/wheel filename and binary name,
# e.g. "uv-x86_64-apple-darwin.tar.gz uv" or "uv-0.10.8-py3-none-win_amd64.whl uv.exe".
while IFS= read -r binary; do
bin_name=$(basename "$binary")
# Walk up to find the archive or wheel this binary came from.
archive=""
for f in "$dest"/*.tar.gz "$dest"/*.zip "$dest"/*.whl; do
[ -f "$f" ] && archive=$(basename "$f") && break
done
$check "$binary" "${archive:-$artifact} / $bin_name"
done < <(find "$dest" \( -name "uv" -o -name "uvx" -o -name "uv.exe" -o -name "uvx.exe" -o -name "uvw.exe" \) -type f ! -name "*.sha256")
done

echo ""
echo "PASS $PASS / FAIL $FAIL"

if [ "$FAIL" -gt 0 ]; then
exit 1
fi
Loading
Loading