python/cpython

23 workflows · maturity 83% · 14 patterns · GitHub ↗

Security 22.8/100

Security dimensions

permissions
12
security scan
4.2
supply chain
6.7
secret handling
0
harden runner
0

Tools: github/codeql-action/upload-sarif

Workflows (23)

add-issue-header .github/workflows/add-issue-header.yml
Triggers
issues
Runs on
ubuntu-latest
Jobs
add-header
View raw YAML
name: Add issue header
# Automatically edits an issue's descriptions with a header,
# one of:
#
# - Bug report
# - Crash report
# - Feature or enhancement

on:
  issues:
    types:
      # Only ever run once
      - opened


jobs:
  add-header:
    runs-on: ubuntu-latest
    permissions:
      issues: write
    timeout-minutes: 5
    steps:
      - uses: actions/github-script@v8
        with:
          # language=JavaScript
          script: |
            // https://devguide.python.org/triage/labels/#type-labels
            const HEADERS = new Map([
              ['type-bug', 'Bug report'],
              ['type-crash', 'Crash report'],
              ['type-feature', 'Feature or enhancement'],
            ]);
            let issue_data = await github.rest.issues.get({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo
            }).then(issue => issue.data);
            let header = '';
            for (const label_data of issue_data.labels) {
                const label_name = (typeof label_data === 'string') ? label_data : label_data.name;
                if (HEADERS.has(label_name)) {
                    header = HEADERS.get(label_name);
                    break;
                }
            }
            if (header !== '') {
              console.log(`Setting new header: ${header}`);
              await github.rest.issues.update({
                  issue_number: context.issue.number,
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  body: `# ${header}\n\n${issue_data.body.replaceAll('\r', '')}`
              });
            }
build matrix perms .github/workflows/build.yml
Triggers
workflow_dispatch, push, pull_request
Runs on
ubuntu-24.04, ubuntu-24.04, ${{ matrix.os }}, ${{ matrix.runs-on }}, macos-14, ubuntu-24.04, ${{ matrix.os }}, ubuntu-latest, ubuntu-latest
Jobs
build-context, check-docs, check-autoconf-regen, check-generated-files, check-c-api-docs, build-windows, build-windows-msi, build-macos, build-ubuntu, build-ubuntu-ssltests, build-android, build-ios, build-emscripten, build-wasi, test-hypothesis, build-asan, build-san, cross-build-linux, cifuzz, all-required-green
Matrix
arch, bolt, check-name, exclude, exclude.arch, exclude.bolt, exclude.free-threading, exclude.os, exclude.oss-fuzz-project-name, free-threading, include, include.arch, include.bolt, include.check-name, include.free-threading, include.os, include.oss-fuzz-project-name, include.runs-on, include.sanitizer, include.test-opts, os, oss-fuzz-project-name, sanitizer, ssllib, ssllib.name, ssllib.version→ ${{ needs.build-context.outputs.run-ci-fuzz == 'true' && 'no-exclude' || 'cpython3' }}, ${{ needs.build-context.outputs.run-ci-fuzz-stdlib == 'true' && 'no-exclude' || 'python3-libraries' }}, -u cpu, 1.1.1w, 1.68.0, 3.0.19, 3.3.6, 3.4.4, 3.5.5, 3.6.1, False, TSan, Thread, True, UBSan, Undefined behavior, Win32, aarch64, address, arm64, aws-lc, cpython3, macos-26, macos-26-intel, memory, openssl, python3-libraries, ubuntu-24.04, ubuntu-24.04-arm, undefined, x64, x86, x86_64
Actions
egor-tensin/setup-gcc, re-actors/alls-green
Commands
  • apt update && apt install git -yq git config --global --add safe.directory "$GITHUB_WORKSPACE"
  • grep "Generated by GNU Autoconf 2.72" configure grep "aclocal 1.16.5" aclocal.m4 grep -q "runstatedir" configure grep -q "PKG_PROG_PKG_CONFIG" aclocal.m4
  • autoreconf -ivf -Werror
  • git add -u changes=$(git status --porcelain) # Check for changes in regenerated files if test -n "$changes"; then echo "Generated files not up to date." echo "Perhaps you forgot to run make regen-configure ;)" echo "configure files must be regenerated with a specific version of autoconf." echo "$changes" echo "" git diff --staged || true exit 1 fi
  • echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
  • sudo ./.github/workflows/posix-deps-apt.sh
  • # Build Python with the libpython dynamic library ./configure --config-cache --with-pydebug --enable-shared
  • make -j4 regen-all make regen-stdlib-module-names regen-sbom
View raw YAML
name: Tests

on:
  workflow_dispatch:
  push:
    branches:
    - 'main'
    - '3.*'
  pull_request:
    branches:
    - 'main'
    - '3.*'

permissions:
  contents: read

