]> Nutra Git (v1) - gamesguru/git-remote-gcrypt.git/commitdiff
huge wip
authorShane Jaroch <chown_tee@proton.me>
Thu, 8 Jan 2026 21:26:53 +0000 (16:26 -0500)
committerShane Jaroch <chown_tee@proton.me>
Thu, 8 Jan 2026 21:26:53 +0000 (16:26 -0500)
Makefile
README.rst
completions/bash/git-remote-gcrypt
completions/fish/git-remote-gcrypt.fish
completions/zsh/_git-remote-gcrypt
git-remote-gcrypt
tests/sync_docs.py [new file with mode: 0755]

index c1c823fc1e93bb8642ca3728e31a8f19717d749e..097863f2b3b37da30b567e6a980badf5fa138754 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -196,4 +196,14 @@ uninstall/user:    ##H make uninstall prefix=~/.local
 
 .PHONY: clean
 clean: ##H Clean up
-       rm -rf .coverage
+       rm -rf .coverage .build_tmp
+
+# --- Autogeneration ---
+.PHONY: generate
+generate:      ##H Autogenerate README usage & completions
+       @$(call print_info,Generating documentation and completions...)
+       @python3 tests/sync_docs.py
+       @# Update README.rst Usage section (simple version for now)
+       @sed -i '/Detecting gcrypt repos/,/Exit status is 0/c\Detecting gcrypt repos\n======================\n\nTo detect if a git url is a gcrypt repo, use::\n\n    git-remote-gcrypt check url\n\n(Legacy syntax ``--check`` is also supported).\n\nExit status is 0' README.rst
+       @sed -i '/Cleaning gcrypt repos/,/Known issues/c\Cleaning gcrypt repos\n=====================\n\nTo scan for unencrypted files in a remote gcrypt repo, use::\n\n    git-remote-gcrypt clean [url|remote]\n\nIf no URL or remote is specified, ``git-remote-gcrypt`` will list all\navailable ``gcrypt::`` remotes.\n\nBy default, this command only performs a scan. To actually remove the\nunencrypted files, you must use the ``--force`` (or ``-f``) flag::\n\n    git-remote-gcrypt clean url --force\n\nKnown issues' README.rst
+       @$(call print_success,Generated.)
index dc3900ac7967d09ebfe47e9debe34dd96afd291e..1753636099918680aeb9d71ce1a9328c87416d24 100644 (file)
@@ -262,7 +262,7 @@ To detect if a git url is a gcrypt repo, use::
 
 (Legacy syntax ``--check`` is also supported).
 
-Exit status is 0 if the repo exists and can be decrypted, 1 if the repo
+Exit status is 0
 uses gcrypt but could not be decrypted, and 100 if the repo is not
 encrypted with gcrypt (or could not be accessed).
 
