Skip to content
Merged
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
133 changes: 37 additions & 96 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 @@ -100,46 +105,6 @@ jobs:
- name: "Install cargo extensions"
shell: bash
run: scripts/install-cargo-extensions.sh
- name: "Prepare macOS signing certificate"
run: |
set -euo pipefail

CERT_NAME="uv-codesign-ci"
CERT_DIR="$RUNNER_TEMP/codesign-cert"
mkdir -p "$CERT_DIR"

openssl req -x509 -newkey rsa:2048 -sha256 -days 7 -nodes \
-keyout "$CERT_DIR/key.pem" \
-out "$CERT_DIR/cert.pem" \
-subj "/CN=$CERT_NAME" \
-addext "extendedKeyUsage=codeSigning" \
-addext "keyUsage=digitalSignature"

P12_PASSWORD="$(uuidgen | tr -d '-')"

# LibreSSL (shipped with macOS) doesn't support -legacy; OpenSSL 3.x
# requires it for macOS keychain compatibility.
LEGACY_FLAG=""
if openssl version 2>&1 | grep -q "^OpenSSL 3"; then
LEGACY_FLAG="-legacy"
fi

openssl pkcs12 -export $LEGACY_FLAG \
-inkey "$CERT_DIR/key.pem" \
-in "$CERT_DIR/cert.pem" \
-name "$CERT_NAME" \
-out "$CERT_DIR/cert.p12" \
-passout pass:"$P12_PASSWORD"

CERT_B64="$(base64 < "$CERT_DIR/cert.p12" | tr -d '\n')"
CERT_SHA1="$(openssl x509 -in "$CERT_DIR/cert.pem" -noout -fingerprint -sha1 | cut -d= -f2 | tr -d ':')"

{
echo "CODESIGN_IDENTITY=$CERT_SHA1"
echo "CODESIGN_CERTIFICATE=$CERT_B64"
echo "CODESIGN_CERTIFICATE_PASSWORD=$P12_PASSWORD"
echo "CODESIGN_ALLOW_UNTRUSTED=1"
} >> "$GITHUB_ENV"

# uv
- name: "Build wheels - x86_64"
Expand All @@ -150,6 +115,10 @@ jobs:
args: --release --locked --out dist --features self-update --compatibility pypi
env:
CARGO: ${{ github.workspace }}/scripts/cargo.sh
CODESIGN_IDENTITY_MACOS: ${{ secrets.CODESIGN_IDENTITY_MACOS }}
CODESIGN_CERTIFICATE_MACOS: ${{ secrets.CODESIGN_CERTIFICATE_MACOS }}
CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODESIGN_CERTIFICATE_PASSWORD }}
CODESIGN_ALLOW_UNTRUSTED_MACOS: ${{ vars.CODESIGN_ALLOW_UNTRUSTED_MACOS }}
- name: "Upload wheels"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
Expand Down Expand Up @@ -183,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
CODESIGN_IDENTITY_MACOS: ${{ secrets.CODESIGN_IDENTITY_MACOS }}
CODESIGN_CERTIFICATE_MACOS: ${{ secrets.CODESIGN_CERTIFICATE_MACOS }}
CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODESIGN_CERTIFICATE_PASSWORD }}
CODESIGN_ALLOW_UNTRUSTED_MACOS: ${{ vars.CODESIGN_ALLOW_UNTRUSTED_MACOS }}
- name: "Upload wheels uv-build"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
Expand All @@ -192,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 @@ -206,46 +180,6 @@ jobs:
- name: "Install cargo extensions"
shell: bash
run: scripts/install-cargo-extensions.sh
- name: "Prepare macOS signing certificate"
run: |
set -euo pipefail

CERT_NAME="uv-codesign-ci"
CERT_DIR="$RUNNER_TEMP/codesign-cert"
mkdir -p "$CERT_DIR"

openssl req -x509 -newkey rsa:2048 -sha256 -days 7 -nodes \
-keyout "$CERT_DIR/key.pem" \
-out "$CERT_DIR/cert.pem" \
-subj "/CN=$CERT_NAME" \
-addext "extendedKeyUsage=codeSigning" \
-addext "keyUsage=digitalSignature"

P12_PASSWORD="$(uuidgen | tr -d '-')"

# LibreSSL (shipped with macOS) doesn't support -legacy; OpenSSL 3.x
# requires it for macOS keychain compatibility.
LEGACY_FLAG=""
if openssl version 2>&1 | grep -q "^OpenSSL 3"; then
LEGACY_FLAG="-legacy"
fi

