name: Tests # gh-84728: "paths-ignore" is not used to skip documentation-only PRs, because # it prevents to mark a job as mandatory. A PR cannot be merged if a job is # mandatory but not scheduled because of "paths-ignore". on: workflow_dispatch: push: branches: - 'main' - '3.11' - '3.10' - '3.9' - '3.8' pull_request: branches: - 'main' - '3.11' - '3.10' - '3.9' - '3.8' permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}-reusable cancel-in-progress: true jobs: check_source: name: 'Check for source changes' runs-on: ubuntu-latest timeout-minutes: 10 outputs: run-docs: ${{ steps.docs-changes.outputs.run-docs || false }} run_tests: ${{ steps.check.outputs.run_tests }} run_ssl_tests: ${{ steps.check.outputs.run_ssl_tests }} config_hash: ${{ steps.config_hash.outputs.hash }} steps: - uses: actions/checkout@v4 - name: Check for source changes id: check run: | if [ -z "$GITHUB_BASE_REF" ]; then echo "run_tests=true" >> $GITHUB_OUTPUT echo "run_ssl_tests=true" >> $GITHUB_OUTPUT else git fetch origin $GITHUB_BASE_REF --depth=1 # git diff "origin/$GITHUB_BASE_REF..." (3 dots) may be more # reliable than git diff "origin/$GITHUB_BASE_REF.." (2 dots), # but it requires to download more commits (this job uses # "git fetch --depth=1"). # # git diff "origin/$GITHUB_BASE_REF..." (3 dots) works with Git # 2.26, but Git 2.28 is stricter and fails with "no merge base". # # git diff "origin/$GITHUB_BASE_REF.." (2 dots) should be enough on # GitHub, since GitHub starts by merging origin/$GITHUB_BASE_REF # into the PR branch anyway. # # https://github.com/python/core-workflow/issues/373 git diff --name-only origin/$GITHUB_BASE_REF.. | grep -qvE '(\.rst$|^Doc|^Misc|^\.pre-commit-config\.yaml$|\.ruff\.toml$)' && echo "run_tests=true" >> $GITHUB_OUTPUT || true git diff --name-only origin/$GITHUB_BASE_REF.. | grep -qE '(ssl|hashlib|hmac|^.github)' && echo "run_ssl_tests=true" >> $GITHUB_OUTPUT || true fi - name: Compute hash for config cache key id: config_hash run: | echo "hash=${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }}" >> $GITHUB_OUTPUT - name: Get a list of the changed documentation-related files if: github.event_name == 'pull_request' id: changed-docs-files uses: Ana06/get-changed-files@v2.2.0 with: filter: | Doc/** Misc/** .github/workflows/reusable-docs.yml format: csv # works for paths with spaces - name: Check for docs changes if: >- github.event_name == 'pull_request' && steps.changed-docs-files.outputs.added_modified_renamed != '' id: docs-changes run: | echo "run-docs=true" >> "${GITHUB_OUTPUT}" check-docs: name: Docs needs: check_source if: fromJSON(needs.check_source.outputs.run-docs) uses: ./.github/workflows/reusable-docs.yml check_abi: name: 'Check if the ABI has changed' runs-on: ubuntu-20.04 needs: check_source if: needs.check_source.outputs.run_tests == 'true' steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 - name: Install dependencies run: | sudo ./.github/workflows/posix-deps-apt.sh sudo apt-get install -yq abigail-tools - name: Build CPython env: CFLAGS: -g3 -O0 run: | # Build Python with the libpython dynamic library ./configure --enable-shared make -j4 - name: Check for changes in the ABI id: check run: | if ! make check-abidump; then echo "Generated ABI file is not up to date." echo "Please add the release manager of this branch as a reviewer of this PR." echo "" echo "The up to date ABI file should be attached to this build as an artifact." echo "" echo "To learn more about this check: https://devguide.python.org/setup/#regenerate-the-abi-dump" echo "" exit 1 fi - name: Generate updated ABI files if: ${{ failure() && steps.check.conclusion == 'failure' }} run: | make regen-abidump - uses: actions/upload-artifact@v3 name: Publish updated ABI files if: ${{ failure() && steps.check.conclusion == 'failure' }} with: name: abi-data path: ./Doc/data/*.abi check_generated_files: name: 'Check if generated files are up to date' runs-on: ubuntu-latest timeout-minutes: 60 needs: check_source if: needs.check_source.outputs.run_tests == 'true' steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.x' - name: Restore config.cache uses: actions/cache@v3 with: path: config.cache key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }}-${{ env.pythonLocation }} - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Add ccache to PATH run: echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV - name: Configure ccache action uses: hendrikmuhs/ccache-action@v1.2 with: save: false - name: Check Autoconf version 2.69 and aclocal 1.16.3 run: | grep "Generated by GNU Autoconf 2.69" configure grep "aclocal 1.16.3" aclocal.m4 grep -q "runstatedir" configure grep -q "PKG_PROG_PKG_CONFIG" aclocal.m4 - name: Configure CPython run: | # Build Python with the libpython dynamic library ./configure --config-cache --with-pydebug --enable-shared - name: Regenerate autoconf files with container image run: make regen-configure - name: Build CPython run: | # Deepfreeze will usually cause global objects to be added or removed, # so we run it before regen-global-objects gets rum (in regen-all). make regen-deepfreeze make -j4 regen-all make regen-stdlib-module-names - 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 build_windows: name: 'Windows' needs: check_source if: needs.check_source.outputs.run_tests == 'true' uses: ./.github/workflows/reusable-windows.yml build_macos: name: 'macOS' needs: check_source if: needs.check_source.outputs.run_tests == 'true' uses: ./.github/workflows/reusable-macos.yml with: config_hash: ${{ needs.check_source.outputs.config_hash }} build_ubuntu: name: 'Ubuntu' needs: check_source if: needs.check_source.outputs.run_tests == 'true' uses: ./.github/workflows/reusable-ubuntu.yml with: config_hash: ${{ needs.check_source.outputs.config_hash }} options: | ../cpython-ro-srcdir/configure \ --config-cache \ --with-pydebug \ --with-openssl=$OPENSSL_DIR build_ubuntu_ssltests: name: 'Ubuntu SSL tests with OpenSSL' runs-on: ubuntu-20.04 timeout-minutes: 60 needs: check_source if: needs.check_source.outputs.run_tests == 'true' && needs.check_source.outputs.run_ssl_tests == 'true' strategy: fail-fast: false matrix: openssl_ver: [1.1.1w, 3.0.13, 3.1.5, 3.2.1] env: OPENSSL_VER: ${{ matrix.openssl_ver }} MULTISSL_DIR: ${{ github.workspace }}/multissl OPENSSL_DIR: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }} LD_LIBRARY_PATH: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }}/lib steps: - uses: actions/checkout@v4 - name: Restore config.cache uses: actions/cache@v3 with: path: config.cache key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - 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@v3 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: Add ccache to PATH run: | echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV - name: Configure ccache action uses: hendrikmuhs/ccache-action@v1.2 with: save: false - name: Configure CPython run: ./configure --config-cache --with-pydebug --with-openssl=$OPENSSL_DIR - name: Build CPython run: make -j4 - name: Display build info run: make pythoninfo - name: SSL tests run: ./python Lib/test/ssltests.py build_asan: name: 'Address sanitizer' runs-on: ubuntu-20.04 timeout-minutes: 60 needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: OPENSSL_VER: 3.0.13 PYTHONSTRICTEXTENSIONBUILD: 1 ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 steps: - uses: actions/checkout@v4 - name: Restore config.cache uses: actions/cache@v3 with: path: config.cache key: ${{ github.job }}-${{ runner.os }}-${{ needs.check_source.outputs.config_hash }} - 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@v1 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@v3 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: Add ccache to PATH run: | echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV - name: Configure ccache action uses: hendrikmuhs/ccache-action@v1.2 with: save: ${{ github.event_name == 'push' }} max-size: "200M" - name: Configure CPython run: ./configure --config-cache --with-address-sanitizer --without-pymalloc - name: Build CPython run: make -j4 - name: Display build info run: make pythoninfo - name: Tests run: xvfb-run make buildbottest TESTOPTS="-j4 -uall,-cpu" all-required-green: # This job does nothing and is only used for the branch protection name: All required checks pass if: always() needs: - check_source # Transitive dependency, needed to access `run_tests` value - check-docs - check_generated_files - build_macos - build_ubuntu - build_ubuntu_ssltests - build_windows - build_asan runs-on: ubuntu-latest steps: - name: Check whether the needed jobs succeeded or failed uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe with: allowed-failures: >- build_macos, build_ubuntu_ssltests, allowed-skips: >- ${{ !fromJSON(needs.check_source.outputs.run-docs) && ' check-docs, ' || '' }} ${{ needs.check_source.outputs.run_tests != 'true' && ' check_generated_files, build_macos, build_ubuntu, build_ubuntu_ssltests, build_windows, build_asan, ' || '' }} jobs: ${{ toJSON(needs) }}