concurrency:
  # https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#concurrency
  # 'group' must be a key uniquely representing a PR or push event.
  # github.workflow is the workflow name
  # github.actor is the user invoking the workflow
  # github.head_ref is the source branch of the PR or otherwise blank
  # github.run_id is a unique number for the current run
  group: ${{ github.workflow }}-${{ github.actor }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

env:
  FORCE_COLOR: 1

jobs:
  build-context:
    name: Change detection
    # To use boolean outputs from this job, parse them as JSON.
    # Here's some examples:
    #
    #   if: fromJSON(needs.build-context.outputs.run-docs)
    #
    #   ${{
    #        fromJSON(needs.build-context.outputs.run-tests)
    #        && 'truthy-branch'
    #        || 'falsy-branch'
    #   }}
    #
    uses: ./.github/workflows/reusable-context.yml

  check-docs:
    name: Docs
    needs: build-context
    if: fromJSON(needs.build-context.outputs.run-docs)
    uses: ./.github/workflows/reusable-docs.yml

  check-autoconf-regen:
    name: 'Check if Autoconf files are up to date'
    # Don't use ubuntu-latest but a specific version to make the job
    # reproducible: to get the same tools versions (autoconf, aclocal, ...)
    runs-on: ubuntu-24.04
    container:
      image: ghcr.io/python/autoconf:2025.01.02.12581854023
    timeout-minutes: 60
    needs: build-context
    if: needs.build-context.outputs.run-tests == 'true'
    steps:
      - name: Install Git
        run: |
          apt update && apt install git -yq
          git config --global --add safe.directory "$GITHUB_WORKSPACE"
      - uses: actions/checkout@v6
        with:
          fetch-depth: 1
          persist-credentials: false
      - name: Check Autoconf and aclocal versions
        run: |
          grep "Generated by GNU Autoconf 2.72" configure
          grep "aclocal 1.16.5" aclocal.m4
          grep -q "runstatedir" configure
          grep -q "PKG_PROG_PKG_CONFIG" aclocal.m4
      - name: Regenerate autoconf files
        # Same command used by Tools/build/regen-configure.sh ($AUTORECONF)
        run: autoreconf -ivf -Werror
      - name: Check for changes
        run: |
          git add -u
          changes=$(git status --porcelain)
          # Check for changes in regenerated files
          if test -n "$changes"; then
            echo "Generated files not up to date."
            echo "Perhaps you forgot to run make regen-configure ;)"
            echo "configure files must be regenerated with a specific version of autoconf."
            echo "$changes"
            echo ""
            git diff --staged || true
            exit 1
          fi

  check-generated-files:
    name: 'Check if generated files are up to date'
    # Don't use ubuntu-latest but a specific version to make the job
    # reproducible: to get the same tools versions (autoconf, aclocal, ...)
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    needs: build-context
    if: needs.build-context.outputs.run-tests == 'true'
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3.x'
      - name: Runner image version
        run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
      - name: Install dependencies
        run: sudo ./.github/workflows/posix-deps-apt.sh
      - name: Configure CPython
        run: |
          # Build Python with the libpython dynamic library
          ./configure --config-cache --with-pydebug --enable-shared
      - name: Build CPython
        run: |
          make -j4 regen-all
          make regen-stdlib-module-names regen-sbom
      - name: Check for changes
        run: |
          git add -u
          changes=$(git status --porcelain)
          # Check for changes in regenerated files
          if test -n "$changes"; then
            echo "Generated files not up to date."
            echo "Perhaps you forgot to run make regen-all or build.bat --regen. ;)"
            echo "configure files must be regenerated with a specific version of autoconf."
            echo "$changes"
            echo ""
            git diff --staged || true
            exit 1
          fi
      - name: Check exported libpython symbols
        run: make smelly
      - name: Check limited ABI symbols
        run: make check-limited-abi
      - name: Check for unsupported C global variables
        if: github.event_name == 'pull_request'  # $GITHUB_EVENT_NAME
        run: make check-c-globals

  check-c-api-docs:
    name: C API Docs
    needs: build-context
    if: >-
      needs.build-context.outputs.run-tests == 'true'
      || needs.build-context.outputs.run-docs == 'true'
    uses: ./.github/workflows/reusable-check-c-api-docs.yml

  build-windows:
    name: >-
      Windows
      ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
    needs: build-context
    if: fromJSON(needs.build-context.outputs.run-windows-tests)
    strategy:
      fail-fast: false
      matrix:
        arch:
          - x64
          - Win32
          - arm64
        free-threading:
          - false
          - true
        exclude:
          # Skip Win32 on free-threaded builds
          - { arch: Win32, free-threading: true }
    uses: ./.github/workflows/reusable-windows.yml
    with:
      arch: ${{ matrix.arch }}
      free-threading: ${{ matrix.free-threading }}

  build-windows-msi:
    # ${{ '' } is a hack to nest jobs under the same sidebar category.
    name: Windows MSI${{ '' }}  # zizmor: ignore[obfuscation]
    needs: build-context
    if: fromJSON(needs.build-context.outputs.run-windows-msi)
    strategy:
      fail-fast: false
      matrix:
        arch:
        - x86
        - x64
        - arm64
    uses: ./.github/workflows/reusable-windows-msi.yml
    with:
      arch: ${{ matrix.arch }}

  build-macos:
    name: >-
      macOS
      ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
    needs: build-context
    if: needs.build-context.outputs.run-macos == 'true'
    strategy:
      fail-fast: false
      matrix:
        # macos-26 is Apple Silicon, macos-26-intel is Intel.
        # macos-26-intel only runs tests against the GIL-enabled CPython.
        os:
        - macos-26
        - macos-26-intel
        free-threading:
        - false
        - true
        exclude:
        - os: macos-26-intel
          free-threading: true
    uses: ./.github/workflows/reusable-macos.yml
    with:
      free-threading: ${{ matrix.free-threading }}
      os: ${{ matrix.os }}

  build-ubuntu:
    name: >-
      Ubuntu
      ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
      ${{ fromJSON(matrix.bolt) && '(bolt)' || '' }}
    needs: build-context
    if: needs.build-context.outputs.run-ubuntu == 'true'
    strategy:
      fail-fast: false
      matrix:
        bolt:
        - false
        - true
        free-threading:
        - false
        - true
        os:
        - ubuntu-24.04
        - ubuntu-24.04-arm
        exclude:
        # Do not test BOLT with free-threading, to conserve resources
        - bolt: true
          free-threading: true
        # BOLT currently crashes during instrumentation on aarch64
        - os: ubuntu-24.04-arm
          bolt: true
        include:
        # Enable CPU-intensive tests on ARM (default build only)
        - os: ubuntu-24.04-arm
          bolt: false
          free-threading: false
          test-opts: '-u cpu'
    uses: ./.github/workflows/reusable-ubuntu.yml
    with:
      bolt-optimizations: ${{ matrix.bolt }}
      free-threading: ${{ matrix.free-threading }}
      os: ${{ matrix.os }}
      test-opts: ${{ matrix.test-opts || '' }}

  build-ubuntu-ssltests:
    name: 'Ubuntu SSL tests'
    runs-on: ${{ matrix.os }}
    timeout-minutes: 60
    needs: build-context
    if: needs.build-context.outputs.run-ubuntu == 'true'
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-24.04]
        ssllib:
          # See Tools/ssl/make_ssl_data.py for notes on adding a new version
          ## OpenSSL
          # Keep 1.1.1w in our list despite it being upstream EOL and otherwise
          # unsupported as it most resembles other 1.1.1-work-a-like ssl APIs
          # supported by important vendors such as AWS-LC.
          - { name: openssl, version: 1.1.1w }
          - { name: openssl, version: 3.0.19 }
          - { name: openssl, version: 3.3.6 }
          - { name: openssl, version: 3.4.4 }
          - { name: openssl, version: 3.5.5 }
          - { name: openssl, version: 3.6.1 }
          ## AWS-LC
          - { name: aws-lc, version: 1.68.0 }
    env:
      SSLLIB_VER: ${{ matrix.ssllib.version }}
      MULTISSL_DIR: ${{ github.workspace }}/multissl
      SSLLIB_DIR: ${{ github.workspace }}/multissl/${{ matrix.ssllib.name }}/${{ matrix.ssllib.version }}
      LD_LIBRARY_PATH: ${{ github.workspace }}/multissl/${{ matrix.ssllib.name }}/${{ matrix.ssllib.version }}/lib
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: Runner image version
      run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
    - name: Register gcc problem matcher
      run: echo "::add-matcher::.github/problem-matchers/gcc.json"
    - name: Install dependencies
      run: sudo ./.github/workflows/posix-deps-apt.sh
    - name: 'Restore SSL library build'
      id: cache-ssl-lib
      uses: actions/cache@v5
      with:
        path: ./multissl/${{ matrix.ssllib.name }}/${{ matrix.ssllib.version }}
        key: ${{ matrix.os }}-multissl-${{ matrix.ssllib.name }}-${{ matrix.ssllib.version }}
    - name: Install SSL Library
      if: steps.cache-ssl-lib.outputs.cache-hit != 'true'
      run: |
        python3 Tools/ssl/multissltests.py \
          --steps=library \
          --base-directory "$MULTISSL_DIR" \
          '--${{ matrix.ssllib.name }}' '${{ matrix.ssllib.version }}' \
          --system Linux
    - name: Configure CPython
      run: |
        ./configure CFLAGS="-fdiagnostics-format=json" \
          --config-cache \
          --enable-slower-safety \
          --with-pydebug \
          --with-openssl="$SSLLIB_DIR" \
          --with-builtin-hashlib-hashes=blake2 \
          --with-ssl-default-suites=openssl
    - name: Build CPython
      run: make -j4
    - name: Display build info
      run: make pythoninfo
    - name: Verify python is linked to the right lib
      run: |
        ./python -c 'import ssl; print(ssl.OPENSSL_VERSION)' \
          | grep -iE '${{ matrix.ssllib.name }}.*${{ matrix.ssllib.version }}'
    - name: SSL tests
      run: ./python Lib/test/ssltests.py

  build-android:
    name: Android (${{ matrix.arch }})
    needs: build-context
    if: needs.build-context.outputs.run-android == 'true'
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          - arch: aarch64
            runs-on: macos-26
          - arch: x86_64
            runs-on: ubuntu-24.04

    runs-on: ${{ matrix.runs-on }}
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Build and test
        run: ./Android/android.py ci --fast-ci ${{ matrix.arch }}-linux-android

  build-ios:
    name: iOS
    needs: build-context
    if: needs.build-context.outputs.run-ios == 'true'
    timeout-minutes: 60
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false

      # GitHub recommends explicitly selecting the desired Xcode version:
      # https://github.com/actions/runner-images/issues/12541#issuecomment-3083850140
      # This became a necessity as a result of
      # https://github.com/actions/runner-images/issues/12541 and
      # https://github.com/actions/runner-images/issues/12751.
      - name: Select Xcode version
        run: |
          sudo xcode-select --switch /Applications/Xcode_15.4.app

      - name: Build and test
        run: python3 Platforms/Apple ci iOS --fast-ci --simulator 'iPhone SE (3rd generation),OS=17.5'

  build-emscripten:
    name: 'Emscripten'
    needs: build-context
    if: needs.build-context.outputs.run-emscripten == 'true'
    uses: ./.github/workflows/reusable-emscripten.yml

  build-wasi:
    name: 'WASI'
    needs: build-context
    if: needs.build-context.outputs.run-wasi == 'true'
    uses: ./.github/workflows/reusable-wasi.yml

  test-hypothesis:
    name: "Hypothesis tests on Ubuntu"
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    needs: build-context
    if: needs.build-context.outputs.run-ubuntu == 'true'
    env:
      OPENSSL_VER: 3.5.5
      PYTHONSTRICTEXTENSIONBUILD: 1
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: Register gcc problem matcher
      run: echo "::add-matcher::.github/problem-matchers/gcc.json"
    - name: Install dependencies
      run: sudo ./.github/workflows/posix-deps-apt.sh
    - name: Configure OpenSSL env vars
      run: |
        echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV"
        echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> "$GITHUB_ENV"
        echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> "$GITHUB_ENV"
    - name: 'Restore OpenSSL build'
      id: cache-openssl
      uses: actions/cache@v5
      with:
        path: ./multissl/openssl/${{ env.OPENSSL_VER }}
        key: ${{ runner.os }}-multissl-openssl-${{ env.OPENSSL_VER }}
    - name: Install OpenSSL
      if: steps.cache-openssl.outputs.cache-hit != 'true'
      run: python3 Tools/ssl/multissltests.py --steps=library --base-directory "$MULTISSL_DIR" --openssl "$OPENSSL_VER" --system Linux
    - name: Setup directory envs for out-of-tree builds
      run: |
        echo "CPYTHON_RO_SRCDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-ro-srcdir)" >> "$GITHUB_ENV"
        echo "CPYTHON_BUILDDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-builddir)" >> "$GITHUB_ENV"
    - name: Create directories for read-only out-of-tree builds
      run: mkdir -p "$CPYTHON_RO_SRCDIR" "$CPYTHON_BUILDDIR"
    - name: Bind mount sources read-only
      run: sudo mount --bind -o ro "$GITHUB_WORKSPACE" "$CPYTHON_RO_SRCDIR"
    - name: Runner image version
      run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
    - name: Configure CPython out-of-tree
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      run: |
        ../cpython-ro-srcdir/configure \
          --config-cache \
          --with-pydebug \
          --enable-slower-safety \
          --with-openssl="$OPENSSL_DIR"
    - name: Build CPython out-of-tree
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      run: make -j4
    - name: Display build info
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      run: make pythoninfo
    - name: Remount sources writable for tests
      # some tests write to srcdir, lack of pyc files slows down testing
      run: sudo mount "$CPYTHON_RO_SRCDIR" -oremount,rw
    - name: Setup directory envs for out-of-tree builds
      run: |
        echo "CPYTHON_BUILDDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-builddir)" >> "$GITHUB_ENV"
    - name: "Create hypothesis venv"
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      run: |
        VENV_LOC=$(realpath -m .)/hypovenv
        VENV_PYTHON=$VENV_LOC/bin/python
        echo "HYPOVENV=${VENV_LOC}" >> "$GITHUB_ENV"
        echo "VENV_PYTHON=${VENV_PYTHON}" >> "$GITHUB_ENV"
        ./python -m venv "$VENV_LOC" && "$VENV_PYTHON" -m pip install -r "${GITHUB_WORKSPACE}/Tools/requirements-hypothesis.txt"
    - name: 'Restore Hypothesis database'
      id: cache-hypothesis-database
      uses: actions/cache@v5
      with:
        path: ${{ env.CPYTHON_BUILDDIR }}/.hypothesis/
        key: hypothesis-database-${{ github.head_ref || github.run_id }}
        restore-keys: |
          hypothesis-database-
    - name: "Run tests"
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      run: |
        # Most of the excluded tests are slow test suites with no property tests
        #
        # (GH-104097) test_sysconfig is skipped because it has tests that are
        # failing when executed from inside a virtual environment.
        "${VENV_PYTHON}" -m test \
          -W \
          --slowest \
          -j4 \
          --timeout 900 \
          -x test_asyncio \
          -x test_multiprocessing_fork \
          -x test_multiprocessing_forkserver \
          -x test_multiprocessing_spawn \
          -x test_concurrent_futures \
          -x test_socket \
          -x test_subprocess \
          -x test_signal \
          -x test_sysconfig
    - uses: actions/upload-artifact@v7
      if: always()
      with:
        name: hypothesis-example-db
        path: ${{ env.CPYTHON_BUILDDIR }}/.hypothesis/examples/

  build-asan:
    name: 'Address sanitizer'
    runs-on: ${{ matrix.os }}
    timeout-minutes: 60
    needs: build-context
    if: needs.build-context.outputs.run-ubuntu == 'true'
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-24.04]
    env:
      OPENSSL_VER: 3.5.5
      PYTHONSTRICTEXTENSIONBUILD: 1
      ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: Runner image version
      run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
    - name: Register gcc problem matcher
      run: echo "::add-matcher::.github/problem-matchers/gcc.json"
    - name: Install dependencies
      run: sudo ./.github/workflows/posix-deps-apt.sh
    - name: Set up GCC-10 for ASAN
      uses: egor-tensin/setup-gcc@v2
      with:
        version: 10
    - name: Configure OpenSSL env vars
      run: |
        echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV"
        echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> "$GITHUB_ENV"
        echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> "$GITHUB_ENV"
    - name: 'Restore OpenSSL build'
      id: cache-openssl
      uses: actions/cache@v5
      with:
        path: ./multissl/openssl/${{ env.OPENSSL_VER }}
        key: ${{ matrix.os }}-multissl-openssl-${{ env.OPENSSL_VER }}
    - name: Install OpenSSL
      if: steps.cache-openssl.outputs.cache-hit != 'true'
      run: python3 Tools/ssl/multissltests.py --steps=library --base-directory "$MULTISSL_DIR" --openssl "$OPENSSL_VER" --system Linux
    - name: Configure CPython
      run: ./configure --config-cache --with-address-sanitizer --without-pymalloc --with-openssl="$OPENSSL_DIR"
    - name: Build CPython
      run: make -j4
    - name: Display build info
      run: make pythoninfo
    - name: Tests
      run: xvfb-run make ci

  build-san:
    # ${{ '' } is a hack to nest jobs under the same sidebar category.
    name: Sanitizers${{ '' }}  # zizmor: ignore[obfuscation]
    needs: build-context
    if: needs.build-context.outputs.run-ubuntu == 'true'
    strategy:
      fail-fast: false
      matrix:
        check-name:
        - Thread
        free-threading:
        - false
        - true
        sanitizer:
        - TSan
        include:
        - check-name: Undefined behavior
          sanitizer: UBSan
          free-threading: false
    uses: ./.github/workflows/reusable-san.yml
    with:
      sanitizer: ${{ matrix.sanitizer }}
      free-threading: ${{ matrix.free-threading }}

  cross-build-linux:
    name: Cross build Linux
    runs-on: ubuntu-latest
    timeout-minutes: 60
    needs: build-context
    if: needs.build-context.outputs.run-ubuntu == 'true'
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Runner image version
        run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
      - name: Register gcc problem matcher
        run: echo "::add-matcher::.github/problem-matchers/gcc.json"
      - name: Set build dir
        run:
          # an absolute path outside of the working directoy
          echo "BUILD_DIR=$(realpath ${{ github.workspace }}/../build)" >> "$GITHUB_ENV"
      - name: Install dependencies
        run: sudo ./.github/workflows/posix-deps-apt.sh
      - name: Configure host build
        run: ./configure --prefix="$BUILD_DIR/host-python"
      - name: Install host Python
        run: make -j8 install
      - name: Run test subset with host build
        run: |
          "$BUILD_DIR/host-python/bin/python3" -m test test_sysconfig test_site test_embed
      - name: Configure cross build
        run: ./configure --prefix="$BUILD_DIR/cross-python" --with-build-python="$BUILD_DIR/host-python/bin/python3"
      - name: Install cross Python
        run: make -j8 install
      - name: Run test subset with host build
        run: |
          "$BUILD_DIR/cross-python/bin/python3" -m test test_sysconfig test_site test_embed

  cifuzz:
    # ${{ '' } is a hack to nest jobs under the same sidebar category.
    name: CIFuzz${{ '' }}  # zizmor: ignore[obfuscation]
    needs: build-context
    if: >-
      needs.build-context.outputs.run-ci-fuzz == 'true'
      || needs.build-context.outputs.run-ci-fuzz-stdlib == 'true'
    permissions:
      security-events: write
    strategy:
      fail-fast: false
      matrix:
        sanitizer:
        - address
        oss-fuzz-project-name:
        - cpython3
        - python3-libraries
        include:
        - sanitizer: undefined
          oss-fuzz-project-name: cpython3
        - sanitizer: memory
          oss-fuzz-project-name: cpython3
        exclude:
        # Note that the 'no-exclude' sentinel below is to prevent
        # an empty string value from excluding all jobs and causing
        # GHA to create a 'default' matrix entry with all empty values.
        - oss-fuzz-project-name: >-
            ${{
              needs.build-context.outputs.run-ci-fuzz == 'true'
              && 'no-exclude'
              || 'cpython3'
            }}
        - oss-fuzz-project-name: >-
            ${{
              needs.build-context.outputs.run-ci-fuzz-stdlib == 'true'
              && 'no-exclude'
              || 'python3-libraries'
            }}
    uses: ./.github/workflows/reusable-cifuzz.yml
    with:
      oss-fuzz-project-name: ${{ matrix.oss-fuzz-project-name }}
      sanitizer: ${{ matrix.sanitizer }}

  all-required-green:  # This job does nothing and is only used for the branch protection
    name: All required checks pass
    runs-on: ubuntu-latest
    timeout-minutes: 5
    needs:
    - build-context  # Transitive dependency, needed to access `run-tests` value
    - check-docs
    - check-autoconf-regen
    - check-generated-files
    - check-c-api-docs
    - build-windows
    - build-windows-msi
    - build-macos
    - build-ubuntu
    - build-ubuntu-ssltests
    - build-ios
    - build-emscripten
    - build-wasi
    - test-hypothesis
    - build-asan
    - build-san
    - cross-build-linux
    - cifuzz
    if: always()

    steps:
    - name: Check whether the needed jobs succeeded or failed
      uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe
      with:
        allowed-failures: >-
          build-android,
          build-emscripten,
          build-windows-msi,
          build-ubuntu-ssltests,
          test-hypothesis,
          cifuzz,
        allowed-skips: >-
          ${{ !fromJSON(needs.build-context.outputs.run-docs) && 'check-docs,' || '' }}
          ${{
            needs.build-context.outputs.run-tests != 'true'
            && '
            check-autoconf-regen,
            check-generated-files,
            '
            || ''
          }}
          ${{
            !fromJSON(needs.build-context.outputs.run-tests)
            && !fromJSON(needs.build-context.outputs.run-docs)
            && 'check-c-api-docs,'
            || ''
          }}
          ${{ !fromJSON(needs.build-context.outputs.run-windows-tests) && 'build-windows,' || '' }}
          ${{
            !fromJSON(needs.build-context.outputs.run-ci-fuzz)
            && !fromJSON(needs.build-context.outputs.run-ci-fuzz-stdlib)
            && 'cifuzz,' ||
            ''
          }}
          ${{ !fromJSON(needs.build-context.outputs.run-macos) && 'build-macos,' || '' }}
          ${{
            !fromJSON(needs.build-context.outputs.run-ubuntu)
            && '
            build-ubuntu,
            build-ubuntu-ssltests,
            test-hypothesis,
            build-asan,
            build-san,
            cross-build-linux,
            '
            || ''
          }}
          ${{ !fromJSON(needs.build-context.outputs.run-android) && 'build-android,' || '' }}
          ${{ !fromJSON(needs.build-context.outputs.run-ios) && 'build-ios,' || '' }}
          ${{ !fromJSON(needs.build-context.outputs.run-emscripten) && 'build-emscripten,' || '' }}
          ${{ !fromJSON(needs.build-context.outputs.run-wasi) && 'build-wasi,' || '' }}
        jobs: ${{ toJSON(needs) }}
