Skip to content

Add Windows binary signing with Azure Trusted Signing to CI workflow#106

Draft
Amund211 with Copilot wants to merge 2 commits into
mainfrom
copilot/add-signing-to-windows-binary
Draft

Add Windows binary signing with Azure Trusted Signing to CI workflow#106
Amund211 with Copilot wants to merge 2 commits into
mainfrom
copilot/add-signing-to-windows-binary

Conversation

Copilot AI commented Jan 14, 2026

Copy link
Copy Markdown
Contributor

Enables code signing for Windows executables in the testing workflow using Azure's Trusted Signing service with OIDC authentication.

Changes

  • Added OIDC permission: id-token: write for Azure authentication
  • Fixed expression syntax: startswithstartsWith (GitHub Actions requirement)
  • Added Azure login step: OIDC-based authentication for Windows builds only
  • Added signing step: Signs Windows EXE using Azure Trusted Signing with SHA256 digests and RFC3161 timestamps

The signing occurs after binary creation and before artifact upload, ensuring only signed executables are distributed.

- name: Azure login (windows)
  if: env.OS_NAME == 'windows'
  uses: azure/login@v2
  with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Sign Windows EXE (windows)
  if: env.OS_NAME == 'windows'
  uses: Azure/trusted-signing-action@v0
  with:
    endpoint: ${{ secrets.AZURE_CODESIGNING_ENDPOINT }}
    files: ${{ env.BUILD_RESULT_NAME }}
    file-digest: SHA256
    timestamp-rfc3161: http://timestamp.acs.microsoft.com

Requires Azure secrets to be configured: AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_SUBSCRIPTION_ID, AZURE_CODESIGNING_ENDPOINT, AZURE_CODESIGNING_ACCOUNT_NAME, and AZURE_CODESIGNING_CERT_PROFILE_NAME.

Original prompt

Add this code to the testing workflow to enable signing the windows binary

name: Python testing

permissions:
contents: read
id-token: write # needed for Azure OIDC login

on: [push, workflow_dispatch]

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macOS-15]
defaults:
run:
shell: bash # For source
steps:
- uses: actions/checkout@v6

- name: Set up Python
  uses: actions/setup-python@v6
  with:
    python-version: "3.13"

- name: Install uv
  uses: astral-sh/setup-uv@v7

- name: OS specific setup (windows)
  if: startsWith(matrix.os, 'windows')
  run: |
    echo 'OS_NAME=windows' >> $GITHUB_ENV
    echo 'RESULT_EXTENSION=.exe' >> $GITHUB_ENV
    echo 'PYINSTALLER_ARGS=--hide-console=minimize-early --onefile' >> $GITHUB_ENV
    echo 'INSTALL_CA_CERT=certutil.exe -addstore root local-ca.pem' >> $GITHUB_ENV

- name: OS specific setup (linux)
  if: startsWith(matrix.os, 'ubuntu')
  run: |
    echo 'OS_NAME=linux' >> $GITHUB_ENV
    echo 'RESULT_EXTENSION=' >> $GITHUB_ENV
    echo 'PYINSTALLER_ARGS=--onefile' >> $GITHUB_ENV
    echo 'INSTALL_CA_CERT=sudo cp local-ca.pem /usr/local/share/ca-certificates/local-ca.crt; sudo update-ca-certificates' >> $GITHUB_ENV

- name: OS specific setup (mac)
  if: startsWith(matrix.os, 'macOS')
  run: |
    echo 'OS_NAME=mac' >> $GITHUB_ENV
    # Upload the built .dmg app-installer
    echo 'RESULT_EXTENSION=.dmg' >> $GITHUB_ENV
    # Target universal2 so that the built binary can run on both intel and apple silicon
    # Pass --windowed to build a .app bundle
    # Use --onedir instead of --onefile to avoid repeated security scans on startup
    echo 'PYINSTALLER_ARGS=--target-architecture universal2 --windowed --onedir' >> $GITHUB_ENV
    echo 'INSTALL_CA_CERT=sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain local-ca.pem' >> $GITHUB_ENV

