rolfedh-doc-utils 0.1.4__py3-none-any.whl → 0.1.41__py3-none-any.whl

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.
Files changed (52) hide show
  1. archive_unused_files.py +18 -5
  2. archive_unused_images.py +9 -2
  3. callout_lib/__init__.py +22 -0
  4. callout_lib/converter_bullets.py +103 -0
  5. callout_lib/converter_comments.py +295 -0
  6. callout_lib/converter_deflist.py +134 -0
  7. callout_lib/detector.py +364 -0
  8. callout_lib/table_parser.py +804 -0
  9. check_published_links.py +1083 -0
  10. check_scannability.py +6 -0
  11. check_source_directives.py +101 -0
  12. convert_callouts_interactive.py +567 -0
  13. convert_callouts_to_deflist.py +628 -0
  14. convert_freemarker_to_asciidoc.py +288 -0
  15. convert_tables_to_deflists.py +479 -0
  16. doc_utils/convert_freemarker_to_asciidoc.py +708 -0
  17. doc_utils/duplicate_content.py +409 -0
  18. doc_utils/duplicate_includes.py +347 -0
  19. doc_utils/extract_link_attributes.py +618 -0
  20. doc_utils/format_asciidoc_spacing.py +285 -0
  21. doc_utils/insert_abstract_role.py +220 -0
  22. doc_utils/inventory_conditionals.py +164 -0
  23. doc_utils/missing_source_directive.py +211 -0
  24. doc_utils/replace_link_attributes.py +187 -0
  25. doc_utils/spinner.py +119 -0
  26. doc_utils/unused_adoc.py +150 -22
  27. doc_utils/unused_attributes.py +218 -6
  28. doc_utils/unused_images.py +81 -9
  29. doc_utils/validate_links.py +576 -0
  30. doc_utils/version.py +8 -0
  31. doc_utils/version_check.py +243 -0
  32. doc_utils/warnings_report.py +237 -0
  33. doc_utils_cli.py +158 -0
  34. extract_link_attributes.py +120 -0
  35. find_duplicate_content.py +209 -0
  36. find_duplicate_includes.py +198 -0
  37. find_unused_attributes.py +84 -6
  38. format_asciidoc_spacing.py +134 -0
  39. insert_abstract_role.py +163 -0
  40. inventory_conditionals.py +53 -0
  41. replace_link_attributes.py +214 -0
  42. rolfedh_doc_utils-0.1.41.dist-info/METADATA +246 -0
  43. rolfedh_doc_utils-0.1.41.dist-info/RECORD +52 -0
  44. {rolfedh_doc_utils-0.1.4.dist-info → rolfedh_doc_utils-0.1.41.dist-info}/WHEEL +1 -1
  45. rolfedh_doc_utils-0.1.41.dist-info/entry_points.txt +20 -0
  46. rolfedh_doc_utils-0.1.41.dist-info/top_level.txt +21 -0
  47. validate_links.py +213 -0
  48. rolfedh_doc_utils-0.1.4.dist-info/METADATA +0 -285
  49. rolfedh_doc_utils-0.1.4.dist-info/RECORD +0 -17
  50. rolfedh_doc_utils-0.1.4.dist-info/entry_points.txt +0 -5
  51. rolfedh_doc_utils-0.1.4.dist-info/top_level.txt +0 -5
  52. {rolfedh_doc_utils-0.1.4.dist-info → rolfedh_doc_utils-0.1.41.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,246 @@
1
+ Metadata-Version: 2.4
2
+ Name: rolfedh-doc-utils
3
+ Version: 0.1.41
4
+ Summary: CLI tools for AsciiDoc documentation projects
5
+ Author: Rolfe Dlugy-Hegwer
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Rolfe Dlugy-Hegwer
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Requires-Python: >=3.8
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: PyYAML>=6.0
32
+ Dynamic: license-file
33
+
34
+ # doc-utils
35
+
36
+ [![PyPI version](https://badge.fury.io/py/rolfedh-doc-utils.svg)](https://pypi.org/project/rolfedh-doc-utils/)
37
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
38
+ [![License](https://img.shields.io/github/license/rolfedh/doc-utils)](https://github.com/rolfedh/doc-utils/blob/main/LICENSE)
39
+ [![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-brightgreen)](https://rolfedh.github.io/doc-utils/)
40
+
41
+ Python CLI tools for maintaining clean, consistent AsciiDoc documentation repositories.
42
+
43
+ 📚 **[View Full Documentation](https://rolfedh.github.io/doc-utils/)** | 📦 **[PyPI Package](https://pypi.org/project/rolfedh-doc-utils/)** | 🐙 **[GitHub](https://github.com/rolfedh/doc-utils)**
44
+
45
+ ## ⚠️ Safety First
46
+
47
+ These tools can modify or delete files. **Always:**
48
+ - Work in a git branch (never main/master)
49
+ - Review changes with `git diff`
50
+ - Test documentation builds after changes
51
+
52
+ ## 🚀 Quick Start
53
+
54
+ ### Install with pipx (Recommended)
55
+
56
+ ```bash
57
+ # Install
58
+ pipx install rolfedh-doc-utils
59
+
60
+ # Verify installation
61
+ doc-utils --version
62
+
63
+ # Upgrade to latest version
64
+ pipx upgrade rolfedh-doc-utils
65
+ ```
66
+
67
+ ### Alternative Installation
68
+
69
+ ```bash
70
+ # With pip user flag
71
+ pip install --user rolfedh-doc-utils
72
+
73
+ # For development
74
+ git clone https://github.com/rolfedh/doc-utils.git
75
+ cd doc-utils
76
+ pip install -e .
77
+ ```
78
+
79
+ ## 🛠️ Available Tools
80
+
81
+ ### Quick Reference
82
+
83
+ Run `doc-utils` to see all available tools and their descriptions:
84
+
85
+ ```bash
86
+ doc-utils --help # Show comprehensive help
87
+ doc-utils --list # List all tools
88
+ doc-utils --version # Show version
89
+ ```
90
+
91
+ ### Individual Tools
92
+
93
+ **Note:** Commands use hyphens (`-`), while Python files use underscores (`_`). After installing with pipx, use the hyphenated commands directly.
94
+
95
+ | Tool | Description | Usage |
96
+ |------|-------------|-------|
97
+ | **`validate-links`** | Validates links in AsciiDoc source files, with URL transposition for preview environments | `validate-links --transpose "https://prod--https://preview"` |
98
+ | **`check-published-links`** | Validates links on published HTML docs using linkchecker, with URL rewriting for misresolved paths | `check-published-links https://docs.example.com/guide/` |
99
+ | **`extract-link-attributes`** | Extracts link/xref macros with attributes into reusable definitions | `extract-link-attributes --dry-run` |
100
+ | **`replace-link-attributes`** | Resolves Vale LinkAttribute issues by replacing attributes in link URLs | `replace-link-attributes --dry-run` |
101
+ | **`format-asciidoc-spacing`** | Standardizes spacing after headings and around includes | `format-asciidoc-spacing --dry-run modules/` |
102
+ | **`check-scannability`** | Analyzes readability (sentence/paragraph length) | `check-scannability --max-words 25` |
103
+ | **`archive-unused-files`** | Finds and archives unreferenced .adoc files | `archive-unused-files` (preview)<br>`archive-unused-files --archive` (execute) |
104
+ | **`archive-unused-images`** | Finds and archives unreferenced images | `archive-unused-images` (preview)<br>`archive-unused-images --archive` (execute) |
105
+ | **`find-unused-attributes`** | Identifies unused attribute definitions | `find-unused-attributes attributes.adoc` |
106
+ | **`inventory-conditionals`** | Creates inventory of `ifdef`/`ifndef` conditionals | `inventory-conditionals ~/docs -o ~/reports/` |
107
+ | **`convert-callouts-to-deflist`** | Converts callout-style annotations to definition list format | `convert-callouts-to-deflist --dry-run modules/` |
108
+
109
+ ## 📖 Documentation
110
+
111
+ Comprehensive documentation is available at **[rolfedh.github.io/doc-utils](https://rolfedh.github.io/doc-utils/)**
112
+
113
+ ### OpenShift-docs Example
114
+
115
+ For OpenShift documentation repositories:
116
+
117
+ ```bash
118
+ # Format spacing in specific directories
119
+ format-asciidoc-spacing modules/
120
+ format-asciidoc-spacing microshift_networking/
121
+
122
+ # Preview changes first
123
+ format-asciidoc-spacing --dry-run modules/networking/
124
+
125
+ # Process specific file
126
+ format-asciidoc-spacing modules/networking/about-networking.adoc
127
+ ```
128
+
129
+ - [Getting Started Guide](https://rolfedh.github.io/doc-utils/getting-started)
130
+ - [Tools Reference](https://rolfedh.github.io/doc-utils/tools/)
131
+ - [Best Practices](https://rolfedh.github.io/doc-utils/best-practices)
132
+ - [Contributing](https://rolfedh.github.io/doc-utils/contributing)
133
+
134
+ ## 💡 Common Workflows
135
+
136
+ ### Clean Up Documentation
137
+
138
+ ```bash
139
+ # 1. Create a branch
140
+ git checkout -b doc-cleanup
141
+
142
+ # 2. Preview what would change
143
+ format-asciidoc-spacing --dry-run .
144
+ archive-unused-files
145
+ archive-unused-images
146
+
147
+ # 3. Apply changes
148
+ format-asciidoc-spacing .
149
+ archive-unused-files --archive
150
+ archive-unused-images --archive
151
+
152
+ # 4. Review and commit
153
+ git diff
154
+ git add -A && git commit -m "Clean up documentation"
155
+ ```
156
+
157
+ ### Check Documentation Quality
158
+
159
+ ```bash
160
+ # Check readability
161
+ check-scannability --max-words 30 --max-sentences 5
162
+
163
+ # Find unused attributes
164
+ find-unused-attributes attributes.adoc
165
+
166
+ # Save results
167
+ find-unused-attributes attributes.adoc --output unused.txt
168
+ ```
169
+
170
+ ## 🔧 Exclusions
171
+
172
+ All tools support excluding files and directories:
173
+
174
+ ```bash
175
+ # Exclude specific directories
176
+ archive-unused-files --exclude-dir ./temp --exclude-dir ./drafts
177
+
178
+ # Exclude specific files
179
+ check-scannability --exclude-file ./README.adoc
180
+
181
+ # Use exclusion list file
182
+ echo "./deprecated/" > .docutils-ignore
183
+ archive-unused-images --exclude-list .docutils-ignore
184
+ ```
185
+
186
+ ## 🧪 Development
187
+
188
+ ```bash
189
+ # Install dev dependencies
190
+ pip install -r requirements-dev.txt
191
+
192
+ # Run tests
193
+ python -m pytest tests/ -v
194
+
195
+ # Run specific test
196
+ python -m pytest tests/test_file_utils.py
197
+ ```
198
+
199
+ ## 🤝 Contributing
200
+
201
+ We welcome contributions! See our [Contributing Guide](https://rolfedh.github.io/doc-utils/contributing) for details.
202
+
203
+ Before submitting PRs:
204
+ - ✅ All tests pass
205
+ - ✅ Code follows PEP 8
206
+ - ✅ Documentation updated
207
+ - ✅ Changelog entry added
208
+
209
+ ## 📊 Project Status
210
+
211
+ - **Latest Version**: 0.1.16 (with automatic update notifications)
212
+ - **Python Support**: 3.8+
213
+ - **Test Coverage**: 112+ tests (100% passing)
214
+ - **Dependencies**: Minimal (PyYAML for OpenShift-docs support)
215
+
216
+ ### 🔔 Update Notifications
217
+
218
+ All tools automatically check for updates and notify you when a new version is available. The notification will recommend the appropriate upgrade command based on how you installed the package:
219
+
220
+ ```
221
+ 📦 Update available: 0.1.19 → 0.1.20
222
+ Run: pipx upgrade rolfedh-doc-utils
223
+ ```
224
+
225
+ To disable update checks, set the environment variable:
226
+ ```bash
227
+ export DOC_UTILS_NO_VERSION_CHECK=1
228
+ ```
229
+
230
+ Update checks are cached for 24 hours to minimize network requests.
231
+
232
+ ## 🔗 Resources
233
+
234
+ - [Documentation](https://rolfedh.github.io/doc-utils/)
235
+ - [PyPI Package](https://pypi.org/project/rolfedh-doc-utils/)
236
+ - [Issue Tracker](https://github.com/rolfedh/doc-utils/issues)
237
+ - [Changelog](https://github.com/rolfedh/doc-utils/blob/main/CHANGELOG.md)
238
+ - [License](https://github.com/rolfedh/doc-utils/blob/main/LICENSE)
239
+
240
+ ## 📝 License
241
+
242
+ This project is licensed under the terms specified in the [LICENSE](https://github.com/rolfedh/doc-utils/blob/main/LICENSE) file.
243
+
244
+ ---
245
+
246
+ Created by [Rolfe Dlugy-Hegwer](https://github.com/rolfedh) for technical writers everywhere.
@@ -0,0 +1,52 @@
1
+ archive_unused_files.py,sha256=YKYPtuBHEZcsyQSwSYxSYvw9v9Mh6Of8MqT53A5bM44,2438
2
+ archive_unused_images.py,sha256=EvPhMIwp6_AHKtuNYQ663q6biXBeXaqf88NzWrhvtIE,2029
3
+ check_published_links.py,sha256=nk07prV6xHVqVrYCy2Eb8BWkjkgJBhczk8U0E-KeIvA,43258
4
+ check_scannability.py,sha256=O6ROr-e624jVPvPpASpsWo0gTfuCFpA2mTSX61BjAEI,5478
5
+ check_source_directives.py,sha256=JiIvn_ph9VKPMH4zg-aSsuIGQZcnI_imj7rZLLE04L8,3660
6
+ convert_callouts_interactive.py,sha256=4PjiVIOWxNJiJLQuBHT3x6rE46-hgfFHSaoo5quYIs8,22889
7
+ convert_callouts_to_deflist.py,sha256=BoqW5_GkQ-KqNzn4vmE6lsQosrPV0lkB-bfAx3dzyMw,25886
8
+ convert_freemarker_to_asciidoc.py,sha256=ki0bFDPWxl9aUHK_-xqffIKF4KJYMXA8S4XLG_mOA0U,10097
9
+ convert_tables_to_deflists.py,sha256=PIP6xummuMqC3aSzahKKRBYahes_j5ZpHp_-k6BjurY,15599
10
+ doc_utils_cli.py,sha256=J3CE7cTDDCRGkhAknYejNWHhk5t9YFGt27WDVfR98Xk,5111
11
+ extract_link_attributes.py,sha256=wR2SmR2la-jR6DzDbas2PoNONgRZ4dZ6aqwzkwEv8Gs,3516
12
+ find_duplicate_content.py,sha256=iYWekmriItXWSd8nBnIQN_FoZkv6quPJNL0qjv6UxUA,6343
13
+ find_duplicate_includes.py,sha256=sQaVLOe4Ksc3t08_A_2GaLMwQCgKe9Nsr8c3ipp1Ph0,5456
14
+ find_unused_attributes.py,sha256=AQVJsvRRgGsDjOZClcvJRQ5i5H2YrClcR-1nRLVBzI8,5140
15
+ format_asciidoc_spacing.py,sha256=nmWpw2dgwhd81LXyznq0rT8w6Z7cNRyGtPJGRyKFRdc,4212
16
+ insert_abstract_role.py,sha256=C1PZilpYTC1xUfdujAarNXo3oYXbToLdQB4wCpWQrsg,5454
17
+ inventory_conditionals.py,sha256=vLWEDTj9MbqUnA_iw4g-HEVX47fSG8tfd4KpSJKg6kA,1416
18
+ replace_link_attributes.py,sha256=Cpc4E-j9j-4_y0LOstAKYOPl02Ln_2bGNIeqp3ZVCdA,7624
19
+ validate_links.py,sha256=lWuK8sgfiFdfcUdSVAt_5U9JHVde_oa6peSUlBQtsac,6145
20
+ callout_lib/__init__.py,sha256=8B82N_z4D1LaZVYgd5jZR53QAabtgPzADOyGlnvihj0,665
21
+ callout_lib/converter_bullets.py,sha256=nfH0hz4p8qNM2F-MhtBjwH-lUYcNf2m1sdJebRlCxoo,4405
22
+ callout_lib/converter_comments.py,sha256=do0dH8uOyNFpn5CDEzR0jYYCMIPP3oPFM8cEB-Fp22c,9767
23
+ callout_lib/converter_deflist.py,sha256=Ocr3gutTo_Sl_MkzethZH1UO6mCDEcuExGMZF5MfZFg,6131
24
+ callout_lib/detector.py,sha256=S0vZDa4zhTSn6Kv0hWfG56W-5srGxUc-nvpLe_gIx-A,15971
25
+ callout_lib/table_parser.py,sha256=ZucisADE8RDAk5HtIrttaPgBi6Hf8ZUpw7KzfbcmEjc,31450
26
+ doc_utils/__init__.py,sha256=qqZR3lohzkP63soymrEZPBGzzk6-nFzi4_tSffjmu_0,74
27
+ doc_utils/convert_freemarker_to_asciidoc.py,sha256=UGQ7iS_9bkVdDMAWBORXbK0Q5mLPmDs1cDJqoR4LLH8,22491
28
+ doc_utils/duplicate_content.py,sha256=rFrIuiDE5CqWQyL7wTLL-GlrGVDNs1fsq36eQIsCMug,14580
29
+ doc_utils/duplicate_includes.py,sha256=8hpL7fq_pHcKMS0C50LTwTyzqth39nMQ9Lz67gie8b0,10654
30
+ doc_utils/extract_link_attributes.py,sha256=U0EvPZReJQigNfbT-icBsVT6Li64hYki5W7MQz6qqbc,22743
31
+ doc_utils/file_utils.py,sha256=fpTh3xx759sF8sNocdn_arsP3KAv8XA6cTQTAVIZiZg,4247
32
+ doc_utils/format_asciidoc_spacing.py,sha256=RL2WU_dG_UfGL01LnevcyJfKsvYy_ogNyeoVX-Fyqks,13579
33
+ doc_utils/insert_abstract_role.py,sha256=z_Pm8A3EE03DU2fBReJDwMfIUT1qRbDcOKw4EI63I90,6641
34
+ doc_utils/inventory_conditionals.py,sha256=PSrdmeBHbpayvXgaRryqvjUlLZYryPgU9js8IBYqB7g,5486
35
+ doc_utils/missing_source_directive.py,sha256=X3Acn0QJTk6XjmBXhGus5JAjlIitCiicCRE3fslifyw,8048
36
+ doc_utils/replace_link_attributes.py,sha256=gmAs68_njBqEz-Qni-UGgeYEDTMxlTWk_IOm76FONNE,7279
37
+ doc_utils/scannability.py,sha256=XwlmHqDs69p_V36X7DLjPTy0DUoLszSGqYjJ9wE-3hg,982
38
+ doc_utils/spinner.py,sha256=lJg15qzODiKoR0G6uFIk2BdVNgn9jFexoTRUMrjiWvk,3554
39
+ doc_utils/topic_map_parser.py,sha256=tKcIO1m9r2K6dvPRGue58zqMr0O2zKU1gnZMzEE3U6o,4571
40
+ doc_utils/unused_adoc.py,sha256=LPQWPGEOizXECxepk7E_5cjTVvKn6RXQYTWG97Ps5VQ,9077
41
+ doc_utils/unused_attributes.py,sha256=2UmqdXd5ogaPtj9_teApM0IlkdCmzBZNRh7XXrVYJOk,9032
42
+ doc_utils/unused_images.py,sha256=hL8Qrik9QCkVh54eBLuNczRS9tMnsqIEfavNamM1UeQ,5664
43
+ doc_utils/validate_links.py,sha256=iBGXnwdeLlgIT3fo3v01ApT5k0X2FtctsvkrE6E3VMk,19610
44
+ doc_utils/version.py,sha256=6M2GqXp9MDStl7sAFi0I6plYG3mZnB2F3aR5fYWcdNI,203
45
+ doc_utils/version_check.py,sha256=-31Y6AN0KGi_CUCAVOOhf6bPO3r7SQIXPxxeffLAF0w,7535
46
+ doc_utils/warnings_report.py,sha256=20yfwqBjOprfFhQwCujbcsvjJCbHHhmH84uAujm-y-o,8877
47
+ rolfedh_doc_utils-0.1.41.dist-info/licenses/LICENSE,sha256=vLxtwMVOJA_hEy8b77niTkdmQI9kNJskXHq0dBS36e0,1075
48
+ rolfedh_doc_utils-0.1.41.dist-info/METADATA,sha256=TFcXE1pz0bqxhUSvIN20DJzYn4bS9Ix-HLw1b38cdUk,8654
49
+ rolfedh_doc_utils-0.1.41.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
50
+ rolfedh_doc_utils-0.1.41.dist-info/entry_points.txt,sha256=VbjA2E5PzSdELQ59Gutdj2C7ZXVhTypTzc1LyTdepis,1023
51
+ rolfedh_doc_utils-0.1.41.dist-info/top_level.txt,sha256=5ajEGX1siKKjC1cahR-_X-XKMsH8BdY9RPaH8vdSHB8,460
52
+ rolfedh_doc_utils-0.1.41.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,20 @@
1
+ [console_scripts]
2
+ archive-unused-files = archive_unused_files:main
3
+ archive-unused-images = archive_unused_images:main
4
+ check-published-links = check_published_links:main
5
+ check-scannability = check_scannability:main
6
+ check-source-directives = check_source_directives:main
7
+ convert-callouts-interactive = convert_callouts_interactive:main
8
+ convert-callouts-to-deflist = convert_callouts_to_deflist:main
9
+ convert-freemarker-to-asciidoc = convert_freemarker_to_asciidoc:main
10
+ convert-tables-to-deflists = convert_tables_to_deflists:main
11
+ doc-utils = doc_utils_cli:main
12
+ extract-link-attributes = extract_link_attributes:main
13
+ find-duplicate-content = find_duplicate_content:main
14
+ find-duplicate-includes = find_duplicate_includes:main
15
+ find-unused-attributes = find_unused_attributes:main
16
+ format-asciidoc-spacing = format_asciidoc_spacing:main
17
+ insert-abstract-role = insert_abstract_role:main
18
+ inventory-conditionals = inventory_conditionals:main
19
+ replace-link-attributes = replace_link_attributes:main
20
+ validate-links = validate_links:main
@@ -0,0 +1,21 @@
1
+ archive_unused_files
2
+ archive_unused_images
3
+ callout_lib
4
+ check_published_links
5
+ check_scannability
6
+ check_source_directives
7
+ convert_callouts_interactive
8
+ convert_callouts_to_deflist
9
+ convert_freemarker_to_asciidoc
10
+ convert_tables_to_deflists
11
+ doc_utils
12
+ doc_utils_cli
13
+ extract_link_attributes
14
+ find_duplicate_content
15
+ find_duplicate_includes
16
+ find_unused_attributes
17
+ format_asciidoc_spacing
18
+ insert_abstract_role
19
+ inventory_conditionals
20
+ replace_link_attributes
21
+ validate_links
validate_links.py ADDED
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Validate links in AsciiDoc documentation.
4
+
5
+ This tool checks all links in AsciiDoc files for validity, including:
6
+ - External HTTP/HTTPS links
7
+ - Internal cross-references (xref)
8
+ - Image paths
9
+ """
10
+
11
+ import argparse
12
+ import sys
13
+ import json
14
+ from doc_utils.validate_links import LinkValidator, parse_transpositions, format_results
15
+ from doc_utils.version_check import check_version_on_startup
16
+ from doc_utils.version import __version__
17
+ from doc_utils.spinner import Spinner
18
+
19
+
20
+ def main():
21
+ # Check for updates (non-blocking, won't interfere with tool operation)
22
+ check_version_on_startup()
23
+ """Main entry point for the validate-links CLI tool."""
24
+ parser = argparse.ArgumentParser(
25
+ description='Validate links in AsciiDoc documentation',
26
+ formatter_class=argparse.RawDescriptionHelpFormatter,
27
+ epilog="""
28
+ Examples:
29
+ # Basic validation
30
+ validate-links
31
+
32
+ # Validate against preview environment
33
+ validate-links --transpose "https://docs.redhat.com--https://preview.docs.redhat.com"
34
+
35
+ # Multiple transpositions
36
+ validate-links \\
37
+ --transpose "https://docs.redhat.com--https://preview.docs.redhat.com" \\
38
+ --transpose "https://access.redhat.com--https://stage.access.redhat.com"
39
+
40
+ # With specific options
41
+ validate-links \\
42
+ --transpose "https://docs.example.com--https://preview.example.com" \\
43
+ --attributes-file common-attributes.adoc \\
44
+ --timeout 15 \\
45
+ --retry 3 \\
46
+ --parallel 20 \\
47
+ --exclude-domain localhost \\
48
+ --exclude-domain example.com
49
+
50
+ # Export results to JSON
51
+ validate-links --output report.json --format json
52
+ """
53
+ )
54
+
55
+ parser.add_argument(
56
+ '--transpose',
57
+ action='append',
58
+ help='Transpose URLs from production to preview/staging (format: from_url--to_url)'
59
+ )
60
+
61
+ parser.add_argument(
62
+ '--attributes-file',
63
+ help='Path to the AsciiDoc attributes file'
64
+ )
65
+
66
+ parser.add_argument(
67
+ '--scan-dir',
68
+ action='append',
69
+ help='Directory to scan for .adoc files (can be used multiple times, default: current directory)'
70
+ )
71
+
72
+ parser.add_argument(
73
+ '--timeout',
74
+ type=int,
75
+ default=10,
76
+ help='Timeout in seconds for each URL check (default: 10)'
77
+ )
78
+
79
+ parser.add_argument(
80
+ '--retry',
81
+ type=int,
82
+ default=3,
83
+ help='Number of retries for failed URLs (default: 3)'
84
+ )
85
+
86
+ parser.add_argument(
87
+ '--parallel',
88
+ type=int,
89
+ default=10,
90
+ help='Number of parallel URL checks (default: 10)'
91
+ )
92
+
93
+ parser.add_argument(
94
+ '--cache-duration',
95
+ type=int,
96
+ default=3600,
97
+ help='Cache duration in seconds (default: 3600)'
98
+ )
99
+
100
+ parser.add_argument(
101
+ '--exclude-domain',
102
+ action='append',
103
+ dest='exclude_domains',
104
+ help='Domain to exclude from validation (can be used multiple times)'
105
+ )
106
+
107
+ parser.add_argument(
108
+ '--no-cache',
109
+ action='store_true',
110
+ help='Disable caching of validation results'
111
+ )
112
+
113
+ parser.add_argument(
114
+ '--output',
115
+ help='Output file for results'
116
+ )
117
+
118
+ parser.add_argument(
119
+ '--format',
120
+ choices=['text', 'json', 'junit'],
121
+ default='text',
122
+ help='Output format (default: text)'
123
+ )
124
+
125
+ parser.add_argument(
126
+ '-v', '--verbose',
127
+ action='store_true',
128
+ help='Show verbose output including warnings'
129
+ )
130
+
131
+ parser.add_argument(
132
+ '--fail-on-broken',
133
+ action='store_true',
134
+ help='Exit with error code if broken links are found'
135
+ )
136
+ parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
137
+
138
+ args = parser.parse_args()
139
+
140
+ # Parse transpositions
141
+ transpositions = parse_transpositions(args.transpose)
142
+
143
+ # Show configuration
144
+ print("Validating links in documentation...")
145
+ if args.attributes_file:
146
+ print(f"Loading attributes from {args.attributes_file}")
147
+ if transpositions:
148
+ print("\nURL Transposition Rules:")
149
+ for from_url, to_url in transpositions:
150
+ print(f" {from_url} → {to_url}")
151
+ print()
152
+
153
+ # Create validator
154
+ validator = LinkValidator(
155
+ timeout=args.timeout,
156
+ retry=args.retry,
157
+ parallel=args.parallel,
158
+ cache_duration=args.cache_duration if not args.no_cache else 0,
159
+ transpositions=transpositions
160
+ )
161
+
162
+ try:
163
+ # Run validation
164
+ spinner = Spinner("Validating links")
165
+ spinner.start()
166
+ results = validator.validate_all(
167
+ scan_dirs=args.scan_dir,
168
+ attributes_file=args.attributes_file,
169
+ exclude_domains=args.exclude_domains
170
+ )
171
+ total = results['summary']['total']
172
+ valid = results['summary']['valid']
173
+ spinner.stop(f"Validated {total} links: {valid} valid")
174
+
175
+ # Format output
176
+ if args.format == 'json':
177
+ output = json.dumps(results, indent=2)
178
+ elif args.format == 'junit':
179
+ # TODO: Implement JUnit XML format
180
+ output = format_results(results, verbose=args.verbose)
181
+ else:
182
+ output = format_results(results, verbose=args.verbose)
183
+
184
+ # Save or print output
185
+ if args.output:
186
+ with open(args.output, 'w', encoding='utf-8') as f:
187
+ f.write(output)
188
+ print(f"Results saved to {args.output}")
189
+ # Still print summary to console
190
+ if args.format != 'text':
191
+ summary = results['summary']
192
+ print(f"\nSummary: {summary['valid']} valid, {summary['broken']} broken, "
193
+ f"{summary['warnings']} warnings")
194
+ else:
195
+ print(output)
196
+
197
+ # Exit code
198
+ if args.fail_on_broken and results['summary']['broken'] > 0:
199
+ sys.exit(1)
200
+
201
+ except KeyboardInterrupt:
202
+ print("\nValidation cancelled.")
203
+ sys.exit(1)
204
+ except Exception as e:
205
+ print(f"Error: {e}", file=sys.stderr)
206
+ if args.verbose:
207
+ import traceback
208
+ traceback.print_exc()
209
+ sys.exit(1)
210
+
211
+
212
+ if __name__ == '__main__':
213
+ main()