documentation-links .github/workflows/documentation-links.yml
Triggers
pull_request_target
Runs on
ubuntu-latest
Jobs
documentation-links
Actions
readthedocs/actions/preview
View raw YAML
name: Read the Docs PR preview
# Automatically edits a pull request's descriptions with a link
# to the documentation's preview on Read the Docs.

on:
  pull_request_target:
    types:
      - opened
    paths:
    - 'Doc/**'
    - '.github/workflows/doc.yml'

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:
  documentation-links:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    timeout-minutes: 5

    steps:
      - uses: readthedocs/actions/preview@v1
        with:
          project-slug: "cpython-previews"
          single-version: "true"
jit matrix perms .github/workflows/jit.yml
Triggers
pull_request, push, workflow_dispatch
Runs on
ubuntu-24.04, ${{ matrix.runner }}, ${{ matrix.runner }}, ${{ matrix.runner }}, ubuntu-24.04
Jobs
interpreter, windows, macos, linux, linux-extras
Matrix
debug, include, include.architecture, include.configure_flags, include.continue_on_error, include.name, include.run_tests, include.runner, include.target, include.test_env, include.use_clang, target→ --enable-experimental-jit --with-pydebug, --enable-experimental-jit --with-pydebug --disable-gil, --enable-experimental-jit --with-tail-call-interp --with-pydebug, ARM64, False, Free-Threaded (Debug), JIT with tail calling interpreter, JIT without optimizations (Debug), PYTHON_UOPS_OPTIMIZE=0, True, Win32, aarch64-apple-darwin/clang, aarch64-pc-windows-msvc/msvc, aarch64-unknown-linux-gnu/gcc, i686-pc-windows-msvc/msvc, macos-26, macos-26-intel, ubuntu-24.04, ubuntu-24.04-arm, windows-11-arm, windows-2025-vs2026, x64, x86_64-apple-darwin/clang, x86_64-pc-windows-msvc/msvc, x86_64-unknown-linux-gnu/gcc
Commands
  • ./configure --enable-experimental-jit=interpreter --with-pydebug make all --jobs 4
  • ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
  • ./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }}
  • ./PCbuild/rt.bat ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3
  • brew update brew install llvm@${{ env.LLVM_VERSION }}
  • export SDKROOT="$(xcrun --show-sdk-path)" # Set MACOSX_DEPLOYMENT_TARGET and -Werror=unguarded-availability to # make sure we don't break downstream distributors (like uv): export CFLAGS_JIT='-Werror=unguarded-availability' export MACOSX_DEPLOYMENT_TARGET=10.15 ./configure --enable-experimental-jit --enable-universalsdk --with-universal-archs=universal2 ${{ matrix.debug && '--with-pydebug' || '' }} make all --jobs 4
  • ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
  • sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ env.LLVM_VERSION }} export PATH="$(llvm-config-${{ env.LLVM_VERSION }} --bindir):$PATH" ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '' }} make all --jobs 4
View raw YAML
name: JIT
on:
  pull_request:
    paths: &paths
      - '**jit**'
      - 'Python/bytecodes.c'
      - 'Python/optimizer*.c'
      - 'Python/executor_cases.c.h'
      - 'Python/optimizer_cases.c.h'
      - '**_testinternalcapi**'
      - '!Python/perf_jit_trampoline.c'
      - '!**/*.md'
      - '!**/*.ini'
  push:
    paths: *paths
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

env:
  FORCE_COLOR: 1
  LLVM_VERSION: 21

