Release: PdfCraft v0.11.0 (#3893)

* Release: PdfCraft v0.11.0

- Bump the workspace to 0.11.0.
- Fix main's red CI (cargo-deny): update rustls-webpki 0.103.15 -> 0.103.16
  (RUSTSEC-2026-0382) and webpki-root-certs 1.0.9 -> 1.0.10 (RUSTSEC-2026-0383),
  both reached through winprint's sigstore build dependency.
- `cargo xtask version set` now moves the workspace crates' pins in
  [workspace.dependencies] with the version; before, `cargo update --workspace`
  refused the bump and the pins had to be edited by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tests: a healthy private retry may answer during the watchdog pass that scheduled it

shared_timeouts_retry_healthy_pages_privately_before_blacklisting failed on
CI (PR #3893): the try_recv that runs the watchdog spawns the private retries,
and page 1's retry has no gate, so on a loaded runner its healthy result
arrived before that same call read the channel. Accept that early answer
(page 1, no error) and use it as the healthy result; an error still fails.
Reproduced by sleeping 300 ms after the watchdog in try_recv: the old test
fails every time, the new one passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-21 15:27:33 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 4164951453
commit 3e3064532f
4 changed files with 117 additions and 83 deletions
Generated
+46 -46
View File
@@ -1887,7 +1887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -3938,7 +3938,7 @@ dependencies = [
[[package]]
name = "pdfcraft"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"craft-quit",
"eframe",
@@ -3963,7 +3963,7 @@ dependencies = [
[[package]]
name = "pdfcraft-a11y"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"pdfcraft-content",
"pdfcraft-cos",
@@ -3975,7 +3975,7 @@ dependencies = [
[[package]]
name = "pdfcraft-annot"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"hayro-syntax",
"pdfcraft-cos",
@@ -3986,7 +3986,7 @@ dependencies = [
[[package]]
name = "pdfcraft-audit"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"pdfcraft-content",
"pdfcraft-cos",
@@ -3997,7 +3997,7 @@ dependencies = [
[[package]]
name = "pdfcraft-automation"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"base64 0.22.1",
"flate2",
@@ -4018,7 +4018,7 @@ dependencies = [
[[package]]
name = "pdfcraft-cli"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"base64 0.22.1",
"pdfcraft-audit",
@@ -4035,18 +4035,18 @@ dependencies = [
[[package]]
name = "pdfcraft-compare"
version = "0.10.1"
version = "0.11.0"
[[package]]
name = "pdfcraft-content"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"pdfcraft-cos",
]
[[package]]
name = "pdfcraft-cos"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"hayro-syntax",
"pdfcraft-crypt",
@@ -4060,7 +4060,7 @@ dependencies = [
[[package]]
name = "pdfcraft-create"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"encoding_rs",
"hayro-syntax",
@@ -4076,7 +4076,7 @@ dependencies = [
[[package]]
name = "pdfcraft-crypt"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"aes",
"md-5",
@@ -4088,7 +4088,7 @@ dependencies = [
[[package]]
name = "pdfcraft-edit"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"hayro-syntax",
"kurbo 0.13.1",
@@ -4103,7 +4103,7 @@ dependencies = [
[[package]]
name = "pdfcraft-engine"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"chrono",
"craft-docpath",
@@ -4143,14 +4143,14 @@ dependencies = [
[[package]]
name = "pdfcraft-export"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"flate2",
]
[[package]]
name = "pdfcraft-filters"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"flate2",
"proptest",
@@ -4160,7 +4160,7 @@ dependencies = [
[[package]]
name = "pdfcraft-fonts"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"harfrust",
"pdfcraft-cos",
@@ -4170,7 +4170,7 @@ dependencies = [
[[package]]
name = "pdfcraft-forms"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"hayro-syntax",
"pdfcraft-content",
@@ -4182,18 +4182,18 @@ dependencies = [
[[package]]
name = "pdfcraft-geom"
version = "0.10.1"
version = "0.11.0"
[[package]]
name = "pdfcraft-js"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"boa_engine",
]
[[package]]
name = "pdfcraft-measure"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"pdfcraft-annot",
"pdfcraft-content",
@@ -4206,14 +4206,14 @@ dependencies = [
[[package]]
name = "pdfcraft-model"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"pdfcraft-cos",
]
[[package]]
name = "pdfcraft-ocr"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"ocrs",
"pdfcraft-fonts",
@@ -4223,7 +4223,7 @@ dependencies = [
[[package]]
name = "pdfcraft-optimize"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"image",
"pdfcraft-annot",
@@ -4236,11 +4236,11 @@ dependencies = [
[[package]]
name = "pdfcraft-oracle"
version = "0.10.1"
version = "0.11.0"
[[package]]
name = "pdfcraft-organize"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"hayro-syntax",
"pdfcraft-content",
@@ -4251,7 +4251,7 @@ dependencies = [
[[package]]
name = "pdfcraft-platform"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"async-channel",
"futures-lite",
@@ -4261,7 +4261,7 @@ dependencies = [
[[package]]
name = "pdfcraft-preflight"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"flate2",
"pdfcraft-content",
@@ -4272,7 +4272,7 @@ dependencies = [
[[package]]
name = "pdfcraft-print"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"craft-print",
"pdfcraft-content",
@@ -4283,7 +4283,7 @@ dependencies = [
[[package]]
name = "pdfcraft-redact"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"pdfcraft-annot",
"pdfcraft-content",
@@ -4299,7 +4299,7 @@ dependencies = [
[[package]]
name = "pdfcraft-render"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"bytemuck",
"flate2",
@@ -4318,7 +4318,7 @@ dependencies = [
[[package]]
name = "pdfcraft-sign"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"aes",
"aws-lc-rs",
@@ -4352,11 +4352,11 @@ dependencies = [
[[package]]
name = "pdfcraft-testkit"
version = "0.10.1"
version = "0.11.0"
[[package]]
name = "pdfcraft-ui-egui"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"arboard",
"base64 0.22.1",
@@ -4400,7 +4400,7 @@ dependencies = [
[[package]]
name = "pdfcraft-web"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"eframe",
"egui",
@@ -4412,7 +4412,7 @@ dependencies = [
[[package]]
name = "pdfcraft-xfa"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"base64 0.22.1",
"image",
@@ -4426,7 +4426,7 @@ dependencies = [
[[package]]
name = "pdfcraft-xfdf"
version = "0.10.1"
version = "0.11.0"
dependencies = [
"pdfcraft-annot",
"pdfcraft-cos",
@@ -4934,7 +4934,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -5484,7 +5484,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.12.1",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -5554,7 +5554,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -5565,9 +5565,9 @@ checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f"
[[package]]
name = "rustls-webpki"
version = "0.103.15"
version = "0.103.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281"
dependencies = [
"aws-lc-rs",
"ring",
@@ -6431,7 +6431,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix 1.1.5",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -7306,9 +7306,9 @@ dependencies = [
[[package]]
name = "webpki-root-certs"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b"
checksum = "58ec80e73fd8baf57bf0d9615554c75348bda990d6d5bbeab964dd068319fecb"
dependencies = [
"rustls-pki-types",
]
@@ -7514,7 +7514,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
+34 -34
View File
@@ -5,7 +5,7 @@ exclude = ["vendor"]
default-members = ["crates/*", "apps/pdfcraft", "apps/pdfcraft-cli", "xtask"]
[workspace.package]
version = "0.10.1"
version = "0.11.0"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.95"
@@ -21,39 +21,39 @@ craft-print = { git = "https://github.com/storytold/craft-libs", version = "0.1.
# Whether a path stored in a document may be opened (GoToR link targets).
craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "82a24b006eefa036b43c09a67fb9a3fbd17ffe63" }
# internal
pdfcraft-geom = { path = "crates/geom", version = "0.10.1" }
pdfcraft-filters = { path = "crates/filters", version = "0.10.1" }
pdfcraft-cos = { path = "crates/cos", version = "0.10.1" }
pdfcraft-crypt = { path = "crates/crypt", version = "0.10.1" }
pdfcraft-organize = { path = "crates/organize", version = "0.10.1" }
pdfcraft-measure = { path = "crates/measure", version = "0.10.1" }
pdfcraft-annot = { path = "crates/annot", version = "0.10.1" }
pdfcraft-audit = { path = "crates/audit", version = "0.10.1" }
pdfcraft-fonts = { path = "crates/fonts", version = "0.10.1" }
pdfcraft-model = { path = "crates/model", version = "0.10.1" }
pdfcraft-content = { path = "crates/content", version = "0.10.1" }
pdfcraft-redact = { path = "crates/redact", version = "0.10.1" }
pdfcraft-print = { path = "crates/print", version = "0.10.1" }
pdfcraft-xfdf = { path = "crates/xfdf", version = "0.10.1" }
pdfcraft-sign = { path = "crates/sign", version = "0.10.1" }
pdfcraft-optimize = { path = "crates/optimize", version = "0.10.1" }
pdfcraft-a11y = { path = "crates/a11y", version = "0.10.1" }
pdfcraft-oracle = { path = "crates/oracle", version = "0.10.1" }
pdfcraft-testkit = { path = "crates/testkit", version = "0.10.1" }
pdfcraft-ocr = { path = "crates/ocr", version = "0.10.1" }
pdfcraft-js = { path = "crates/js", version = "0.10.1" }
pdfcraft-compare = { path = "crates/compare", version = "0.10.1" }
pdfcraft-preflight = { path = "crates/preflight", version = "0.10.1" }
pdfcraft-export = { path = "crates/export", version = "0.10.1" }
pdfcraft-edit = { path = "crates/edit", version = "0.10.1" }
pdfcraft-create = { path = "crates/create", version = "0.10.1" }
pdfcraft-forms = { path = "crates/forms", version = "0.10.1" }
pdfcraft-xfa = { path = "crates/xfa", version = "0.10.1" }
pdfcraft-render = { path = "crates/render", version = "0.10.1" }
pdfcraft-engine = { path = "crates/engine", version = "0.10.1" }
pdfcraft-platform = { path = "crates/platform", version = "0.10.1" }
pdfcraft-ui-egui = { path = "crates/ui-egui", version = "0.10.1" }
pdfcraft-automation = { path = "crates/automation", version = "0.10.1", default-features = false }
pdfcraft-geom = { path = "crates/geom", version = "0.11.0" }
pdfcraft-filters = { path = "crates/filters", version = "0.11.0" }
pdfcraft-cos = { path = "crates/cos", version = "0.11.0" }
pdfcraft-crypt = { path = "crates/crypt", version = "0.11.0" }
pdfcraft-organize = { path = "crates/organize", version = "0.11.0" }
pdfcraft-measure = { path = "crates/measure", version = "0.11.0" }
pdfcraft-annot = { path = "crates/annot", version = "0.11.0" }
pdfcraft-audit = { path = "crates/audit", version = "0.11.0" }
pdfcraft-fonts = { path = "crates/fonts", version = "0.11.0" }
pdfcraft-model = { path = "crates/model", version = "0.11.0" }
pdfcraft-content = { path = "crates/content", version = "0.11.0" }
pdfcraft-redact = { path = "crates/redact", version = "0.11.0" }
pdfcraft-print = { path = "crates/print", version = "0.11.0" }
pdfcraft-xfdf = { path = "crates/xfdf", version = "0.11.0" }
pdfcraft-sign = { path = "crates/sign", version = "0.11.0" }
pdfcraft-optimize = { path = "crates/optimize", version = "0.11.0" }
pdfcraft-a11y = { path = "crates/a11y", version = "0.11.0" }
pdfcraft-oracle = { path = "crates/oracle", version = "0.11.0" }
pdfcraft-testkit = { path = "crates/testkit", version = "0.11.0" }
pdfcraft-ocr = { path = "crates/ocr", version = "0.11.0" }
pdfcraft-js = { path = "crates/js", version = "0.11.0" }
pdfcraft-compare = { path = "crates/compare", version = "0.11.0" }
pdfcraft-preflight = { path = "crates/preflight", version = "0.11.0" }
pdfcraft-export = { path = "crates/export", version = "0.11.0" }
pdfcraft-edit = { path = "crates/edit", version = "0.11.0" }
pdfcraft-create = { path = "crates/create", version = "0.11.0" }
pdfcraft-forms = { path = "crates/forms", version = "0.11.0" }
pdfcraft-xfa = { path = "crates/xfa", version = "0.11.0" }
pdfcraft-render = { path = "crates/render", version = "0.11.0" }
pdfcraft-engine = { path = "crates/engine", version = "0.11.0" }
pdfcraft-platform = { path = "crates/platform", version = "0.11.0" }
pdfcraft-ui-egui = { path = "crates/ui-egui", version = "0.11.0" }
pdfcraft-automation = { path = "crates/automation", version = "0.11.0", default-features = false }
# pdf
hayro = "0.7.1"
+13 -2
View File
@@ -1553,13 +1553,24 @@ mod tests {
]);
shared_bad.entered.wait();
shared_healthy.entered.wait();
assert!(pool.try_recv().is_none(), "shared timeouts retry instead of answering yet");
// This watchdog pass retries both pages privately. Page 1's retry has no gate, so on a
// loaded machine it can finish before this same call reads the results: that early
// answer is the healthy page, never a timeout error.
let early = pool.try_recv();
assert!(
early.as_ref().is_none_or(|page| page.request.page == 1 && page.error.is_none()),
"shared timeouts retry instead of answering with an error: {:?}",
early.as_ref().map(|page| (page.request.page, &page.error))
);
shared_bad.release.wait();
shared_healthy.release.wait();
shared_bad.finished.wait();
shared_healthy.finished.wait();
private_bad.entered.wait();
let healthy = pool.results.recv_timeout(Duration::from_secs(30)).expect("healthy private retry");
let healthy = match early {
Some(page) => page,
None => pool.results.recv_timeout(Duration::from_secs(30)).expect("healthy private retry"),
};
assert!(healthy.error.is_none(), "{:?}", healthy.error);
assert_eq!((healthy.request.page, healthy.width, healthy.height, healthy.request.tag), (1, 100, 50, 41));
pool.watchdog();
+24 -1
View File
@@ -15,12 +15,21 @@ pub fn read(manifest: &str) -> Result<String, String> {
Ok(v)
}
/// `manifest` with the workspace version replaced; everything else is byte-for-byte unchanged.
/// `manifest` with the workspace version replaced, together with the matching `version` of every
/// path dependency in `[workspace.dependencies]` (the workspace's own crates, which must agree
/// with it or `cargo update` refuses them); everything else is byte-for-byte unchanged.
pub fn replace(manifest: &str, new: &str) -> Result<String, String> {
validate(new)?;
let old = read(manifest)?;
let (idx, _) = find_line(manifest)?;
let (old_pin, new_pin) = (format!("version = \"{old}\""), format!("version = \"{new}\""));
let mut out = String::with_capacity(manifest.len() + 8);
let mut in_deps = false;
for (i, line) in manifest.split_inclusive('\n').enumerate() {
let t = line.trim();
if t.starts_with('[') {
in_deps = t == "[workspace.dependencies]";
}
if i == idx {
let eol = if line.ends_with("\r\n") {
"\r\n"
@@ -30,6 +39,8 @@ pub fn replace(manifest: &str, new: &str) -> Result<String, String> {
""
};
out.push_str(&format!("version = \"{new}\"{eol}"));
} else if in_deps && line.contains("path = \"") && line.contains(&old_pin) {
out.push_str(&line.replace(&old_pin, &new_pin));
} else {
out.push_str(line);
}
@@ -131,6 +142,18 @@ mod tests {
assert!(out.contains("foo = { version = \"1\" }"));
}
#[test]
fn moves_the_workspace_crates_pins_with_it() {
let manifest = "[workspace.package]\nversion = \"0.1.0\"\n\n[workspace.dependencies]\n\
own = { path = \"crates/own\", version = \"0.1.0\" }\n\
dep = { version = \"0.1.0\" }\n\
other = { path = \"crates/other\", version = \"0.2\" }\n";
let out = replace(manifest, "0.2.0").unwrap();
assert!(out.contains("own = { path = \"crates/own\", version = \"0.2.0\" }"), "{out}");
assert!(out.contains("dep = { version = \"0.1.0\" }"), "a registry dependency keeps its version");
assert!(out.contains("other = { path = \"crates/other\", version = \"0.2\" }"));
}
#[test]
fn keeps_crlf_line_endings() {
let crlf = MANIFEST.replace('\n', "\r\n");