Parcourir la source

[#1] chore: make git-ident-filter line-restricted and automate remote model deployment

Lange François il y a 3 semaines
Parent
commit
24634c07f4
2 fichiers modifiés avec 59 ajouts et 17 suppressions
  1. 57 16
      local_tools/git-ident-filter.py
  2. 2 1
      scripts/deploy_to_server.py

+ 57 - 16
local_tools/git-ident-filter.py

@@ -6,78 +6,119 @@ import subprocess
 import re
 from datetime import datetime
 
-# Force LF-only line endings on Windows for stdin and stdout to prevent automatic CRLF translation
+# -----------------------------------------------------------------------------
+# STEP 1: LINE ENDINGS CONFIGURATION FOR WINDOWS/UNIX COMPATIBILITY
+# -----------------------------------------------------------------------------
+# On Windows, python streams sometimes default to automatically translating LF to CRLF.
+# Since Git filters must handle raw file streams, we force standard Unix LF ('\n') 
+# line endings on stdin and stdout to prevent Git from raising corrupt content errors.
 if hasattr(sys.stdin, 'reconfigure'):
     sys.stdin.reconfigure(newline='\n')
 if hasattr(sys.stdout, 'reconfigure'):
     sys.stdout.reconfigure(newline='\n')
 
-# Detect execution mode (clean or smudge) passed by Git
+# -----------------------------------------------------------------------------
+# STEP 2: PARSE COMMAND LINE ARGUMENTS
+# -----------------------------------------------------------------------------
+# The Git configuration passes arguments to this script:
+# sys.argv[1] is either 'clean' or 'smudge' (determining execution mode)
+# sys.argv[2] (for smudge mode) is the relative file path of the file being smudged
 mode = sys.argv[1] if len(sys.argv) > 1 else "smudge"
 
+# -----------------------------------------------------------------------------
+# STEP 3: DEFINE METADATA RETRIEVAL FUNCTION
+# -----------------------------------------------------------------------------
 def get_git_info(file_path):
     """Retrieves commit metadata for the specific file using git log, or falls back to system context."""
     try:
-        # 1. Query git log for the last commit details of the specific file
+        # A. Query git log for the last commit details of the specific file.
+        # We specify the YYYY/MM/DD HH:MM:SS format using '--date=format:...'.
+        # We use a pipe '|' delimiter to separate fields in the output format.
         cmd = [
             "git", "log", "-1",
             "--date=format:%Y/%m/%d %H:%M:%S",
             "--format=%an|%ae|%ad|%cn|%ce|%cd|%H|%D|%N",
             "--", file_path
         ]
+        
+        # Run the git command, capture stdout, ignore stderr, decode bytes to string.
         out = subprocess.check_output(cmd, stderr=subprocess.DEVNULL).decode('utf-8', errors='ignore').strip()
         if out:
+            # Split the pipe-delimited string back into an array of fields
             parts = out.split('|')
             if len(parts) == 9:
                 return parts
     except Exception:
+        # If git log fails (e.g., file not tracked yet), proceed to fallback logic below.
         pass
     
-    # 2. Fallback: Query local Git configuration if file is not committed yet
+    # B. Fallback: Query local Git configuration if file is not committed yet.
     try:
+        # Retrieve the user's local git user.name
         author_name = subprocess.check_output(["git", "config", "user.name"], stderr=subprocess.DEVNULL).decode('utf-8', errors='ignore').strip() or "system"
+        # Retrieve the user's local git user.email
         author_email = subprocess.check_output(["git", "config", "user.email"], stderr=subprocess.DEVNULL).decode('utf-8', errors='ignore').strip() or "system@mail.com"
     except Exception:
+        # If Git is not installed or configured, fallback to default system strings.
         author_name = "system"
         author_email = "system@mail.com"
         
+    # Get current system date & time formatted consistently
     now_str = datetime.now().strftime("%Y/%m/%d %H:%M:%S")
+    # Return placeholder list in the same structure as git log output
     return [author_name, author_email, now_str, author_name, author_email, now_str, "Not Committed Yet", "local", "none"]
 
+# -----------------------------------------------------------------------------
+# STEP 4: EXECUTE SPECIFIED FILTER MODE
+# -----------------------------------------------------------------------------
 if mode == "clean":
-    # CLEAN Mode: Replaces any smudged $Format:...$ tag back to the standard neutral git representation
+    # -------------------------------------------------------------------------
+    # CLEAN MODE (Staging phase / git add)
+    # -------------------------------------------------------------------------
+    # Read the file's contents directly from standard input (passed by Git)
     content = sys.stdin.read()
+    
     # Non-greedy substitution to restore standard placeholder format for Git storage.
     # We construct the search pattern and replacement dynamically to avoid matching our own code.
-    pattern = r'\$Format' + r':([^%:\r\n]+):([^%:\r\n]+):[^\r\n$]*?\$'
-    cleaned = re.sub(pattern, r'$Format:\1:\2:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$', content)
+    pattern = r'\$F' + r'ormat:[^\r\n$]+\$'
+    repl = r'$F' + r'ormat:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$'
+    
+    # Run regular expression search and replace
+    cleaned = re.sub(pattern, repl, content)
+    
+    # Write the cleaned output directly to stdout so Git can write it to the index
     sys.stdout.write(cleaned)
 
 else:
-    # SMUDGE Mode: Dynamically injects actual project, file path, and commit metadata into the file
+    # -------------------------------------------------------------------------
+    # SMUDGE MODE (Checkout / pull phase)
+    # -------------------------------------------------------------------------
     try:
         # Get absolute path of repository to find project directory name
         toplevel = subprocess.check_output(["git", "rev-parse", "--show-toplevel"], stderr=subprocess.DEVNULL).decode().strip()
         project_name = os.path.basename(toplevel)
 
-        # Get the relative path of the file being smudged
+        # Get the relative path of the file being smudged (passed as 2nd CLI arg by git configuration)
         file_name = sys.argv[2] if len(sys.argv) > 2 else "unknown_file"
-        file_name = file_name.replace('\\', '/')
 
-        # Read the file content sent by Git on stdin
+        # Read the raw file content sent by Git on stdin
         content = sys.stdin.read()
 
-        # Query git log metadata or local fallbacks
+        # Query git log metadata or local config fallbacks
         info = get_git_info(file_name)
 
-        # Format replacement string using dynamic project and file name
-        replacement = f"$Format" + f":{project_name}:{file_name}:{info[0]}:{info[1]}:{info[2]}:{info[3]}:{info[4]}:{info[5]}:{info[6]}:{info[7]}:{info[8]}$"
+        # Format replacement string using dynamic project name and relative file path
+        # This replaces the placeholder metadata fields with actual git variables
+        replacement = f"$F" + f"ormat:{project_name}:{file_name}:{info[0]}:{info[1]}:{info[2]}:{info[3]}:{info[4]}:{info[5]}:{info[6]}:{info[7]}:{info[8]}$"
 
         # Regex replacement targeting the dynamic format placeholders
-        pattern = r'\$Format' + r':([^%:\r\n]+):([^%:\r\n]+):%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N\$'
+        # Pattern explanation: Matches "$Format:LocalFoodAI:app.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N$"
+        pattern = r'\$F' + r'ormat:LocalFoodAI:app\.py:%an:%ae:%ad:%cn:%ce:%cd:%H:%D:%N\$'
         smudged = re.sub(pattern, replacement, content)
+        
+        # Write smudged file contents to stdout so Git can output the file onto the filesystem
         sys.stdout.write(smudged)
 
     except Exception:
-        # Security fallback: If executed outside Git repo, write stream unchanged
+        # Safety fallback: If execution fails (e.g. outside git repository), write stream unchanged
         sys.stdout.write(sys.stdin.read())

+ 2 - 1
scripts/deploy_to_server.py

@@ -34,7 +34,8 @@ def deploy():
         ssh.connect(host, username=user, password=password, timeout=10)
         print("Connected successfully!")
         
-        command = "cd food_project && git stash && rm -f git_version.txt git_id.txt && git -c filter.ident-dynamic.clean= -c filter.ident-dynamic.smudge= pull && git stash clear && docker-compose up -d --build"
+        local_model = os.environ.get('LLM_MODEL', 'llama3.2-vision:11b')
+        command = f"cd food_project && git stash && rm -f git_version.txt git_id.txt && git -c filter.ident-dynamic.clean= -c filter.ident-dynamic.smudge= pull && git stash clear && sed -i 's/^LLM_MODEL=.*/LLM_MODEL={local_model}/' .env && docker-compose up -d --build"
         print(f"Executing: {command}")
         
         stdin, stdout, stderr = ssh.exec_command(command)