| name: Tests |
| |
| on: |
| workflow_dispatch: |
| push: |
| branches: |
| - 'main' |
| - '3.*' |
| pull_request: |
| branches: |
| - 'main' |
| - '3.*' |
| |
| permissions: |
| contents: read |
| |
| concurrency: |
| group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}-reusable |
| cancel-in-progress: true |
| |
| jobs: |
| check_source: |
| name: Change detection |
| # To use boolean outputs from this job, parse them as JSON. |
| # Here's some examples: |
| # |
| # if: fromJSON(needs.check_source.outputs.run-docs) |
| # |
| # ${{ |
| # fromJSON(needs.check_source.outputs.run_tests) |
| # && 'truthy-branch' |
| # || 'falsy-branch' |
| # }} |
| # |
| uses: ./.github/workflows/reusable-change-detection.yml |
| |
| check-docs: |
| name: Docs |
| needs: check_source |
| if: fromJSON(needs.check_source.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:2024.10.16.11360930377 |
| timeout-minutes: 60 |
| needs: check_source |
| if: needs.check_source.outputs.run_tests == 'true' |
| steps: |
| - name: Install Git |
| run: | |
| apt install git -yq |
| git config --global --add safe.directory "$GITHUB_WORKSPACE" |
| - uses: actions/checkout@v4 |
| with: |
| fetch-depth: 1 |
| - name: Runner image version |
| run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV" |
| - name: Check Autoconf and aclocal versions |
| run: | |
| grep "Generated by GNU Autoconf 2.71" 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-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 |
| |
| 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: check_source |
| if: needs.check_source.outputs.run_tests == 'true' |
| steps: |
| - uses: actions/checkout@v4 |
| - uses: actions/setup-python@v5 |
| with: |
| python-version: '3.x' |
| - name: Runner image version |
| run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV" |
| - name: Restore config.cache |
| uses: actions/cache@v4 |
| with: |
| path: config.cache |
| # Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python |
| key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ 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: 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 |
| |
| build_windows: |
| name: >- |
| Windows |
| ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }} |
| needs: check_source |
| if: fromJSON(needs.check_source.outputs.run_tests) |
| strategy: |
| matrix: |
| arch: |
| - Win32 |
| - x64 |
| - arm64 |
| free-threading: |
| - false |
| - true |
| uses: ./.github/workflows/reusable-windows.yml |
| with: |
| arch: ${{ matrix.arch }} |
| free-threading: ${{ matrix.free-threading }} |
| |
| build_windows_msi: |
| name: >- # ${{ '' } is a hack to nest jobs under the same sidebar category |
| Windows MSI${{ '' }} |
| needs: check_source |
| if: fromJSON(needs.check_source.outputs.run-win-msi) |
| strategy: |
| 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: check_source |
| if: needs.check_source.outputs.run_tests == 'true' |
| strategy: |
| fail-fast: false |
| matrix: |
| # Cirrus and macos-14 are M1, macos-13 is default GHA Intel. |
| # macOS 13 only runs tests against the GIL-enabled CPython. |
| # Cirrus used for upstream, macos-14 for forks. |
| os: |
| - ghcr.io/cirruslabs/macos-runner:sonoma |
| - macos-14 |
| - macos-13 |
| is-fork: # only used for the exclusion trick |
| - ${{ github.repository_owner != 'python' }} |
| free-threading: |
| - false |
| - true |
| exclude: |
| - os: ghcr.io/cirruslabs/macos-runner:sonoma |
| is-fork: true |
| - os: macos-14 |
| is-fork: false |
| - os: macos-13 |
| free-threading: true |
| uses: ./.github/workflows/reusable-macos.yml |
| with: |
| config_hash: ${{ needs.check_source.outputs.config_hash }} |
| free-threading: ${{ matrix.free-threading }} |
| os: ${{ matrix.os }} |
| |
| build_ubuntu: |
| name: >- |
| Ubuntu |
| ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }} |
| needs: check_source |
| if: needs.check_source.outputs.run_tests == 'true' |
| strategy: |
| matrix: |
| free-threading: |
| - false |
| - true |
| uses: ./.github/workflows/reusable-ubuntu.yml |
| with: |
| config_hash: ${{ needs.check_source.outputs.config_hash }} |
| free-threading: ${{ matrix.free-threading }} |
| |
| build_ubuntu_ssltests: |
| name: 'Ubuntu SSL tests with OpenSSL' |
| runs-on: ${{ matrix.os }} |
| timeout-minutes: 60 |
| needs: check_source |
| if: needs.check_source.outputs.run_tests == 'true' |
| strategy: |
| fail-fast: false |
| matrix: |
| os: [ubuntu-24.04] |
| openssl_ver: [3.0.15, 3.1.7, 3.2.3, 3.3.2] |
| 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: Runner image version |
| run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV" |
| - name: Restore config.cache |
| uses: actions/cache@v4 |
| with: |
| path: config.cache |
| key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ 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@v4 |
| 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: 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 CFLAGS="-fdiagnostics-format=json" --config-cache --enable-slower-safety --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_wasi: |
| name: 'WASI' |
| needs: check_source |
| if: needs.check_source.outputs.run_tests == 'true' |
| uses: ./.github/workflows/reusable-wasi.yml |
| with: |
| config_hash: ${{ needs.check_source.outputs.config_hash }} |
| |
| test_hypothesis: |
| name: "Hypothesis tests on Ubuntu" |
| runs-on: ubuntu-24.04 |
| timeout-minutes: 60 |
| needs: check_source |
| if: needs.check_source.outputs.run_tests == 'true' && needs.check_source.outputs.run_hypothesis == 'true' |
| env: |
| OPENSSL_VER: 3.0.15 |
| PYTHONSTRICTEXTENSIONBUILD: 1 |
| steps: |
| - uses: actions/checkout@v4 |
| - 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@v4 |
| 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: 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_VERSION=${ImageVersion}" >> "$GITHUB_ENV" |
| - name: Restore config.cache |
| uses: actions/cache@v4 |
| with: |
| path: ${{ env.CPYTHON_BUILDDIR }}/config.cache |
| key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }} |
| - 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@v4 |
| 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. |
| ${{ env.VENV_PYTHON }} -m test \ |
| -W \ |
| -o \ |
| -j4 \ |
| -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@v4 |
| 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: check_source |
| if: needs.check_source.outputs.run_tests == 'true' |
| strategy: |
| matrix: |
| os: [ubuntu-24.04] |
| env: |
| OPENSSL_VER: 3.0.15 |
| PYTHONSTRICTEXTENSIONBUILD: 1 |
| ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 |
| steps: |
| - uses: actions/checkout@v4 |
| - name: Runner image version |
| run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV" |
| - name: Restore config.cache |
| uses: actions/cache@v4 |
| with: |
| path: config.cache |
| key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ 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@v4 |
| 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: 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 ci |
| |
| build_tsan: |
| name: 'Thread sanitizer' |
| needs: check_source |
| if: needs.check_source.outputs.run_tests == 'true' |
| uses: ./.github/workflows/reusable-tsan.yml |
| with: |
| config_hash: ${{ needs.check_source.outputs.config_hash }} |
| options: ./configure --config-cache --with-thread-sanitizer --with-pydebug |
| suppressions_path: Tools/tsan/supressions.txt |
| tsan_logs_artifact_name: tsan-logs-default |
| |
| build_tsan_free_threading: |
| name: 'Thread sanitizer (free-threading)' |
| needs: check_source |
| if: needs.check_source.outputs.run_tests == 'true' |
| uses: ./.github/workflows/reusable-tsan.yml |
| with: |
| config_hash: ${{ needs.check_source.outputs.config_hash }} |
| options: ./configure --config-cache --disable-gil --with-thread-sanitizer --with-pydebug |
| suppressions_path: Tools/tsan/suppressions_free_threading.txt |
| tsan_logs_artifact_name: tsan-logs-free-threading |
| |
| # CIFuzz job based on https://google.github.io/oss-fuzz/getting-started/continuous-integration/ |
| cifuzz: |
| name: CIFuzz |
| runs-on: ubuntu-latest |
| timeout-minutes: 60 |
| needs: check_source |
| if: needs.check_source.outputs.run_cifuzz == 'true' |
| permissions: |
| security-events: write |
| strategy: |
| fail-fast: false |
| matrix: |
| sanitizer: [address, undefined, memory] |
| steps: |
| - name: Build fuzzers (${{ matrix.sanitizer }}) |
| id: build |
| uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master |
| with: |
| oss-fuzz-project-name: cpython3 |
| sanitizer: ${{ matrix.sanitizer }} |
| - name: Run fuzzers (${{ matrix.sanitizer }}) |
| uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master |
| with: |
| fuzz-seconds: 600 |
| oss-fuzz-project-name: cpython3 |
| output-sarif: true |
| sanitizer: ${{ matrix.sanitizer }} |
| - name: Upload crash |
| uses: actions/upload-artifact@v4 |
| if: failure() && steps.build.outcome == 'success' |
| with: |
| name: ${{ matrix.sanitizer }}-artifacts |
| path: ./out/artifacts |
| - name: Upload SARIF |
| if: always() && steps.build.outcome == 'success' |
| uses: github/codeql-action/upload-sarif@v3 |
| with: |
| sarif_file: cifuzz-sarif/results.sarif |
| checkout_path: cifuzz-sarif |
| |
| 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_autoconf_regen |
| - check_generated_files |
| - build_macos |
| - build_ubuntu |
| - build_ubuntu_ssltests |
| - build_wasi |
| - build_windows |
| - build_windows_msi |
| - test_hypothesis |
| - build_asan |
| - build_tsan |
| - build_tsan_free_threading |
| - cifuzz |
| |
| runs-on: ubuntu-latest |
| |
| steps: |
| - name: Check whether the needed jobs succeeded or failed |
| uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe |
| with: |
| allowed-failures: >- |
| build_ubuntu_ssltests, |
| build_windows_msi, |
| cifuzz, |
| test_hypothesis, |
| allowed-skips: >- |
| ${{ |
| !fromJSON(needs.check_source.outputs.run-docs) |
| && ' |
| check-docs, |
| ' |
| || '' |
| }} |
| ${{ |
| needs.check_source.outputs.run_tests != 'true' |
| && ' |
| check_autoconf_regen, |
| check_generated_files, |
| build_macos, |
| build_ubuntu, |
| build_ubuntu_ssltests, |
| build_wasi, |
| build_windows, |
| build_asan, |
| build_tsan, |
| build_tsan_free_threading, |
| ' |
| || '' |
| }} |
| ${{ |
| !fromJSON(needs.check_source.outputs.run_cifuzz) |
| && ' |
| cifuzz, |
| ' |
| || '' |
| }} |
| ${{ |
| !fromJSON(needs.check_source.outputs.run_hypothesis) |
| && ' |
| test_hypothesis, |
| ' |
| || '' |
| }} |
| jobs: ${{ toJSON(needs) }} |