index b4013110004c17944561c87eb5454c982a2c129d..93bcd1563789fdd6cf91afaaeffe09060c4f038c 100644 (file)
@@ -7,7 +7,7 @@ _git_remote_gcrypt() {
        cur="${COMP_WORDS[COMP_CWORD]}"
        prev="${COMP_WORDS[COMP_CWORD - 1]}"
        opts="-h --help -v --version --check"
-       commands="capabilities list push fetch check clean"
+       commands="capabilities check clean fetch list push"
 
        # If we're after a subcommand, only offer -h/--help
        if [[ " $commands " =~ " ${COMP_WORDS[1]:-} " ]]; then
index 2560ccb35cf81446158150c87a842ee9d0dc8c45..613c89c3af96eeb28f140e49522a53f2ddd6839c 100644 (file)
@@ -6,14 +6,14 @@ complete -c git-remote-gcrypt -s v -l version -d 'Show version information'
 complete -c git-remote-gcrypt -l check -d '(Legacy) Check if URL is a gcrypt repository' -r -F
 
 # Subcommands
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities list push fetch check clean" -a 'check' -d 'Check if URL is a gcrypt repository'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities list push fetch check clean" -a 'clean' -d 'Scan/Clean unencrypted files from remote'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'check' -d 'Check if URL is a gcrypt repository'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'clean' -d 'Scan/Clean unencrypted files from remote'
 
 # Clean flags
-complete -c git-remote-gcrypt -f -n "__fish_seen_subcommand_from clean" -s f -l force -d 'Actually delete files during clean'
+complete -c git-remote-gcrypt -f -n "__fish_seen_subcommand_from capabilities check clean fetch list push" -s f -l force -d 'Actually delete files during clean'
 
 # Git protocol commands
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities list push fetch check clean" -a 'capabilities' -d 'Show git remote helper capabilities'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities list push fetch check clean" -a 'list' -d 'List refs in remote repository'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities list push fetch check clean" -a 'push' -d 'Push refs to remote repository'
-complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities list push fetch check clean" -a 'fetch' -d 'Fetch refs from remote repository'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'capabilities' -d 'Show git remote helper capabilities'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'list' -d 'List refs in remote repository'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'push' -d 'Push refs to remote repository'
+complete -c git-remote-gcrypt -f -n "not __fish_seen_subcommand_from capabilities check clean fetch list push" -a 'fetch' -d 'Fetch refs from remote repository'
index 86c5d83ee4d483ce350d98f33e0cfa6d9061f1c3..d8001b1adbd0330363a35fff809474420c42a28e 100644 (file)
@@ -8,7 +8,7 @@ _git_remote_gcrypt() {
                '(- *)'{-h,--help}'[show help message]'
                '(- *)'{-v,--version}'[show version information]'
                '--check[check if URL is a gcrypt repository]:URL:_files'
-               '1:command:(capabilities list push fetch check clean)'
+               '1:command:(capabilities check clean fetch list push)'
                '*::subcommand arguments:->args'
        )
        _arguments -s -S $args
index 0fd697b1b82c44303207ca21f1c6001d89a2f743..7e0041d7b957666b4d47e53234e83035e6a7ea4d 100755 (executable)
@@ -34,9 +34,7 @@ VERSION="@@DEV_VERSION@@"
 
 
 # Help function
-show_help() {
-       cat >&2 <<EOF
-git-remote-gcrypt version $VERSION
+HELP_TEXT="git-remote-gcrypt version $VERSION
 GPG-encrypted git remote helper
 
 Usage: Automatically invoked by git when using gcrypt:: URLs
@@ -59,89 +57,70 @@ Git Protocol Commands (for debugging):
 Environment Variables:
   GCRYPT_DEBUG=1        Enable verbose debug logging to stderr
   GCRYPT_TRACE=1        Enable shell tracing (set -x) for rsync/curl commands
-  GCRYPT_FULL_REPACK=1  Force full repack when pushing
-EOF
-}
+  GCRYPT_FULL_REPACK=1  Force full repack when pushing"
 
-# Handle subcommands early
-case "$1" in
-       check|--check)
-               NAME=gcrypt-check
-               URL=$2
-               ;;
-       clean)
-               NAME=gcrypt-clean
-               shift
-               FORCE_CLEAN=
-               URL=
-               while [ $# -gt 0 ]; do
-                       case "$1" in
-                               --force|-f) FORCE_CLEAN=yes ;;
-                               -*) echo "Unknown option: $1" >&2; exit 1 ;;
-                               *)
-                                       if [ -z "$URL" ]; then
-                                               URL="$1"
-                                       else
-                                               echo "Error: Multiple URLs/remotes provided to clean" >&2
-                                               exit 1
-                                       fi
-                                       ;;
-                       esac
-                       shift
-               done
-               ;;
-       help|--help|-h)
-               show_help
-               exit 0
-               ;;
-       version|--version|-v)
-               echo "git-remote-gcrypt version $VERSION" >&2
-               exit 0
-               ;;
-esac
+# Help function
+show_help() {
+       echo "$HELP_TEXT" >&2
+}
 
-# Parse flags
-while getopts "hv-:" opt; do
-       case "$opt" in
-               h)
+# Parse arguments
+while [ $# -gt 0 ]; do
+       case "$1" in
+               help|--help|-h)
                        show_help
                        exit 0
                        ;;
-               v)
+               version|--version|-v)
                        echo "git-remote-gcrypt version $VERSION" >&2
                        exit 0
                        ;;
-               -)
-                       # Handle long options
-                       case "$OPTARG" in
-                               help)
-                                       show_help
-                                       exit 0
-                                       ;;
-                               version)
-                                       echo "git-remote-gcrypt version $VERSION" >&2
-                                       exit 0
-                                       ;;
-                               check)
-                                       # Allow check to pass through to the main logic at the bottom
-                                       ;;
-                               clean)
-                                       # Allow clean to pass through to the main logic at the bottom
-                                       ;;
-                               *)
-                                       echo "Unknown option: --$OPTARG" >&2
-                                       exit 1
-                                       ;;
-                       esac
+               check|--check)
+                       NAME=gcrypt-check
+                       URL="$2"
+                       shift
                        ;;
