fix(governance): retain malformed link follow-ups

This commit is contained in:
Seekers2001
2026-09-05 17:40:33 +08:00
parent 162bff9438
commit 9b21b2ccd5
3 changed files with 42 additions and 18 deletions
@@ -9,6 +9,7 @@ INLINE_LINK_START_RE = re.compile(r"\[[^\]\n]*\]\(")
REFERENCE_DEFINITION_RE = re.compile(
r"""(?mx)
^\ {0,3}\[(?P<label>[^\]\n]+)\]:[\t\ ]*
(?:\r?\n[\t\ ]+)?
(?P<target><[^>\n]+>|[^\t\ \n]+)
(?:[\t\ ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\([^()]*\)))?
[\t\ ]*$
@@ -75,19 +76,21 @@ def without_inline_code(text: str) -> str:
def inline_link_targets(text: str) -> list[str]:
targets: list[str] = []
for match in INLINE_LINK_START_RE.finditer(text):
matches = list(INLINE_LINK_START_RE.finditer(text))
for index, match in enumerate(matches):
start = match.end()
next_start = (
matches[index + 1].start() if index + 1 < len(matches) else len(text)
)
if start < len(text) and text[start] == "<":
end = text.find(">", start + 1)
end = text.find(">", start + 1, next_start)
if end != -1:
targets.append(text[start : end + 1])
continue
break
continue
depth = 0
quote: str | None = None
escaped = False
closed = False
for end in range(start, len(text)):
for end in range(start, next_start):
character = text[end]
if quote is not None:
if escaped:
@@ -105,11 +108,8 @@ def inline_link_targets(text: str) -> list[str]:
elif character == ")":
if depth == 0:
targets.append(text[start:end])
closed = True
break
depth -= 1
if not closed:
break
return targets
@@ -9,6 +9,7 @@ INLINE_LINK_START_RE = re.compile(r"\[[^\]\n]*\]\(")
REFERENCE_DEFINITION_RE = re.compile(
r"""(?mx)
^\ {0,3}\[(?P<label>[^\]\n]+)\]:[\t\ ]*
(?:\r?\n[\t\ ]+)?
(?P<target><[^>\n]+>|[^\t\ \n]+)
(?:[\t\ ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\([^()]*\)))?
[\t\ ]*$
@@ -75,19 +76,21 @@ def without_inline_code(text: str) -> str:
def inline_link_targets(text: str) -> list[str]:
targets: list[str] = []
for match in INLINE_LINK_START_RE.finditer(text):
matches = list(INLINE_LINK_START_RE.finditer(text))
for index, match in enumerate(matches):
start = match.end()
next_start = (
matches[index + 1].start() if index + 1 < len(matches) else len(text)
)
if start < len(text) and text[start] == "<":
end = text.find(">", start + 1)
end = text.find(">", start + 1, next_start)
if end != -1:
targets.append(text[start : end + 1])
continue
break
continue
depth = 0
quote: str | None = None
escaped = False
closed = False
for end in range(start, len(text)):
for end in range(start, next_start):
character = text[end]
if quote is not None:
if escaped:
@@ -105,11 +108,8 @@ def inline_link_targets(text: str) -> list[str]:
elif character == ")":
if depth == 0:
targets.append(text[start:end])
closed = True
break
depth -= 1
if not closed:
break
return targets
+24
View File
@@ -220,6 +220,19 @@ def test_artifact_scope_handles_many_unclosed_inline_links_linearly(
assert result.returncode == 0, result.stdout
@pytest.mark.parametrize("prefix", ("<unterminated ", "unterminated "))
def test_artifact_scope_checks_link_after_unclosed_destination(
project: Path, prefix: str
) -> None:
(project / "index.md").write_text(
f"[broken]({prefix}[missing](missing-guide.md)\n",
encoding="utf-8",
)
result = run_audit(project, "artifacts")
assert result.returncode == 1
assert "Broken Markdown link: 'index.md' -> 'missing-guide.md'" in result.stdout
def test_artifact_scope_ignores_links_inside_code_examples(project: Path) -> None:
(project / "guide.md").write_text(
"`[inline](missing-inline.md)`\n\n```md\n[fenced](missing-fenced.md)\n```\n",
@@ -276,6 +289,17 @@ def test_artifact_scope_checks_reference_style_markdown_links(
assert "missing-guide.md" in result.stdout
def test_artifact_scope_checks_continuation_line_reference_target(
project: Path,
) -> None:
(project / "index.md").write_text(
"[docs]\n\n[docs]:\n missing-guide.md\n", encoding="utf-8"
)
result = run_audit(project, "artifacts")
assert result.returncode == 1
assert "Broken Markdown link: 'index.md' -> 'missing-guide.md'" in result.stdout
def test_adr_scope_requires_every_file_in_index(project: Path) -> None:
adr_dir = project / "docs" / "adr"
adr_dir.mkdir(parents=True)