name: CI
on:
push:
branches: [master]
pull_request:
jobs:
test:
name: Test
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test --workspace
lint:
name: Lint
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- run: cargo fmt --check
- run: cargo clippy --workspace --all-targets -- -D warnings
reranker-tests:
name: Reranker training/eval script tests
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- run: pip install lightgbm scikit-learn
- run: python3 scripts/train_reranker.py --self-test
- run: python3 -m unittest discover -s scripts/tests -p "test_*.py"
version-check:
name: Version sync
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- run: |
VER=$(grep '^version' Cargo.toml | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
echo "Checking version: $VER"
if grep -rE 'renkin = "0\.[0-9]\."' docs/; then
echo "ERROR: stale renkin version in docs/" && exit 1
fi
grep -q "version: \"${VER}\"" CITATION.cff || \
{ echo "ERROR: CITATION.cff version not updated to $VER"; exit 1; }
echo "OK: all version references match $VER"
python-smoke:
name: Python smoke
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: PyO3/maturin-action@v1
with:
command: build
args: --release --features python --out dist
- run: pip install dist/*.whl --force-reinstall
- name: Build CLI binary (for the CLI/Python coverage-mode field-parity test below)
run: cargo build --release --bin renkin
- name: Smoke test from a non-repo directory
run: |
cd /tmp
python -c "
import renkin
assert hasattr(renkin, '__version__'), '__version__ missing'
assert callable(renkin.find_routes), 'find_routes missing'
assert callable(renkin.predict_forward), 'predict_forward missing'
assert callable(renkin.validate_forward), 'validate_forward missing'
result = renkin.find_routes('c1ccccc1', 3, 1, 0)
assert result, 'find_routes returned empty'
result = renkin.find_routes('c1ccccc1', depth=3, max_routes=1, beam_width=0, template_metadata_path=None)
assert result, 'find_routes with template_metadata_path=None returned empty'
import json
result = renkin.find_routes('c1ccccc1', depth=3, max_routes=1, beam_width=0, reranker_model_path=None, reranker_freq_table_path=None)
assert result, 'find_routes with reranker paths=None returned empty'
assert 'reranker_failures' not in json.loads(result), 'reranker_failures must be absent when no reranker is configured'
result = renkin.find_routes('c1ccccc1', depth=3, max_routes=1, beam_width=0, reranker_model_path='/nonexistent/model.txt', reranker_freq_table_path='/nonexistent/freq.json')
assert result, 'find_routes must fall back to legacy ordering on a bad reranker path, not raise'
assert 'reranker_failures' not in json.loads(result), 'reranker_failures must be absent when the reranker failed to load'
result = renkin.find_routes('c1ccccc1', depth=3, max_routes=1, beam_width=0, reranker_model_path='/nonexistent/model.txt')
assert result, 'find_routes must fall back to legacy ordering when only one reranker path is given, not raise'
assert 'reranker_failures' not in json.loads(result), 'reranker_failures must be absent for a mismatched (one-None) reranker path pair too'
print('ok:', renkin.__version__)
"
- name: Coverage-mode Python tests (from repo checkout)
run: python3 -m unittest discover -s scripts/tests -p "test_python_coverage_mode.py" -v
docs-check:
name: Docs facts & examples
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Derive live facts (rule count, building block file/fallback counts)
id: facts
run: |
FACTS=$(cargo run --quiet --example doc_facts)
echo "$FACTS"
echo "rule_count=$(echo "$FACTS" | grep -oP 'HAND_CRAFTED_RULE_COUNT=\K\d+')" >> "$GITHUB_OUTPUT"
echo "bb_file_count=$(echo "$FACTS" | grep -oP 'BUILDING_BLOCK_FILE_COUNT=\K\d+')" >> "$GITHUB_OUTPUT"
echo "bb_fallback_count=$(echo "$FACTS" | grep -oP 'BUILDING_BLOCK_FALLBACK_COUNT=\K\d+')" >> "$GITHUB_OUTPUT"
- name: Check docs/README against live facts
run: python3 scripts/check_docs_facts.py "${{ steps.facts.outputs.rule_count }}" "${{ steps.facts.outputs.bb_file_count }}" "${{ steps.facts.outputs.bb_fallback_count }}"
- name: Compile and run the Rust quickstart example
run: cargo run --example quickstart
- uses: PyO3/maturin-action@v1
with:
command: build
args: --release --features python --out dist
- name: Run the Python quickstart example
run: |
pip install dist/*.whl --force-reinstall
python3 examples/quickstart.py
- name: Run the Python coverage-mode example
run: python3 examples/coverage_mode.py
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build WASM (nodejs target) and run the JS quickstart example
run: |
wasm-pack build --target nodejs --no-default-features
node examples/quickstart.mjs
rm -rf pkg
- name: Install Cairo (required by the social plugin for OG card images)
run: sudo apt-get update && sudo apt-get install -y libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev
- name: Install MkDocs Material
run: pip install "mkdocs-material[imaging]"
- name: Build docs site (--strict fails on broken links/snippet includes/missing social-card deps)
run: mkdocs build --strict -d /tmp/site
- name: Check sitemap.xml includes key pages
run: |
for page in api/python api/rust api/wasm benchmark benchmark_ja getting_started/installation getting_started/quickstart examples/aspirin examples/druglike guides/python-retrosynthesis guides/rust-retrosynthesis guides/reaction-evidence guides/forward-prediction guides/forward-enumeration guides/forward-retrieval-hints guides/open-source-retrosynthesis-comparison comparison/open-source-retrosynthesis-tools comparison/askcos-feasibility-issue-66; do
grep -q "$page/" /tmp/site/sitemap.xml || { echo "ERROR: $page missing from sitemap.xml"; exit 1; }
done
echo "OK: sitemap.xml includes all key pages"
- name: Check social card images were generated for key pages
run: |
for page in index api/python api/rust api/wasm benchmark benchmark_ja getting_started/installation getting_started/quickstart examples/aspirin examples/druglike guides/python-retrosynthesis guides/rust-retrosynthesis guides/reaction-evidence guides/forward-prediction guides/forward-enumeration guides/forward-retrieval-hints guides/open-source-retrosynthesis-comparison comparison/open-source-retrosynthesis-tools comparison/askcos-feasibility-issue-66; do
test -s "/tmp/site/assets/images/social/$page.png" || { echo "ERROR: social card missing for $page"; exit 1; }
done
echo "OK: social card images generated for all key pages"