diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 000000000..f8ea27a8c --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,93 @@ +on: [push, pull_request, workflow_dispatch] + +name: Aderyn-py + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install git submodules + run: | + git submodule update --init --recursive + + - name: Run cargo check + uses: actions-rs/cargo@v1 + with: + command: check + + reports: + name: Check Reports + runs-on: ubuntu-latest + steps: + - name: foundry-toolchain + uses: foundry-rs/foundry-toolchain@v1.2.0 + + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + + - name: Submodule init + run: | + git submodule update --init --recursive + + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 20 + cache: "npm" + + - uses: pnpm/action-setup@v3 + with: + version: 8 + + - uses: bahmutov/npm-install@v1 + with: + useLockFile: false + working-directory: tests/2024-05-Sablier/v2-core + + - uses: bahmutov/npm-install@v1 + with: + useLockFile: false + working-directory: tests/prb-math/ + + - name: Setup virtual environment + run: | + python -m venv venv + source venv/bin/activate + pip install -r ./aderyn_py/requirements.txt + + - name: Run tests + run: | + source venv/bin/activate + cd aderyn_py + maturin develop + pytest tests diff --git a/Cargo.lock b/Cargo.lock index b7eb90584..c4005d063 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,7 @@ dependencies = [ "criterion", "cyfrin-foundry-compilers", "cyfrin-foundry-config", + "field_access", "rayon", "serde", "serde_json", @@ -82,6 +83,7 @@ name = "aderyn_py" version = "0.1.10" dependencies = [ "aderyn_driver", + "field_access", "pyo3", ] @@ -1201,6 +1203,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "field_access" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e623ab785a56a622537034c7109d47898c41cb9219803155759dcca1f19655c" +dependencies = [ + "field_access_derive", + "paste", +] + +[[package]] +name = "field_access_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917d5dcdd27ef8b8bd66913702385e92fa2a2a9fd6f7b94ffa03e884d847a8d9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "figment" version = "0.10.19" @@ -1795,9 +1818,9 @@ dependencies = [ [[package]] name = "indoc" -version = "1.0.9" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "inlinable_string" @@ -2483,6 +2506,12 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "portable-atomic" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" + [[package]] name = "ppv-lite86" version = "0.2.18" @@ -2586,15 +2615,16 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.19.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38" +checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", - "parking_lot", + "once_cell", + "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -2603,9 +2633,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.19.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5" +checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8" dependencies = [ "once_cell", "target-lexicon", @@ -2613,9 +2643,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.19.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9" +checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6" dependencies = [ "libc", "pyo3-build-config", @@ -2623,25 +2653,27 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.19.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1" +checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] name = "pyo3-macros-backend" -version = "0.19.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" +checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372" dependencies = [ + "heck 0.5.0", "proc-macro2", + "pyo3-build-config", "quote", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -3867,9 +3899,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unindent" -version = "0.1.11" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" [[package]] name = "untrusted" diff --git a/aderyn_driver/Cargo.toml b/aderyn_driver/Cargo.toml index 16be8294e..dd6669b01 100644 --- a/aderyn_driver/Cargo.toml +++ b/aderyn_driver/Cargo.toml @@ -17,6 +17,7 @@ serde = { version = "1.0.160", features = ["derive"] } serde_repr = "0.1.12" cyfrin-foundry-config = { version = "0.2.1" } toml = "0.8.13" +field_access = "0.1.8" [dev-dependencies] criterion = "0.5.1" diff --git a/aderyn_driver/src/driver.rs b/aderyn_driver/src/driver.rs index 3abef9b39..9ff0bb9e6 100644 --- a/aderyn_driver/src/driver.rs +++ b/aderyn_driver/src/driver.rs @@ -13,9 +13,10 @@ use aderyn_core::{ }, run, }; +use field_access::FieldAccess; use std::{collections::HashMap, error::Error, path::PathBuf}; -#[derive(Clone)] +#[derive(Clone, FieldAccess)] pub struct Args { pub root: String, pub output: String, diff --git a/aderyn_py/Cargo.toml b/aderyn_py/Cargo.toml index f40308ac5..04ed51e0b 100644 --- a/aderyn_py/Cargo.toml +++ b/aderyn_py/Cargo.toml @@ -15,8 +15,9 @@ crate-type = ["cdylib"] [dependencies] aderyn_driver = { path = "../aderyn_driver", version = "0.1.10" } +field_access = "0.1.8" [dependencies.pyo3] -version = "0.19.0" +version = "0.22.2" # "abi3-py38" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.8 -features = ["abi3-py37"] \ No newline at end of file +features = ["abi3-py38"] \ No newline at end of file diff --git a/aderyn_py/requirements.txt b/aderyn_py/requirements.txt new file mode 100644 index 000000000..cb6f7b95b --- /dev/null +++ b/aderyn_py/requirements.txt @@ -0,0 +1,2 @@ +pytest==8.3.2 +maturin==1.7.0 \ No newline at end of file diff --git a/aderyn_py/src/lib.rs b/aderyn_py/src/lib.rs index 70a664733..f75501787 100644 --- a/aderyn_py/src/lib.rs +++ b/aderyn_py/src/lib.rs @@ -1,26 +1,43 @@ #![allow(unused)] use aderyn_driver::driver; +use field_access::{FieldAccess, FieldMut}; fn main() { use pyo3::prelude::*; + use pyo3::types::{PyBool, PyDict}; #[pyfunction] - fn generate_report(root: String, output: String) { - let args = driver::Args { + #[pyo3(signature = (root, output, **py_kwargs))] + fn generate_report(root: String, output: String, py_kwargs: Option<&Bound<'_, PyDict>>) { + let mut args = driver::Args { root, output, - src: None, // TODO support this later - no_snippets: false, // TODO support this later - skip_build: false, // TODO support this later - skip_cloc: false, // TODO support this later - path_includes: None, // TODO support this later - path_excludes: None, // TODO support this later - stdout: false, // TODO support this later - skip_update_check: false, // TODO support this later - auditor_mode: false, // TODO support this later - highs_only: false, // TODO support this later + src: None, + no_snippets: false, + skip_build: false, + skip_cloc: false, + path_includes: None, + path_excludes: None, + stdout: false, + skip_update_check: false, + auditor_mode: false, + highs_only: false, }; + + if let Some(kwargs) = py_kwargs { + kwargs.iter().for_each(|(py_key, py_value)| { + let rust_key: String = py_key.extract().unwrap(); + if py_value.is_instance_of::() { + let rust_value: bool = py_value.extract().unwrap(); + args.field_mut(&rust_key).unwrap().replace(rust_value); + } else { + let rust_value: Vec = py_value.extract().unwrap_or_default(); + args.field_mut(&rust_key).unwrap().replace(Some(rust_value)); + } + }) + } + driver::drive(args); } @@ -28,7 +45,7 @@ fn main() { /// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to /// import the module. #[pymodule] - fn aderynpy(_py: Python, m: &PyModule) -> PyResult<()> { + fn aderynpy(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(generate_report, m)?)?; Ok(()) diff --git a/aderyn_py/tests/test_generate_report.py b/aderyn_py/tests/test_generate_report.py new file mode 100644 index 000000000..8d932c9f6 --- /dev/null +++ b/aderyn_py/tests/test_generate_report.py @@ -0,0 +1,27 @@ +import subprocess +import pytest +from aderynpy import generate_report + +@pytest.mark.parametrize("root, report", [ + ("../tests/contract-playground", "../reports/report.md"), + ("../tests/2024-05-Sablier", "../reports/sablier-aderyn-toml-nested-root.md"), + ("../tests/adhoc-sol-files", "../reports/adhoc-sol-files-report.md"), + ("../tests/foundry-nft-f23", "../reports/nft-report.md"), + ("../tests/prb-math", "../reports/prb-math-report.md"), +]) +def test_generate_report(root, report): + # Define output file path + out_file = f"./{root.split('../')[-1]}-workflow.md" + + # Call the generate_report function + generate_report(root, out_file) + + # Run the diff command to compare the generated report with the original report + result = subprocess.run( + ["diff", str(out_file), report], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + # Check if diff command found any differences (result.returncode == 0 means no differences) + assert result.returncode == 1