Skip to content

Commit

Permalink
introduce snippets
Browse files Browse the repository at this point in the history
  • Loading branch information
brainelectronics committed May 30, 2024
1 parent db4f2df commit c3535bd
Show file tree
Hide file tree
Showing 14 changed files with 327 additions and 55 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ jobs:
echo "latest_version=$latest_version" >> $GITHUB_ENV
- name: Build package
run: |
poetry run changelog-generator \
changelog changelog.md \
--snippets=.snippets
poetry run changelog2version \
--changelog_file changelog.md \
--changelog_file changelog.md.new \
--version_file snippets2changelog/version.py \
--version_file_type py \
--debug
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/test-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ jobs:
echo "latest_version=$latest_version" >> $GITHUB_ENV
- name: Build package
run: |
poetry run changelog-generator \
changelog changelog.md \
--snippets=.snippets
poetry run changelog2version \
--changelog_file changelog.md \
--changelog_file changelog.md.new \
--version_file snippets2changelog/version.py \
--version_file_type py \
--additional_version_info="-rc${{ github.run_number }}.dev${{ github.event.number }}" \
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ jobs:
poetry self add "poetry-dynamic-versioning[plugin]"
- name: Build package
run: |
poetry run changelog-generator \
changelog changelog.md \
--snippets=.snippets
poetry run changelog2version \
--changelog_file changelog.md \
--changelog_file changelog.md.new \
--version_file snippets2changelog/version.py \
--version_file_type py \
--debug
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# snippets2changelog specific
changelog.md.new

# custom, package specific ignores
.DS_Store
.DS_Store?
Expand Down Expand Up @@ -114,7 +117,7 @@ celerybeat.pid

# Environments
.env
.venv
.venv*
env/
venv/
ENV/
Expand Down
34 changes: 34 additions & 0 deletions .snippets/3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Introduce snippets
<!--
type: breaking
scope: all
affected: all
-->

### Added
- `ChangelogCreator` class to render new changelog from snippets and existing base changelog
- New `changelog-generator changelog` CLI interface available and documented
- `SnippetCollector` to provide iterable of snippets found in specified folder, sorted by the appearance in the Git history (oldest first)
- Template for single changelog entry
- Template for complete rendered and updated changelog

### Changed
#### Breaking
- `SnippetCreator` class has no `file_name` init parameter anymore
- `content` property renamed to `parsed_content`
- `parse` function takes `file_name` parameter
- `parsed_content` gets resetted with every new `parse(file_name)` call
- `_required_keys` renamed to `_required_parser_keys`
- `SnippetCreator` class has no `file_name` and `content` init parameter anymore
- `snippets_file` property removed
- `content` property renamed to `rendered_content`
- `render` function takes `content` parameter and returns `None`
- `create` function takes `file_name` parameter

#### Other
- `changelog2version` moved from dev dependency to package dependency
- Create changelog from snippets in GitHub workflow before creating the python package

