rtfc 0.1.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.
Files changed (46) hide show
  1. rtfc-0.1.0/.github/workflows/ci.yml +130 -0
  2. rtfc-0.1.0/.github/workflows/lint.yml +83 -0
  3. rtfc-0.1.0/.gitignore +220 -0
  4. rtfc-0.1.0/.readthedocs.yml +31 -0
  5. rtfc-0.1.0/DESIGN.md +188 -0
  6. rtfc-0.1.0/LICENSE +21 -0
  7. rtfc-0.1.0/PKG-INFO +122 -0
  8. rtfc-0.1.0/PLAN.md +16 -0
  9. rtfc-0.1.0/README.rst +98 -0
  10. rtfc-0.1.0/changelog/aa9e0244.feature.rtfc +6 -0
  11. rtfc-0.1.0/changelog/entry.rst.jinja +4 -0
  12. rtfc-0.1.0/docs/Makefile +23 -0
  13. rtfc-0.1.0/docs/make.bat +35 -0
  14. rtfc-0.1.0/docs/source/changelog.rst +8 -0
  15. rtfc-0.1.0/docs/source/cli.rst +12 -0
  16. rtfc-0.1.0/docs/source/conf.py +93 -0
  17. rtfc-0.1.0/docs/source/configuration.rst +185 -0
  18. rtfc-0.1.0/docs/source/index.rst +19 -0
  19. rtfc-0.1.0/docs/source/sphinx.rst +54 -0
  20. rtfc-0.1.0/docs/source/usage.rst +89 -0
  21. rtfc-0.1.0/prek.toml +20 -0
  22. rtfc-0.1.0/pyproject.toml +132 -0
  23. rtfc-0.1.0/src/rtfc/__init__.py +5 -0
  24. rtfc-0.1.0/src/rtfc/__main__.py +6 -0
  25. rtfc-0.1.0/src/rtfc/_changelog.py +46 -0
  26. rtfc-0.1.0/src/rtfc/_cli.py +227 -0
  27. rtfc-0.1.0/src/rtfc/_config.py +298 -0
  28. rtfc-0.1.0/src/rtfc/_entry.py +256 -0
  29. rtfc-0.1.0/src/rtfc/_format/__init__.py +39 -0
  30. rtfc-0.1.0/src/rtfc/_format/base.py +59 -0
  31. rtfc-0.1.0/src/rtfc/_format/rst.py +22 -0
  32. rtfc-0.1.0/src/rtfc/_render/__init__.py +15 -0
  33. rtfc-0.1.0/src/rtfc/_render/base.py +98 -0
  34. rtfc-0.1.0/src/rtfc/_render/jinja.py +78 -0
  35. rtfc-0.1.0/src/rtfc/_validation.py +529 -0
  36. rtfc-0.1.0/src/rtfc/py.typed +0 -0
  37. rtfc-0.1.0/src/rtfc/sphinx.py +105 -0
  38. rtfc-0.1.0/tests/test_changelog.py +65 -0
  39. rtfc-0.1.0/tests/test_cli.py +231 -0
  40. rtfc-0.1.0/tests/test_config.py +399 -0
  41. rtfc-0.1.0/tests/test_entry.py +255 -0
  42. rtfc-0.1.0/tests/test_format.py +64 -0
  43. rtfc-0.1.0/tests/test_render.py +259 -0
  44. rtfc-0.1.0/tests/test_sphinx.py +90 -0
  45. rtfc-0.1.0/tests/test_validation.py +397 -0
  46. rtfc-0.1.0/uv.lock +1638 -0