-               *)
+               clean)
+                       NAME=gcrypt-clean
+                       shift
+                       while [ $# -gt 0 ]; do
+                               case "$1" in
+                                       --force|-f) FORCE_CLEAN=yes ;;
+                                       -*) echo "Unknown option: $1" >&2; exit 1 ;;
+                                       *)
+                                               if [ -z "$URL" ]; then
+                                                       URL="$1"
+                                               else
+                                                       echo "Error: Multiple URLs/remotes provided to clean" >&2
+                                                       exit 1
+                                               fi
+                                               ;;
+                               esac
+                               shift
+                       done
+                       break # Stop parsing outer loop
+                       ;;
+               -*)
+                       echo "Unknown option: $1" >&2
                        exit 1
                        ;;
+               *)
+                       # Assume it's the remote name if not already set
+                       if [ -z "$NAME" ]; then
+                               NAME="$1"
+                       elif [ -z "$URL" ]; then
+                               URL="$1"
+                       fi
+                       ;;
        esac
+       shift || :
 done
 
-# Handle subcommand help (e.g., git-remote-gcrypt capabilities --help)
-shift $((OPTIND - 1))
+# If NAME is not set, we might be invoked as a remote helper
+if [ -z "$NAME" ]; then
+       show_help
+       exit 1
+fi
 case "${1:-}" in
        capabilities)
                if [ "${2:-}" = "-h" ] || [ "${2:-}" = "--help" ]; then
