ai-cr 3.2.2__py3-none-any.whl → 3.3.0__py3-none-any.whl
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.
- {ai_cr-3.2.2.dist-info → ai_cr-3.3.0.dist-info}/LICENSE +21 -21
- {ai_cr-3.2.2.dist-info → ai_cr-3.3.0.dist-info}/METADATA +1 -1
- ai_cr-3.3.0.dist-info/RECORD +41 -0
- {ai_cr-3.2.2.dist-info → ai_cr-3.3.0.dist-info}/WHEEL +1 -1
- gito/__main__.py +4 -4
- gito/bootstrap.py +90 -90
- gito/cli.py +255 -244
- gito/cli_base.py +104 -94
- gito/commands/__init__.py +1 -1
- gito/commands/deploy.py +138 -138
- gito/commands/fix.py +160 -160
- gito/commands/gh_post_review_comment.py +111 -111
- gito/commands/gh_react_to_comment.py +217 -217
- gito/commands/linear_comment.py +53 -53
- gito/commands/repl.py +30 -30
- gito/commands/version.py +8 -8
- gito/config.toml +450 -448
- gito/constants.py +15 -14
- gito/context.py +19 -19
- gito/core.py +520 -508
- gito/env.py +8 -7
- gito/gh_api.py +116 -116
- gito/issue_trackers.py +50 -50
- gito/pipeline.py +83 -83
- gito/pipeline_steps/jira.py +62 -62
- gito/pipeline_steps/linear.py +85 -85
- gito/project_config.py +85 -85
- gito/report_struct.py +136 -136
- gito/tpl/answer.j2 +25 -25
- gito/tpl/github_workflows/components/env-vars.j2 +11 -11
- gito/tpl/github_workflows/components/installs.j2 +23 -23
- gito/tpl/github_workflows/gito-code-review.yml.j2 +32 -32
- gito/tpl/github_workflows/gito-react-to-comments.yml.j2 +70 -70
- gito/tpl/partial/aux_files.j2 +8 -8
- gito/tpl/questions/changes_summary.j2 +55 -55
- gito/tpl/questions/release_notes.j2 +26 -26
- gito/tpl/questions/test_cases.j2 +37 -37
- gito/utils.py +267 -267
- ai_cr-3.2.2.dist-info/RECORD +0 -41
- {ai_cr-3.2.2.dist-info → ai_cr-3.3.0.dist-info}/entry_points.txt +0 -0
gito/utils.py
CHANGED
@@ -1,267 +1,267 @@
|
|
1
|
-
import logging
|
2
|
-
import re
|
3
|
-
import sys
|
4
|
-
import os
|
5
|
-
from dataclasses import fields
|
6
|
-
from pathlib import Path
|
7
|
-
import importlib.metadata
|
8
|
-
from typing import Optional
|
9
|
-
|
10
|
-
import typer
|
11
|
-
import git
|
12
|
-
from git import Repo
|
13
|
-
|
14
|
-
|
15
|
-
_EXT_TO_HINT: dict[str, str] = {
|
16
|
-
# scripting & languages
|
17
|
-
".py": "python",
|
18
|
-
".js": "javascript",
|
19
|
-
".ts": "typescript",
|
20
|
-
".java": "java",
|
21
|
-
".c": "c",
|
22
|
-
".cpp": "cpp",
|
23
|
-
".cc": "cpp",
|
24
|
-
".cxx": "cpp",
|
25
|
-
".h": "cpp",
|
26
|
-
".hpp": "cpp",
|
27
|
-
".cs": "csharp",
|
28
|
-
".rb": "ruby",
|
29
|
-
".go": "go",
|
30
|
-
".rs": "rust",
|
31
|
-
".swift": "swift",
|
32
|
-
".kt": "kotlin",
|
33
|
-
".scala": "scala",
|
34
|
-
".dart": "dart",
|
35
|
-
".php": "php",
|
36
|
-
".pl": "perl",
|
37
|
-
".pm": "perl",
|
38
|
-
".lua": "lua",
|
39
|
-
# web & markup
|
40
|
-
".html": "html",
|
41
|
-
".htm": "html",
|
42
|
-
".css": "css",
|
43
|
-
".scss": "scss",
|
44
|
-
".less": "less",
|
45
|
-
".json": "json",
|
46
|
-
".xml": "xml",
|
47
|
-
".yaml": "yaml",
|
48
|
-
".yml": "yaml",
|
49
|
-
".toml": "toml",
|
50
|
-
".ini": "ini",
|
51
|
-
".csv": "csv",
|
52
|
-
".md": "markdown",
|
53
|
-
".rst": "rest",
|
54
|
-
# shell & config
|
55
|
-
".sh": "bash",
|
56
|
-
".bash": "bash",
|
57
|
-
".zsh": "bash",
|
58
|
-
".fish": "bash",
|
59
|
-
".ps1": "powershell",
|
60
|
-
".dockerfile": "dockerfile",
|
61
|
-
# build & CI
|
62
|
-
".makefile": "makefile",
|
63
|
-
".mk": "makefile",
|
64
|
-
"CMakeLists.txt": "cmake",
|
65
|
-
"Dockerfile": "dockerfile",
|
66
|
-
".gradle": "groovy",
|
67
|
-
".travis.yml": "yaml",
|
68
|
-
# data & queries
|
69
|
-
".sql": "sql",
|
70
|
-
".graphql": "graphql",
|
71
|
-
".proto": "protobuf",
|
72
|
-
".yara": "yara",
|
73
|
-
}
|
74
|
-
|
75
|
-
|
76
|
-
def syntax_hint(file_path: str | Path) -> str:
|
77
|
-
"""
|
78
|
-
Returns a syntax highlighting hint based on the file's extension or name.
|
79
|
-
|
80
|
-
This can be used to annotate code blocks for rendering with syntax highlighting,
|
81
|
-
e.g., using Markdown-style code blocks: ```<syntax_hint>\n<code>\n```.
|
82
|
-
|
83
|
-
Args:
|
84
|
-
file_path (str | Path): Path to the file.
|
85
|
-
|
86
|
-
Returns:
|
87
|
-
str: A syntax identifier suitable for code highlighting (e.g., 'python', 'json').
|
88
|
-
"""
|
89
|
-
p = Path(file_path)
|
90
|
-
ext = p.suffix.lower()
|
91
|
-
if not ext:
|
92
|
-
name = p.name.lower()
|
93
|
-
if name == "dockerfile":
|
94
|
-
return "dockerfile"
|
95
|
-
return ""
|
96
|
-
return _EXT_TO_HINT.get(ext, ext.lstrip("."))
|
97
|
-
|
98
|
-
|
99
|
-
def is_running_in_github_action():
|
100
|
-
return os.getenv("GITHUB_ACTIONS") == "true"
|
101
|
-
|
102
|
-
|
103
|
-
def no_subcommand(app: typer.Typer) -> bool:
|
104
|
-
"""
|
105
|
-
Checks if the current script is being invoked as a command in a target Typer application.
|
106
|
-
"""
|
107
|
-
return not (
|
108
|
-
(first_arg := next((a for a in sys.argv[1:] if not a.startswith('-')), None))
|
109
|
-
and first_arg in (
|
110
|
-
cmd.name or cmd.callback.__name__.replace('_', '-')
|
111
|
-
for cmd in app.registered_commands
|
112
|
-
)
|
113
|
-
or '--help' in sys.argv
|
114
|
-
)
|
115
|
-
|
116
|
-
|
117
|
-
def parse_refs_pair(refs: str) -> tuple[str | None, str | None]:
|
118
|
-
SEPARATOR = '..'
|
119
|
-
if not refs:
|
120
|
-
return None, None
|
121
|
-
if SEPARATOR not in refs:
|
122
|
-
return refs, None
|
123
|
-
what, against = refs.split(SEPARATOR, 1)
|
124
|
-
return what or None, against or None
|
125
|
-
|
126
|
-
|
127
|
-
def max_line_len(text: str) -> int:
|
128
|
-
return max((len(line) for line in text.splitlines()), default=0)
|
129
|
-
|
130
|
-
|
131
|
-
def block_wrap_lr(
|
132
|
-
text: str,
|
133
|
-
left: str = "",
|
134
|
-
right: str = "",
|
135
|
-
max_rwrap: int = 60,
|
136
|
-
min_wrap: int = 0,
|
137
|
-
) -> str:
|
138
|
-
ml = max(max_line_len(text), min_wrap)
|
139
|
-
lines = text.splitlines()
|
140
|
-
wrapped_lines = []
|
141
|
-
for line in lines:
|
142
|
-
ln = left+line
|
143
|
-
if ml <= max_rwrap:
|
144
|
-
ln += ' ' * (ml - len(line)) + right
|
145
|
-
wrapped_lines.append(ln)
|
146
|
-
return "\n".join(wrapped_lines)
|
147
|
-
|
148
|
-
|
149
|
-
def extract_gh_owner_repo(repo: git.Repo) -> tuple[str, str]:
|
150
|
-
"""
|
151
|
-
Extracts the GitHub owner and repository name.
|
152
|
-
|
153
|
-
Returns:
|
154
|
-
tuple[str, str]: A tuple containing the owner and repository name.
|
155
|
-
"""
|
156
|
-
remote_url = repo.remotes.origin.url
|
157
|
-
if remote_url.startswith('git@github.com:'):
|
158
|
-
# SSH format: git@github.com:owner/repo.git
|
159
|
-
repo_path = remote_url.split(':')[1].replace('.git', '')
|
160
|
-
elif remote_url.startswith('https://github.com/'):
|
161
|
-
# HTTPS format: https://github.com/owner/repo.git
|
162
|
-
repo_path = remote_url.replace('https://github.com/', '').replace('.git', '')
|
163
|
-
else:
|
164
|
-
raise ValueError("Unsupported remote URL format")
|
165
|
-
owner, repo_name = repo_path.split('/')
|
166
|
-
return owner, repo_name
|
167
|
-
|
168
|
-
|
169
|
-
def detect_github_env() -> dict:
|
170
|
-
"""
|
171
|
-
Try to detect GitHub repository/PR info from environment variables (for GitHub Actions).
|
172
|
-
Returns a dict with github_repo, github_pr_sha, github_pr_number, github_ref, etc.
|
173
|
-
"""
|
174
|
-
repo = os.environ.get("GITHUB_REPOSITORY", "")
|
175
|
-
pr_sha = os.environ.get("GITHUB_SHA", "")
|
176
|
-
pr_number = os.environ.get("GITHUB_REF", "")
|
177
|
-
branch = ""
|
178
|
-
ref = os.environ.get("GITHUB_REF", "")
|
179
|
-
# Try to resolve PR head SHA if available.
|
180
|
-
# On PRs, GITHUB_HEAD_REF/BASE_REF contain branch names.
|
181
|
-
if "GITHUB_HEAD_REF" in os.environ:
|
182
|
-
branch = os.environ["GITHUB_HEAD_REF"]
|
183
|
-
elif ref.startswith("refs/heads/"):
|
184
|
-
branch = ref[len("refs/heads/"):]
|
185
|
-
elif ref.startswith("refs/pull/"):
|
186
|
-
# for pull_request events
|
187
|
-
branch = ref
|
188
|
-
|
189
|
-
d = {
|
190
|
-
"github_repo": repo,
|
191
|
-
"github_pr_sha": pr_sha,
|
192
|
-
"github_pr_number": pr_number,
|
193
|
-
"github_branch": branch,
|
194
|
-
"github_ref": ref,
|
195
|
-
}
|
196
|
-
# Fallback for local usage: try to get from git
|
197
|
-
if not repo:
|
198
|
-
git_repo = None
|
199
|
-
try:
|
200
|
-
git_repo = Repo(
|
201
|
-
origin = git_repo.remotes.origin.url
|
202
|
-
# e.g. git@github.com:Nayjest/ai-code-review.git -> Nayjest/ai-code-review
|
203
|
-
match = re.search(r"[:/]([\w\-]+)/([\w\-\.]+?)(\.git)?$", origin)
|
204
|
-
if match:
|
205
|
-
d["github_repo"] = f"{match.group(1)}/{match.group(2)}"
|
206
|
-
d["github_pr_sha"] = git_repo.head.commit.hexsha
|
207
|
-
d["github_branch"] = (
|
208
|
-
git_repo.active_branch.name if hasattr(git_repo, "active_branch") else ""
|
209
|
-
)
|
210
|
-
except Exception:
|
211
|
-
pass
|
212
|
-
finally:
|
213
|
-
if git_repo:
|
214
|
-
try:
|
215
|
-
git_repo.close()
|
216
|
-
except Exception:
|
217
|
-
pass
|
218
|
-
# If branch is not a commit SHA, prefer branch for links
|
219
|
-
if d["github_branch"]:
|
220
|
-
d["github_pr_sha_or_branch"] = d["github_branch"]
|
221
|
-
elif d["github_pr_sha"]:
|
222
|
-
d["github_pr_sha_or_branch"] = d["github_pr_sha"]
|
223
|
-
else:
|
224
|
-
d["github_pr_sha_or_branch"] = "main"
|
225
|
-
return d
|
226
|
-
|
227
|
-
|
228
|
-
def make_streaming_function(handler: Optional[callable] = None) -> callable:
|
229
|
-
def stream(text):
|
230
|
-
if handler:
|
231
|
-
text = handler(text)
|
232
|
-
print(text, end='', flush=True)
|
233
|
-
return stream
|
234
|
-
|
235
|
-
|
236
|
-
def version() -> str:
|
237
|
-
return importlib.metadata.version("gito.bot")
|
238
|
-
|
239
|
-
|
240
|
-
def remove_html_comments(text):
|
241
|
-
"""
|
242
|
-
Removes all HTML comments (<!-- ... -->) from the input text.
|
243
|
-
"""
|
244
|
-
return re.sub(r'<!--.*?-->\s*', '', text, flags=re.DOTALL)
|
245
|
-
|
246
|
-
|
247
|
-
def filter_kwargs(cls, kwargs, log_warnings=True):
|
248
|
-
"""
|
249
|
-
Filters the keyword arguments to only include those that are fields of the given dataclass.
|
250
|
-
Args:
|
251
|
-
cls: The dataclass type to filter against.
|
252
|
-
kwargs: A dictionary of keyword arguments.
|
253
|
-
log_warnings: If True, logs warnings for fields not in the dataclass.
|
254
|
-
Returns:
|
255
|
-
A dictionary containing only the fields that are defined in the dataclass.
|
256
|
-
"""
|
257
|
-
cls_fields = {f.name for f in fields(cls)}
|
258
|
-
filtered = {}
|
259
|
-
for k, v in kwargs.items():
|
260
|
-
if k in cls_fields:
|
261
|
-
filtered[k] = v
|
262
|
-
else:
|
263
|
-
if log_warnings:
|
264
|
-
logging.warning(
|
265
|
-
f"Warning: field '{k}' not in {cls.__name__}, dropping."
|
266
|
-
)
|
267
|
-
return filtered
|
1
|
+
import logging
|
2
|
+
import re
|
3
|
+
import sys
|
4
|
+
import os
|
5
|
+
from dataclasses import fields
|
6
|
+
from pathlib import Path
|
7
|
+
import importlib.metadata
|
8
|
+
from typing import Optional
|
9
|
+
|
10
|
+
import typer
|
11
|
+
import git
|
12
|
+
from git import Repo
|
13
|
+
from .env import Env
|
14
|
+
|
15
|
+
_EXT_TO_HINT: dict[str, str] = {
|
16
|
+
# scripting & languages
|
17
|
+
".py": "python",
|
18
|
+
".js": "javascript",
|
19
|
+
".ts": "typescript",
|
20
|
+
".java": "java",
|
21
|
+
".c": "c",
|
22
|
+
".cpp": "cpp",
|
23
|
+
".cc": "cpp",
|
24
|
+
".cxx": "cpp",
|
25
|
+
".h": "cpp",
|
26
|
+
".hpp": "cpp",
|
27
|
+
".cs": "csharp",
|
28
|
+
".rb": "ruby",
|
29
|
+
".go": "go",
|
30
|
+
".rs": "rust",
|
31
|
+
".swift": "swift",
|
32
|
+
".kt": "kotlin",
|
33
|
+
".scala": "scala",
|
34
|
+
".dart": "dart",
|
35
|
+
".php": "php",
|
36
|
+
".pl": "perl",
|
37
|
+
".pm": "perl",
|
38
|
+
".lua": "lua",
|
39
|
+
# web & markup
|
40
|
+
".html": "html",
|
41
|
+
".htm": "html",
|
42
|
+
".css": "css",
|
43
|
+
".scss": "scss",
|
44
|
+
".less": "less",
|
45
|
+
".json": "json",
|
46
|
+
".xml": "xml",
|
47
|
+
".yaml": "yaml",
|
48
|
+
".yml": "yaml",
|
49
|
+
".toml": "toml",
|
50
|
+
".ini": "ini",
|
51
|
+
".csv": "csv",
|
52
|
+
".md": "markdown",
|
53
|
+
".rst": "rest",
|
54
|
+
# shell & config
|
55
|
+
".sh": "bash",
|
56
|
+
".bash": "bash",
|
57
|
+
".zsh": "bash",
|
58
|
+
".fish": "bash",
|
59
|
+
".ps1": "powershell",
|
60
|
+
".dockerfile": "dockerfile",
|
61
|
+
# build & CI
|
62
|
+
".makefile": "makefile",
|
63
|
+
".mk": "makefile",
|
64
|
+
"CMakeLists.txt": "cmake",
|
65
|
+
"Dockerfile": "dockerfile",
|
66
|
+
".gradle": "groovy",
|
67
|
+
".travis.yml": "yaml",
|
68
|
+
# data & queries
|
69
|
+
".sql": "sql",
|
70
|
+
".graphql": "graphql",
|
71
|
+
".proto": "protobuf",
|
72
|
+
".yara": "yara",
|
73
|
+
}
|
74
|
+
|
75
|
+
|
76
|
+
def syntax_hint(file_path: str | Path) -> str:
|
77
|
+
"""
|
78
|
+
Returns a syntax highlighting hint based on the file's extension or name.
|
79
|
+
|
80
|
+
This can be used to annotate code blocks for rendering with syntax highlighting,
|
81
|
+
e.g., using Markdown-style code blocks: ```<syntax_hint>\n<code>\n```.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
file_path (str | Path): Path to the file.
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
str: A syntax identifier suitable for code highlighting (e.g., 'python', 'json').
|
88
|
+
"""
|
89
|
+
p = Path(file_path)
|
90
|
+
ext = p.suffix.lower()
|
91
|
+
if not ext:
|
92
|
+
name = p.name.lower()
|
93
|
+
if name == "dockerfile":
|
94
|
+
return "dockerfile"
|
95
|
+
return ""
|
96
|
+
return _EXT_TO_HINT.get(ext, ext.lstrip("."))
|
97
|
+
|
98
|
+
|
99
|
+
def is_running_in_github_action():
|
100
|
+
return os.getenv("GITHUB_ACTIONS") == "true"
|
101
|
+
|
102
|
+
|
103
|
+
def no_subcommand(app: typer.Typer) -> bool:
|
104
|
+
"""
|
105
|
+
Checks if the current script is being invoked as a command in a target Typer application.
|
106
|
+
"""
|
107
|
+
return not (
|
108
|
+
(first_arg := next((a for a in sys.argv[1:] if not a.startswith('-')), None))
|
109
|
+
and first_arg in (
|
110
|
+
cmd.name or cmd.callback.__name__.replace('_', '-')
|
111
|
+
for cmd in app.registered_commands
|
112
|
+
)
|
113
|
+
or '--help' in sys.argv
|
114
|
+
)
|
115
|
+
|
116
|
+
|
117
|
+
def parse_refs_pair(refs: str) -> tuple[str | None, str | None]:
|
118
|
+
SEPARATOR = '..'
|
119
|
+
if not refs:
|
120
|
+
return None, None
|
121
|
+
if SEPARATOR not in refs:
|
122
|
+
return refs, None
|
123
|
+
what, against = refs.split(SEPARATOR, 1)
|
124
|
+
return what or None, against or None
|
125
|
+
|
126
|
+
|
127
|
+
def max_line_len(text: str) -> int:
|
128
|
+
return max((len(line) for line in text.splitlines()), default=0)
|
129
|
+
|
130
|
+
|
131
|
+
def block_wrap_lr(
|
132
|
+
text: str,
|
133
|
+
left: str = "",
|
134
|
+
right: str = "",
|
135
|
+
max_rwrap: int = 60,
|
136
|
+
min_wrap: int = 0,
|
137
|
+
) -> str:
|
138
|
+
ml = max(max_line_len(text), min_wrap)
|
139
|
+
lines = text.splitlines()
|
140
|
+
wrapped_lines = []
|
141
|
+
for line in lines:
|
142
|
+
ln = left+line
|
143
|
+
if ml <= max_rwrap:
|
144
|
+
ln += ' ' * (ml - len(line)) + right
|
145
|
+
wrapped_lines.append(ln)
|
146
|
+
return "\n".join(wrapped_lines)
|
147
|
+
|
148
|
+
|
149
|
+
def extract_gh_owner_repo(repo: git.Repo) -> tuple[str, str]:
|
150
|
+
"""
|
151
|
+
Extracts the GitHub owner and repository name.
|
152
|
+
|
153
|
+
Returns:
|
154
|
+
tuple[str, str]: A tuple containing the owner and repository name.
|
155
|
+
"""
|
156
|
+
remote_url = repo.remotes.origin.url
|
157
|
+
if remote_url.startswith('git@github.com:'):
|
158
|
+
# SSH format: git@github.com:owner/repo.git
|
159
|
+
repo_path = remote_url.split(':')[1].replace('.git', '')
|
160
|
+
elif remote_url.startswith('https://github.com/'):
|
161
|
+
# HTTPS format: https://github.com/owner/repo.git
|
162
|
+
repo_path = remote_url.replace('https://github.com/', '').replace('.git', '')
|
163
|
+
else:
|
164
|
+
raise ValueError("Unsupported remote URL format")
|
165
|
+
owner, repo_name = repo_path.split('/')
|
166
|
+
return owner, repo_name
|
167
|
+
|
168
|
+
|
169
|
+
def detect_github_env() -> dict:
|
170
|
+
"""
|
171
|
+
Try to detect GitHub repository/PR info from environment variables (for GitHub Actions).
|
172
|
+
Returns a dict with github_repo, github_pr_sha, github_pr_number, github_ref, etc.
|
173
|
+
"""
|
174
|
+
repo = os.environ.get("GITHUB_REPOSITORY", "")
|
175
|
+
pr_sha = os.environ.get("GITHUB_SHA", "")
|
176
|
+
pr_number = os.environ.get("GITHUB_REF", "")
|
177
|
+
branch = ""
|
178
|
+
ref = os.environ.get("GITHUB_REF", "")
|
179
|
+
# Try to resolve PR head SHA if available.
|
180
|
+
# On PRs, GITHUB_HEAD_REF/BASE_REF contain branch names.
|
181
|
+
if "GITHUB_HEAD_REF" in os.environ:
|
182
|
+
branch = os.environ["GITHUB_HEAD_REF"]
|
183
|
+
elif ref.startswith("refs/heads/"):
|
184
|
+
branch = ref[len("refs/heads/"):]
|
185
|
+
elif ref.startswith("refs/pull/"):
|
186
|
+
# for pull_request events
|
187
|
+
branch = ref
|
188
|
+
|
189
|
+
d = {
|
190
|
+
"github_repo": repo,
|
191
|
+
"github_pr_sha": pr_sha,
|
192
|
+
"github_pr_number": pr_number,
|
193
|
+
"github_branch": branch,
|
194
|
+
"github_ref": ref,
|
195
|
+
}
|
196
|
+
# Fallback for local usage: try to get from git
|
197
|
+
if not repo or repo == "octocat/Hello-World":
|
198
|
+
git_repo = None
|
199
|
+
try:
|
200
|
+
git_repo = Repo(Env.working_folder, search_parent_directories=True)
|
201
|
+
origin = git_repo.remotes.origin.url
|
202
|
+
# e.g. git@github.com:Nayjest/ai-code-review.git -> Nayjest/ai-code-review
|
203
|
+
match = re.search(r"[:/]([\w\-]+)/([\w\-\.]+?)(\.git)?$", origin)
|
204
|
+
if match:
|
205
|
+
d["github_repo"] = f"{match.group(1)}/{match.group(2)}"
|
206
|
+
d["github_pr_sha"] = git_repo.head.commit.hexsha
|
207
|
+
d["github_branch"] = (
|
208
|
+
git_repo.active_branch.name if hasattr(git_repo, "active_branch") else ""
|
209
|
+
)
|
210
|
+
except Exception:
|
211
|
+
pass
|
212
|
+
finally:
|
213
|
+
if git_repo:
|
214
|
+
try:
|
215
|
+
git_repo.close()
|
216
|
+
except Exception:
|
217
|
+
pass
|
218
|
+
# If branch is not a commit SHA, prefer branch for links
|
219
|
+
if d["github_branch"]:
|
220
|
+
d["github_pr_sha_or_branch"] = d["github_branch"]
|
221
|
+
elif d["github_pr_sha"]:
|
222
|
+
d["github_pr_sha_or_branch"] = d["github_pr_sha"]
|
223
|
+
else:
|
224
|
+
d["github_pr_sha_or_branch"] = "main"
|
225
|
+
return d
|
226
|
+
|
227
|
+
|
228
|
+
def make_streaming_function(handler: Optional[callable] = None) -> callable:
|
229
|
+
def stream(text):
|
230
|
+
if handler:
|
231
|
+
text = handler(text)
|
232
|
+
print(text, end='', flush=True)
|
233
|
+
return stream
|
234
|
+
|
235
|
+
|
236
|
+
def version() -> str:
|
237
|
+
return importlib.metadata.version("gito.bot")
|
238
|
+
|
239
|
+
|
240
|
+
def remove_html_comments(text):
|
241
|
+
"""
|
242
|
+
Removes all HTML comments (<!-- ... -->) from the input text.
|
243
|
+
"""
|
244
|
+
return re.sub(r'<!--.*?-->\s*', '', text, flags=re.DOTALL)
|
245
|
+
|
246
|
+
|
247
|
+
def filter_kwargs(cls, kwargs, log_warnings=True):
|
248
|
+
"""
|
249
|
+
Filters the keyword arguments to only include those that are fields of the given dataclass.
|
250
|
+
Args:
|
251
|
+
cls: The dataclass type to filter against.
|
252
|
+
kwargs: A dictionary of keyword arguments.
|
253
|
+
log_warnings: If True, logs warnings for fields not in the dataclass.
|
254
|
+
Returns:
|
255
|
+
A dictionary containing only the fields that are defined in the dataclass.
|
256
|
+
"""
|
257
|
+
cls_fields = {f.name for f in fields(cls)}
|
258
|
+
filtered = {}
|
259
|
+
for k, v in kwargs.items():
|
260
|
+
if k in cls_fields:
|
261
|
+
filtered[k] = v
|
262
|
+
else:
|
263
|
+
if log_warnings:
|
264
|
+
logging.warning(
|
265
|
+
f"Warning: field '{k}' not in {cls.__name__}, dropping."
|
266
|
+
)
|
267
|
+
return filtered
|
ai_cr-3.2.2.dist-info/RECORD
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
gito/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
gito/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
|
3
|
-
gito/bootstrap.py,sha256=hJfRKTmCLKxMgzqWvTYhSF5zFKnS4t2N98BS5E7Ggew,3217
|
4
|
-
gito/cli.py,sha256=h4Bn7BUa4Nxr1yQWcO5s6T-mBRHBJpW60rfpNvOCx5Y,8203
|
5
|
-
gito/cli_base.py,sha256=nJGxE_zcGr51KZ4EXTFLrHwj88JIz3OQyQXL5m22FJY,2573
|
6
|
-
gito/commands/__init__.py,sha256=B2uUQsLMEsHfNT1N3lWYm38WSuQIHFmjiGs2tdBuDBA,55
|
7
|
-
gito/commands/deploy.py,sha256=ybmlBvuDsOXKBhNvYI9xp6ph7LaH0d39stxVUORhEjo,4965
|
8
|
-
gito/commands/fix.py,sha256=kROlQSlW1dDDBvl0tr21vBTuH8APcKjL05xeCOOH_Mc,5375
|
9
|
-
gito/commands/gh_post_review_comment.py,sha256=xrCauuifrUEufBjx43sw5FMtWP9seHiGYzwAq5SFnvQ,3795
|
10
|
-
gito/commands/gh_react_to_comment.py,sha256=QAc5cZOu9GfPtbEOWH6dQYhsgFSSWFWzzZ1RPNzHxFA,7090
|
11
|
-
gito/commands/linear_comment.py,sha256=c67A_v263rYorO43pyvSKqnV4WEVQZM8WEovDDu411M,1484
|
12
|
-
gito/commands/repl.py,sha256=s3GxYw3m7wMi7lMDiVovvVU9xW6keL9nej7OX1lkGUE,631
|
13
|
-
gito/commands/version.py,sha256=OdAuKtjGV9Ok2_igAs-EqdJijXCKU1dcLcA5KEz1ydg,178
|
14
|
-
gito/config.toml,sha256=51TeM_m0_v89u3X1lmafA-D5TqjiXWc-j1W_12VBppg,17777
|
15
|
-
gito/constants.py,sha256=1ElhE4RH0EPEq3xlhwyYRUcgr38X8wXduos0gV9yPy8,819
|
16
|
-
gito/context.py,sha256=OBfcQOREsNx8WHANsplNrnrKYrXz1PyZyne11lSfZjw,446
|
17
|
-
gito/core.py,sha256=nw3eUiLFWDOQJB7N_y6UE5hUrr4cS8xD_Q_RgktepUM,17795
|
18
|
-
gito/env.py,sha256=TVNxqrnLefqfZt5sSg495p8UenSgHaMgTImK1rRMIlY,146
|
19
|
-
gito/gh_api.py,sha256=2yDikXr9BM2tndYpCo-weJxjoCAlEtuPh-QBinlnHtg,4052
|
20
|
-
gito/issue_trackers.py,sha256=XYspyaIuf0ANQSvDUea5_oOdo9tQvpZZsapI5S9g78U,1551
|
21
|
-
gito/pipeline.py,sha256=H6eiyDHUg_p3jxNVhSnyB7EVVNbMIZkA35WMymgPnh0,2610
|
22
|
-
gito/pipeline_steps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
|
-
gito/pipeline_steps/jira.py,sha256=NjFgpGFAkT5PSXi6jQ9Yr8CY9M8sa3_rMb7RFhdpoNg,1862
|
24
|
-
gito/pipeline_steps/linear.py,sha256=6UDc8nGKGpwHruPq8VItE2QBWshWxaTapoMhu_qjN_g,2445
|
25
|
-
gito/project_config.py,sha256=ZdHy4aNzjlVAgTUjmznrGcdd2NHMMvGX8VeSmAnzZ34,3237
|
26
|
-
gito/report_struct.py,sha256=zSMhPojsBIK34Kp9i74qKJ09DupKluuHHsubS1t9_1E,4399
|
27
|
-
gito/tpl/answer.j2,sha256=JrBOv-a8xBqTccRJeWly2U1Y5jehc_mOBt5nSbRJldI,700
|
28
|
-
gito/tpl/github_workflows/components/env-vars.j2,sha256=ypmf938h5PA38mXTZnP1eI4Un3AIhhmnl6wXg2X4kqI,406
|
29
|
-
gito/tpl/github_workflows/components/installs.j2,sha256=j5wl0yVEIrXZDpAgzqBwmhXQA9End3xFspPxr2ZzHR0,693
|
30
|
-
gito/tpl/github_workflows/gito-code-review.yml.j2,sha256=rciiX_HzygwVFTp0nUxzsLYQTRcJlR-wnft7-6gM_6Q,963
|
31
|
-
gito/tpl/github_workflows/gito-react-to-comments.yml.j2,sha256=EpBXgFwF7jMU-Zty0usx6q2lj31Ocd_OMoTPYRg7kr0,2129
|
32
|
-
gito/tpl/partial/aux_files.j2,sha256=lJhqnCsHBbEEocpyyOmQX27jzuLvEIuEVXY0RGqxWnY,191
|
33
|
-
gito/tpl/questions/changes_summary.j2,sha256=N80OQoo9UKii0CWLuck5bOwbijul5RefvCqHJljenmE,2213
|
34
|
-
gito/tpl/questions/release_notes.j2,sha256=OXi6o7T1bum88_2Pt4FiLHmKUe86A1t9Be_3s4mrnmU,889
|
35
|
-
gito/tpl/questions/test_cases.j2,sha256=bB7ESjy02mwWml4zyq87DqkFDj-I0-BYpfJVgzE77cc,1410
|
36
|
-
gito/utils.py,sha256=-agcMHug8nbrPxfz2nAtOKEAox7Kt-0_AIC3d-X23NE,7693
|
37
|
-
ai_cr-3.2.2.dist-info/LICENSE,sha256=VbdF_GbbDK24JvdTfnsxa2M6jmhsxmRSFeHCx-lICGE,1075
|
38
|
-
ai_cr-3.2.2.dist-info/METADATA,sha256=9M0y1sbIyF1v3zl96TjACPBfdKn8ha5PZWk0t_sS-B0,8874
|
39
|
-
ai_cr-3.2.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
40
|
-
ai_cr-3.2.2.dist-info/entry_points.txt,sha256=Ua1DxkhJJ8TZuLgnH-IlWCkrre_0S0dq_GtYRaYupWk,38
|
41
|
-
ai_cr-3.2.2.dist-info/RECORD,,
|
File without changes
|