clvmr 0.19.0

Implementation of `clvm` for Chia Network's cryptocurrency
Documentation
name: Build Mac, Linux, and Windows wheels

on:
  push:
    branches:
      - main
      - dev
    tags:
      - "**"
  pull_request:
    branches:
      - "**"

permissions:
  contents: read
  id-token: write

jobs:
  build_wheels:
    name: Wheel on ${{ matrix.os }} py-${{ matrix.python }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macos-15-intel, ubuntu-latest, windows-latest]
        python: ["3.10"]

    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - uses: chia-network/actions/setup-python@main
        name: Install Python ${{ matrix.python }}
        with:
          python-version: ${{ matrix.python }}

      - name: Update pip
        run: |
          python -m pip install --upgrade pip

      - name: Set up rust
        uses: dtolnay/rust-toolchain@stable

      - name: Install dependencies
        run: |
          python -m pip install maturin==1.12.2   # 1.12.3 changes how `--sdist` produces artifacts, breaking CI

      - name: Build MacOs with maturin on Python ${{ matrix.python }}
        if: startsWith(matrix.os, 'macos')
        env:
          MACOSX_DEPLOYMENT_TARGET: "15.0"
        run: |
          python -m venv venv
          ln -s venv/bin/activate
          . ./activate
          maturin build -m wheel/Cargo.toml --sdist -i python --release --strip --features=openssl

      - name: Build Linux in manylinux_2_28 with maturin on Python ${{ matrix.python }}
        if: startsWith(matrix.os, 'ubuntu')
        run: |
          docker run --rm \
            -v ${{ github.workspace }}:/ws --workdir=/ws \
            ghcr.io/chia-network/build-images/centos-pypa-rust-x86_64 \
            bash -exc '\
              yum -y install perl-IPC-Cmd && \
              yum -y --disablerepo=epel install openssl-devel && \
              source $HOME/.cargo/env && \
              rustup target add x86_64-unknown-linux-musl && \
              PY_VERSION=${{ matrix.python }}
              PY_VERSION=${PY_VERSION/.} && \
              echo "Python version with dot removed is $PY_VERSION" && \
              export PATH=/opt/python/cp$PY_VERSION-cp$PY_VERSION/bin/:$PATH && \
              python -m venv /venv && \
              . /venv/bin/activate && \
              pip install --upgrade pip && \
              pip install maturin==1.12.2 && \
              CC=gcc maturin build -m wheel/Cargo.toml --release --strip --manylinux 2_28 --features=openssl --sdist \
            '
          python -m venv venv
          ln -s venv/bin/activate

      - name: Build Windows with maturin on Python ${{ matrix.python }}
        if: startsWith(matrix.os, 'windows')
        run: |
          python -m venv venv
          . .\venv\Scripts\Activate.ps1
          ln -s venv\Scripts\Activate.ps1 activate
          maturin build -m wheel/Cargo.toml -i python --release --strip
          # this will install into the venv
          # it'd be better to use the wheel, but I can't figure out how to do that
          # TODO: figure this out
          # this does NOT work: pip install target/wheels/clvm_rs-*.whl
          maturin develop --release -m wheel/Cargo.toml
          # the line above also doesn't seem to work

      - name: Install clvm_rs wheel
        if: ${{ !startsWith(matrix.os, 'windows') }}
        run: |
          . ./activate
          ls target/wheels/
          # this mess puts the name of the `.whl` file into `$WHEEL_PATH`
          # remove the dot, use the `glob` lib to grab the file from the directory
          WHEEL_PATH=$(echo ${{ matrix.python }} | python -c 'DOTLESS=input().replace(".", ""); import glob; print(" ".join(glob.glob("target/wheels/clvm_rs-*-cp%s-abi3-*.whl" % DOTLESS)))' )
          echo ${WHEEL_PATH}
          pip install ${WHEEL_PATH}

      - name: Install other wheels
        run: |
          . ./activate
          python -m pip install pytest
          python -m pip install blspy

      - name: Run tests from wheel
        run: |
          . ./activate
          cd wheel/python
          pytest --import-mode append tests
          # we use `append` because otherwise the `clvm_rs` source is added
          # to `sys.path` and it uses that instead of the wheel (and so
          # ignoring `clvm_rs.so`, which is pretty important)

      - name: Upload artifacts
        uses: actions/upload-artifact@v7
        with:
          name: wheels-${{ matrix.os }}-py-${{ matrix.python }}
          path: ./target/wheels/

  upload:
    name: Upload to PyPI
    runs-on: ubuntu-latest
    needs: build_wheels
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Install python
        uses: Chia-Network/actions/setup-python@main
        with:
          python-version: "3.10"

      - name: Download artifacts
        uses: actions/download-artifact@v6
        with:
          merge-multiple: true
          pattern: wheels-*
          path: ./target/wheels/

      - name: publish (PyPi)
        if: startsWith(github.event.ref, 'refs/tags')
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: target/wheels/
          skip-existing: true

  checks:
    runs-on: ubuntu-22.04
    name: Checks
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 1
      - name: Set up rusts
        uses: dtolnay/rust-toolchain@1.92.0
      - name: Clippy
        run: cargo clippy --all-targets --workspace -- -D warnings
      - name: fmt
        run: cargo fmt --all -- --files-with-diff --check

  # leaving out Windows fuzzing for now though it seems supported
  # https://llvm.org/docs/LibFuzzer.html#q-does-libfuzzer-support-windows
  fuzz_targets:
    name: Run fuzz targets
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macos-15-intel, ubuntu-latest]
        python: ["3.10"]
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 1
      - name: Set up rusts
        uses: dtolnay/rust-toolchain@nightly
      - name: fmt
        run: |
          cd fuzz
          cargo fmt -- --files-with-diff --check
      - name: clippy
        run: |
          cd fuzz
          cargo clippy
      - name: cargo-fuzz
        run: cargo install cargo-fuzz
      - name: Restore fuzz corpus
        uses: actions/cache/restore@v5
        with:
          path: fuzz/corpus
          key: fuzz-corpus-${{ matrix.os }}-${{ hashFiles('fuzz/fuzz_targets/**/*.rs') }}
          restore-keys: |
            fuzz-corpus-${{ matrix.os }}-
      - name: Build all fuzz targets
        run: cargo +nightly fuzz build
      - name: Run fuzz targets
        run: |
          for target in $(cargo +nightly fuzz list); do
            cargo +nightly fuzz run "$target" -- -max_total_time=30 || exit 1
          done
      - name: Save fuzz corpus
        uses: actions/cache/save@v5
        if: always()
        with:
          path: fuzz/corpus
          key: fuzz-corpus-${{ matrix.os }}-${{ hashFiles('fuzz/fuzz_targets/**/*.rs') }}-${{ github.run_id }}

  unit_tests:
    name: Unit tests
    runs-on: ${{ matrix.os }}
    env:
      CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS: true
      CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS: true
    strategy:
      fail-fast: false
      matrix:
        os: [macos-15-intel, ubuntu-latest, windows-latest]
        python: ["3.10"]
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 1
      - name: Set up rusts
        uses: dtolnay/rust-toolchain@1.92.0

      - uses: chia-network/actions/setup-python@main
        name: Install Python ${{ matrix.python }}
        with:
          python-version: ${{ matrix.python }}

      - name: build
        run: cargo build --workspace --exclude clvm_rs-fuzz

      - name: cargo test (default)
        run: cargo test && cargo test --release -- --include-ignored

      - name: cargo test (allocator-debug)
        run: cargo test --features allocator-debug && cargo test --features allocator-debug --release

      - name: cargo test (counters)
        run: cargo test --features=counters && cargo test --features=counters --release

      - name: cargo test (pre-eval)
        run: cargo test --features=pre-eval && cargo test --features=pre-eval --release

      - name: cargo test (pre-eval and counters)
        run: cargo test --features=pre-eval,counters && cargo test --features=pre-eval,counters --release

      - name: cargo test (no-fastpath)
        run: cargo test --features=no-fastpath && cargo test --features=no-fastpath --release

  coverage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - name: Run for coverage
        run: |
          sudo apt-get update
          sudo apt-get install lcov -y
          rustup component add llvm-tools-preview
          export CARGOFLAGS="-j $(nproc)"
          cargo install grcov
          export RUSTFLAGS="-Cinstrument-coverage"
          export LLVM_PROFILE_FILE=$(pwd)/target/clvm_rs-%p-%m.profraw
          export CARGO_TARGET_DIR=$(pwd)/target
          export RUST_TEST_THREADS=$(nproc)
          cargo test --release --workspace
          python -m venv venv
          source venv/bin/activate
          pip install colorama maturin pytest pytest-cov
          maturin develop --release -m wheel/Cargo.toml --features=openssl
          (cd tests && python generate-programs.py && python run-programs.py) || true
          pytest wheel/python/tests --cov=clvm_rs --cov-report lcov:py_cov.info --cov-branch
          grcov . --binary-path target -s . --branch --ignore-not-existing --ignore='*/.cargo/*' --ignore='target/*' -o pre_rust_cov.info
          python -c 'with open("pre_rust_cov.info") as f: lines = [l for l in f if not (l.startswith("DA:") and int(l.split(",")[1].strip()) >= 2**63)]; open("rust_cov.info", "w").writelines(lines)'
          lcov --add-tracefile rust_cov.info -a py_cov.info -o lcov.info
      - name: Upload to Coveralls
        uses: coverallsapp/github-action@v2
        if: always()
        continue-on-error: true
        env:
          COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
        with:
          fail-on-error: false
          path-to-lcov: "./lcov.info"