Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .agents/skills/build-cadmus-native/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ automatically by `build.rs` when you run any Cargo command that compiles
| Check formatting | `cargo xtask fmt` |
| Run clippy | `cargo xtask clippy` |
| Run tests (default features) | `cargo xtask test --features default` |
| Run tests with coverage | `cadmus-test-coverage --features default` (devenv) |
| View coverage (project-wide) | `cadmus-coverage-show` (after test-coverage) |
| View coverage (patch diff) | `cadmus-coverage-diff` (after test-coverage) |
| Run tests with telemetry | `cargo xtask test --features "profiling + test + tracing"` |
| Run the emulator | `cargo xtask run-emulator` (builds the EPUB first if missing) |
| Install the importer CLI | `cargo xtask install-importer` |
Expand All @@ -75,11 +78,30 @@ cargo xtask test --features "profiling + test + tracing"
Use `cargo xtask ci matrix` to see all available feature combinations if you
need to verify a specific one.

### Coverage locally

In a devenv shell (`cadmus-test-coverage`, `cadmus-coverage-show`, `cadmus-coverage-diff`):

```bash
cadmus-test-coverage
cadmus-coverage-show
cadmus-coverage-diff
```

Without devenv:

```bash
cargo xtask test --coverage --features default
```

This writes `target/coverage/lcov.info`. Project HTML uses `cargo llvm-cov report --html`.
Patch HTML uses `diff-cover` (see `devenv.nix` scripts).

## What the xtask wrappers do

- **`fmt`** — runs `cargo fmt --check` (or `--apply` in CI) across the workspace
- **`clippy`** — iterates the full feature matrix; use `--features` to narrow it
- **`test`** — iterates the test feature matrix
- **`test`** — iterates the test feature matrix; `--coverage` enables llvm-cov instrumentation
- **`run-emulator`** — ensures the documentation EPUB exists, then runs `cargo run -p emulator`
- **`install-importer`** — runs `cargo install --path crates/importer`

Expand Down
18 changes: 18 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
comment:
layout: "condensed_header, condensed_files, condensed_footer"
hide_project_coverage: true

ignore:
- "thirdparty/**"
- "mupdf_wrapper/**"
- "**/build.rs"

coverage:
status:
project:
default:
informational: true
threshold: 1%
patch:
default:
informational: true
4 changes: 4 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[profile.ci.junit]
path = "junit.xml"
report-name = "cadmus"
store-failure-output = true
7 changes: 4 additions & 3 deletions .github/actions/cargo-cache/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ description: >
Restore or save the shared Cargo cache. The cache key incorporates the
Rust toolchain fingerprint, the Cargo lockfile, build scripts, the
mupdf_wrapper C glue, and the gitlink SHAs of every thirdparty
submodule so that the key changes when any submodule pointer moves.
submodule so that the key changes when any submodule pointer moves. The
workflow file is hashed so CI build-step changes invalidate stale target/ caches.

Defaults to restore-only. Pass `mode: save` in the cache-warmup job to
write a fresh cache.
Expand Down Expand Up @@ -46,7 +47,7 @@ runs:
~/.cargo/git/db/
target/
libs/
key: ${{ runner.os }}-cargo-shared-${{ inputs.cachekey }}-${{ hashFiles('**/Cargo.lock', 'crates/build-deps/**', 'build-scripts/**', '.gitmodules', 'mupdf_wrapper/mupdf_wrapper.c', 'crates/core/build.rs') }}-${{ steps.submodules.outputs.hash }}
key: ${{ runner.os }}-cargo-shared-${{ inputs.cachekey }}-${{ hashFiles('**/Cargo.lock', 'crates/build-deps/**', 'build-scripts/**', '.gitmodules', 'mupdf_wrapper/mupdf_wrapper.c', 'crates/core/build.rs', '.github/workflows/cargo.yml') }}-${{ steps.submodules.outputs.hash }}
restore-keys: |
${{ runner.os }}-cargo-shared-${{ inputs.cachekey }}-

Expand All @@ -62,6 +63,6 @@ runs:
~/.cargo/git/db/
target/
libs/
key: ${{ runner.os }}-cargo-shared-${{ inputs.cachekey }}-${{ hashFiles('**/Cargo.lock', 'crates/build-deps/**', 'build-scripts/**', '.gitmodules', 'mupdf_wrapper/mupdf_wrapper.c', 'crates/core/build.rs') }}-${{ steps.submodules.outputs.hash }}
key: ${{ runner.os }}-cargo-shared-${{ inputs.cachekey }}-${{ hashFiles('**/Cargo.lock', 'crates/build-deps/**', 'build-scripts/**', '.gitmodules', 'mupdf_wrapper/mupdf_wrapper.c', 'crates/core/build.rs', '.github/workflows/cargo.yml') }}-${{ steps.submodules.outputs.hash }}
restore-keys: |
${{ runner.os }}-cargo-shared-${{ inputs.cachekey }}-
100 changes: 98 additions & 2 deletions .github/workflows/cargo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ jobs:
id: rust-toolchain
with:
targets: arm-unknown-linux-gnueabihf
components: llvm-tools

