diff --git a/penify_hook/api_client.py b/penify_hook/api_client.py index a333a6a..07b604e 100644 --- a/penify_hook/api_client.py +++ b/penify_hook/api_client.py @@ -23,11 +23,15 @@ def send_file_for_docstring_generation(self, file_name, content, line_numbers, r 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. + 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. """ payload = { 'file_path': file_name, @@ -65,6 +69,9 @@ def generate_commit_summary(self, git_diff, instruction: str = "", repo_details 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, @@ -113,18 +120,20 @@ 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): - """ - Generate a commit summary using a local LLM client instead of the API. - + """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: Git diff of changes - message: User-provided commit message or instructions - repo_details: Details about the repository - llm_client: Instance of LLMClient - jira_context: Optional JIRA issue context to enhance the summary - + 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 with title and description for the commit + dict: A dictionary containing the title and description for the commit. """ try: return llm_client.generate_commit_summary(diff, message, generate_description, repo_details, jira_context) @@ -134,6 +143,16 @@ 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. + + 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. + + Returns: + str: The API key if the request is successful, `None` otherwise. + """ + url = self.api_url+"/v1/apiToken/get" response = requests.get(url, headers={"Authorization": f"Bearer {self.BEARER_TOKEN}"}, timeout=60*10) diff --git a/penify_hook/commit_analyzer.py b/penify_hook/commit_analyzer.py index 5b89c6d..31ab46b 100644 --- a/penify_hook/commit_analyzer.py +++ b/penify_hook/commit_analyzer.py @@ -24,18 +24,23 @@ def get_summary(self, instruction: str, generate_description: bool) -> dict: This function retrieves the differences of the staged changes in the repository and generates a commit summary using the provided instruction. If there are no changes staged for commit, an exception is - raised. If an LLM client is provided, it will use that for generating - the summary, otherwise it will use the API client. + raised. If a JIRA client is connected, it will attempt to extract issue + keys from the current branch and use them to fetch context. The summary + can be generated either with a Language Model (LLM) client or through + the API client. Args: instruction (str): A string containing instructions for generating the commit summary. + generate_description (bool): Whether to include detailed descriptions in the summary. Returns: - str: The generated commit summary based on the staged changes and provided - instruction. + dict: The generated commit summary based on the staged changes, provided + instruction, and any relevant JIRA context. The dictionary contains keys + such as 'summary', 'description', etc., depending on whether a + description was requested. Raises: - Exception: If there are no changes staged for commit. + ValueError: If there are no changes staged for commit. """ diff = self.repo.git.diff('--cached') if not diff: @@ -68,17 +73,17 @@ def get_summary(self, instruction: str, generate_description: bool) -> dict: def run(self, msg: Optional[str], edit_commit_message: bool, generate_description: bool): """Run the post-commit hook. - This method retrieves the list of modified files from the last commit - and processes each file. It stages any files that have been modified - during processing and creates an auto-commit if changes were made. A - progress bar is displayed to indicate the processing status of each - file. If there is an error generating the commit summary, an exception - is raised. + This method processes the modified files from the last commit, stages + them, and creates an auto-commit with an optional message. It also + handles JIRA integration if available. If there is an error generating + the commit summary, an exception is raised. Args: msg (Optional[str]): An optional message to include in the commit. - edit_commit_message (bool): A flag indicating whether to open the - git commit edit terminal after committing. + edit_commit_message (bool): A flag indicating whether to open the git commit edit terminal after + committing. + generate_description (bool): A flag indicating whether to include a description in the commit + message. Raises: Exception: If there is an error generating the commit summary. @@ -106,16 +111,16 @@ def run(self, msg: Optional[str], edit_commit_message: bool, generate_descriptio self._amend_commit() def process_jira_integration(self, title: str, description: str, msg: str) -> tuple: - """ - Process JIRA integration for the commit message. - + """Process JIRA integration for the commit message. + Args: - title: Generated commit title - description: Generated commit description - msg: Original user message that might contain JIRA references - + title (str): Generated commit title. + description (str): Generated commit description. + msg (str): Original user message that might contain JIRA references. + Returns: - tuple: (updated_title, updated_description) with JIRA information + tuple: A tuple containing the updated commit title and description with + included JIRA information. """ # Look for JIRA issue keys in commit message, title, description and user message issue_keys = [] diff --git a/penify_hook/file_analyzer.py b/penify_hook/file_analyzer.py index 2a0eda8..cfbe826 100644 --- a/penify_hook/file_analyzer.py +++ b/penify_hook/file_analyzer.py @@ -103,7 +103,11 @@ def process_file(self, file_path, pbar): return False def print_processing(self, file_path): - """Print a processing message for a file.""" + """Print a processing message for a file. + + Args: + file_path (str): The path to the file being processed. + """ formatted_path = format_file_path(file_path) print(f"\n{format_highlight(f'Processing file: {formatted_path}')}") @@ -116,6 +120,9 @@ def run(self): an error message indicating that the file was not processed. The method displays a progress bar and colored output to provide visual feedback on the processing status. + + Args: + self (PostCommitHook): An instance of the PostCommitHook class. """ # Create a progress bar with appropriate stages diff --git a/penify_hook/folder_analyzer.py b/penify_hook/folder_analyzer.py index c04ed7b..3e23f7d 100644 --- a/penify_hook/folder_analyzer.py +++ b/penify_hook/folder_analyzer.py @@ -12,7 +12,21 @@ def __init__(self, dir_path: str, api_client: APIClient): super().__init__(dir_path, api_client) def list_all_files_in_dir(self, dir_path: str): - """List all files in a directory and its subdirectories.""" + """List all files in a directory and its subdirectories. + + This function traverses the specified directory using `os.walk`, + collecting paths of all non-hidden files into a list. It filters out + hidden directories (those starting with a dot) to ensure only visible + files are returned. + + Args: + dir_path (str): The path to the directory whose files and subdirectory files need to be + listed. + + Returns: + list: A list containing the full paths of all non-hidden files within the + specified directory and its subdirectories. + """ files = [] for dirpath, dirnames, filenames in os.walk(dir_path): @@ -24,7 +38,17 @@ def list_all_files_in_dir(self, dir_path: str): return files def run(self): - """Run the post-commit hook.""" + """Run the post-commit hook. + + This function processes all files in a specified directory using a + progress bar. It lists all files, initializes a `FileAnalyzerGenHook` + for each file, and runs it. Errors during processing of individual files + are caught and logged, but do not stop the processing of other files. A + progress bar is displayed indicating the number of files processed. + + Args: + self (PostCommitHook): The instance of the post-commit hook class. + """ try: file_list = self.list_all_files_in_dir(self.dir_path) total_files = len(file_list) diff --git a/penify_hook/jira_client.py b/penify_hook/jira_client.py index a367405..b193ecf 100644 --- a/penify_hook/jira_client.py +++ b/penify_hook/jira_client.py @@ -44,28 +44,35 @@ def __init__(self, jira_url: str = None, jira_user: str = None, jira_api_token: self.jira_client = None def is_connected(self) -> bool: - """ - Check if the JIRA client is connected. - + """Check if the JIRA client is connected. + + This function checks whether the JIRA client has successfully + established a connection. It returns `True` if the client is connected, + and `False` otherwise. + Returns: - bool: True if connected, False otherwise + bool: True if the JIRA client is connected, False otherwise """ return self.jira_client is not None def extract_issue_keys_from_branch(self, branch_name: str) -> List[str]: - """ - Extract JIRA issue keys from a branch name. - - Many teams follow conventions like: - - feature/ABC-123-description - - bugfix/ABC-123-fix-something - - hotfix/ABC-123/short-desc - + """Extracts JIRA issue keys from a branch name. + + This function searches through a given git branch name to find and + return any JIRA issue keys that match the pattern. Common conventions + for JIRA issue keys in branch names include: - + feature/PROJECT-123-description - bugfix/PROJECT-123-fix-something - + hotfix/PROJECT-123/short-desc + Args: - branch_name: The git branch name - + branch_name (str): The name of the git branch to search for JIRA issue keys. + Returns: - List of JIRA issue keys found + List[str]: A list of unique JIRA issue keys found in the branch name. + + Examples: + extract_issue_keys_from_branch("feature/PROJ-456-add-new-feature") + # Output: ['PROJ-456'] """ # Common JIRA issue key pattern: PROJECT-123 pattern = r'[A-Z][A-Z0-9_]+-[0-9]+' @@ -75,14 +82,13 @@ def extract_issue_keys_from_branch(self, branch_name: str) -> List[str]: return list(set(matches)) # Remove duplicates def extract_issue_keys(self, text: str) -> List[str]: - """ - Extract JIRA issue keys from text. - + """Extract JIRA issue keys from a given text. + Args: - text: Text to search for JIRA issue keys - + text (str): The text in which to search for JIRA issue keys. + Returns: - List of JIRA issue keys found + List[str]: A list of unique JIRA issue keys found in the text. """ # Common JIRA issue key pattern: PROJECT-123 pattern = r'[A-Z][A-Z0-9_]+-[0-9]+' @@ -90,14 +96,24 @@ def extract_issue_keys(self, text: str) -> List[str]: return list(set(matches)) # Remove duplicates def get_issue_details(self, issue_key: str) -> Optional[Dict[str, Any]]: - """ - Get details of a JIRA issue. - + """Retrieve details of a JIRA issue based on its key. + + This function fetches detailed information about a specified JIRA issue + using the provided issue key. It checks if the JIRA client is connected + before attempting to retrieve the issue. If the client is not connected, + it logs a warning and returns `None`. The function then attempts to + fetch the issue from the JIRA server and constructs a dictionary + containing various details about the issue such as its key, summary, + status, description, assignee, reporter, type, priority, and URL. If any + errors occur during this process, they are logged, and `None` is + returned. + Args: - issue_key: JIRA issue key (e.g., "PROJECT-123") - + issue_key (str): The JIRA issue key (e.g., "PROJECT-123"). + Returns: - Dict with issue details or None if not found + Dict[str, Any] or None: A dictionary containing the details of the JIRA + issue if found, otherwise `None`. """ if not self.is_connected(): logging.warning("JIRA client not connected") @@ -121,15 +137,14 @@ def get_issue_details(self, issue_key: str) -> Optional[Dict[str, Any]]: return None def add_comment(self, issue_key: str, comment: str) -> bool: - """ - Add a comment to a JIRA issue. - + """Add a comment to a JIRA issue. + Args: - issue_key: JIRA issue key (e.g., "PROJECT-123") - comment: Comment text to add - + issue_key (str): JIRA issue key (e.g., "PROJECT-123") + comment (str): Comment text to add + Returns: - bool: True if comment was added successfully, False otherwise + bool: True if the comment was added successfully, False otherwise """ if not self.is_connected(): logging.warning("JIRA client not connected") @@ -144,15 +159,14 @@ def add_comment(self, issue_key: str, comment: str) -> bool: return False def update_issue_status(self, issue_key: str, transition_name: str) -> bool: - """ - Update the status of a JIRA issue. - + """Update the status of a JIRA issue. + Args: - issue_key: JIRA issue key (e.g., "PROJECT-123") - transition_name: Name of the transition (e.g., "In Progress", "Done") - + issue_key (str): The key of the JIRA issue to be updated (e.g., "PROJECT-123"). + transition_name (str): The name of the desired transition (e.g., "In Progress", "Done"). + Returns: - bool: True if status was updated successfully, False otherwise + bool: True if the status was successfully updated, False otherwise. """ if not self.is_connected(): logging.warning("JIRA client not connected") @@ -182,16 +196,18 @@ def update_issue_status(self, issue_key: str, transition_name: str) -> bool: return False def format_commit_message_with_jira_info(self, commit_title: str, commit_description: str, issue_keys: List[str] = None) -> tuple: - """ - Format commit message with JIRA issue information. - + """Format commit message with JIRA issue information. + Args: - commit_title: Original commit title - commit_description: Original commit description - issue_keys: List of JIRA issue keys to include (optional, will extract from title/description if not provided) - + commit_title (str): The original commit title. + commit_description (str): The original commit description. + issue_keys (List[str]?): A list of JIRA issue keys to include in the commit message. If not + provided, + issue keys will be extracted from both the title and the description. + Returns: - tuple: (updated_title, updated_description) with JIRA information included + tuple: A tuple containing the updated commit title and description with JIRA + information included. """ # If no issue keys provided, extract them from title and description if not issue_keys: @@ -230,14 +246,14 @@ def format_commit_message_with_jira_info(self, commit_title: str, commit_descrip return updated_title, updated_description def get_detailed_issue_context(self, issue_key: str) -> Dict[str, Any]: - """ - Get comprehensive details about a JIRA issue including context for better commit messages. - + """Get comprehensive details about a JIRA issue including context for + better commit messages. + Args: - issue_key: JIRA issue key (e.g., "PROJECT-123") - + issue_key (str): The JIRA issue key (e.g., "PROJECT-123"). + Returns: - Dict containing business and technical context from the issue + Dict[str, Any]: A dictionary containing business and technical context from the issue. """ if not self.is_connected(): logging.warning("JIRA client not connected") @@ -307,12 +323,19 @@ def get_detailed_issue_context(self, issue_key: str) -> Dict[str, Any]: return {} def get_commit_context_from_issues(self, issue_keys: List[str]) -> Dict[str, Any]: - """ - Gather contextual information from JIRA issues to improve commit messages. - + """Gather contextual information from JIRA issues to improve commit + messages. + + This function processes a list of JIRA issue keys, retrieves detailed + context for each issue, and aggregates it into a dictionary that can be + used to enhance commit messages. It first retrieves the primary issue + (the first key in the list) and then gathers basic details for any + related issues. The resulting context includes information from both the + primary and related issues, along with all issue keys. + Args: issue_keys: List of JIRA issue keys to gather information from - + Returns: Dict containing business and technical context from the issues """ @@ -339,16 +362,17 @@ def get_commit_context_from_issues(self, issue_keys: List[str]) -> Dict[str, Any return context def enhance_commit_message(self, title: str, description: str, issue_keys: List[str]) -> tuple: - """ - Enhance commit message with business and technical context from JIRA issues. - + """Enhance a commit message with business and technical context from JIRA + issues. + Args: - title: Original commit title - description: Original commit description - issue_keys: List of JIRA issue keys to include - + title (str): Original commit title. + description (str): Original commit description. + issue_keys (List[str]): List of JIRA issue keys to include in the enhanced commit message. + Returns: - tuple: (enhanced_title, enhanced_description) with JIRA context + tuple: A tuple containing the enhanced commit title and description with added + context from JIRA issues. """ if not issue_keys or not self.is_connected(): return title, description diff --git a/penify_hook/login_command.py b/penify_hook/login_command.py index d0a1469..93da34e 100644 --- a/penify_hook/login_command.py +++ b/penify_hook/login_command.py @@ -3,6 +3,22 @@ def setup_login_parser(parser): # Add all other necessary arguments for login command def handle_login(args): + """Handle the login command. + + This function is responsible for initiating a user login process by + calling the `login` function from the + `penify_hook.commands.auth_commands` module. It uses predefined + constants `API_URL` and `DASHBOARD_URL` from the `penify_hook.constants` + module to perform the login operation. + + Args: + args (argparse.Namespace): Parsed arguments containing necessary parameters for the login command. + + Returns: + None: This function does not return any value; it is expected to handle the + login process internally. + """ + from penify_hook.constants import API_URL, DASHBOARD_URL from penify_hook.commands.auth_commands import login diff --git a/penify_hook/main.py b/penify_hook/main.py index 268800b..3273e23 100644 --- a/penify_hook/main.py +++ b/penify_hook/main.py @@ -4,6 +4,15 @@ def main(): + """Penify CLI tool for generating smart commit messages with JIRA + integration, configuring local-LLM and JIRA, + and generating code documentation. This tool provides a command-line + interface to interact with Penify's services. It supports basic commands + that do not require login and advanced commands that require user + authentication. The `--version` flag can be used to display the version + information. + """ + parser = argparse.ArgumentParser( description="""Penify CLI tool for: 1. AI commit message generation with JIRA integration to enhance commit messages. diff --git a/penify_hook/ui_utils.py b/penify_hook/ui_utils.py index ebb2142..6c7c27a 100644 --- a/penify_hook/ui_utils.py +++ b/penify_hook/ui_utils.py @@ -26,56 +26,148 @@ PROCESSING_SYMBOL = "⟳" def format_info(message): - """Format an informational message with appropriate color.""" + """Format an informational message with appropriate color. + + Args: + message (str): The text of the informational message to be formatted. + + Returns: + str: The formatted informational message with the specified color. + """ return f"{INFO_COLOR}{message}{Style.RESET_ALL}" def format_success(message): - """Format a success message with appropriate color.""" + """Format a success message with appropriate color. + + This function takes a message as input and wraps it in ANSI escape codes + to display it in green, indicating a successful operation. The + Style.RESET_ALL is applied at the end to ensure that any subsequent text + is displayed in the default style. + + Args: + message (str): The message to be formatted as a success message. + + Returns: + str: The formatted success message with green color and reset style. + """ return f"{SUCCESS_COLOR}{message}{Style.RESET_ALL}" def format_warning(message): - """Format a warning message with appropriate color.""" + """Format a warning message with appropriate color. + + Args: + message (str): The warning message to be formatted. + + Returns: + str: The formatted warning message with the specified color. + """ return f"{WARNING_COLOR}{message}{Style.RESET_ALL}" def format_error(message): - """Format an error message with appropriate color.""" + """Format an error message with appropriate color. + + This function takes a plain error message and wraps it in ANSI escape + codes to apply the specified error color, ensuring that the error + message is visually distinct when output. + + Args: + message (str): The plain text error message to be formatted. + + Returns: + str: The formatted error message with the error color applied. + """ return f"{ERROR_COLOR}{message}{Style.RESET_ALL}" def format_highlight(message): - """Format a highlighted message with appropriate color.""" + """Format a highlighted message with appropriate color. + + Args: + message (str): The message to be formatted and highlighted. + + Returns: + str: The formatted message with applied highlight style. + """ return f"{HIGHLIGHT_COLOR}{message}{Style.RESET_ALL}" def format_file_path(file_path): - """Format a file path with appropriate color.""" + """Format a file path with appropriate color. + + This function takes a file path as input and wraps it in ANSI escape + codes to apply a warning color. The original file path is then reset to + default style using Style.RESET_ALL. + + Args: + file_path (str): The file path to be formatted. + + Returns: + str: The formatted file path with the warning color applied. + """ return f"{WARNING_COLOR}{file_path}{Style.RESET_ALL}" def print_info(message): - """Print an informational message with appropriate formatting.""" + """Print an informational message with appropriate formatting. + + This function takes a string message as input and prints it in a + formatted manner. It utilizes the `format_info` function to apply any + necessary formatting before printing. + + Args: + message (str): The message to be printed. + """ print(format_info(message)) def print_success(message): - """Print a success message with appropriate formatting.""" + """Print a success message with appropriate formatting. + + This function takes a string `message` and prints it as a formatted + success message. The formatting includes adding a prefix "Success: " to + the message and enclosing it within asterisks for emphasis. + + Args: + message (str): The message to be printed as a success message. + """ print(format_success(message)) def print_warning(message): - """Print a warning message with appropriate formatting.""" + """Print a warning message with appropriate formatting. + + Args: + message (str): The warning message to be printed. + """ print(format_warning(message)) def print_error(message): - """Print an error message with appropriate formatting.""" + """Print an error message with appropriate formatting. + + Args: + message (str): The error message to be printed. + """ print(format_error(message)) def print_processing(file_path): - """Print a processing message for a file.""" + """Print a processing message for a specified file. + + This function takes a file path, formats it using `format_file_path`, + and then prints a formatted message indicating that the file is being + processed. The formatted path is highlighted using `format_highlight`. + + Args: + file_path (str): The path of the file to be processed. + """ formatted_path = format_file_path(file_path) print(f"\n{format_highlight(f'Processing file: {formatted_path}')}") def print_status(status, message): """Print a status message with an appropriate symbol. - + + This function takes a status and a message, then prints them with a + colored symbol that corresponds to the given status. The available + statuses are 'success', 'warning', 'error', and any other value will + default to a processing indicator. + Args: - status (str): One of 'success', 'warning', or 'error' - message (str): The message to print + status (str): The status type ('success', 'warning', 'error') or another string. + message (str): The message to be displayed along with the symbol. """ if status == 'success': print(f" {SUCCESS_COLOR}{SUCCESS_SYMBOL} {message}{Style.RESET_ALL}") @@ -88,14 +180,14 @@ def print_status(status, message): def create_progress_bar(total, desc="Processing", unit="item"): """Create a tqdm progress bar with consistent styling. - + Args: - total (int): Total number of items to process - desc (str): Description for the progress bar - unit (str): Unit label for the progress items - + total (int): Total number of items to process. + desc (str): Description for the progress bar. Defaults to "Processing". + unit (str): Unit label for the progress items. Defaults to "item". + Returns: - tqdm: A configured tqdm progress bar instance + tqdm: A configured tqdm progress bar instance. """ return tqdm( total=total, @@ -106,14 +198,19 @@ def create_progress_bar(total, desc="Processing", unit="item"): ) def create_stage_progress_bar(stages, desc="Processing"): - """Create a tqdm progress bar for processing stages with consistent styling. - + """Create a tqdm progress bar for processing stages with consistent + styling. + + This function initializes and returns a tqdm progress bar object for + tracking the progress through a series of stages. It also provides a + description for the progress bar to enhance its usability. + Args: - stages (list): List of stage names - desc (str): Description for the progress bar - + stages (list): A list of strings representing individual stages in the process. + desc (str?): A description for the progress bar. Defaults to "Processing". + Returns: - tuple: (tqdm progress bar, list of stages) + tuple: A tuple containing the tqdm progress bar object and the list of stages. """ pbar = tqdm( total=len(stages), @@ -126,10 +223,15 @@ def create_stage_progress_bar(stages, desc="Processing"): def update_stage(pbar, stage_name): """Update the progress bar with a new stage name. - + + This function updates the provided tqdm progress bar to reflect the + current stage of a process. It clears any existing postfix and sets a + new description based on the provided stage name. The display is then + refreshed to ensure that the update is visible immediately. + Args: - pbar (tqdm): The progress bar to update - stage_name (str): The name of the current stage + pbar (tqdm): The progress bar object to be updated. + stage_name (str): A string representing the current stage of the process. """ # Force refresh with a custom description and ensure it's visible pbar.set_postfix_str("") # Clear any existing postfix diff --git a/penify_hook/utils.py b/penify_hook/utils.py index 3dc6d02..3854ef4 100644 --- a/penify_hook/utils.py +++ b/penify_hook/utils.py @@ -11,18 +11,21 @@ class GitRepoNotFoundError(Exception): def get_repo_details(repo: Repo): - """Get the details of the repository, including the hosting service, + """Get the details of a Git repository, including its hosting service, organization name, and repository name. - This method checks the remote URL of the repository to determine whether - it is hosted on GitHub, Azure DevOps, Bitbucket, GitLab, or another - service. It extracts the organization (or user) name and the repository - name from the URL. If the hosting service cannot be determined, it will - return "Unknown Hosting Service". + This function extracts these details from the remote URL of the provided + Git repository. It supports popular platforms like GitHub, Azure DevOps, + Bitbucket, and GitLab. If the hosting service cannot be determined, it + defaults to "Unknown Hosting Service". + + Args: + repo (Repo): The GitPython Repo object representing the repository. Returns: - dict: A dictionary containing the organization name, repository name, and - hosting service. + dict: A dictionary containing keys for 'organization_name', 'repo_name', and + 'vendor' which represent the organization name, repository name, and + hosting service respectively. """ remote_url = None hosting_service = "Unknown" @@ -85,7 +88,8 @@ def recursive_search_git_folder(folder_path): folder_path (str): The path of the directory to search for the .git folder. Returns: - str: The path of the directory containing the .git folder. + str: The path of the directory containing the .git folder. If no .git folder + is found, returns None. """ if os.path.isdir(folder_path): if '.git' in os.listdir(folder_path): @@ -112,7 +116,8 @@ def find_git_parent(path): str: The absolute path to the parent directory containing the `.git` folder. Raises: - GitRepoNotFoundError: If no Git repository is found in the specified path or any + GitRepoNotFoundError: If no Git repository is found in the specified path or any of its parent + directories. """ current_dir = os.path.abspath(path)