update/fix stuff. tidy.
authorShane Jaroch <chown_tee@proton.me>
Wed, 14 Jan 2026 16:06:10 +0000 (11:06 -0500)
committerShane Jaroch <chown_tee@proton.me>
Wed, 14 Jan 2026 16:06:10 +0000 (11:06 -0500)
20 files changed:
.editorconfig
.github/workflows/coverage.yaml
.github/workflows/lint.yaml
Makefile
README.rst
completions/templates/bash.in
completions/templates/zsh.in
completions/zsh/_git-remote-gcrypt
git-remote-gcrypt
install.sh
tests/coverage_report.py
tests/system-test-multikey.sh
tests/system-test-repack.sh
tests/system-test.sh
tests/test-clean-command.sh
tests/test-gc.sh
tests/test-install-logic.sh
tests/test-privacy-leaks.sh
uninstall.sh
utils/gen_docs.sh

index 13b95c34d0cb94c81b6c33504d7cbf2bb9ca448d..606a808b25e003d31dcc46dc2979588e8c0e9a06 100644 (file)
@@ -1,10 +1,10 @@
 [[bash]]
 # For extension-less files (i.e., git-remote-gcrypt)
 indent_style=tab
-indent=4
+indent_size=4
 
 [*.sh]
 # For bash scripts with .sh extension
 indent_style=tab
-indent=4
+indent_size=4
 
index cb2d5ab624ffcc14a6fe561f91b15c0ff77de543..4c25036dc566e899af9cf21d2bc856d53db337a0 100644 (file)
@@ -23,7 +23,6 @@ jobs:
 
       # python3-docutils enables rst2man
       - name: Install kcov Dependencies
-        if: steps.cache-kcov.outputs.cache-hit != 'true'
         run: |
           sudo apt-get update
           sudo apt-get install -y binutils-dev build-essential cmake git \
@@ -35,7 +34,7 @@ jobs:
       - name: Build and Install kcov
         if: steps.cache-kcov.outputs.cache-hit != 'true'
         run: |
-          git clone https://github.com/SimonKagstrom/kcov.git
+          git clone --branch v42 --depth 1 https://github.com/SimonKagstrom/kcov.git
           cd kcov
           mkdir build && cd build
           cmake ..
index c4866c75c936800ca754a25c1f909ec9918415ef..1cf0c1e4c2eb424ee66f494d8d2c104432b75ea0 100644 (file)
@@ -13,6 +13,9 @@ name: lint
   schedule:
     - cron: "0 0 * * 0" # Sunday at 12 AM
 
+permissions:
+  contents: read
+
 jobs:
   # Handles Ubuntu and macOS
   install-unix:
index 8ddfcc76218c740caf0bc4325797766a32b31fe2..1bb405eb5093f97b3c014d5d67cda62c4bdf0d4b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -42,10 +42,15 @@ define print_success
 printf "\033[1;34m✓ %s\033[0m\n" "$(1)"
 endef
 
+
 define print_info
 printf "\033[1;36m%s\033[0m\n" "$(1)"
 endef
 
+define print_target
+printf "\033[1;35m-> %s\033[0m\n" "$(1)"
+endef
+
 
 .PHONY: check/deps
 check/deps:    ##H Verify kcov & shellcheck
@@ -96,7 +101,7 @@ test/:       ##H Run tests (purity checks only if kcov missing)
        @if command -v kcov >/dev/null 2>&1; then \
                $(MAKE) test/installer test/system test/cov; \
        else \
-               $(call print_warn,kcov not found: skipping coverage/bash tests.); \
+               printf "\033[1;33mkcov not found: skipping coverage/bash tests.\033[0m\n"; \
                $(MAKE) test/purity; \
        fi
 
@@ -128,7 +133,7 @@ test/system:        ##H Run logic tests (with bash & coverage)
        @rm -rf $(COV_SYSTEM)
        @mkdir -p $(COV_SYSTEM)
        @export GPG_TTY=$$(tty); \