jobs:
  interpreter:
    name: Interpreter (Debug)
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Build tier two interpreter
        run: |
          ./configure --enable-experimental-jit=interpreter --with-pydebug
          make all --jobs 4
      - name: Test tier two interpreter
        run: |
          ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3

  windows:
    name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }})

    runs-on: ${{ matrix.runner }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        target:
          - i686-pc-windows-msvc/msvc
          - x86_64-pc-windows-msvc/msvc
          - aarch64-pc-windows-msvc/msvc
        debug:
          - true
          - false
        include:
          - target: i686-pc-windows-msvc/msvc
            architecture: Win32
            runner: windows-2025-vs2026
          - target: x86_64-pc-windows-msvc/msvc
            architecture: x64
            runner: windows-2025-vs2026
          - target: aarch64-pc-windows-msvc/msvc
            architecture: ARM64
            runner: windows-11-arm
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3.11'
      # PCbuild downloads LLVM automatically:
      - name: Build
        run: |
          ./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }}
      - name: Test
        run: |
          ./PCbuild/rt.bat ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3

  macos:
    name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }})

    runs-on: ${{ matrix.runner }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        target:
          - x86_64-apple-darwin/clang
          - aarch64-apple-darwin/clang
        debug:
          - true
          - false
        include:
          - target: x86_64-apple-darwin/clang
            runner: macos-26-intel
          - target: aarch64-apple-darwin/clang
            runner: macos-26
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3.11'
      - name: Install LLVM
        run: |
          brew update
          brew install llvm@${{ env.LLVM_VERSION }}
      - name: Build
        run: |
          export SDKROOT="$(xcrun --show-sdk-path)"
          # Set MACOSX_DEPLOYMENT_TARGET and -Werror=unguarded-availability to
          # make sure we don't break downstream distributors (like uv):
          export CFLAGS_JIT='-Werror=unguarded-availability'
          export MACOSX_DEPLOYMENT_TARGET=10.15
          ./configure --enable-experimental-jit --enable-universalsdk --with-universal-archs=universal2 ${{ matrix.debug && '--with-pydebug' || '' }}
          make all --jobs 4
      - name: Test
        run: |
          ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3

  linux:
    name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }})

    runs-on: ${{ matrix.runner }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        target:
          - x86_64-unknown-linux-gnu/gcc
          - aarch64-unknown-linux-gnu/gcc
        debug:
          - true
          - false
        include:
          - target: x86_64-unknown-linux-gnu/gcc
            runner: ubuntu-24.04
          - target: aarch64-unknown-linux-gnu/gcc
            runner: ubuntu-24.04-arm
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3.11'
      - name: Build
        run: |
          sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ env.LLVM_VERSION }}
          export PATH="$(llvm-config-${{ env.LLVM_VERSION }} --bindir):$PATH"
          ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '' }}
          make all --jobs 4
      - name: Test
        run: |
          ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3

  linux-extras:
    name: ${{ matrix.name }}

    runs-on: ubuntu-24.04
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: Free-Threaded (Debug)
            configure_flags: --enable-experimental-jit --with-pydebug --disable-gil
            continue_on_error: true
          - name: JIT without optimizations (Debug)
            configure_flags: --enable-experimental-jit --with-pydebug
            test_env: "PYTHON_UOPS_OPTIMIZE=0"
          - name: JIT with tail calling interpreter
            configure_flags: --enable-experimental-jit --with-tail-call-interp --with-pydebug
            use_clang: true
            run_tests: false
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3.11'
      - name: Build
        run: |
          sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ env.LLVM_VERSION }}
          export PATH="$(llvm-config-${{ env.LLVM_VERSION }} --bindir):$PATH"
          if [ "${{ matrix.use_clang }}" = "true" ]; then
            export CC=clang-${{ env.LLVM_VERSION }}
          fi
          ./configure ${{ matrix.configure_flags }}
          make all --jobs 4
      - name: Test
        if: matrix.run_tests != false
        run: |
          ${{ matrix.test_env }} ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
        continue-on-error: ${{ matrix.continue_on_error }}
lint perms .github/workflows/lint.yml
Triggers
push, pull_request, workflow_dispatch
Runs on
ubuntu-latest
Jobs
lint
Actions
j178/prek-action
View raw YAML
name: Lint

on: [push, pull_request, workflow_dispatch]

permissions:
  contents: read

env:
  FORCE_COLOR: 1
  RUFF_OUTPUT_FORMAT: github

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:
  lint:
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: j178/prek-action@v1
mypy matrix perms .github/workflows/mypy.yml
Triggers
push, pull_request, workflow_dispatch
Runs on
ubuntu-latest
Jobs
mypy
Matrix
target→ Lib/_pyrepl, Lib/test/libregrtest, Lib/tomllib, Tools/build, Tools/cases_generator, Tools/check-c-api-docs, Tools/clinic, Tools/jit, Tools/peg_generator
Commands
  • pip install -r Tools/requirements-dev.txt
  • python3 Misc/mypy/make_symlinks.py --symlink
  • mypy --config-file ${{ matrix.target }}/mypy.ini
View raw YAML
# Workflow to run mypy on select parts of the CPython repo
name: mypy

on:
  push:
    branches:
      - main
  pull_request:
    paths:
      - ".github/workflows/mypy.yml"
      - "Lib/_colorize.py"
      - "Lib/_pyrepl/**"
      - "Lib/test/libregrtest/**"
      - "Lib/tomllib/**"
      - "Misc/mypy/**"
      - "Tools/build/check_extension_modules.py"
      - "Tools/build/check_warnings.py"
      - "Tools/build/compute-changes.py"
      - "Tools/build/consts_getter.py"
      - "Tools/build/deepfreeze.py"
      - "Tools/build/generate-build-details.py"
      - "Tools/build/generate_sbom.py"
      - "Tools/build/generate_stdlib_module_names.py"
      - "Tools/build/mypy.ini"
      - "Tools/build/umarshal.py"
      - "Tools/build/update_file.py"
      - "Tools/build/verify_ensurepip_wheels.py"
      - "Tools/cases_generator/**"
      - "Tools/check-c-api-docs/**"
      - "Tools/clinic/**"
      - "Tools/jit/**"
      - "Tools/peg_generator/**"
      - "Tools/requirements-dev.txt"
  workflow_dispatch:

permissions:
  contents: read

env:
  PIP_DISABLE_PIP_VERSION_CHECK: 1
  FORCE_COLOR: 1
  TERM: xterm-256color  # needed for FORCE_COLOR to work on mypy on Ubuntu, see https://github.com/python/mypy/issues/13817

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:
  mypy:
    name: Run mypy on ${{ matrix.target }}
    runs-on: ubuntu-latest
    timeout-minutes: 10
    strategy:
      fail-fast: false
      matrix:
        target: [
          "Lib/_pyrepl",
          "Lib/test/libregrtest",
          "Lib/tomllib",
          "Tools/build",
          "Tools/cases_generator",
          "Tools/check-c-api-docs",
          "Tools/clinic",
          "Tools/jit",
          "Tools/peg_generator",
        ]
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: "3.13"
          cache: pip
          cache-dependency-path: Tools/requirements-dev.txt
      - run: pip install -r Tools/requirements-dev.txt
      - run: python3 Misc/mypy/make_symlinks.py --symlink
      - run: mypy --config-file ${{ matrix.target }}/mypy.ini
new-bugs-announce-notifier perms .github/workflows/new-bugs-announce-notifier.yml
Triggers
issues
Runs on
ubuntu-latest
Jobs
notify-new-bugs-announce
Commands
  • npm install mailgun.js form-data
View raw YAML
name: new-bugs-announce notifier

on:
  issues:
    types:
      - opened

permissions:
  issues: read

jobs:
  notify-new-bugs-announce:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/setup-node@v6
        with:
          node-version: 20
      - run: npm install mailgun.js form-data
      - name: Send notification
        uses: actions/github-script@v8
        env:
          MAILGUN_API_KEY: ${{ secrets.MAILGUN_PYTHON_ORG_MAILGUN_KEY }}
        with:
          script: |
            const Mailgun = require("mailgun.js");
            const formData = require('form-data');
            const mailgun = new Mailgun(formData);
            const DOMAIN = "mailgun.python.org";
            const mg = mailgun.client({username: 'api', key: process.env.MAILGUN_API_KEY});
            github.rest.issues.get({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
            })
            .then(function(issue) {
              const payload = {
                author : issue.data.user.login,
                issue  : issue.data.number,
                title  : issue.data.title,
                url    : issue.data.html_url,
                labels : issue.data.labels.map(label => { return label.name }).join(", "),
                assignee : issue.data.assignees.map(assignee => { return assignee.login }),
                // We need to truncate the body size, because the max size for
                // the whole payload is 16kb. We want to be safe and assume that
                // body can take up to ~8kb of space.
                body   : (issue.data.body || "").substring(0, 8000)
              };

              const data = {
                from: "CPython Issues <github@mailgun.python.org>",
                to: "new-bugs-announce@python.org",
                subject: `[Issue ${issue.data.number}] ${issue.data.title}`,
                template: "new-github-issue",
                'o:tracking-clicks': 'no',
                'h:X-Mailgun-Variables': JSON.stringify(payload)
              };
              return mg.messages.create(DOMAIN, data)
            })
            .then(msg => console.log(msg));
require-pr-label .github/workflows/require-pr-label.yml
Triggers
pull_request
Runs on
ubuntu-latest, ubuntu-latest
Jobs
label-dnm, label-reviews
Actions
mheap/github-action-required-labels, mheap/github-action-required-labels, mheap/github-action-required-labels, mheap/github-action-required-labels
View raw YAML
name: Check labels

on:
  pull_request:
    types: [opened, reopened, labeled, unlabeled, synchronize]

jobs:
  label-dnm:
    name: DO-NOT-MERGE
    if: github.repository_owner == 'python'
    runs-on: ubuntu-latest
    permissions:
      pull-requests: read
    timeout-minutes: 10

    steps:
      - name: Check there's no DO-NOT-MERGE
        uses: mheap/github-action-required-labels@v5
        with:
          mode: exactly
          count: 0
          labels: |
            DO-NOT-MERGE

  label-reviews:
    name: Unresolved review
    if: github.repository_owner == 'python'
    runs-on: ubuntu-latest
    permissions:
      pull-requests: read
    timeout-minutes: 10

    steps:
      # Check that the PR is not awaiting changes from the author due to previous review.
      - name: Check there's no required changes
        uses: mheap/github-action-required-labels@v5
        with:
          mode: exactly
          count: 0
          labels: |
            awaiting changes
            awaiting change review
      - id: is-feature
        name: Check whether this PR is a feature (contains a "type-feature" label)
        uses: mheap/github-action-required-labels@v5
        with:
          mode: exactly
          count: 1
          labels: |
            type-feature
          exit_type: success  # don't fail the check if the PR is not a feature, just record the result
      # In case of a feature PR, check for a complete review (contains an "awaiting merge" label).
      - id: awaiting-merge
        if: steps.is-feature.outputs.status == 'success'
        name: Check for complete review
        uses: mheap/github-action-required-labels@v5
        with:
          mode: exactly
          count: 1
          labels: |
            awaiting merge
reusable-check-c-api-docs perms .github/workflows/reusable-check-c-api-docs.yml
Triggers
workflow_call
Runs on
ubuntu-latest
Jobs
check-c-api-docs
Commands
  • python Tools/check-c-api-docs/main.py
View raw YAML
name: Reusable C API Docs Check

on:
  workflow_call:

permissions:
  contents: read

env:
  FORCE_COLOR: 1

jobs:
  check-c-api-docs:
    name: 'Check if all C APIs are documented'
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3.x'
      - name: Check for undocumented C APIs
        run: python Tools/check-c-api-docs/main.py
reusable-cifuzz security .github/workflows/reusable-cifuzz.yml
Triggers
workflow_call
Runs on
ubuntu-latest
Jobs
cifuzz
Actions
google/oss-fuzz/infra/cifuzz/actions/build_fuzzers, google/oss-fuzz/infra/cifuzz/actions/run_fuzzers, github/codeql-action/upload-sarif
View raw YAML
# CIFuzz job based on https://google.github.io/oss-fuzz/getting-started/continuous-integration/
name: Reusable CIFuzz

on:
  workflow_call:
    inputs:
      oss-fuzz-project-name:
        description: OSS-Fuzz project name
        required: true
        type: string
      sanitizer:
        description: OSS-Fuzz sanitizer
        required: true
        type: string

