-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path__init__.py
More file actions
216 lines (188 loc) · 7.83 KB
/
__init__.py
File metadata and controls
216 lines (188 loc) · 7.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
from __future__ import annotations
import os
import re
import sys
from collections import OrderedDict
from textwrap import dedent
from typing import TYPE_CHECKING, Any, ClassVar # noqa: F401
from commitizen import defaults, git
from commitizen.cz.base import BaseCommitizen
from jinja2 import Template
from cz_version_bump.git import repo_name_from_git_remote
from cz_version_bump.thanks import Thanker
if sys.version_info >= (3, 12):
from typing import override
else:
from typing_extensions import override
if TYPE_CHECKING:
from collections.abc import Mapping
from commitizen.question import CzQuestion
issue_id_pattern = re.compile(r"\s+\(#(\d+)\)$")
class MeltanoCommitizen(BaseCommitizen):
bump_pattern = r"^(feat|fix|refactor|perf|break|docs|ci|chore|style|revert|test|build|packaging)(\(.+\))?(!)?" # noqa: E501
bump_map = OrderedDict( # noqa: RUF012
(
(
r"^break",
defaults.MINOR,
), # A major release can only be created explicitly.
(r"^feat", defaults.MINOR),
(r"^fix", defaults.PATCH),
(r"^refactor", defaults.PATCH),
(r"^perf", defaults.PATCH),
(r"^docs", defaults.PATCH),
(r"^ci", defaults.PATCH),
(r"^chore", defaults.PATCH),
(r"^style", defaults.PATCH),
(r"^revert", defaults.PATCH),
(r"^test", defaults.PATCH),
(r"^build", defaults.PATCH),
(r"^packaging", defaults.PATCH),
)
)
commit_parser = r"^(?P<change_type>feat|fix|refactor|perf|break|docs|packaging)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:\s(?P<message>.*)?" # noqa: E501
change_type_order = [ # noqa: RUF012
"BREAKING CHANGES",
"✨ New",
"🐛 Fixes",
"⚙️ Under the Hood",
"⚡ Performance Improvements",
"📚 Documentation Improvements",
"📦 Packaging changes",
]
change_type_map = { # noqa: RUF012
"break": "BREAKING CHANGES",
"feat": "✨ New",
"fix": "🐛 Fixes",
"refactor": "⚙️ Under the Hood",
"docs": "📚 Documentation Improvements",
"perf": "⚡ Performance Improvements",
"packaging": "📦 Packaging changes",
}
@override
def __init__(self: MeltanoCommitizen, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.repo_name = os.environ.get(
"GITHUB_REPOSITORY",
repo_name_from_git_remote(),
)
self.thanker = Thanker(self.repo_name)
@override
def schema(self: MeltanoCommitizen) -> str:
"""Schema definition of the commit message."""
return dedent(
"""
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
(BREAKING CHANGE: )<footer>
"""
).strip("\n")
@override
def schema_pattern(self: MeltanoCommitizen) -> str:
"""Regex matching the schema used for message validation."""
return r"(feat|fix|refactor|perf|break|docs|ci|chore|style|revert|test|build|packaging)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:(\s.*)" # noqa: E501
@override
def questions(self: MeltanoCommitizen) -> list[CzQuestion]:
"""Questions regarding the commit message."""
return [
{
"type": "list",
"name": "change_type",
"choices": [
{"value": "feat", "name": "feat: A new feature."},
{"value": "fix", "name": "fix: A bug fix."},
{
"value": "refactor",
"name": "refactor: A code change that neither fixes a bug nor adds a feature.", # noqa: E501
},
{
"value": "perf",
"name": "perf: A code change that improves performance.",
},
{"value": "docs", "name": "docs: A documentation change."},
{"value": "break", "name": "break: A breaking change."},
{
"value": "chore",
"name": "chore: A change that doesn't affect the meaning of the codebase.", # noqa: E501
},
{"value": "style", "name": "style: A code style change."},
{"value": "revert", "name": "revert: Revert to a commit."},
{"value": "test", "name": "test: A test change."},
{"value": "build", "name": "build: A build system change."},
{"value": "ci", "name": "ci: A change to CI/CD."},
{
"value": "packaging",
"name": "packaging: A change to how the project is packaged or distributed.", # noqa: E501
},
],
"message": "Select the type of change you are committing",
},
{
"type": "input",
"name": "message",
"message": "Subject",
},
]
@override
def message(self: MeltanoCommitizen, answers: Mapping[str, Any]) -> str:
"""Format the git message."""
message_template = Template("{{change_type}}: {{message}}")
return message_template.render(**answers)
@override
def changelog_message_builder_hook(
self: MeltanoCommitizen,
parsed_message: dict[str, Any],
commit: git.GitCommit,
) -> dict:
"""Alter each git log line of the changelog.
Parameters:
parsed_message: The commit message as parsed by `self.commit_parser`.
commit: The commit object from which the message was obtained.
Returns:
The updated parsed commit message to be written into the changelog.
"""
message = parsed_message["message"]
# Capitalize the first letter of the message. If the message begins
# with punctuation, it is unaffected.
message = message[:1].upper() + message[1:]
try:
# Convert to int then back to str to validate that it is an integer:
issue_id = str(int(issue_id_pattern.findall(message)[0]))
message = issue_id_pattern.sub("", message)
except Exception: # noqa: BLE001, S110
pass
else:
# NOTE: The "issue ID" will usually be for a pull request. GitHub considers
# PRs to be issues in their APIs, but not vice versa.
parsed_message["message"] = (
f"[#{issue_id}](https://github.com/{self.repo_name}/issues/{issue_id}) {message}" # noqa: E501
)
# Remove the scope because we are too inconsistent with them.
parsed_message["scope"] = None
# Thank third-party contributors:
parsed_message["message"] += self.thanker.thanks_message(commit)
# Remove the commit message body because is isn't needed for the changelog, and
# can cause formatting issues if present.
commit.body = ""
return parsed_message
@override
def changelog_hook(
self: MeltanoCommitizen,
full_changelog: str,
partial_changelog: str | None,
) -> str:
"""Perform custom action at the end of changelog generation.
full_changelog: The full changelog about to being written into the file.
partial_changelog: The new content in the changelog.
Return:
The full changelog.
"""
return full_changelog
@override
def example(self: MeltanoCommitizen) -> str:
return "feat: Add a new feature"
@override
def info(self: MeltanoCommitizen) -> str:
return "Commitizen customized for Meltano projects (https://commitizen-tools.github.io/commitizen/customization)"