- &set-build-metadata
name: Set build metadata
Expand Down Expand Up @@ -143,6 +144,17 @@ jobs:
if: steps.cargo-nextest-cache.outputs.cache-hit != 'true'
run: cargo install cargo-nextest --version "$NEXTEST_VERSION" --locked --force

- name: Cache cargo-llvm-cov
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae
id: cargo-llvm-cov-cache
with:
path: ~/.cargo/bin/cargo-llvm-cov
key: ${{ runner.os }}-cargo-llvm-cov

- name: Install cargo-llvm-cov
if: steps.cargo-llvm-cov-cache.outputs.cache-hit != 'true'
uses: taiki-e/install-action@cargo-llvm-cov

- name: Download documentation EPUB
if: steps.cache.outputs.cache-hit != 'true'
uses: actions/download-artifact@v8
Expand All @@ -163,6 +175,13 @@ jobs:
- name: Warmup build (native)
if: steps.cache.outputs.cache-hit != 'true'
run: cargo check --workspace --all-features --all-targets
- name: Warmup llvm-cov build
if: steps.cache.outputs.cache-hit != 'true'
run: |
cargo llvm-cov nextest-archive \
--workspace --all-features --all-targets \
--archive-file target/llvm-cov-warmup.tar.zst
rm -f target/llvm-cov-warmup.tar.zst
- name: Warmup build (Kobo release)
if: steps.cache.outputs.cache-hit != 'true'
run: cargo build --release --target arm-unknown-linux-gnueabihf -p cadmus
Expand Down Expand Up @@ -272,6 +291,8 @@ jobs:
fetch-tags: true
- uses: dtolnay/rust-toolchain@stable
id: rust-toolchain
with:
components: llvm-tools
- *set-build-metadata
- name: Download documentation EPUB
uses: actions/download-artifact@v8
Expand Down Expand Up @@ -311,8 +332,83 @@ jobs:
if: steps.cargo-nextest-cache.outputs.cache-hit != 'true'
run: cargo install cargo-nextest --version "$NEXTEST_VERSION" --locked --force

- name: Run cargo test (${{ matrix.label }})
run: cargo xtask test --features "${{ matrix.label }}"
- name: Restore cargo-llvm-cov
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae
id: cargo-llvm-cov-cache
with:
path: ~/.cargo/bin/cargo-llvm-cov
key: ${{ runner.os }}-cargo-llvm-cov
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Install cargo-llvm-cov
if: steps.cargo-llvm-cov-cache.outputs.cache-hit != 'true'
uses: taiki-e/install-action@cargo-llvm-cov

- name: Run cargo test with coverage (${{ matrix.label }})
id: test
continue-on-error: true
run: |
slug="${{ matrix.label }}"
slug="${slug// + /-}"
echo "slug=$slug" >> "$GITHUB_OUTPUT"
cargo xtask test \
--features "${{ matrix.label }}" \
--coverage \
--save-coverage "coverage-json/${slug}.json" \
--save-junit "test-results/${slug}.junit.xml"

- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
report_type: test_results
files: test-results/${{ steps.test.outputs.slug }}.junit.xml
flags: ${{ matrix.label }}
disable_search: true
fail_ci_if_error: false

- name: Upload coverage artifact
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: coverage-json-${{ steps.test.outputs.slug }}
path: coverage-json/
retention-days: 1

- name: Fail if tests failed
if: steps.test.outcome == 'failure'
run: exit 1

coverage-report:
if: |
github.event_name == 'pull_request' ||
github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
needs: test
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false