openssl pkcs12 -export $LEGACY_FLAG \
-inkey "$CERT_DIR/key.pem" \
-in "$CERT_DIR/cert.pem" \
-name "$CERT_NAME" \
-out "$CERT_DIR/cert.p12" \
-passout pass:"$P12_PASSWORD"

CERT_B64="$(base64 < "$CERT_DIR/cert.p12" | tr -d '\n')"
CERT_SHA1="$(openssl x509 -in "$CERT_DIR/cert.pem" -noout -fingerprint -sha1 | cut -d= -f2 | tr -d ':')"

{
echo "CODESIGN_IDENTITY=$CERT_SHA1"
echo "CODESIGN_CERTIFICATE=$CERT_B64"
echo "CODESIGN_CERTIFICATE_PASSWORD=$P12_PASSWORD"
echo "CODESIGN_ALLOW_UNTRUSTED=1"
} >> "$GITHUB_ENV"

# uv
- name: "Build wheels - aarch64"
Expand All @@ -257,6 +191,10 @@ jobs:
args: --release --locked --out dist --features self-update --compatibility pypi
env:
CARGO: ${{ github.workspace }}/scripts/cargo.sh
CODESIGN_IDENTITY_MACOS: ${{ secrets.CODESIGN_IDENTITY_MACOS }}
CODESIGN_CERTIFICATE_MACOS: ${{ secrets.CODESIGN_CERTIFICATE_MACOS }}
CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODESIGN_CERTIFICATE_PASSWORD }}
CODESIGN_ALLOW_UNTRUSTED_MACOS: ${{ vars.CODESIGN_ALLOW_UNTRUSTED_MACOS }}
- name: "Test wheel - aarch64"
run: |
pip install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall
Expand Down Expand Up @@ -296,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
CODESIGN_IDENTITY_MACOS: ${{ secrets.CODESIGN_IDENTITY_MACOS }}
CODESIGN_CERTIFICATE_MACOS: ${{ secrets.CODESIGN_CERTIFICATE_MACOS }}
CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODESIGN_CERTIFICATE_PASSWORD }}
CODESIGN_ALLOW_UNTRUSTED_MACOS: ${{ vars.CODESIGN_ALLOW_UNTRUSTED_MACOS }}
- name: "Test wheel - aarch64"
run: |
pip install ${PACKAGE_NAME}_build --no-index --find-links crates/uv-build/dist --force-reinstall
Expand All @@ -310,6 +252,7 @@ jobs:
windows:
name: ${{ matrix.platform.target }}
runs-on: ${{ matrix.platform.runner }}
environment: ${{ inputs.environment }}
strategy:
matrix:
platform:
Expand Down Expand Up @@ -339,19 +282,14 @@ jobs:
- name: "Prepare Windows signing certificate"
shell: pwsh
run: |
$cert = New-SelfSignedCertificate `
-Type CodeSigningCert `
-Subject "CN=uv-codesign-ci" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-NotAfter (Get-Date).AddDays(7)
$passwordPlain = [Guid]::NewGuid().ToString("N")
$password = ConvertTo-SecureString -String $passwordPlain -Force -AsPlainText
$pfxPath = Join-Path $env:RUNNER_TEMP "uv-codesign-ci.pfx"

