Generate Changelog
Sometimes, you have a changelog like this: (See frinyvonnick/gitmoji-changelog)
# Changelog
<a name="0.0.3"></a>
## 0.0.3 (2023-03-04)
### Added
- Support generating changelog [[b1c8b40](https://github.com/Freed-Wu/setuptools-generate/commit/b1c8b40fbdc0dfa9277ec1f52c3f1ecca32b4492)]
<a name="0.0.2"></a>
## 0.0.2 (2023-03-04)
### Added
- Fix tests [[311196a](https://github.com/Freed-Wu/setuptools-generate/commit/311196ada60a59975f6e6fba263d595031eb4bf9)]
- Update pre-commit.ci [[cf59635](https://github.com/Freed-Wu/setuptools-generate/commit/cf59635f07bf49e2bac52cf13503153bfa92d354)]
### Fixed
- Upgrade pre-commit [[ca1fc30](https://github.com/Freed-Wu/setuptools-generate/commit/ca1fc308aef923e29f6d407d695afe9b484a7d19)]
- Fix code coverage [[d529532](https://github.com/Freed-Wu/setuptools-generate/commit/d529532c1de796f6a8b1e6c4be96c6e266e94844)]
### Miscellaneous
- Add CHANGELOG.md [[b635c0f](https://github.com/Freed-Wu/setuptools-generate/commit/b635c0f4a78d8cb19ad4e37e5b08101092f99d47)]
<a name="0.0.1"></a>
## 0.0.1 (2022-12-13)
### Added
- Initial [[cb046a6](https://github.com/Freed-Wu/setuptools-generate/commit/cb046a674fbe8fa921678375ee7e54cc77b7a1d7)]
This tool can generate a build/CHANGELOG.md
.
<h3>Added</h3>
<ul>
<li>Support generating changelog [<a href="https://github.com/Freed-Wu/setuptools-generate/commit/b1c8b40fbdc0dfa9277ec1f52c3f1ecca32b4492">b1c8b40</a>]</li>
</ul>
<p><a name="0.0.2"></a></p>
So you can
- uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
body_path: build/CHANGELOG.md
files: |
dist
sdist
More information can be seen in softprops/action-gh-release.
The Intention of This Tool
Dependencies
There are many different types of dependencies: (The following syntax is
pyproject.toml
, you can write requirements.txt
freely if you prefer it.)
Build dependencies
Needed when packagers and CI/CD are building a python project.
[build-system]
dependencies = [ "XXX", "YYY",]
Runtime dependencies
Needed when users run a python program. If they are not installed, the program will fail.
[project]
dependencies = [ "XXX", "YYY",]
Extra dependencies
Needed when users run a python program to realize some extra functions. If they are not installed, the program will lose some function.
[project.optional-dependencies]
function_name = [ "XXX", "YYY",]
Develop dependencies
A special extra dependency which just for developers and CI/CD run some test like
unit test
code coverage
pre commit hooks for linters, formatters, grammar checkers, spell checkers, …
[project.optional-dependencies]
dev = [ "XXX", "YYY",]
Document build dependencies
Only for document build.
[project.optional-dependencies]
docs = [ "XXX", "YYY",]
Conclusion
Developers write code and tests, then let CI/CD test (install develop dependencies), build (if test pass, install build dependencies) and release (if build pass).
Packagers download the released packages (for python, it is a wheel file) and repackage them to packages of debian, rpm, pacman, portage, homebrew, nix, etc.
Users use their package managers to download the package and install them to their software distributions like Windows msys2, Android termux, Android termux-pacman, Linux and macOS’s homebrew, Linux and macOS’s nix, etc (install runtime dependencies and some extra dependencies).
The Aim of This Tool
Any software is not only consisted of executable files and necessary libraries, it also contains some resources like shell completions, man pages, desktop entries, icons, etc. Some resources, like shell completions and man pages, can be generated from executable files. So who should generate them? Developers or packagers?
Obviously, developers build once, all packagers of different package managers can use the built resources without repeatedly building them. For developers, it just write some code to let CI/CD do this job.
Usually, developers build these resources in the test job of CI/CD and install some packages needed by building these resource as develop dependencies. However, these packages are not truly related to any test. It increases the redundant dependencies in test job, which is not pure (Although we don’t need a absolute pure environment like nix or docker, pure is better than impure). And the other dependencies for test job is also possible to effect these resources’ building.
Or developers create a new job named build-resource in CI/CD. Although it increases some troubles, it is also acceptable.
Why not let them become build dependencies? If so, we can use CI/CD to build all resources needed for release, then just release them together:
# ...
jobs:
# ...
build:
# ...
steps:
# ...
- name: Build
run: |
pip install build
python -m build
- uses: actions/upload-artifact@v3
if: ${{ ! startsWith(github.ref, 'refs/tags/') }}
with:
path: |
dist/*
sdist/*
- uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
dist/*
sdist/*
- uses: pypa/gh-action-pypi-publish@release/v1
if: startsWith(github.ref, 'refs/tags/') && runner.os == 'Linux'
with:
password: ${{ secrets.PYPI_API_TOKEN }}
Pits
However, a trouble is: we need to run the executable file to build some resources in build job. It imports runtime dependencies to build dependencies. In order to reduce the imported dependencies, the entry of program should be designed carefully.
#!/usr/bin/env python
"""A template for ``__main__.py``."""
from argparse import ArgumentParser
from contextlib import suppress
# Users don't need to install build dependencies
with suppress(ImportError):
import shtab
def get_parser() -> ArgumentParser:
"""Get parser for ``main()`` and unit test."""
parser = ArgumentParser()
with suppress(NameError):
shtab.add_argument_to(parser)
# ...
return parser
def main() -> None:
"""This module can be called by
`python -m <https://docs.python.org/3/library/__main__.html>`_.
Wrap the mostly logic to ``run()``, because ``--help``, ``--version``,
``--print-completion`` will exit on ``parser.parse_args()``. The following
code of ``run()`` will not be sourced. It will save time for those users
who just want to see the usage of this program by ``--help`` without
sourcing ``run()``, especially when ``run()`` import many large libraries,
it will be very slow.
"""
parser = get_parser()
args = parser.parse_args()
if args.gui:
from .ui.gui import run
else:
from .ui.cli import run
run()
if __name__ == "__main__":
main()
Now all runtime dependencies are imported in run()
, where not be imported on
building shell completions.
__init__.py
is likely.
Support
Currently, this tool support:
Install
It is unnecessary to install this tool directly because it is just a setuptools plugin and have no other usages. Its aim is to become a build dependency of a python project which need to build some resources.
Generate Metainfo
This tool also support generating some metainfo to a file like
Just
[tool.setuptools-generate]
write-to = "src/translate_shell/_metainfo.py"
You can customize template by
[tool.setuptools-generate]
write-to = "src/translate_shell/_metainfo.py"
[tool.setuptools-generate.metainfo-template]
text = "XXXXX"
or
[tool.setuptools-generate]
write-to = "src/translate_shell/_metainfo.py"
[tool.setuptools-generate.metainfo-template]
file = "XXXXX"
The template language is jinja2:
"""This file is generated by setuptools-generate.
The information comes from pyproject.toml.
It provide some metainfo for docs/conf.py to build documents and
help2man to build man pages.
"""
# For docs/conf.py
project = "{{ data['project']['name'] }}"
author = """{% for author in data['project']['authors'] -%}
{{ author['name'] }} <{{ author['email'] }}> {% endfor -%}
"""
copyright = "{{ year }}"
# For help2man
DESCRIPTION = "{{ data['project']['description'] }}"
EPILOG = "Report bugs to {{ data['project']['urls']['Bug Report'] }}"
# format __version__ by yourself
VERSION = """{{ data['project']['name'] }} {__version__}
Copyright (C) {{ year }}
Written by {% for author in data['project']['authors'] -%}
{{ author['name'] }} <{{ author['email'] }}> {% endfor %}"""
sphinx
For sphinx’s docs/conf.py
, just
from translate_shell._metainfo import author, copyright, project
You don’t need write these metainfo twice: in pyproject.toml
and
docs/conf.py
.
help2man
For help2man, just in your __main__.py
:
from argparse import ArgumentParser
from translate_shell._metainfo import author, copyright, project
def get_parser():
parser = ArgumentParser(description=DESCRIPTION, epilog=EPILOG)
parser.add_argument("--version", action="version", version=VERSION)
Changelog
0.0.5 (2023-04-05)
Added
👷♂️ Add perltidy [fd127e5]
Removed
🔥 Don’t compress man page [384f7c2]
Fixed
💚 Disable latex docs [834b0ab]
Miscellaneous
0.0.4 (2023-03-16)
Fixed
🐛 Fix wrong EOL on windows [fa8fb1f]
Miscellaneous
0.0.3 (2023-03-04)
Added
0.0.2 (2023-03-04)
Added
Fixed
Miscellaneous
📝 Add CHANGELOG.md [b635c0f]
0.0.1 (2022-12-13)
Added
🎉 Initial [cb046a6]
setuptools-generate
Provide __version__
for
importlib.metadata.version().
- setuptools_generate.generate(distribution: Distribution | None = None) None [source]
Generate.
- Parameters:
distribution (Distribution | None) –
- Return type:
None
click
- setuptools_generate._click.generate_complete(command: BaseCommand, prog: str, resources: str) None [source]
Generate complete.
- Parameters:
command (BaseCommand) –
prog (str) –
resources (str) –
- Return type:
None
help2man
- setuptools_generate._help2man.generate_man(function: Callable, prog: str, sdist: str, build: str) None [source]
Generate man.
- Parameters:
function (Callable) –
prog (str) –
sdist (str) –
build (str) –
- Return type:
None
Markdown it
- setuptools_generate._markdown_it.generate_changelog(build: str, fname: str) None [source]
Generate changelog.
- Parameters:
build (str) –
:param fname :type fname: str :rtype: None
shtab
- setuptools_generate._shtab.generate_complete(function: Callable, prog: str, resources: str) None [source]
Generate complete.
- Parameters:
function (Callable) –
prog (str) –
resources (str) –
- Return type:
None
Metainfo
- setuptools_generate.metainfo.generate_metainfo(write_to: str, data: dict) None [source]
Generate metainfo.
- Parameters:
write_to (str) –
data (dict) –
- Return type:
None
Utilities
setuptools-generate
Generate shell completions and man page when building a python package.
Usage
Add this package to your build requires:
[build-system]
requires = [ "setuptools-generate",]
build-backend = "setuptools.build_meta"
[project]
name = "demo"
version = "0.0.1"
[project.scripts]
demo = "demo:main"
Build your package:
python -m build
See your sdist
:
$ tree sdist
sdist
├── _demo # zsh completion script
├── demo # bash completion script
├── demo-0.0.1-py3-none-any.whl # wheel file
├── demo-0.0.1.tar.gz # source distribution file
├── demo.1 # man page
└── demo.fish # fish completion script
You got them.
Example projects:
See document to know more.