From d00f8fb1791e6f412d4a8898b53cdbe3cac20daa Mon Sep 17 00:00:00 2001 From: "quentin@aristote.fr" Date: Sun, 25 Jan 2026 20:19:01 +0100 Subject: git: add shell-commands --- config/services/git/shell-commands/default.nix | 12 ++++ config/services/git/shell-commands/repo/.gitignore | 1 + .../services/git/shell-commands/repo/default.nix | 19 +++++ .../git/shell-commands/repo/src/bashly.yml | 83 ++++++++++++++++++++++ .../git/shell-commands/repo/src/create_command.sh | 25 +++++++ .../repo/src/lib/check-git-repository.sh | 7 ++ .../shell-commands/repo/src/lib/check-realpath.sh | 9 +++ .../git/shell-commands/repo/src/lib/colors.sh | 14 ++++ .../git/shell-commands/repo/src/lib/is-git-dir.sh | 3 + .../git/shell-commands/repo/src/lib/show-descr.sh | 3 + .../shell-commands/repo/src/lib/show-privacy.sh | 12 ++++ .../repo/src/lib/show-repo-summary.sh | 8 +++ .../git/shell-commands/repo/src/lib/show-repo.sh | 6 ++ .../git/shell-commands/repo/src/lib/show-size.sh | 3 + .../git/shell-commands/repo/src/remove_command.sh | 10 +++ .../repo/src/set_description_command.sh | 14 ++++ .../shell-commands/repo/src/set_privacy_command.sh | 17 +++++ .../git/shell-commands/repo/src/show_command.sh | 35 +++++++++ 18 files changed, 281 insertions(+) create mode 100644 config/services/git/shell-commands/default.nix create mode 100644 config/services/git/shell-commands/repo/.gitignore create mode 100644 config/services/git/shell-commands/repo/default.nix create mode 100644 config/services/git/shell-commands/repo/src/bashly.yml create mode 100644 config/services/git/shell-commands/repo/src/create_command.sh create mode 100644 config/services/git/shell-commands/repo/src/lib/check-git-repository.sh create mode 100644 config/services/git/shell-commands/repo/src/lib/check-realpath.sh create mode 100644 config/services/git/shell-commands/repo/src/lib/colors.sh create mode 100644 config/services/git/shell-commands/repo/src/lib/is-git-dir.sh create mode 100644 config/services/git/shell-commands/repo/src/lib/show-descr.sh create mode 100644 config/services/git/shell-commands/repo/src/lib/show-privacy.sh create mode 100644 config/services/git/shell-commands/repo/src/lib/show-repo-summary.sh create mode 100644 config/services/git/shell-commands/repo/src/lib/show-repo.sh create mode 100644 config/services/git/shell-commands/repo/src/lib/show-size.sh create mode 100644 config/services/git/shell-commands/repo/src/remove_command.sh create mode 100644 config/services/git/shell-commands/repo/src/set_description_command.sh create mode 100644 config/services/git/shell-commands/repo/src/set_privacy_command.sh create mode 100644 config/services/git/shell-commands/repo/src/show_command.sh (limited to 'config/services/git/shell-commands') diff --git a/config/services/git/shell-commands/default.nix b/config/services/git/shell-commands/default.nix new file mode 100644 index 0000000..b88e6b0 --- /dev/null +++ b/config/services/git/shell-commands/default.nix @@ -0,0 +1,12 @@ +{ lib, pkgs, ... }: +let + repo = import ./repo { inherit lib pkgs; }; + gitShellCommands = "/srv/git/git-shell-commands"; +in +{ + system.activationScripts.buildGitShell.text = '' + install --mode u=rX,g=,o= --owner git --group git --directory ${gitShellCommands} + rm -rf ${gitShellCommands}/* + ln -s ${repo} ${gitShellCommands}/repo + ''; +} diff --git a/config/services/git/shell-commands/repo/.gitignore b/config/services/git/shell-commands/repo/.gitignore new file mode 100644 index 0000000..ffdcec4 --- /dev/null +++ b/config/services/git/shell-commands/repo/.gitignore @@ -0,0 +1 @@ +repo \ No newline at end of file diff --git a/config/services/git/shell-commands/repo/default.nix b/config/services/git/shell-commands/repo/default.nix new file mode 100644 index 0000000..bbccbfd --- /dev/null +++ b/config/services/git/shell-commands/repo/default.nix @@ -0,0 +1,19 @@ +{ lib, pkgs }: + +pkgs.stdenvNoCC.mkDerivation { + name = "repo"; + src = lib.fileset.toSource { + root = ./src; + fileset = ./src; + }; + buildInputs = [ pkgs.bashly ]; + unpackPhase = '' + ln --symbolic --no-target-directory "$src" src + ''; + buildPhase = '' + bashly generate + ''; + installPhase = '' + install ./repo "$out" + ''; +} diff --git a/config/services/git/shell-commands/repo/src/bashly.yml b/config/services/git/shell-commands/repo/src/bashly.yml new file mode 100644 index 0000000..6a216ae --- /dev/null +++ b/config/services/git/shell-commands/repo/src/bashly.yml @@ -0,0 +1,83 @@ +name: repo +help: show and configure repositories + +commands: +- name: show + help: show repositories + default: true + + args: + - name: path + required: false + help: path in which to list repositories + + flags: + - long: --verbose + short: -v + help: show repository description + - long: --tree + short: -t + help: show intermediate directories + + examples: + - "repo show # list all repositories" + - "repo show --verbose # list repositories in details" + - "repo show about-me # list repositories in the about-me/ folder" + - "repo show about-me/cv # show the about-me/cv repository" + - "repo show --tree # list all repositories as well as intermediate directories" + +- name: set + help: configure repository + + commands: + - name: description + alias: descr + help: set repository description + + args: + - name: repo + required: true + help: repository path + + examples: + - repo set descr about-me/cv + + - name: privacy + alias: prv + help: recursively set repository visibility + + args: + - name: path + required: true + help: repository path + - name: visibility + required: true + help: new repository visibility + allowed: [public, private] + + examples: + - "repo set prv public about-me/cv # change visibility of a single repository" + - "repo set prv public about-me # change visibility of all repositories in a tree" + +- name: create + help: create bare repositories + + args: + - name: repo + required: true + help: repository path + + examples: + - repo create path/to/repo + +- name: remove + alias: rm + help: remove repository or empty directory + + args: + - name: path + required: true + help: repository path + + examples: + - repo rm path/to/repo diff --git a/config/services/git/shell-commands/repo/src/create_command.sh b/config/services/git/shell-commands/repo/src/create_command.sh new file mode 100644 index 0000000..ab8bf3f --- /dev/null +++ b/config/services/git/shell-commands/repo/src/create_command.sh @@ -0,0 +1,25 @@ +repo=$(realpath --canonicalize-missing /srv/git/"${args[repo]}") + +if [[ ! "$repo" == /srv/git/* ]] +then + red "Forbidden path." + exit 2 +fi + +subpath=/srv/git +IFS='/' read -ra dirs <<< "${repo#/srv/git/}" +for dir in "${dirs[@]}"; do + subpath="$subpath/$dir" + if [[ ! -d "$subpath" ]] + then + break + fi +done + +mkdir --parents "$repo" +chmod u=rwX,g=,o= --recursive "$subpath" +git init --bare "$repo" + +args[path]="$repo" +args[--tree]=1 +repo_show_command diff --git a/config/services/git/shell-commands/repo/src/lib/check-git-repository.sh b/config/services/git/shell-commands/repo/src/lib/check-git-repository.sh new file mode 100644 index 0000000..c974890 --- /dev/null +++ b/config/services/git/shell-commands/repo/src/lib/check-git-repository.sh @@ -0,0 +1,7 @@ +check-git-repository() { + if [[ ! $(is-git-dir "$1") ]] + then + red "Not a git repository." + exit 2 + fi +} diff --git a/config/services/git/shell-commands/repo/src/lib/check-realpath.sh b/config/services/git/shell-commands/repo/src/lib/check-realpath.sh new file mode 100644 index 0000000..c344bd2 --- /dev/null +++ b/config/services/git/shell-commands/repo/src/lib/check-realpath.sh @@ -0,0 +1,9 @@ +check-realpath() { + local path="$1" + if [[ ! -e "$path" ]] \ + || [[ ! "$path" == /srv/git/* ]] + then + red "Forbidden path." + exit 2 + fi +} diff --git a/config/services/git/shell-commands/repo/src/lib/colors.sh b/config/services/git/shell-commands/repo/src/lib/colors.sh new file mode 100644 index 0000000..a78911d --- /dev/null +++ b/config/services/git/shell-commands/repo/src/lib/colors.sh @@ -0,0 +1,14 @@ +print_in_color() { + local color="$1" + shift + if [[ "${NO_COLOR:-}" == "" ]]; then + printf "$color%b\e[0m\n" "$*" + else + printf "%b\n" "$*" + fi +} + +red() { print_in_color "\e[31m" "$*"; } +green() { print_in_color "\e[32m" "$*"; } +blue() { print_in_color "\e[34m" "$*"; } +bold() { print_in_color "\e[1m" "$*"; } diff --git a/config/services/git/shell-commands/repo/src/lib/is-git-dir.sh b/config/services/git/shell-commands/repo/src/lib/is-git-dir.sh new file mode 100644 index 0000000..d94f2ba --- /dev/null +++ b/config/services/git/shell-commands/repo/src/lib/is-git-dir.sh @@ -0,0 +1,3 @@ +is-git-dir() { + git rev-parse --resolve-git-dir "$1" 2>/dev/null +} diff --git a/config/services/git/shell-commands/repo/src/lib/show-descr.sh b/config/services/git/shell-commands/repo/src/lib/show-descr.sh new file mode 100644 index 0000000..edf6d34 --- /dev/null +++ b/config/services/git/shell-commands/repo/src/lib/show-descr.sh @@ -0,0 +1,3 @@ +show-descr() { + echo "| $(cat "$1"/description)" +} diff --git a/config/services/git/shell-commands/repo/src/lib/show-privacy.sh b/config/services/git/shell-commands/repo/src/lib/show-privacy.sh new file mode 100644 index 0000000..11df8d1 --- /dev/null +++ b/config/services/git/shell-commands/repo/src/lib/show-privacy.sh @@ -0,0 +1,12 @@ +show-privacy() { + local mode=$(( $(stat -c %a "$1")/10 %10 )) + if [[ $mode == 0 ]] + then + red private + elif [[ $mode == 5 ]] + then + green public + else + blue unknown + fi +} diff --git a/config/services/git/shell-commands/repo/src/lib/show-repo-summary.sh b/config/services/git/shell-commands/repo/src/lib/show-repo-summary.sh new file mode 100644 index 0000000..7c0bf63 --- /dev/null +++ b/config/services/git/shell-commands/repo/src/lib/show-repo-summary.sh @@ -0,0 +1,8 @@ +show-repo-summary() { + local repo="$1" + local name="${repo#/srv/git/}" + local privacy=$(show-privacy "$repo") + local size=$(show-size "$repo") + printf "%-16s %4s " "$privacy" "$size" + bold "$name" +} diff --git a/config/services/git/shell-commands/repo/src/lib/show-repo.sh b/config/services/git/shell-commands/repo/src/lib/show-repo.sh new file mode 100644 index 0000000..d125341 --- /dev/null +++ b/config/services/git/shell-commands/repo/src/lib/show-repo.sh @@ -0,0 +1,6 @@ +show-repo() { + local repo="$1" + show-repo-summary "$repo" + printf "%13s" "" + show-descr "$repo" +} diff --git a/config/services/git/shell-commands/repo/src/lib/show-size.sh b/config/services/git/shell-commands/repo/src/lib/show-size.sh new file mode 100644 index 0000000..739f61d --- /dev/null +++ b/config/services/git/shell-commands/repo/src/lib/show-size.sh @@ -0,0 +1,3 @@ +show-size() { + du --human-readable --summarize "$1" | cut --field 1 +} diff --git a/config/services/git/shell-commands/repo/src/remove_command.sh b/config/services/git/shell-commands/repo/src/remove_command.sh new file mode 100644 index 0000000..b45406f --- /dev/null +++ b/config/services/git/shell-commands/repo/src/remove_command.sh @@ -0,0 +1,10 @@ +path=$(realpath "${args[path]}") + +check-realpath "$path" + +if [[ $(is-git-dir "$path") ]] +then + rm --recursive "$path" +else + rm --dir "$path" +fi diff --git a/config/services/git/shell-commands/repo/src/set_description_command.sh b/config/services/git/shell-commands/repo/src/set_description_command.sh new file mode 100644 index 0000000..b6b40f0 --- /dev/null +++ b/config/services/git/shell-commands/repo/src/set_description_command.sh @@ -0,0 +1,14 @@ +repo=$(realpath "${args[repo]}") + +echo "$repo" +check-realpath "$repo" +check-git-repository "$repo" +echo "Old description:" +show-descr "$repo" +echo "New description:" +echo -n "| " +read descr +echo +echo "$descr" > "$repo"/description +show-repo "$repo" + diff --git a/config/services/git/shell-commands/repo/src/set_privacy_command.sh b/config/services/git/shell-commands/repo/src/set_privacy_command.sh new file mode 100644 index 0000000..db033e6 --- /dev/null +++ b/config/services/git/shell-commands/repo/src/set_privacy_command.sh @@ -0,0 +1,17 @@ +path=$(realpath "${args[path]}") + +check-realpath "$path" + +case "${args[visibility]}" in + public) + mode="rX" + ;; + private) + mode="" + ;; +esac + +chmod g="$mode" --recursive "$path" +args[--tree]=1 +args[path]="$path" +repo_show_command diff --git a/config/services/git/shell-commands/repo/src/show_command.sh b/config/services/git/shell-commands/repo/src/show_command.sh new file mode 100644 index 0000000..77bb92f --- /dev/null +++ b/config/services/git/shell-commands/repo/src/show_command.sh @@ -0,0 +1,35 @@ +if [[ -z "${args[path]}" ]] +then + path=/srv/git/ +else + path=$(realpath "${args[path]}") + check-realpath "$path" +fi + +if [[ $(is-git-dir "$path") ]] +then + args[--verbose]=1 +fi + +dirs=$(find "$path" -type f -name HEAD | while read -r head; do + dir="${head%/*}" + if [[ "${args[--tree]}" ]] + then + while [[ "$dir" != "/srv/git" && -n "$dir" ]]; do + echo "$dir/" + dir="${dir%/*}" + done + else + echo "$dir/" + fi + done | sort -u) + +for dir in $dirs +do + if [[ "${args[--verbose]}" ]] && [[ $(is-git-dir "$dir") ]] + then + show-repo "$dir" + else + show-repo-summary "$dir" + fi +done -- cgit v1.2.3