Skip to content

Commit 99d0cea

Browse files
feat: implement code execution integration and serde prevention
- Add code execution to build pipeline with validation - Integrate CodeExecutionResult tracking in SiteOutput - Fail builds on code execution errors in production mode - Add comprehensive error reporting with source locations - Fix PlugResult return types in code execution plugin - Update baseline crate to use Facet instead of serde - Add clippy.toml to prevent serde usage across workspace - Add graceful terminal cleanup before error display
1 parent c4175f0 commit 99d0cea

File tree

9 files changed

+668
-255
lines changed

9 files changed

+668
-255
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
[workspace]
22
members = [".", "crates/gingembre", "crates/livereload-client", "crates/dodeca-devtools", "crates/dodeca-protocol", "crates/plugcard", "crates/plugcard-macros", "crates/dodeca-baseline", "crates/dodeca-webp", "crates/dodeca-jxl", "crates/dodeca-minify", "crates/dodeca-svgo", "crates/dodeca-sass", "crates/dodeca-css", "crates/dodeca-js", "crates/dodeca-pagefind", "crates/dodeca-image", "crates/dodeca-fonts", "crates/dodeca-linkcheck", "crates/dodeca-code-execution", "xtask"]
33

4+
5+
46
[package]
57
name = "dodeca"
68
version = "0.2.12"
@@ -14,6 +16,8 @@ homepage = "https://dodeca.bearcove.eu/"
1416
keywords = ["static-site", "ssg", "incremental"]
1517
categories = ["command-line-utilities", "web-programming"]
1618

19+
20+
1721
[package.metadata.wix]
1822
upgrade-guid = "F32CA0D7-4DDE-4333-A5EA-374211B0C136"
1923
path-guid = "68C82E46-C932-4CA9-BEE0-216F988EADF9"
@@ -91,6 +95,9 @@ postcard = { version = "1", features = ["alloc"] } # Fast, compact binary seria
9195
# Devtools protocol (shared types between server and WASM client)
9296
dodeca-protocol = { path = "crates/dodeca-protocol" }
9397

98+
# Code execution plugin types
99+
dodeca-code-execution = { path = "crates/dodeca-code-execution" }
100+
94101
# Content-addressed storage
95102
rapidhash = "4"
96103
canopydb = "0.2.5"
@@ -112,6 +119,8 @@ lol_html = "2.7" # Streaming HTML rewriter
112119

113120
# Image processing is now handled entirely via plugins (dodeca-image, dodeca-webp, dodeca-jxl)
114121

122+
# Code execution is handled via the dodeca-code-execution plugin
123+
115124
# Plugin system for image encoding/decoding, minification, etc.
116125
plugcard = { path = "crates/plugcard" }
117126

clippy.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Clippy configuration for dodeca
2+
# Disallow serde usage - we use facet/postcard for serialization instead
3+
4+
disallowed-types = [
5+
{ path = "serde::Serialize", reason = "Use facet::Facet for serialization instead" },
6+
{ path = "serde::Deserialize", reason = "Use facet::Facet for serialization instead" },
7+
{ path = "serde_json::Value", reason = "Use facet_postcard for serialization instead" },
8+
]
9+
10+
disallowed-methods = [
11+
{ path = "serde_json::to_string", reason = "Use facet_postcard::to_allocvec for serialization instead" },
12+
{ path = "serde_json::from_str", reason = "Use facet_postcard::from_bytes for deserialization instead" },
13+
]

crates/dodeca-baseline/src/lib.rs

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@ pub fn add(a: i32, b: i32) -> i32 {
1616

1717
#[cfg(test)]
1818
mod tests {
19-
use plugcard::{list_methods, MethodCallData, MethodCallResult};
19+
use plugcard::{MethodCallData, MethodCallResult, list_methods};
2020

2121
#[test]
2222
fn test_methods_registered() {
2323
let methods = list_methods();
24-
assert!(methods.len() >= 2, "Expected at least 2 methods, got {}", methods.len());
24+
assert!(
25+
methods.len() >= 2,
26+
"Expected at least 2 methods, got {}",
27+
methods.len()
28+
);
2529

2630
let names: Vec<_> = methods.iter().map(|m| m.name).collect();
2731
assert!(names.contains(&"reverse_string"), "Missing reverse_string");
@@ -34,12 +38,15 @@ mod tests {
3438
let reverse = methods.iter().find(|m| m.name == "reverse_string").unwrap();
3539

3640
// Serialize input: { input: "hello" }
37-
#[derive(plugcard::serde::Serialize)]
38-
#[serde(crate = "plugcard::serde")]
39-
struct Input { input: String }
41+
#[derive(plugcard::facet::Facet)]
42+
struct Input {
43+
input: String,
44+
}
4045

41-
let input = Input { input: "hello".to_string() };
42-
let input_bytes = plugcard::postcard::to_allocvec(&input).unwrap();
46+
let input = Input {
47+
input: "hello".to_string(),
48+
};
49+
let input_bytes = plugcard::facet_postcard::to_allocvec(&input).unwrap();
4350

4451
let mut output_buf = [0u8; 256];
4552
let mut data = MethodCallData {
@@ -58,26 +65,29 @@ mod tests {
5865
assert_eq!(data.result, MethodCallResult::Success);
5966

6067
// Deserialize output
61-
let output: String = plugcard::postcard::from_bytes(&output_buf[..data.output_len]).unwrap();
68+
let output: String =
69+
plugcard::facet_postcard::from_bytes(&output_buf[..data.output_len]).unwrap();
6270
assert_eq!(output, "olleh");
6371
}
6472

6573
#[test]
6674
fn test_add_dispatch() {
6775
let methods = list_methods();
68-
let add_method = methods.iter().find(|m| m.name == "add").unwrap();
76+
let add = methods.iter().find(|m| m.name == "add").unwrap();
6977

70-
// Serialize input: { a: 2, b: 3 }
71-
#[derive(plugcard::serde::Serialize)]
72-
#[serde(crate = "plugcard::serde")]
73-
struct Input { a: i32, b: i32 }
78+
// Serialize input: { a: 5, b: 3 }
79+
#[derive(plugcard::facet::Facet)]
80+
struct Input {
81+
a: i32,
82+
b: i32,
83+
}
7484

75-
let input = Input { a: 2, b: 3 };
76-
let input_bytes = plugcard::postcard::to_allocvec(&input).unwrap();
85+
let input = Input { a: 5, b: 3 };
86+
let input_bytes = plugcard::facet_postcard::to_allocvec(&input).unwrap();
7787

7888
let mut output_buf = [0u8; 256];
7989
let mut data = MethodCallData {
80-
key: add_method.key,
90+
key: add.key,
8191
input_ptr: input_bytes.as_ptr(),
8292
input_len: input_bytes.len(),
8393
output_ptr: output_buf.as_mut_ptr(),
@@ -87,12 +97,13 @@ mod tests {
8797
};
8898

8999
// Call the method
90-
unsafe { (add_method.call)(&mut data) };
100+
unsafe { (add.call)(&mut data) };
91101

92102
assert_eq!(data.result, MethodCallResult::Success);
93103

94104
// Deserialize output
95-
let output: i32 = plugcard::postcard::from_bytes(&output_buf[..data.output_len]).unwrap();
96-
assert_eq!(output, 5);
105+
let output: i32 =
106+
plugcard::facet_postcard::from_bytes(&output_buf[..data.output_len]).unwrap();
107+
assert_eq!(output, 8);
97108
}
98109
}

0 commit comments

Comments
 (0)