M0.4/M0.5: deny.toml + xtask deny in ci, GitHub CI workflow; MCP module behind a Cargo feature

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Brandon ThomasandClaude Opus 5.5 committed 2026-09-30 23:45:10 -04:00
1 parent b8fed39be5
commit 19788d3336
10 files changed
+169 -14

No files matched your search

+86
View File
@@ -0,0 +1,86 @@
name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- name: Install Linux system dependencies (egui/winit/rfd)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libxkbcommon-dev libwayland-dev libx11-dev libxrandr-dev libxi-dev \
libgl1-mesa-dev libgtk-3-dev
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- name: Format
if: runner.os == 'Linux'
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --workspace --all-targets -- -D warnings
- name: Test
run: cargo test --workspace
- name: Layering
run: cargo xtask layers
- name: Assets (licences and attribution)
if: runner.os == 'Linux'
run: cargo xtask assets
- name: CLI without the MCP server still builds
if: runner.os == 'Linux'
run: cargo check -p printcraft-cli --no-default-features
wasm:
name: wasm32 check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
with:
key: wasm32
- name: Check library crates for wasm32
run: cargo xtask wasm
deny:
name: dependency licences and advisories
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check
# Nightly fuzzing arrives with the M1 fuzz targets (execution plan M0.5 / M1).
Generated
+2 -2
View File
@@ -5039,9 +5039,9 @@ dependencies = [
[[package]]
name = "yoke-derive"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78"
checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71"
dependencies = [
"proc-macro2",
"quote",
+9 -9
View File
@@ -13,15 +13,15 @@ repository = "https://github.com/storytold/printcraft"
[workspace.dependencies]
# internal
printcraft-geom = { path = "crates/geom" }
printcraft-filters = { path = "crates/filters" }
printcraft-cos = { path = "crates/cos" }
printcraft-crypt = { path = "crates/crypt" }
printcraft-organize = { path = "crates/organize" }
printcraft-render = { path = "crates/render" }
printcraft-engine = { path = "crates/engine" }
printcraft-ui-egui = { path = "crates/ui-egui" }
printcraft-automation = { path = "crates/automation" }
printcraft-geom = { path = "crates/geom", version = "0.1.0" }
printcraft-filters = { path = "crates/filters", version = "0.1.0" }
printcraft-cos = { path = "crates/cos", version = "0.1.0" }
printcraft-crypt = { path = "crates/crypt", version = "0.1.0" }
printcraft-organize = { path = "crates/organize", version = "0.1.0" }
printcraft-render = { path = "crates/render", version = "0.1.0" }
printcraft-engine = { path = "crates/engine", version = "0.1.0" }
printcraft-ui-egui = { path = "crates/ui-egui", version = "0.1.0" }
printcraft-automation = { path = "crates/automation", version = "0.1.0", default-features = false }
# pdf
hayro = "0.7.1"
+1 -1
View File
@@ -21,4 +21,4 @@ printcraft-automation = { workspace = true }
[features]
default = ["mcp"]
# The opt-in MCP server (`printcraft-cli mcp`). Build with --no-default-features to leave it out.
mcp = []
mcp = ["printcraft-automation/mcp"]
+6 -1
View File
@@ -14,5 +14,10 @@ printcraft-render = { workspace = true }
printcraft-organize = { workspace = true }
serde_json = { workspace = true }
png = "0.18"
base64 = "0.22"
base64 = { version = "0.22", optional = true }
[features]
default = ["mcp"]
# The MCP server module. Opt-in at run time either way; disable to leave it out of the build.
mcp = ["dep:base64"]
+1
View File
@@ -9,6 +9,7 @@
//! with the origin at the top-left of the displayed page.
//! - An optional root directory confines every path a tool reads or writes.
#[cfg(feature = "mcp")]
pub mod mcp;
mod tools;
+4
View File
@@ -2,6 +2,7 @@
use std::path::{Path, PathBuf};
#[cfg(feature = "mcp")]
use printcraft_automation::mcp::McpServer;
use printcraft_automation::{Automation, Content, ToolError, tools};
use serde_json::{Value, json};
@@ -231,11 +232,13 @@ fn command_list_reports_enablement_and_tools() {
// ---- MCP ---------------------------------------------------------------------------------------
#[cfg(feature = "mcp")]
fn rpc(server: &mut McpServer, id: u64, method: &str, params: Value) -> Value {
let line = json!({ "jsonrpc": "2.0", "id": id, "method": method, "params": params }).to_string();
serde_json::from_str(&server.handle_line(&line).expect("a reply")).unwrap()
}
#[cfg(feature = "mcp")]
#[test]
fn mcp_session_over_stdio() {
let dir = workdir("mcp");
@@ -268,6 +271,7 @@ fn mcp_session_over_stdio() {
assert_eq!(&png[1..4], b"PNG");
}
#[cfg(feature = "mcp")]
#[test]
fn mcp_errors() {
let mut s = McpServer::new(Automation::new());
+43
View File
@@ -0,0 +1,43 @@
# cargo-deny policy (M0.4). Run with `cargo xtask deny`; part of `cargo xtask ci`.
# Code dependencies follow the same rule as assets (AGENTS.md): permissive licences only.
# Copyleft (GPL, AGPL, LGPL) is never linked; such tools run only as external oracle processes.
[graph]
all-features = true
[licenses]
confidence-threshold = 0.9
allow = [
"MIT",
"MIT-0",
"Apache-2.0",
"Apache-2.0 WITH LLVM-exception",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"Zlib",
"Unicode-3.0",
"BSL-1.0",
"CC0-1.0",
]
[licenses.private]
ignore = true
[bans]
multiple-versions = "allow"
wildcards = "deny"
allow-wildcard-paths = true
[sources]
unknown-registry = "deny"
unknown-git = "deny"
[advisories]
yanked = "warn"
# egui's default UI fonts (Ubuntu-Light, Hack, emoji-icon-font): font licences on the AGENTS.md
# §1.2 allowlist, attributed in ATTRIBUTION.toml as dependency-bundled assets.
[[licenses.exceptions]]
crate = "epaint_default_fonts"
allow = ["OFL-1.1", "Ubuntu-font-1.0"]
+15
View File
@@ -92,6 +92,7 @@ pub fn ci(_: &[String]) -> anyhow::Result<()> {
("layers", Box::new(|| layers(&[]))),
("wasm", Box::new(|| wasm(&[]))),
("assets", Box::new(|| crate::assets::run(&[]))),
("deny", Box::new(|| deny(&[]))),
];
for (i, (name, f)) in steps.iter().enumerate() {
eprintln!("\n=== ci: {name} ===");
@@ -104,6 +105,20 @@ pub fn ci(_: &[String]) -> anyhow::Result<()> {
Ok(())
}
/// Dependency licences, bans, sources and advisories (`deny.toml`). Skipped with a hint when
/// cargo-deny is not installed, except under CI (`CI` set), where it is required.
pub fn deny(_: &[String]) -> anyhow::Result<()> {
let installed = Command::new("cargo-deny").arg("--version").output().is_ok_and(|o| o.status.success());
if !installed {
if std::env::var_os("CI").is_some() {
bail!("cargo-deny is required in CI: cargo install cargo-deny --locked");
}
eprintln!("deny: SKIPPED, cargo-deny is not installed (cargo install cargo-deny --locked)");
return Ok(());
}
run_args(&["deny", "--log-level", "error", "check"])
}
fn run_args(args: &[&str]) -> anyhow::Result<()> {
let mut c = cargo();
c.args(args);
+2 -1
View File
@@ -14,7 +14,8 @@ type Command = fn(&[String]) -> anyhow::Result<()>;
const COMMANDS: &[(&str, &str, Command)] = &[
("layers", "Enforce the crate dependency layering (plan/architecture.md §3)", gates::layers),
("wasm", "cargo check --target wasm32-unknown-unknown for every crate below L8", gates::wasm),
("ci", "fmt --check, clippy -D warnings, test, layers, wasm, assets (stops at first failure)", gates::ci),
("deny", "Dependency licences, bans, sources and advisories (deny.toml; needs cargo-deny)", gates::deny),
("ci", "fmt --check, clippy -D warnings, test, layers, wasm, assets, deny (stops at first failure)", gates::ci),
("assets", "Enforce the asset policy (AGENTS.md §1) against ATTRIBUTION.toml; --write regenerates ATTRIBUTION.md", assets::run),
("corpus", "Fetch test corpora into corpus/ (git-ignored): pdf.js test PDFs", gates::corpus),
("check", "Robustness sweep over corpus/ with printcraft-cli; fails on crashes or regressions vs xtask/baselines", gates::check),