Merge pull request 'dev' (#3) from dev into master
Reviewed-on: https://chacha.ddns.net/gitea/chacha/pychangelogfactory/pulls/3 new-tag: 1.0.2
This commit was merged in pull request #3.
This commit is contained in:
@@ -1,17 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?eclipse-pydev version="1.0"?><pydev_project>
|
||||
|
||||
|
||||
|
||||
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
||||
|
||||
|
||||
|
||||
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
|
||||
|
||||
|
||||
|
||||
|
||||
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
||||
<path>/${PROJECT_DIR_NAME}/src</path>
|
||||
<path>/${PROJECT_DIR_NAME}</path>
|
||||
</pydev_pathproperty>
|
||||
|
||||
|
||||
|
||||
|
||||
</pydev_project>
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding//src/pychangelogfactory/changelogfactory.py=utf-8
|
||||
encoding/<project>=UTF-8
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
|
||||
# pyChangeLogHelper
|
||||
|
||||
A simple changelog formater that consume merged commit message and produce nice pre-formated changelogs
|
||||
A simple changelog formater that consume raw changes list text and produce nice pre-formated changelogs.
|
||||
The input data mainly aim to be a merged commit report.
|
||||
|
||||
Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/pychangelogfactory/master/latest/).
|
||||
|
||||
## Features
|
||||
Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/pychangelogfactory/master/latest/).
|
||||
16
RUN_changelog.launch
Normal file
16
RUN_changelog.launch
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/${project_name}/helpers"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="2"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc}/helpers"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--changelog-gen"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pychangelogfactory"/>
|
||||
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
|
||||
</launchConfiguration>
|
||||
16
RUN_complexity.launch
Normal file
16
RUN_complexity.launch
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/${project_name}/helpers"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="2"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:pychangelogfactory/helpers}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--complexity-check"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pychangelogfactory"/>
|
||||
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
|
||||
</launchConfiguration>
|
||||
19
RUN_mkdocs.launch
Normal file
19
RUN_mkdocs.launch
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/${project_name}/helpers"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="2"/>
|
||||
</listAttribute>
|
||||
<mapAttribute key="org.eclipse.debug.core.environmentVariables">
|
||||
<mapEntry key="PATH" value="C:\Program Files\GTK3-Runtime Win64\bin"/>
|
||||
</mapAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:pychangelogfactory/helpers}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--doc-gen --doc-gen-pdf"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pychangelogfactory"/>
|
||||
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
|
||||
</launchConfiguration>
|
||||
16
RUN_quality.launch
Normal file
16
RUN_quality.launch
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/${project_name}/helpers"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="2"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:pychangelogfactory/helpers}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--type-check --quality-check"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pychangelogfactory"/>
|
||||
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
|
||||
</launchConfiguration>
|
||||
16
RUN_unittest.launch
Normal file
16
RUN_unittest.launch
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.debug.core.ATTR_FORCE_SYSTEM_CONSOLE_ENCODING" value="false"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/${project_name}/helpers"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="2"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:pychangelogfactory/helpers}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--unit-test --coverage-check"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
|
||||
<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="pychangelogfactory"/>
|
||||
<stringAttribute key="process_factory_id" value="org.python.pydev.debug.processfactory.PyProcessFactory"/>
|
||||
</launchConfiguration>
|
||||
@@ -1,5 +1,28 @@
|
||||
# Usage
|
||||
|
||||
## Theory
|
||||
|
||||
This lib will try to extract normalized changes from a given raw history.
|
||||
|
||||
_It's up to the user to provide this merged history._
|
||||
|
||||
To realize this job, parsing is done in two rounds:
|
||||
|
||||
- first round extrats formal changes messages: e.g.: `<change_type>(<change_target>): <change_message>`
|
||||
- secound round extracts lines from remaining ones, based on keywords dictionnaries
|
||||
|
||||
/// warning | searching policy
|
||||
When formal search (1), _lines must contain at least 2 words_
|
||||
When keywords search (2), _lines must contain at least 3 words_
|
||||
///
|
||||
|
||||
/// note | ignored lines
|
||||
lines with comment tags are ignored:
|
||||
|
||||
- `[0..N space]//`
|
||||
- `[0..N space]#`
|
||||
///
|
||||
|
||||
## Installation
|
||||
|
||||
From pypi repository (prefered):
|
||||
@@ -15,10 +38,34 @@ From master git repository:
|
||||
python -m pip install git+https://chacha.ddns.net/gitea/chacha/pychangelogfactory.git@master
|
||||
|
||||
|
||||
## Use in your project
|
||||
|
||||
### Sample code
|
||||
``` py
|
||||
from pychangelogfactory import ChangeLogFormater
|
||||
|
||||
## Import in your project
|
||||
raw_changelog='''
|
||||
feat: add a nice feature to the project
|
||||
style: reindent the full Foo class
|
||||
security: fix a security leak on the Foo2 component
|
||||
'''
|
||||
ChangeLogFormater.FactoryProcessFullChangelog(raw_changelog)
|
||||
changelog = ChangeLogFormater.RenderFullChangelog()
|
||||
print(changelog)
|
||||
```
|
||||
### Output(Raw)
|
||||
|
||||
Add this line on the top of your python script:
|
||||
|
||||
from pychangelogfactory import ChangeLogFormater
|
||||
#### Features :sparkles::
|
||||
> add a nice feature to the project
|
||||
#### Security :shield::
|
||||
> fix a security leak on the Foo2 component
|
||||
#### Style :art::
|
||||
> reindent the full Foo class
|
||||
|
||||
### Output (rendered)
|
||||
#### Features :sparkles::
|
||||
> add a nice feature to the project
|
||||
#### Security :shield::
|
||||
> fix a security leak on the Foo2 component
|
||||
#### Style :art::
|
||||
> reindent the full Foo class
|
||||
@@ -72,9 +72,11 @@ class doc_gen(helper_withresults_base):
|
||||
# little hack here, to enable / disable pdf generation using own class config
|
||||
# => reason is mkdocs seems to try loading the plugin even if we disable it, so we need to
|
||||
# manually process the configuration file.
|
||||
mkdocsCfg = None
|
||||
with open(cls.project_rootdir_path / "mkdocs.yml", "r") as mkdocsCfgFile:
|
||||
mkdocsCfg = yaml.load(mkdocsCfgFile, Loader=yaml.SafeLoader)
|
||||
mkdocsCfg = yaml.load(mkdocsCfgFile, Loader=yaml.Loader)
|
||||
|
||||
if "plugins" in mkdocsCfg:
|
||||
mkdocsCfg["plugins"] = [_ for _ in mkdocsCfg["plugins"] if (not isinstance(_, dict) or "with-pdf" not in _.keys())]
|
||||
|
||||
if cls.enable_gen_pdf == True:
|
||||
mkdocsCfg["plugins"].append(
|
||||
@@ -83,19 +85,11 @@ class doc_gen(helper_withresults_base):
|
||||
"cover_subtitle": "User Manual",
|
||||
"cover_logo": str(cls.project_rootdir_path / "docs-static" / "Library.jpg"),
|
||||
"verbose": False,
|
||||
"media_type": "print",
|
||||
"exclude_pages": ["LICENSE"],
|
||||
"output_path": str(site_path / "pdf" / "manual.pdf"),
|
||||
}
|
||||
}
|
||||
)
|
||||
else:
|
||||
for subelem in mkdocsCfg["plugins"]:
|
||||
if isinstance(subelem, dict):
|
||||
if "with-pdf" in subelem.keys():
|
||||
mkdocsCfg["plugins"].remove(subelem)
|
||||
break
|
||||
|
||||
with open(cls.project_rootdir_path / "mkdocs.yml", "w") as mkdocsCfgFile:
|
||||
mkdocsCfgFile.write(yaml.dump(mkdocsCfg, Dumper=Dumper, default_flow_style=False, sort_keys=False))
|
||||
|
||||
|
||||
178
mkdocs.yml
178
mkdocs.yml
@@ -1,92 +1,110 @@
|
||||
# pyChaChaDummyProject (c) by chacha
|
||||
#
|
||||
# pyChaChaDummyProject is licensed under a
|
||||
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Unported License.
|
||||
#
|
||||
# You should have received a copy of the license along with this
|
||||
# work. If not, see <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
|
||||
|
||||
docs_dir: docs
|
||||
site_name: pychangelogfactory
|
||||
site_url: https://chacha.ddns.net/mkdocs-web/chacha/pychangelogfactory/latest/
|
||||
site_description: A simple changelog builder that you can feed with your repository change history
|
||||
site_url: 'https://chacha.ddns.net/mkdocs-web/chacha/pychangelogfactory/latest/'
|
||||
site_description: 'A simple changelog builder that you can feed with your repository change history'
|
||||
site_author: chacha
|
||||
repo_url: https://chacha.ddns.net/gitea/chacha/pychangelogfactory
|
||||
repo_url: 'https://chacha.ddns.net/gitea/chacha/pychangelogfactory'
|
||||
use_directory_urls: false
|
||||
copyright: CC BY-NC-SA 4.0
|
||||
copyright: 'CC BY-NC-SA 4.0'
|
||||
theme:
|
||||
name: material
|
||||
features:
|
||||
- navigation.instant
|
||||
- navigation.tracking
|
||||
- navigation.tabs
|
||||
- navigation.tabs.sticky
|
||||
- toc.integrate
|
||||
- navigation.top
|
||||
- navigation.instant
|
||||
- navigation.tracking
|
||||
- navigation.tabs
|
||||
- navigation.tabs.sticky
|
||||
- navigation.footer
|
||||
- toc.integrate
|
||||
- navigation.top
|
||||
- navigation.section
|
||||
- content.code.annotate
|
||||
- navigation.prune
|
||||
- toc.follow
|
||||
palette:
|
||||
- media: '(prefers-color-scheme: dark)'
|
||||
scheme: slate
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to system preference
|
||||
- media: (prefers-color-scheme)
|
||||
toggle:
|
||||
icon: material/brightness-auto
|
||||
name: Switch to light mode
|
||||
- media: '(prefers-color-scheme: light)'
|
||||
scheme: default
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
- media: '(prefers-color-scheme: dark)'
|
||||
scheme: slate
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to system preference
|
||||
- media: (prefers-color-scheme)
|
||||
toggle:
|
||||
icon: material/brightness-auto
|
||||
name: Switch to light mode
|
||||
- media: '(prefers-color-scheme: light)'
|
||||
scheme: default
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
plugins:
|
||||
- search
|
||||
- markdownextradata
|
||||
- mermaid2
|
||||
- localsearch
|
||||
- autorefs
|
||||
- mkdocstrings:
|
||||
default_handler: python
|
||||
handlers:
|
||||
python:
|
||||
selection:
|
||||
filters:
|
||||
- '!^_(?!_init__)'
|
||||
inherited_members: true
|
||||
rendering:
|
||||
show_root_heading: false
|
||||
show_root_toc_entry: false
|
||||
show_root_full_path: false
|
||||
show_if_no_docstring: true
|
||||
show_signature_annotations: true
|
||||
show_source: false
|
||||
heading_level: 2
|
||||
group_by_category: true
|
||||
show_category_heading: true
|
||||
- search
|
||||
- markdownextradata
|
||||
- mermaid2
|
||||
- localsearch
|
||||
- mkdocstrings:
|
||||
default_handler: python
|
||||
handlers:
|
||||
python:
|
||||
options:
|
||||
filters:
|
||||
- '!^_[^_]'
|
||||
inherited_members: true
|
||||
show_if_no_docstring: true
|
||||
show_signature_annotations: true
|
||||
show_source: false
|
||||
show_category_heading: true
|
||||
group_by_category: true
|
||||
docstring_section_style: spacy
|
||||
show_root_full_path: false
|
||||
merge_init_into_class: true
|
||||
separate_signature: true
|
||||
markdown_extensions:
|
||||
- def_list
|
||||
- tables
|
||||
- attr_list
|
||||
- abbr
|
||||
- pymdownx.betterem:
|
||||
smart_enable: all
|
||||
- pymdownx.caret
|
||||
- pymdownx.critic
|
||||
- pymdownx.details
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
line_spans: __span
|
||||
pygments_lang_class: true
|
||||
- pymdownx.keys
|
||||
- pymdownx.mark
|
||||
- pymdownx.progressbar
|
||||
- pymdownx.smartsymbols
|
||||
- pymdownx.tasklist:
|
||||
custom_checkbox: true
|
||||
- pymdownx.tilde
|
||||
- footnotes
|
||||
|
||||
- def_list
|
||||
- tables
|
||||
- attr_list
|
||||
- abbr
|
||||
- pymdownx.blocks.admonition:
|
||||
types:
|
||||
- new
|
||||
- settings
|
||||
- note
|
||||
- abstract
|
||||
- info
|
||||
- tip
|
||||
- success
|
||||
- question
|
||||
- warning
|
||||
- failure
|
||||
- danger
|
||||
- bug
|
||||
- example
|
||||
- quote
|
||||
- pymdownx.blocks.definition
|
||||
- pymdownx.blocks.details
|
||||
- pymdownx.blocks.tab
|
||||
- pymdownx.blocks.html
|
||||
- pymdownx.betterem:
|
||||
smart_enable: all
|
||||
- pymdownx.caret
|
||||
- pymdownx.critic
|
||||
- pymdownx.details
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
line_spans: __span
|
||||
pygments_lang_class: true
|
||||
- pymdownx.keys
|
||||
- pymdownx.mark
|
||||
- pymdownx.progressbar
|
||||
- pymdownx.smartsymbols
|
||||
- pymdownx.tasklist:
|
||||
custom_checkbox: true
|
||||
- pymdownx.tilde
|
||||
- footnotes
|
||||
- pymdownx.superfences
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||
emoji_generator: !!python/name:materialx.emoji.to_svg
|
||||
extra:
|
||||
branch: master
|
||||
repository: pygitversionhelper
|
||||
repository: pygitversionhelper
|
||||
|
||||
@@ -59,7 +59,7 @@ coverage-check = ["coverage>=7.0"]
|
||||
complexity-check = ["radon>=5.1"]
|
||||
quality-check = ["pylint>=2.15","pylint-json2html>=0.4","pandas>=1.5"]
|
||||
type-check = ["mypy[reports]>=0.99" ]
|
||||
doc-gen = ["mkdocs>=1.4.0", "mkdocs-material>=8.5", "mkdocs-localsearch>=0.9.0", "mkdocstrings[python]>=0.19", "mkdocs-with-pdf>=0.9.3","pyyaml>=6.0","pymdown-extensions>=9","mkdocs-markdownextradata-plugin","mkdocs-mermaid2-plugin"]
|
||||
doc-gen = ["mkdocs>=1.4.0", "mkdocs-material>=8.5","mkdocs-pymdownx-material-extras", "mkdocs-localsearch>=0.9.0", "mkdocstrings[python]>=0.19", "mkdocs-with-pdf>=0.9.3","pyyaml>=6.0","pymdown-extensions>=9","mkdocs-markdownextradata-plugin","mkdocs-mermaid2-plugin"]
|
||||
|
||||
#[project.scripts]
|
||||
#my-script = "my_package.module:function"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# You should have received a copy of the license along with this
|
||||
# work. If not, see <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
|
||||
|
||||
""" A simple changelog formater that consume merged commit message and produce nice pre-formated changelogs
|
||||
"""A simple changelog formater that consume merged commit message and produce nice pre-formated changelogs.
|
||||
|
||||
"""
|
||||
|
||||
@@ -20,35 +20,60 @@ from abc import ABC
|
||||
|
||||
|
||||
def ChangeLogFormaterRecordType(Klass: type) -> type:
|
||||
"""decorator helper function to register interface implementation"""
|
||||
"""Decorator helper function to register interface implementation in factory
|
||||
Args:
|
||||
Klass: class to register in the factory
|
||||
Returns:
|
||||
untouched class"""
|
||||
ChangeLogFormater.ar_Klass.append(Klass)
|
||||
return Klass
|
||||
|
||||
|
||||
class ChangeLogFormater(ABC):
|
||||
"""the main changelog class that define nearly everythings.
|
||||
"""The main changelog class that define nearly everythings.
|
||||
|
||||
This was supposed to be a very shorty script this is why it is all-in-one...
|
||||
Factory and base-object are mixed.
|
||||
/// warning
|
||||
Factory and base-objects are mixed in the same class.
|
||||
///
|
||||
"""
|
||||
|
||||
ar_Klass: list[ChangeLogFormater] = []
|
||||
ar_LinesResult: list[ChangeLogFormater] = []
|
||||
prefix: str = ""
|
||||
prefix: str = "^\s+"
|
||||
title: str = "Others :"
|
||||
checkCommentPattern: str = r"^[ \t]*(?:\/\/|#)"
|
||||
keywords: list[str] = []
|
||||
priority: int = 0
|
||||
|
||||
def __init__(self, scope: str | None, ChangelogString: str):
|
||||
"""Main ChangeLogFormater class constructor
|
||||
|
||||
This class contain both formater and factory.
|
||||
|
||||
/// warning
|
||||
this class does not aim to be instantiated by user.
|
||||
///
|
||||
Args:
|
||||
scope: scope of the formater (tag)
|
||||
ChangelogString: formater rendered title
|
||||
"""
|
||||
self._scope = scope
|
||||
self._ChangelogString = ChangelogString.strip()
|
||||
|
||||
def RenderLine(self):
|
||||
"""return a rendered line"""
|
||||
"""Get a rendered line
|
||||
Returns:
|
||||
the rendered line
|
||||
"""
|
||||
return self._ChangelogString.strip()
|
||||
|
||||
@classmethod
|
||||
def RenderLines(cls) -> str:
|
||||
"""render all lines"""
|
||||
"""Render all lines
|
||||
Returns:
|
||||
the rendered lines
|
||||
"""
|
||||
changelog_category: str = ""
|
||||
lines = cls.GetLines()
|
||||
if len(lines) > 0:
|
||||
@@ -61,24 +86,41 @@ class ChangeLogFormater(ABC):
|
||||
return changelog_category
|
||||
|
||||
def GetScope(self) -> str:
|
||||
"""return the current scope (category)"""
|
||||
"""Return the current scope (category)
|
||||
Returns:
|
||||
the current scope
|
||||
"""
|
||||
return self._scope if self._scope is not None else ""
|
||||
|
||||
@classmethod
|
||||
def Clear(cls) -> None:
|
||||
"""clear internal memory"""
|
||||
"""Clear internal memory"""
|
||||
ChangeLogFormater.ar_LinesResult = []
|
||||
|
||||
@classmethod
|
||||
def CheckLine(cls, content: str) -> bool:
|
||||
"""check if a line is in the current scope (lazy identification)"""
|
||||
def CheckLine(cls, content: str) -> re.Match:
|
||||
"""Check if a line is in the current scope (lazy identification)
|
||||
only formal tags are parsed by this function
|
||||
eg: <change_type>(<change_target>): <change_message>
|
||||
Args:
|
||||
content: line to parse
|
||||
Returns:
|
||||
match object
|
||||
"""
|
||||
regex = re.compile(r"^(?:-\s+)?(?:{0})(?:\((.*)\))?(?::)(?:\s*)([^\s].+)".format(cls.prefix))
|
||||
_match = regex.match(content)
|
||||
return _match
|
||||
|
||||
@classmethod
|
||||
def CheckLine_keywords(cls, content: str) -> bool:
|
||||
"""check if a line is in the current scope (deeper in-word identification)"""
|
||||
"""Check if a line is in the current scope (deeper in-word identification)
|
||||
any word in the message can be used to categorize this message.
|
||||
this function test only for the current category.
|
||||
Args:
|
||||
content: line to parse
|
||||
Returns:
|
||||
True if a keyword has matched
|
||||
"""
|
||||
keyword_list = cls.keywords
|
||||
for _keyword in keyword_list:
|
||||
if (_keyword != "") and re.search(_keyword, content):
|
||||
@@ -87,7 +129,14 @@ class ChangeLogFormater(ABC):
|
||||
|
||||
@classmethod
|
||||
def FactoryProcessLineMain(cls, RawChangelogLine: str) -> ChangeLogFormater:
|
||||
"""Process a line and look for identified ones"""
|
||||
"""Process a line and look for identified ones
|
||||
this function will try to apply every available formater for the 1st search round: formal search
|
||||
order of search is set according to formater's configuration
|
||||
Args:
|
||||
RawChangelogLine: line to parse
|
||||
Returns:
|
||||
a corresponding ChangeLogFormater_XXX() object, or a ChangeLogFormater_others()
|
||||
"""
|
||||
for Klass in sorted(ChangeLogFormater.ar_Klass, key=lambda x: x.priority):
|
||||
content = Klass.CheckLine(RawChangelogLine)
|
||||
if content is not None:
|
||||
@@ -96,7 +145,14 @@ class ChangeLogFormater(ABC):
|
||||
|
||||
@classmethod
|
||||
def FactoryProcessLineSecond(cls, RawChangelogLine: str) -> ChangeLogFormater:
|
||||
"""Process a line and look for non-identified ones"""
|
||||
"""Process a line and look for non-identified ones
|
||||
this function will try to apply every available formater for the 2ns search round: any keyword
|
||||
order of search is set according to formater's configuration
|
||||
Args:
|
||||
RawChangelogLine: line to parse
|
||||
Returns:
|
||||
a corresponding ChangeLogFormater_XXX() object, or a ChangeLogFormater_others()
|
||||
"""
|
||||
for Klass in sorted(ChangeLogFormater.ar_Klass, key=lambda x: x.priority, reverse=True):
|
||||
if Klass.CheckLine_keywords(RawChangelogLine):
|
||||
return Klass(None, RawChangelogLine)
|
||||
@@ -105,16 +161,29 @@ class ChangeLogFormater(ABC):
|
||||
|
||||
@classmethod
|
||||
def FactoryProcessFullChangelog(cls, RawChangelogMessage: str) -> list[ChangeLogFormater]:
|
||||
"""Process all input lines"""
|
||||
"""Process all input lines
|
||||
This function handle the main 2-round changes search algo.
|
||||
Tt takes care of search-order and automatically skip any non-relevants message line.
|
||||
A non relevant line can be a commented one, or a to short one.
|
||||
Available comment patterns are: // and #
|
||||
A relevant commit line must contain:
|
||||
- at least 2 words for formal
|
||||
- at least 3 words for keywords
|
||||
Args:
|
||||
RawChangelogMessage: The full raw changelog (merged commit-history)
|
||||
Returns:
|
||||
a list of ChangeLogFormater_XXX() object
|
||||
"""
|
||||
LinesResult = []
|
||||
Lines2ndRound = []
|
||||
|
||||
for line in RawChangelogMessage.split("\n"):
|
||||
if line.strip() != "":
|
||||
lineWordsCount = len(line.split())
|
||||
if (lineWordsCount > 1) and (not re.match(cls.checkCommentPattern, line)):
|
||||
res = cls.FactoryProcessLineMain(line)
|
||||
if res is not ChangeLogFormater_others:
|
||||
if type(res) is not ChangeLogFormater_others:
|
||||
LinesResult.append(res)
|
||||
else:
|
||||
elif lineWordsCount > 2:
|
||||
Lines2ndRound.append(line)
|
||||
|
||||
for line in Lines2ndRound:
|
||||
@@ -125,76 +194,93 @@ class ChangeLogFormater(ABC):
|
||||
|
||||
@classmethod
|
||||
def GetLinesOfType(cls, Klass: type) -> list[ChangeLogFormater]:
|
||||
"""retrieve all lines of specified type"""
|
||||
"""Retrieve all lines of specified formater type
|
||||
Args:
|
||||
Klass: type of formater to get
|
||||
Returns:
|
||||
a list of ChangeLogFormater_XXX() object
|
||||
"""
|
||||
return [_ for _ in ChangeLogFormater.ar_LinesResult if isinstance(_, Klass)]
|
||||
|
||||
@classmethod
|
||||
def GetLines(cls) -> list[ChangeLogFormater]:
|
||||
"""retrieve all lines for the current formater"""
|
||||
"""Retrieve all lines for the current formater
|
||||
Returns:
|
||||
a list of ChangeLogFormater_XXX() object
|
||||
"""
|
||||
return ChangeLogFormater.GetLinesOfType(cls)
|
||||
|
||||
@classmethod
|
||||
def RenderFullChangelog(cls) -> str:
|
||||
"""render the main changelog"""
|
||||
def RenderFullChangelog(cls, include_unknown: bool = False) -> str:
|
||||
"""Render the main changelog
|
||||
Args:
|
||||
include_unknown: includes unknown lines in an Unknown category
|
||||
Returns:
|
||||
the final formated changelog
|
||||
"""
|
||||
full_changelog = ""
|
||||
for Klass in sorted(ChangeLogFormater.ar_Klass, key=lambda x: x.priority, reverse=True):
|
||||
if (include_unknown is False) and (Klass == ChangeLogFormater_others):
|
||||
continue
|
||||
full_changelog = full_changelog + Klass.RenderLines()
|
||||
return full_changelog
|
||||
|
||||
|
||||
# to avoid writing class, they are initialized with the following structure:
|
||||
# creating category classes: '<NAME>': (priority, ['<prefix1>',...], '<header>')
|
||||
# creating category classes: '<NAME>': ( priority, ['<prefix1>',...],
|
||||
# '<header>'
|
||||
# )
|
||||
#
|
||||
# => priority is both for ordering categories in final changelog
|
||||
# and parsing commit to extract messages
|
||||
#
|
||||
for RecordType, Config in {
|
||||
"break": (
|
||||
20,
|
||||
[],
|
||||
":rotating_light: Breaking changes :rotating_light::",
|
||||
),
|
||||
"feat": (20, ["feat", "new", "create", "add"], "Features :sparkles::"),
|
||||
"fix": (10, ["issue", "problem"], "Fixes :wrench::"),
|
||||
"security": (20, ["safe", "leak"], "Security :shield::"),
|
||||
"chore": (
|
||||
20,
|
||||
["task", "refactor", "build", "better", "improve"],
|
||||
"Chore :building_construction::",
|
||||
),
|
||||
"perf": (
|
||||
0,
|
||||
[
|
||||
"fast",
|
||||
],
|
||||
"Performance Enhancements :rocket::",
|
||||
),
|
||||
"wip": (
|
||||
0,
|
||||
[
|
||||
"temp",
|
||||
],
|
||||
"Work in progress changes :construction::",
|
||||
),
|
||||
"docs": (
|
||||
0,
|
||||
[
|
||||
"doc",
|
||||
],
|
||||
"Documentations :book::",
|
||||
),
|
||||
"style": (
|
||||
5,
|
||||
[
|
||||
"beautify",
|
||||
],
|
||||
"Style :art::",
|
||||
),
|
||||
"refactor": (0, [], "Refactorings :recycle::"),
|
||||
"ci": (0, ["jenkins", "git"], "Continuous Integration :cyclone::"),
|
||||
"test": (15, ["unittest", "check", r"^(?:\s)*test(?:\s)*$"], "Testings :vertical_traffic_light::"),
|
||||
"build": (0, ["compile", "version"], "Builds :package:"),
|
||||
# fmt: off
|
||||
"break": ( 20, ["break"],
|
||||
":rotating_light: Breaking changes :rotating_light::",
|
||||
),
|
||||
"feat": ( 20, ["feat", "new", "create", "add"],
|
||||
"Features :sparkles::"
|
||||
),
|
||||
"fix": ( 0, ["fix","issue", "problem"],
|
||||
"Fixes :wrench::"
|
||||
),
|
||||
"security": ( 20, ["safe", "leak"],
|
||||
"Security :shield::"
|
||||
),
|
||||
"chore": ( 20, ["task", "refactor", "build", "better", "improve"],
|
||||
"Chore :building_construction::",
|
||||
),
|
||||
"perf": ( 0, ["fast", ],
|
||||
"Performance Enhancements :rocket::",
|
||||
),
|
||||
"wip": ( 0, ["temp", ],
|
||||
"Work in progress changes :construction::",
|
||||
),
|
||||
"docs": ( 0, [ "doc", ],
|
||||
"Documentations :book::",
|
||||
),
|
||||
"style": ( 5, ["beautify", ],
|
||||
"Style :art::",
|
||||
),
|
||||
"refactor": ( 0, [],
|
||||
"Refactorings :recycle::"
|
||||
),
|
||||
"ci": ( 0, ["jenkins", "git"],
|
||||
"Continuous Integration :cyclone::"
|
||||
),
|
||||
"test": ( -5, ["unittest", "check", r"^(?:\s)*test(?:\s)*$"],
|
||||
"Testings :vertical_traffic_light::"
|
||||
),
|
||||
"build": ( 0, ["compile", "version"],
|
||||
"Builds :package:"
|
||||
),
|
||||
# fmt: on
|
||||
}.items():
|
||||
# then we instantiate all of them
|
||||
name = f"ChangeLogFormater_{RecordType}"
|
||||
tmp = globals()[name] = type(
|
||||
name,
|
||||
_name = f"ChangeLogFormater_{RecordType}"
|
||||
_tmp = globals()[_name] = type(
|
||||
_name,
|
||||
(ChangeLogFormater,),
|
||||
{
|
||||
"prefix": RecordType,
|
||||
@@ -203,12 +289,12 @@ for RecordType, Config in {
|
||||
"priority": Config[0],
|
||||
},
|
||||
)
|
||||
ChangeLogFormater.ar_Klass.append(tmp)
|
||||
ChangeLogFormater.ar_Klass.append(_tmp)
|
||||
|
||||
|
||||
@ChangeLogFormaterRecordType
|
||||
class ChangeLogFormater_revert(ChangeLogFormater):
|
||||
"""revert scope formater"""
|
||||
"""Revert scope formater"""
|
||||
|
||||
prefix: str = "revert"
|
||||
title: str = "Reverts :back::"
|
||||
@@ -216,12 +302,16 @@ class ChangeLogFormater_revert(ChangeLogFormater):
|
||||
priority: int = 0
|
||||
|
||||
def RenderLine(self) -> str:
|
||||
"""an overloaded RenderLine implementation that adds surrounding '~~'
|
||||
Returns:
|
||||
the rendered pattern
|
||||
"""
|
||||
return "~~" + super().RenderLine() + "~~"
|
||||
|
||||
|
||||
@ChangeLogFormaterRecordType
|
||||
class ChangeLogFormater_others(ChangeLogFormater):
|
||||
"""others / unknown scope formater"""
|
||||
"""Others / unknown scope formater"""
|
||||
|
||||
prefix: str = "other"
|
||||
title: str = "Others :question::"
|
||||
|
||||
@@ -18,82 +18,122 @@ class Testtest_module(unittest.TestCase):
|
||||
for test in teststrs:
|
||||
self.assertIn(test, changelog)
|
||||
|
||||
def test_simplegeneration_ignored2(self):
|
||||
raw = "break: testbreak break" + "\n" + "#docs: testdoc doc" + "\n" + "#style: teststyle beautify" + "\n" + "//test: testtest check"
|
||||
|
||||
pychangelogfactory.ChangeLogFormater.FactoryProcessFullChangelog(raw)
|
||||
changelog = pychangelogfactory.ChangeLogFormater.RenderFullChangelog()
|
||||
|
||||
self.assertIn("testbreak", changelog)
|
||||
self.assertNotIn("testdoc", changelog)
|
||||
self.assertNotIn("teststyle", changelog)
|
||||
self.assertNotIn("testtest", changelog)
|
||||
|
||||
def test_simplegeneration_ignored(self):
|
||||
raw = "break: testbreak" + "\n" + "#docs: testdoc" + "\n" + "#style: teststyle" + "\n" + "//test: testtest"
|
||||
|
||||
pychangelogfactory.ChangeLogFormater.FactoryProcessFullChangelog(raw)
|
||||
changelog = pychangelogfactory.ChangeLogFormater.RenderFullChangelog()
|
||||
self.assertIn("testbreak", changelog)
|
||||
self.assertNotIn("testdoc", changelog)
|
||||
self.assertNotIn("teststyle", changelog)
|
||||
self.assertNotIn("testtest", changelog)
|
||||
|
||||
def test_simplegeneration_order(self):
|
||||
raw = "break: testbreak" + "\n" + "docs: testdoc" + "\n" + "style: teststyle" + "\n" + "test: testtest"
|
||||
pychangelogfactory.ChangeLogFormater.FactoryProcessFullChangelog(raw)
|
||||
changelog = pychangelogfactory.ChangeLogFormater.RenderFullChangelog().splitlines()
|
||||
self.assertIn("testbreak", changelog[1])
|
||||
self.assertIn("testtest", changelog[3])
|
||||
self.assertIn("teststyle", changelog[5])
|
||||
self.assertIn("testdoc", changelog[7])
|
||||
self.assertIn("teststyle", changelog[3])
|
||||
self.assertIn("testdoc", changelog[5])
|
||||
self.assertIn("testtest", changelog[7])
|
||||
|
||||
def test_simplegeneration_multiple(self):
|
||||
raw = "break: testbreak" + "\n" + "docs: testdoc" + "\n" + "style: teststyle"
|
||||
|
||||
self.simplegeneration(raw, ["testbreak", "testdoc", "teststyle"])
|
||||
|
||||
def test_simplegeneration_breaking(self):
|
||||
self.simplegeneration("break: teststring", ["teststring"])
|
||||
self.simplegeneration("test break", ["test break"])
|
||||
self.simplegeneration("test break dummy1 dummy2", ["test break"])
|
||||
|
||||
def test_simplegeneration_features(self):
|
||||
self.simplegeneration("feat: teststring", ["teststring"])
|
||||
self.simplegeneration("test feat", ["test feat"])
|
||||
self.simplegeneration("test new", ["test new"])
|
||||
self.simplegeneration("test create", ["test create"])
|
||||
self.simplegeneration("test add", ["test add"])
|
||||
self.simplegeneration("test feat dummy1 dummy2", ["test feat"])
|
||||
self.simplegeneration("test new dummy1 dummy2", ["test new"])
|
||||
self.simplegeneration("test create dummy1 dummy2", ["test create"])
|
||||
self.simplegeneration("test add dummy1 dummy2", ["test add"])
|
||||
|
||||
def test_simplegeneration_fix(self):
|
||||
self.simplegeneration("fix: teststring", ["teststring"])
|
||||
self.simplegeneration("test fix", ["test fix"])
|
||||
self.simplegeneration("test issue", ["test issue"])
|
||||
self.simplegeneration("test problem", ["test problem"])
|
||||
self.simplegeneration("test fix dummy1 dummy2", ["test fix"])
|
||||
self.simplegeneration("test issue dummy1 dummy2", ["test issue"])
|
||||
self.simplegeneration("test problem dummy1 dummy2", ["test problem"])
|
||||
|
||||
def test_simplegeneration_security(self):
|
||||
self.simplegeneration("security: teststring", ["teststring"])
|
||||
self.simplegeneration("test safe", ["test safe"])
|
||||
self.simplegeneration("test leak", ["test leak"])
|
||||
self.simplegeneration("test safe dummy1 dummy2", ["test safe"])
|
||||
self.simplegeneration("test leak dummy1 dummy2", ["test leak"])
|
||||
|
||||
def test_simplegeneration_task(self):
|
||||
self.simplegeneration("task: teststring", ["teststring"])
|
||||
self.simplegeneration("test refactor", ["test refactor"])
|
||||
self.simplegeneration("test build", ["test build"])
|
||||
self.simplegeneration("test better", ["test better"])
|
||||
self.simplegeneration("test improve", ["test improve"])
|
||||
def test_simplegeneration_chore(self):
|
||||
self.simplegeneration("chore: teststring", ["teststring"])
|
||||
self.simplegeneration("chore refactor dummy1 dummy2", ["chore refactor"])
|
||||
self.simplegeneration("chore build dummy1 dummy2", ["chore build"])
|
||||
self.simplegeneration("chore better dummy1 dummy2", ["chore better"])
|
||||
self.simplegeneration("chore improve dummy1 dummy2", ["chore improve"])
|
||||
|
||||
def test_simplegeneration_perf(self):
|
||||
self.simplegeneration("perf: teststring", ["teststring"])
|
||||
self.simplegeneration("test fast", ["test fast"])
|
||||
self.simplegeneration("test fast dummy1 dummy2", ["test fast"])
|
||||
|
||||
def test_simplegeneration_wip(self):
|
||||
self.simplegeneration("wip: teststring", ["teststring"])
|
||||
self.simplegeneration("test temp", ["test temp"])
|
||||
self.simplegeneration("test temp dummy1 dummy2", ["test temp"])
|
||||
|
||||
def test_simplegeneration_docs(self):
|
||||
self.simplegeneration("docs: teststring", ["teststring"])
|
||||
self.simplegeneration("test doc", ["test doc"])
|
||||
self.simplegeneration("test doc dummy1 dummy2", ["test doc"])
|
||||
|
||||
def test_simplegeneration_style(self):
|
||||
self.simplegeneration("style: teststring", ["teststring"])
|
||||
self.simplegeneration("test beautify", ["test beautify"])
|
||||
self.simplegeneration("test beautify dummy1 dummy2", ["test beautify"])
|
||||
|
||||
def test_simplegeneration_refactor(self):
|
||||
self.simplegeneration("refactor: teststring", ["teststring"])
|
||||
|
||||
def test_simplegeneration_ci(self):
|
||||
self.simplegeneration("ci: teststring", ["teststring"])
|
||||
self.simplegeneration("test jenkins", ["test jenkins"])
|
||||
self.simplegeneration("test git", ["test git"])
|
||||
self.simplegeneration("test jenkins dummy1 dummy2", ["test jenkins"])
|
||||
self.simplegeneration("test git dummy1 dummy2", ["test git"])
|
||||
|
||||
def test_simplegeneration_test(self):
|
||||
self.simplegeneration("test: teststring", ["teststring"])
|
||||
self.simplegeneration("test unittest", ["test unittest"])
|
||||
self.simplegeneration("test check", ["test check"])
|
||||
self.simplegeneration("test unittest dummy1 dummy2", ["test unittest"])
|
||||
self.simplegeneration("test check dummy1 dummy2", ["test check"])
|
||||
|
||||
def test_simplegeneration_build(self):
|
||||
self.simplegeneration("build: teststring", ["teststring"])
|
||||
self.simplegeneration("test compile", ["test compile"])
|
||||
self.simplegeneration("test version", ["test version"])
|
||||
self.simplegeneration("test compile dummy1 dummy2", ["test compile"])
|
||||
self.simplegeneration("test version dummy1 dummy2", ["test version"])
|
||||
|
||||
def test_simplegeneration_revert(self):
|
||||
self.simplegeneration("revert: teststring", ["~~teststring~~"])
|
||||
|
||||
def test_sample(self):
|
||||
raw_changelog = (
|
||||
"feat: add a nice feature to the project\n"
|
||||
"style: reindent the full Foo class\n"
|
||||
"security: fix a security leak on the Foo2 component"
|
||||
)
|
||||
pychangelogfactory.ChangeLogFormater.FactoryProcessFullChangelog(raw_changelog)
|
||||
changelog = pychangelogfactory.ChangeLogFormater.RenderFullChangelog()
|
||||
|
||||
expected_formated = (
|
||||
"#### Features :sparkles::\n"
|
||||
"> add a nice feature to the project\n"
|
||||
"#### Security :shield::\n"
|
||||
"> fix a security leak on the Foo2 component\n"
|
||||
"#### Style :art::\n"
|
||||
"> reindent the full Foo class\n"
|
||||
)
|
||||
|
||||
self.assertEqual(changelog, expected_formated)
|
||||
|
||||
Reference in New Issue
Block a user