- name: Download all coverage artifacts
uses: actions/download-artifact@v8
with:
pattern: coverage-json-*
path: coverage-json
merge-multiple: true

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
report_type: coverage
files: coverage-json/*.json
disable_search: true
fail_ci_if_error: false

build-kobo:
needs: [cache-warmup, build-docs-epub]
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/codecov-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Codecov config validation

on:
pull_request:
paths:
- ".codecov.yml"
- ".github/workflows/codecov-config.yml"

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Validate Codecov YAML
run: curl -sf -X POST --data-binary @.codecov.yml https://codecov.io/validate
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<a href="https://github.com/OGKevin/cadmus/actions/workflows/cargo.yml?query=branch%3Amaster++">
<img src="https://img.shields.io/github/actions/workflow/status/ogkevin/cadmus/cargo.yml?style=for-the-badge" />
</a>
<a href="https://codecov.io/github/OGKevin/cadmus" alt="Coverage">
<img src="https://img.shields.io/codecov/c/github/OGKevin/cadmus/master?style=for-the-badge" />
</a>
<a href="https://discord.gg/3AJHp6rV5a" alt="Discord">
<img src="https://img.shields.io/discord/1459138935203565741?style=for-the-badge" /></a>
<a href="https://github.com/OGKevin/cadmus/releases/latest" alt="Latest Release">
Expand Down
62 changes: 62 additions & 0 deletions devenv.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ let
inherit (pkgs.stdenv) isLinux;
inherit (pkgs.stdenv) isDarwin;

openHtml =
path:
if isLinux then
"xdg-open ${path}"
else if isDarwin then
"open ${path}"
else
"echo \"Open ${path} in a browser\"";

# Rust 1.94+ changed lib/ in tarballs from a directory to a symlink.
# devenv's rust module calls mk-aggregated.nix directly from the rust-overlay
# source, bypassing pkgs overlays. lndir can't merge components when $out/lib
Expand Down Expand Up @@ -341,6 +350,8 @@ in
pkgs.wrangler

pkgs.cargo-llvm-cov
pkgs.python3Packages.diff-cover
pkgs.xdg-utils

# Linaro ARM cross-compilation toolchain (provides arm-linux-gnueabihf-* commands)
linaroToolchain
Expand Down Expand Up @@ -400,6 +411,10 @@ in
PYROSCOPE_SERVER_URL = "http://localhost:4040";
NEXTEST_NO_TESTS = "pass";

# Default branch for patch coverage (cadmus-coverage-diff). Override in devenv.local.nix.
CADMUS_DEFAULT_BRANCH = "master";
CARGO_TARGET_DIR = "${config.devenv.root}/target";

# jemalloc configure uses -O0 in debug builds, which triggers _FORTIFY_SOURCE warnings
# treated as errors under Nix hardening. Disable hardening for jemalloc's build script.
NIX_HARDENING_ENABLE = "";
Expand Down Expand Up @@ -803,6 +818,48 @@ in
echo "Updated docs/po/messages.pot"
echo "Translate on Crowdin: https://crowdin.com/project/cadmus"
'';

# Run instrumented tests; writes target/coverage/lcov.info
cadmus-test-coverage.exec = ''
cd "$DEVENV_ROOT"
if [ $# -eq 0 ]; then
set -- --features default
fi
cargo xtask test --coverage "$@"
'';

# Project-wide HTML from last coverage run (no re-test)
cadmus-coverage-show.exec = ''
set -e
cd "$DEVENV_ROOT"
if ! cargo llvm-cov report --html \
--ignore-filename-regex '(thirdparty/|mupdf_wrapper/|build\.rs)'; then
echo "No coverage data. Run 'cadmus-test-coverage' first." >&2
exit 1
fi
${openHtml "target/llvm-cov/html/index.html"}
'';

# Patch HTML vs base branch (no re-test). Branch from $1 or $CADMUS_DEFAULT_BRANCH.
cadmus-coverage-diff.exec = ''
set -e
cd "$DEVENV_ROOT"
BRANCH="''${1:-$CADMUS_DEFAULT_BRANCH}"
LCOV="target/coverage/lcov.info"
if [ ! -f "$LCOV" ]; then
echo "No coverage data. Run 'cadmus-test-coverage' first." >&2
exit 1
fi
case "$BRANCH" in
origin/*) COMPARE_BRANCH="$BRANCH" ;;
*) COMPARE_BRANCH="origin/$BRANCH" ;;
esac
mkdir -p target/coverage
diff-cover "$LCOV" \
--compare-branch "$COMPARE_BRANCH" \
--format "html:target/coverage/diff-coverage.html"
${openHtml "target/coverage/diff-coverage.html"}
'';
};

enterShell = ''
Expand All @@ -817,6 +874,11 @@ in
echo " cargo xtask run-emulator - Run the emulator (after setup)"
echo " cadmus-translate - Regenerate docs/po/messages.pot (translations on Crowdin)"
echo ""
echo "Coverage (diff base: $CADMUS_DEFAULT_BRANCH):"
echo " cadmus-test-coverage - Run tests with llvm-cov instrumentation"
echo " cadmus-coverage-show - Project-wide HTML report (after test-coverage)"
echo " cadmus-coverage-diff - Patch HTML vs origin/\$CADMUS_DEFAULT_BRANCH or [branch] arg"
echo ""
echo "xtask commands (cargo xtask <cmd> --help for options):"
echo " cargo xtask fmt - Check formatting"
echo " cadmus-clippy - Lint lines changed vs master (reviewdog)"
Expand Down
Loading
Loading