ticket2pr 0.1.0__tar.gz
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.
- ticket2pr-0.1.0/LICENSE +21 -0
- ticket2pr-0.1.0/PKG-INFO +83 -0
- ticket2pr-0.1.0/README.md +56 -0
- ticket2pr-0.1.0/pyproject.toml +35 -0
- ticket2pr-0.1.0/setup.cfg +10 -0
- ticket2pr-0.1.0/setup.py +60 -0
- ticket2pr-0.1.0/src/__init__.py +0 -0
- ticket2pr-0.1.0/src/branch_creator.py +49 -0
- ticket2pr-0.1.0/src/cli.py +211 -0
- ticket2pr-0.1.0/src/console_utils.py +284 -0
- ticket2pr-0.1.0/src/enhanced_git.py +130 -0
- ticket2pr-0.1.0/src/exceptions.py +133 -0
- ticket2pr-0.1.0/src/logging_setup.py +121 -0
- ticket2pr-0.1.0/src/main.py +6 -0
- ticket2pr-0.1.0/src/pr_content.py +14 -0
- ticket2pr-0.1.0/src/settings.py +120 -0
- ticket2pr-0.1.0/src/settings_init.py +232 -0
- ticket2pr-0.1.0/src/validators.py +66 -0
- ticket2pr-0.1.0/src/workflow.py +85 -0
- ticket2pr-0.1.0/tests/__init__.py +0 -0
- ticket2pr-0.1.0/tests/integration/__init__.py +0 -0
- ticket2pr-0.1.0/tests/integration/test_find_first_toml.py +32 -0
- ticket2pr-0.1.0/tests/unit/__init__.py +0 -0
- ticket2pr-0.1.0/tests/unit/test_nothing.py +3 -0
- ticket2pr-0.1.0/ticket2pr.egg-info/PKG-INFO +83 -0
- ticket2pr-0.1.0/ticket2pr.egg-info/SOURCES.txt +30 -0
- ticket2pr-0.1.0/ticket2pr.egg-info/dependency_links.txt +1 -0
- ticket2pr-0.1.0/ticket2pr.egg-info/entry_points.txt +2 -0
- ticket2pr-0.1.0/ticket2pr.egg-info/not-zip-safe +1 -0
- ticket2pr-0.1.0/ticket2pr.egg-info/requires.txt +11 -0
- ticket2pr-0.1.0/ticket2pr.egg-info/top_level.txt +2 -0
ticket2pr-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ben Gabay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ticket2pr-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ticket2pr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Automate Jira ticket to GitHub PR workflow
|
|
5
|
+
Home-page: https://github.com/bengabay11/ticket2pr
|
|
6
|
+
Author: Ben Gabay
|
|
7
|
+
Author-email: ben.gabay38@gmail.com
|
|
8
|
+
Requires-Python: >=3.13
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: claude-agent-sdk>=0.1.25
|
|
12
|
+
Requires-Dist: colorlog>=6.9.0
|
|
13
|
+
Requires-Dist: gitpython>=3.1.46
|
|
14
|
+
Requires-Dist: jira>=3.10.5
|
|
15
|
+
Requires-Dist: pydantic>=2.11.5
|
|
16
|
+
Requires-Dist: pydantic-settings>=2.9.1
|
|
17
|
+
Requires-Dist: pygithub>=2.8.1
|
|
18
|
+
Requires-Dist: python-dotenv>=1.1.0
|
|
19
|
+
Requires-Dist: rich>=14.0.0
|
|
20
|
+
Requires-Dist: tomli-w>=1.2.0
|
|
21
|
+
Requires-Dist: typer>=0.21.1
|
|
22
|
+
Dynamic: author
|
|
23
|
+
Dynamic: author-email
|
|
24
|
+
Dynamic: home-page
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
Dynamic: requires-python
|
|
27
|
+
|
|
28
|
+
# Ticket2PR
|
|
29
|
+
|
|
30
|
+
Ticket2PR is an AI-powered automation tool designed to streamline the process of converting development tickets into ready-to-merge pull requests. It integrates with Jira and GitHub to automate tasks such as branch creation, commit message generation, code linting fixes, and pull request content generation, significantly reducing manual effort and accelerating development workflows.
|
|
31
|
+
|
|
32
|
+
## What's Included
|
|
33
|
+
|
|
34
|
+
Ticket2PR provides a comprehensive set of features to automate your development workflow:
|
|
35
|
+
|
|
36
|
+
- **🚀 Automated Workflow:** Orchestrates the entire process from ticket to pull request, handling branch creation, commit message generation, and PR content.
|
|
37
|
+
- **🤖 AI-Powered Agents:** Utilizes specialized agents for tasks like crafting intelligent commit messages, automatically fixing pre-commit issues, and assisting with ticket resolution.
|
|
38
|
+
- **🔗 Jira Integration:** Connects with Jira to fetch ticket details, enabling context-aware automation.
|
|
39
|
+
- **🐙 GitHub Integration:** Interacts with GitHub for branch management, pull request creation, and status updates.
|
|
40
|
+
|
|
41
|
+
## Prerequisites
|
|
42
|
+
|
|
43
|
+
- **Jira Account and API Token:** An account with access to a Jira instance and a valid API token with necessary permissions to view tickets.
|
|
44
|
+
- **GitHub Account and Personal Access Token:** A GitHub account with permissions to create branches and pull requests in your target repository, and a [Personal Access Token (PAT)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) with `repo` scope for GitHub API access.
|
|
45
|
+
- **Claude Code API Token:** Obtain an API token for Claude Code for AI-powered code assistance.
|
|
46
|
+
|
|
47
|
+
## Getting Started
|
|
48
|
+
|
|
49
|
+
Follow these steps to set up and start using Ticket2PR:
|
|
50
|
+
|
|
51
|
+
### Installation
|
|
52
|
+
|
|
53
|
+
You can install `ticket2pr` directly from PyPI:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
pip install ticket2pr
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Configuration
|
|
60
|
+
|
|
61
|
+
Ticket2PR automatically guides you through the initial configuration process the first time you run the CLI.
|
|
62
|
+
|
|
63
|
+
To re-initialize the interactive configuration session, run:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
ticket2pr init
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Alternatively, you can manually configure settings by editing the `~/.ticket2pr/config.toml` file or by setting environment variables.
|
|
70
|
+
|
|
71
|
+
### Usage
|
|
72
|
+
|
|
73
|
+
Run the Ticket2PR CLI to create a pull request from a Jira ticket:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
ticket2pr run <JIRA_ISSUE_KEY>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Replace `<JIRA_ISSUE_KEY>` with the actual ID of your Jira ticket (e.g., `PROJ-123`).
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
This project is open-sourced under the terms of the [LICENSE](LICENSE).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Ticket2PR
|
|
2
|
+
|
|
3
|
+
Ticket2PR is an AI-powered automation tool designed to streamline the process of converting development tickets into ready-to-merge pull requests. It integrates with Jira and GitHub to automate tasks such as branch creation, commit message generation, code linting fixes, and pull request content generation, significantly reducing manual effort and accelerating development workflows.
|
|
4
|
+
|
|
5
|
+
## What's Included
|
|
6
|
+
|
|
7
|
+
Ticket2PR provides a comprehensive set of features to automate your development workflow:
|
|
8
|
+
|
|
9
|
+
- **🚀 Automated Workflow:** Orchestrates the entire process from ticket to pull request, handling branch creation, commit message generation, and PR content.
|
|
10
|
+
- **🤖 AI-Powered Agents:** Utilizes specialized agents for tasks like crafting intelligent commit messages, automatically fixing pre-commit issues, and assisting with ticket resolution.
|
|
11
|
+
- **🔗 Jira Integration:** Connects with Jira to fetch ticket details, enabling context-aware automation.
|
|
12
|
+
- **🐙 GitHub Integration:** Interacts with GitHub for branch management, pull request creation, and status updates.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- **Jira Account and API Token:** An account with access to a Jira instance and a valid API token with necessary permissions to view tickets.
|
|
17
|
+
- **GitHub Account and Personal Access Token:** A GitHub account with permissions to create branches and pull requests in your target repository, and a [Personal Access Token (PAT)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) with `repo` scope for GitHub API access.
|
|
18
|
+
- **Claude Code API Token:** Obtain an API token for Claude Code for AI-powered code assistance.
|
|
19
|
+
|
|
20
|
+
## Getting Started
|
|
21
|
+
|
|
22
|
+
Follow these steps to set up and start using Ticket2PR:
|
|
23
|
+
|
|
24
|
+
### Installation
|
|
25
|
+
|
|
26
|
+
You can install `ticket2pr` directly from PyPI:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
pip install ticket2pr
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Configuration
|
|
33
|
+
|
|
34
|
+
Ticket2PR automatically guides you through the initial configuration process the first time you run the CLI.
|
|
35
|
+
|
|
36
|
+
To re-initialize the interactive configuration session, run:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
ticket2pr init
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Alternatively, you can manually configure settings by editing the `~/.ticket2pr/config.toml` file or by setting environment variables.
|
|
43
|
+
|
|
44
|
+
### Usage
|
|
45
|
+
|
|
46
|
+
Run the Ticket2PR CLI to create a pull request from a Jira ticket:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
ticket2pr run <JIRA_ISSUE_KEY>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Replace `<JIRA_ISSUE_KEY>` with the actual ID of your Jira ticket (e.g., `PROJ-123`).
|
|
53
|
+
|
|
54
|
+
## License
|
|
55
|
+
|
|
56
|
+
This project is open-sourced under the terms of the [LICENSE](LICENSE).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ticket2pr"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Automate Jira ticket to GitHub PR workflow"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"claude-agent-sdk>=0.1.25",
|
|
9
|
+
"colorlog>=6.9.0",
|
|
10
|
+
"gitpython>=3.1.46",
|
|
11
|
+
"jira>=3.10.5",
|
|
12
|
+
"pydantic>=2.11.5",
|
|
13
|
+
"pydantic-settings>=2.9.1",
|
|
14
|
+
"pygithub>=2.8.1",
|
|
15
|
+
"python-dotenv>=1.1.0",
|
|
16
|
+
"rich>=14.0.0",
|
|
17
|
+
"tomli-w>=1.2.0",
|
|
18
|
+
"typer>=0.21.1",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
ticket2pr = "src.cli:cli_main"
|
|
23
|
+
|
|
24
|
+
[dependency-groups]
|
|
25
|
+
dev = [
|
|
26
|
+
"absolufy-imports>=0.3.1",
|
|
27
|
+
"bandit>=1.8.3",
|
|
28
|
+
"codespell>=2.4.1",
|
|
29
|
+
"mypy>=1.16.0",
|
|
30
|
+
"pre-commit>=4.2.0",
|
|
31
|
+
"pydantic[mypy]>=2.11.5",
|
|
32
|
+
"pytest>=8.3.5",
|
|
33
|
+
"pytest-cases>=3.8.6",
|
|
34
|
+
"ruff>=0.11.12",
|
|
35
|
+
]
|
ticket2pr-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Setup script for ticket2pr package."""
|
|
2
|
+
|
|
3
|
+
from setuptools import find_packages, setup
|
|
4
|
+
|
|
5
|
+
# Read the long description from README
|
|
6
|
+
with open("README.md", encoding="utf-8") as f:
|
|
7
|
+
long_description = f.read()
|
|
8
|
+
|
|
9
|
+
setup(
|
|
10
|
+
name="ticket2pr",
|
|
11
|
+
version="0.1.0",
|
|
12
|
+
author="Ben Gabay",
|
|
13
|
+
author_email="ben.gabay38@gmail.com",
|
|
14
|
+
description="Automate Jira ticket to GitHub PR workflow",
|
|
15
|
+
long_description=long_description,
|
|
16
|
+
long_description_content_type="text/markdown",
|
|
17
|
+
url="https://github.com/bengabay11/ticket2pr",
|
|
18
|
+
packages=find_packages(),
|
|
19
|
+
classifiers=[
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
],
|
|
27
|
+
python_requires=">=3.13",
|
|
28
|
+
install_requires=[
|
|
29
|
+
"claude-agent-sdk>=0.1.25",
|
|
30
|
+
"colorlog>=6.9.0",
|
|
31
|
+
"gitpython>=3.1.46",
|
|
32
|
+
"jira>=3.10.5",
|
|
33
|
+
"pydantic>=2.11.5",
|
|
34
|
+
"pydantic-settings>=2.9.1",
|
|
35
|
+
"pygithub>=2.8.1",
|
|
36
|
+
"python-dotenv>=1.1.0",
|
|
37
|
+
"rich>=14.0.0",
|
|
38
|
+
"tomli-w>=1.2.0",
|
|
39
|
+
"typer>=0.21.1",
|
|
40
|
+
],
|
|
41
|
+
extras_require={
|
|
42
|
+
"dev": [
|
|
43
|
+
"absolufy-imports>=0.3.1",
|
|
44
|
+
"bandit>=1.8.3",
|
|
45
|
+
"codespell>=2.4.1",
|
|
46
|
+
"mypy>=1.16.0",
|
|
47
|
+
"pre-commit>=4.2.0",
|
|
48
|
+
"pytest>=8.3.5",
|
|
49
|
+
"pytest-cases>=3.8.6",
|
|
50
|
+
"ruff>=0.11.12",
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
entry_points={
|
|
54
|
+
"console_scripts": [
|
|
55
|
+
"ticket2pr=src.cli:cli_main",
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
include_package_data=True,
|
|
59
|
+
zip_safe=False,
|
|
60
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from src.clients.github_client import GitHubClient
|
|
4
|
+
from src.clients.jira_client import JiraClient, JiraIssue
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def sanitize_branch_name(name: str, max_length: int = 100) -> str:
|
|
8
|
+
name = name.lower()
|
|
9
|
+
# Replace any character that's not a lowercase letter, digit, or hyphen with a hyphen
|
|
10
|
+
name = re.sub(r"[^a-z0-9-]", "-", name)
|
|
11
|
+
# Replace one or more consecutive hyphens with a single hyphen
|
|
12
|
+
name = re.sub(r"-+", "-", name)
|
|
13
|
+
name = name.strip("-")
|
|
14
|
+
if len(name) > max_length:
|
|
15
|
+
name = name[:max_length].rstrip("-")
|
|
16
|
+
return name
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def generate_branch_name(
|
|
20
|
+
issue_key: str,
|
|
21
|
+
issue_summary: str,
|
|
22
|
+
issue_type: str | None = None,
|
|
23
|
+
max_length: int = 255,
|
|
24
|
+
) -> str:
|
|
25
|
+
sanitized_summary = sanitize_branch_name(issue_summary)
|
|
26
|
+
branch_name = f"{issue_key}-{sanitized_summary}"
|
|
27
|
+
if issue_type:
|
|
28
|
+
branch_name = f"{issue_type.lower()}/{branch_name}"
|
|
29
|
+
|
|
30
|
+
if len(branch_name) > max_length:
|
|
31
|
+
branch_name = branch_name[:max_length].rstrip("-")
|
|
32
|
+
|
|
33
|
+
return branch_name
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def create_branch_from_jira_issue(
|
|
37
|
+
jira_issue: JiraIssue,
|
|
38
|
+
jira_client: JiraClient,
|
|
39
|
+
github_client: GitHubClient,
|
|
40
|
+
base_branch: str = "main",
|
|
41
|
+
) -> str:
|
|
42
|
+
branch_name = generate_branch_name(jira_issue.key, jira_issue.summary, jira_issue.type)
|
|
43
|
+
# TODO: Consider replacing the 2 next lines with local git client
|
|
44
|
+
base_ref = github_client.get_base_branch_ref(base_branch)
|
|
45
|
+
branch_url = github_client.create_branch(branch_name, base_ref)
|
|
46
|
+
|
|
47
|
+
jira_client.link_branch(jira_issue.key, branch_url, branch_name)
|
|
48
|
+
|
|
49
|
+
return branch_name
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from src.console_utils import (
|
|
11
|
+
format_dim,
|
|
12
|
+
format_success_with_checkmark,
|
|
13
|
+
format_yellow,
|
|
14
|
+
get_status,
|
|
15
|
+
print_empty_line,
|
|
16
|
+
print_error,
|
|
17
|
+
print_error_inline,
|
|
18
|
+
print_info,
|
|
19
|
+
print_label_value,
|
|
20
|
+
print_success,
|
|
21
|
+
print_warning,
|
|
22
|
+
)
|
|
23
|
+
from src.enhanced_git import EnhancedGit
|
|
24
|
+
from src.logging_setup import LoggerHandlerType, SetupLoggerParams, setup_logger
|
|
25
|
+
from src.settings import AppSettings
|
|
26
|
+
from src.shell.claude_auth_status import is_claude_logged_in
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from src.clients.github_client import GitHubClient
|
|
30
|
+
from src.clients.jira_client import JiraClient
|
|
31
|
+
|
|
32
|
+
app = typer.Typer(
|
|
33
|
+
name="ticket2pr",
|
|
34
|
+
help="Automate Jira ticket to GitHub PR workflow",
|
|
35
|
+
add_completion=False,
|
|
36
|
+
rich_markup_mode="rich",
|
|
37
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _load_settings() -> AppSettings:
|
|
42
|
+
from dotenv import load_dotenv
|
|
43
|
+
|
|
44
|
+
load_dotenv()
|
|
45
|
+
try:
|
|
46
|
+
return AppSettings()
|
|
47
|
+
except Exception as e:
|
|
48
|
+
print_error_inline(f"loading settings: {e}")
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _initialize_clients(settings: AppSettings) -> tuple[GitHubClient, JiraClient, EnhancedGit]:
|
|
53
|
+
from src.clients.github_client import GitHubClient
|
|
54
|
+
from src.clients.jira_client import JiraClient
|
|
55
|
+
from src.enhanced_git import EnhancedGit
|
|
56
|
+
|
|
57
|
+
github_client = GitHubClient(
|
|
58
|
+
github_token=settings.github.api_token,
|
|
59
|
+
repo_full_name=settings.github.repo_full_name,
|
|
60
|
+
)
|
|
61
|
+
jira_client = JiraClient(
|
|
62
|
+
url=settings.jira.base_url,
|
|
63
|
+
username=settings.jira.username,
|
|
64
|
+
password=settings.jira.api_token,
|
|
65
|
+
)
|
|
66
|
+
git = EnhancedGit(settings.core.workspace_path)
|
|
67
|
+
return github_client, jira_client, git
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _init() -> None:
|
|
71
|
+
from src.settings import DEFAULT_CONFIG_DIR
|
|
72
|
+
from src.settings_init import initialize_settings
|
|
73
|
+
|
|
74
|
+
config_path = DEFAULT_CONFIG_DIR / "config.toml"
|
|
75
|
+
initialize_settings(config_path)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def workflow_with_prints(
|
|
79
|
+
jira_issue_key: str,
|
|
80
|
+
workspace_path: Path,
|
|
81
|
+
base_branch: str,
|
|
82
|
+
github_client: GitHubClient,
|
|
83
|
+
jira_client: JiraClient,
|
|
84
|
+
local_git: EnhancedGit,
|
|
85
|
+
mcp_config_path: Path | None = None,
|
|
86
|
+
) -> None:
|
|
87
|
+
header_msg = f"Running workflow for {format_yellow(jira_issue_key)}"
|
|
88
|
+
print_info(header_msg)
|
|
89
|
+
print_label_value("Workspace", workspace_path)
|
|
90
|
+
print_label_value("Base branch", base_branch)
|
|
91
|
+
print_label_value("Github repository", github_client.repo.full_name)
|
|
92
|
+
print_empty_line()
|
|
93
|
+
|
|
94
|
+
from src.workflow import workflow
|
|
95
|
+
|
|
96
|
+
result = await workflow(
|
|
97
|
+
github_client=github_client,
|
|
98
|
+
jira_client=jira_client,
|
|
99
|
+
jira_issue_key=jira_issue_key,
|
|
100
|
+
git=local_git,
|
|
101
|
+
base_branch=base_branch,
|
|
102
|
+
mcp_config_path=mcp_config_path,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
success_msg = format_success_with_checkmark("Workflow completed successfully!")
|
|
106
|
+
issue_msg = format_dim(f"Issue: {result.jira_issue_permalink}")
|
|
107
|
+
pr_msg = format_dim(f"Pull Request: {result.pr_url}")
|
|
108
|
+
branch_msg = format_dim(f"Branch: {result.branch_name}")
|
|
109
|
+
base_branch_msg = format_dim(f"Base Branch: {base_branch}")
|
|
110
|
+
|
|
111
|
+
final_msg = "\n".join([success_msg, issue_msg, pr_msg, branch_msg, base_branch_msg])
|
|
112
|
+
print_success(final_msg)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@app.command()
|
|
116
|
+
def run(
|
|
117
|
+
jira_issue_key: str = typer.Argument(..., help="Jira issue key (e.g., PROJ-123)"),
|
|
118
|
+
workspace_path: Path | None = typer.Option( # noqa: B008
|
|
119
|
+
None, "--workspace-path", "-w", help="Workspace path (overrides settings)"
|
|
120
|
+
),
|
|
121
|
+
base_branch: str | None = typer.Option(
|
|
122
|
+
None, "--base-branch", "-b", help="Base branch (overrides settings)"
|
|
123
|
+
),
|
|
124
|
+
mcp_config_path: Path | None = typer.Option( # noqa: B008
|
|
125
|
+
None, "--mcp-config-path", "-m", help="Path to mcp.json config file for Claude agents"
|
|
126
|
+
),
|
|
127
|
+
) -> None:
|
|
128
|
+
"""Execute the workflow for a specific Jira ticket."""
|
|
129
|
+
|
|
130
|
+
settings = _load_settings()
|
|
131
|
+
|
|
132
|
+
if not is_claude_logged_in():
|
|
133
|
+
hint = "Run /login or set the 'ANTHROPIC_API_KEY' environment variable."
|
|
134
|
+
message = "Claude Code authentication not found."
|
|
135
|
+
print_error(message)
|
|
136
|
+
print_label_value("Hint", hint)
|
|
137
|
+
sys.exit(1)
|
|
138
|
+
|
|
139
|
+
final_workspace_path = workspace_path or settings.core.workspace_path
|
|
140
|
+
final_base_branch = base_branch or settings.core.base_branch
|
|
141
|
+
|
|
142
|
+
setup_logger(
|
|
143
|
+
SetupLoggerParams(
|
|
144
|
+
level=settings.logging.min_log_level,
|
|
145
|
+
handler_types={LoggerHandlerType.STREAM, LoggerHandlerType.FILE},
|
|
146
|
+
file_path=settings.logging.log_file_path,
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
with get_status("Initializing clients...", spinner="dots"):
|
|
151
|
+
try:
|
|
152
|
+
github_client, jira_client, local_git = _initialize_clients(settings)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
print_error(str(e))
|
|
155
|
+
sys.exit(1)
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
asyncio.run(
|
|
159
|
+
workflow_with_prints(
|
|
160
|
+
jira_issue_key,
|
|
161
|
+
final_workspace_path,
|
|
162
|
+
final_base_branch,
|
|
163
|
+
github_client,
|
|
164
|
+
jira_client,
|
|
165
|
+
local_git,
|
|
166
|
+
mcp_config_path,
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
except KeyboardInterrupt:
|
|
170
|
+
print_empty_line()
|
|
171
|
+
print_warning("Workflow interrupted by user")
|
|
172
|
+
sys.exit(1)
|
|
173
|
+
except Exception as e:
|
|
174
|
+
print_error(str(e), title="Error")
|
|
175
|
+
sys.exit(1)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@app.command()
|
|
179
|
+
def init() -> None:
|
|
180
|
+
"""Initialize settings configuration."""
|
|
181
|
+
_init()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@app.command(name="help")
|
|
185
|
+
def help_command(ctx: typer.Context) -> None:
|
|
186
|
+
"""Show help information."""
|
|
187
|
+
typer.echo(ctx.find_root().get_help())
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def settings_exist() -> bool:
|
|
191
|
+
config_file_path = Path.home() / ".ticket2pr" / "config.toml"
|
|
192
|
+
return config_file_path.exists()
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@app.callback(invoke_without_command=True)
|
|
196
|
+
def main(ctx: typer.Context) -> None:
|
|
197
|
+
if not settings_exist():
|
|
198
|
+
_init()
|
|
199
|
+
sys.exit(0)
|
|
200
|
+
|
|
201
|
+
if ctx.invoked_subcommand is None:
|
|
202
|
+
typer.echo(ctx.get_help())
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def cli_main() -> None:
|
|
206
|
+
"""Entry point for the CLI."""
|
|
207
|
+
app()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
cli_main()
|