v0.2.10
RustBox CI / rustbox (push) Canceled after 0s

This commit is contained in:
Mateusz Gruszczyński committed 2026-11-01 23:52:18 +01:00
1 parent a116890e20
commit 02d0b13ec1
24 files changed
+245 -93

No files matched your search

+21 -7
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Static/offline release checks for RustBox 0.2.9.
"""Static/offline release checks for RustBox 0.2.10.
These checks intentionally avoid pretending to replace `cargo test`: they validate
configuration, SQL migrations, Docker examples, query-catalog parity and release
@@ -49,14 +49,16 @@ def extract_section(text: str, begin: str, end: str) -> str:
# Release/toolchain metadata.
cargo = tomllib.loads(read("Cargo.toml"))
require(cargo["package"]["version"] == "0.2.9", "Cargo package version is 0.2.9")
require(cargo["package"]["version"] == "0.2.10", "Cargo package version is 0.2.10")
require(cargo["package"]["rust-version"] == "1.99.1", "Cargo rust-version is 1.99.1")
toolchain = tomllib.loads(read("rust-toolchain.toml"))
require(toolchain["toolchain"]["channel"] == "1.99.1", "rust-toolchain pins Rust 1.99.1")
require((ROOT / "Cargo.lock").is_file(), "Cargo.lock is retained for reproducible application builds")
lock = tomllib.loads(read("Cargo.lock"))
lock_rustbox = next((pkg for pkg in lock.get("package", []) if pkg.get("name") == "rustbox"), {})
require(lock_rustbox.get("version") == "0.2.9", "Cargo.lock RustBox package version is 0.2.9")
require(lock_rustbox.get("version") == "0.2.10", "Cargo.lock RustBox package version is 0.2.10")
require(cargo["dependencies"].get("encoding_rs") == "0.8", "direct charset decoder dependency is pinned")
require("encoding_rs" in lock_rustbox.get("dependencies", []), "Cargo.lock records direct charset decoder dependency")
require("cargo generate-lockfile" in read("scripts/build-release.sh"), "release script generates Cargo.lock when missing")
release_script = read("scripts/build-release.sh")
release_script_flat = " ".join(release_script.replace("\\", " ").split())
@@ -69,14 +71,16 @@ require("make_zip.py" in package_script and "sha256sum" in package_script, "rele
require("cargo generate-lockfile" in read(".github/workflows/ci.yml"), "CI regenerates Cargo.lock before --locked checks")
require("validate-release-static.py" in read("scripts/build-release.sh"), "release script runs offline/static release gate")
require("validate-release-static.py" in read(".github/workflows/ci.yml"), "CI runs offline/static release gate")
require(read("README.md").startswith("# RustBox 0.2.9"), "README identifies RustBox 0.2.9")
require(read("RELEASE-MANIFEST.txt").startswith("RustBox 0.2.9"), "release manifest identifies RustBox 0.2.9")
require(read("RELEASE-VALIDATION.txt").startswith("RustBox 0.2.9"), "release validation identifies RustBox 0.2.9")
require(read("docs/IMPLEMENTATION_STATUS.md").startswith("# RustBox 0.2.9"), "implementation status identifies RustBox 0.2.9")
require(read("README.md").startswith("# RustBox 0.2.10"), "README identifies RustBox 0.2.10")
require(read("RELEASE-MANIFEST.txt").startswith("RustBox 0.2.10"), "release manifest identifies RustBox 0.2.10")
require(read("RELEASE-VALIDATION.txt").startswith("RustBox 0.2.10"), "release validation identifies RustBox 0.2.10")
require(read("docs/IMPLEMENTATION_STATUS.md").startswith("# RustBox 0.2.10"), "implementation status identifies RustBox 0.2.10")
bootstrap_js = read("src/web/js/00-bootstrap.js")
bindings_js = read("src/web/js/95-bindings-boot.js")
realtime_js = read("src/web/js/80-settings-realtime.js")
mail_actions_js = read("src/web/js/60-mail-actions-search.js")
auth_js = read("src/web/js/20-preferences-auth.js")
reader_js = read("src/web/js/40-messages-reader.js")
require("perPage: 300" in bootstrap_js and "messages_per_page: 300" in bootstrap_js, "browser mailbox default is 300 messages")
require(re.search(r"default_per_page\(\)\s*->\s*u32\s*\{\s*300\s*\}", read("src/routes/messages/types.rs")) is not None, "folder message API default page size is 300")
require('1..=300' in read("src/routes/messages/mod.rs"), "folder message API accepts up to 300 messages per page")
@@ -84,6 +88,15 @@ require('20..=300' in read("src/db/mail_preferences.rs"), "mailbox preference ac
require('const appShortcut = keyboardIdle && !event.ctrlKey && !event.metaKey && !event.altKey;' in bindings_js, "single-key shortcuts preserve Ctrl/Cmd/Alt browser shortcuts")
require('realtimeNotificationsReady' in realtime_js and 'suppressRealtimeNotificationsForFolder' in realtime_js, "realtime new-mail notification gating is present")
require('suppressRealtimeNotificationsForFolder(notificationDestination)' in mail_actions_js, "bulk destination notifications are suppressed")
require('finishAuthenticatedStartup({ showLoginToast = false } = {})' in auth_js and 'finishAuthenticatedStartup({ showLoginToast: true })' in auth_js and bindings_js.count('finishAuthenticatedStartup();') >= 1, "login success toast is limited to real login, not refresh/session restoration")
require('<meta charset="utf-8">${csp}' in reader_js, "message preview srcdoc explicitly declares UTF-8")
require('function sanitizeMessagePreviewHtml(html)' in bootstrap_js and 'if (!node.hasAttribute("target")) node.setAttribute("target", "_blank");' in bootstrap_js and '!["_self", "_parent", "_top"].includes(target)' in bootstrap_js and 'rel.add("noopener");' in bootstrap_js and 'rel.add("noreferrer");' in bootstrap_js and 'sanitizeMessagePreviewHtml(detail.html)' in reader_js, "message preview links default to safe new-window navigation without overriding explicit targets")
require('attachmentContentUrl(folder, uid, attachment)' in reader_js and 'frame.src = attachmentContentUrl(folder, uid, attachment);' in reader_js and 'URL.createObjectURL(blob)' in reader_js, "PDF preview uses authenticated inline URL while other attachment previews retain blob handling")
require('mail_parser::MessageParser::default().parse(synthetic.as_bytes())' in read("src/imap/parse.rs") and 'decode_header_bytes' in read("src/imap/parse.rs"), "RFC 2047 header decoding uses mail-parser and tolerant envelope byte handling")
require('encoding_rs::Encoding::for_label' in read("src/imap/parse.rs") and 'decodes_polish_rfc2047_subjects' in read("src/imap/parse.rs"), "Polish UTF-8/ISO-8859-2/Windows-1250 header decoding is covered")
require('let is_pdf = response_content_type_is(&response, "application/pdf");' in read("src/routes/mod.rs") and 'HeaderValue::from_static("SAMEORIGIN")' in read("src/routes/mod.rs") and "HeaderValue::from_static(\"frame-ancestors 'self'\")" in read("src/routes/mod.rs"), "security headers allow same-origin framing for inline PDF previews only")
index_html = read("src/web/index.html")
require('class="compose-limit-summary"' in index_html and 'Per-file upload limit set with' not in index_html and 'Hard limit for the complete RFC822/MIME message' not in index_html, "Composing shows compact raw size limits without implementation details")
require("V048" in read("RELEASE-MANIFEST.txt") and "V049" in read("RELEASE-MANIFEST.txt"), "release manifest documents V048 and reserves V049")
require("V048" in read("migrations/README.md") and "V049" in read("migrations/README.md"), "migration guide documents V048 and reserves V049")
require((ROOT / "docs/STORAGE_SEARCH_OPERATIONS.md").is_file(), "storage/search operations guide exists")
@@ -177,6 +190,7 @@ route_tests = read("src/routes/tests.rs")
auth_routes = read("src/routes/auth.rs")
auth_js = read("src/web/js/20-preferences-auth.js")
require("security_headers_are_opt_in_and_content_type_aware" in route_tests, "security-header regression test covers content-type-aware behavior")
require("pdf_api_responses_allow_same_origin_preview_frames" in route_tests, "PDF preview framing has a same-origin security regression test")
require("auth_policy_exposes_only_login_requirements_before_authentication" in route_tests, "public auth-policy minimization has a regression test")
require("pub struct SessionPolicyResponse" in auth_routes and '.route("/session-policy", get(auth::session_policy))' in routes_mod, "feature policy is exposed only behind the authenticated session route")
require('request("/auth/session-policy"' in auth_js and "async function loadSessionPolicy()" in auth_js, "frontend loads optional feature policy only after authentication")