doc_guard 1.0.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +166 -0
- data/bin/doc_guard +7 -0
- data/doc_guard.gemspec +44 -0
- data/lib/doc_guard/assess_documentation_relevance/process.rb +94 -0
- data/lib/doc_guard/assess_documentation_relevance/subprocesses/assess_relevance.rb +34 -0
- data/lib/doc_guard/assess_documentation_relevance/subprocesses/compare_digests.rb +29 -0
- data/lib/doc_guard/assess_documentation_relevance/subprocesses/load_stored_digests.rb +22 -0
- data/lib/doc_guard/assess_documentation_relevance/subprocesses/report_assessment.rb +33 -0
- data/lib/doc_guard/cli.rb +60 -0
- data/lib/doc_guard/config.rb +76 -0
- data/lib/doc_guard/record_documentation_relevance/process.rb +73 -0
- data/lib/doc_guard/record_documentation_relevance/subprocesses/record_digests.rb +27 -0
- data/lib/doc_guard/record_documentation_relevance/subprocesses/report_recording.rb +28 -0
- data/lib/doc_guard/shared/subprocesses/calculate_current_digests.rb +40 -0
- data/lib/doc_guard/shared/subprocesses/load_tracked_files_from_documentation.rb +44 -0
- data/lib/doc_guard/version.rb +8 -0
- data/lib/doc_guard.rb +22 -0
- metadata +116 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7e1710f91fe17ebf7bc8d1f123fc093d6c7e0ad4fc2c42fec0866445f707849e
|
|
4
|
+
data.tar.gz: 9553cff66d4bc4c958f7a2849c9d317e36e611b662b5816fb8b8a7d476b722b6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0a595ad237f74d9757914eabc28766a8b649a84e25d42e7dc7a909dcc99726f718bedd102e3a3463e8820d1ffcaf75a8cda941154f9a3bc5f2285d489acab3ae
|
|
7
|
+
data.tar.gz: d38e09d03d05e5deb09ce66b9d2148cb2e5ae1989748a565a4489f523ada659d25986a234e300b9acaf9aece4a8d691facf31fd59ee9e9d056deb4d0df2c551e
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [1.0.0] - 2025-07-02
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release of DocGuard gem, a tool to help keep documentation up-to-date with your source code.
|
|
14
|
+
- Support for annotating markdown documentation files with tracked source files via special HTML comments.
|
|
15
|
+
- Available CLI commands (built on Thor):
|
|
16
|
+
- `doc_guard record` - records current digests of tracked files.
|
|
17
|
+
- `doc_guard assess` - checks if documentation is up to date based on tracked file changes.
|
|
18
|
+
- `doc_guard help` - displays available commands and usage instructions.
|
|
19
|
+
- Configuration support:
|
|
20
|
+
- Load settings via CLI options, environment variables, and YAML config file (`.doc_guard.yml`).
|
|
21
|
+
- Defaults provided for documentation path and digests store file.
|
|
22
|
+
- Integration-ready for CI/CD pipelines to automate documentation relevance checks.
|
|
23
|
+
- Autoloading using Zeitwerk for cleaner project structure.
|
|
24
|
+
- Comprehensive error handling in CLI with proper exit codes.
|
|
25
|
+
- Basic RSpec tests covering configuration and core functionality.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- N/A (initial release)
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- N/A (initial release)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Patryk Gramatowski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<img src="doc_guard-logo.png" alt="doc_guard logo" width="250" style="display: block; margin: 0 auto"/>
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
### 🛡️ Keep your documentation up-to-date with your code
|
|
6
|
+
|
|
7
|
+
**DocGuard** is a lightweight, extensible Ruby gem that helps **ensure your documentation remains accurate** as your code evolves. It **tracks** source files referenced in documentation and **warns** when changes occur.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
### 🚀 Features
|
|
12
|
+
|
|
13
|
+
- **Automatic File Tracking:**
|
|
14
|
+
Easily annotate your documentation files with the source code files they reference. DocGuard automatically detects which files your docs depend on, eliminating manual tracking.
|
|
15
|
+
|
|
16
|
+
- **Digest Calculation & Change Detection:**
|
|
17
|
+
DocGuard computes SHA256 digests (hashes) of tracked files and compares them with previously stored digests to detect any changes. This helps identify when code changes may impact your documentation.
|
|
18
|
+
|
|
19
|
+
- **Real-time Relevance Assessment:**
|
|
20
|
+
Quickly determine if your documentation is still relevant based on the current state of your code. Get instant feedback when your docs might be outdated.
|
|
21
|
+
|
|
22
|
+
- **Clear and Actionable Reporting:**
|
|
23
|
+
Receive concise CLI reports highlighting which files caused potential documentation mismatches, helping you prioritize documentation updates.
|
|
24
|
+
|
|
25
|
+
- **Fail-safe Exit Codes:**
|
|
26
|
+
Integrate DocGuard seamlessly into CI/CD pipelines, it exits with meaningful statuses:
|
|
27
|
+
- `0` when docs are up to date,
|
|
28
|
+
- `1` when documentation may be outdated,
|
|
29
|
+
- `2` on unexpected errors.
|
|
30
|
+
|
|
31
|
+
- **Flexible CLI (built on [Thor](https://github.com/rails/thor)):**
|
|
32
|
+
Easy-to-use commands to assess relevance and extend with future commands for recording or other workflows.
|
|
33
|
+
|
|
34
|
+
- **Lightweight & Framework Agnostic:**
|
|
35
|
+
Works well in Rails projects but designed to be framework-independent and lightweight enough to fit any Ruby codebase.
|
|
36
|
+
|
|
37
|
+
- **JSON-based Digest Storage:**
|
|
38
|
+
Stores digests in a human-readable JSON file, making it easy to audit or manage outside the tool if needed.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### 💿 Installation
|
|
42
|
+
|
|
43
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
bundle add doc_guard
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
gem install doc_guard
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
or add this line to your application's Gemfile:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
gem "doc_guard"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### ⚙️ Usage
|
|
62
|
+
|
|
63
|
+
#### 1. Annotate Documentation with Tracked Files 📝
|
|
64
|
+
|
|
65
|
+
To enable DocGuard to track which source files your documentation depends on, add a special HTML comment in your markdown files (e.g., `README.md` or any `.md` files inside a `docs/` folder).
|
|
66
|
+
|
|
67
|
+
##### How to annotate:
|
|
68
|
+
Insert the following comment at the top (or anywhere relevant) in your markdown file:
|
|
69
|
+
|
|
70
|
+
```md
|
|
71
|
+
<!-- doc_guard files: [app/models/user.rb, app/services/authenticator.rb] -->
|
|
72
|
+
Your amazing and very important documentation related to the user model and authentication service is here.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### 2. Record the current state of the relationship between your documentation and your code 💾
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
$ doc_guard record
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This stores the current digests of referenced files in `.doc_guard_digests.json.`
|
|
82
|
+
|
|
83
|
+
#### 3. Assess documentation relevance 🚦
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
$ doc_guard assess
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Example output:
|
|
90
|
+
```bash
|
|
91
|
+
‼️ Documentation may be outdated!
|
|
92
|
+
|
|
93
|
+
📄 docs/user.md
|
|
94
|
+
- app/models/user.rb
|
|
95
|
+
- app/services/authenticator.rb
|
|
96
|
+
|
|
97
|
+
📄 docs/admin.md
|
|
98
|
+
- app/models/admin.rb
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 🔧 Configuration
|
|
102
|
+
|
|
103
|
+
DocGuard can be configured in multiple flexible ways to suit different environments (e.g., local development, CI/CD pipelines, GitHub Actions).
|
|
104
|
+
|
|
105
|
+
Configuration values are resolved using the following priority:
|
|
106
|
+
1. CLI flags
|
|
107
|
+
2. Environment variables
|
|
108
|
+
3. YAML config file (`.doc_guard.yml`)
|
|
109
|
+
4. Internal defaults
|
|
110
|
+
|
|
111
|
+
#### 🗂️ Available Configuration Options
|
|
112
|
+
|
|
113
|
+
| Setting | CLI Flag | ENV Variable | YAML Config Key | Default Value |
|
|
114
|
+
|-----------------------------|------------------------------------|--------------------------------------|----------------------------------------|-------------------------------|
|
|
115
|
+
| Documentation path | `--documentation-path` | `DOC_GUARD_DOCUMENTATION_PATH` | `documentation_path` | `"docs"` |
|
|
116
|
+
| Digests store file path | `--digests-store-file-path` | `DOC_GUARD_DIGESTS_STORE_FILE_PATH` | `digests_store_file_path` | `".doc_guard_digests.json"` |
|
|
117
|
+
| Config file path (for YAML) | `--config-file-path` | `DOC_GUARD_CONFIG_FILE_PATH` | *(Not loaded from YAML)* | `".doc_guard.yml"` |
|
|
118
|
+
|
|
119
|
+
*Note: The `config_file_path` is used only to load other settings from a YAML file. It cannot itself be configured via YAML.*
|
|
120
|
+
|
|
121
|
+
#### 📁 Config File Example
|
|
122
|
+
```yaml
|
|
123
|
+
# .doc_guard.yml
|
|
124
|
+
|
|
125
|
+
documentation_path: custom_docs
|
|
126
|
+
digests_store_file_path: tmp/doc_guard_state.json
|
|
127
|
+
```
|
|
128
|
+
*Note: Place this file in your project root.*
|
|
129
|
+
|
|
130
|
+
This configuration approach supports usage in CI/CD pipelines, local development, or scripts, **without any dependency** on Rails or other frameworks.
|
|
131
|
+
|
|
132
|
+
### 🛣️ Roadmap
|
|
133
|
+
|
|
134
|
+
The following features are being considered or are actively planned for future releases:
|
|
135
|
+
|
|
136
|
+
- **External documentation integrations**
|
|
137
|
+
Support for syncing and validating documentation that lives outside the main codebase, e.g., Confluence, GitHub Wikis, or other remote documentation tools.
|
|
138
|
+
|
|
139
|
+
- **Custom digest strategies**
|
|
140
|
+
Allow users to define custom strategies for calculating file "change relevance", e.g., ignore comments or whitespace, or weigh different parts of the code differently.
|
|
141
|
+
|
|
142
|
+
- **AI-assisted relevance analysis**
|
|
143
|
+
In the future, integrate with AI systems to help assess whether documentation should be updated based on the semantic nature of recent code changes (e.g., if a developer introduces a new feature but forgets to update corresponding docs).
|
|
144
|
+
|
|
145
|
+
- **Advanced code–documentation annotation system**
|
|
146
|
+
Improve how source code is linked to documentation, potentially with fine-grained annotations (e.g., per method, class, or even logic block), offering a more accurate mapping between code changes and relevant documentation sections.
|
|
147
|
+
|
|
148
|
+
Have an idea or need a feature? Feel free to [open an issue](https://github.com/patrickgramatowski/doc_guard/issues) or contribute!
|
|
149
|
+
|
|
150
|
+
### 🧑💻 Development
|
|
151
|
+
|
|
152
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
153
|
+
|
|
154
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
155
|
+
|
|
156
|
+
### 🤝 Contributing
|
|
157
|
+
|
|
158
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/patrickgramatowski/doc_guard. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/patrickgramatowski/doc_guard/blob/master/CODE_OF_CONDUCT.md).
|
|
159
|
+
|
|
160
|
+
### 📄 License
|
|
161
|
+
|
|
162
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
163
|
+
|
|
164
|
+
### 📜 Code of Conduct
|
|
165
|
+
|
|
166
|
+
Everyone interacting in the DocGuard project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/patrickgramatowski/doc_guard/blob/master/CODE_OF_CONDUCT.md).
|
data/bin/doc_guard
ADDED
data/doc_guard.gemspec
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/doc_guard/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "doc_guard"
|
|
7
|
+
spec.version = DocGuard::VERSION
|
|
8
|
+
spec.authors = ["Patryk Gramatowski"]
|
|
9
|
+
spec.email = ["patrick.gramatowski@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "DocGuard helps maintain up-to-date project documentation by tracking files referenced in your docs."
|
|
12
|
+
spec.description = <<~DESCRIPTION
|
|
13
|
+
DocGuard helps maintain up-to-date project documentation by tracking files referenced in your docs,
|
|
14
|
+
calculating file digests, and assessing whether code changes impact your documentation relevance.
|
|
15
|
+
It provides CLI commands to assess documentation relevance and record the current documentation state,
|
|
16
|
+
enabling automated enforcement and better documentation quality in Rails and Ruby projects.
|
|
17
|
+
DESCRIPTION
|
|
18
|
+
|
|
19
|
+
spec.homepage = "https://github.com/patrickgramatowski/doc_guard"
|
|
20
|
+
spec.license = "MIT"
|
|
21
|
+
spec.required_ruby_version = ">= 3.2"
|
|
22
|
+
|
|
23
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
24
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
25
|
+
|
|
26
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
27
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
28
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
|
|
29
|
+
|
|
30
|
+
spec.bindir = "bin"
|
|
31
|
+
spec.executables = [spec.name]
|
|
32
|
+
spec.require_paths = ["lib"]
|
|
33
|
+
spec.files = [
|
|
34
|
+
Dir.glob("{#{spec.require_paths.join(",")}}/**/*.rb"),
|
|
35
|
+
%W[#{spec.name}.gemspec],
|
|
36
|
+
%w[CHANGELOG.md README.md LICENSE.txt]
|
|
37
|
+
].flatten
|
|
38
|
+
|
|
39
|
+
spec.add_dependency "digest", "~> 3.0"
|
|
40
|
+
spec.add_dependency "thor", "~> 1.3"
|
|
41
|
+
spec.add_dependency "zeitwerk", "~> 2.6"
|
|
42
|
+
|
|
43
|
+
spec.post_install_message = "=> ✅ Setup complete! You can now use `#{spec.name}` on the command line."
|
|
44
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
module AssessDocumentationRelevance
|
|
5
|
+
# Main orchestration class that assesses whether documentation is still relevant
|
|
6
|
+
# based on digests of referenced files.
|
|
7
|
+
class Process
|
|
8
|
+
# Entry point to run the relevance assessment process.
|
|
9
|
+
#
|
|
10
|
+
# @param config [DocGuard::Config] Optional configuration.
|
|
11
|
+
# @return [void]
|
|
12
|
+
def self.run(config: ::DocGuard::Config.new)
|
|
13
|
+
new(config: config).call
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Initializes the process with the given configuration.
|
|
17
|
+
#
|
|
18
|
+
# @param config [DocGuard::Config]
|
|
19
|
+
def initialize(config: ::DocGuard::Config.new)
|
|
20
|
+
@config = config
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Executes the full relevance assessment pipeline.
|
|
24
|
+
#
|
|
25
|
+
# @return [void]
|
|
26
|
+
def call
|
|
27
|
+
tracked_files = load_tracked_files
|
|
28
|
+
stored_digests = load_stored_digests
|
|
29
|
+
current_digests = calculate_current_digests(tracked_files)
|
|
30
|
+
mismatches = compare_digests(stored_digests, current_digests)
|
|
31
|
+
assessment = assess_relevance(tracked_files, mismatches)
|
|
32
|
+
|
|
33
|
+
report_assessment(assessment)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
attr_reader :config
|
|
39
|
+
|
|
40
|
+
# Loads files tracked from documentation annotations.
|
|
41
|
+
#
|
|
42
|
+
# @return [Hash{String => Array<String>}] Mapping of documentation files to arrays of tracked source files.
|
|
43
|
+
def load_tracked_files
|
|
44
|
+
::DocGuard::Shared::Subprocesses::LoadTrackedFilesFromDocumentation.run(config: config)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Loads stored digests for previously tracked files.
|
|
48
|
+
#
|
|
49
|
+
# @return [Hash<String, Hash<String, String>>] Mapping of documentation files to
|
|
50
|
+
# tracked file digests (e.g., { "docs/file.md" => { "app/file.rb" => "digest" } }),
|
|
51
|
+
# or an empty hash if the file does not exist
|
|
52
|
+
def load_stored_digests
|
|
53
|
+
Subprocesses::LoadStoredDigests.run(config: config)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Calculates current digests of tracked files.
|
|
57
|
+
#
|
|
58
|
+
# @param tracked_files [Hash<String, Array<String>>] Mapping of documentation files to lists of tracked source file paths.
|
|
59
|
+
# @return [Hash<String, Hash<String, String?>>] Mapping of documentation files to tracked source files
|
|
60
|
+
# and their SHA256 digests (or nil if missing).
|
|
61
|
+
def calculate_current_digests(tracked_files)
|
|
62
|
+
::DocGuard::Shared::Subprocesses::CalculateCurrentDigests.run(tracked_files: tracked_files)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Compares stored and current digests to find mismatches.
|
|
66
|
+
#
|
|
67
|
+
# @param stored_digests [Hash<String, Hash<String, String>>] Previously stored digests.
|
|
68
|
+
# @param current_digests [Hash<String, Hash<String, String?>>] Freshly calculated digests.
|
|
69
|
+
# @return [Array<String>] List of file paths with mismatched or missing digests.
|
|
70
|
+
def compare_digests(stored_digests, current_digests)
|
|
71
|
+
Subprocesses::CompareDigests.run(stored_digests: stored_digests, current_digests: current_digests)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Assesses documentation relevance based on mismatched source files.
|
|
75
|
+
#
|
|
76
|
+
# @param tracked_files [Hash{String => Array<String>}] Doc-to-tracked-files mapping.
|
|
77
|
+
# @param mismatches [Array<String>] list of changed file paths.
|
|
78
|
+
# @return [Hash{Symbol => Object}]
|
|
79
|
+
# - :relevant [Boolean] true if any documentation is outdated.
|
|
80
|
+
# - :mismatches [Hash{String => Array<String>}] docs with the changed files they reference.
|
|
81
|
+
def assess_relevance(tracked_files, mismatches)
|
|
82
|
+
Subprocesses::AssessRelevance.run(tracked_files: tracked_files, mismatches: mismatches)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Reports the result of the assessment and exits if outdated.
|
|
86
|
+
#
|
|
87
|
+
# @param assessment [Hash{Symbol => Object}] Result of the relevance check.
|
|
88
|
+
# @return [void]
|
|
89
|
+
def report_assessment(assessment)
|
|
90
|
+
Subprocesses::ReportAssessment.run(assessment: assessment)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
module AssessDocumentationRelevance
|
|
5
|
+
module Subprocesses
|
|
6
|
+
# Determines if documentation is outdated based on digest mismatches.
|
|
7
|
+
#
|
|
8
|
+
# This subprocess receives a mapping of documentation files to the source files
|
|
9
|
+
# they describe, and compares that with a list of source files that have changed.
|
|
10
|
+
# If any documentation file references at least one changed file, it's considered outdated.
|
|
11
|
+
class AssessRelevance
|
|
12
|
+
# Assesses which documentation files are outdated based on file digest mismatches.
|
|
13
|
+
#
|
|
14
|
+
# @param tracked_files [Hash{String => Array<String>}] Mapping of documentation file paths to the source files they reference.
|
|
15
|
+
# @param mismatches [Array<String>] List of source files whose digests have changed.
|
|
16
|
+
#
|
|
17
|
+
# @return [Hash{Symbol => Object}]
|
|
18
|
+
# - :relevant [Boolean] `true` if any documentation files are affected.
|
|
19
|
+
# - :mismatches [Hash{String => Array<String>}] Mapping of documentation files to the list of changed source files they reference.
|
|
20
|
+
def self.run(tracked_files: {}, mismatches: [])
|
|
21
|
+
documentation_mismatches = tracked_files.each_with_object({}) do |(documentation_file, source_files), acc|
|
|
22
|
+
changed = source_files.select { |file| mismatches.include?(file) }
|
|
23
|
+
acc[documentation_file] = changed unless changed.empty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
{
|
|
27
|
+
relevant: documentation_mismatches.any?,
|
|
28
|
+
mismatches: documentation_mismatches
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
module AssessDocumentationRelevance
|
|
5
|
+
module Subprocesses
|
|
6
|
+
# Compares stored and current file digests per documentation file
|
|
7
|
+
# to detect which documentation files may now be outdated.
|
|
8
|
+
class CompareDigests
|
|
9
|
+
# Returns a list of documentation files where any tracked source file changed.
|
|
10
|
+
#
|
|
11
|
+
# @param stored_digests [Hash<String, Hash<String, String>>] Currently mapping of documentation files to tracked source files
|
|
12
|
+
# and their SHA256 digests.
|
|
13
|
+
# @param current_digests [Hash<String, Hash<String, String?>>] New mapping of documentation files to tracked source files
|
|
14
|
+
# and their SHA256 digests (or nil if missing).
|
|
15
|
+
# @return [Array<String>] List of file paths with mismatched or missing digests.
|
|
16
|
+
def self.run(stored_digests: {}, current_digests: {})
|
|
17
|
+
(stored_digests.keys | current_digests.keys).each_with_object([]) do |documentation_file, mismatches|
|
|
18
|
+
stored = stored_digests[documentation_file] || {}
|
|
19
|
+
current = current_digests[documentation_file] || {}
|
|
20
|
+
|
|
21
|
+
(stored.keys | current.keys).each do |source_file|
|
|
22
|
+
stored[source_file] != current[source_file] ? mismatches.push(source_file) : next
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
module AssessDocumentationRelevance
|
|
5
|
+
module Subprocesses
|
|
6
|
+
# Loads previously stored digests from a JSON file.
|
|
7
|
+
class LoadStoredDigests
|
|
8
|
+
# Runs the subprocess to load stored digests from the configured file path.
|
|
9
|
+
#
|
|
10
|
+
# @param config [DocGuard::Config] The configuration containing the path to the digests store.
|
|
11
|
+
# @return [Hash<String, Hash<String, String>>] Mapping of documentation files to
|
|
12
|
+
# tracked file digests (e.g., { "docs/file.md" => { "app/file.rb" => "digest" } }),
|
|
13
|
+
# or an empty hash if the file does not exist.
|
|
14
|
+
def self.run(config: ::DocGuard::Config.new)
|
|
15
|
+
return {} unless File.exist?(config.digests_store_file_path)
|
|
16
|
+
|
|
17
|
+
JSON.parse(File.read(config.digests_store_file_path))
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
module AssessDocumentationRelevance
|
|
5
|
+
module Subprocesses
|
|
6
|
+
# Reports the result of the documentation relevance assessment.
|
|
7
|
+
class ReportAssessment
|
|
8
|
+
# Outputs the result and exits if documentation is outdated.
|
|
9
|
+
#
|
|
10
|
+
# @param assessment [Hash{Symbol => Object}]
|
|
11
|
+
# - :relevant [Boolean] Whether documentation is outdated.
|
|
12
|
+
# - :mismatches [Hash<String, Array<String>>] Mapping of documentation files to lists of tracked source file paths.
|
|
13
|
+
# @return [void]
|
|
14
|
+
def self.run(assessment: {})
|
|
15
|
+
if assessment[:relevant]
|
|
16
|
+
warn "‼️ Documentation may be outdated!"
|
|
17
|
+
|
|
18
|
+
assessment[:mismatches].each do |documentation_file, changed_files|
|
|
19
|
+
warn "\n📄 #{documentation_file}"
|
|
20
|
+
changed_files.each do |file|
|
|
21
|
+
warn "- #{file}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
exit(1)
|
|
26
|
+
else
|
|
27
|
+
puts "✅ Documentation is up to date."
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
# Command-line interface for DocGuard.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# doc_guard assess --documentation-path=docs --digests-store-file-path=.digests.json
|
|
8
|
+
# doc_guard record
|
|
9
|
+
#
|
|
10
|
+
class Cli < Thor
|
|
11
|
+
class_option :documentation_path, type: :string, desc: "Path to documentation files"
|
|
12
|
+
class_option :digests_store_file_path, type: :string, desc: "Path to the digests store file"
|
|
13
|
+
class_option :config_file_path, type: :string, default: ::DocGuard::Config::DEFAULT_CONFIG_FILE_PATH, desc: "Path to the config file"
|
|
14
|
+
|
|
15
|
+
desc "assess", "Assess documentation relevance"
|
|
16
|
+
# @option options [String] :documentation_path Path to documentation files
|
|
17
|
+
# @option options [String] :digests_store_file_path Path to digests file
|
|
18
|
+
# @option options [String] :config_file_path Path to config file
|
|
19
|
+
# @exitstatus 0 if documentation is up to date
|
|
20
|
+
# @exitstatus 1 if outdated
|
|
21
|
+
# @exitstatus 2 on errors
|
|
22
|
+
def assess
|
|
23
|
+
::DocGuard::AssessDocumentationRelevance::Process.run(config: build_config_from_options)
|
|
24
|
+
rescue SystemExit => e
|
|
25
|
+
exit e.status
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
warn "‼️ Error: #{e.message}"
|
|
28
|
+
exit 2
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
desc "record", "Record current documentation state"
|
|
32
|
+
# @option options [String] :documentation_path Path to documentation files
|
|
33
|
+
# @option options [String] :digests_store_file_path Path to digests file
|
|
34
|
+
# @option options [String] :config_file_path Path to config file
|
|
35
|
+
# @exitstatus 0 if success
|
|
36
|
+
# @exitstatus 1 if failure (e.g. file error)
|
|
37
|
+
# @exitstatus 2 on errors
|
|
38
|
+
def record
|
|
39
|
+
::DocGuard::RecordDocumentationRelevance::Process.run(config: build_config_from_options)
|
|
40
|
+
rescue SystemExit => e
|
|
41
|
+
exit e.status
|
|
42
|
+
rescue StandardError => e
|
|
43
|
+
warn "‼️ Error: #{e.message}"
|
|
44
|
+
exit 2
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# Builds a configuration object from CLI options.
|
|
50
|
+
#
|
|
51
|
+
# @return [DocGuard::Config] The configuration instance initialized with CLI options or defaults.
|
|
52
|
+
def build_config_from_options
|
|
53
|
+
::DocGuard::Config.new(
|
|
54
|
+
documentation_path: options[:documentation_path],
|
|
55
|
+
digests_store_file_path: options[:digests_store_file_path],
|
|
56
|
+
config_file_path: options[:config_file_path]
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
# Handles configuration for DocGuard by loading values from:
|
|
5
|
+
# - Explicit parameters (highest priority)
|
|
6
|
+
# - Environment variables
|
|
7
|
+
# - YAML config file (.doc_guard.yml)
|
|
8
|
+
# - Internal defaults (lowest priority)
|
|
9
|
+
#
|
|
10
|
+
# This class supports usage in CLI, CI/CD pipelines, or manual scripts
|
|
11
|
+
# without requiring any Rails integration.
|
|
12
|
+
class Config
|
|
13
|
+
DEFAULT_CONFIG_FILE_PATH = ".doc_guard.yml"
|
|
14
|
+
DEFAULT_DIGESTS_STORE_FILE_PATH = ".doc_guard_digests.json"
|
|
15
|
+
DEFAULT_DOCUMENTATION_PATH = "docs"
|
|
16
|
+
|
|
17
|
+
# Initialize configuration for DocGuard.
|
|
18
|
+
#
|
|
19
|
+
# You can pass `nil` to `digests_store_file_path` or `documentation_path` to allow fallback to ENV, config file, or defaults.
|
|
20
|
+
#
|
|
21
|
+
# @param digests_store_file_path [String, nil] Optional override path for storing digests. Defaults to `.doc_guard_digests.json`.
|
|
22
|
+
# @param documentation_path [String, nil] Optional override path to the documentation folder. Defaults to `docs`.
|
|
23
|
+
# @param config_file_path [String, nil] Optional override path to the config file (YAML). Defaults to `.doc_guard.yml`.
|
|
24
|
+
#
|
|
25
|
+
# @example Basic usage
|
|
26
|
+
# DocGuard::Config.new(
|
|
27
|
+
# digests_store_file_path: nil,
|
|
28
|
+
# documentation_path: nil
|
|
29
|
+
# )
|
|
30
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
31
|
+
def initialize(digests_store_file_path: nil, documentation_path: nil, config_file_path: nil)
|
|
32
|
+
@config_file_path =
|
|
33
|
+
config_file_path ||
|
|
34
|
+
ENV["DOC_GUARD_CONFIG_FILE_PATH"] ||
|
|
35
|
+
DEFAULT_CONFIG_FILE_PATH
|
|
36
|
+
|
|
37
|
+
config = load_config_file(@config_file_path)
|
|
38
|
+
|
|
39
|
+
@documentation_path =
|
|
40
|
+
documentation_path ||
|
|
41
|
+
ENV["DOC_GUARD_DOCUMENTATION_PATH"] ||
|
|
42
|
+
config["documentation_path"] ||
|
|
43
|
+
DEFAULT_DOCUMENTATION_PATH
|
|
44
|
+
|
|
45
|
+
@digests_store_file_path =
|
|
46
|
+
digests_store_file_path ||
|
|
47
|
+
ENV["DOC_GUARD_DIGESTS_STORE_FILE_PATH"] ||
|
|
48
|
+
config["digests_store_file_path"] ||
|
|
49
|
+
DEFAULT_DIGESTS_STORE_FILE_PATH
|
|
50
|
+
end
|
|
51
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
52
|
+
|
|
53
|
+
# @return [String] Resolved documentation path.
|
|
54
|
+
attr_reader :documentation_path
|
|
55
|
+
|
|
56
|
+
# @return [String] Resolved path to the digests JSON file.
|
|
57
|
+
attr_reader :digests_store_file_path
|
|
58
|
+
|
|
59
|
+
# @return [String] Resolved path to the digests JSON file.
|
|
60
|
+
attr_reader :config_file_path
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# Loads a YAML config file if it exists, otherwise returns an empty hash.
|
|
65
|
+
#
|
|
66
|
+
# @param path [String] Absolute or relative path to the YAML config file.
|
|
67
|
+
# @return [Hash{String => String}] Parsed YAML contents or empty hash.
|
|
68
|
+
def load_config_file(config_file_path)
|
|
69
|
+
if File.exist?(config_file_path)
|
|
70
|
+
YAML.load_file(config_file_path) || {}
|
|
71
|
+
else
|
|
72
|
+
{}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
module RecordDocumentationRelevance
|
|
5
|
+
# Main orchestration class that records the current documentation relevance state
|
|
6
|
+
# by capturing the latest digests of files referenced in the documentation.
|
|
7
|
+
class Process
|
|
8
|
+
# Entry point to run the relevance recording process.
|
|
9
|
+
#
|
|
10
|
+
# @param config [DocGuard::Config] Optional configuration.
|
|
11
|
+
# @return [void]
|
|
12
|
+
def self.run(config: ::DocGuard::Config.new)
|
|
13
|
+
new(config: config).call
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Initializes the process with the given configuration.
|
|
17
|
+
#
|
|
18
|
+
# @param config [DocGuard::Config]
|
|
19
|
+
def initialize(config: ::DocGuard::Config.new)
|
|
20
|
+
@config = config
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Executes the full relevance recording pipeline.
|
|
24
|
+
#
|
|
25
|
+
# @return [void]
|
|
26
|
+
def call
|
|
27
|
+
tracked_files = load_tracked_files
|
|
28
|
+
current_digests = calculate_current_digests(tracked_files)
|
|
29
|
+
recording = record_digests(current_digests)
|
|
30
|
+
|
|
31
|
+
report_recording(recording)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
attr_reader :config
|
|
37
|
+
|
|
38
|
+
# Loads files tracked from documentation annotations.
|
|
39
|
+
#
|
|
40
|
+
# @return [Hash{String => Array<String>}] Mapping of documentation files to lists of tracked source file paths.
|
|
41
|
+
def load_tracked_files
|
|
42
|
+
::DocGuard::Shared::Subprocesses::LoadTrackedFilesFromDocumentation.run(config: config)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Calculates current digests of tracked files.
|
|
46
|
+
#
|
|
47
|
+
# @param tracked_files [Hash<String, Array<String>>] Mapping of documentation files to lists of tracked source file paths.
|
|
48
|
+
# @return [Hash<String, Hash<String, String?>>] Mapping of documentation files to tracked source files
|
|
49
|
+
# and their SHA256 digests (or nil if missing).
|
|
50
|
+
def calculate_current_digests(tracked_files)
|
|
51
|
+
::DocGuard::Shared::Subprocesses::CalculateCurrentDigests.run(tracked_files: tracked_files)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Records the given digests by saving them to the configured file path.
|
|
55
|
+
#
|
|
56
|
+
# @param digests [Hash<String, Hash<String, String?>>] Mapping of documentation files to tracked source files
|
|
57
|
+
# and their SHA256 digests (or nil if missing).
|
|
58
|
+
# @return [Hash{Symbol => Object}] The result hash from the RecordDigests subprocess,
|
|
59
|
+
# containing :status and optionally :error keys.
|
|
60
|
+
def record_digests(digests)
|
|
61
|
+
Subprocesses::RecordDigests.run(digests: digests, config: config)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Outputs result message to the user.
|
|
65
|
+
#
|
|
66
|
+
# @param digests [Hash<String, String>] A map of file paths to their digest strings (current).
|
|
67
|
+
# @return [void]
|
|
68
|
+
def report_recording(recording)
|
|
69
|
+
Subprocesses::ReportRecording.run(recording: recording)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module DocGuard
|
|
6
|
+
module RecordDocumentationRelevance
|
|
7
|
+
module Subprocesses
|
|
8
|
+
# Records the current file digests into a configured file path.
|
|
9
|
+
class RecordDigests
|
|
10
|
+
# Writes the given digests as pretty-printed JSON to the configured file path.
|
|
11
|
+
#
|
|
12
|
+
# @param digests [Hash<String, Hash<String, String?>>] Mapping of documentation files to tracked source files
|
|
13
|
+
# and their SHA256 digests (or nil if missing).
|
|
14
|
+
# @param config [DocGuard::Config] Configuration object with `digests_store_file_path`.
|
|
15
|
+
# @return [Hash{Symbol => Symbol, Object}] Status result:
|
|
16
|
+
# - On success: { status: :ok }
|
|
17
|
+
# - On failure: { status: :error, error: error_object }
|
|
18
|
+
def self.run(digests: {}, config: ::DocGuard::Config.new)
|
|
19
|
+
File.write(config.digests_store_file_path, JSON.pretty_generate(digests))
|
|
20
|
+
{ status: :ok }
|
|
21
|
+
rescue StandardError => error
|
|
22
|
+
{ status: :error, error: error }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
module RecordDocumentationRelevance
|
|
5
|
+
module Subprocesses
|
|
6
|
+
# Outputs a message confirming the result of the documentation state recording.
|
|
7
|
+
class ReportRecording
|
|
8
|
+
# Runs the subprocess that reports the result of recording digests.
|
|
9
|
+
#
|
|
10
|
+
# @param recording [Hash{Symbol => Symbol, Object}] Status result:
|
|
11
|
+
# - On success: { status: :ok }
|
|
12
|
+
# - On failure: { status: :error, error: error_object }
|
|
13
|
+
# @return [void]
|
|
14
|
+
def self.run(recording: {})
|
|
15
|
+
case recording[:status]
|
|
16
|
+
when :ok
|
|
17
|
+
puts "✅ Documentation state recorded successfully."
|
|
18
|
+
when :error
|
|
19
|
+
warn "‼️ Failed to record documentation state: #{recording[:error].message}"
|
|
20
|
+
exit(1)
|
|
21
|
+
else
|
|
22
|
+
warn "⚠️ Unknown recording status."
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
module Shared
|
|
5
|
+
module Subprocesses
|
|
6
|
+
# Calculates SHA256 digests for a map of documentation files and their tracked source files.
|
|
7
|
+
#
|
|
8
|
+
# Input:
|
|
9
|
+
# {
|
|
10
|
+
# "docs/user.md" => ["app/models/user.rb", "app/services/authenticator.rb"],
|
|
11
|
+
# "docs/admin.md" => ["app/models/admin.rb"]
|
|
12
|
+
# }
|
|
13
|
+
#
|
|
14
|
+
# Output:
|
|
15
|
+
# {
|
|
16
|
+
# "docs/user.md" => {
|
|
17
|
+
# "app/models/user.rb" => "abcd1234...",
|
|
18
|
+
# "app/services/authenticator.rb" => "efgh5678..."
|
|
19
|
+
# },
|
|
20
|
+
# "docs/admin.md" => {
|
|
21
|
+
# "app/models/admin.rb" => nil # file missing
|
|
22
|
+
# }
|
|
23
|
+
# }
|
|
24
|
+
class CalculateCurrentDigests
|
|
25
|
+
# Runs the subprocess to calculate SHA256 digests per documentation file and its tracked source files.
|
|
26
|
+
#
|
|
27
|
+
# @param tracked_files [Hash<String, Array<String>>] Mapping of documentation files to lists of tracked source file paths.
|
|
28
|
+
# @return [Hash<String, Hash<String, String?>>] Mapping of documentation files to tracked source files
|
|
29
|
+
# and their SHA256 digests (or nil if missing).
|
|
30
|
+
def self.run(tracked_files: {})
|
|
31
|
+
tracked_files.transform_values do |source_files|
|
|
32
|
+
source_files.each_with_object({}) do |file, digest_map|
|
|
33
|
+
digest_map[file] = File.exist?(file) ? Digest::SHA256.hexdigest(File.read(file)) : nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocGuard
|
|
4
|
+
module Shared
|
|
5
|
+
module Subprocesses
|
|
6
|
+
# Loads a mapping between documentation files and the files they track.
|
|
7
|
+
#
|
|
8
|
+
# Scans all `.md` files under the configured documentation path,
|
|
9
|
+
# and builds a map of:
|
|
10
|
+
# documentation_file_path => [list of tracked source file paths]
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
# {
|
|
14
|
+
# "docs/user.md" => ["app/models/user.rb", "app/services/authenticator.rb"],
|
|
15
|
+
# "docs/admin.md" => ["app/models/admin.rb"]
|
|
16
|
+
# }
|
|
17
|
+
class LoadTrackedFilesFromDocumentation
|
|
18
|
+
TAG_PATTERN = /<!--\s*doc_guard\s+files:\s*\[(?<files_to_track>[\s\S]*?)\]\s*-->/
|
|
19
|
+
|
|
20
|
+
# Builds a hash mapping documentation files to the files they track.
|
|
21
|
+
#
|
|
22
|
+
# @param config [DocGuard::Config]
|
|
23
|
+
# @return [Hash{String => Array<String>}] Mapping of documentation files to lists of tracked source file paths.
|
|
24
|
+
def self.run(config: ::DocGuard::Config.new)
|
|
25
|
+
mapping = {}
|
|
26
|
+
|
|
27
|
+
Dir.glob("#{config.documentation_path}/**/*.md") do |documentation_file|
|
|
28
|
+
tracked = []
|
|
29
|
+
|
|
30
|
+
content = File.read(documentation_file)
|
|
31
|
+
content.scan(TAG_PATTERN).each do |match|
|
|
32
|
+
files = match[0].split(",").map(&:strip)
|
|
33
|
+
tracked.concat(files)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
mapping[documentation_file] = tracked.uniq unless tracked.empty?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
mapping
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/doc_guard.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
require "digest"
|
|
5
|
+
require "json"
|
|
6
|
+
require "thor"
|
|
7
|
+
require "yaml"
|
|
8
|
+
|
|
9
|
+
# Loads all Ruby files under the DocGuard directory.
|
|
10
|
+
Zeitwerk::Loader.new.tap do |loader|
|
|
11
|
+
loader.push_dir(File.join(__dir__))
|
|
12
|
+
loader.setup
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Top-level module for DocGuard gem functionality.
|
|
16
|
+
#
|
|
17
|
+
# @example Raise a custom DocGuard error
|
|
18
|
+
# raise DocGuard::Error, "something went wrong"
|
|
19
|
+
module DocGuard
|
|
20
|
+
# Base error class for DocGuard-related exceptions.
|
|
21
|
+
class Error < StandardError; end
|
|
22
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: doc_guard
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Patryk Gramatowski
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-07-04 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: digest
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: thor
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.3'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.3'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: zeitwerk
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '2.6'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.6'
|
|
55
|
+
description: |
|
|
56
|
+
DocGuard helps maintain up-to-date project documentation by tracking files referenced in your docs,
|
|
57
|
+
calculating file digests, and assessing whether code changes impact your documentation relevance.
|
|
58
|
+
It provides CLI commands to assess documentation relevance and record the current documentation state,
|
|
59
|
+
enabling automated enforcement and better documentation quality in Rails and Ruby projects.
|
|
60
|
+
email:
|
|
61
|
+
- patrick.gramatowski@gmail.com
|
|
62
|
+
executables:
|
|
63
|
+
- doc_guard
|
|
64
|
+
extensions: []
|
|
65
|
+
extra_rdoc_files: []
|
|
66
|
+
files:
|
|
67
|
+
- CHANGELOG.md
|
|
68
|
+
- LICENSE.txt
|
|
69
|
+
- README.md
|
|
70
|
+
- bin/doc_guard
|
|
71
|
+
- doc_guard.gemspec
|
|
72
|
+
- lib/doc_guard.rb
|
|
73
|
+
- lib/doc_guard/assess_documentation_relevance/process.rb
|
|
74
|
+
- lib/doc_guard/assess_documentation_relevance/subprocesses/assess_relevance.rb
|
|
75
|
+
- lib/doc_guard/assess_documentation_relevance/subprocesses/compare_digests.rb
|
|
76
|
+
- lib/doc_guard/assess_documentation_relevance/subprocesses/load_stored_digests.rb
|
|
77
|
+
- lib/doc_guard/assess_documentation_relevance/subprocesses/report_assessment.rb
|
|
78
|
+
- lib/doc_guard/cli.rb
|
|
79
|
+
- lib/doc_guard/config.rb
|
|
80
|
+
- lib/doc_guard/record_documentation_relevance/process.rb
|
|
81
|
+
- lib/doc_guard/record_documentation_relevance/subprocesses/record_digests.rb
|
|
82
|
+
- lib/doc_guard/record_documentation_relevance/subprocesses/report_recording.rb
|
|
83
|
+
- lib/doc_guard/shared/subprocesses/calculate_current_digests.rb
|
|
84
|
+
- lib/doc_guard/shared/subprocesses/load_tracked_files_from_documentation.rb
|
|
85
|
+
- lib/doc_guard/version.rb
|
|
86
|
+
homepage: https://github.com/patrickgramatowski/doc_guard
|
|
87
|
+
licenses:
|
|
88
|
+
- MIT
|
|
89
|
+
metadata:
|
|
90
|
+
allowed_push_host: https://rubygems.org
|
|
91
|
+
rubygems_mfa_required: 'true'
|
|
92
|
+
homepage_uri: https://github.com/patrickgramatowski/doc_guard
|
|
93
|
+
source_code_uri: https://github.com/patrickgramatowski/doc_guard
|
|
94
|
+
changelog_uri: https://github.com/patrickgramatowski/doc_guard/CHANGELOG.md
|
|
95
|
+
post_install_message: "=> ✅ Setup complete! You can now use `doc_guard` on the command
|
|
96
|
+
line."
|
|
97
|
+
rdoc_options: []
|
|
98
|
+
require_paths:
|
|
99
|
+
- lib
|
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - ">="
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '3.2'
|
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
requirements: []
|
|
111
|
+
rubygems_version: 3.4.10
|
|
112
|
+
signing_key:
|
|
113
|
+
specification_version: 4
|
|
114
|
+
summary: DocGuard helps maintain up-to-date project documentation by tracking files
|
|
115
|
+
referenced in your docs.
|
|
116
|
+
test_files: []
|