Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rumdl for openSUSE:Factory checked in at 2026-04-13 23:19:09 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rumdl (Old) and /work/SRC/openSUSE:Factory/.rumdl.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rumdl" Mon Apr 13 23:19:09 2026 rev:56 rq:1346342 version:0.1.70 Changes: -------- --- /work/SRC/openSUSE:Factory/rumdl/rumdl.changes 2026-04-11 22:31:12.156239441 +0200 +++ /work/SRC/openSUSE:Factory/.rumdl.new.21863/rumdl.changes 2026-04-13 23:20:02.162970134 +0200 @@ -1,0 +2,8 @@ +Mon Apr 13 05:11:09 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 0.1.70: + * Fixed + - MD044: do not flag proper names inside bare-domain link text + (56a45df) + +------------------------------------------------------------------- Old: ---- rumdl-0.1.69.obscpio New: ---- rumdl-0.1.70.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rumdl.spec ++++++ --- /var/tmp/diff_new_pack.Me9C8z/_old 2026-04-13 23:20:03.431022475 +0200 +++ /var/tmp/diff_new_pack.Me9C8z/_new 2026-04-13 23:20:03.435022640 +0200 @@ -17,7 +17,7 @@ Name: rumdl -Version: 0.1.69 +Version: 0.1.70 Release: 0 Summary: Markdown Linter written in Rust License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.Me9C8z/_old 2026-04-13 23:20:03.495025117 +0200 +++ /var/tmp/diff_new_pack.Me9C8z/_new 2026-04-13 23:20:03.499025282 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/rvben/rumdl.git</param> <param name="scm">git</param> <param name="submodules">enable</param> - <param name="revision">v0.1.69</param> + <param name="revision">v0.1.70</param> <param name="match-tag">v*.*.*</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.Me9C8z/_old 2026-04-13 23:20:03.531026602 +0200 +++ /var/tmp/diff_new_pack.Me9C8z/_new 2026-04-13 23:20:03.535026768 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/rvben/rumdl.git</param> - <param name="changesrevision">d81181d8fb04ef01bf9c612812644362533dce1c</param></service></servicedata> + <param name="changesrevision">96f0828bd8dd9ca31bb1b5c77f9c0cf305b45767</param></service></servicedata> (No newline at EOF) ++++++ rumdl-0.1.69.obscpio -> rumdl-0.1.70.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/.gitignore new/rumdl-0.1.70/.gitignore --- old/rumdl-0.1.69/.gitignore 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/.gitignore 2026-04-12 21:52:00.000000000 +0200 @@ -25,6 +25,7 @@ site/ docs/superpowers/ docs/design/ +.claude/investigations/ # npm platform package binaries npm/cli-*/rumdl diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/CHANGELOG.md new/rumdl-0.1.70/CHANGELOG.md --- old/rumdl-0.1.69/CHANGELOG.md 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/CHANGELOG.md 2026-04-12 21:52:00.000000000 +0200 @@ -12,6 +12,13 @@ + +## [0.1.70](https://github.com/rvben/rumdl/compare/v0.1.69...v0.1.70) - 2026-04-12 + +### Fixed + +- **MD044**: do not flag proper names inside bare-domain link text ([56a45df](https://github.com/rvben/rumdl/commit/56a45dfa92eb271a56cc8a9d4dc187fa732f1650)) + ## [0.1.69](https://github.com/rvben/rumdl/compare/v0.1.68...v0.1.69) - 2026-04-10 ### Fixed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/Cargo.lock new/rumdl-0.1.70/Cargo.lock --- old/rumdl-0.1.69/Cargo.lock 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/Cargo.lock 2026-04-12 21:52:00.000000000 +0200 @@ -2269,7 +2269,7 @@ [[package]] name = "rumdl" -version = "0.1.69" +version = "0.1.70" dependencies = [ "anyhow", "assert_cmd", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/Cargo.toml new/rumdl-0.1.70/Cargo.toml --- old/rumdl-0.1.69/Cargo.toml 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/Cargo.toml 2026-04-12 21:52:00.000000000 +0200 @@ -1,6 +1,6 @@ [package] name = "rumdl" -version = "0.1.69" +version = "0.1.70" edition = "2024" rust-version = "1.94.0" description = "A fast Markdown linter written in Rust (Ru(st) MarkDown Linter)" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/npm/cli-darwin-arm64/package.json new/rumdl-0.1.70/npm/cli-darwin-arm64/package.json --- old/rumdl-0.1.69/npm/cli-darwin-arm64/package.json 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/npm/cli-darwin-arm64/package.json 2026-04-12 21:52:00.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@rumdl/cli-darwin-arm64", - "version": "0.1.69", + "version": "0.1.70", "description": "rumdl binary for macOS ARM64 (Apple Silicon)", "license": "MIT", "repository": { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/npm/cli-darwin-x64/package.json new/rumdl-0.1.70/npm/cli-darwin-x64/package.json --- old/rumdl-0.1.69/npm/cli-darwin-x64/package.json 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/npm/cli-darwin-x64/package.json 2026-04-12 21:52:00.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@rumdl/cli-darwin-x64", - "version": "0.1.69", + "version": "0.1.70", "description": "rumdl binary for macOS x64 (Intel)", "license": "MIT", "repository": { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/npm/cli-linux-arm64/package.json new/rumdl-0.1.70/npm/cli-linux-arm64/package.json --- old/rumdl-0.1.69/npm/cli-linux-arm64/package.json 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/npm/cli-linux-arm64/package.json 2026-04-12 21:52:00.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@rumdl/cli-linux-arm64", - "version": "0.1.69", + "version": "0.1.70", "description": "rumdl binary for Linux ARM64 (glibc)", "license": "MIT", "repository": { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/npm/cli-linux-arm64-musl/package.json new/rumdl-0.1.70/npm/cli-linux-arm64-musl/package.json --- old/rumdl-0.1.69/npm/cli-linux-arm64-musl/package.json 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/npm/cli-linux-arm64-musl/package.json 2026-04-12 21:52:00.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@rumdl/cli-linux-arm64-musl", - "version": "0.1.69", + "version": "0.1.70", "description": "rumdl binary for Linux ARM64 (musl/Alpine)", "license": "MIT", "repository": { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/npm/cli-linux-x64/package.json new/rumdl-0.1.70/npm/cli-linux-x64/package.json --- old/rumdl-0.1.69/npm/cli-linux-x64/package.json 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/npm/cli-linux-x64/package.json 2026-04-12 21:52:00.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@rumdl/cli-linux-x64", - "version": "0.1.69", + "version": "0.1.70", "description": "rumdl binary for Linux x64 (glibc)", "license": "MIT", "repository": { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/npm/cli-linux-x64-musl/package.json new/rumdl-0.1.70/npm/cli-linux-x64-musl/package.json --- old/rumdl-0.1.69/npm/cli-linux-x64-musl/package.json 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/npm/cli-linux-x64-musl/package.json 2026-04-12 21:52:00.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@rumdl/cli-linux-x64-musl", - "version": "0.1.69", + "version": "0.1.70", "description": "rumdl binary for Linux x64 (musl/Alpine)", "license": "MIT", "repository": { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/npm/cli-win32-x64/package.json new/rumdl-0.1.70/npm/cli-win32-x64/package.json --- old/rumdl-0.1.69/npm/cli-win32-x64/package.json 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/npm/cli-win32-x64/package.json 2026-04-12 21:52:00.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "@rumdl/cli-win32-x64", - "version": "0.1.69", + "version": "0.1.70", "description": "rumdl binary for Windows x64", "license": "MIT", "repository": { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/npm/rumdl/package.json new/rumdl-0.1.70/npm/rumdl/package.json --- old/rumdl-0.1.69/npm/rumdl/package.json 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/npm/rumdl/package.json 2026-04-12 21:52:00.000000000 +0200 @@ -1,6 +1,6 @@ { "name": "rumdl", - "version": "0.1.69", + "version": "0.1.70", "description": "A fast Markdown linter written in Rust", "license": "MIT", "repository": { @@ -33,12 +33,12 @@ "node": ">=18.0.0" }, "optionalDependencies": { - "@rumdl/cli-darwin-x64": "0.1.69", - "@rumdl/cli-darwin-arm64": "0.1.69", - "@rumdl/cli-linux-x64": "0.1.69", - "@rumdl/cli-linux-arm64": "0.1.69", - "@rumdl/cli-linux-x64-musl": "0.1.69", - "@rumdl/cli-linux-arm64-musl": "0.1.69", - "@rumdl/cli-win32-x64": "0.1.69" + "@rumdl/cli-darwin-x64": "0.1.70", + "@rumdl/cli-darwin-arm64": "0.1.70", + "@rumdl/cli-linux-x64": "0.1.70", + "@rumdl/cli-linux-arm64": "0.1.70", + "@rumdl/cli-linux-x64-musl": "0.1.70", + "@rumdl/cli-linux-arm64-musl": "0.1.70", + "@rumdl/cli-win32-x64": "0.1.70" } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rumdl-0.1.69/src/rules/md044_proper_names.rs new/rumdl-0.1.70/src/rules/md044_proper_names.rs --- old/rumdl-0.1.69/src/rules/md044_proper_names.rs 2026-04-10 15:49:32.000000000 +0200 +++ new/rumdl-0.1.70/src/rules/md044_proper_names.rs 2026-04-12 21:52:00.000000000 +0200 @@ -411,9 +411,13 @@ }; let text_end = text_start + link.text.len(); - // If position is within the text portion, skip only if text is a URL + // If position is within the text portion, skip only if text is a URL. + // WikiLinks use the page name as both text and url; never treat them + // as bare-domain URLs regardless of whether the name contains dots. if byte_pos >= text_start && byte_pos < text_end { - return Self::link_text_is_url(&link.text); + let is_wikilink = matches!(link.link_type, LinkType::WikiLink { .. }); + return Self::link_text_is_url(&link.text) + || (!is_wikilink && Self::link_text_matches_link_url(&link.text, &link.url)); } // Position is in the URL/reference portion, skip it return true; @@ -443,10 +447,49 @@ } /// Check if link text is a URL that should not have proper name corrections. - /// Matches markdownlint behavior: skip text starting with `http://`, `https://`, or `www.`. fn link_text_is_url(text: &str) -> bool { let lower = text.trim().to_ascii_lowercase(); - lower.starts_with("http://") || lower.starts_with("https://") || lower.starts_with("www.") + lower.starts_with("http://") + || lower.starts_with("https://") + || lower.starts_with("www.") + || lower.starts_with("//") + } + + /// Check if link text is the bare hostname/path of its destination URL. + /// + /// When the display text is the URL with the scheme stripped (e.g., + /// `[example.github.io](https://example.github.io)`), the text is a domain + /// label, not a prose reference to a product, and should not be corrected. + /// + /// Requires the text to contain a dot, which distinguishes domain-like display + /// text from single-word WikiLink targets (e.g. `[[javascript]]`) where + /// `url == text` but neither is a domain name. Dotted WikiLink targets are + /// excluded separately via the `!is_wikilink` guard in `is_in_link`. Comparison + /// is case-insensitive because URL schemes and hostnames are case-insensitive. + fn link_text_matches_link_url(text: &str, url: &str) -> bool { + let text = text.trim(); + // Only domain-like text (containing a dot) can be a bare hostname. + if !text.contains('.') { + return false; + } + let url_lower = url.to_ascii_lowercase(); + let url_without_scheme = url_lower + .strip_prefix("https://") + .or_else(|| url_lower.strip_prefix("http://")) + .or_else(|| url_lower.strip_prefix("//")) + .unwrap_or(&url_lower); + let text_lower = text.to_ascii_lowercase(); + // Exact match: text equals the URL with the scheme removed. + if url_without_scheme == text_lower.as_str() { + return true; + } + // Prefix match: text is the hostname portion and the URL has a path/query/fragment. + url_without_scheme.len() > text_lower.len() + && url_without_scheme.starts_with(text_lower.as_str()) + && matches!( + url_without_scheme.as_bytes().get(text_lower.len()), + Some(b'/') | Some(b'?') | Some(b'#') + ) } /// Check if a position within a line falls inside an angle-bracket URL (`<scheme://...>`). @@ -1781,7 +1824,9 @@ fn test_non_url_link_text_still_flagged() { let rule = MD044ProperNames::new(vec!["GitHub".to_string()], true); - // Link text that is NOT a URL should still be flagged + // Only prose descriptions in link text should be flagged. + // Bare-domain, protocol-relative, and scheme-prefixed link texts that + // match the destination URL are all URLs and must not be corrected. let content = r#"[github.com/org/repo](https://github.com/org/repo) [Visit github](https://github.com/org/repo) @@ -1793,11 +1838,19 @@ let ctx = create_context(content); let result = rule.check(&ctx).unwrap(); - assert_eq!(result.len(), 4, "Non-URL link text should be flagged, got: {result:?}"); - assert!(result.iter().any(|w| w.line == 1)); // github.com (no protocol) - assert!(result.iter().any(|w| w.line == 3)); // Visit github - assert!(result.iter().any(|w| w.line == 5)); // //github.com (protocol-relative) - assert!(result.iter().any(|w| w.line == 7)); // ftp://github.com + // Line 1: bare-domain text matches destination — not flagged + // Line 3: prose description — flagged + // Line 5: protocol-relative URL text — not flagged + // Line 7: ftp:// URL text matches destination — not flagged + assert_eq!( + result.len(), + 1, + "Only prose link text should be flagged, got: {result:?}" + ); + assert!( + result.iter().any(|w| w.line == 3), + "Expected 'Visit github' on line 3 to be flagged" + ); } #[test] @@ -3108,4 +3161,117 @@ let fixed = rule.fix(&ctx).unwrap(); assert_eq!(fixed, content, "Fix should be a no-op when content is already correct"); } + + // --- Bare-domain link text: display text is the destination URL with scheme stripped --- + + #[test] + fn test_bare_domain_link_text_not_flagged() { + // `[ravencentric.github.io](https://ravencentric.github.io)` — the display text + // is the URL with the scheme stripped; "github" here is a domain label, not a + // reference to "GitHub" the product, and must not be corrected. + let rule = MD044ProperNames::new(vec!["GitHub".to_string()], false); + let content = "My site is [ravencentric.github.io](https://ravencentric.github.io).\n"; + let ctx = create_context(content); + let result = rule.check(&ctx).unwrap(); + assert!( + result.is_empty(), + "Should not flag 'github' in a bare-domain link text that matches the link URL: {result:?}" + ); + } + + #[test] + fn test_bare_domain_link_text_not_fixed() { + // fix() must not rewrite the link text when it is the bare URL hostname. + let rule = MD044ProperNames::new(vec!["GitHub".to_string()], false); + let content = "My site is [ravencentric.github.io](https://ravencentric.github.io).\n"; + let ctx = create_context(content); + let fixed = rule.fix(&ctx).unwrap(); + assert_eq!( + fixed, content, + "fix() must not alter bare-domain link text that matches the destination URL" + ); + } + + #[test] + fn test_bare_domain_link_text_with_path_not_flagged() { + // Display text is the hostname only; destination has a path. + let rule = MD044ProperNames::new(vec!["GitHub".to_string()], false); + let content = "Visit [ravencentric.github.io](https://ravencentric.github.io/projects).\n"; + let ctx = create_context(content); + let result = rule.check(&ctx).unwrap(); + assert!( + result.is_empty(), + "Should not flag 'github' when bare-domain text is the hostname of its destination URL: {result:?}" + ); + } + + #[test] + fn test_bare_domain_link_text_full_path_not_flagged() { + // Display text is the full URL-without-scheme including a path. + let rule = MD044ProperNames::new(vec!["GitHub".to_string()], false); + let content = "See [ravencentric.github.io/blog](https://ravencentric.github.io/blog).\n"; + let ctx = create_context(content); + let result = rule.check(&ctx).unwrap(); + assert!( + result.is_empty(), + "Should not flag 'github' when link text is the full URL path without scheme: {result:?}" + ); + } + + #[test] + fn test_github_product_name_in_link_text_still_flagged() { + // `[github pages](https://pages.github.com)` — the display text is a human + // description, not a bare domain; "github" should still be corrected to "GitHub". + let rule = MD044ProperNames::new(vec!["GitHub".to_string()], false); + let content = "Hosted on [github pages](https://pages.github.com).\n"; + let ctx = create_context(content); + let result = rule.check(&ctx).unwrap(); + assert!( + !result.is_empty(), + "Should still flag 'github' in descriptive link text that does not match the destination URL" + ); + } + + #[test] + fn test_protocol_relative_bare_domain_link_text_not_flagged() { + // Protocol-relative URL `[github.io](//github.io)`. + let rule = MD044ProperNames::new(vec!["GitHub".to_string()], false); + let content = "See [github.io](//github.io).\n"; + let ctx = create_context(content); + let result = rule.check(&ctx).unwrap(); + assert!( + result.is_empty(), + "Should not flag 'github' in bare-domain text matching a protocol-relative destination: {result:?}" + ); + } + + #[test] + fn test_dotted_wikilink_target_still_flagged() { + // `[[node.js]]` is a WikiLink whose page name contains a dot. + // The dot guard alone does not protect it because text == url == "node.js". + // The is_in_link WikiLink guard must prevent bare-domain suppression, + // so the improper capitalization is still caught. + let rule = MD044ProperNames::new(vec!["Node.js".to_string()], false); + let content = "See [[node.js]] for details.\n"; + let ctx = create_context(content); + let result = rule.check(&ctx).unwrap(); + assert!( + !result.is_empty(), + "Should flag 'node.js' in a dotted WikiLink target: {result:?}" + ); + } + + #[test] + fn test_bare_domain_link_text_case_insensitive_url() { + // URL with uppercase scheme `[github.io](HTTPS://github.io)` — the scheme is + // case-insensitive, so the display text should still be recognised as a bare domain. + let rule = MD044ProperNames::new(vec!["GitHub".to_string()], false); + let content = "See [github.io](HTTPS://github.io).\n"; + let ctx = create_context(content); + let result = rule.check(&ctx).unwrap(); + assert!( + result.is_empty(), + "Should not flag bare-domain text when destination URL has an uppercase scheme: {result:?}" + ); + } } ++++++ rumdl.obsinfo ++++++ --- /var/tmp/diff_new_pack.Me9C8z/_old 2026-04-13 23:20:04.651072834 +0200 +++ /var/tmp/diff_new_pack.Me9C8z/_new 2026-04-13 23:20:04.663073330 +0200 @@ -1,5 +1,5 @@ name: rumdl -version: 0.1.69 -mtime: 1775828972 -commit: d81181d8fb04ef01bf9c612812644362533dce1c +version: 0.1.70 +mtime: 1776023520 +commit: 96f0828bd8dd9ca31bb1b5c77f9c0cf305b45767 ++++++ vendor.tar.zst ++++++ /work/SRC/openSUSE:Factory/rumdl/vendor.tar.zst /work/SRC/openSUSE:Factory/.rumdl.new.21863/vendor.tar.zst differ: char 7, line 1
