|
18 | 18 | from typing import cast |
19 | 19 | from urllib.parse import quote, unquote |
20 | 20 |
|
| 21 | +from markdown_it import MarkdownIt |
| 22 | + |
21 | 23 | if sys.version_info >= (3, 11): |
22 | 24 | import tomllib |
23 | 25 | else: |
@@ -194,6 +196,18 @@ def _actions_path_has_canonical_url( |
194 | 196 | return False |
195 | 197 |
|
196 | 198 |
|
| 199 | +def _rendered_markdown_link_targets(text: str) -> list[str]: |
| 200 | + targets: list[str] = [] |
| 201 | + for token in MarkdownIt("commonmark", {"html": True}).parse(text): |
| 202 | + for child in token.children or []: |
| 203 | + if child.type != "link_open": |
| 204 | + continue |
| 205 | + target = child.attrGet("href") |
| 206 | + if isinstance(target, str): |
| 207 | + targets.append(target) |
| 208 | + return targets |
| 209 | + |
| 210 | + |
197 | 211 | _WHEEL_DIGEST = re.compile( |
198 | 212 | r"\bwheel\s+sha256\b[^0-9a-f]{0,96}(?P<digest>[0-9a-f]{64})(?![0-9a-f])", |
199 | 213 | re.IGNORECASE | re.DOTALL, |
@@ -762,6 +776,12 @@ def _canonical_actions_run_violations( |
762 | 776 | ) |
763 | 777 | for variant in variants |
764 | 778 | ] |
| 779 | + malformed = malformed or any( |
| 780 | + _ACTIONS_PATH.search(target) is not None |
| 781 | + and _CANONICAL_ACTIONS_URL.fullmatch(target) is None |
| 782 | + for variant in variants |
| 783 | + for target in _rendered_markdown_link_targets(variant) |
| 784 | + ) |
765 | 785 | malformed = malformed or any( |
766 | 786 | count > direct_paths[identity] |
767 | 787 | for paths in variant_paths[1:] |
|
0 commit comments