1 parent
5dc9db0b1c
commit
83440f20e2
28 files changed
+1925
-213
No files matched your search
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Static/offline release checks for RustBox 0.2.11.
|
||||
"""Static/offline release checks for RustBox 0.2.12.
|
||||
|
||||
These checks intentionally avoid pretending to replace `cargo test`: they validate
|
||||
configuration, SQL migrations, Docker examples, query-catalog parity and release
|
||||
@@ -49,14 +49,14 @@ 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.11", "Cargo package version is 0.2.11")
|
||||
require(cargo["package"]["version"] == "0.2.12", "Cargo package version is 0.2.12")
|
||||
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.11", "Cargo.lock RustBox package version is 0.2.11")
|
||||
require(lock_rustbox.get("version") == "0.2.12", "Cargo.lock RustBox package version is 0.2.12")
|
||||
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")
|
||||
@@ -71,27 +71,54 @@ 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.11"), "README identifies RustBox 0.2.11")
|
||||
require(read("RELEASE-MANIFEST.txt").startswith("RustBox 0.2.11"), "release manifest identifies RustBox 0.2.11")
|
||||
require(read("RELEASE-VALIDATION.txt").startswith("RustBox 0.2.11"), "release validation identifies RustBox 0.2.11")
|
||||
require(read("docs/IMPLEMENTATION_STATUS.md").startswith("# RustBox 0.2.11"), "implementation status identifies RustBox 0.2.11")
|
||||
require(read("README.md").startswith("# RustBox 0.2.12"), "README identifies RustBox 0.2.12")
|
||||
require(read("RELEASE-MANIFEST.txt").startswith("RustBox 0.2.12"), "release manifest identifies RustBox 0.2.12")
|
||||
require(read("RELEASE-VALIDATION.txt").startswith("RustBox 0.2.12"), "release validation identifies RustBox 0.2.12")
|
||||
require(read("docs/IMPLEMENTATION_STATUS.md").startswith("# RustBox 0.2.12"), "implementation status identifies RustBox 0.2.12")
|
||||
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")
|
||||
compose_js = read("src/web/js/50-compose.js")
|
||||
compose_css = read("src/web/css/40-compose.css")
|
||||
mail_css = read("src/web/css/30-mail.css")
|
||||
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")
|
||||
require('20..=300' in read("src/db/mail_preferences.rs"), "mailbox preference accepts 300 messages per page")
|
||||
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('realtimeFolderIsDrafts' in realtime_js and 'if (realtimeFolderIsDrafts(data?.folder)) return true;' in realtime_js, "realtime new-mail notifications are suppressed for Drafts folders")
|
||||
require('const draftsFolder = syncImap' in compose_js and 'suppressRealtimeNotificationsForFolder(draftsFolder)' in compose_js and 'releaseRealtimeSuppression();' in compose_js, "manual IMAP draft save suppresses its own realtime new-mail notification")
|
||||
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('function composeAttachmentInsertionRange' in compose_js and 'target.collapse(false);' in compose_js and 'range.insertNode(inserted);' in compose_js and 'document.execCommand("insertHTML"' not in compose_js, "compose attachment embedding uses non-destructive DOM Range insertion")
|
||||
require('position: fixed;' in compose_css and '.compose-image-actions' in compose_css and 'grid-template-rows: auto auto auto minmax(240px, 1fr) auto auto auto auto;' in compose_css, "inline image actions float without consuming a composer grid row")
|
||||
require('function beginInlineImageResize' in compose_js and 'data-image-resize="nw"' in read("src/web/index.html") and '.compose-image-resize-handle' in compose_css, "selected inline images expose pointer resize handles")
|
||||
require('application/x-rustbox-inline-image' in bindings_js and 'function moveInlineImageToRange' in compose_js, "inline images can be repositioned by drag and drop")
|
||||
require('clipboardData' in bindings_js and 'uploadInlineImage(images, range)' in bindings_js, "clipboard images are uploaded and embedded inline")
|
||||
require('.compose-attachment-kind' in compose_css and 'Image attachment' in compose_js and 'Inline image' in compose_js, "composer distinguishes embedded images from regular attachments")
|
||||
require("function mergeComposeAttachments" in compose_js and "await refreshComposeAttachmentsFromServer(id);" in compose_js and "attachments: mergeComposeAttachments(local.attachments, server.attachments)" in compose_js, "composer preserves multiple attachments and draft recovery merges authoritative server attachment metadata")
|
||||
require("const attachments = mergeComposeAttachments(state.composeAttachments);" in compose_js and "Inline link" in compose_js and "detachComposeAttachmentFromBody" in compose_js, "composer attachment strip keeps inline and regular attachments visible together")
|
||||
require('grid-template-columns: repeat(2, minmax(0, 1fr));' in mail_css and '.load-more {' in mail_css and 'width: 100%;' in mail_css.split('.load-more {', 1)[1].split('}', 1)[0], "message load actions share the full available list width")
|
||||
require('id="compose-fields-toggle"' in read("src/web/index.html") and 'function setComposeFieldsCollapsed' in compose_js and 'function toggleComposeFields' in compose_js and '.compose-dialog.compose-fields-collapsed .compose-addresses' in compose_css and 'setComposeFieldsCollapsed(false)' in bindings_js, "composer addressing fields can collapse to maximize editor space and reopen on invalid recipient input")
|
||||
require('id="compose-save-draft"' in read("src/web/index.html") and '$("#compose-save-draft").addEventListener("click", saveComposeDraftToImap);' in bindings_js and 'saveCurrentDraft(false, true, true)' in compose_js, "composer exposes an explicit manual IMAP draft save before cancel")
|
||||
require('id="compose-delete-draft"' in read("src/web/index.html") and '$("#compose-delete-draft").addEventListener("click", deleteComposeDraft);' in bindings_js and 'function deleteComposeDraft()' in compose_js and 'imap_uid_validity' in compose_js, "composer exposes permanent draft deletion with current IMAP coordinates")
|
||||
require('if req.sync_imap {' in read("src/routes/drafts.rs") and 'Failed to save draft to IMAP' in read("src/routes/drafts.rs") and 'AppError::ServiceUnavailable' in read("src/routes/drafts.rs"), "manual IMAP draft save fails visibly when the IMAP snapshot cannot be appended")
|
||||
drafts_rs = read("src/routes/drafts.rs")
|
||||
require('.keep_bcc()' in drafts_rs and 'if valid_recipients == 0 {' in drafts_rs and 'builder = builder.envelope(envelope);' in drafts_rs, "IMAP draft snapshots allow recipient-less drafts and preserve Bcc headers")
|
||||
require('db::special_folders::get_role(conn, "drafts")?' in drafts_rs and 'default_drafts_folder' in drafts_rs and 'find_folder_by_attribute(conn, "\\\\Drafts")?' in drafts_rs, "manual IMAP draft save honors configured and detected Drafts folders")
|
||||
require('for common_name in ["Drafts", "Draft"]' in drafts_rs, "manual IMAP draft save recognizes common Draft and Drafts folder names")
|
||||
require('get_imap_draft_handler' in drafts_rs and '/messages/{folder}/{uid}/draft-compose' in read("src/routes/mod.rs") and 'fetch_body(&creds, &folder, uid)' in drafts_rs, "saved IMAP drafts reopen from RFC822/MIME without a local draft-row dependency")
|
||||
require('delete_imap_draft_source' in drafts_rs and 'Incomplete IMAP draft deletion coordinates' in drafts_rs and 'let mut attachments_deleted = 0usize;' in drafts_rs, "draft deletion verifies the IMAP source and cascades local attachment cleanup")
|
||||
require('messageLooksLikeImapDraft' in reader_js and 'await openImapDraftComposer(folder, uid);' in reader_js and 'state.composeRemoteDraft = draft.imap_source' in compose_js, "IMAP Drafts messages open directly in the normal composer")
|
||||
require('imap_source' in compose_js and 'imap_attachments' in compose_js and 'load_imap_source_attachments' in drafts_rs and 'remove_imap_source_snapshot' in drafts_rs, "IMAP draft edits preserve remote MIME attachments and replace the source snapshot safely")
|
||||
require('Attachment::new_inline_with_name' in drafts_rs and 'Attachment::new_inline_with_name' in read("src/smtp/builder.rs"), "inline MIME parts preserve attachment filenames in saved drafts and sent messages")
|
||||
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")
|
||||
|
||||
Reference in new issue
Block a user