### Fixed
- Sorted package dependencies in `pyproject.toml`
- Ignore all `.venv*` directories
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Create version info files based on the latest changelog entry.
- [Usage](#usage)
- [Info](#info)
- [Create](#create)
- [Snippet](#snippet)
- [Changelog](#changelog)
- [Parse](#parse)
- [Contributing](#contributing)
- [Setup](#setup)
Expand All @@ -46,6 +48,7 @@ changelog-generator info
```

### Create
#### Snippet

Create a new snippet with the given name at the specified snippets folder

Expand Down Expand Up @@ -74,6 +77,15 @@ TBD
```

#### Changelog

Create or update a changelog with all snippets.
New changelog will be named `<OLD_CHANGELOG_NAME.new>`

```bash
changelog-generator changelog changelog.md --snippets=.snippets
```

### Parse

Parse an existing snippet file and return the data as JSON without indentation
Expand Down
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ format-jinja = """{{ check_output(["python3", "-c", "from pathlib import Path; e
changelog-generator = 'snippets2changelog.cli:main'

[tool.poetry.dependencies]
python = "^3.11"
pyyaml = "~6.0"
changelog2version = "^0.10"
GitPython = "~3.1.43"
jinja2 = "^3.1.4"
python = "^3.11"
pyyaml = "~6.0"

[tool.poetry.group.dev.dependencies]
black = "*"
changelog2version = "^0.10"
flake8 = "*"
isort = "*"
mypy = "*"
Expand Down
36 changes: 29 additions & 7 deletions snippets2changelog/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from typing import Sequence

from .common import LOG_LEVELS, collect_user_choice
from .creator import SnippetCreator
from .creator import ChangelogCreator, SnippetCreator
from .parser import SnippetParser

LOGGER_FORMAT = '[%(asctime)s] [%(levelname)-8s] [%(filename)-15s @'\
Expand Down Expand Up @@ -50,6 +50,22 @@ def parse_args(argv: Sequence[str] | None = None) -> Args:
)
parser_info.set_defaults(func=fn_info)

parser_changelog = subparsers.add_parser(
"changelog",
help="Create a changelog",
)
parser_changelog.set_defaults(func=fn_changelog)
parser_changelog.add_argument(
"changelog",
type=Path,
help="Path to existing changelog",
)
parser_changelog.add_argument(
"--snippets",
type=lambda x: does_exist(parser, x),
help="Directory to crawl for snippets",
)

parser_create = subparsers.add_parser(
"create",
help="Create a snippet",
Expand Down Expand Up @@ -97,6 +113,11 @@ def fn_info(_args: Args) -> None:
print(f"Version: {extract_version()}")


def fn_changelog(args: Args) -> None:
cc = ChangelogCreator(changelog=args.changelog, snippets_folder=args.snippets, verbosity=args.verbose)
cc.update_changelog()


def fn_create(args: Args) -> None:
content = {
"short_description": input("Short description: "),
Expand All @@ -107,15 +128,16 @@ def fn_create(args: Args) -> None:
"affected": input("Affected users (default all): ") or "all",
"content": "TBD",
}
sc = SnippetCreator(file_name=args.name, content=content)
logger.debug(f"rendered content: >>>>>>\n{sc.render()}\n<<<<<<")
sc.create()
sc = SnippetCreator()
sc.render(content=content)
logger.debug(f"rendered content: >>>>>>\n{sc.rendered_content}\n<<<<<<")
sc.create(file_name=args.name)


def fn_parse(args: Args) -> None:
sp = SnippetParser(file_name=args.name, verbosity=args.verbose)
sp.parse()
print(json.dumps(sp.content, indent=args.indent))
sp = SnippetParser(verbosity=args.verbose)
sp.parse(file_name=args.name)
print(json.dumps(sp.parsed_content, indent=args.indent))


def main() -> int:
Expand Down
114 changes: 114 additions & 0 deletions snippets2changelog/collector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env python3

"""Snippets collector"""

from collections.abc import Iterator
from pathlib import Path

from git import Commit, GitCmdObjectDB, Repo, Submodule, TagReference
from git.refs.head import Head


class CollectorError(Exception):
"""Base class for exceptions in this module."""

pass


class HistoryWalker(object):
"""docstring for HistoryWalker"""

def __init__(
self, repo: Path, search_parent_directories: bool = True, branch_only: bool = True
) -> None:
if repo.exists():
try:
self._repo = Repo(repo, search_parent_directories=search_parent_directories)
except Exception as e:
raise CollectorError(e)
else:
raise CollectorError(f"Given repo folder '{repo}' does not exist")

# limit amount of commits to crawl to a positive number
self._max_count = None
self._branch_only = branch_only

@property
def repo_root(self) -> Path:
return Path(self._repo.working_dir)

@property
def branch_name(self) -> Head:
return self._repo.active_branch

@property
def tags(self) -> list[TagReference]:
return sorted(self._repo.tags, key=lambda t: t.commit.committed_datetime)

def commits(self) -> Iterator[Commit]:
kwargs = dict()

if self._branch_only:
# Collect the commits on this branch only
# aka ignore commits on other branches
kwargs["first-parent"] = True

# latest commit is first element
for ele in self._repo.iter_commits(
rev=self.branch_name,
max_count=self._max_count,
**kwargs,
):
yield ele


class SnippetCollector(HistoryWalker):
"""docstring for SnippetCollector"""

def __init__(self, snippets_folder: Path, file_extension: str = "md", **kwargs) -> None:
if snippets_folder.exists():
self._snippets_folder = snippets_folder
else:
raise CollectorError(f"Given snippets folder '{snippets_folder}' does not exist")

HistoryWalker.__init__(self, repo=self._snippets_folder, **kwargs)
self._file_extension = file_extension

@property
def snippets_folder(self) -> Path:
return self._snippets_folder

def all_snippet_files(self) -> Iterator[Path]:
"""Get all potential snippet files from the snippets folder"""
for file in self._snippets_folder.iterdir():
if file.is_file() and (file.suffix == ".{}".format(self._file_extension)):
yield file

def snippets(self) -> Iterator[tuple[Commit, Path]]:
collected_snippets = list(self.all_snippet_files())

# nice chaos :)
# close to midnight, stop now or the problem of tomorrow will catch you

# self._logger.debug(f"Repo root: {self.repo_root}")
# changelog-generator/

# self._logger.debug(f"collected_snippets: {collected_snippets}, looking for {self.snippets_folder}")
# collected_snippets: [PosixPath('.snippets/3.md')], looking for .snippets

# use reversed to have oldest commit as first element
for idx, commit in reversed(list(enumerate(self.commits()))):
for file in commit.stats.files.keys():
# self._logger.debug(f"{idx}: {commit}, looking for file: {file} in {collected_snippets}")
# 0: b768d6983432b730d81b34d125a4bbefb0a66525, looking for file: .snippets/3.md in [PosixPath('.snippets/3.md')]
# self._logger.debug(Path(file) in collected_snippets)
# True
"""
if self.snippets_folder / file in collected_snippets:
self._logger.warning(f"file {self.snippets_folder / file} is a match")
yield (commit, self.snippets_folder / file)
"""
if Path(file) in collected_snippets:
# self._logger.debug(f"file {file} is a match")
# file .snippets/3.md is a match
yield (commit, Path(file))
Loading

0 comments on commit c3535bd

Please sign in to comment.