-        [ -n "$(DEBUG)$(V)" ] && export GCRYPT_DEBUG=1 && print_warn "Debug mode enabled"; \
+        [ -n "$(DEBUG)$(V)" ] && export GCRYPT_DEBUG=1 && printf "\033[1;33mDebug mode enabled\033[0m\n"; \
         export GIT_CONFIG_PARAMETERS="'gcrypt.gpg-args=--pinentry-mode loopback --no-tty'"; \
         sed -i 's|^#!/bin/sh|#!/bin/bash|' git-remote-gcrypt; \
         trap "sed -i 's|^#!/bin/bash|#!/bin/sh|' git-remote-gcrypt" EXIT; \
index 2b59a800896d0d61d621148bf0d372919dd7a31f..00d555718d584f766d3a6c876db673bb66adbb5e 100644 (file)
@@ -288,7 +288,7 @@ To detect if a git url is a gcrypt repo, use::
 
 (Legacy syntax ``--check`` is also supported).
 
-Exit status is 0
+Exit status is 0 if the remote uses gcrypt and was decrypted successfully, 1 if it
 uses gcrypt but could not be decrypted, and 100 if the repo is not
 encrypted with gcrypt (or could not be accessed).
 
index 32956add259c1da1905b6c951dd9b9ded4ceb4a6..d13292782f66022c31a756f204189d0801d230b5 100644 (file)
@@ -21,12 +21,12 @@ _git_remote_gcrypt() {
        # 2. Handle subcommands
        case "${COMP_WORDS[1]}" in
                clean)
-                       local remotes=$(git remote -v 2>/dev/null | grep 'gcrypt::' | awk '{print $1}' | sort -u || :)
+                       local remotes=$(git remote -v 2>/dev/null | grep 'gcrypt::' | awk '{print $1}' | sort -u || :
                        COMPREPLY=($(compgen -W "{clean_flags_bash} $remotes" -- "$cur"))
                        return 0
                        ;;
                check)
-                       local remotes=$(git remote 2>/dev/null || :)
+                       local remotes=$(git remote 2>/dev/null || :
                        COMPREPLY=($(compgen -W "$remotes" -- "$cur"))
                        return 0
                        ;;
index 7d2794de833b914aaaebb789c468275b2eadcc2f..b357d1f4bbf5cf92ef24845e221880e1c207b7ec 100644 (file)
@@ -12,7 +12,7 @@ _git_remote_gcrypt() {
        )
        _arguments -s -S $args
 
-       case $words[1] in
+       case $line[1] in
        clean)
                _arguments \
                        {clean_flags_zsh} \
index 46b09af281c1eb002bcbfe3fafbe9aa5290c0a2f..87e0987c9095d240c75d2d15e9fcdfefd2dcb824 100644 (file)
@@ -12,10 +12,10 @@ _git_remote_gcrypt() {
        )
        _arguments -s -S $args
 
-       case $words[1] in
+       case $line[1] in
        clean)
                _arguments \
-                       '()' {} '[flag]' \
+                       {clean_flags_zsh} \
                        '*:gcrypt URL: _alternative "remotes:gcrypt remote:($(git remote -v 2>/dev/null | grep "gcrypt::" | awk "{print \$1}" | sort -u))" "files:file:_files"'
                ;;
        check)
index 951d7c250ccb4b3ccab6e3da73aa56475073e4a1..05b66438e726d2d4b8010d8d41936807a93a4715 100755 (executable)
@@ -1398,7 +1398,7 @@ get_remote_file_list()
        if isurl rsync "$URL"; then
                r_files=$(rsync --no-motd --list-only "$(rsynclocation "$URL")/" | awk '{print $NF}' | grep -vE '^\.$|^\.\.$') || return 1
        elif isurl rclone "$URL"; then
-               r_files=$(rclone lsf "$(rclonelocation "$URL")") || return 1
+               r_files=$(rclone lsf "${URL#rclone://}") || return 1
        elif isurl sftp "$URL"; then
                r_files=$(curl -s -S -k "$URL/" | grep -vE '^\.$|^\.\.$') || return 1
        elif islocalrepo "$URL"; then
