diff --git a/.github/actions/check-quality/action.yml b/.github/actions/check-quality/action.yml index 9f735e8eb..623631a62 100644 --- a/.github/actions/check-quality/action.yml +++ b/.github/actions/check-quality/action.yml @@ -31,6 +31,8 @@ runs: set -euo pipefail python -m ruff check scripts .github/specula python -m compileall -q .github/specula + python scripts/check_markdown_links.py + python -m unittest discover -s scripts -p "test_check_markdown_links.py" ${SHELLCHECK} --shell=sh \ guest/common/init guest/alpine/nvx-container-enter \ guest/alpine/nvx-container-launch \ diff --git a/aci_edge_sandboxes/README.md b/aci_edge_sandboxes/README.md index 616aaadc6..121ed7b47 100644 --- a/aci_edge_sandboxes/README.md +++ b/aci_edge_sandboxes/README.md @@ -1030,7 +1030,7 @@ An MXC `StatefulSandboxBackend` adapter maps onto this crate as follows: | `policy.network_egress` rules | `EgressPolicy` rules, field for field | | `working_directory` | `ExecRequest::with_cwd(nvx::guest_path(...))`; a directory that the workload cannot enter ends with `Failed(WorkingDirectory)` | | `process.env`, `process.inheritDefaultEnv` | `ProcessSpec::env`, `None` when omitted, and `inherit_default_env`, or `ExecRequest::with_envs` and `with_inherit_default_env`; see [Environment](#environment) | -| `runtimeConfig.networkProxy` | `ProvisionRequest::with_network_proxy` with the `agent` backend; see [Native proxy and forwarded ports](#native-proxy-and-forwarded-ports) | +| `runtimeConfig.networkProxy` | `ProvisionRequest::with_network_proxy` with the `agent` backend; see [Native proxy and forwarded ports](#native-proxy-and-host-loopback) | A proof-of-concept MXC adapter, `nvx_backend`, implements this mapping. It runs each phase in its own process against a real VM and consumes exec pipes diff --git a/scripts/check_markdown_links.py b/scripts/check_markdown_links.py new file mode 100644 index 000000000..54107ea2d --- /dev/null +++ b/scripts/check_markdown_links.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python3 +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +"""Check relative links, images, and anchors in tracked Markdown files. + +Every relative link or image target must exist in the working tree, and every +anchor must match a heading slug in the target file, using GitHub's rules. +Inline links, reference-style definitions, and HTML ``href`` and ``src`` +attributes are checked. Fenced code blocks and code spans are ignored. Files +under ``openvmm/`` are vendored and skipped. +""" + +from __future__ import annotations + +import html +import re +import subprocess +import sys +from pathlib import Path +from urllib.parse import unquote + +REPO_ROOT = Path(__file__).resolve().parent.parent +SKIPPED_PREFIXES = ("openvmm/",) + +FENCE_OPEN = re.compile(r"^[ \t]*(?P`{3,}|~{3,})(?P.*)$") +FENCE_CLOSE = re.compile(r"^[ \t]*(?P`{3,}|~{3,})[ \t]*$") +HEADING = re.compile(r"^ {0,3}#{1,6}[ \t]+(?P.+?)[ \t]*#*[ \t]*$") +SETEXT_UNDERLINE = re.compile(r"^ {0,3}(?:=+|-+)[ \t]*$") +# A code span opens and closes with the same run length and may span lines. +CODE_SPAN = re.compile( + r"(?`+)(?!`)(?P(?:(?!\n[ \t]*\n).)*?)(?(?:[^\[\]]|\[[^\]]*\])*)\]" + r"\([ \t\n]*[^)\s>]+)>?(?:[ \t\n]+\"[^\"]*\")?[ \t\n]*\)" +) +REFERENCE_DEFINITION = re.compile( + r"^ {0,3}\[(?P