Skip to content

feat: incremental builds#95

Merged
Princesseuh merged 9 commits into
mainfrom
feat/depinfo-recompile-check
Mar 15, 2026
Merged

feat: incremental builds#95
Princesseuh merged 9 commits into
mainfrom
feat/depinfo-recompile-check

Conversation

@Princesseuh

Copy link
Copy Markdown
Member

Fixes #

What does this change?

  • Be short and concise. Bullet points can help!
  • Before/after screenshots can help as well.

How is it tested?

How is it documented?

@sampo-s-bot

sampo-s-bot Bot commented Feb 3, 2026

Copy link
Copy Markdown

🧭 Changeset detected

Merging this PR will release the following updates:

maudit (Cargo) — minor version bump

Minor changes

  • Adds support for incremental builds. Subsequent builds will now only re-render pages whose content or assets have changed, making rebuilds significantly faster. This is enabled by default. To disable it, set incremental: false in your BuildOptions:

    use maudit::{content_sources, coronate, routes, BuildOptions, BuildOutput};
    
    fn main() -> Result<BuildOutput, Box<dyn std::error::Error>> {
      coronate(
        routes![],
        content_sources![],
        BuildOptions {
          incremental: false,
          ..Default::default()
        },
      )
    }

Patch changes

  • The Maudit CLI will now directly rerun the website's binary instead of using Cargo when changes do not require recompilation, this on average speeds up the feedback loop by 300-1000ms.
  • Improves performance when using dynamic routes

maudit-cli (Cargo) — patch version bump

Patch changes

  • The Maudit CLI will now directly rerun the website's binary instead of using Cargo when changes do not require recompilation, this on average speeds up the feedback loop by 300-1000ms.

maudit-macros (Cargo) — minor version bump

Minor changes

  • Adds support for incremental builds. Subsequent builds will now only re-render pages whose content or assets have changed, making rebuilds significantly faster. This is enabled by default. To disable it, set incremental: false in your BuildOptions:

    use maudit::{content_sources, coronate, routes, BuildOptions, BuildOutput};
    
    fn main() -> Result<BuildOutput, Box<dyn std::error::Error>> {
      coronate(
        routes![],
        content_sources![],
        BuildOptions {
          incremental: false,
          ..Default::default()
        },
      )
    }

@netlify

netlify Bot commented Feb 3, 2026

Copy link
Copy Markdown

Deploy Preview for maudit-framework ready!

Name Link
🔨 Latest commit 5b7633f
🔍 Latest deploy log https://app.netlify.com/projects/maudit-framework/deploys/69b6cccb36cbd30008e0d1e8
😎 Deploy Preview https://deploy-preview-95--maudit-framework.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added the crt:cli Changes in Maudit's CLI crate. label Feb 3, 2026
@github-actions github-actions Bot added website Improvements or additions to documentation crt:maudit Changes in Maudit crate. crt:macros Changes in Maudit's macros crate examples crt:oubli Changes in Oubli crate. labels Mar 10, 2026
@Princesseuh Princesseuh changed the title feat: use depinfo to conditionally recompile feat: incremental builds Mar 10, 2026
@codspeed-hq

codspeed-hq Bot commented Mar 10, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 65.41%

⚡ 6 improved benchmarks
✅ 1 untouched benchmark
🆕 1 new benchmark

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation markdown[500] 15.1 ms 13.3 ms +13.31%
Simulation overhead 65.7 ms 39.7 ms +65.41%
Simulation markdown[250] 7.5 ms 6.7 ms +11.42%
🆕 Simulation incremental_no_changes N/A 87.1 ms N/A
Simulation markdown[1000] 31.9 ms 27.4 ms +16.53%
Simulation markdown[2000] 68.2 ms 54.5 ms +25.09%
Simulation markdown[4000] 159 ms 115.2 ms +38.05%

Comparing feat/depinfo-recompile-check (5b7633f) with main (8348fd5)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (63c0b07) during the generation of this report, so 8348fd5 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@Princesseuh Princesseuh marked this pull request as ready for review March 14, 2026 20:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an incremental build system for Maudit that tracks per-page content and asset dependencies, enabling subsequent builds (and dev rebuilds) to skip re-rendering unchanged pages for faster feedback loops.

Changes:

  • Add a persisted build cache and dependency-diffing logic (content entries, iterated sources, asset fingerprints, stale output cleanup).
  • Introduce tracked content access APIs (ctx.content::<T>(...)) to record dependencies during render()/pages().
  • Improve dev workflow by detecting whether Rust changes require recompilation vs just rerunning the built binary; add e2e coverage for the behavior.

Reviewed changes