@@ -251,7 +230,10 @@ xgrep() { command grep "$@" || : ; }
 # Resolve URL or remote name, or list remotes if empty
 resolve_url() {
        local cmd="$1"
-       if [ -z "$URL" ]; then
+       if [ -z "$URL" ] || [ "$URL" = "$cmd" ]; then
+               # Handle cases where shift might have messed up or URL was empty
+               if [ "$URL" = "$cmd" ]; then URL=""; fi
+               
                local remotes
                remotes=$(git remote -v | grep 'gcrypt::' | awk '{print $1}' | sort -u || :)
                echo "Usage: git-remote-gcrypt $cmd [URL|REMOTE]" >&2
@@ -263,10 +245,13 @@ resolve_url() {
                fi
                exit 1
        fi
-       if ! echo "$URL" | grep -q -E '://|::'; then
+       
+       # If it's not a URL, try to resolve as a remote name
+       if ! echo "$URL" | grep -q -E '://|::' || [ -n "${URL##*/*}" ]; then
                local potential_url
                potential_url=$(git config --get "remote.$URL.url" || :)
                if [ -n "$potential_url" ]; then
+                       print_debug "Resolved remote '$URL' to '$potential_url'"
                        URL="$potential_url"
                fi
        fi
@@ -761,33 +746,24 @@ ensure_connected()
        
        # EARLY SAFETY CHECK for gitception backends:
        # Before GPG validation, check if the remote has unencrypted files.
-       # This prevents the GPG error from masking the privacy leak warning.
-       # Skip this check if we are explicitly running the clean command.
        if [ "$NAME" != "gcrypt-clean" ] && ! isurl sftp "$URL" && ! isurl rsync "$URL" && ! isurl rclone "$URL" && ! islocalrepo "$URL"; then
-               # It's a gitception backend - do early safety check
-               # Fetch the default branch to see what files exist
                local check_files=""
                git fetch --quiet "$URL" "refs/heads/master:refs/gcrypt/safety-check" 2>/dev/null ||
                git fetch --quiet "$URL" "refs/heads/main:refs/gcrypt/safety-check" 2>/dev/null || true
 
                if git rev-parse --verify "refs/gcrypt/safety-check" >/dev/null 2>&1; then
                        check_files=$(git ls-tree --name-only "refs/gcrypt/safety-check" 2>/dev/null || :)
-                       # Clean up the temp ref
                        git update-ref -d "refs/gcrypt/safety-check" 2>/dev/null || true
 
                        if isnonnull "$check_files"; then
-                               # Check if ANY file doesn't match gcrypt pattern (hash filenames)
                                early_bad_files=$(echo "$check_files" | grep -v -E '^[a-f0-9]{56}$|^[a-f0-9]{64}$|^[a-f0-9]{96}$|^[a-f0-9]{128}$' || :)
                                if isnonnull "$early_bad_files"; then
-                                       # Check config to see if we should ignore
                                        if [ "$(git config --bool gcrypt.allow-unencrypted-remote)" != "true" ]; then
                                                echo_info "ERROR: Remote repository contains unencrypted or unknown files!"
                                                echo_info "To protect your privacy, git-remote-gcrypt will NOT push to this remote."
-                                               echo_info "Found the following unexpected files:"
-                                               echo_info "$early_bad_files" | head -n 5 | sed 's/^/  /' >&2
-                                               echo_info ""
-                                               echo_info "To fix: use 'git-remote-gcrypt clean --force $URL' to remove these files,"
-                                               echo_info "or set 'git config gcrypt.allow-unencrypted-remote true' to ignore."
+                                               echo_info "Found unexpected files: $(echo "$early_bad_files" | head -n 3 | tr '\n' ' ')"
+                                               echo_info "To see full list of unexpected files, use: git-remote-gcrypt clean --force $URL"
+                                               echo_info "To fix and remove these files, use: git-remote-gcrypt clean --force $URL"
                                                exit 1
                                        fi
                                fi
@@ -1441,5 +1417,5 @@ elif [ "$1" = --version ] || [ "$1" = -v ]; then
        echo "git-remote-gcrypt version $VERSION"
        exit 0
 else
-       gcrypt_main_loop "$@"
+       gcrypt_main_loop "$NAME" "$URL"
 fi
diff --git a/tests/sync_docs.py b/tests/sync_docs.py
new file mode 100755 (executable)
index 0000000..c3a6969
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+import re
+import os
+import sys
+
+def extract_help_text(script_path):
+    with open(script_path, 'r') as f:
+        content = f.read()
+    
+    match = re.search(r'HELP_TEXT="(.*?)"', content, re.DOTALL)
+    if not match:
+        print("Error: Could not find HELP_TEXT in git-remote-gcrypt", file=sys.stderr)
+        sys.exit(1)
+    return match.group(1)
+
+def parse_commands(help_text):
+    commands = []
+    # Look for lines starting with lowercase words in the Options: or Git Protocol Commands sections
+    lines = help_text.split('\n')
+    capture = False
+    for line in lines:
+        line = line.strip()
+        if line.startswith('Options:') or line.startswith('Git Protocol Commands'):
+            capture = True
+            continue
+        if line.startswith('Environment Variables:'):
+            capture = False
+            continue
+        
+        if capture and line:
+            # Match lines like "check [URL]      Description" or "capabilities     Description"
+            match = re.match(r'^([a-z-]+)(\s+.*)?$', line)
+            if match:
+                cmd = match.group(1)
+                if cmd not in ['help', 'version']:
+                    commands.append(cmd)
+    return sorted(list(set(commands)))
+def update_bash_completion(path, commands):
+    if not os.path.exists(path): return
+    with open(path, 'r') as f: content = f.read()
+    cmd_str = ' '.join(commands)
+    new_content = re.sub(r'commands="[^"]+"', f'commands="{cmd_str}"', content)
+    with open(path, 'w') as f: f.write(new_content)
+
+def update_zsh_completion(path, commands):
+    if not os.path.exists(path): return
+    with open(path, 'r') as f: content = f.read()
+    cmd_str = ' '.join(commands)
+    # Match 1:command:(capabilities list push fetch check clean)
+    new_content = re.sub(r'1:command:\([^)]+\)', f'1:command:({cmd_str})', content)
+    with open(path, 'w') as f: f.write(new_content)
+
+def update_fish_completion(path, commands):
+    if not os.path.exists(path): return
+    with open(path, 'r') as f: content = f.read()
+    # Replace the list in "not __fish_seen_subcommand_from ..."
+    cmd_str = ' '.join(commands)
+    new_content = re.sub(r'not __fish_seen_subcommand_from [^"]+', f'not __fish_seen_subcommand_from {cmd_str}', content)
+    with open(path, 'w') as f: f.write(new_content)
+
+def main():
+    script_dir = os.path.dirname(os.path.abspath(__file__))
+    root_dir = os.path.dirname(script_dir)
+    script_path = os.path.join(root_dir, 'git-remote-gcrypt')
+    
+    help_text = extract_help_text(script_path)
+    commands = parse_commands(help_text)
+    
+    # We always want protocol commands in completions too
+    comp_commands = sorted(list(set(commands + ['capabilities', 'list', 'push', 'fetch'])))
+    
+    print(f"Detected commands: {' '.join(comp_commands)}")
+    
+    update_bash_completion(os.path.join(root_dir, 'completions/bash/git-remote-gcrypt'), comp_commands)
+    update_zsh_completion(os.path.join(root_dir, 'completions/zsh/_git-remote-gcrypt'), comp_commands)
+    update_fish_completion(os.path.join(root_dir, 'completions/fish/git-remote-gcrypt.fish'), comp_commands)
+    
+    print("Completions updated.")
+
+if __name__ == "__main__":
+    main()