jobs:
  cifuzz:
    name: ${{ inputs.oss-fuzz-project-name }} (${{ inputs.sanitizer }})
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - name: Build fuzzers (${{ inputs.sanitizer }})
        id: build
        uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
        with:
          oss-fuzz-project-name: ${{ inputs.oss-fuzz-project-name }}
          sanitizer: ${{ inputs.sanitizer }}
      - name: Run fuzzers (${{ inputs.sanitizer }})
        uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
        with:
          fuzz-seconds: 600
          oss-fuzz-project-name: ${{ inputs.oss-fuzz-project-name }}
          output-sarif: true
          sanitizer: ${{ inputs.sanitizer }}
      - name: Upload crash
        if: failure() && steps.build.outcome == 'success'
        uses: actions/upload-artifact@v7
        with:
          name: ${{ inputs.sanitizer }}-artifacts
          path: ./out/artifacts
      - name: Upload SARIF
        if: always() && steps.build.outcome == 'success'
        uses: github/codeql-action/upload-sarif@v4
        with:
          sarif_file: cifuzz-sarif/results.sarif
          checkout_path: cifuzz-sarif
reusable-context .github/workflows/reusable-context.yml
Triggers
workflow_call
Runs on
ubuntu-latest
Jobs
compute-changes
Commands
  • echo '${{ github.event_name }}'
  • set -eux # Fetch enough history to find a common ancestor commit (aka merge-base): git fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \ --no-tags --prune --no-recurse-submodules # This should get the oldest commit in the local fetched history (which may not be the commit the PR branched from): COMMON_ANCESTOR=$( git rev-list --first-parent --max-parents=0 --max-count=1 "${branch_pr}" ) DATE=$( git log --date=iso8601 --format=%cd "${COMMON_ANCESTOR}" ) # Get all commits since that commit date from the base branch (eg: main): git fetch origin "${refspec_base}" --shallow-since="${DATE}" \ --no-tags --prune --no-recurse-submodules
  • python Tools/build/compute-changes.py
View raw YAML
name: Reusable build context

on:  # yamllint disable-line rule:truthy
  workflow_call:
    outputs:
      # Every referenced step MUST always set its output variable,
      # either via ``Tools/build/compute-changes.py`` or in this workflow file.
      # Boolean outputs (generally prefixed ``run-``) can then later be used
      # safely through the following idiom in job conditionals and other
      # expressions. Here's some examples:
      #
      #   if: fromJSON(needs.build-context.outputs.run-tests)
      #
      #   ${{
      #        fromJSON(needs.build-context.outputs.run-tests)
      #        && 'truthy-branch'
      #        || 'falsy-branch'
      #   }}
      #
      run-android:
        description: Whether to run the Android tests
        value: ${{ jobs.compute-changes.outputs.run-android }}  # bool
      run-ci-fuzz:
        description: Whether to run the CIFuzz job for 'cpython' fuzzer
        value: ${{ jobs.compute-changes.outputs.run-ci-fuzz }}  # bool
      run-ci-fuzz-stdlib:
        description: Whether to run the CIFuzz job for 'python3-libraries' fuzzer
        value: ${{ jobs.compute-changes.outputs.run-ci-fuzz-stdlib }}  # bool
      run-docs:
        description: Whether to build the docs
        value: ${{ jobs.compute-changes.outputs.run-docs }}  # bool
      run-ios:
        description: Whether to run the iOS tests
        value: ${{ jobs.compute-changes.outputs.run-ios }}  # bool
      run-macos:
        description: Whether to run the macOS tests
        value: ${{ jobs.compute-changes.outputs.run-macos }}  # bool
      run-tests:
        description: Whether to run the regular tests
        value: ${{ jobs.compute-changes.outputs.run-tests  }}  # bool
      run-ubuntu:
        description: Whether to run the Ubuntu tests
        value: ${{ jobs.compute-changes.outputs.run-ubuntu }}  # bool
      run-emscripten:
        description: Whether to run the Emscripten tests
        value: ${{ jobs.compute-changes.outputs.run-emscripten }}  # bool
      run-wasi:
        description: Whether to run the WASI tests
        value: ${{ jobs.compute-changes.outputs.run-wasi }}  # bool
      run-windows-msi:
        description: Whether to run the MSI installer smoke tests
        value: ${{ jobs.compute-changes.outputs.run-windows-msi }}  # bool
      run-windows-tests:
        description: Whether to run the Windows tests
        value: ${{ jobs.compute-changes.outputs.run-windows-tests }}  # bool

jobs:
  compute-changes:
    name: Create context from changed files
    runs-on: ubuntu-latest
    timeout-minutes: 10
    outputs:
      run-android: ${{ steps.changes.outputs.run-android }}
      run-ci-fuzz: ${{ steps.changes.outputs.run-ci-fuzz }}
      run-ci-fuzz-stdlib: ${{ steps.changes.outputs.run-ci-fuzz-stdlib }}
      run-docs: ${{ steps.changes.outputs.run-docs }}
      run-ios: ${{ steps.changes.outputs.run-ios }}
      run-macos: ${{ steps.changes.outputs.run-macos }}
      run-tests: ${{ steps.changes.outputs.run-tests }}
      run-ubuntu: ${{ steps.changes.outputs.run-ubuntu }}
      run-emscripten: ${{ steps.changes.outputs.run-emscripten }}
      run-wasi: ${{ steps.changes.outputs.run-wasi }}
      run-windows-msi: ${{ steps.changes.outputs.run-windows-msi }}
      run-windows-tests: ${{ steps.changes.outputs.run-windows-tests }}
    steps:
    - name: Set up Python
      uses: actions/setup-python@v6
      with:
        python-version: "3"

    - run: >-
        echo '${{ github.event_name }}'

    - uses: actions/checkout@v6
      with:
        persist-credentials: false
        ref: >-
          ${{
            github.event_name == 'pull_request'
            && github.event.pull_request.head.sha
            || ''
          }}

    # Adapted from https://github.com/actions/checkout/issues/520#issuecomment-1167205721
    - name: Fetch commits to get branch diff
      if: github.event_name == 'pull_request'
      run: |
        set -eux

        # Fetch enough history to find a common ancestor commit (aka merge-base):
        git fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \
          --no-tags --prune --no-recurse-submodules

        # This should get the oldest commit in the local fetched history (which may not be the commit the PR branched from):
        COMMON_ANCESTOR=$( git rev-list --first-parent --max-parents=0 --max-count=1 "${branch_pr}" )
        DATE=$( git log --date=iso8601 --format=%cd "${COMMON_ANCESTOR}" )

        # Get all commits since that commit date from the base branch (eg: main):
        git fetch origin "${refspec_base}" --shallow-since="${DATE}" \
          --no-tags --prune --no-recurse-submodules
      env:
        branch_pr: 'origin/${{ github.event.pull_request.head.ref }}'
        commits: ${{ github.event.pull_request.commits }}
        refspec_base: '+${{ github.event.pull_request.base.sha }}:remotes/origin/${{ github.event.pull_request.base.ref }}'
        refspec_pr: '+${{ github.event.pull_request.head.sha }}:remotes/origin/${{ github.event.pull_request.head.ref }}'

    # We only want to run tests on PRs when related files are changed,
    # or when someone triggers a manual workflow run.
    - name: Compute changed files
      id: changes
      run: python Tools/build/compute-changes.py
      env:
        GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
        GITHUB_EVENT_NAME: ${{ github.event_name }}
        CCF_TARGET_REF: ${{ github.base_ref || github.event.repository.default_branch }}
        CCF_HEAD_REF: ${{ github.event.pull_request.head.sha || github.sha }}
reusable-docs perms .github/workflows/reusable-docs.yml
Triggers
workflow_call, workflow_dispatch
Runs on
ubuntu-latest, ubuntu-24.04, ubuntu-latest
Jobs
build-doc, doctest, check-epub
Commands
  • # Fetch enough history to find a common ancestor commit (aka merge-base): git fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \ --no-tags --prune --no-recurse-submodules # This should get the oldest commit in the local fetched history (which may not be the commit the PR branched from): COMMON_ANCESTOR=$( git rev-list --first-parent --max-parents=0 --max-count=1 "${branch_pr}" ) DATE=$( git log --date=iso8601 --format=%cd "${COMMON_ANCESTOR}" ) # Get all commits since that commit date from the base branch (eg: master or main): git fetch origin "${refspec_base}" --shallow-since="${DATE}" \ --no-tags --prune --no-recurse-submodules
  • make -C Doc/ venv
  • set -Eeuo pipefail # Build docs with the nit-picky option; write warnings to file make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet --nitpicky --warning-file sphinx-warnings.txt" html
  • python Doc/tools/check-warnings.py \ --annotate-diff "${branch_base}" "${branch_pr}" \ --fail-if-regression \ --fail-if-improved \ --fail-if-new-news-nit
  • sudo ./.github/workflows/posix-deps-apt.sh && sudo apt-get install --no-install-recommends wamerican
  • ./configure --with-pydebug
  • make -j4
  • make -C Doc/ PYTHON=../python venv
View raw YAML
name: Reusable Docs

on:
  workflow_call:
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

env:
  FORCE_COLOR: 1

