Sfoglia il codice sorgente

Create initial files for AI commit generation

    This commit introduces the necessary files for generating Git
    commit messages using the Gemini AI model. It includes:

    - `Dockerfile`: Defines the environment for running the script,
      including Python 3.9, git, and the google-generativeai library.
      It also creates a non-root user for enhanced security.

    - `git_commit_ai.py`: Contains the core logic for retrieving
      staged changes using `git diff --staged`, generating a commit
      message with the Gemini API, and creating the commit.  It uses
      the `subprocess` module for interacting with Git.

    - `ai-commit.sh`: A shell script to run the Docker container,
      mounting the current directory and Git configuration for access
      by the script.

    - `README.md`:  A basic README file to provide context.

    This initial commit sets up the foundation for automating the
    commit message generation process, reducing manual effort and
    improving commit quality.
seno 6 giorni fa
commit
8b7cb7ced1
4 ha cambiato i file con 210 aggiunte e 0 eliminazioni
  1. 35 0
      Dockerfile
  2. 1 0
      README.md
  3. 9 0
      ai-commit.sh
  4. 165 0
      git_commit_ai.py

+ 35 - 0
Dockerfile

@@ -0,0 +1,35 @@
+# Use a Python base image (slim version to keep it small)
+FROM python:3.9-slim-buster
+
+# Install git
+RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
+
+# Set the working directory inside the container
+WORKDIR /app
+
+# Set the home directory environment variable
+ENV HOME=/home/appuser
+
+# Create a non-root user and group
+RUN groupadd -r appgroup && useradd --no-log-init -r -g appgroup -d /home/appuser -m appuser
+
+# Install any necessary dependencies (in this case, just the google-generativeai library)
+RUN pip install --no-cache-dir google-generativeai
+
+# Copy the Python script into the container
+COPY git_commit_ai.py /app/
+
+# Make the script executable
+RUN chmod +x /app/git_commit_ai.py
+
+WORKDIR /repo
+
+# Change ownership of the app and repo directories
+RUN chown -R appuser:appgroup /app && chown -R appuser:appgroup /repo
+
+# Switch to the non-root user
+USER appuser
+
+# Set the entrypoint to run the script when the container starts
+ENTRYPOINT ["python", "/app/git_commit_ai.py"]
+

+ 1 - 0
README.md

@@ -0,0 +1 @@
+# CREATE GEN-AI COMMIT MESSAGE

+ 9 - 0
ai-commit.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+
+docker run --rm -it \
+	-v "$(pwd):/repo" \
+	-v "$HOME/.gitconfig:/home/appuser/.gitconfig:ro" \
+	-v "$HOME/.git-credentials:/home/appuser/.git-credentials:ro" \
+	-e GEMINI_API_KEY="$GEMINI_API_KEY" \
+	-u "$(id -u):$(id -g)" \
+	docker.senomas.com/commit:1.0

+ 165 - 0
git_commit_ai.py

