Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 17 additions & 73 deletions penify_hook/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,7 @@ def __init__(self, api_url, api_token: str = None, bearer_token: str = None):
self.BEARER_TOKEN = bearer_token

def send_file_for_docstring_generation(self, file_name, content, line_numbers, repo_details = None):
"""Send file content and modified lines to the API and return modified
content.

This function constructs a payload containing the file path, content,
and modified line numbers, and sends it to a specified API endpoint for
processing. It handles the response from the API, returning the modified
content if the request is successful. If the request fails, it logs the
error details and returns the original content.

Args:
file_name (str): The path to the file being sent.
content (str): The content of the file to be processed.
line_numbers (list): A list of line numbers that have been modified.
repo_details (str?): Additional repository details if applicable. Defaults to None.

Returns:
str: The modified content returned by the API, or the original content if the
request fails.

Raises:
Exception: If there is an error in processing the file and no specific error
message is provided.
"""
"""Send file content and modified lines to the API and return modified content."""
payload = {
'file_path': file_name,
'content': content,
Expand All @@ -53,25 +31,22 @@ def send_file_for_docstring_generation(self, file_name, content, line_numbers, r
raise Exception(f"API Error: {error_message}")

def generate_commit_summary(self, git_diff, instruction: str = "", repo_details = None, jira_context: dict = None):
"""Generate a commit summary by sending a POST request to the API endpoint.

This function constructs a payload containing the git diff and any
additional instructions provided. It then sends this payload to a
specified API endpoint to generate a summary of the commit. If the
request is successful, it returns the response from the API; otherwise,
it returns None.

"""Generates a commit summary by sending a POST request to the API endpoint.
This function constructs a payload containing the git diff and any additional
instructions provided. It then sends this payload to a specified API endpoint
to generate a summary of the commit. If the request is successful, it returns
the response from the API; otherwise, it returns None. The function also
handles optional repository details and JIRA context if they are provided.
Args:
git_diff (str): The git diff of the commit.
instruction (str??): Additional instruction for the commit. Defaults to "".
repo_details (dict??): Details of the git repository. Defaults to None.
jira_context (dict??): JIRA issue details to enhance the commit summary. Defaults to None.

instruction (str): Additional instruction for the commit. Defaults to "".
repo_details (dict): Details of the git repository. Defaults to None.
jira_context (dict): JIRA issue details to enhance the commit summary. Defaults to None.
Returns:
dict: The response from the API if the request is successful, None otherwise.

Raises:
Exception: If there is an error during the API request.
"""
payload = {
'git_diff': git_diff,
Expand Down Expand Up @@ -100,18 +75,8 @@ def generate_commit_summary(self, git_diff, instruction: str = "", repo_details
return None

def get_supported_file_types(self) -> list[str]:
"""Retrieve the supported file types from the API.

This function sends a request to the API endpoint
`/v1/file/supported_languages` to obtain a list of supported file types.
If the API call is successful (status code 200), it parses the JSON
response and returns the list of supported file types. If the API call
fails, it returns a default list of common file types.

Returns:
list[str]: A list of supported file types, either from the API or a default set.
"""

"""Retrieve supported file types from the API or return a default list."""
url = self.api_url+"/v1/cli/supported_languages"
response = requests.get(url)
if response.status_code == 200:
Expand All @@ -121,21 +86,8 @@ def get_supported_file_types(self) -> list[str]:
return ["py", "js", "ts", "java", "kt", "cs", "c"]

def generate_commit_summary_with_llm(self, diff, message, generate_description: bool, repo_details, llm_client : LLMClient, jira_context=None):
"""Generates a commit summary using a local LLM client. If an error occurs
during the generation process,
it falls back to using the API.

Args:
diff (str): The Git diff of changes.
message (str): User-provided commit message or instructions.
generate_description (bool): Flag indicating whether to generate a description for the commit.
repo_details (dict): Details about the repository.
llm_client (LLMClient): An instance of LLMClient used to generate the summary.
jira_context (JIRAContext?): Optional JIRA issue context to enhance the summary.

Returns:
dict: A dictionary containing the title and description for the commit.
"""
"""Generates a commit summary using a local LLM client; falls back to API on
error."""
try:
return llm_client.generate_commit_summary(diff, message, generate_description, repo_details, jira_context)
except Exception as e:
Expand All @@ -144,17 +96,9 @@ def generate_commit_summary_with_llm(self, diff, message, generate_description:
return self.generate_commit_summary(diff, message, repo_details, jira_context)

def get_api_key(self):
"""Fetch an API key from a specified URL.

This function sends a GET request to retrieve an API token using a
Bearer token in the headers. It handles the response and returns the API
key if the request is successful, or `None` otherwise.

Returns:
str: The API key if the request is successful, `None` otherwise.
"""


"""Fetch an API key from a specified URL using a Bearer token."""
url = self.api_url+"/v1/apiToken/get"
response = requests.get(url, headers={"Authorization": f"Bearer {self.BEARER_TOKEN}"}, timeout=60*10)
if response.status_code == 200:
Expand Down
52 changes: 20 additions & 32 deletions penify_hook/commands/auth_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,26 @@
from pathlib import Path

def save_credentials(api_key):
"""
Save the token and API keys based on priority:
1. .env file in Git repo root (if in a git repo)
2. .penify file in home directory (global fallback)
# Try to save in .env file in git repo first
"""Save the API key in a priority-based manner.

This function attempts to save the API key in two locations, based on priority:
1. In a `.env` file located in the root of the Git repository if one is found.
2. In a global `.penify` file located in the user's home directory as a
fallback. The function first tries to locate the Git repository using
`recursive_search_git_folder`. If a Git repository is found, it reads the
existing `.env` file (if present), updates or adds the API key under the key
`PENIFY_API_TOKEN`, and writes the updated content back. If any error occurs
during this process, it falls back to saving the credentials in the global
`.penify` file. The function handles exceptions and prints appropriate error
messages.

Args:
api_key: The API key to save
api_key (str): The API key to save.

Returns:
bool: True if saved successfully, False otherwise
bool: True if the API key is saved successfully, False otherwise.
"""
# Try to save in .env file in git repo first
try:
from ..utils import recursive_search_git_folder
current_dir = os.getcwd()
Expand Down Expand Up @@ -80,21 +88,7 @@ def save_credentials(api_key):
return False

def login(api_url, dashboard_url):
"""Open the login page in a web browser and listen for the redirect URL to
capture the token.

This function generates a random redirect port, constructs the full
login URL with the provided dashboard URL, opens the login page in the
default web browser, and sets up a simple HTTP server to listen for the
redirect. Upon receiving the redirect, it extracts the token from the
query parameters, fetches API keys using the token, saves them if
successful, and handles login failures by notifying the user.

Args:
api_url (str): The URL of the API service to fetch API keys.
dashboard_url (str): The URL of the dashboard where the user will be redirected after logging
in.
"""
"""Open the login page in a web browser and capture the token via redirect."""
redirect_port = random.randint(30000, 50000)
redirect_url = f"http://localhost:{redirect_port}/callback"

Expand All @@ -105,16 +99,9 @@ def login(api_url, dashboard_url):

class TokenHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
"""Handle a GET request to process login token and redirect or display
error message.

This method processes the incoming GET request, extracts the token from
the query string, and performs actions based on whether the token is
present. If the token is valid, it redirects the user to the Penify
dashboard and fetches API keys if successful. If the token is invalid,
it displays an error message.
"""

"""Handle a GET request to process login token and redirect or display error
message."""
query = urllib.parse.urlparse(self.path).query
query_components = urllib.parse.parse_qs(query)
token = query_components.get("token", [None])[0]
Expand Down Expand Up @@ -171,6 +158,7 @@ def do_GET(self):

def log_message(self, format, *args):
# Suppress log messages
"""Suppress log messages."""
return

with socketserver.TCPServer(("", redirect_port), TokenHandler) as httpd:
Expand Down
44 changes: 11 additions & 33 deletions penify_hook/commands/commit_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,27 @@
def commit_code(api_url, token, message, open_terminal, generate_description,
llm_model=None, llm_api_base=None, llm_api_key=None,
jira_url=None, jira_user=None, jira_api_token=None):
"""Enhance Git commits with AI-powered commit messages.

This function allows for the generation of enhanced commit messages
using natural language processing models and optionally integrates with
JIRA for additional context. It processes the current Git folder to find
relevant files and generates a detailed commit message based on the
provided parameters.

"""Enhance Git commits with AI-powered commit messages.

This function allows for the generation of enhanced commit messages using
natural language processing models and optionally integrates with JIRA for
additional context. It processes the current Git folder to find relevant files
and generates a detailed commit message based on the provided parameters.

Args:
api_url (str): URL of the API endpoint.
token (str): Authentication token for the API.
message (str): Initial commit message provided by the user.
open_terminal (bool): Whether to open the terminal after committing.
generate_description (bool): Whether to generate a detailed description in the commit message.
llm_model (str?): The language model to use for generating the commit message. Defaults to
None.
llm_model (str?): The language model to use for generating the commit message. Defaults to None.
llm_api_base (str?): Base URL of the LLM API. Defaults to None.
llm_api_key (str?): API key for accessing the LLM service. Defaults to None.
jira_url (str?): URL of the JIRA instance. Defaults to None.
jira_user (str?): Username for authenticating with JIRA. Defaults to None.
jira_api_token (str?): API token for accessing JIRA. Defaults to None.
"""

from penify_hook.ui_utils import print_error
from penify_hook.utils import recursive_search_git_folder
from ..commit_analyzer import CommitDocGenHook
Expand Down Expand Up @@ -98,18 +96,8 @@ def commit_code(api_url, token, message, open_terminal, generate_description,


def setup_commit_parser(parser):
"""Generates a parser for setting up a command to generate smart commit
messages.

This function sets up an argument parser that can be used to generate
commit messages with contextual information. It allows users to specify
options such as including a message, opening an edit terminal before
committing, and generating a detailed commit message.

Args:
parser (argparse.ArgumentParser): The ArgumentParser object to be configured.
"""

"""Sets up an argument parser for generating smart commit messages."""
commit_parser_description = """
It generates smart commit messages. By default, it will just generate just the Title of the commit message.
1. If you have not configured LLM, it will give an error. You either need to configure LLM or use the API key.
Expand All @@ -126,19 +114,9 @@ def setup_commit_parser(parser):
parser.add_argument("-d", "--description", action="store_false", help="It will generate commit message with title and description.", default=False)

def handle_commit(args):
"""Handle the commit functionality by processing arguments and invoking the
appropriate commands.

This function processes the provided command-line arguments to configure
settings for commit operations, including LLM (Language Model) and Jira
configurations. It then calls the `commit_code` function with these
configurations to perform the actual commit operation.

Args:
args (argparse.Namespace): The parsed command-line arguments containing options like terminal,
description, message, etc.
"""

"""Handle commit functionality by processing arguments and invoking the
appropriate commands."""
from penify_hook.commands.commit_commands import commit_code
from penify_hook.commands.config_commands import get_jira_config, get_llm_config, get_token
from penify_hook.constants import API_URL
Expand Down
Loading
Loading