mkdocs-decision-records 1.0.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.
- mkdocs_decision_records-1.0.0/LICENSE +21 -0
- mkdocs_decision_records-1.0.0/PKG-INFO +100 -0
- mkdocs_decision_records-1.0.0/README.md +86 -0
- mkdocs_decision_records-1.0.0/mkdocs_decision_records/__init__.py +0 -0
- mkdocs_decision_records-1.0.0/mkdocs_decision_records/_markdown_utils.py +15 -0
- mkdocs_decision_records-1.0.0/mkdocs_decision_records/plugin.py +127 -0
- mkdocs_decision_records-1.0.0/pyproject.toml +19 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Timo Reymann
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: mkdocs-decision-records
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Manage decision records with mkdocs in a customizable and minimal fashion.
|
|
5
|
+
Author: Timo Reymann
|
|
6
|
+
Author-email: mail@timo-reymann.de
|
|
7
|
+
Requires-Python: >=3.12,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Requires-Dist: mkdocs (>=1.6.1,<2.0.0)
|
|
12
|
+
Requires-Dist: mkdocs-material (>=9.5.48,<10.0.0)
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
mkdocs-decision-records
|
|
16
|
+
==
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img width="600" src="https://raw.githubusercontent.com/timo-reymann/mkdocs-decision-records/main/.github/images/demo.png">
|
|
20
|
+
<br />
|
|
21
|
+
Manage decision records with mkdocs in a customizable and minimal fashion.
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- Customizable status colors and lifecycle
|
|
29
|
+
- Enforces information to be present for ADRs
|
|
30
|
+
- Allows description being kept as markdown
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
1. Install `mkdocs-decision-records` from the PyPi registry using your favorite package manager
|
|
35
|
+
2. Configure your `mkdocs.yml`
|
|
36
|
+
```yaml
|
|
37
|
+
plugins:
|
|
38
|
+
- decision-records:
|
|
39
|
+
# Folder where your decision records are located, defaults to adr
|
|
40
|
+
decisions_folder: adr
|
|
41
|
+
# Optional prefix to prepend to ticket numbers
|
|
42
|
+
ticket_url_prefix: https://ticket.example.com/
|
|
43
|
+
# Configure amount of required deciders
|
|
44
|
+
required_deciders_count: 1
|
|
45
|
+
# Configure available stages and the badge colors
|
|
46
|
+
lifecycle_stages:
|
|
47
|
+
{status}: {color}
|
|
48
|
+
```
|
|
49
|
+
3. Create your ADRs ensuring to add the frontmatter meta data:
|
|
50
|
+
```markdown
|
|
51
|
+
---
|
|
52
|
+
id: 000
|
|
53
|
+
status: proposed | rejected | accepted | deprecated | … | superseded by
|
|
54
|
+
date: YYYY-MM-DD
|
|
55
|
+
deciders:
|
|
56
|
+
- decider 1
|
|
57
|
+
- decider 2
|
|
58
|
+
# Optional ticket
|
|
59
|
+
ticket: FOO-1
|
|
60
|
+
---
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Motivation
|
|
64
|
+
|
|
65
|
+
I love ADRs and documenting decisions in general. This plugin makes it a bit easier, enforcing basic meta information
|
|
66
|
+
while keeping the format open enough so you can do your thing.
|
|
67
|
+
|
|
68
|
+
## Contributing
|
|
69
|
+
|
|
70
|
+
I love your input! I want to make contributing to this project as easy and transparent as possible, whether it's:
|
|
71
|
+
|
|
72
|
+
- Reporting a bug
|
|
73
|
+
- Discussing the current state of the configuration
|
|
74
|
+
- Submitting a fix
|
|
75
|
+
- Proposing new features
|
|
76
|
+
- Becoming a maintainer
|
|
77
|
+
|
|
78
|
+
To get started please read the [Contribution Guidelines](./CONTRIBUTING.md).
|
|
79
|
+
|
|
80
|
+
## Development
|
|
81
|
+
|
|
82
|
+
### Requirements
|
|
83
|
+
|
|
84
|
+
- Python 3.12+
|
|
85
|
+
- Poetry
|
|
86
|
+
|
|
87
|
+
### Build
|
|
88
|
+
|
|
89
|
+
````sh
|
|
90
|
+
poetry install
|
|
91
|
+
````
|
|
92
|
+
|
|
93
|
+
### Alternatives
|
|
94
|
+
|
|
95
|
+
- [mkdocs-material](https://pypi.org/project/mkdocs-material-adr/)
|
|
96
|
+
- Needs to use the theme
|
|
97
|
+
- ADR graph
|
|
98
|
+
- [mkdocs-macros-adr-summary](https://github.com/febus982/mkdocs-macros-adr-summary)
|
|
99
|
+
- works entirely with macros
|
|
100
|
+
- no metadata table at the top
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
mkdocs-decision-records
|
|
2
|
+
==
|
|
3
|
+
|
|
4
|
+
<p align="center">
|
|
5
|
+
<img width="600" src="https://raw.githubusercontent.com/timo-reymann/mkdocs-decision-records/main/.github/images/demo.png">
|
|
6
|
+
<br />
|
|
7
|
+
Manage decision records with mkdocs in a customizable and minimal fashion.
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- Customizable status colors and lifecycle
|
|
15
|
+
- Enforces information to be present for ADRs
|
|
16
|
+
- Allows description being kept as markdown
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
1. Install `mkdocs-decision-records` from the PyPi registry using your favorite package manager
|
|
21
|
+
2. Configure your `mkdocs.yml`
|
|
22
|
+
```yaml
|
|
23
|
+
plugins:
|
|
24
|
+
- decision-records:
|
|
25
|
+
# Folder where your decision records are located, defaults to adr
|
|
26
|
+
decisions_folder: adr
|
|
27
|
+
# Optional prefix to prepend to ticket numbers
|
|
28
|
+
ticket_url_prefix: https://ticket.example.com/
|
|
29
|
+
# Configure amount of required deciders
|
|
30
|
+
required_deciders_count: 1
|
|
31
|
+
# Configure available stages and the badge colors
|
|
32
|
+
lifecycle_stages:
|
|
33
|
+
{status}: {color}
|
|
34
|
+
```
|
|
35
|
+
3. Create your ADRs ensuring to add the frontmatter meta data:
|
|
36
|
+
```markdown
|
|
37
|
+
---
|
|
38
|
+
id: 000
|
|
39
|
+
status: proposed | rejected | accepted | deprecated | … | superseded by
|
|
40
|
+
date: YYYY-MM-DD
|
|
41
|
+
deciders:
|
|
42
|
+
- decider 1
|
|
43
|
+
- decider 2
|
|
44
|
+
# Optional ticket
|
|
45
|
+
ticket: FOO-1
|
|
46
|
+
---
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Motivation
|
|
50
|
+
|
|
51
|
+
I love ADRs and documenting decisions in general. This plugin makes it a bit easier, enforcing basic meta information
|
|
52
|
+
while keeping the format open enough so you can do your thing.
|
|
53
|
+
|
|
54
|
+
## Contributing
|
|
55
|
+
|
|
56
|
+
I love your input! I want to make contributing to this project as easy and transparent as possible, whether it's:
|
|
57
|
+
|
|
58
|
+
- Reporting a bug
|
|
59
|
+
- Discussing the current state of the configuration
|
|
60
|
+
- Submitting a fix
|
|
61
|
+
- Proposing new features
|
|
62
|
+
- Becoming a maintainer
|
|
63
|
+
|
|
64
|
+
To get started please read the [Contribution Guidelines](./CONTRIBUTING.md).
|
|
65
|
+
|
|
66
|
+
## Development
|
|
67
|
+
|
|
68
|
+
### Requirements
|
|
69
|
+
|
|
70
|
+
- Python 3.12+
|
|
71
|
+
- Poetry
|
|
72
|
+
|
|
73
|
+
### Build
|
|
74
|
+
|
|
75
|
+
````sh
|
|
76
|
+
poetry install
|
|
77
|
+
````
|
|
78
|
+
|
|
79
|
+
### Alternatives
|
|
80
|
+
|
|
81
|
+
- [mkdocs-material](https://pypi.org/project/mkdocs-material-adr/)
|
|
82
|
+
- Needs to use the theme
|
|
83
|
+
- ADR graph
|
|
84
|
+
- [mkdocs-macros-adr-summary](https://github.com/febus982/mkdocs-macros-adr-summary)
|
|
85
|
+
- works entirely with macros
|
|
86
|
+
- no metadata table at the top
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _meta_table(items: list[tuple[str, str]]) -> Generator[str, None, None]:
|
|
5
|
+
yield "<table>"
|
|
6
|
+
for (h, v) in items:
|
|
7
|
+
yield f"<tr><td><strong>{h}</strong></td><td>{v}</td></tr>"
|
|
8
|
+
yield "</table>"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _list(items: list[str]) -> Generator[str]:
|
|
12
|
+
yield "<ul>"
|
|
13
|
+
for item in items:
|
|
14
|
+
yield f"<li>{item}</li>"
|
|
15
|
+
yield "</ul>"
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from mkdocs.config import config_options
|
|
4
|
+
from mkdocs.config.defaults import MkDocsConfig
|
|
5
|
+
from mkdocs.exceptions import PluginError
|
|
6
|
+
from mkdocs.plugins import BasePlugin
|
|
7
|
+
from mkdocs.structure.files import Files
|
|
8
|
+
from mkdocs.structure.pages import Page
|
|
9
|
+
|
|
10
|
+
from mkdocs_decision_records._markdown_utils import _list, _meta_table
|
|
11
|
+
|
|
12
|
+
CONFIG_DECISIONS_FOLDER_KEY = "decisions_folder"
|
|
13
|
+
CONFIG_TICKET_URL_PREFIX = "ticket_url_prefix"
|
|
14
|
+
|
|
15
|
+
CONFIG_LIFECYCLE_COLORS_KEY = "lifecycle_stages"
|
|
16
|
+
CONFIG_DECISIONS_FOLDER_DEFAULT = "adr"
|
|
17
|
+
|
|
18
|
+
CONFIG_REQUIRED_DECIDERS_COUNT_KEY = "required_deciders_count"
|
|
19
|
+
CONFIG_REQUIRED_DECIDERS_COUNT_DEFAULT = 1
|
|
20
|
+
|
|
21
|
+
DR_TITLE_WITH_NUM = re.compile(r'\d{3,}.*')
|
|
22
|
+
|
|
23
|
+
DEFAULT_LIFECYCLE_COLORS = {
|
|
24
|
+
"accepted": "#28a745",
|
|
25
|
+
"proposed": "gray",
|
|
26
|
+
"rejected": "#dc3545",
|
|
27
|
+
"deprecated": "#6c757d",
|
|
28
|
+
"superseded": "#17a2b8",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class InvalidMetaDataError(PluginError):
|
|
33
|
+
def __init__(self, page: Page, field: str, message: str):
|
|
34
|
+
self.field = field
|
|
35
|
+
self.raw_message = message
|
|
36
|
+
self.message = f"Invalid metadata for field '{field}' in {page.file.src_path}: {message}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _require_meta(page: Page, field: str) -> any:
|
|
40
|
+
val = page.meta.get(field, None)
|
|
41
|
+
if val is None:
|
|
42
|
+
raise InvalidMetaDataError(page, field, "Required, but not set.")
|
|
43
|
+
return val
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class DecisionRecordsPlugin(BasePlugin):
|
|
47
|
+
config_scheme = (
|
|
48
|
+
(CONFIG_DECISIONS_FOLDER_KEY, config_options.Type(str, default=CONFIG_DECISIONS_FOLDER_DEFAULT)),
|
|
49
|
+
(CONFIG_TICKET_URL_PREFIX, config_options.Type(str, default=None)),
|
|
50
|
+
(CONFIG_LIFECYCLE_COLORS_KEY, config_options.Type(dict, default=DEFAULT_LIFECYCLE_COLORS)),
|
|
51
|
+
(CONFIG_REQUIRED_DECIDERS_COUNT_KEY, config_options.Type(int, default=CONFIG_REQUIRED_DECIDERS_COUNT_DEFAULT))
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def on_page_markdown(self, markdown: str, page: Page, config: MkDocsConfig, files: Files):
|
|
55
|
+
if not page.file.src_path.startswith(self.config.get(CONFIG_DECISIONS_FOLDER_KEY)):
|
|
56
|
+
return markdown
|
|
57
|
+
|
|
58
|
+
title = page.meta.get("title", None) or page.title
|
|
59
|
+
dr_id = _require_meta(page, "id")
|
|
60
|
+
|
|
61
|
+
if dr_id == 0:
|
|
62
|
+
page.title = "000 - Template"
|
|
63
|
+
return markdown
|
|
64
|
+
|
|
65
|
+
meta = [
|
|
66
|
+
("Status", self._create_status_badge(page)),
|
|
67
|
+
("Date", _require_meta(page, "date")),
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
deciders = page.meta.get("deciders", [])
|
|
71
|
+
if len(deciders) < self.required_deciders_count:
|
|
72
|
+
raise InvalidMetaDataError(page, "deciders",
|
|
73
|
+
f"At least {self.required_deciders_count} deciders are required for a decision")
|
|
74
|
+
elif len(deciders) > 0:
|
|
75
|
+
meta.append((
|
|
76
|
+
"Deciders" if len(deciders) > 1 else "Decider",
|
|
77
|
+
"\n".join(_list(deciders)) if len(deciders) > 1 else deciders[0],
|
|
78
|
+
))
|
|
79
|
+
|
|
80
|
+
ticket = page.meta.get("ticket", None)
|
|
81
|
+
if ticket is not None:
|
|
82
|
+
if self.config.get(CONFIG_TICKET_URL_PREFIX) is not None:
|
|
83
|
+
ticket_text = f"<a href='{self.config.get(CONFIG_TICKET_URL_PREFIX)}/{ticket}'>{ticket.upper()}</a>"
|
|
84
|
+
else:
|
|
85
|
+
ticket_text = ticket.upper()
|
|
86
|
+
|
|
87
|
+
meta.append((
|
|
88
|
+
"Ticket",
|
|
89
|
+
ticket_text,
|
|
90
|
+
))
|
|
91
|
+
|
|
92
|
+
meta_info = "\n".join(_meta_table(meta))
|
|
93
|
+
|
|
94
|
+
header = title if DR_TITLE_WITH_NUM.match(title) else f"{dr_id:03d} - {title}"
|
|
95
|
+
page.title = header
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
f"{header}\n"
|
|
99
|
+
f"===\n"
|
|
100
|
+
f"{meta_info}\n"
|
|
101
|
+
f"{markdown}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def lifecycles(self) -> dict[str, str]:
|
|
106
|
+
configured_lifecycle_colors = self.config.get(CONFIG_LIFECYCLE_COLORS_KEY, None)
|
|
107
|
+
return {
|
|
108
|
+
**DEFAULT_LIFECYCLE_COLORS,
|
|
109
|
+
**configured_lifecycle_colors,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def required_deciders_count(self):
|
|
114
|
+
return self.config.get(CONFIG_REQUIRED_DECIDERS_COUNT_KEY, CONFIG_REQUIRED_DECIDERS_COUNT_DEFAULT)
|
|
115
|
+
|
|
116
|
+
def _create_status_badge(self, page):
|
|
117
|
+
status = _require_meta(page, "status")
|
|
118
|
+
|
|
119
|
+
status_color = self.lifecycles.get(status, None)
|
|
120
|
+
if status_color is None:
|
|
121
|
+
raise InvalidMetaDataError(page, "status", f"Invalid status {status}")
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
f"<span style='color: white;background:{status_color};padding:.4em;border-radius:8px;font-size:100%;'>"
|
|
125
|
+
f"{status}"
|
|
126
|
+
f"</span>"
|
|
127
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "mkdocs-decision-records"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Manage decision records with mkdocs in a customizable and minimal fashion."
|
|
5
|
+
authors = ["Timo Reymann <mail@timo-reymann.de>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
include = ["mkdocs_decision_records"]
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = "^3.12"
|
|
11
|
+
mkdocs = "^1.6.1"
|
|
12
|
+
mkdocs-material = "^9.5.48"
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["poetry-core"]
|
|
16
|
+
build-backend = "poetry.core.masonry.api"
|
|
17
|
+
|
|
18
|
+
[tool.poetry.plugins."mkdocs.plugins"]
|
|
19
|
+
decision-records = "mkdocs_decision_records.plugin:DecisionRecordsPlugin"
|