@@ -1409,15 +1409,17 @@ get_remote_file_list()
                        r_files=""
                fi
        else
-               # Git backend: Check safety-check ref first (most reliable if early_safety_check ran)
-               # Or try to fetch master?
-               # If early_safety_check ran, it fetched to refs/gcrypt/safety-check.
-               if git rev-parse --verify "refs/gcrypt/safety-check" >/dev/null 2>&1; then
-                       r_files=$(git ls-tree -r --name-only "refs/gcrypt/safety-check") || return 1
+               # Git backend:
+               # We need to fetch the remote state to list its files.
+               # Try fetching master and main to a temporary ref.
+               if git fetch --quiet "$URL" "refs/heads/master:refs/gcrypt/list-files" 2>/dev/null || \
+                  git fetch --quiet "$URL" "refs/heads/main:refs/gcrypt/list-files" 2>/dev/null; then
+                       r_files=$(git ls-tree -r --name-only "refs/gcrypt/list-files") || return 1
+                       git update-ref -d "refs/gcrypt/list-files"
                else
-                       # Try fetching default branch?
-                       # If we can't verify emptiness, we should return error to prevent implicit init.
-                       # Using $Gref (refs/gcrypt/gitception...) might be empty if we haven't pushed yet.
+                       # Could not fetch, or remote is empty.
+                       # If checking, this might be fine, but for clean it's an issue if we expected files.
+                       # Returning 1 is safer.
                        return 1
                fi
        fi
index f83d3534350003333d7b26400e9925b2ce61c124..ef42ede89bd54c2b15defcccdb9f66c9007b76e0 100755 (executable)
@@ -34,7 +34,7 @@ else
        fi
        VERSION=$(grep ^git-remote-gcrypt debian/changelog | head -n 1 | awk '{print $2}' | tr -d '()')
 fi
-VERSION="$VERSION (deb running on $OS_IDENTIFIER)"
+VERSION="$VERSION ($OS_IDENTIFIER)"
 
 echo "Detected version: $VERSION"
 
index b7ba1274b5b66c67e7c44326d2fc276443741997..77a5e115b6c946b790811d7e1e8d11f8e2c8467d 100644 (file)
@@ -13,6 +13,14 @@ import xml.etree.ElementTree as E
 xml_file = os.environ.get("XML_FILE")
 patt = os.environ.get("PATT")
 
+if not xml_file:
+    print("Error: XML_FILE environment variable is not set.")
+    sys.exit(1)
+
+if not patt:
+    print("Error: PATT environment variable is not set.")
+    sys.exit(1)
+
 tree = E.parse(xml_file)
 missed = []
 total_lines = 0
index 2024a1378730c1c206369612b4f8ac04c3d34e96..324be51bb29ba829a9e1a8db303929630bb79afb 100755 (executable)
@@ -193,7 +193,7 @@ print_info "Step 5: Unhappy Path - Test clone with NO matching keys..."
        (
                set +e
                if git clone -b "${default_branch}" "gcrypt::${tempdir}/second.git#${default_branch}" -- "${tempdir}/fail_test"; then
-                       print_info "ERROR: Clone succeeded unexpectedly with empty keyring!"
+                       print_err "ERROR: Clone succeeded unexpectedly with empty keyring!"
                        exit 1
                fi
        ) 2>&1 | indent
index 02e819c96ee413fe4d67552c92f03debe342d005..ef876ac5b33408db66722fb801e0b7fa97559125 100755 (executable)
@@ -118,7 +118,7 @@ print_info "Step 2: Creating repository with large random files..."
                        random_data_index=$((file_index * random_data_per_file))
                        echo "Writing large file $((file_index + 1))/${total_files} ($((random_data_per_file / 1024 / 1024)) MiB)"
                        head -c "${random_data_per_file}" >"$((file_index)).data" < \
-                               <(tail -c "+${random_data_index}" "${random_data_file}" || :)
+                               <(tail -c "+$((random_data_index + 1))" "${random_data_file}" || :)
                done
                git add -- "${tempdir}/first"
                git commit -m "Commit #${i}"