@@ -0,0 +1,130 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ tags:
8
+ - '**'
9
+ pull_request: {}
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ tests:
16
+ name: Run tests with pytest
17
+ runs-on: ubuntu-latest
18
+ strategy:
19
+ matrix:
20
+ python-version: ['3.11', '3.11', '3.12', '3.13', '3.14', '3.15']
21
+ steps:
22
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
23
+ with:
24
+ persist-credentials: false
25
+
26
+ - name: Set up Python ${{ matrix.python-version }}
27
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
28
+ with:
29
+ python-version: ${{ matrix.python-version }}
30
+ enable-cache: true # zizmor: ignore[cache-poisoning] (Job does not produce release artifacts and does not have sensitive permissions)
31
+
32
+ - name: Install tox
33
+ run: uv tool install --managed-python --python 3.13 tox --with tox-uv --with tox-gh
34
+
35
+ - name: Install Python ${{ matrix.python-version }}
36
+ run: uv python install --managed-python ${{ matrix.python-version }}
37
+
38
+ - name: Test with tox
39
+ run: tox run
40
+ env:
41
+ TOX_GH_MAJOR_MINOR: ${{ matrix.python-version }}
42
+ UV_PYTHON_DOWNLOADS: never
43
+
44
+ docs:
45
+ name: Build and check documentation
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
49
+ with:
50
+ persist-credentials: false
51
+
52
+ - name: Set up Python 3.14
53
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
54
+ with:
55
+ python-version: '3.14'
56
+
57
+ - name: Install dependencies
58
+ run: uv sync --group docs
59
+
60
+ - name: Lint docs with sphinx-lint
61
+ run: |
62
+ uv run sphinx-lint docs
63
+
64
+ - name: Build docs and check the integrity of external links
65
+ run: |
66
+ uv run sphinx-build --builder linkcheck --fail-on-warning "source" "build"
67
+ uv run sphinx-build --builder html --fail-on-warning "source" "build"
68
+ working-directory:
69
+ ./docs
70
+
71
+ build:
72
+ name: Build project
73
+ runs-on: ubuntu-latest
74
+ steps:
75
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
76
+ with:
77
+ persist-credentials: false
78
+
79
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
80
+ with:
81
+ enable-cache: false
82
+
83
+ - run: uv sync --no-install-project --only-group build
84
+
85
+ - name: Build library
86
+ run: uv run --no-sync python -m build --installer uv
87
+
88
+ - run: ls -lh dist/
89
+
90
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
91
+ with:
92
+ name: pypi_files
93
+ path: dist
94
+
95
+ release:
96
+ needs: [tests, docs, build]
97
+ runs-on: ubuntu-latest
98
+ if: startsWith(github.ref, 'refs/tags/')
99
+ environment: release
100
+
101
+ permissions:
102
+ id-token: write
103
+ contents: write
104
+
105
+ steps:
106
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
107
+ with:
108
+ persist-credentials: false
109
+
110
+ - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
111
+ with:
112
+ enable-cache: false
113
+
114
+ - run: uv sync --no-install-project --only-group build
115
+
116
+ - name: Get pydantic dist artifacts
117
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
118
+ with:
119
+ name: pypi_files
120
+ path: dist
121
+
122
+ - name: Test artifacts integrity
123
+ run: |
124
+ for whl in dist/*.whl; do unzip -qt "$whl"; done
125
+ uv run --no-sync twine check --strict dist/*
126
+
127
+ - name: Upload package to PyPI
128
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
129
+ with:
130
+ packages-dir: dist/
@@ -0,0 +1,83 @@
1
+ name: lint
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ tags:
8
+ - '**'
9
+ pull_request: {}
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ prek:
16
+ name: Run prek pre-commit hooks
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
+ with:
21
+ persist-credentials: false
22
+
23
+ - uses: j178/prek-action@4e14d07f9231acabce116ccfca13b13dd9755ece # v3.0.0
24
+
25
+ ruff-format:
26
+ name: Check code formatting with Ruff
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
30
+ with:
31
+ persist-credentials: false
32
+
33
+ - name: Set up Python 3.14
34
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
35
+ with:
36
+ python-version: '3.14'
37
+ enable-cache: true # zizmor: ignore[cache-poisoning] (Job does not produce release artifacts and does not have sensitive permissions)
38
+
39
+ - name: Install dependencies
40
+ run: uv sync --group dev
41
+
42
+ - name: Run Ruff formatter
43
+ run: uv run ruff format --diff
44
+
45
+ ruff-check:
46
+ name: Check code linting with Ruff
47
+ runs-on: ubuntu-latest
48
+ steps:
49
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
50
+ with:
51
+ persist-credentials: false
52
+
53
+ - name: Set up Python 3.14
54
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
55
+ with:
56
+ python-version: '3.14'
57
+ enable-cache: true # zizmor: ignore[cache-poisoning] (Job does not produce release artifacts and does not have sensitive permissions)
58
+
59
+ - name: Install dependencies
60
+ run: uv sync --group dev
61
+
62
+ - name: Run Ruff formatter
63
+ run: uv run ruff check --output-format=github
64
+
65
+ typecheck:
66
+ name: Run typechecking with pyright
67
+ runs-on: ubuntu-latest
68
+ steps:
69
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
70
+ with:
71
+ persist-credentials: false
72
+
73
+ - name: Set up Python 3.14
74
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
75
+ with:
76
+ python-version: '3.14'
77
+ enable-cache: true # zizmor: ignore[cache-poisoning] (Job does not produce release artifacts and does not have sensitive permissions)
78
+
79
+ - name: Install dependencies
80
+ run: uv sync --group dev
81
+
82
+ - name: Run pyright
83
+ run: uv run pyright
rtfc-0.1.0/.gitignore ADDED
@@ -0,0 +1,220 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ *.lcov
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ # Pipfile.lock
97
+
98
+ # UV
99
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # uv.lock
103
+
104
+ # poetry
105
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
109
+ # poetry.lock
110
+ # poetry.toml
111
+
112
+ # pdm
113
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
114
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
115
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
116
+ # pdm.lock
117
+ # pdm.toml
118
+ .pdm-python
119
+ .pdm-build/
120
+
121
+ # pixi
122
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
123
+ # pixi.lock
124
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
125
+ # in the .venv directory. It is recommended not to include this directory in version control.
126
+ .pixi/*
127
+ !.pixi/config.toml
128
+
129
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
130
+ __pypackages__/
131
+
132
+ # Celery stuff
133
+ celerybeat-schedule*
134
+ celerybeat.pid
135
+
136
+ # Redis
137
+ *.rdb
138
+ *.aof
139
+ *.pid
140
+
141
+ # RabbitMQ
142
+ mnesia/
143
+ rabbitmq/
144
+ rabbitmq-data/
145
+
146
+ # ActiveMQ
147
+ activemq-data/
148
+
149
+ # SageMath parsed files
150
+ *.sage.py
151
+
152
+ # Environments
153
+ .env
154
+ .envrc
155
+ .venv
156
+ env/
157
+ venv/
158
+ ENV/
159
+ env.bak/
160
+ venv.bak/
161
+
162
+ # Spyder project settings
163
+ .spyderproject
164
+ .spyproject
165
+
166
+ # Rope project settings
167
+ .ropeproject
168
+
169
+ # mkdocs documentation
170
+ /site
171
+
172
+ # mypy
173
+ .mypy_cache/
174
+ .dmypy.json
175
+ dmypy.json
176
+
177
+ # Pyre type checker
178
+ .pyre/
179
+
180
+ # pytype static type analyzer
181
+ .pytype/
182
+
183
+ # Cython debug symbols
184
+ cython_debug/
185
+
186
+ # PyCharm
187
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
188
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
189
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
190
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
191
+ # .idea/
192
+
193
+ # Abstra
194
+ # Abstra is an AI-powered process automation framework.
195
+ # Ignore directories containing user credentials, local state, and settings.
196
+ # Learn more at https://abstra.io/docs
197
+ .abstra/
198
+
199
+ # Visual Studio Code
200
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
201
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
202
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
203
+ # you could uncomment the following to ignore the entire vscode folder
204
+ # .vscode/
205
+ # Temporary file for partial code execution
206
+ tempCodeRunnerFile.py
207
+
208
+ # Ruff stuff:
209
+ .ruff_cache/
210
+
211
+ # PyPI configuration file
212
+ .pypirc
213
+
214
+ # Marimo
215
+ marimo/_static/
216
+ marimo/_lsp/
217
+ __marimo__/
218
+
219
+ # Streamlit
220
+ .streamlit/secrets.toml
@@ -0,0 +1,31 @@
1
+ # .readthedocs.yaml
2
+ # Read the Docs configuration file
3
+ # See https://docs.readthedocs.com/platform/stable/config-file/v2.html for details
4
+
5
+ # Required
6
+ version: 2
7
+
8
+ # Set the OS, Python version and other tools you might need
9
+ build:
10
+ os: ubuntu-24.04
11
+ tools:
12
+ python: "3.14"
13
+ # https://docs.readthedocs.com/platform/stable/build-customization.html#install-dependencies-with-uv
14
+ jobs:
15
+ pre_create_environment:
16
+ - asdf plugin add uv
17
+ - asdf install uv latest
18
+ - asdf global uv latest
19
+ create_environment:
20
+ - uv venv "${READTHEDOCS_VIRTUALENV_PATH}"
21
+ install:
22
+ - UV_PROJECT_ENVIRONMENT="${READTHEDOCS_VIRTUALENV_PATH}" uv sync --frozen --group docs
23
+
24
+ # Build documentation in the "docs/" directory with Sphinx
25
+ sphinx:
26
+ configuration: docs/source/conf.py
27
+
28
+ # Optionally build your docs in additional formats such as PDF and ePub
29
+ formats:
30
+ - pdf
31
+ - epub
rtfc-0.1.0/DESIGN.md ADDED
@@ -0,0 +1,188 @@
1
+ # rtfc — design plan
2
+
3
+ rtfc ("read the freaking changelog") is a Python tool to manage changelogs and versioning,
4
+ similar in spirit to towncrier. Changelog entries live as individual files in a
5
+ configurable directory and are combined into a changelog document on release.
6
+ Primarily targets Python documentation (sphinx/rst first), but designed to be
7
+ extensible to other formats (e.g. mkdocs markdown).
8
+
9
+ Status: draft — iterating.
10
+
11
+ ## 1. Entry file format
12
+
13
+ **Decision: TOML frontmatter + raw body.** One file per entry, structured header and
14
+ an **opaque** body, separated by a `+++` delimiter:
15
+
16
+ ```
17
+ +++
18
+ date = 2025-08-01
19
+ nonce = "k3jf9a"
20
+ section = "bugfix" # optional, may be absent (null)
21
+
22
+ [metadata] # optional, free-form
23
+ gh_issue = 123
24
+ is_backport = true
25
+ +++
26
+ Fix a bug where :meth:`~pydantic.BaseModel.model_dump` would crash on
27
+ recursive references. Multi-line, arbitrary rst — never parsed by rtfc.
28
+ ```
29
+
30
+ Rationale, vs the alternatives considered:
31
+
32
+ - **Pure TOML/JSON/YAML**: multiline content in TOML (`"""..."""` escaping) or YAML
33
+ (indentation-sensitive block scalars) is annoying to write and review in PRs.
34
+ JSON is a non-starter for humans.
35
+ - **Native `.rst` with docinfo field lists**: bare docutils on a body containing
36
+ sphinx-specific syntax doesn't hard-crash, but emits "Unknown interpreted text
37
+ role" / "Unknown directive type" system messages and mangles the parse. Also
38
+ needs a different metadata convention per docs format, and nested `metadata`
39
+ doesn't map onto rst field lists. Dead end.
40
+ - **TOML frontmatter** gives: real nested mappings for `metadata`, real date types,
41
+ trivial parsing (split on delimiter, `tomllib` the header, keep the rest
42
+ verbatim), and the body stays raw text in the target docs format. The body is
43
+ only ever interpreted by sphinx itself, after assembly into `changelog.rst`.
44
+
45
+ Parsing: split on the delimiter, parse header with `tomllib`, body kept verbatim.
46
+
47
+ Filename: `{nonce}.rtfc` — the nonce (generated by `rtfc new`) guarantees filenames
48
+ can't collide across concurrent PRs. Extension TBD (see open questions); cost of a
49
+ dedicated extension is no editor highlighting for the body.
50
+
51
+ Note: `tomllib` is stdlib from 3.11 — either bump `requires-python` to `>=3.11`
52
+ (recommended for a new tool) or depend on `tomli` for 3.10.
53
+
54
+ ## 2. Core data model
55
+
56
+ ```python
57
+ @dataclass
58
+ class Entry:
59
+ date: date
60
+ nonce: str
61
+ section: str | None # must be a key of configured sections, or None
62
+ metadata: dict[str, Any] # free-form
63
+ content: str # raw body, never parsed
64
+ ```
65
+
66
+ Validation at load time: mandatory fields present (`date`, `nonce`), `section`
67
+ (if set) exists in config, dates parseable. `rtfc check` runs this in CI.
68
+
69
+ Terminology note: "section" here is what the original brief also called "type"
70
+ (the entry's category: feature, bugfix, ...). Naming TBD.
71
+
72
+ ## 3. Configuration
73
+
74
+ Discovery: `rtfc.toml` if present (takes priority), else `[tool.rtfc]` in
75
+ `pyproject.toml`. Loaded into frozen dataclasses (no pydantic dependency needed
76
+ for a tool this size).
77
+
78
+ ```toml
79
+ [tool.rtfc]
80
+ directory = "changelog.d"
81
+ changelog = "docs/changelog.rst"
82
+ format = "rst" # entry point name of the format plugin
83
+
84
+ # defaults if omitted; declaration order = output order
85
+ [tool.rtfc.sections.feature]
86
+ label = "Features"
87
+
88
+ [tool.rtfc.sections.bugfix]
89
+ label = "Bug fixes"
90
+
91
+ [tool.rtfc.render]
92
+ sort = ["date", "metadata.gh_issue"] # sort key within a section
93
+ flat = false # true: ignore sections, one flat list
94
+ entry_template = "{{ content }}{% if metadata.gh_issue %} (:gh:`{{ metadata.gh_issue }}`){% endif %}"
95
+ ```
96
+
97
+ Templating: **Jinja2**, per-entry only. Standard, known from towncrier; inventing a
98
+ mini-language would be worse. The template receives `content`, `date`, `section`,
99
+ `metadata`. Section headers and version headers are produced by the format plugin,
100
+ not templates (version header non-configurable for now: just the version number
101
+ provided on the command line).
102
+
103
+ Entries with `section = None` in sectioned mode: rendered in an unlabeled group
104
+ before the first section (could become configurable later).
105
+
106
+ ## 4. Format plugins
107
+
108
+ Small protocol; rst implementation built in; third parties register via the
109
+ `rtfc.formats` entry-point group:
110
+
111
+ ```python
112
+ class Format(Protocol):
113
+ name: str
114
+ def version_header(self, version: str, date: date) -> str: ... # underlined title for rst
115
+ def section_header(self, label: str) -> str: ...
116
+ def render_entry(self, rendered_text: str) -> str: ... # "- " bullet + indent continuation lines
117
+ def markers(self) -> tuple[str, str]: ... # comment syntax for insertion/unreleased markers
118
+ ```
119
+
120
+ The plugin never parses entry bodies — it only wraps already-rendered text in
121
+ format-level structure. Keeps a future mkdocs/markdown implementation trivial.
122
+
123
+ ## 5. Assembly into the changelog file
124
+
125
+ The changelog file contains an insertion marker (rst comment):
126
+
127
+ ```rst
128
+ Changelog
129
+ =========
130
+
131
+ .. rtfc-insert
132
+
133
+ v1.2.0 (2025-07-01)
134
+ -------------------
135
+ ...
136
+ ```
137
+
138
+ - `rtfc build --version 1.3.0` — collects entries, renders the version block,
139
+ inserts it right after the marker, **deletes** the entry files.
140
+ - `rtfc build --unreleased` — renders the same block titled "Unreleased", wrapped
141
+ in begin/end markers (`.. rtfc-unreleased-start` / `-end`) so re-running
142
+ **replaces** the previous unreleased block idempotently; deletes nothing.
143
+ A release build first removes any existing unreleased block.
144
+ - `--draft` — print to stdout instead of writing (useful in CI and for release
145
+ PR/GitHub-release descriptions).
146
+
147
+ ## 6. CLI
148
+
149
+ - `rtfc new [--section bugfix] [--meta gh_issue=123]` — generates nonce + today's
150
+ date, opens `$EDITOR` or takes `--content`.
151
+ - `rtfc build [--version X.Y.Z | --unreleased] [--draft]`
152
+ - `rtfc check` — validate all entries (CI-friendly).
153
+
154
+ Library: argparse, keeping runtime deps at just `jinja2` (+ `tomli` on 3.10).
155
+ Switch to click only if subcommand UX outgrows it.
156
+
157
+ ## 7. Future exports (design awareness only)
158
+
159
+ Faithful markdown export (e.g. for GitHub releases) requires sphinx, since
160
+ `` :gh:`123` `` can expand to a link, a section, a table, anything. The
161
+ architecture keeps this open in two ways:
162
+
163
+ - Entries stay structured until the final render, so an exporter can re-render
164
+ from `Entry` objects rather than scraping `changelog.rst`.
165
+ - A future sphinx-based export path can build just the changelog doc through
166
+ sphinx with a text/markdown builder to resolve roles.
167
+
168
+ Nothing in the core needs to change for either — the renderer produces a
169
+ version-block string; writing it into the changelog file is a separate step, so
170
+ "changelog file is the only output" is not baked into the renderer API.
171
+
172
+ ## 8. Milestones
173
+
174
+ 1. Config loading (`rtfc.toml` / `pyproject.toml`) + entry parser + `Entry`
175
+ model + `check` — pure, easily tested.
176
+ 2. rst format plugin + renderer (sections, sorting, Jinja entry template).
177
+ 3. File assembly (markers, unreleased replacement, entry deletion) + `build` CLI.
178
+ 4. `rtfc new`, docs, polish; entry-point plugin loading.
179
+
180
+ ## Open questions
181
+
182
+ 1. `section` vs `type` naming for the entry category field.
183
+ 2. `.rtfc` extension vs something else for entry files?
184
+ 3. Bump to Python ≥3.11 for `tomllib`?
185
+ 4. `--unreleased` as a flag on `build` vs a separate command — leaning flag
186
+ (same logic, one code path).
187
+ 5. Delimiter: `+++` (Hugo-style, signals TOML) vs `---` (usually implies YAML) —
188
+ leaning `+++`.
rtfc-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Victorien
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.