jobs:
  build-doc:
    name: 'Docs'
    runs-on: ubuntu-latest
    timeout-minutes: 60
    env:
      branch_base: 'origin/${{ github.event.pull_request.base.ref }}'
      branch_pr: 'origin/${{ github.event.pull_request.head.ref }}'
      commits: ${{ github.event.pull_request.commits }}
      refspec_base: '+${{ github.event.pull_request.base.sha }}:remotes/origin/${{ github.event.pull_request.base.ref }}'
      refspec_pr: '+${{ github.event.pull_request.head.sha }}:remotes/origin/${{ github.event.pull_request.head.ref }}'
    steps:
    - name: 'Check out latest PR branch commit'
      uses: actions/checkout@v6
      with:
        persist-credentials: false
        ref: >-
          ${{
            github.event_name == 'pull_request'
            && github.event.pull_request.head.sha
            || ''
          }}
    # Adapted from https://github.com/actions/checkout/issues/520#issuecomment-1167205721
    - name: 'Fetch commits to get branch diff'
      if: github.event_name == 'pull_request'
      run: |
        # Fetch enough history to find a common ancestor commit (aka merge-base):
        git fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \
          --no-tags --prune --no-recurse-submodules

        # This should get the oldest commit in the local fetched history (which may not be the commit the PR branched from):
        COMMON_ANCESTOR=$( git rev-list --first-parent --max-parents=0 --max-count=1 "${branch_pr}" )
        DATE=$( git log --date=iso8601 --format=%cd "${COMMON_ANCESTOR}" )

        # Get all commits since that commit date from the base branch (eg: master or main):
        git fetch origin "${refspec_base}" --shallow-since="${DATE}" \
          --no-tags --prune --no-recurse-submodules
    - name: 'Set up Python'
      uses: actions/setup-python@v6
      with:
        python-version: '3'
        cache: 'pip'
        cache-dependency-path: 'Doc/requirements.txt'
    - name: 'Install build dependencies'
      run: make -C Doc/ venv

    # To annotate PRs with Sphinx nitpicks (missing references)
    - name: 'Build HTML documentation'
      continue-on-error: true
      run: |
        set -Eeuo pipefail
        # Build docs with the nit-picky option; write warnings to file
        make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet --nitpicky --warning-file sphinx-warnings.txt" html
    - name: 'Check warnings'
      if: github.event_name == 'pull_request'
      run: |
        python Doc/tools/check-warnings.py \
          --annotate-diff "${branch_base}" "${branch_pr}" \
          --fail-if-regression \
          --fail-if-improved \
          --fail-if-new-news-nit

  # Run "doctest" on HEAD as new syntax doesn't exist in the latest stable release
  doctest:
    name: 'Doctest'
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - uses: actions/cache@v5
      with:
        path: ~/.cache/pip
        key: ubuntu-doc-${{ hashFiles('Doc/requirements.txt') }}
        restore-keys: |
          ubuntu-doc-
    - name: 'Install Dependencies'
      run: sudo ./.github/workflows/posix-deps-apt.sh && sudo apt-get install --no-install-recommends wamerican
    - name: 'Configure CPython'
      run: ./configure --with-pydebug
    - name: 'Build CPython'
      run: make -j4
    - name: 'Install build dependencies'
      run: make -C Doc/ PYTHON=../python venv
    # Use "xvfb-run" since some doctest tests open GUI windows
    - name: 'Run documentation doctest'
      run: xvfb-run make -C Doc/ PYTHON=../python SPHINXERRORHANDLING="--fail-on-warning" doctest

  check-epub:
    name: 'Check EPUB'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: 'Set up Python'
      uses: actions/setup-python@v6
      with:
        python-version: '3'
        cache: 'pip'
        cache-dependency-path: 'Doc/requirements.txt'
    - name: 'Install build dependencies'
      run: |
        make -C Doc/ venv
        python -m pip install epubcheck
    - name: 'Build EPUB documentation'
      run: make -C Doc/ PYTHON=../python epub
    - name: 'Run epubcheck'
      continue-on-error: true
      run: epubcheck Doc/build/epub/Python.epub &> Doc/epubcheck.txt
    - run: cat Doc/epubcheck.txt
    - name: 'Check for fatal errors in EPUB'
      run: python Doc/tools/check-epub.py
reusable-emscripten .github/workflows/reusable-emscripten.yml
Triggers
workflow_call
Runs on
ubuntu-24.04
Jobs
build-emscripten-reusable
Commands
  • import hashlib import json import os import tomllib from pathlib import Path config = tomllib.loads(Path("Platforms/emscripten/config.toml").read_text()) h = hashlib.sha256() h.update(json.dumps(config["dependencies"], sort_keys=True).encode()) h.update(Path("Platforms/emscripten/make_libffi.sh").read_bytes()) h.update(b'1') # Update to explicitly bust cache emsdk_cache = Path(os.environ["RUNNER_TEMP"]) / "emsdk-cache" with open(os.environ["GITHUB_OUTPUT"], "a") as f: f.write(f"emscripten-version={config['emscripten-version']}\n") f.write(f"node-version={config['node-version']}\n") f.write(f"deps-hash={h.hexdigest()}\n") with open(os.environ["GITHUB_ENV"], "a") as f: f.write(f"EMSDK_CACHE={emsdk_cache}\n")
  • echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
  • python3 Platforms/emscripten install-emscripten
  • python3 Platforms/emscripten configure-build-python -- --config-cache --with-pydebug
  • python3 Platforms/emscripten make-build-python
  • python3 Platforms/emscripten make-dependencies ${{ steps.emsdk-cache.outputs.cache-hit == 'true' && '--check-up-to-date' || '' }}
  • python3 Platforms/emscripten configure-host --host-runner node -- --config-cache
  • python3 Platforms/emscripten make-host
View raw YAML
name: Reusable Emscripten

on:
  workflow_call:

env:
  FORCE_COLOR: 1

jobs:
  build-emscripten-reusable:
    name: 'build and test'
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: "Read Emscripten config"
      id: emscripten-config
      shell: python
      run: |
        import hashlib
        import json
        import os
        import tomllib
        from pathlib import Path

        config = tomllib.loads(Path("Platforms/emscripten/config.toml").read_text())
        h = hashlib.sha256()
        h.update(json.dumps(config["dependencies"], sort_keys=True).encode())
        h.update(Path("Platforms/emscripten/make_libffi.sh").read_bytes())
        h.update(b'1') # Update to explicitly bust cache
        emsdk_cache = Path(os.environ["RUNNER_TEMP"]) / "emsdk-cache"
        with open(os.environ["GITHUB_OUTPUT"], "a") as f:
            f.write(f"emscripten-version={config['emscripten-version']}\n")
            f.write(f"node-version={config['node-version']}\n")
            f.write(f"deps-hash={h.hexdigest()}\n")
        with open(os.environ["GITHUB_ENV"], "a") as f:
            f.write(f"EMSDK_CACHE={emsdk_cache}\n")
    - name: "Install Node.js"
      uses: actions/setup-node@v6
      with:
        node-version: ${{ steps.emscripten-config.outputs.node-version }}
    - name: "Cache Emscripten SDK"
      id: emsdk-cache
      uses: actions/cache@v5
      with:
        path: ${{ env.EMSDK_CACHE }}
        key: emsdk-${{ steps.emscripten-config.outputs.emscripten-version }}-${{ steps.emscripten-config.outputs.deps-hash }}
        restore-keys: emsdk-${{ steps.emscripten-config.outputs.emscripten-version }}
    - name: "Install Python"
      uses: actions/setup-python@v6
      with:
        python-version: '3.x'
    - name: "Runner image version"
      run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
    - name: "Install Emscripten"
      run: python3 Platforms/emscripten install-emscripten
    - name: "Configure build Python"
      run: python3 Platforms/emscripten configure-build-python -- --config-cache --with-pydebug
    - name: "Make build Python"
      run: python3 Platforms/emscripten make-build-python
    - name: "Make dependencies"
      run: >-
        python3 Platforms/emscripten make-dependencies
        ${{ steps.emsdk-cache.outputs.cache-hit == 'true' && '--check-up-to-date' || '' }}
    - name: "Configure host Python"
      run: python3 Platforms/emscripten configure-host --host-runner node -- --config-cache
    - name: "Make host Python"
      run: python3 Platforms/emscripten make-host
    - name: "Display build info"
      run: python3 Platforms/emscripten run --pythoninfo
    - name: "Test"
      run: python3 Platforms/emscripten run --test
reusable-macos .github/workflows/reusable-macos.yml
Triggers
workflow_call
Runs on
${{ inputs.os }}
Jobs
build-macos
Commands
  • echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
  • brew install pkg-config openssl@3.5 xz gdbm tcl-tk@9 make # Because alternate versions are not symlinked into place by default: brew link --overwrite tcl-tk@9
  • MACOSX_DEPLOYMENT_TARGET=10.15 \ GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \ GDBM_LIBS="-L$(brew --prefix gdbm)/lib -lgdbm" \ ./configure \ --config-cache \ --with-pydebug \ --enable-slower-safety \ --enable-safety \ ${{ inputs.free-threading && '--disable-gil' || '' }} \ --prefix=/opt/python-dev \ --with-openssl="$(brew --prefix openssl@3.5)"
  • gmake -j8
  • set -o pipefail; gmake -j8 --output-sync 2>&1 | tee compiler_output_macos.txt
  • make pythoninfo
  • python3 Tools/build/check_warnings.py --compiler-output-file-path=compiler_output_macos.txt --warning-ignore-file-path=Tools/build/.warningignore_macos --compiler-output-type=clang --fail-on-regression --fail-on-improvement --path-prefix="./"
  • make ci
View raw YAML
name: Reusable macOS

on:
  workflow_call:
    inputs:
      free-threading:
        required: false
        type: boolean
        default: false
      os:
        description: OS to run the job
        required: true
        type: string

env:
  FORCE_COLOR: 1

jobs:
  build-macos:
    name: build and test (${{ inputs.os }})
    runs-on: ${{ inputs.os }}
    timeout-minutes: 60
    env:
      HOMEBREW_NO_ANALYTICS: 1
      HOMEBREW_NO_AUTO_UPDATE: 1
      HOMEBREW_NO_INSTALL_CLEANUP: 1
      HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
      PYTHONSTRICTEXTENSIONBUILD: 1
      TERM: linux
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: Runner image version
      run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
    - name: Install Homebrew dependencies
      run: |
        brew install pkg-config openssl@3.5 xz gdbm tcl-tk@9 make
        # Because alternate versions are not symlinked into place by default:
        brew link --overwrite tcl-tk@9
    - name: Configure CPython
      run: |
        MACOSX_DEPLOYMENT_TARGET=10.15 \
        GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \
        GDBM_LIBS="-L$(brew --prefix gdbm)/lib -lgdbm" \
        ./configure \
          --config-cache \
          --with-pydebug \
          --enable-slower-safety \
          --enable-safety \
          ${{ inputs.free-threading && '--disable-gil' || '' }} \
          --prefix=/opt/python-dev \
          --with-openssl="$(brew --prefix openssl@3.5)"
    - name: Build CPython
      if : ${{ inputs.free-threading || inputs.os != 'macos-26-intel' }}
      run: gmake -j8
    - name: Build CPython for compiler warning check
      if : ${{ !inputs.free-threading && inputs.os == 'macos-26-intel' }}
      run: set -o pipefail; gmake -j8 --output-sync 2>&1 | tee compiler_output_macos.txt
    - name: Display build info
      run: make pythoninfo
    - name: Check compiler warnings
      if : ${{ !inputs.free-threading && inputs.os == 'macos-26-intel' }}
      run: >-
        python3 Tools/build/check_warnings.py
        --compiler-output-file-path=compiler_output_macos.txt
        --warning-ignore-file-path=Tools/build/.warningignore_macos
        --compiler-output-type=clang
        --fail-on-regression
        --fail-on-improvement
        --path-prefix="./"
    - name: Tests
      run: make ci
reusable-san .github/workflows/reusable-san.yml
Triggers
workflow_call
Runs on
ubuntu-24.04
Jobs
build-san-reusable
Commands
  • echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
  • sudo ./.github/workflows/posix-deps-apt.sh # Install clang wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh if [ "${SANITIZER}" = "TSan" ]; then sudo ./llvm.sh 17 # gh-121946: llvm-18 package is temporarily broken sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-17 100 sudo update-alternatives --set clang /usr/bin/clang-17 sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-17 100 sudo update-alternatives --set clang++ /usr/bin/clang++-17 # Reduce ASLR to avoid TSan crashing sudo sysctl -w vm.mmap_rnd_bits=28 else sudo ./llvm.sh 20 fi
  • if [ "${SANITIZER}" = "TSan" ]; then echo "TSAN_OPTIONS=${SAN_LOG_OPTION} suppressions=${GITHUB_WORKSPACE}/Tools/tsan/suppressions${{ fromJSON(inputs.free-threading) && '_free_threading' || '' }}.txt handle_segv=0" >> "$GITHUB_ENV" else echo "UBSAN_OPTIONS=${SAN_LOG_OPTION}" >> "$GITHUB_ENV" fi echo "CC=clang" >> "$GITHUB_ENV" echo "CXX=clang++" >> "$GITHUB_ENV"
  • ./configure --config-cache ${{ inputs.sanitizer == 'TSan' && '--with-thread-sanitizer' || '--with-undefined-behavior-sanitizer' }} --with-pydebug ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
  • make -j4
  • make pythoninfo
  • ./python -m test ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }} -j4
  • ./python -m test --tsan-parallel --parallel-threads=4 -j4
