From 65fc32459adcfa877b8c65689b67a96b13b53b39 Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Sat, 21 Feb 2026 11:41:21 +0100 Subject: [PATCH] Refactor Dockerfile and add linguist module This commit refactors the Dockerfile to: - Reintroduce the programmer user creation with sudo privileges - Add necessary system dependencies for development tools - Update Go installation to include PATH in root's bashrc - Install github-linguist gem It also includes the addition of a new linguist command module with functions to interact with GitHub Linguist for language statistics and file information retrieval. --- Dockerfile => old/Dockerfile | 22 +- Makefile => old/Makefile | 0 {Notes => old/Notes}/jj_to_json.md | 0 README.md => old/README.md | 0 docker_startup.sh => old/docker_startup.sh | 0 logrotate-config => old/logrotate-config | 0 opencode.json => old/opencode.json | 0 {src => old/src}/PMOCrazyCoder.py | 0 {src => old/src}/crazy_mcp/__init__.py | 0 {src => old/src}/crazy_mcp/tools/__init__.py | 0 {src => old/src}/crazy_mcp/tools/jj.py | 0 {src => old/src}/crazy_mcp/tools/sandbox.py | 0 {src => old/src}/json_extract/__init__.py | 0 {src => old/src}/jujutsu/__init__.py | 0 {src => old/src}/jujutsu/command.py | 0 .../src/linguist/__init__.py | 0 old/src/linguist/command.py | 248 ++++++++++++++++++ {src => old/src}/ollama/__init__.py | 0 {src => old/src}/sandbox/__init__.py | 0 old/stamp/build_PMOCrazyCoder | 0 test.py => old/test.py | 0 {tools => old/tools}/install_latest_go.sh | 0 22 files changed, 265 insertions(+), 5 deletions(-) rename Dockerfile => old/Dockerfile (86%) rename Makefile => old/Makefile (100%) rename {Notes => old/Notes}/jj_to_json.md (100%) rename README.md => old/README.md (100%) rename docker_startup.sh => old/docker_startup.sh (100%) rename logrotate-config => old/logrotate-config (100%) rename opencode.json => old/opencode.json (100%) rename {src => old/src}/PMOCrazyCoder.py (100%) rename {src => old/src}/crazy_mcp/__init__.py (100%) rename {src => old/src}/crazy_mcp/tools/__init__.py (100%) rename {src => old/src}/crazy_mcp/tools/jj.py (100%) rename {src => old/src}/crazy_mcp/tools/sandbox.py (100%) rename {src => old/src}/json_extract/__init__.py (100%) rename {src => old/src}/jujutsu/__init__.py (100%) rename {src => old/src}/jujutsu/command.py (100%) rename stamp/build_PMOCrazyCoder => old/src/linguist/__init__.py (100%) create mode 100644 old/src/linguist/command.py rename {src => old/src}/ollama/__init__.py (100%) rename {src => old/src}/sandbox/__init__.py (100%) create mode 100644 old/stamp/build_PMOCrazyCoder rename test.py => old/test.py (100%) rename {tools => old/tools}/install_latest_go.sh (100%) diff --git a/Dockerfile b/old/Dockerfile similarity index 86% rename from Dockerfile rename to old/Dockerfile index c15777a..9a6990f 100644 --- a/Dockerfile +++ b/old/Dockerfile @@ -5,9 +5,6 @@ FROM python:3.11-slim RUN echo "deb http://deb.debian.org/debian testing main" \ > /etc/apt/sources.list.d/testing.list -# Création d'un utilisateur programmeur avec UID 1000 et home dans /home/programmer -RUN useradd -u 1000 -m -d /home/programmer -s /bin/bash programmer - WORKDIR /app # Installer les outils de développement Go et Rust @@ -28,12 +25,25 @@ RUN apt-get update && \ flake8 \ mypy \ isort \ - logrotate + logrotate \ + sudo \ + cmake \ + libicu-dev \ + zlib1g-dev \ + libcurl4-openssl-dev \ + libssl-dev \ + ruby-dev \ + jq COPY tools tools +# Création d'un utilisateur programmeur avec UID 1000 et home dans /home/programmer +RUN useradd -u 1000 -m -d /home/programmer -s /bin/bash programmer && \ + echo "programmer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/programmer + # Installer la dernière version de Go -RUN bash tools/install_latest_go.sh +RUN bash tools/install_latest_go.sh && \ + echo "PATH=/usr/local/go/bin:$PATH" >> /root/.bashrc ENV PATH=/usr/local/go/bin:$PATH # Installer la dernière version de Rust @@ -51,6 +61,8 @@ RUN npm install -g typescript tsc @types/node eslint prettier RUN cargo binstall --strategies crate-meta-data jj-cli +RUN gem install github-linguist + # Installer le SDK MCP Python RUN pip install --no-cache-dir ruff fastmcp ipython diff --git a/Makefile b/old/Makefile similarity index 100% rename from Makefile rename to old/Makefile diff --git a/Notes/jj_to_json.md b/old/Notes/jj_to_json.md similarity index 100% rename from Notes/jj_to_json.md rename to old/Notes/jj_to_json.md diff --git a/README.md b/old/README.md similarity index 100% rename from README.md rename to old/README.md diff --git a/docker_startup.sh b/old/docker_startup.sh similarity index 100% rename from docker_startup.sh rename to old/docker_startup.sh diff --git a/logrotate-config b/old/logrotate-config similarity index 100% rename from logrotate-config rename to old/logrotate-config diff --git a/opencode.json b/old/opencode.json similarity index 100% rename from opencode.json rename to old/opencode.json diff --git a/src/PMOCrazyCoder.py b/old/src/PMOCrazyCoder.py similarity index 100% rename from src/PMOCrazyCoder.py rename to old/src/PMOCrazyCoder.py diff --git a/src/crazy_mcp/__init__.py b/old/src/crazy_mcp/__init__.py similarity index 100% rename from src/crazy_mcp/__init__.py rename to old/src/crazy_mcp/__init__.py diff --git a/src/crazy_mcp/tools/__init__.py b/old/src/crazy_mcp/tools/__init__.py similarity index 100% rename from src/crazy_mcp/tools/__init__.py rename to old/src/crazy_mcp/tools/__init__.py diff --git a/src/crazy_mcp/tools/jj.py b/old/src/crazy_mcp/tools/jj.py similarity index 100% rename from src/crazy_mcp/tools/jj.py rename to old/src/crazy_mcp/tools/jj.py diff --git a/src/crazy_mcp/tools/sandbox.py b/old/src/crazy_mcp/tools/sandbox.py similarity index 100% rename from src/crazy_mcp/tools/sandbox.py rename to old/src/crazy_mcp/tools/sandbox.py diff --git a/src/json_extract/__init__.py b/old/src/json_extract/__init__.py similarity index 100% rename from src/json_extract/__init__.py rename to old/src/json_extract/__init__.py diff --git a/src/jujutsu/__init__.py b/old/src/jujutsu/__init__.py similarity index 100% rename from src/jujutsu/__init__.py rename to old/src/jujutsu/__init__.py diff --git a/src/jujutsu/command.py b/old/src/jujutsu/command.py similarity index 100% rename from src/jujutsu/command.py rename to old/src/jujutsu/command.py diff --git a/stamp/build_PMOCrazyCoder b/old/src/linguist/__init__.py similarity index 100% rename from stamp/build_PMOCrazyCoder rename to old/src/linguist/__init__.py diff --git a/old/src/linguist/command.py b/old/src/linguist/command.py new file mode 100644 index 0000000..5d9ea98 --- /dev/null +++ b/old/src/linguist/command.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +""" +GitHub Linguist Command Module + +This module provides functions to interact with GitHub Linguist, +particularly for retrieving language statistics in JSON format. +""" + +import subprocess +import json +import sys +import os +from typing import Dict, Any, Optional + + +def _linguist_call(path: str, is_file: bool) -> Dict[str, Any]: + """ + Execute github-linguist --json on the given path and return results as JSON. + + This is a utility function that handles the common execution logic + for both linguist_stats and linguist_file_info functions. + + Args: + path (str): The file or directory path to analyze + is_file (bool): True if path should be treated as a file, False for directory + + Returns: + Dict[str, Any]: JSON output from github-linguist + + Raises: + subprocess.CalledProcessError: If github-linguist command fails + FileNotFoundError: If github-linguist is not installed + ValueError: If path validation fails + """ + # Validate that the path exists + if not os.path.exists(path): + raise ValueError(f"Path does not exist: {path}") + + # Validate path type + if is_file and not os.path.isfile(path): + raise ValueError(f"Path is not a file: {path}") + elif not is_file and not os.path.isdir(path): + raise ValueError(f"Path is not a directory: {path}") + + try: + # Execute the github-linguist command with --json flag + result = subprocess.run( + ["github-linguist", "--json", path], + capture_output=True, + text=True, + check=True, + ) + + # Parse and return JSON output + return json.loads(result.stdout) + + except subprocess.CalledProcessError as e: + raise subprocess.CalledProcessError(e.returncode, e.cmd, e.stdout, e.stderr) + except FileNotFoundError: + raise FileNotFoundError( + "github-linguist command not found. " + "Please install it with: gem install github-linguist" + ) + + +def linguist_stats(path: str) -> Dict[str, Any]: + """ + Execute github-linguist --json on the given directory path and return results as JSON. + + The returned JSON contains language statistics with the following structure: + { + "LanguageName": { + "size": number, + "percentage": "XX.XX" + }, + ... + } + + For example: + { + "Rust": { + "size": 3900397, + "percentage": "64.04" + }, + "Python": { + "size": 67459, + "percentage": "1.11" + }, + ... + } + + Args: + path (str): The directory path to analyze + + Returns: + Dict[str, Any]: Language statistics in JSON format with language names as keys + and size/percentage information as values + + Raises: + subprocess.CalledProcessError: If github-linguist command fails + FileNotFoundError: If github-linguist is not installed + ValueError: If path doesn't exist or is not a directory + """ + return _linguist_call(path, is_file=False) + + +def linguist_file_info(path: str) -> Dict[str, Any]: + """ + Execute github-linguist --json on the given file path and return results as JSON. + + The returned JSON contains file information with the following structure: + { + "filename": { + "lines": number, + "sloc": number, + "type": "Text", + "mime_type": "application/x-sh", + "language": "Shell", + "large": false, + "generated": false, + "vendored": false + } + } + + For example: + { + "docker-build.sh": { + "lines": 135, + "sloc": 118, + "type": "Text", + "mime_type": "application/x-sh", + "language": "Shell", + "large": false, + "generated": false, + "vendored": false + } + } + + Args: + path (str): The file path to analyze + + Returns: + Dict[str, Any]: File information in JSON format with filename as key + and detailed file metadata as values + + Raises: + subprocess.CalledProcessError: If github-linguist command fails + FileNotFoundError: If github-linguist is not installed + ValueError: If path doesn't exist or is not a file + """ + return _linguist_call(path, is_file=True) + + +def linguist_stats_safe(path: str) -> Optional[Dict[str, Any]]: + """ + Safely execute linguist_stats with error handling. + + Args: + path (str): The directory path to analyze + + Returns: + Optional[Dict[str, Any]]: Language statistics or None if failed + """ + try: + return linguist_stats(path) + except Exception as e: + print(f"Error running linguist_stats: {e}", file=sys.stderr) + return None + + +def linguist_file_info_safe(path: str) -> Optional[Dict[str, Any]]: + """ + Safely execute linguist_file_info with error handling. + + Args: + path (str): The file path to analyze + + Returns: + Optional[Dict[str, Any]]: File information or None if failed + """ + try: + return linguist_file_info(path) + except Exception as e: + print(f"Error running linguist_file_info: {e}", file=sys.stderr) + return None + + +# Example usage +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python command.py ") + sys.exit(1) + + path = sys.argv[1] + try: + # Try to determine if it's a file or directory + if os.path.isfile(path): + info = linguist_file_info(path) + print(json.dumps(info, indent=2)) + elif os.path.isdir(path): + stats = linguist_stats(path) + print(json.dumps(stats, indent=2)) + else: + print(f"Error: Path is neither a file nor a directory: {path}") + sys.exit(1) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + + path = sys.argv[1] + try: + # Try to determine if it's a file or directory + if os.path.isfile(path): + info = linguist_file_info(path) + print(json.dumps(info, indent=2)) + elif os.path.isdir(path): + stats = linguist_stats(path) + print(json.dumps(stats, indent=2)) + else: + print(f"Error: Path is neither a file nor a directory: {path}") + sys.exit(1) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + + path = sys.argv[1] + try: + stats = linguist_stats(path) + print(json.dumps(stats, indent=2)) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + + path = sys.argv[1] + try: + stats = linguist_stats(path) + print(json.dumps(stats, indent=2)) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + + path = sys.argv[1] + try: + stats = linguist_stats(path) + print(json.dumps(stats, indent=2)) + except Exception as e: + print(f"Error: {e}") + sys.exit(1) diff --git a/src/ollama/__init__.py b/old/src/ollama/__init__.py similarity index 100% rename from src/ollama/__init__.py rename to old/src/ollama/__init__.py diff --git a/src/sandbox/__init__.py b/old/src/sandbox/__init__.py similarity index 100% rename from src/sandbox/__init__.py rename to old/src/sandbox/__init__.py diff --git a/old/stamp/build_PMOCrazyCoder b/old/stamp/build_PMOCrazyCoder new file mode 100644 index 0000000..e69de29 diff --git a/test.py b/old/test.py similarity index 100% rename from test.py rename to old/test.py diff --git a/tools/install_latest_go.sh b/old/tools/install_latest_go.sh similarity index 100% rename from tools/install_latest_go.sh rename to old/tools/install_latest_go.sh