Export-PfxCertificate `
-Cert "Cert:\CurrentUser\My\$($cert.Thumbprint)" `
-FilePath $pfxPath `
-Password $password | Out-Null
if (-not $env:CODESIGN_CERTIFICATE_WINDOWS) {
Write-Host "No signing certificate configured, skipping."
return
}

$certBytes = [Convert]::FromBase64String($env:CODESIGN_CERTIFICATE_WINDOWS)
$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 |
Expand All @@ -362,9 +300,12 @@ jobs:
throw "signtool.exe not found in Windows SDK"
}

"SIGNTOOL_PATH=$signtool" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"SIGNTOOL_CERTIFICATE_PATH=$pfxPath" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"SIGNTOOL_CERTIFICATE_PASSWORD=$passwordPlain" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"CODESIGN_TOOL_PATH_WINDOWS=$signtool" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"CODESIGN_CERTIFICATE_PATH_WINDOWS=$pfxPath" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"CODESIGN_CERTIFICATE_PASSWORD=$($env:CODESIGN_CERTIFICATE_PASSWORD)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
env:
CODESIGN_CERTIFICATE_WINDOWS: ${{ secrets.CODESIGN_CERTIFICATE_WINDOWS }}
CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODESIGN_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
92 changes: 92 additions & 0 deletions scripts/generate-codesign-test-secrets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env bash
## Generate a self-signed code signing certificate and populate a GitHub
## environment with the resulting secrets and variables via the `gh` CLI.
##
## Secrets: CODESIGN_CERTIFICATE_PASSWORD, CODESIGN_IDENTITY_MACOS,
## CODESIGN_CERTIFICATE_MACOS, CODESIGN_CERTIFICATE_WINDOWS
## Variables: CODESIGN_ALLOW_UNTRUSTED_MACOS
##
## Usage:
##
## scripts/generate-codesign-test-secrets.sh

set -euo pipefail

if ! command -v gh &>/dev/null; then
echo "error: gh CLI is required but not found. Install from https://cli.github.com" >&2
exit 1
fi

REPO="astral-sh/uv"
ENV_NAME="release-test"

echo "Generating self-signed code signing certificate..."

CERT_DIR="$(mktemp -d)"
trap 'rm -rf "$CERT_DIR"' EXIT

CERT_NAME="uv-codesign-test"
P12_PASSWORD="$(uuidgen | tr -d '-')"

# ---------------------------------------------------------------------------
# Generate a self-signed code-signing certificate as a PKCS#12 / PFX.
# The same file is used for both macOS (.p12) and Windows (.pfx) — they are
# the same format.
# ---------------------------------------------------------------------------

openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes \
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-keyout "$CERT_DIR/key.pem" \
-out "$CERT_DIR/cert.pem" \
-subj "/CN=$CERT_NAME" \
-addext "extendedKeyUsage=codeSigning" \
-addext "keyUsage=digitalSignature" \
2>/dev/null

# Detect whether we need -legacy (OpenSSL 3.x requires it for macOS keychain
# compatibility; LibreSSL shipped with macOS does not support it).
LEGACY_FLAG=""
if openssl version 2>&1 | grep -q "^OpenSSL 3"; then
LEGACY_FLAG="-legacy"
fi

# shellcheck disable=SC2086
openssl pkcs12 -export $LEGACY_FLAG \
-inkey "$CERT_DIR/key.pem" \
-in "$CERT_DIR/cert.pem" \
-name "$CERT_NAME" \
-out "$CERT_DIR/cert.p12" \
-passout pass:"$P12_PASSWORD" \
2>/dev/null

CERT_B64="$(base64 < "$CERT_DIR/cert.p12" | tr -d '\n')"
CERT_SHA1="$(openssl x509 -in "$CERT_DIR/cert.pem" -noout -fingerprint -sha1 \
| cut -d= -f2 | tr -d ':')"

# ---------------------------------------------------------------------------
# Populate the GitHub environment.
# ---------------------------------------------------------------------------

echo "Setting secrets and variables in '${ENV_NAME}' environment for ${REPO}..."

gh secret set CODESIGN_CERTIFICATE_PASSWORD \
--repo "$REPO" --env "$ENV_NAME" --body "$P12_PASSWORD"

gh secret set CODESIGN_IDENTITY_MACOS \
--repo "$REPO" --env "$ENV_NAME" --body "$CERT_SHA1"

gh secret set CODESIGN_CERTIFICATE_MACOS \
--repo "$REPO" --env "$ENV_NAME" --body "$CERT_B64"

gh secret set CODESIGN_CERTIFICATE_WINDOWS \
--repo "$REPO" --env "$ENV_NAME" --body "$CERT_B64"

gh variable set CODESIGN_ALLOW_UNTRUSTED_MACOS \
--repo "$REPO" --env "$ENV_NAME" --body "1"

echo ""
echo "Done. Set in '${ENV_NAME}' environment for ${REPO}:"
echo " CODESIGN_CERTIFICATE_PASSWORD"
echo " CODESIGN_IDENTITY_MACOS"
echo " CODESIGN_CERTIFICATE_MACOS"
echo " CODESIGN_CERTIFICATE_WINDOWS"
echo " CODESIGN_ALLOW_UNTRUSTED_MACOS"
2 changes: 1 addition & 1 deletion scripts/install-cargo-extensions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ CARGO_AUDITABLE_INSTALL="cargo install cargo-auditable \
CARGO_CODE_SIGN_INSTALL="cargo install cargo-code-sign \
--locked \
--git https://github.com/zanieb/cargo-code-sign \
--rev 3448dce9525127604dc65db1dc2a5f4b67f214b6"
--rev 5d3dea1e1f4319a37dfa18d8018703a04050a561"

# In Linux containers running on x86_64, build a static musl binary so the installed tool works in
# musl-based environments (Alpine, etc.).
Expand Down
Loading