View raw YAML
name: Reusable Sanitizer

on:
  workflow_call:
    inputs:
      sanitizer:
        required: true
        type: string
      free-threading:
        description: Whether to use free-threaded mode
        required: false
        type: boolean
        default: false

env:
  FORCE_COLOR: 1

jobs:
  build-san-reusable:
    name: >-
      ${{ inputs.sanitizer }}${{
        inputs.free-threading
        && ' (free-threading)'
        || ''
      }}
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: Runner image version
      run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
    - name: Install dependencies
      run: |
        sudo ./.github/workflows/posix-deps-apt.sh
        # Install clang
        wget https://apt.llvm.org/llvm.sh
        chmod +x llvm.sh

        if [ "${SANITIZER}" = "TSan" ]; then
          sudo ./llvm.sh 17  # gh-121946: llvm-18 package is temporarily broken
          sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-17 100
          sudo update-alternatives --set clang /usr/bin/clang-17
          sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-17 100
          sudo update-alternatives --set clang++ /usr/bin/clang++-17
          # Reduce ASLR to avoid TSan crashing
          sudo sysctl -w vm.mmap_rnd_bits=28
        else
          sudo ./llvm.sh 20
        fi

    - name: Sanitizer option setup
      run: |
        if [ "${SANITIZER}" = "TSan" ]; then
          echo "TSAN_OPTIONS=${SAN_LOG_OPTION} suppressions=${GITHUB_WORKSPACE}/Tools/tsan/suppressions${{
              fromJSON(inputs.free-threading)
              && '_free_threading'
              || ''
            }}.txt handle_segv=0" >> "$GITHUB_ENV"
        else
          echo "UBSAN_OPTIONS=${SAN_LOG_OPTION}" >> "$GITHUB_ENV"
        fi
        echo "CC=clang" >> "$GITHUB_ENV"
        echo "CXX=clang++" >> "$GITHUB_ENV"
      env:
        SANITIZER: ${{ inputs.sanitizer }}
        SAN_LOG_OPTION: log_path=${{ github.workspace }}/san_log
    - name: Configure CPython
      run: >-
        ./configure
        --config-cache
        ${{
          inputs.sanitizer == 'TSan'
          && '--with-thread-sanitizer'
          || '--with-undefined-behavior-sanitizer'
        }}
        --with-pydebug
        ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
    - name: Build CPython
      run: make -j4
    - name: Display build info
      run: make pythoninfo
    - name: Tests
      run: >-
        ./python -m test
        ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }}
        -j4
    - name: Parallel tests
      if: >-
        inputs.sanitizer == 'TSan'
        && fromJSON(inputs.free-threading)
      run: ./python -m test --tsan-parallel --parallel-threads=4 -j4
    - name: Display logs
      if: always()
      run: find "${GITHUB_WORKSPACE}" -name 'san_log.*' | xargs head -n 1000
    - name: Archive logs
      if: always()
      uses: actions/upload-artifact@v7
      with:
        name: >-
          ${{ inputs.sanitizer }}-logs-${{
            fromJSON(inputs.free-threading)
            && 'free-threading'
            || 'default'
          }}
        path: san_log.*
        if-no-files-found: ignore
reusable-ubuntu .github/workflows/reusable-ubuntu.yml
Triggers
workflow_call
Runs on
${{ inputs.os }}
Jobs
build-ubuntu-reusable
Commands
  • echo "::add-matcher::.github/problem-matchers/gcc.json"
  • sudo ./.github/workflows/posix-deps-apt.sh
  • sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 19 sudo apt-get install --no-install-recommends bolt-19 echo PATH="$(llvm-config-19 --bindir):$PATH" >> $GITHUB_ENV
  • echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV" echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> "$GITHUB_ENV" echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> "$GITHUB_ENV"
  • python3 Tools/ssl/multissltests.py --steps=library --base-directory "$MULTISSL_DIR" --openssl "$OPENSSL_VER" --system Linux
  • echo "CPYTHON_RO_SRCDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-ro-srcdir)" >> "$GITHUB_ENV" echo "CPYTHON_BUILDDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-builddir)" >> "$GITHUB_ENV"
  • mkdir -p "$CPYTHON_RO_SRCDIR" "$CPYTHON_BUILDDIR"
  • sudo mount --bind -o ro "$GITHUB_WORKSPACE" "$CPYTHON_RO_SRCDIR"
View raw YAML
name: Reusable Ubuntu

on:
  workflow_call:
    inputs:
      bolt-optimizations:
        description: Whether to enable BOLT optimizations
        required: false
        type: boolean
        default: false
      free-threading:
        description: Whether to use free-threaded mode
        required: false
        type: boolean
        default: false
      os:
         description: OS to run the job
         required: true
         type: string
      test-opts:
         description: Extra options to pass to the test runner via TESTOPTS
         required: false
         type: string
         default: ''

env:
  FORCE_COLOR: 1

jobs:
  build-ubuntu-reusable:
    name: build and test (${{ inputs.os }})
    runs-on: ${{ inputs.os }}
    timeout-minutes: 60
    env:
      OPENSSL_VER: 3.5.5
      PYTHONSTRICTEXTENSIONBUILD: 1
      TERM: linux
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: Register gcc problem matcher
      run: echo "::add-matcher::.github/problem-matchers/gcc.json"
    - name: Install dependencies
      run: sudo ./.github/workflows/posix-deps-apt.sh
    - name: Install Clang and BOLT
      if: ${{ fromJSON(inputs.bolt-optimizations) }}
      run: |
        sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 19
        sudo apt-get install --no-install-recommends bolt-19
        echo PATH="$(llvm-config-19 --bindir):$PATH" >> $GITHUB_ENV
    - name: Configure OpenSSL env vars
      run: |
        echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV"
        echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> "$GITHUB_ENV"
        echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> "$GITHUB_ENV"
    - name: 'Restore OpenSSL build'
      id: cache-openssl
      uses: actions/cache@v5
      with:
        path: ./multissl/openssl/${{ env.OPENSSL_VER }}
        key: ${{ inputs.os }}-multissl-openssl-${{ env.OPENSSL_VER }}
    - name: Install OpenSSL
      if: steps.cache-openssl.outputs.cache-hit != 'true'
      run: python3 Tools/ssl/multissltests.py --steps=library --base-directory "$MULTISSL_DIR" --openssl "$OPENSSL_VER" --system Linux
    - name: Setup directory envs for out-of-tree builds
      run: |
        echo "CPYTHON_RO_SRCDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-ro-srcdir)" >> "$GITHUB_ENV"
        echo "CPYTHON_BUILDDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-builddir)" >> "$GITHUB_ENV"
    - name: Create directories for read-only out-of-tree builds
      run: mkdir -p "$CPYTHON_RO_SRCDIR" "$CPYTHON_BUILDDIR"
    - name: Bind mount sources read-only
      run: sudo mount --bind -o ro "$GITHUB_WORKSPACE" "$CPYTHON_RO_SRCDIR"
    - name: Runner image version
      run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
    - name: Configure CPython out-of-tree
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      # `test_unpickle_module_race` writes to the source directory, which is
      # read-only during builds — so we exclude it from profiling with BOLT.
      run: >-
        PROFILE_TASK='-m test --pgo --ignore test_unpickle_module_race'
        ../cpython-ro-srcdir/configure
        --config-cache
        --with-pydebug
        --enable-slower-safety
        --enable-safety
        --with-openssl="$OPENSSL_DIR"
        ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
        ${{ fromJSON(inputs.bolt-optimizations) && '--enable-bolt' || '' }}
    - name: Build CPython out-of-tree
      if: ${{ inputs.free-threading }}
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      run: make -j
    - name: Build CPython out-of-tree (for compiler warning check)
      if: ${{ !inputs.free-threading }}
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      run: set -o pipefail; make -j --output-sync 2>&1 | tee compiler_output_ubuntu.txt
    - name: Display build info
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      run: make pythoninfo
    - name: Check compiler warnings
      if: ${{ !inputs.free-threading }}
      run: >-
        python Tools/build/check_warnings.py
        --compiler-output-file-path="${CPYTHON_BUILDDIR}/compiler_output_ubuntu.txt"
        --warning-ignore-file-path "${GITHUB_WORKSPACE}/Tools/build/.warningignore_ubuntu"
        --compiler-output-type=gcc
        --fail-on-regression
        --fail-on-improvement
        --path-prefix="../cpython-ro-srcdir/"
    - name: Remount sources writable for tests
      # some tests write to srcdir, lack of pyc files slows down testing
      run: sudo mount "$CPYTHON_RO_SRCDIR" -oremount,rw
    - name: Tests
      working-directory: ${{ env.CPYTHON_BUILDDIR }}
      run: xvfb-run make ci EXTRATESTOPTS="${TEST_OPTS}"
      env:
        TEST_OPTS: ${{ inputs.test-opts }}
reusable-wasi .github/workflows/reusable-wasi.yml
Triggers
workflow_call
Runs on
ubuntu-24.04-arm
Jobs
build-wasi-reusable
Actions
bytecodealliance/actions/wasmtime/setup, bytecodealliance/setup-wasi-sdk-action
Commands
  • import tomllib from pathlib import Path import os config = tomllib.loads(Path("Platforms/WASI/config.toml").read_text()) version = config["targets"]["wasi-sdk"] with open(os.environ["GITHUB_OUTPUT"], "a") as f: f.write(f"version={version}\n")
  • echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
  • python3 Platforms/WASI configure-build-python -- --config-cache --with-pydebug
  • python3 Platforms/WASI make-build-python
  • python3 Platforms/WASI configure-host -- --config-cache
  • python3 Platforms/WASI make-host
  • make --directory "${CROSS_BUILD_WASI}" pythoninfo
  • make --directory "${CROSS_BUILD_WASI}" test
View raw YAML
name: Reusable WASI

on:
  workflow_call:

env:
  FORCE_COLOR: 1