- name: Install dependencies
  run: |
    uv sync

    VERSION_STRING="$(uv run python -c 'from prism import VERSION_STRING; print(VERSION_STRING)')"
    echo "VERSION_STRING=$VERSION_STRING" >> $GITHUB_ENV
    echo "SIMPLE_NAME=prism-$VERSION_STRING" >> $GITHUB_ENV
    # NOTE: removed the non-breaking space before env.OS_NAME
    echo "BUILD_RESULT_NAME=prism-$VERSION_STRING-${{ env.OS_NAME }}${{ env.RESULT_EXTENSION }}" >> $GITHUB_ENV

- name: Run typechecking
  run: |
    uv run mypy --strict .

- name: Run tests
  run: |
    uv run coverage run
    uv run coverage report

- name: tee | stderr
  if: env.OS_NAME != 'windows'
  run: |
    echo 'TEE_STDERR=| tee /dev/stderr' >> $GITHUB_ENV

- name: Test ssl system certs
  run: |
    certifi_ca_certs="$(uv run python -m certifi)"

    (
      cd tests/system_certs

      echo 'Print certs' >&2
      sh print.sh

      echo "Run local https server and ensure it's running" >&2
      uv run server.py >/dev/null 2>&1 &
      sleep 1

      echo 'Ensure request succeeds with local ca' >&2
      curl --ssl-no-revoke --fail --silent https://localhost:12345 --cacert local-ca.pem ${{ env.TEE_STDERR }} | grep '^Hello world' >/dev/null 2>&1

      echo 'Ensure request fails with certifi ca' >&2
      ! curl --ssl-no-revoke --fail https://localhost:12345 --cacert "$certifi_ca_certs" 2>&1 ${{ env.TEE_STDERR }} | grep 'SSL certificate problem' >/dev/null 2>&1

      echo 'Install certs' >&2
      ${{ env.INSTALL_CA_CERT }}
    )

    # Create placeholder logfile
    touch latest.log

    echo 'Ensure ssl error is caught when using included certs' >&2
    echo 'use_included_certs = true' > included_certs_settings.toml
    # Don't open the settings prompt
    echo 'autowho = true' >> included_certs_settings.toml

    uv run prism_overlay.py --test-ssl --settings=included_certs_settings.toml --logfile=latest.log ${{ env.TEE_STDERR }} | grep '^Caught missing local issuer SSLError: ' >/dev/null 2>&1

    echo 'Ensure request succeeds when using system certs' >&2
    echo 'use_included_certs = false' > system_certs_settings.toml
    # Don't open the settings prompt
    echo 'autowho = true' >> system_certs_settings.toml

    uv run prism_overlay.py --test-ssl --settings=system_certs_settings.toml --logfile=latest.log ${{ env.TEE_STDERR }} | grep '^Got response: Hello world' >/dev/null 2>&1

- name: Build binary with pyinstaller
  run: |
    uv run add_version_to_icon.py  # Create the icon file
    uv run pyinstaller prism_overlay.py --noconfirm --icon=pyinstaller/who_with_version.ico --name "${{ env.SIMPLE_NAME }}" --additional-hooks-dir=pyinstaller ${{ env.PYINSTALLER_ARGS }}

- name: Prepare to package app bundle to di...

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

- Add id-token: write permission for Azure OIDC login
- Fix startswith to startsWith (correct case)
- Add Azure login step for Windows signing
- Add Windows EXE signing step with Azure Trusted Signing

Co-authored-by: Amund211 <14028449+Amund211@users.noreply.github.com>
Copilot AI changed the title [WIP] Add code to enable signing the windows binary in testing workflow Add Windows binary signing with Azure Trusted Signing to CI workflow Jan 14, 2026
Copilot AI requested a review from Amund211 January 14, 2026 22:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants