readcon-core 0.14.4

An oxidized single and multiple CON file reader and writer with FFI bindings for ergonomic C/C++ usage.
Documentation
# Multi-language coverage for Codecov (rust / python / julia / fortran flags).
# Adapted from https://github.com/metatensor/metatensor/pull/939 and
# HaoZeke/capnp-fortran coverage soft-fail pattern.
#
# Live dashboard: https://app.codecov.io/gh/lode-org/readcon-core
#
# Auth: Codecov GitHub App is installed on lode-org (all repos). Uploads use
# OIDC (use_oidc: true) so a HaoZeke-scoped CODECOV_TOKEN is not required and
# will not 404 "Repository not found" for the org slug. Optional CODECOV_TOKEN
# remains unused when OIDC is enabled. fail_ci_if_error: false soft-fails
# upload issues so coverage generation still greens the job.

name: Coverage

on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

concurrency:
  group: coverage-${{ github.ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

# OIDC for codecov-action (required when use_oidc: true)
permissions:
  contents: read
  id-token: write

jobs:
  coverage-rust:
    runs-on: ubuntu-22.04
    name: rust coverage
    strategy:
      matrix:
        llvm-version: [20]
    env:
      CMAKE_C_COMPILER: clang-${{ matrix.llvm-version }}
      CMAKE_CXX_COMPILER: clang++-${{ matrix.llvm-version }}
      CC: clang-${{ matrix.llvm-version }}
      CXX: clang++-${{ matrix.llvm-version }}

    steps:
      - uses: actions/checkout@v6

      - name: Setup LLVM
        run: |
            wget https://apt.llvm.org/llvm.sh
            chmod +x llvm.sh
            sudo ./llvm.sh ${{ matrix.llvm-version }}
            sudo apt-get update
            sudo apt-get install --no-install-recommends -y clang-${{ matrix.llvm-version }}

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview

      - name: Install cargo-llvm-cov
        uses: taiki-e/install-action@cargo-llvm-cov

      - uses: prefix-dev/setup-pixi@v0.9.4
        with:
          cache: true
          environments: default

      # Intentionally no sccache: coverage instrumentation must not be cached/stripped.

      - name: Generate rust code coverage
        id: coverage
        env:
          # Full non-CUDA feature set (chemfiles/metatensor/zstd/grammar/parallel).
          # PyO3/RPC/CUDA excluded from this report (see codecov.yml ignore + script).
          READCON_COV_FEATURES: parallel,chemfiles,zstd,grammar,metatensor
        run: |
          unset RUSTC_WRAPPER || true
          CAPNP_BIN="$(pixi r -e default bash -c 'dirname "$(command -v capnp)"')"
          export PATH="${CAPNP_BIN}:${PATH}"
          bash scripts/run_coverage_rust.sh rust_codecov.json lcov.info
          test -s lcov.info
          test -s rust_codecov.json

      - name: Upload rust coverage artifact
        uses: actions/upload-artifact@v4
        with:
          name: coverage-rust
          path: |
            lcov.info
            rust_codecov.json
          if-no-files-found: error

      - name: Upload rust coverage to Codecov
        uses: codecov/codecov-action@v5
        with:
          files: lcov.info,rust_codecov.json
          flags: rust
          name: readcon-core-rust
          use_oidc: true
          fail_ci_if_error: false
          verbose: true

  coverage-python:
    runs-on: ubuntu-22.04
    name: python coverage
    strategy:
      matrix:
        llvm-version: [20]
    env:
      CMAKE_C_COMPILER: clang-${{ matrix.llvm-version }}
      CMAKE_CXX_COMPILER: clang++-${{ matrix.llvm-version }}
      CC: clang-${{ matrix.llvm-version }}
      CXX: clang++-${{ matrix.llvm-version }}
    steps:
      - uses: actions/checkout@v6

      - name: Setup LLVM
        run: |
            wget https://apt.llvm.org/llvm.sh
            chmod +x llvm.sh
            sudo ./llvm.sh ${{ matrix.llvm-version }}
            sudo apt-get update
            sudo apt-get install --no-install-recommends -y \
              clang-${{ matrix.llvm-version }} emacs-nox

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview

      - name: Install cargo-llvm-cov
        uses: taiki-e/install-action@cargo-llvm-cov

      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Generate python binding coverage (src/python.rs LCOV)
        id: coverage
        env:
          READCON_PY_FEATURES: python,chemfiles
          READCON_PY_VENV: ${{ github.workspace }}/.venv-coverage-python
          RUSTFLAGS: "-C link-arg=-fuse-ld=bfd"
        run: |
          unset RUSTC_WRAPPER || true
          bash scripts/run_coverage_python.sh python_lcov.info
          test -s python_lcov.info
          grep -E '^(SF:|DA:)' python_lcov.info | head -20

      - name: Upload python coverage artifact
        uses: actions/upload-artifact@v4
        with:
          name: coverage-python
          path: python_lcov.info
          if-no-files-found: error

      - name: Upload python coverage to Codecov
        uses: codecov/codecov-action@v5
        with:
          files: python_lcov.info
          flags: python
          name: readcon-core-python
          use_oidc: true
          fail_ci_if_error: false
          verbose: true

  coverage-julia:
    runs-on: ubuntu-22.04
    name: julia coverage
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable

      - uses: julia-actions/setup-julia@v2
        with:
          version: '1.10'

      - name: Build libreadcon_core
        run: cargo build --release --features chemfiles

      - name: Julia tests with Coverage.jl
        env:
          READCON_CORE_LIB: ${{ github.workspace }}/target/release/libreadcon_core.so
          READCON_LIB_PATH: ${{ github.workspace }}/target/release/libreadcon_core.so
          LD_LIBRARY_PATH: ${{ github.workspace }}/target/release
        run: |
          set -euo pipefail
          ROOT="${{ github.workspace }}"
          cd "$ROOT/julia/ReadCon"
          julia --project=. -e '
            using Pkg
            Pkg.instantiate()
            Pkg.add("Coverage")
            Pkg.test(; coverage=true)
            using Coverage
            cov = process_folder("src")
            LCOV.writefile("lcov.info", cov)
            println("Julia coverage files: ", length(cov))
          '
          test -s lcov.info
          # Rewrite SF: paths so Codecov maps under julia/ReadCon/src/**
          python3 "$ROOT/scripts/rewrite_julia_lcov.py" \
            lcov.info "$ROOT/julia_lcov.info" \
            --package-root "$ROOT/julia/ReadCon"
          test -s "$ROOT/julia_lcov.info"
          grep -E '^(SF:|DA:)' "$ROOT/julia_lcov.info" | head -10

      - name: Upload julia coverage artifact
        uses: actions/upload-artifact@v4
        with:
          name: coverage-julia
          path: julia_lcov.info
          if-no-files-found: error

      - name: Upload julia coverage to Codecov
        uses: codecov/codecov-action@v5
        with:
          files: julia_lcov.info
          flags: julia
          name: readcon-core-julia
          use_oidc: true
          fail_ci_if_error: false
          verbose: true

  coverage-fortran:
    runs-on: ubuntu-22.04
    name: fortran coverage
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable

      - name: Install gfortran, fpm, lcov
        run: |
          sudo apt-get update
          sudo apt-get install -y gfortran build-essential cmake lcov
          curl -LO https://github.com/fortran-lang/fpm/releases/download/v0.10.1/fpm-0.10.1-linux-x86_64
          sudo install -m 755 fpm-0.10.1-linux-x86_64 /usr/local/bin/fpm

      - name: Build libreadcon_core (chemfiles)
        run: |
          for attempt in 1 2 3; do
            if cargo build --release --features chemfiles; then
              break
            fi
            if [[ "$attempt" -eq 3 ]]; then
              exit 1
            fi
            echo "cargo build failed (attempt $attempt), retrying..." >&2
            sleep 5
          done

      - name: Fortran instrumented tests + gcov/lcov
        env:
          GFORTRAN_ERROR_BACKTRACE: "0"
        run: |
          set -euo pipefail
          ROOT="${{ github.workspace }}"
          export LD_LIBRARY_PATH="$ROOT/target/release:${LD_LIBRARY_PATH:-}"
          FFLAGS="-cpp -ffpe-summary=none --coverage"
          EXTRA="-lstdc++ --coverage"
          cd "$ROOT/fortran/ReadCon"
          rm -rf build
          fpm test --flag "-L$ROOT/target/release $FFLAGS" \
            --link-flag "-L$ROOT/target/release -lreadcon_core -ldl -lpthread -lm $EXTRA"
          find build -name '*.gcda' | head -20
          lcov --directory build --capture --output-file "$ROOT/fortran_coverage.info" \
            --rc lcov_branch_coverage=1 || \
            lcov --directory build --capture --output-file "$ROOT/fortran_coverage.info"
          lcov --extract "$ROOT/fortran_coverage.info" '*/fortran/ReadCon/src/*' \
            --output-file "$ROOT/fortran_coverage.info" || true
          if ! grep -q '^SF:' "$ROOT/fortran_coverage.info" 2>/dev/null; then
            lcov --directory build --capture --output-file "$ROOT/fortran_coverage.info"
            lcov --remove "$ROOT/fortran_coverage.info" '/usr/*' '*/fpm/*' \
              --output-file "$ROOT/fortran_coverage.info" || true
          fi
          test -s "$ROOT/fortran_coverage.info"
          grep -E '^(SF:|DA:)' "$ROOT/fortran_coverage.info" | head -20

      - name: Upload fortran coverage artifact
        uses: actions/upload-artifact@v4
        with:
          name: coverage-fortran
          path: fortran_coverage.info
          if-no-files-found: error

      - name: Upload fortran coverage to Codecov
        uses: codecov/codecov-action@v5
        with:
          files: fortran_coverage.info
          flags: fortran
          name: readcon-core-fortran
          use_oidc: true
          fail_ci_if_error: false
          verbose: true