jobs:
  build-wasi-reusable:
    name: 'build and test'
    runs-on: ubuntu-24.04-arm
    timeout-minutes: 60
    env:
      WASMTIME_VERSION: 38.0.3
      CROSS_BUILD_PYTHON: cross-build/build
      CROSS_BUILD_WASI: cross-build/wasm32-wasip1
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    # No problem resolver registered as one doesn't currently exist for Clang.
    - name: "Install wasmtime"
      uses: bytecodealliance/actions/wasmtime/setup@v1
      with:
        version: ${{ env.WASMTIME_VERSION }}
    - name: "Read WASI SDK version"
      id: wasi-sdk-version
      run: |
        import tomllib
        from pathlib import Path
        import os
        config = tomllib.loads(Path("Platforms/WASI/config.toml").read_text())
        version = config["targets"]["wasi-sdk"]
        with open(os.environ["GITHUB_OUTPUT"], "a") as f:
            f.write(f"version={version}\n")
      shell: python
    - name: "Install WASI SDK"
      id: install-wasi-sdk
      uses: bytecodealliance/setup-wasi-sdk-action@b2de090b44eb70013ee96b393727d473b35e1728
      with:
        version: ${{ steps.wasi-sdk-version.outputs.version }}
        add-to-path: false
    - name: "Install Python"
      uses: actions/setup-python@v6
      with:
        python-version: '3.x'
    - name: "Runner image version"
      run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
    - name: "Configure build Python"
      run: python3 Platforms/WASI configure-build-python -- --config-cache --with-pydebug
    - name: "Make build Python"
      run: python3 Platforms/WASI make-build-python
    - name: "Configure host"
      # `--with-pydebug` inferred from configure-build-python
      run: python3 Platforms/WASI configure-host -- --config-cache
      env:
        WASI_SDK_PATH: ${{ steps.install-wasi-sdk.outputs.wasi-sdk-path }}
    - name: "Make host"
      run: python3 Platforms/WASI make-host
    - name: "Display build info"
      run: make --directory "${CROSS_BUILD_WASI}" pythoninfo
    - name: "Test"
      run: make --directory "${CROSS_BUILD_WASI}" test
reusable-windows .github/workflows/reusable-windows.yml
Triggers
workflow_call
Runs on
${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025-vs2026' }}
Jobs
build
Commands
  • echo "::add-matcher::.github/problem-matchers/msvc.json"
  • .\\PCbuild\\build.bat -e -d -v -p "${ARCH}" ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
  • .\\python.bat -m test.pythoninfo
  • .\\PCbuild\\rt.bat -p "${ARCH}" -d -q --fast-ci ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
View raw YAML
name: Reusable Windows

on:
  workflow_call:
    inputs:
      arch:
        description: CPU architecture
        required: true
        type: string
      free-threading:
        description: Whether to compile CPython in free-threading mode
        required: false
        type: boolean
        default: false

env:
  FORCE_COLOR: 1
  IncludeUwp: >-
    true

jobs:
  build:
    name: Build and test (${{ inputs.arch }})
    runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025-vs2026' }}
    timeout-minutes: 60
    env:
      ARCH: ${{ inputs.arch }}
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: Register MSVC problem matcher
      if: inputs.arch != 'Win32'
      run: echo "::add-matcher::.github/problem-matchers/msvc.json"
    - name: Build CPython
      run: >-
        .\\PCbuild\\build.bat
        -e -d -v
        -p "${ARCH}"
        ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
      shell: bash
    - name: Display build info
      run: .\\python.bat -m test.pythoninfo
    - name: Tests
      run: >-
        .\\PCbuild\\rt.bat
        -p "${ARCH}"
        -d -q --fast-ci
        ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
      shell: bash
reusable-windows-msi perms .github/workflows/reusable-windows-msi.yml
Triggers
workflow_call
Runs on
${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025-vs2026' }}
Jobs
build
Commands
  • ./Tools/msi/build.bat --doc -"${ARCH}"
View raw YAML
name: Reusable Windows MSI

on:
  workflow_call:
    inputs:
      arch:
        description: CPU architecture
        required: true
        type: string

permissions:
  contents: read

env:
  FORCE_COLOR: 1

jobs:
  build:
    name: installer for ${{ inputs.arch }}
    runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025-vs2026' }}
    timeout-minutes: 60
    env:
      ARCH: ${{ inputs.arch }}
      IncludeFreethreaded: true
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: Build CPython installer
      run: ./Tools/msi/build.bat --doc -"${ARCH}"
      shell: bash
stale .github/workflows/stale.yml
Triggers
schedule
Runs on
ubuntu-latest
Jobs
stale
Actions
actions/stale
View raw YAML
name: Mark stale pull requests

on:
  schedule:
  - cron: "0 */6 * * *"

jobs:
  stale:
    if: github.repository_owner == 'python'
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    timeout-minutes: 10

    steps:
    - name: "Check PRs"
      uses: actions/stale@v10
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}
        stale-pr-message: 'This PR is stale because it has been open for 30 days with no activity.'
        stale-pr-label: 'stale'
        days-before-issue-stale: -1
        days-before-pr-stale: 30
        days-before-close: -1
        ascending: true
        operations-per-run: 120
tail-call matrix perms .github/workflows/tail-call.yml
Triggers
pull_request, push, workflow_dispatch
Runs on
${{ matrix.runner }}, ${{ matrix.runner }}, ${{ matrix.runner }}
Jobs
windows, macos, linux
Matrix
include, include.architecture, include.build_flags, include.configure_flags, include.run_tests, include.runner, include.target→ , --disable-gil, --with-pydebug, False, True, aarch64-apple-darwin/clang, aarch64-unknown-linux-gnu/gcc, macos-26, macos-26-intel, ubuntu-24.04, ubuntu-24.04-arm, windows-2025-vs2026, x64, x86_64-apple-darwin/clang, x86_64-pc-windows-msvc/msvc, x86_64-pc-windows-msvc/msvc-free-threading, x86_64-unknown-linux-gnu/gcc, x86_64-unknown-linux-gnu/gcc-free-threading
Commands
  • ./PCbuild/build.bat --tail-call-interp ${{ matrix.build_flags }} -c Release -p ${{ matrix.architecture }}
  • ./PCbuild/rt.bat -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3
  • brew update brew install llvm@${{ env.LLVM_VERSION }}
  • export SDKROOT="$(xcrun --show-sdk-path)" export PATH="/usr/local/opt/llvm@${{ env.LLVM_VERSION }}/bin:$PATH" export PATH="/opt/homebrew/opt/llvm@${{ env.LLVM_VERSION }}/bin:$PATH" CC=clang-${{ env.LLVM_VERSION }} ./configure --with-tail-call-interp make all --jobs 4
  • ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
  • sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ env.LLVM_VERSION }} export PATH="$(llvm-config-${{ env.LLVM_VERSION }} --bindir):$PATH" CC=clang-${{ env.LLVM_VERSION }} ./configure --with-tail-call-interp ${{ matrix.configure_flags }} make all --jobs 4
  • ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
View raw YAML
name: Tail calling interpreter
on:
  pull_request:
    paths: &paths
      - '.github/workflows/tail-call.yml'
      - 'Python/bytecodes.c'
      - 'Python/ceval.c'
      - 'Python/ceval_macros.h'
      - 'Python/generated_cases.c.h'
  push:
    paths: *paths
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

env:
  FORCE_COLOR: 1
  LLVM_VERSION: 21

jobs:
  windows:
    name: ${{ matrix.target }}
    runs-on: ${{ matrix.runner }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-pc-windows-msvc/msvc
            architecture: x64
            runner: windows-2025-vs2026
            build_flags: ""
            run_tests: true
          - target: x86_64-pc-windows-msvc/msvc-free-threading
            architecture: x64
            runner: windows-2025-vs2026
            build_flags: --disable-gil
            run_tests: false
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3.11'
      - name: Build
        shell: pwsh
        run: |
          ./PCbuild/build.bat --tail-call-interp ${{ matrix.build_flags }} -c Release -p ${{ matrix.architecture }}
      - name: Test
        if: matrix.run_tests
        shell: pwsh
        run: |
          ./PCbuild/rt.bat -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3

  macos:
    name: ${{ matrix.target }}
    runs-on: ${{ matrix.runner }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-apple-darwin/clang
            runner: macos-26-intel
          - target: aarch64-apple-darwin/clang
            runner: macos-26
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3.11'
      - name: Install dependencies
        run: |
          brew update
          brew install llvm@${{ env.LLVM_VERSION }}
      - name: Build
        run: |
          export SDKROOT="$(xcrun --show-sdk-path)"
          export PATH="/usr/local/opt/llvm@${{ env.LLVM_VERSION }}/bin:$PATH"
          export PATH="/opt/homebrew/opt/llvm@${{ env.LLVM_VERSION }}/bin:$PATH"
          CC=clang-${{ env.LLVM_VERSION }} ./configure --with-tail-call-interp
          make all --jobs 4
      - name: Test
        run: |
          ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3

  linux:
    name: ${{ matrix.target }}
    runs-on: ${{ matrix.runner }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu/gcc
            runner: ubuntu-24.04
            configure_flags: --with-pydebug
          - target: x86_64-unknown-linux-gnu/gcc-free-threading
            runner: ubuntu-24.04
            configure_flags: --disable-gil
          - target: aarch64-unknown-linux-gnu/gcc
            runner: ubuntu-24.04-arm
            configure_flags: --with-pydebug
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3.11'
      - name: Build
        run: |
          sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ env.LLVM_VERSION }}
          export PATH="$(llvm-config-${{ env.LLVM_VERSION }} --bindir):$PATH"
          CC=clang-${{ env.LLVM_VERSION }} ./configure --with-tail-call-interp ${{ matrix.configure_flags }}
          make all --jobs 4
      - name: Test
        run: |
          ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
verify-ensurepip-wheels perms .github/workflows/verify-ensurepip-wheels.yml
Triggers
workflow_dispatch, push, pull_request
Runs on
ubuntu-latest
Jobs
verify
Commands
  • ./Tools/build/verify_ensurepip_wheels.py
View raw YAML
name: Verify bundled wheels

on:
  workflow_dispatch:
  push:
    paths:
      - 'Lib/ensurepip/_bundled/**'
      - '.github/workflows/verify-ensurepip-wheels.yml'
      - 'Tools/build/verify_ensurepip_wheels.py'
  pull_request:
    paths:
      - 'Lib/ensurepip/_bundled/**'
      - '.github/workflows/verify-ensurepip-wheels.yml'
      - 'Tools/build/verify_ensurepip_wheels.py'

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:
  verify:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: actions/setup-python@v6
        with:
          python-version: '3'
      - name: Compare checksum of bundled wheels to the ones published on PyPI
        run: ./Tools/build/verify_ensurepip_wheels.py
verify-expat perms .github/workflows/verify-expat.yml
Triggers
workflow_dispatch, push, pull_request
Runs on
ubuntu-latest
Jobs
verify
Commands
  • ./Modules/expat/refresh.sh git diff --exit-code Modules/expat/
View raw YAML
name: Verify bundled libexpat

on:
  workflow_dispatch:
  push:
    paths:
      - 'Modules/expat/**'
      - '.github/workflows/verify-expat.yml'
  pull_request:
    paths:
      - 'Modules/expat/**'
      - '.github/workflows/verify-expat.yml'

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

jobs:
  verify:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - name: Download and verify bundled libexpat files
        run: |
          ./Modules/expat/refresh.sh
          git diff --exit-code Modules/expat/