Markdown-Checker 1.2.1__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/PKG-INFO +6 -6
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/README.md +3 -4
- markdown_checker-1.3.0/docs/api/index.rst +20 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/explanation/link-detection.rst +10 -7
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/howto/github-actions.rst +3 -32
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/howto/pyproject-config.rst +1 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/howto/skip-links.rst +4 -3
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/index.rst +1 -2
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/reference/checks.rst +14 -9
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/reference/cli.rst +24 -4
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/reference/configuration.rst +4 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/reference/report-formats.rst +1 -1
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/tutorial.rst +1 -1
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/pyproject.toml +1 -1
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/__init__.py +1 -1
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/checks/broken_paths.py +2 -1
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/checks/broken_urls.py +2 -5
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/checks/locale.py +1 -4
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/checks/tracking.py +12 -12
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/cli.py +36 -9
- markdown_checker-1.3.0/src/markdown_checker/models/base.py +84 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/models/config.py +16 -5
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/models/path.py +27 -53
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/models/url.py +14 -0
- markdown_checker-1.3.0/src/markdown_checker/patterns.py +57 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/model.py +8 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/renderers/markdown.py +9 -3
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/checks/test_base.py +1 -1
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/checks/test_broken_paths.py +2 -1
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/checks/test_tracking.py +20 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/models/test_base.py +4 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/models/test_path.py +52 -16
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/models/test_url.py +17 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/conftest.py +1 -1
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/renderers/test_json.py +1 -1
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/renderers/test_markdown.py +31 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/test_model.py +22 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/test_cli.py +12 -1
- markdown_checker-1.3.0/uv.lock +1835 -0
- markdown_checker-1.2.1/src/markdown_checker/models/base.py +0 -59
- markdown_checker-1.2.1/src/markdown_checker/patterns.py +0 -36
- markdown_checker-1.2.1/uv.lock +0 -1561
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/LICENSE +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/CODE_OF_CONDUCT.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/CONTRIBUTING.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/api/checker.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/api/checks.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/api/cli.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/api/main.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/api/models.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/api/patterns.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/api/reports.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/api/utils.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/changes.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/conf.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/explanation/index.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/explanation/url-checking.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/howto/index.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/howto/rate-limits.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/howto/report-formats.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/howto/select-files.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/howto/shell-completions.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/license.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/reference/exit-codes.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/docs/reference/index.rst +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/checker.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/checks/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/checks/base.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/models/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/py.typed +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/base.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/registry.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/renderers/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/renderers/annotations.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/renderers/console.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/renderers/json.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/templates/paths/broken.md +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/templates/paths/tracking.md +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/templates/urls/broken.md +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/templates/urls/locale.md +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/templates/urls/tracking.md +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/reports/writer.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/utils/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/utils/extract_links.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/utils/github_env.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/utils/list_files.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/utils/pyproject_config.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/utils/spinner.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/utils/url_pipeline.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/checks/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/checks/test_broken_urls.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/checks/test_checks.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/checks/test_locale.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/conftest.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/models/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/models/test_config.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/models/test_models.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/renderers/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/renderers/test_annotations.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/renderers/test_console.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/renderers/test_renderers.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/test_base.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/test_registry.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/test_reports.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/reports/test_writer.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/resources/sample1.md +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/resources/sample2.md +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/resources/sample3.md +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/resources/sample4.md +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/smoke/test_cli_smoke.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/test_checker.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/test_markdown_checker.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/test_patterns.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/utils/__init__.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/utils/test_extract_links.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/utils/test_github_env.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/utils/test_list_files.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/utils/test_pyproject_config.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/utils/test_spinner.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/utils/test_url_pipeline.py +0 -0
- {markdown_checker-1.2.1 → markdown_checker-1.3.0}/tests/utils/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: markdown-checker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A markdown link validation reporting tool.
|
|
5
5
|
Author-email: John Aziz <johnaziz269@gmail.com>
|
|
6
6
|
Maintainer-email: John Aziz <johnaziz269@gmail.com>
|
|
@@ -26,6 +26,7 @@ Project-URL: Documentation, https://markdown-checker.readthedocs.io/en/latest/
|
|
|
26
26
|
Project-URL: Homepage, https://github.com/john0isaac/markdown-checker
|
|
27
27
|
Project-URL: Issues, https://github.com/john0isaac/markdown-checker/issues
|
|
28
28
|
Project-URL: Releases, https://github.com/john0isaac/markdown-checker/releases
|
|
29
|
+
Import-Name: markdown_checker
|
|
29
30
|
|
|
30
31
|
[](https://pypi.org/project/markdown-checker/)
|
|
31
32
|
[](https://markdown-checker.readthedocs.io/en/latest/?badge=latest)
|
|
@@ -77,7 +78,7 @@ exits with a non-zero status:
|
|
|
77
78
|
🔍 Checked 42 links in 10 files.
|
|
78
79
|
😭 Found 1 issues in the following files:
|
|
79
80
|
File 'docs/index.md', line 5
|
|
80
|
-
./missing.md
|
|
81
|
+
./missing.md could not be found at /docs/missing.md.
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
## Using `markdown-checker` in GitHub Actions
|
|
@@ -87,7 +88,7 @@ Run this tool within a GitHub workflow using the
|
|
|
87
88
|
GitHub Action, which posts the report as a pull request comment:
|
|
88
89
|
|
|
89
90
|
```yaml
|
|
90
|
-
- uses: john0isaac/action-check-markdown@v1.
|
|
91
|
+
- uses: john0isaac/action-check-markdown@v1.3.0
|
|
91
92
|
with:
|
|
92
93
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
93
94
|
command: check_broken_paths
|
|
@@ -96,8 +97,7 @@ GitHub Action, which posts the report as a pull request comment:
|
|
|
96
97
|
```
|
|
97
98
|
|
|
98
99
|
See the [GitHub Actions how-to](https://markdown-checker.readthedocs.io/en/latest/howto/github-actions/)
|
|
99
|
-
for the full workflow
|
|
100
|
-
supported by the wrapper action) directly as a step.
|
|
100
|
+
for the full workflow.
|
|
101
101
|
|
|
102
102
|
## Documentation
|
|
103
103
|
|
|
@@ -48,7 +48,7 @@ exits with a non-zero status:
|
|
|
48
48
|
🔍 Checked 42 links in 10 files.
|
|
49
49
|
😭 Found 1 issues in the following files:
|
|
50
50
|
File 'docs/index.md', line 5
|
|
51
|
-
./missing.md
|
|
51
|
+
./missing.md could not be found at /docs/missing.md.
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
## Using `markdown-checker` in GitHub Actions
|
|
@@ -58,7 +58,7 @@ Run this tool within a GitHub workflow using the
|
|
|
58
58
|
GitHub Action, which posts the report as a pull request comment:
|
|
59
59
|
|
|
60
60
|
```yaml
|
|
61
|
-
- uses: john0isaac/action-check-markdown@v1.
|
|
61
|
+
- uses: john0isaac/action-check-markdown@v1.3.0
|
|
62
62
|
with:
|
|
63
63
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
64
64
|
command: check_broken_paths
|
|
@@ -67,8 +67,7 @@ GitHub Action, which posts the report as a pull request comment:
|
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
See the [GitHub Actions how-to](https://markdown-checker.readthedocs.io/en/latest/howto/github-actions/)
|
|
70
|
-
for the full workflow
|
|
71
|
-
supported by the wrapper action) directly as a step.
|
|
70
|
+
for the full workflow.
|
|
72
71
|
|
|
73
72
|
## Documentation
|
|
74
73
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
API Reference
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
If you are looking for information on a specific function, class or
|
|
5
|
+
method, this part of the documentation is for you. These pages are
|
|
6
|
+
generated from the docstrings in ``markdown_checker`` and are meant for
|
|
7
|
+
programmatic/library use. For the command-line interface, see
|
|
8
|
+
:doc:`../reference/cli` instead.
|
|
9
|
+
|
|
10
|
+
.. toctree::
|
|
11
|
+
:maxdepth: 1
|
|
12
|
+
|
|
13
|
+
main
|
|
14
|
+
checker
|
|
15
|
+
cli
|
|
16
|
+
models
|
|
17
|
+
checks
|
|
18
|
+
patterns
|
|
19
|
+
reports
|
|
20
|
+
utils
|
|
@@ -38,16 +38,19 @@ Fragments on paths
|
|
|
38
38
|
----------------------
|
|
39
39
|
|
|
40
40
|
A path may carry a trailing ``?query`` and/or ``#anchor`` - for example
|
|
41
|
-
``docs/usage.md#configuration``.
|
|
42
|
-
|
|
43
|
-
part needs to exist on disk; the anchor itself is never
|
|
44
|
-
the target file's headings.
|
|
41
|
+
``docs/usage.md#configuration``. As in any URL, everything from the first
|
|
42
|
+
``?`` or ``#`` onward is stripped before resolving the file, so only the
|
|
43
|
+
``docs/usage.md`` part needs to exist on disk; the anchor itself is never
|
|
44
|
+
validated against the target file's headings. The remaining path is then
|
|
45
|
+
percent-decoded, so ``my%20file.md`` refers to ``my file.md``. A file name
|
|
46
|
+
that really contains ``#`` or ``?`` must be written as ``%23``/``%3F``.
|
|
45
47
|
|
|
46
48
|
Resolving a path to a file
|
|
47
49
|
--------------------------------
|
|
48
50
|
|
|
49
51
|
``check_broken_paths`` tries to resolve a path relative to the markdown
|
|
50
52
|
file it was found in first (e.g. ``../img.png`` in ``docs/usage.md``
|
|
51
|
-
resolves against ``docs/``), then
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
resolves against ``docs/``), then relative to the current working
|
|
54
|
+
directory. Root-relative paths (``/docs/usage.md``) are tried the same way
|
|
55
|
+
with the leading ``/`` dropped. A path is reported as broken only if
|
|
56
|
+
neither resolution finds an existing file.
|
|
@@ -35,48 +35,19 @@ replacing ``guide-url`` with the URL of your own contribution guide:
|
|
|
35
35
|
contents: read
|
|
36
36
|
steps:
|
|
37
37
|
- uses: actions/checkout@v7
|
|
38
|
-
- uses: john0isaac/action-check-markdown@v1.
|
|
38
|
+
- uses: john0isaac/action-check-markdown@v1.3.0
|
|
39
39
|
with:
|
|
40
40
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
41
41
|
command: check_broken_paths
|
|
42
42
|
directory: ./
|
|
43
43
|
guide-url: "https://github.com/<owner>/<repo>/blob/main/CONTRIBUTING.md"
|
|
44
44
|
|
|
45
|
-
The ``command`` input selects which check to run
|
|
45
|
+
The ``command`` input selects which check to run it
|
|
46
46
|
supports ``check_broken_paths``, ``check_paths_tracking``,
|
|
47
|
-
``check_urls_tracking``, and ``check_urls_locale``
|
|
47
|
+
``check_urls_tracking``, and ``check_urls_locale``
|
|
48
48
|
``check_broken_urls``. Add a separate job (or step) per command if you want
|
|
49
49
|
to run more than one check in the same workflow.
|
|
50
50
|
|
|
51
|
-
Run ``check_broken_urls`` directly (no wrapper action)
|
|
52
|
-
------------------------------------------------------------
|
|
53
|
-
|
|
54
|
-
Since the wrapper action doesn't support URL checking yet, install and
|
|
55
|
-
invoke markdown-checker directly as a workflow step instead. Use
|
|
56
|
-
``--report-format github-annotations`` so failures show up as inline
|
|
57
|
-
annotations on the PR diff, the same way the action's output does:
|
|
58
|
-
|
|
59
|
-
.. code-block:: yaml
|
|
60
|
-
|
|
61
|
-
jobs:
|
|
62
|
-
check-broken-urls:
|
|
63
|
-
runs-on: ubuntu-latest
|
|
64
|
-
permissions:
|
|
65
|
-
contents: read
|
|
66
|
-
steps:
|
|
67
|
-
- uses: actions/checkout@v7
|
|
68
|
-
- uses: actions/setup-python@v6
|
|
69
|
-
with:
|
|
70
|
-
python-version: "3.11"
|
|
71
|
-
- run: pip install markdown-checker
|
|
72
|
-
- run: >
|
|
73
|
-
markdown-checker . -f check_broken_urls
|
|
74
|
-
-rf github-annotations -o -
|
|
75
|
-
|
|
76
|
-
Because ``$CI=true`` is set automatically in GitHub Actions, warnings
|
|
77
|
-
(rate-limited/unverifiable links) are emitted as ``::warning`` annotations
|
|
78
|
-
rather than failing the job; only broken links (an error-level issue) fail it.
|
|
79
|
-
|
|
80
51
|
Verify it worked
|
|
81
52
|
------------------
|
|
82
53
|
|
|
@@ -18,6 +18,7 @@ Add a ``[tool.markdown-checker]`` table to your project's
|
|
|
18
18
|
extensions = [".md", ".ipynb"]
|
|
19
19
|
skip-files = ["CODE_OF_CONDUCT.md", "SECURITY.md"]
|
|
20
20
|
tracking-domains = ["github.com", "microsoft.com", "visualstudio.com", "aka.ms", "azure.com"]
|
|
21
|
+
tracking-param = ["wt.mc_id"]
|
|
21
22
|
guide-url = "https://github.com/john0isaac/markdown-checker/blob/main/CONTRIBUTING.md"
|
|
22
23
|
max-workers = 10
|
|
23
24
|
report-format = "markdown"
|
|
@@ -17,8 +17,9 @@ check automatically (e.g. it blocks bots) but you still want to keep the link:
|
|
|
17
17
|
|
|
18
18
|
markdown-checker . -f check_broken_urls --skip-domains=example.com,test.com
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
``--skip-domains=example.com`` also matches ``www.example.com
|
|
20
|
+
A domain matches the URL's hostname exactly or as a parent domain, so
|
|
21
|
+
``--skip-domains=example.com`` also matches ``www.example.com`` (but not
|
|
22
|
+
``notexample.com``).
|
|
22
23
|
|
|
23
24
|
Skip URLs containing a substring
|
|
24
25
|
------------------------------------
|
|
@@ -35,7 +36,7 @@ List syntax
|
|
|
35
36
|
-----------
|
|
36
37
|
|
|
37
38
|
Every list option (``--skip-domains``, ``--skip-urls-containing``,
|
|
38
|
-
``--extensions``, ``--skip-files``, ``--tracking-domains``) takes a single
|
|
39
|
+
``--extensions``, ``--skip-files``, ``--tracking-domains``, ``--tracking-param``) takes a single
|
|
39
40
|
comma-separated value:
|
|
40
41
|
|
|
41
42
|
- Correct: ``--skip-domains=example.com,test.com``
|
|
@@ -26,7 +26,7 @@ Sample finding
|
|
|
26
26
|
.. code-block:: text
|
|
27
27
|
|
|
28
28
|
File 'docs/index.md', line 5
|
|
29
|
-
./missing.md
|
|
29
|
+
./missing.md could not be found at /docs/missing.md.
|
|
30
30
|
|
|
31
31
|
``check_broken_urls``
|
|
32
32
|
------------------------
|
|
@@ -55,8 +55,10 @@ Sample finding
|
|
|
55
55
|
|
|
56
56
|
Flags
|
|
57
57
|
URLs whose path contains a locale segment, e.g. ``/en-us/`` in
|
|
58
|
-
``https://example.com/en-us/docs``. A "locale
|
|
59
|
-
language-country pair
|
|
58
|
+
``https://example.com/en-us/docs`` or a trailing ``/en-us``. A "locale
|
|
59
|
+
segment" is a known language-country pair of the form ``xx-xx``
|
|
60
|
+
(case-insensitive), so look-alikes such as ``/ci-cd/`` aren't flagged.
|
|
61
|
+
The host, query string and anchor are not searched.
|
|
60
62
|
Ignores
|
|
61
63
|
``www.nvidia.com`` (built in - its docs always include a locale
|
|
62
64
|
segment), plus any host in ``--skip-domains`` or URL in
|
|
@@ -73,8 +75,8 @@ Sample finding
|
|
|
73
75
|
--------------------------
|
|
74
76
|
|
|
75
77
|
Flags
|
|
76
|
-
URLs on a configured tracking domain that are missing
|
|
77
|
-
query parameter.
|
|
78
|
+
URLs on a configured tracking domain that are missing every
|
|
79
|
+
``--tracking-param`` query parameter (``wt.mc_id`` by default).
|
|
78
80
|
Ignores
|
|
79
81
|
URLs on hosts *not* listed in ``--tracking-domains`` (the check does
|
|
80
82
|
nothing for them), plus any host in ``--skip-domains`` or URL in
|
|
@@ -83,7 +85,8 @@ Default tracking domains
|
|
|
83
85
|
``github.com``, ``microsoft.com``, ``visualstudio.com``, ``aka.ms``,
|
|
84
86
|
``azure.com``.
|
|
85
87
|
Options that affect it
|
|
86
|
-
``--tracking-domains``, ``--
|
|
88
|
+
``--tracking-domains``, ``--tracking-param``, ``--skip-domains``,
|
|
89
|
+
``--skip-urls-containing``.
|
|
87
90
|
Sample finding
|
|
88
91
|
.. code-block:: text
|
|
89
92
|
|
|
@@ -94,10 +97,12 @@ Sample finding
|
|
|
94
97
|
---------------------------
|
|
95
98
|
|
|
96
99
|
Flags
|
|
97
|
-
Every relative path link missing
|
|
98
|
-
``
|
|
100
|
+
Every relative path link missing every ``--tracking-param`` query
|
|
101
|
+
parameter (``wt.mc_id`` by default). Unlike ``check_urls_tracking``,
|
|
102
|
+
there is no domain filter - all paths are checked.
|
|
99
103
|
Options that affect it
|
|
100
|
-
``SRC``/``--dir``, ``--extensions``, ``--skip-files
|
|
104
|
+
``SRC``/``--dir``, ``--extensions``, ``--skip-files``,
|
|
105
|
+
``--tracking-param``.
|
|
101
106
|
Sample finding
|
|
102
107
|
.. code-block:: text
|
|
103
108
|
|
|
@@ -78,14 +78,33 @@ Type
|
|
|
78
78
|
Default
|
|
79
79
|
``github.com,microsoft.com,visualstudio.com,aka.ms,azure.com``.
|
|
80
80
|
Constraints
|
|
81
|
-
Only affects ``check_urls_tracking``.
|
|
81
|
+
Only affects ``check_urls_tracking``. Subdomains are included, e.g.
|
|
82
|
+
``microsoft.com`` also matches ``learn.microsoft.com``.
|
|
82
83
|
Description
|
|
83
|
-
Hostnames that must carry a
|
|
84
|
+
Hostnames that must carry a tracking parameter (see ``--tracking-param``).
|
|
84
85
|
|
|
85
86
|
.. code-block:: bash
|
|
86
87
|
|
|
87
88
|
markdown-checker . -f check_urls_tracking --tracking-domains=github.com,microsoft.com
|
|
88
89
|
|
|
90
|
+
``-tp``, ``--tracking-param``
|
|
91
|
+
---------------------------------
|
|
92
|
+
|
|
93
|
+
Type
|
|
94
|
+
``list[str]``, comma-separated.
|
|
95
|
+
Default
|
|
96
|
+
``wt.mc_id``.
|
|
97
|
+
Constraints
|
|
98
|
+
Only affects ``check_urls_tracking`` and ``check_paths_tracking``.
|
|
99
|
+
Matched case-insensitively as a query parameter (after ``?`` or ``&``).
|
|
100
|
+
Description
|
|
101
|
+
Tracking query parameters a link must carry. A link passes if it has any
|
|
102
|
+
one of them.
|
|
103
|
+
|
|
104
|
+
.. code-block:: bash
|
|
105
|
+
|
|
106
|
+
markdown-checker . -f check_urls_tracking --tracking-param=wt.mc_id,utm_source
|
|
107
|
+
|
|
89
108
|
``-sf``, ``--skip-files``
|
|
90
109
|
-----------------------------
|
|
91
110
|
|
|
@@ -110,8 +129,9 @@ Type
|
|
|
110
129
|
Default
|
|
111
130
|
``[]`` (none).
|
|
112
131
|
Constraints
|
|
113
|
-
Only affects ``check_broken_urls`` and ``check_urls_locale``.
|
|
114
|
-
|
|
132
|
+
Only affects ``check_broken_urls`` and ``check_urls_locale``. Matches the
|
|
133
|
+
URL's hostname exactly or as a parent domain (``example.com`` matches
|
|
134
|
+
``www.example.com`` but not ``notexample.com``).
|
|
115
135
|
Description
|
|
116
136
|
Domains to exclude from URL checks.
|
|
117
137
|
|
|
@@ -47,6 +47,9 @@ Keys
|
|
|
47
47
|
* - ``tracking-domains``
|
|
48
48
|
- ``--tracking-domains``
|
|
49
49
|
- array of strings
|
|
50
|
+
* - ``tracking-param``
|
|
51
|
+
- ``--tracking-param``
|
|
52
|
+
- array of strings
|
|
50
53
|
* - ``skip-files``
|
|
51
54
|
- ``--skip-files``
|
|
52
55
|
- array of strings
|
|
@@ -96,6 +99,7 @@ Example
|
|
|
96
99
|
skip-files = ["CODE_OF_CONDUCT.md", "SECURITY.md"]
|
|
97
100
|
skip-domains = []
|
|
98
101
|
tracking-domains = ["github.com", "microsoft.com", "visualstudio.com", "aka.ms", "azure.com"]
|
|
102
|
+
tracking-param = ["wt.mc_id"]
|
|
99
103
|
guide-url = "https://github.com/john0isaac/markdown-checker/blob/main/CONTRIBUTING.md"
|
|
100
104
|
timeout = 20
|
|
101
105
|
retries = 3
|
|
@@ -46,7 +46,7 @@ A structured document including both errors and warnings:
|
|
|
46
46
|
.. code-block:: json
|
|
47
47
|
|
|
48
48
|
{
|
|
49
|
-
"tool": {"name": "markdown-checker", "version": "1.
|
|
49
|
+
"tool": {"name": "markdown-checker", "version": "1.3.0"},
|
|
50
50
|
"check": "check_broken_urls",
|
|
51
51
|
"summary": {
|
|
52
52
|
"files_checked": 10,
|
|
@@ -63,7 +63,7 @@ You'll see output similar to:
|
|
|
63
63
|
🔍 Checked 2 links in 2 files.
|
|
64
64
|
😭 Found 1 issues in the following files:
|
|
65
65
|
File 'docs-sample/index.md', line 5
|
|
66
|
-
./missing.md
|
|
66
|
+
./missing.md could not be found at /docs/missing.md.
|
|
67
67
|
|
|
68
68
|
Because an error-level issue was found, markdown-checker also writes a
|
|
69
69
|
``comment.md`` file in the current directory with the same information as a
|
{markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/checks/broken_paths.py
RENAMED
|
@@ -28,6 +28,7 @@ class BrokenPathsCheck(BaseCheck[MarkdownPath]):
|
|
|
28
28
|
detected_issues: list[MarkdownPath] = []
|
|
29
29
|
for path in links.paths:
|
|
30
30
|
if not path.exists():
|
|
31
|
-
path.
|
|
31
|
+
path.resolved_path = path.get_full_path()
|
|
32
|
+
path.issue = f"could not be found at {path.resolved_path.as_posix()}"
|
|
32
33
|
detected_issues.append(path)
|
|
33
34
|
return detected_issues
|
{markdown_checker-1.2.1 → markdown_checker-1.3.0}/src/markdown_checker/checks/broken_urls.py
RENAMED
|
@@ -58,7 +58,7 @@ class BrokenURLsCheck(BaseCheck[MarkdownURL]):
|
|
|
58
58
|
:meth:`collect` to resolve later.
|
|
59
59
|
"""
|
|
60
60
|
config = config or Config()
|
|
61
|
-
effective_skip = [
|
|
61
|
+
effective_skip = [*config.skip_domains, *_BUILTIN_SKIP_DOMAINS]
|
|
62
62
|
skip_urls_containing = config.skip_urls_containing
|
|
63
63
|
|
|
64
64
|
# Standalone fallback (direct calls, tests): own a private service for this run only.
|
|
@@ -67,10 +67,7 @@ class BrokenURLsCheck(BaseCheck[MarkdownURL]):
|
|
|
67
67
|
|
|
68
68
|
pending: list[tuple[MarkdownURL, Future[URLCheckResult]]] = []
|
|
69
69
|
for url in links.urls:
|
|
70
|
-
|
|
71
|
-
if any(domain in hostname for domain in effective_skip) or any(
|
|
72
|
-
substring in url.link for substring in skip_urls_containing
|
|
73
|
-
):
|
|
70
|
+
if url.matches_domain(effective_skip) or any(substring in url.link for substring in skip_urls_containing):
|
|
74
71
|
continue
|
|
75
72
|
pending.append((url, _service.submit(url)))
|
|
76
73
|
return (pending, _service if owns_service else None)
|
|
@@ -40,10 +40,7 @@ class URLsLocaleCheck(BaseCheck[MarkdownURL]):
|
|
|
40
40
|
|
|
41
41
|
detected_issues: list[MarkdownURL] = []
|
|
42
42
|
for url in links.urls:
|
|
43
|
-
|
|
44
|
-
if any(domain.lower() in hostname for domain in effective_skip) or any(
|
|
45
|
-
substring in url.link for substring in skip_urls_containing
|
|
46
|
-
):
|
|
43
|
+
if url.matches_domain(effective_skip) or any(substring in url.link for substring in skip_urls_containing):
|
|
47
44
|
continue
|
|
48
45
|
if url.has_locale():
|
|
49
46
|
url.issue = "has locale"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""The ``check_urls_tracking``/``check_paths_tracking`` checks: flag links missing a
|
|
1
|
+
"""The ``check_urls_tracking``/``check_paths_tracking`` checks: flag links missing a tracking ID."""
|
|
2
2
|
|
|
3
3
|
from markdown_checker.checks.base import BaseCheck
|
|
4
4
|
from markdown_checker.models import Config
|
|
@@ -21,8 +21,9 @@ class URLsTrackingCheck(BaseCheck[MarkdownURL]):
|
|
|
21
21
|
service: URLCheckService | None = None,
|
|
22
22
|
) -> list[MarkdownURL]:
|
|
23
23
|
"""Flag every URL in ``links.urls`` whose host matches
|
|
24
|
-
``config.tracking_domains`` but is missing
|
|
25
|
-
|
|
24
|
+
``config.tracking_domains`` but is missing all of the
|
|
25
|
+
``config.tracking_param`` query parameters (see
|
|
26
|
+
:meth:`MarkdownLinkBase.has_tracking
|
|
26
27
|
<markdown_checker.models.base.MarkdownLinkBase.has_tracking>`). URLs
|
|
27
28
|
on other hosts, or matching ``config.skip_domains``/
|
|
28
29
|
``skip_urls_containing``, are ignored. Sets
|
|
@@ -36,12 +37,9 @@ class URLsTrackingCheck(BaseCheck[MarkdownURL]):
|
|
|
36
37
|
|
|
37
38
|
detected_issues: list[MarkdownURL] = []
|
|
38
39
|
for url in links.urls:
|
|
39
|
-
|
|
40
|
-
if any(domain.lower() in hostname for domain in skip_domains) or any(
|
|
41
|
-
substring in url.link for substring in skip_urls_containing
|
|
42
|
-
):
|
|
40
|
+
if url.matches_domain(skip_domains) or any(substring in url.link for substring in skip_urls_containing):
|
|
43
41
|
continue
|
|
44
|
-
if
|
|
42
|
+
if url.matches_domain(tracking_domains) and not url.has_tracking(config.tracking_param):
|
|
45
43
|
url.issue = "is missing tracking id"
|
|
46
44
|
detected_issues.append(url)
|
|
47
45
|
return detected_issues
|
|
@@ -59,16 +57,18 @@ class PathsTrackingCheck(BaseCheck[MarkdownPath]):
|
|
|
59
57
|
config: Config | None = None,
|
|
60
58
|
service: URLCheckService | None = None,
|
|
61
59
|
) -> list[MarkdownPath]:
|
|
62
|
-
"""Flag every path in ``links.paths`` missing
|
|
63
|
-
|
|
60
|
+
"""Flag every path in ``links.paths`` missing all of the
|
|
61
|
+
``config.tracking_param`` query parameters (see
|
|
62
|
+
:meth:`MarkdownLinkBase.has_tracking
|
|
64
63
|
<markdown_checker.models.base.MarkdownLinkBase.has_tracking>`),
|
|
65
64
|
setting ``issue="is missing tracking id"`` (error-level) on each.
|
|
66
65
|
Unlike ``check_urls_tracking``, there is no domain filter: every
|
|
67
|
-
path is checked. ``
|
|
66
|
+
path is checked. ``service`` is unused.
|
|
68
67
|
"""
|
|
68
|
+
config = config or Config()
|
|
69
69
|
detected_issues: list[MarkdownPath] = []
|
|
70
70
|
for path in links.paths:
|
|
71
|
-
if not path.has_tracking():
|
|
71
|
+
if not path.has_tracking(config.tracking_param):
|
|
72
72
|
path.issue = "is missing tracking id"
|
|
73
73
|
detected_issues.append(path)
|
|
74
74
|
return detected_issues
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import platform
|
|
5
|
+
import sys
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
@@ -10,6 +11,7 @@ import click
|
|
|
10
11
|
from markdown_checker import __version__
|
|
11
12
|
from markdown_checker.checker import run_check_on_files
|
|
12
13
|
from markdown_checker.models.config import Config
|
|
14
|
+
from markdown_checker.patterns import DEFAULT_TRACKING_PARAMS
|
|
13
15
|
from markdown_checker.reports import build_report
|
|
14
16
|
from markdown_checker.reports import get_renderer
|
|
15
17
|
from markdown_checker.reports import RENDERERS
|
|
@@ -38,6 +40,23 @@ class PyprojectCommand(click.Command):
|
|
|
38
40
|
return super().make_context(info_name, args, parent, **extra)
|
|
39
41
|
|
|
40
42
|
|
|
43
|
+
def _echo_safe(message: str, *, fg: str | None = None, err: bool = False) -> None:
|
|
44
|
+
"""Echo a (optionally styled) message, dropping characters the output stream can't encode.
|
|
45
|
+
|
|
46
|
+
On Windows, piping stdout/stderr to another process (e.g. ``| grep``) can make Python
|
|
47
|
+
fall back to a legacy code page (e.g. cp1252) that can't represent emoji. Retry without
|
|
48
|
+
those characters instead of crashing the whole command.
|
|
49
|
+
"""
|
|
50
|
+
styled = click.style(message, fg=fg) if fg else message
|
|
51
|
+
try:
|
|
52
|
+
click.echo(styled, err=err)
|
|
53
|
+
except UnicodeEncodeError:
|
|
54
|
+
stream = sys.stderr if err else sys.stdout
|
|
55
|
+
encoding = getattr(stream, "encoding", None) or "ascii"
|
|
56
|
+
safe_message = styled.encode(encoding, errors="ignore").decode(encoding)
|
|
57
|
+
click.echo(safe_message, err=err)
|
|
58
|
+
|
|
59
|
+
|
|
41
60
|
class ListOfStrings(click.Option):
|
|
42
61
|
"""
|
|
43
62
|
Helper class to parse a comma-separated list of strings from the command line.
|
|
@@ -105,6 +124,15 @@ class ListOfStrings(click.Option):
|
|
|
105
124
|
help="List of tracking domains to check.",
|
|
106
125
|
required=False,
|
|
107
126
|
)
|
|
127
|
+
@click.option(
|
|
128
|
+
"-tp",
|
|
129
|
+
"--tracking-param",
|
|
130
|
+
cls=ListOfStrings,
|
|
131
|
+
type=list[str],
|
|
132
|
+
default=list(DEFAULT_TRACKING_PARAMS),
|
|
133
|
+
help="List of tracking query parameters; a link passes if it has any of them.",
|
|
134
|
+
required=False,
|
|
135
|
+
)
|
|
108
136
|
@click.option(
|
|
109
137
|
"-sf",
|
|
110
138
|
"--skip-files",
|
|
@@ -240,6 +268,7 @@ def main(
|
|
|
240
268
|
skip_domains: list[str],
|
|
241
269
|
skip_urls_containing: list[str],
|
|
242
270
|
tracking_domains: list[str],
|
|
271
|
+
tracking_param: list[str],
|
|
243
272
|
timeout: int,
|
|
244
273
|
retries: int,
|
|
245
274
|
retry_on_429: bool,
|
|
@@ -291,6 +320,7 @@ def main(
|
|
|
291
320
|
skip_domains=skip_domains,
|
|
292
321
|
skip_urls_containing=skip_urls_containing,
|
|
293
322
|
tracking_domains=tracking_domains,
|
|
323
|
+
tracking_param=tracking_param,
|
|
294
324
|
timeout=timeout,
|
|
295
325
|
retries=retries,
|
|
296
326
|
retry_on_429=retry_on_429,
|
|
@@ -309,9 +339,9 @@ def main(
|
|
|
309
339
|
progress_callback=lambda: bar.update(1),
|
|
310
340
|
)
|
|
311
341
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
342
|
+
_echo_safe(
|
|
343
|
+
f"\n🔍 Checked {check_result.links_checked} links in {len(files_paths)} files.",
|
|
344
|
+
fg="blue",
|
|
315
345
|
)
|
|
316
346
|
|
|
317
347
|
if check_result.issues:
|
|
@@ -328,13 +358,10 @@ def main(
|
|
|
328
358
|
renderer = get_renderer(report_format)
|
|
329
359
|
out_path = None if output_file_name == "-" else Path(f"{output_file_name}{renderer.file_extension}")
|
|
330
360
|
write_report(renderer.render(report), output_path=out_path)
|
|
331
|
-
|
|
361
|
+
_echo_safe(f"😭 Found {report.error_count} issues in the following files:", fg="red", err=True)
|
|
332
362
|
|
|
333
363
|
if report.has_warnings:
|
|
334
|
-
|
|
335
|
-
click.style(f"⚠ {report.warning_count} links had warnings:", fg="yellow"),
|
|
336
|
-
err=True,
|
|
337
|
-
)
|
|
364
|
+
_echo_safe(f"⚠ {report.warning_count} links had warnings:", fg="yellow", err=True)
|
|
338
365
|
|
|
339
366
|
terminal_renderer = GitHubAnnotationsRenderer() if config.output_mode == "ci" else ConsoleRenderer()
|
|
340
367
|
for message, level in terminal_renderer.render_lines(report):
|
|
@@ -342,5 +369,5 @@ def main(
|
|
|
342
369
|
|
|
343
370
|
ctx.exit(1 if report.has_errors else 0)
|
|
344
371
|
|
|
345
|
-
|
|
372
|
+
_echo_safe("All files are compliant with the guidelines. 🎉", fg="green")
|
|
346
373
|
ctx.exit(0)
|