diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ded509..4cd0527 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,14 +53,61 @@ jobs: - run: cargo test --workspace --all-features + coverage: + name: Coverage + runs-on: ubuntu-latest + env: + CARGO_INCREMENTAL: 0 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable + with: + components: llvm-tools-preview + - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2 + - uses: taiki-e/install-action@86f7a0ffb25f40114606501a311ebcfaf882ea32 # v2 + with: + tool: cargo-llvm-cov + - name: Generate coverage + run: | + mkdir -p target/coverage + cargo llvm-cov --workspace --all-features \ + --lcov --output-path target/coverage/lcov.info + cargo llvm-cov report --summary-only \ + | tee target/coverage/summary.txt + { + echo "## Test coverage" + echo + echo '```' + cat target/coverage/summary.txt + echo '```' + echo + echo "Coverage is reported but not enforced yet. See docs/testing.md." + } >> "$GITHUB_STEP_SUMMARY" + - name: Apply advisory coverage floor (non-blocking) + continue-on-error: true + env: + # Kept at zero until a baseline is agreed. Raise this value later; + # the step is non-blocking, so it never fails a pull request yet. + COVERAGE_ADVISORY_MIN_LINES: "0" + run: cargo llvm-cov report --fail-under-lines "$COVERAGE_ADVISORY_MIN_LINES" + - name: Upload coverage artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: coverage + path: | + target/coverage/lcov.info + target/coverage/summary.txt + if-no-files-found: error + retention-days: 14 + msrv: - name: MSRV (1.85.0) + name: MSRV (1.88.0) runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@1.85.0 + - uses: dtolnay/rust-toolchain@1.88.0 - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2 - run: cargo build --workspace --locked - run: cargo test --workspace --all-features --locked diff --git a/CHANGELOG.md b/CHANGELOG.md index 66e4e3b..b431fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `observatory-cli`, binary `stellar-contract-platform`) and state explicitly that no crates.io package or prebuilt binary exists yet. See `docs/installation.md`. +### Security + +- Replace the unsound, unmaintained `serde_yml` parser (which pulled in + `noyalib` 0.0.5, RUSTSEC-2026-0333) with the maintained `serde_yaml_ng` + fork. YAML project configuration parsing is unchanged. + +### Changed + +- Raise the workspace MSRV from 1.85 to 1.88 so the locked dependency set + (`wasmparser` 0.259, `stellar-strkey` 0.0.18, `serde_with` 3.23) resolves and + the MSRV CI job matches the workspace `rust-version`. + ### Added +- CI: report test coverage with `cargo-llvm-cov` on pull requests. The job + writes a summary to the workflow run, uploads an `lcov.info` artifact, and + keeps the coverage floor advisory (`continue-on-error`). See + `docs/testing.md`. - Deployment: opt-in, bounded, per-run `LedgerEntryCache` with cached `inspect_with_cache` and `deployed_wasm_with_cache` lookups; repeated ledger keys reuse one `getLedgerEntries` call. See `docs/deployment.md`. diff --git a/Cargo.lock b/Cargo.lock index 2767b75..21c8eb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -719,19 +719,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" -[[package]] -name = "noyalib" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e493c05128df7a83b9676b709d590e0ebc285c7ed3152bc679668e8c1e506af5" -dependencies = [ - "indexmap 2.14.2", - "memchr", - "rustc-hash", - "serde", - "smallvec", -] - [[package]] name = "num-conv" version = "0.2.2" @@ -904,7 +891,7 @@ dependencies = [ "observatory-wasm", "serde", "serde_json", - "serde_yml", + "serde_yaml_ng", "sha2", "stellar-xdr", "toml", @@ -1109,12 +1096,6 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" -[[package]] -name = "rustc-hash" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" - [[package]] name = "rustix" version = "1.1.5" @@ -1134,6 +1115,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "schemars" version = "0.8.22" @@ -1262,13 +1249,16 @@ dependencies = [ ] [[package]] -name = "serde_yml" -version = "0.0.13" +name = "serde_yaml_ng" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909764a65f86829ccdb5eea9ab355843aa02c019a7bfd47465092953565caa05" +checksum = "7b4db627b98b36d4203a7b458cf3573730f2bb591b28871d916dfa9efabfd41f" dependencies = [ - "noyalib", + "indexmap 2.14.2", + "itoa", + "ryu", "serde", + "unsafe-libyaml", ] [[package]] @@ -1294,12 +1284,6 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" -[[package]] -name = "smallvec" -version = "1.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -1508,6 +1492,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 34a6841..984680d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ authors = ["StellarFoundry"] repository = "https://github.com/StellarFoundry/stellar-contract-platform" homepage = "https://github.com/StellarFoundry/stellar-contract-platform" description = "Contract intelligence, inspection, interface analysis, compatibility, deployment verification, and reproducibility tooling for Stellar and Soroban contracts." -rust-version = "1.85" +rust-version = "1.88" [workspace.dependencies] serde = { version = "1", features = ["derive"] } @@ -42,7 +42,7 @@ stellar-xdr = { version = "28.0.0", features = ["serde", "base64", "alloc"] } stellar-strkey = "0.0.18" base64 = "0.22" getrandom = "0.2" -serde_yml = "0.0.13" +serde_yaml_ng = "0.10" toml = "0.8" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/crates/observatory-platform/Cargo.toml b/crates/observatory-platform/Cargo.toml index 761ccd4..3b7b71d 100644 --- a/crates/observatory-platform/Cargo.toml +++ b/crates/observatory-platform/Cargo.toml @@ -22,7 +22,7 @@ observatory-audit.workspace = true observatory-output.workspace = true serde.workspace = true serde_json.workspace = true -serde_yml.workspace = true +serde_yaml_ng.workspace = true toml.workspace = true sha2.workspace = true hex.workspace = true diff --git a/crates/observatory-platform/src/project.rs b/crates/observatory-platform/src/project.rs index bf288f1..83373bc 100644 --- a/crates/observatory-platform/src/project.rs +++ b/crates/observatory-platform/src/project.rs @@ -171,7 +171,7 @@ impl ProjectConfig { /// Parse configuration text in the given format. pub fn parse(input: &str, format: ConfigFormat) -> Result { let parsed = match format { - ConfigFormat::Yaml => serde_yml::from_str(input).map_err(|error| error.to_string()), + ConfigFormat::Yaml => serde_yaml_ng::from_str(input).map_err(|error| error.to_string()), ConfigFormat::Toml => toml::from_str(input).map_err(|error| error.to_string()), ConfigFormat::Json => serde_json::from_str(input).map_err(|error| error.to_string()), }; diff --git a/docs/ci.md b/docs/ci.md index 5b88e6f..118bf90 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -47,6 +47,13 @@ jobs: contract.wasm --contract C... --rpc-fixture ci/rpc.json --network testnet ``` +## Coverage + +The `Coverage` CI job runs `cargo-llvm-cov` on pull requests and pushes to +`main`, writes a summary to the job summary, and uploads an `lcov.info` artifact. +The coverage floor is advisory (`continue-on-error`) while a baseline is +established. See [testing.md](testing.md#coverage) for the exact commands. + ## JSON reports Every command supports `--json`. Reports are wrapped in a versioned envelope: diff --git a/docs/testing.md b/docs/testing.md index fb88fda..ed29afb 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -34,6 +34,42 @@ cargo test --workspace | Event fixtures | `observatory-events` test helpers (base64 `ScVal`) | | Ledger entries | `stellar-xdr` `LedgerEntryData` encoded in tests | +## Coverage + +CI reports line coverage with [`cargo-llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov), +which uses LLVM's source-based instrumentation. The `Coverage` job runs on every +pull request and on pushes to `main`: + +```bash +mkdir -p target/coverage +cargo llvm-cov --workspace --all-features \ + --lcov --output-path target/coverage/lcov.info +cargo llvm-cov report --summary-only | tee target/coverage/summary.txt +``` + +The job: + +- writes a readable summary to the GitHub Actions job summary (visible from the + pull request checks page), +- uploads `target/coverage/lcov.info` and `target/coverage/summary.txt` as the + `coverage` artifact, +- runs an advisory coverage floor with `continue-on-error: true`. The floor is + `0` while a baseline is established, so coverage is **reported but not + enforced**. Raise `COVERAGE_ADVISORY_MIN_LINES` in `.github/workflows/ci.yml` + once a sensible baseline is agreed. + +Run the same commands locally after installing the tool: + +```bash +rustup component add llvm-tools-preview +cargo install cargo-llvm-cov --locked +cargo llvm-cov --workspace --all-features --open +``` + +`cargo llvm-cov` merges the workspace test run, so the numbers include unit, +integration, and CLI tests. Doc tests are not instrumented. Failed tests still +fail the job; only the coverage floor is advisory. + ## Adding tests - Decoding changes must include malformed-input tests.