diff --git a/Caddyfile.localhost b/Caddyfile.localhost deleted file mode 100644 index 80728eedf..000000000 --- a/Caddyfile.localhost +++ /dev/null @@ -1,64 +0,0 @@ -# Run with -# caddy run --envfile ./example.env --config ./Caddyfile.localhost -# -# This is configured for -# - Automatic HTTPS (even for localhost) -# - Reverse Proxying to Ollama API Base URL (http://localhost:11434/api) -# - CORS -# - HTTP Basic Auth API Tokens (uncomment basicauth section) - - -# CORS Preflight (OPTIONS) + Request (GET, POST, PATCH, PUT, DELETE) -(cors-api) { - @match-cors-api-preflight method OPTIONS - handle @match-cors-api-preflight { - header { - Access-Control-Allow-Origin "{http.request.header.origin}" - Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" - Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With" - Access-Control-Allow-Credentials "true" - Access-Control-Max-Age "3600" - defer - } - respond "" 204 - } - - @match-cors-api-request { - not { - header Origin "{http.request.scheme}://{http.request.host}" - } - header Origin "{http.request.header.origin}" - } - handle @match-cors-api-request { - header { - Access-Control-Allow-Origin "{http.request.header.origin}" - Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" - Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With" - Access-Control-Allow-Credentials "true" - Access-Control-Max-Age "3600" - defer - } - } -} - -# replace localhost with example.com or whatever -localhost { - ## HTTP Basic Auth - ## (uncomment to enable) - # basicauth { - # # see .example.env for how to generate tokens - # {env.OLLAMA_API_ID} {env.OLLAMA_API_TOKEN_DIGEST} - # } - - handle /api/* { - # Comment to disable CORS - import cors-api - - reverse_proxy localhost:11434 - } - - # Same-Origin Static Web Server - file_server { - root ./build/ - } -} diff --git a/contribution_stats.py b/contribution_stats.py new file mode 100644 index 000000000..efd3f5567 --- /dev/null +++ b/contribution_stats.py @@ -0,0 +1,74 @@ +import os +import subprocess +from collections import Counter + +CONFIG_FILE_EXTENSIONS = (".json", ".yml", ".yaml", ".ini", ".conf", ".toml") + + +def is_text_file(filepath): + # Check for binary file by scanning for null bytes. + try: + with open(filepath, "rb") as f: + chunk = f.read(4096) + if b"\0" in chunk: + return False + return True + except Exception: + return False + + +def should_skip_file(path): + base = os.path.basename(path) + # Skip dotfiles and dotdirs + if base.startswith("."): + return True + # Skip config files by extension + if base.lower().endswith(CONFIG_FILE_EXTENSIONS): + return True + return False + + +def get_tracked_files(): + try: + output = subprocess.check_output(["git", "ls-files"], text=True) + files = output.strip().split("\n") + files = [f for f in files if f and os.path.isfile(f)] + return files + except subprocess.CalledProcessError: + print("Error: Are you in a git repository?") + return [] + + +def main(): + files = get_tracked_files() + email_counter = Counter() + total_lines = 0 + + for file in files: + if should_skip_file(file): + continue + if not is_text_file(file): + continue + try: + blame = subprocess.check_output( + ["git", "blame", "-e", file], text=True, errors="replace" + ) + for line in blame.splitlines(): + # The email always inside <> + if "<" in line and ">" in line: + try: + email = line.split("<")[1].split(">")[0].strip() + except Exception: + continue + email_counter[email] += 1 + total_lines += 1 + except subprocess.CalledProcessError: + continue + + for email, lines in email_counter.most_common(): + percent = (lines / total_lines * 100) if total_lines else 0 + print(f"{email}: {lines} {percent:.2f}%") + + +if __name__ == "__main__": + main()