@@ -0,0 +1,165 @@
+import subprocess
+import os
+import google.generativeai as genai
+
+
+def get_staged_diff():
+    """
+    Retrieves the diff of staged files using git.
+
+    Returns:
+        str: The diff of the staged files, or None on error.
+    """
+    try:
+        # Use subprocess.run for better control and error handling
+        process = subprocess.run(
+            ["git", "diff", "--staged"],  # Corrected: --staged is the correct option
+            capture_output=True,
+            text=True,  # Ensure output is returned as text
+            check=True,  # Raise an exception for non-zero exit codes
+        )
+        return process.stdout
+    except subprocess.CalledProcessError as e:
+        print(f"Error getting staged diff: {e}")
+        print(f"  stderr: {e.stderr}")  # Print stderr for more details
+        return None
+    except FileNotFoundError:
+        print(
+            "Error: git command not found.  Please ensure Git is installed and in your PATH."
+        )
+        return None
+    except Exception as e:
+        print(f"An unexpected error occurred: {e}")
+        return None
+
+
+def generate_commit_message(diff, gemini_api_key):
+    """
+    Generates a commit message using the Gemini API, given the diff.
+
+    Args:
+        diff (str): The diff of the staged files.
+        gemini_api_key (str): Your Gemini API key.
+
+    Returns:
+        str: The generated commit message, or None on error.
+    """
+    if not diff:
+        print("Error: No diff provided to generate commit message.")
+        return None
+
+    genai.configure(api_key=gemini_api_key)
+    model = genai.GenerativeModel("gemini-2.0-flash")
+
+    prompt = f"""
+    You are a helpful assistant that generates Git commit messages.
+    Analyze the following diff of staged files and generate a commit message adhering to standard Git conventions:
+
+    1.  **Subject Line:** Write a concise, imperative subject line summarizing the change (max 50 characters). Start with a capital letter. Do not end with a period.
+    2.  **Blank Line:** Leave a single blank line between the subject and the body.
+    3.  **Body:** Write a detailed but precise body explaining the 'what' and 'why' of the changes. Wrap lines at 72 characters. Focus on the motivation for the change and contrast its implementation with the previous behavior.
+
+    Diff:
+    ```diff
+    {diff}
+    ```
+
+    Generate only the commit message (subject and body).
+    """
+
+    try:
+        response = model.generate_content(prompt)
+        # Check for a successful response.
+        if response and response.text:
+            return response.text.strip()  # Remove leading/trailing whitespace
+        else:
+            print("Error: Gemini API returned an empty or invalid response.")
+            return None
+
+    except Exception as e:
+        print(f"Error generating commit message with Gemini: {e}")
+        return None
+
+
+def create_commit(message):
+    """
+    Creates a git commit with the given message.
+
+    Args:
+        message (str): The commit message.
+
+    Returns:
+        bool: True if the commit was successful, False otherwise.
+    """
+    if not message:
+        print("Error: No commit message provided to create commit.")
+        return False
+
+    try:
+        process = subprocess.run(
+            ["git", "commit", "-m", message],
+            check=True,  # Important: Raise exception on non-zero exit
+            capture_output=True,  # capture the output
+            text=True,
+        )
+        print(process.stdout)  # print the output
+        return True
+    except subprocess.CalledProcessError as e:
+        print(f"Error creating git commit: {e}")
+        print(e.stderr)
+        return False
+    except FileNotFoundError:
+        print("Error: git command not found.  Is Git installed and in your PATH?")
+        return False
+    except Exception as e:
+        print(f"An unexpected error occurred: {e}")
+        return False
+
+
+def main():
+    """
+    Main function to orchestrate the process of:
+    1. Getting the staged diff.
+    2. Generating a commit message using Gemini.
+    3. Creating a git commit with the generated message.
+    """
+    gemini_api_key = os.environ.get("GEMINI_API_KEY")
+    if not gemini_api_key:
+        print(
+            "Error: GEMINI_API_KEY environment variable not set.\n"
+            "  Please obtain an API key from Google Cloud and set the environment variable.\n"
+            "  For example: export GEMINI_API_KEY='YOUR_API_KEY'"
+        )
+        return
+
+    diff = get_staged_diff()
+    if diff is None:
+        print("Aborting commit due to error getting diff.")
+        return  # Exit the script
+
+    if not diff.strip():  # check if the diff is empty
+        print("Aborting: No changes staged to commit.")
+        return
+
+    message = generate_commit_message(diff, gemini_api_key)
+    if message is None:
+        print("Aborting commit due to error generating message.")
+        return  # Exit if message generation failed
+
+    print(f"Generated commit message:\n{message}")  # Print the message for review
+
+    # Prompt the user for confirmation before committing
+    user_input = input(
+        "Do you want to create the commit with this message? (y/n): "
+    ).lower()
+    if user_input == "y":
+        if create_commit(message):
+            print("Commit created successfully.")
+        else:
+            print("Commit failed.")
+    else:
+        print("Commit aborted by user.")
+
+
+if __name__ == "__main__":
+    main()