Copilot reviewed 55 out of 56 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
website/src/routes/news.rs Update routes to use tracked ctx.content::<T>() and iterator API.
website/src/routes/docs.rs Update docs routes to use tracked ctx.content::<T>().
website/src/layout/docs_sidebars.rs Update sidebar content iteration to use tracked entries iterator.
website/content/docs/library.md Update docs snippets for new RouteAssets + DynamicRouteContext::new APIs.
website/content/docs/content.md Update docs snippets for new tracked content APIs and new Entry::create signature.
examples/oubli-basics/src/routes/index.rs Update example to tracked content access and iterator API.
examples/markdown-components/src/routes.rs Update example to tracked content access API.
examples/library/src/routes/index.rs Update example to tracked content access and iterator API.
examples/library/src/routes/article.rs Update example to tracked content access API.
examples/library/src/build.rs Update example for new RouteAssets::new and DynamicRouteContext::new.
examples/blog/src/routes/index.rs Update example to tracked content access and iterator API.
examples/blog/src/routes/article.rs Update example to tracked content access API.
e2e/tests/test-utils.ts Add dev-server log capture utilities to support log-based polling in tests.
e2e/tests/hot-reload.spec.ts Expand hot-reload coverage to assert recompile vs rerun behavior via logs.
e2e/fixtures/hot-reload/data.txt Add fixture file for “non-Rust change triggers rerun” scenario.
crates/oubli/src/lib.rs Update content entry creation to new dependency list parameter.
crates/oubli/src/archetypes/blog.rs Update archetype helpers to tracked content access and iterator API.
crates/maudit/src/route.rs Add content access logging + tracked content handles; optimize URL/path building; add incremental hooks.
crates/maudit/src/logging.rs Adjust quiet-mode behavior and filter noisy module logs.
crates/maudit/src/content/tracked.rs Introduce tracked content wrapper + access log for incremental dependency tracking.
crates/maudit/src/content/markdown/shortcodes_tests.rs Update tests for new RouteAssets/PageContext fields/signatures.
crates/maudit/src/content/markdown.rs Update markdown content to record file dependencies for incremental hashing.
crates/maudit/src/content.rs Add dependency model, switch entries to map for faster lookup, extend internal content-source interface.
crates/maudit/src/build/options.rs Add incremental/cache options and option hashing for cache invalidation.
crates/maudit/src/build/metadata.rs Track cached vs rendered pages and expose “has_changes” utility.
crates/maudit/src/build/cache.rs Add persisted incremental build cache format and diffing logic.
crates/maudit/src/build.rs Integrate incremental cache into build pipeline; add cache hits, stale cleanup, bundling skip logic.
crates/maudit/src/assets/image_cache.rs Persist image cache separately; add invalidation via mtime/size and GC support.
crates/maudit/src/assets.rs Add shared asset-hash cache to reduce redundant hashing across pages.
crates/maudit/Cargo.toml Add dependencies for cache persistence/tests (bincode, serde derive, serial_test).
crates/maudit-macros/src/lib.rs Extend route macro to support always_revalidate and pass source-entry hints through pages.
crates/maudit-cli/src/dev/server.rs Add StatusManager wrapper for status broadcasting + persistent state.
crates/maudit-cli/src/dev/dep_tracker.rs Add .d-file dependency tracker to decide recompile vs rerun.
crates/maudit-cli/src/dev/build.rs Add rerun path and dependency tracking; refactor status handling via StatusManager.
crates/maudit-cli/src/dev.rs Use dependency tracker decision to rerun vs recompile on file changes.
crates/maudit-cli/Cargo.toml Add depinfo + tempfile test dependency.
benchmarks/realistic-blog/src/routes/index.rs Update benchmark routes to tracked content APIs.
benchmarks/realistic-blog/src/routes/article.rs Update benchmark routes to tracked content APIs.
benchmarks/realistic-blog/src/lib.rs Disable incremental builds for baseline benchmark behavior.
benchmarks/realistic-blog/benches/build.rs Ensure cache dir is cleaned for benchmark repeatability.
benchmarks/overhead/src/lib.rs Disable incremental builds for baseline benchmark behavior.
benchmarks/overhead/benches/build.rs Ensure cache dir is cleaned for benchmark repeatability.
benchmarks/md-benchmark/src/page.rs Update benchmark to tracked content APIs.
benchmarks/md-benchmark/src/lib.rs Disable incremental builds for baseline benchmark behavior.
benchmarks/md-benchmark/benches/build.rs Ensure cache dir is cleaned for benchmark repeatability.
benchmarks/incremental/src/page.rs Add new incremental-specific benchmark route.
benchmarks/incremental/src/main.rs Add binary entrypoint for incremental benchmark crate.
benchmarks/incremental/src/lib.rs Add incremental benchmark harness using incremental builds.
benchmarks/incremental/benches/build.rs Add benchmark for incremental “no changes” rebuilds.
benchmarks/incremental/Cargo.toml Add new benchmark crate manifest.
Cargo.lock Lockfile updates for new Rust dependencies.
.sampo/config.toml Update Sampo ignore list.
.sampo/changesets/stalwart-stormcaller-vipunen.md Changeset: dynamic route perf note.
.sampo/changesets/somber-warden-aurelien.md Changeset: incremental builds release note.
.sampo/changesets/cranky-sage-tursas.md Changeset: CLI rerun optimization release note.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +125 to +135
pub fn needs_recompile(&self, changed_paths: &[PathBuf]) -> bool {
for changed_path in changed_paths {
// Normalize the changed path to handle relative vs absolute paths
let changed_path_canonical = changed_path.canonicalize().ok();

for (dep_path, last_modified) in &self.dependencies {
// Try to match both exact path and canonical path
let matches = changed_path == dep_path
|| changed_path_canonical.as_ref() == Some(dep_path)
|| dep_path.canonicalize().ok().as_ref() == changed_path_canonical.as_ref();

Comment on lines +204 to +211
pub fn save(&self, cache_dir: &Path) -> std::io::Result<()> {
fs::create_dir_all(cache_dir)?;
let path = cache_dir.join(BUILD_CACHE_FILENAME);
let tmp_path = cache_dir.join(format!("{}.tmp", BUILD_CACHE_FILENAME));
let bytes = bincode::serialize(self).expect("BuildCache serialization should not fail");
fs::write(&tmp_path, bytes)?;
fs::rename(&tmp_path, &path)?;
Ok(())
Some(manifest)
let bytes =
bincode::serialize(&persisted).expect("ImageCache serialization should not fail");
fs::write(&tmp_path, bytes)?;
Comment thread e2e/tests/test-utils.ts
Comment on lines +64 to +71
function appendLines(data: Buffer) {
const lines = data.toString().split("\n");
for (const line of lines) {
if (line.trim() !== "") {
capturedLogs.push(line);
}
}
}
Comment thread .sampo/changesets/cranky-sage-tursas.md Outdated
cargo/maudit-cli: patch
---

The Maudit CLI will now directly rerun the website's binary instead of using Cargo when changes do notrequire recompilation, this on average speeds up the feedback loop by 300-1000ms.
Comment thread crates/maudit-cli/src/dev/build.rs Outdated
}

// Acquire semaphore to ensure only one build/run happens at a time
let _ = self.build_semaphore.acquire().await?;
Comment thread crates/maudit-cli/src/dev/build.rs Outdated
@@ -61,13 +251,9 @@ impl BuildManager {
let _ = self.build_semaphore.acquire().await?;
Comment on lines 143 to +145
fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
let entry = ctx
.content
.get_source::<MyType>("my_data")
.get_entry("0");
let source = ctx.collection::<MyType>("my_data");
let entry = source.get_entry("0");
Comment on lines 348 to 391
@@ -348,27 +358,26 @@ impl<T> ContentSource<T> {
{
Self {
name: name.into(),
entries: vec![],
entries: FxHashMap::default(),
init_method: entries,
}
}

pub fn get_entry(&self, id: &str) -> &Entry<T> {
self.entries
.iter()
.find(|entry| entry.id == id)
.get(id)
.unwrap_or_else(|| panic!("Entry with id '{}' not found", id))
}

pub fn get_entry_safe(&self, id: &str) -> Option<&Entry<T>> {
self.entries.iter().find(|entry| entry.id == id)
self.entries.get(id)
}

pub fn into_params<P>(&self, cb: impl FnMut(&Entry<T>) -> P) -> Vec<P>
where
P: Into<PageParams>,
{
self.entries.iter().map(cb).collect()
self.entries.values().map(cb).collect()
}

pub fn into_pages<Params, Props>(
@@ -378,7 +387,7 @@ impl<T> ContentSource<T> {
where
Params: Into<PageParams>,
{
self.entries.iter().map(cb).collect()
self.entries.values().map(cb).collect()
}
Comment thread crates/maudit/src/logging.rs
@Princesseuh Princesseuh merged commit f861bf2 into main Mar 15, 2026
11 checks passed
@Princesseuh Princesseuh deleted the feat/depinfo-recompile-check branch March 15, 2026 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

crt:cli Changes in Maudit's CLI crate. crt:macros Changes in Maudit's macros crate crt:maudit Changes in Maudit crate. crt:oubli Changes in Oubli crate. examples website Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants