ai-cr 3.2.1__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.1.dist-info → ai_cr-3.3.0.dist-info}/LICENSE +21 -21
- {ai_cr-3.2.1.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.1.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 -242
- ai_cr-3.2.1.dist-info/RECORD +0 -41
- {ai_cr-3.2.1.dist-info → ai_cr-3.3.0.dist-info}/entry_points.txt +0 -0
gito/cli_base.py
CHANGED
@@ -1,94 +1,104 @@
|
|
1
|
-
import contextlib
|
2
|
-
import logging
|
3
|
-
import tempfile
|
4
|
-
|
5
|
-
import microcore as mc
|
6
|
-
import typer
|
7
|
-
from git import Repo
|
8
|
-
from gito.
|
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
|
-
|
1
|
+
import contextlib
|
2
|
+
import logging
|
3
|
+
import tempfile
|
4
|
+
|
5
|
+
import microcore as mc
|
6
|
+
import typer
|
7
|
+
from git import Repo
|
8
|
+
from gito.constants import REFS_VALUE_ALL
|
9
|
+
|
10
|
+
from .utils import parse_refs_pair
|
11
|
+
from .env import Env
|
12
|
+
|
13
|
+
|
14
|
+
def args_to_target(refs, what, against) -> tuple[str | None, str | None]:
|
15
|
+
if refs == REFS_VALUE_ALL:
|
16
|
+
return REFS_VALUE_ALL, None
|
17
|
+
_what, _against = parse_refs_pair(refs)
|
18
|
+
if _what:
|
19
|
+
if what:
|
20
|
+
raise typer.BadParameter(
|
21
|
+
"You cannot specify both 'refs' <WHAT>..<AGAINST> and '--what'. Use one of them."
|
22
|
+
)
|
23
|
+
else:
|
24
|
+
_what = what
|
25
|
+
if _against:
|
26
|
+
if against:
|
27
|
+
raise typer.BadParameter(
|
28
|
+
"You cannot specify both 'refs' <WHAT>..<AGAINST> and '--against'. Use one of them."
|
29
|
+
)
|
30
|
+
else:
|
31
|
+
_against = against
|
32
|
+
return _what, _against
|
33
|
+
|
34
|
+
|
35
|
+
def arg_refs() -> typer.Argument:
|
36
|
+
return typer.Argument(
|
37
|
+
default=None,
|
38
|
+
help=(
|
39
|
+
"Git refs to review, [what]..[against] (e.g., 'HEAD..HEAD~1'). "
|
40
|
+
"If omitted, the current index (including added but not committed files) "
|
41
|
+
"will be compared to the repository’s main branch."
|
42
|
+
),
|
43
|
+
)
|
44
|
+
|
45
|
+
|
46
|
+
def arg_what() -> typer.Option:
|
47
|
+
return typer.Option(None, "--what", "-w", help="Git ref to review")
|
48
|
+
|
49
|
+
|
50
|
+
def arg_filters() -> typer.Option:
|
51
|
+
return typer.Option(
|
52
|
+
"", "--filter", "-f", "--filters",
|
53
|
+
help="""
|
54
|
+
filter reviewed files by glob / fnmatch pattern(s),
|
55
|
+
e.g. 'src/**/*.py', may be comma-separated
|
56
|
+
""",
|
57
|
+
)
|
58
|
+
|
59
|
+
|
60
|
+
def arg_out() -> typer.Option:
|
61
|
+
return typer.Option(
|
62
|
+
None,
|
63
|
+
"--out", "-o", "--output",
|
64
|
+
help="Output folder for the code review report"
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
def arg_against() -> typer.Option:
|
69
|
+
return typer.Option(
|
70
|
+
None,
|
71
|
+
"--against", "-vs", "--vs",
|
72
|
+
help="Git ref to compare against"
|
73
|
+
)
|
74
|
+
|
75
|
+
|
76
|
+
app = typer.Typer(pretty_exceptions_show_locals=False)
|
77
|
+
|
78
|
+
|
79
|
+
@contextlib.contextmanager
|
80
|
+
def get_repo_context(url: str, branch: str):
|
81
|
+
if branch == REFS_VALUE_ALL:
|
82
|
+
branch = None
|
83
|
+
"""Context manager for handling both local and remote repositories."""
|
84
|
+
if url:
|
85
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
86
|
+
logging.info(
|
87
|
+
f"get_repo_context: "
|
88
|
+
f"Cloning [{mc.ui.green(url)}] to {mc.utils.file_link(temp_dir)} ..."
|
89
|
+
)
|
90
|
+
repo = Repo.clone_from(url, branch=branch, to_path=temp_dir)
|
91
|
+
prev_folder = Env.working_folder
|
92
|
+
Env.working_folder = temp_dir
|
93
|
+
try:
|
94
|
+
yield repo, temp_dir
|
95
|
+
finally:
|
96
|
+
repo.close()
|
97
|
+
Env.working_folder = prev_folder
|
98
|
+
else:
|
99
|
+
logging.info("get_repo_context: Using local repo...")
|
100
|
+
repo = Repo(".")
|
101
|
+
try:
|
102
|
+
yield repo, "."
|
103
|
+
finally:
|
104
|
+
repo.close()
|
gito/commands/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
# Command modules register themselves with the CLI app
|
1
|
+
# Command modules register themselves with the CLI app
|
gito/commands/deploy.py
CHANGED
@@ -1,138 +1,138 @@
|
|
1
|
-
import logging
|
2
|
-
from pathlib import Path
|
3
|
-
|
4
|
-
import microcore as mc
|
5
|
-
from microcore import ApiType, ui, utils
|
6
|
-
from git import Repo, GitCommandError
|
7
|
-
import typer
|
8
|
-
|
9
|
-
from ..core import get_base_branch
|
10
|
-
from ..utils import version, extract_gh_owner_repo
|
11
|
-
from ..cli_base import app
|
12
|
-
from ..gh_api import gh_api
|
13
|
-
|
14
|
-
|
15
|
-
@app.command(
|
16
|
-
name="deploy",
|
17
|
-
help="\bCreate and configure Gito GitHub Actions for current repository.\naliases: init"
|
18
|
-
)
|
19
|
-
@app.command(name="init", hidden=True)
|
20
|
-
def deploy(
|
21
|
-
api_type: ApiType = None,
|
22
|
-
commit: bool = None,
|
23
|
-
rewrite: bool = False,
|
24
|
-
to_branch: str = typer.Option(
|
25
|
-
default="gito_deploy",
|
26
|
-
help="Branch name for new PR containing with Gito workflows commit"
|
27
|
-
),
|
28
|
-
token: str = typer.Option(
|
29
|
-
"", help="GitHub token (or set GITHUB_TOKEN env var)"
|
30
|
-
),
|
31
|
-
):
|
32
|
-
repo = Repo(".")
|
33
|
-
workflow_files = dict(
|
34
|
-
code_review=Path(".github/workflows/gito-code-review.yml"),
|
35
|
-
react_to_comments=Path(".github/workflows/gito-react-to-comments.yml")
|
36
|
-
)
|
37
|
-
for file in workflow_files.values():
|
38
|
-
if file.exists():
|
39
|
-
message = f"Gito workflow already exists at {utils.file_link(file)}."
|
40
|
-
if rewrite:
|
41
|
-
ui.warning(message)
|
42
|
-
else:
|
43
|
-
message += "\nUse --rewrite to overwrite it."
|
44
|
-
ui.error(message)
|
45
|
-
return False
|
46
|
-
|
47
|
-
api_types = [ApiType.ANTHROPIC, ApiType.OPEN_AI, ApiType.GOOGLE_AI_STUDIO]
|
48
|
-
default_models = {
|
49
|
-
ApiType.ANTHROPIC: "claude-sonnet-4-20250514",
|
50
|
-
ApiType.OPEN_AI: "gpt-4.1",
|
51
|
-
ApiType.GOOGLE_AI_STUDIO: "gemini-2.5-pro",
|
52
|
-
}
|
53
|
-
secret_names = {
|
54
|
-
ApiType.ANTHROPIC: "ANTHROPIC_API_KEY",
|
55
|
-
ApiType.OPEN_AI: "OPENAI_API_KEY",
|
56
|
-
ApiType.GOOGLE_AI_STUDIO: "GOOGLE_AI_API_KEY",
|
57
|
-
}
|
58
|
-
if not api_type:
|
59
|
-
api_type = mc.ui.ask_choose(
|
60
|
-
"Choose your LLM API type",
|
61
|
-
api_types,
|
62
|
-
)
|
63
|
-
elif api_type not in api_types:
|
64
|
-
mc.ui.error(f"Unsupported API type: {api_type}")
|
65
|
-
return False
|
66
|
-
major, minor, *_ = version().split(".")
|
67
|
-
template_vars = dict(
|
68
|
-
model=default_models[api_type],
|
69
|
-
api_type=api_type,
|
70
|
-
secret_name=secret_names[api_type],
|
71
|
-
major=major,
|
72
|
-
minor=minor,
|
73
|
-
ApiType=ApiType,
|
74
|
-
remove_indent=True,
|
75
|
-
)
|
76
|
-
gito_code_review_yml = mc.tpl(
|
77
|
-
"github_workflows/gito-code-review.yml.j2",
|
78
|
-
**template_vars
|
79
|
-
)
|
80
|
-
gito_react_to_comments_yml = mc.tpl(
|
81
|
-
"github_workflows/gito-react-to-comments.yml.j2",
|
82
|
-
**template_vars
|
83
|
-
)
|
84
|
-
|
85
|
-
workflow_files["code_review"].parent.mkdir(parents=True, exist_ok=True)
|
86
|
-
workflow_files["code_review"].write_text(gito_code_review_yml)
|
87
|
-
workflow_files["react_to_comments"].write_text(gito_react_to_comments_yml)
|
88
|
-
print(
|
89
|
-
mc.ui.green("Gito workflows have been created.\n")
|
90
|
-
+ f" - {mc.utils.file_link(workflow_files['code_review'])}\n"
|
91
|
-
+ f" - {mc.utils.file_link(workflow_files['react_to_comments'])}\n"
|
92
|
-
)
|
93
|
-
owner, repo_name = extract_gh_owner_repo(repo)
|
94
|
-
if commit is True or commit is None and mc.ui.ask_yn(
|
95
|
-
"Do you want to commit and push created GitHub workflows to a new branch?"
|
96
|
-
):
|
97
|
-
repo.git.add([str(file) for file in workflow_files.values()])
|
98
|
-
if not repo.active_branch.name.startswith(to_branch):
|
99
|
-
repo.git.checkout("-b", to_branch)
|
100
|
-
try:
|
101
|
-
repo.git.commit("-m", "Deploy Gito workflows")
|
102
|
-
except GitCommandError as e:
|
103
|
-
if "nothing added" in str(e):
|
104
|
-
ui.warning("Failed to commit changes: nothing was added")
|
105
|
-
else:
|
106
|
-
ui.error(f"Failed to commit changes: {e}")
|
107
|
-
return False
|
108
|
-
|
109
|
-
repo.git.push("origin", to_branch)
|
110
|
-
print(f"Changes pushed to {to_branch} branch.")
|
111
|
-
try:
|
112
|
-
api = gh_api(repo=repo)
|
113
|
-
base = get_base_branch(repo).split('/')[-1]
|
114
|
-
logging.info(f"Creating PR {ui.green(to_branch)} -> {ui.yellow(base)}...")
|
115
|
-
res = api.pulls.create(
|
116
|
-
head=to_branch,
|
117
|
-
base=base,
|
118
|
-
title="Deploy Gito workflows",
|
119
|
-
)
|
120
|
-
print(f"Pull request #{res.number} created successfully:\n{res.html_url}")
|
121
|
-
except Exception as e:
|
122
|
-
mc.ui.error(f"Failed to create pull request automatically: {e}")
|
123
|
-
print(
|
124
|
-
f"Please create a PR from '{to_branch}' to your main branch and merge it:\n"
|
125
|
-
f"https://github.com/{owner}/{repo_name}/compare/{to_branch}?expand=1"
|
126
|
-
)
|
127
|
-
else:
|
128
|
-
print(
|
129
|
-
"Now you can commit and push created GitHub workflows to your main repository branch.\n"
|
130
|
-
)
|
131
|
-
|
132
|
-
print(
|
133
|
-
"(!IMPORTANT):\n"
|
134
|
-
f"Add {mc.ui.cyan(secret_names[api_type])} with actual API_KEY "
|
135
|
-
"to your repository secrets here:\n"
|
136
|
-
f"https://github.com/{owner}/{repo_name}/settings/secrets/actions"
|
137
|
-
)
|
138
|
-
return True
|
1
|
+
import logging
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
import microcore as mc
|
5
|
+
from microcore import ApiType, ui, utils
|
6
|
+
from git import Repo, GitCommandError
|
7
|
+
import typer
|
8
|
+
|
9
|
+
from ..core import get_base_branch
|
10
|
+
from ..utils import version, extract_gh_owner_repo
|
11
|
+
from ..cli_base import app
|
12
|
+
from ..gh_api import gh_api
|
13
|
+
|
14
|
+
|
15
|
+
@app.command(
|
16
|
+
name="deploy",
|
17
|
+
help="\bCreate and configure Gito GitHub Actions for current repository.\naliases: init"
|
18
|
+
)
|
19
|
+
@app.command(name="init", hidden=True)
|
20
|
+
def deploy(
|
21
|
+
api_type: ApiType = None,
|
22
|
+
commit: bool = None,
|
23
|
+
rewrite: bool = False,
|
24
|
+
to_branch: str = typer.Option(
|
25
|
+
default="gito_deploy",
|
26
|
+
help="Branch name for new PR containing with Gito workflows commit"
|
27
|
+
),
|
28
|
+
token: str = typer.Option(
|
29
|
+
"", help="GitHub token (or set GITHUB_TOKEN env var)"
|
30
|
+
),
|
31
|
+
):
|
32
|
+
repo = Repo(".")
|
33
|
+
workflow_files = dict(
|
34
|
+
code_review=Path(".github/workflows/gito-code-review.yml"),
|
35
|
+
react_to_comments=Path(".github/workflows/gito-react-to-comments.yml")
|
36
|
+
)
|
37
|
+
for file in workflow_files.values():
|
38
|
+
if file.exists():
|
39
|
+
message = f"Gito workflow already exists at {utils.file_link(file)}."
|
40
|
+
if rewrite:
|
41
|
+
ui.warning(message)
|
42
|
+
else:
|
43
|
+
message += "\nUse --rewrite to overwrite it."
|
44
|
+
ui.error(message)
|
45
|
+
return False
|
46
|
+
|
47
|
+
api_types = [ApiType.ANTHROPIC, ApiType.OPEN_AI, ApiType.GOOGLE_AI_STUDIO]
|
48
|
+
default_models = {
|
49
|
+
ApiType.ANTHROPIC: "claude-sonnet-4-20250514",
|
50
|
+
ApiType.OPEN_AI: "gpt-4.1",
|
51
|
+
ApiType.GOOGLE_AI_STUDIO: "gemini-2.5-pro",
|
52
|
+
}
|
53
|
+
secret_names = {
|
54
|
+
ApiType.ANTHROPIC: "ANTHROPIC_API_KEY",
|
55
|
+
ApiType.OPEN_AI: "OPENAI_API_KEY",
|
56
|
+
ApiType.GOOGLE_AI_STUDIO: "GOOGLE_AI_API_KEY",
|
57
|
+
}
|
58
|
+
if not api_type:
|
59
|
+
api_type = mc.ui.ask_choose(
|
60
|
+
"Choose your LLM API type",
|
61
|
+
api_types,
|
62
|
+
)
|
63
|
+
elif api_type not in api_types:
|
64
|
+
mc.ui.error(f"Unsupported API type: {api_type}")
|
65
|
+
return False
|
66
|
+
major, minor, *_ = version().split(".")
|
67
|
+
template_vars = dict(
|
68
|
+
model=default_models[api_type],
|
69
|
+
api_type=api_type,
|
70
|
+
secret_name=secret_names[api_type],
|
71
|
+
major=major,
|
72
|
+
minor=minor,
|
73
|
+
ApiType=ApiType,
|
74
|
+
remove_indent=True,
|
75
|
+
)
|
76
|
+
gito_code_review_yml = mc.tpl(
|
77
|
+
"github_workflows/gito-code-review.yml.j2",
|
78
|
+
**template_vars
|
79
|
+
)
|
80
|
+
gito_react_to_comments_yml = mc.tpl(
|
81
|
+
"github_workflows/gito-react-to-comments.yml.j2",
|
82
|
+
**template_vars
|
83
|
+
)
|
84
|
+
|
85
|
+
workflow_files["code_review"].parent.mkdir(parents=True, exist_ok=True)
|
86
|
+
workflow_files["code_review"].write_text(gito_code_review_yml)
|
87
|
+
workflow_files["react_to_comments"].write_text(gito_react_to_comments_yml)
|
88
|
+
print(
|
89
|
+
mc.ui.green("Gito workflows have been created.\n")
|
90
|
+
+ f" - {mc.utils.file_link(workflow_files['code_review'])}\n"
|
91
|
+
+ f" - {mc.utils.file_link(workflow_files['react_to_comments'])}\n"
|
92
|
+
)
|
93
|
+
owner, repo_name = extract_gh_owner_repo(repo)
|
94
|
+
if commit is True or commit is None and mc.ui.ask_yn(
|
95
|
+
"Do you want to commit and push created GitHub workflows to a new branch?"
|
96
|
+
):
|
97
|
+
repo.git.add([str(file) for file in workflow_files.values()])
|
98
|
+
if not repo.active_branch.name.startswith(to_branch):
|
99
|
+
repo.git.checkout("-b", to_branch)
|
100
|
+
try:
|
101
|
+
repo.git.commit("-m", "Deploy Gito workflows")
|
102
|
+
except GitCommandError as e:
|
103
|
+
if "nothing added" in str(e):
|
104
|
+
ui.warning("Failed to commit changes: nothing was added")
|
105
|
+
else:
|
106
|
+
ui.error(f"Failed to commit changes: {e}")
|
107
|
+
return False
|
108
|
+
|
109
|
+
repo.git.push("origin", to_branch)
|
110
|
+
print(f"Changes pushed to {to_branch} branch.")
|
111
|
+
try:
|
112
|
+
api = gh_api(repo=repo)
|
113
|
+
base = get_base_branch(repo).split('/')[-1]
|
114
|
+
logging.info(f"Creating PR {ui.green(to_branch)} -> {ui.yellow(base)}...")
|
115
|
+
res = api.pulls.create(
|
116
|
+
head=to_branch,
|
117
|
+
base=base,
|
118
|
+
title="Deploy Gito workflows",
|
119
|
+
)
|
120
|
+
print(f"Pull request #{res.number} created successfully:\n{res.html_url}")
|
121
|
+
except Exception as e:
|
122
|
+
mc.ui.error(f"Failed to create pull request automatically: {e}")
|
123
|
+
print(
|
124
|
+
f"Please create a PR from '{to_branch}' to your main branch and merge it:\n"
|
125
|
+
f"https://github.com/{owner}/{repo_name}/compare/{to_branch}?expand=1"
|
126
|
+
)
|
127
|
+
else:
|
128
|
+
print(
|
129
|
+
"Now you can commit and push created GitHub workflows to your main repository branch.\n"
|
130
|
+
)
|
131
|
+
|
132
|
+
print(
|
133
|
+
"(!IMPORTANT):\n"
|
134
|
+
f"Add {mc.ui.cyan(secret_names[api_type])} with actual API_KEY "
|
135
|
+
"to your repository secrets here:\n"
|
136
|
+
f"https://github.com/{owner}/{repo_name}/settings/secrets/actions"
|
137
|
+
)
|
138
|
+
return True
|