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
23 changes: 12 additions & 11 deletions penify_hook/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ def generate_commit_summary(self, git_diff, instruction: str = "", repo_details

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.
Expand Down Expand Up @@ -102,10 +102,11 @@ def generate_commit_summary(self, git_diff, instruction: str = "", repo_details
def get_supported_file_types(self) -> list[str]:
"""Retrieve the supported file types from the API.

This function sends a request to the API to obtain a list of supported
file types. If the API responds successfully, it returns the list of
supported file types. If the API call fails, it returns a default list
of common file types.
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.
Expand Down Expand Up @@ -143,11 +144,11 @@ 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):
"""Get an API key from the specified URL.
"""Fetch an API key from a specified URL.

It constructs a request to fetch an API token using a Bearer token in
the headers. The function handles the response and returns the API key
if successful, or `None` otherwise.
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.
Expand Down
36 changes: 32 additions & 4 deletions penify_hook/commands/auth_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@
from ..api_client import APIClient

def save_credentials(api_key):
"""
Save the token and API keys in the .penify file in the user's home directory.
"""Save or update the API keys in the .penify file in the user's home
directory.

Args:
api_key (str): The new API key to be saved or updated.

Returns:
bool: if the credentials were successfully saved, False otherwise.
"""
home_dir = Path.home()
penify_file = home_dir / '.penify'
Expand All @@ -35,8 +41,20 @@ 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.
"""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.
"""
redirect_port = random.randint(30000, 50000)
redirect_url = f"http://localhost:{redirect_port}/callback"
Expand All @@ -48,6 +66,16 @@ 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.
"""

query = urllib.parse.urlparse(self.path).query
query_components = urllib.parse.parse_qs(query)
token = query_components.get("token", [None])[0]
Expand Down
48 changes: 46 additions & 2 deletions penify_hook/commands/commit_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +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.
"""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_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
Expand Down Expand Up @@ -79,6 +98,18 @@ 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.
"""

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 @@ -95,6 +126,19 @@ 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.
"""

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
131 changes: 113 additions & 18 deletions penify_hook/commands/config_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@


def get_penify_config() -> Path:
"""
Get the home directory for the .penify configuration file.
This function searches for the .penify file in the current directory
and its parent directories until it finds it or reaches the home directory.
"""Get the home directory for the .penify configuration file.

This function searches for the `.penify` file in the current directory
and its parent directories until it finds it or reaches the home
directory. If not found, it creates the `.penify` directory and an empty
`config.json` file.

Returns:
Path: The path to the `config.json` file within the `.penify` directory.
"""
current_dir = os.getcwd()
home_dir = recursive_search_git_folder(current_dir)
Expand Down Expand Up @@ -43,8 +48,19 @@ def get_penify_config() -> Path:


def save_llm_config(model, api_base, api_key):
"""
Save LLM configuration settings in the .penify file.
"""Save LLM configuration settings in the .penify file.

It reads the existing configuration from the .penify file if it exists,
updates or adds the LLM configuration with the provided model, API base,
and API key, and then writes the updated configuration back to the file.

Args:
model (str): The name of the language model.
api_base (str): The base URL for the API.
api_key (str): The API key for authentication.

Returns:
bool: True if the LLM configuration was successfully saved, False otherwise.
"""

penify_file = get_penify_config()
Expand Down Expand Up @@ -75,8 +91,19 @@ def save_llm_config(model, api_base, api_key):
return False

def save_jira_config(url, username, api_token):
"""
Save JIRA configuration settings in the .penify file.
"""Save JIRA configuration settings in the .penify file.

This function reads existing JIRA configuration from the .penify file,
updates or adds new JIRA configuration details, and writes it back to
the file.

Args:
url (str): The URL of the JIRA instance.
username (str): The username for accessing the JIRA instance.
api_token (str): The API token used for authentication.

Returns:
bool: True if the configuration was successfully saved, False otherwise.
"""
from penify_hook.utils import recursive_search_git_folder

Expand Down Expand Up @@ -108,8 +135,15 @@ def save_jira_config(url, username, api_token):
return False

def get_llm_config():
"""
Get LLM configuration from the .penify file.
"""Retrieve LLM configuration from the .penify file.

This function reads the .penify configuration file and extracts the LLM
settings. If the file does not exist or contains invalid JSON, it
returns an empty dictionary.

Returns:
dict: A dictionary containing the LLM configuration, or an empty dictionary if
the file is missing or invalid.
"""
config_file = get_penify_config()
if config_file.exists():
Expand All @@ -123,8 +157,15 @@ def get_llm_config():
return {}

def get_jira_config():
"""
Get JIRA configuration from the .penify file.
"""Get JIRA configuration from the .penify file.

This function reads the JIRA configuration from a JSON file specified in
the .penify file. If the .penify file exists and contains valid JSON
with a 'jira' key, it returns the corresponding configuration.
Otherwise, it returns an empty dictionary.

Returns:
dict: The JIRA configuration or an empty dictionary if not found or invalid.
"""
config_file = get_penify_config()
if config_file.exists():
Expand All @@ -138,8 +179,16 @@ def get_jira_config():
return {}

def config_llm_web():
"""
Open a web browser interface for configuring LLM settings.
"""Open a web browser interface for configuring LLM settings.

This function starts a temporary HTTP server that serves an HTML
template for configuring Large Language Model (LLM) settings. It handles
GET and POST requests to retrieve the current configuration, save new
configurations, and suppress log messages. The server runs on a random
port between 30000 and 50000, and it is accessible via a URL like
http://localhost:<redirect_port>. The function opens this URL in the
default web browser for configuration. Once configured, the server shuts
down.
"""
redirect_port = random.randint(30000, 50000)
server_url = f"http://localhost:{redirect_port}"
Expand All @@ -148,6 +197,15 @@ def config_llm_web():

class ConfigHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
"""Handle HTTP GET requests.

This function processes incoming GET requests and sends appropriate
responses based on the requested path. It serves an HTML template for
the root path ("/") and returns a JSON response with the current LLM
configuration for the "/get_config" path. For any other paths, it
returns a "Not Found" error.
"""

if self.path == "/":
self.send_response(200)
self.send_header("Content-type", "text/html")
Expand Down Expand Up @@ -189,6 +247,17 @@ def do_GET(self):
self.wfile.write(b"Not Found")

def do_POST(self):
"""Handle POST requests on the /save endpoint.

This method processes incoming POST requests to save language model
configuration data. It extracts the necessary parameters from the
request body, saves the configuration using the provided details, and
then schedules the server to shut down after a successful save.

Args:
self (HTTPRequestHandler): The instance of the HTTPRequestHandler class handling the request.
"""

if self.path == "/save":
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
Expand Down Expand Up @@ -240,8 +309,13 @@ def log_message(self, format, *args):
print("Configuration completed.")

def config_jira_web():
"""
Open a web browser interface for configuring JIRA settings.
"""Open a web browser interface for configuring JIRA settings.

This function sets up a simple HTTP server using Python's built-in
`http.server` module to handle GET and POST requests. The server serves
an HTML page for configuration and handles saving the JIRA configuration
details through API tokens and URLs. Upon successful configuration, it
shuts down the server gracefully.
"""
redirect_port = random.randint(30000, 50000)
server_url = f"http://localhost:{redirect_port}"
Expand All @@ -250,6 +324,14 @@ def config_jira_web():

class ConfigHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
"""Handle GET requests for different paths.

This function processes GET requests based on the path requested. It
serves an HTML template for the root path, returns a JSON configuration
for a specific endpoint, and handles any other paths by returning a 404
error.
"""

if self.path == "/":
self.send_response(200)
self.send_header("Content-type", "text/html")
Expand Down Expand Up @@ -291,6 +373,16 @@ def do_GET(self):
self.wfile.write(b"Not Found")

def do_POST(self):
"""Handle HTTP POST requests to save JIRA configuration.

This method processes incoming POST requests to save JIRA configuration
details. It reads JSON data from the request body, extracts necessary
parameters (URL, username, API token, and verify), saves the
configuration using the `save_jira_config` function, and responds with
success or error messages. If an exception occurs during the process, it
sends a 500 Internal Server Error response.
"""

if self.path == "/save":
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
Expand Down Expand Up @@ -345,8 +437,11 @@ def log_message(self, format, *args):
print("Configuration completed.")

def get_token():
"""
Get the token based on priority.
"""Get the token based on priority from environment variables or
configuration files.

Returns:
str: The API token if found, otherwise None.
"""
import os
env_token = os.getenv('PENIFY_API_TOKEN')
Expand Down
Loading
Loading