diff --git a/.github/workflows/build-release-binaries.yml b/.github/workflows/build-release-binaries.yml index 6f35245c71106..e730b37f8d0b9 100644 --- a/.github/workflows/build-release-binaries.yml +++ b/.github/workflows/build-release-binaries.yml @@ -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 }} @@ -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: @@ -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" @@ -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: @@ -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: @@ -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: @@ -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" @@ -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 @@ -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 @@ -310,6 +252,7 @@ jobs: windows: name: ${{ matrix.platform.target }} runs-on: ${{ matrix.platform.runner }} + environment: ${{ inputs.environment }} strategy: matrix: platform: @@ -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 | @@ -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" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81528ccb69ac2..e96f0dabb522e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 334840df1af56..0621f8cb4d41d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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: diff --git a/scripts/generate-codesign-test-secrets.sh b/scripts/generate-codesign-test-secrets.sh new file mode 100755 index 0000000000000..03fff20fcab47 --- /dev/null +++ b/scripts/generate-codesign-test-secrets.sh @@ -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 \ + -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" diff --git a/scripts/install-cargo-extensions.sh b/scripts/install-cargo-extensions.sh index 3dca708d9c44e..b3a2cddf5bfd2 100755 --- a/scripts/install-cargo-extensions.sh +++ b/scripts/install-cargo-extensions.sh @@ -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.).