index ba476f9c45c4ccf3b315e8eeed9ff7560dff9db6..f51a791b4b158e769f23e8d0c9110622d06921de 100755 (executable)
@@ -76,7 +76,8 @@ trap "rm -Rf -- '${tempdir}'" EXIT
 repo_root=$(git rev-parse --show-toplevel)
 test_version=$(git describe --tags --always --dirty 2>/dev/null || echo "test")
 cp "$repo_root/git-remote-gcrypt" "$tempdir/git-remote-gcrypt"
-sed -i "s/@@DEV_VERSION@@/$test_version/" "$tempdir/git-remote-gcrypt"
+sed "s/@@DEV_VERSION@@/$test_version/" "$tempdir/git-remote-gcrypt" > "$tempdir/git-remote-gcrypt.tmp"
+mv "$tempdir/git-remote-gcrypt.tmp" "$tempdir/git-remote-gcrypt"
 chmod +x "$tempdir/git-remote-gcrypt"
 PATH=$tempdir:${PATH}
 readonly PATH
@@ -127,7 +128,6 @@ random_data_size=$(( total_files * random_data_per_file ))
 random_data_file="${tempdir}/data"
 head -c "${random_data_size}" "${random_source}" > "${random_data_file}"
 
-# Create gpg key and subkey.
 # Create gpg key and subkey.
 print_info "Step 1: Creating a new GPG key and subkey to use for testing:"
 (
@@ -386,7 +386,7 @@ print_info "Step 9: Network Failure Guard Test (manifest unavailable):"
     
     # DEBUG: Dump directory listing to stdout
     print_info "DEBUG: Listing ${tempdir}/second.git contents:"
-    find "${tempdir}/second.git" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort | indent
+    find "${tempdir}/second.git" -mindepth 1 -maxdepth 1 -exec basename {} \; | sort | indent
 
     # Use find to robustly locate manifest files (56-64 hex chars)
     # matching basename explicitly via grep. Using sed for portable basename extraction.
index 11dc50b33eefe3d9af18ae85c68df256bfa98198..84de92b336b3a1b072506a795f06d7e287c20f84 100755 (executable)
@@ -102,7 +102,7 @@ rm index.dirty
 COMMIT=$(echo "Dirty commit with nested files" | $GIT commit-tree "$TREE")
 $GIT update-ref refs/heads/master "$COMMIT"
 
-print_info "Created dirty remote with 2 unencrypted files"
+print_info "Created dirty remote with 4 unencrypted files"
 
 # Test helper
 assert_grep() {
index 44af71e3fb78bbc4853cee4b47f3412ba2436469..1562cf42c40361c4ce373192ac394853ac88560a 100755 (executable)
@@ -46,7 +46,7 @@ dd if=/dev/urandom of=largeblob bs=1K count=100 2>/dev/null # 100KB is enough to
 $GIT add largeblob
 $GIT commit -m "Add large blob" >/dev/null
 echo "Pushing initial data..."
-git push origin master >/dev/null 2>&1 || {
+$GIT push origin master >/dev/null 2>&1 || {
        echo "Push failed"
        exit 1
 }
index 8b44cbf5b9d58d64441821a7665f372ea73d8b2f..cc97fcca0a35d564e6e07610e400af0a10699a82 100755 (executable)
@@ -84,7 +84,7 @@ else
 fi
 
 # Use the identified OS for the expected string
-EXPECTED_TAG="5.5.5-1 (deb running on $OS_IDENTIFIER)"
+EXPECTED_TAG="5.5.5-1 ($OS_IDENTIFIER)"
 
 assert_version "$EXPECTED_TAG"
 
@@ -94,7 +94,10 @@ rm -rf "${SANDBOX:?}/usr"
 export DESTDIR="$SANDBOX/pkg_root"
 export prefix="/usr"
 
-"bash" "$INSTALLER" >/dev/null 2>&1
+"bash" "$INSTALLER" >/dev/null 2>&1 || {
+       print_err "Installer FAILED"
+       exit 1
+}
 
 if [ -f "$SANDBOX/pkg_root/usr/bin/git-remote-gcrypt" ]; then
        printf "  âœ“ %s\n" "DESTDIR honored"
index 316318a2b1f29ece6fd0e5d548b07dc48e34e4d5..9fce02956f5e73cf70457848f1060b01fc2563a4 100755 (executable)
@@ -17,7 +17,8 @@ trap 'rm -Rf -- "$tempdir"' EXIT
 repo_root=$(git rev-parse --show-toplevel)
 test_version=$(git describe --tags --always --dirty 2>/dev/null || echo "test")
 cp "$repo_root/git-remote-gcrypt" "$tempdir/git-remote-gcrypt"
-sed -i "s/@@DEV_VERSION@@/$test_version/" "$tempdir/git-remote-gcrypt"
+sed "s/@@DEV_VERSION@@/$test_version/" "$tempdir/git-remote-gcrypt" >"$tempdir/git-remote-gcrypt.tmp"
+mv "$tempdir/git-remote-gcrypt.tmp" "$tempdir/git-remote-gcrypt"
 chmod +x "$tempdir/git-remote-gcrypt"
 PATH=$tempdir:${PATH}
 export PATH
@@ -82,7 +83,6 @@ git remote add origin "gcrypt::${tempdir}/remote-repo"
 git config remote.origin.gcrypt-participants "test@example.com"
 git config remote.origin.gcrypt-signingkey "test@example.com"
 
-# Force push is required to initialize gcrypt over an existing repo
 # Force push is required to initialize gcrypt over an existing repo
 # Now EXPECT FAILURE because of our new safety check!
 print_info "Attempting push to dirty repo (should fail due to safety check)..."
index 7d8d52b9801e0f96791e805cbf2ca2b6ee49eb76..7cc6ae5b3f5e72bfa20bfd15dbc187f7aa8b1f8e 100644 (file)
@@ -25,4 +25,16 @@ else
        echo "Man page not found: $MAN_PATH"
 fi
 
+# Completions
+COMP_BASH="$DESTDIR$prefix/share/bash-completion/completions/git-remote-gcrypt"
+COMP_ZSH="$DESTDIR$prefix/share/zsh/site-functions/_git-remote-gcrypt"
+COMP_FISH="$DESTDIR$prefix/share/fish/vendor_completions.d/git-remote-gcrypt.fish"
+
+for f in "$COMP_BASH" "$COMP_ZSH" "$COMP_FISH"; do
+       if [ -f "$f" ]; then
+               verbose rm -f "$f"
+               echo "Removed completion: $f"
+       fi
+done
+
 echo "Uninstallation complete."
index 85691ff1e49b3e361739eb88f06f4a6971c194e7..878ccc2639095446e7dd9bfe6ed22767d4f087be 100755 (executable)
@@ -70,7 +70,7 @@ unset IFS
 
 # 3. Generate README
 echo "Generating $README_OUT..."
-sed "s/{commands_help}/$(echo "$COMMANDS_HELP" | sed 's/[\/&]/\\&/g' | sed ':a;N;$!ba;s/\n/\\n/g')/" "$README_TMPL" >"$README_OUT"
+sed "s/{commands_help}/$(echo "$COMMANDS_HELP" | sed 's/[\/&]/\\&/g' | awk '{printf "%s\\n", $0}')/" "$README_TMPL" >"$README_OUT"
 
 # 4. Generate Bash
 echo "Generating Bash completions..."
@@ -81,7 +81,7 @@ echo "Generating Zsh completions..."
 # Zsh substitution is tricky with the complex string.
 # We'll stick to replacing {commands} and {clean_flags_zsh}
 # Need to escape special chars for sed
-SAFE_CMDS=$(echo "$COMMANDS_LIST" | sed 's/ / /g') # just space separated
+# safe_cmds removed as unused
 # For clean_flags_zsh, since it contains quotes and braces, we need care.
 # We'll read the template line by line? No, sed is standard.
 # We use a temp file for the replacement string to avoid sed escaping hell for large blocks?