#!/usr/bin/env bash BASEDIR=/git C_bg_green=$(echo -e "\e[42m") C_bg_magenta=$(echo -e "\e[45m") C_bg_default=$(echo -e "\e[49m") C_bg_lblue=$(echo -e "\e[104m") _getent() { local db=$1 local key=$2 getent $db $key | cut -d ':' -f 1 } _list_repo_dirs() { find "$1" -maxdepth 6 -type d -not -path '*.git/*' -name '*.git' 2>/dev/null } do_list() { if [ -n "$1" ]; then local root_path=$(readlink -f "$1") else local root_path=$BASEDIR fi echo -n "Working.. listing repositories under $root_path" for repo_path in $(_list_repo_dirs $root_path | sort); do echo -ne "\r" # to remove working eval $(stat --format 'local repo_path_uid=%u repo_path_gid=%g repo_path_mode=%a' "$repo_path") if [ "$repo_path_uid" != "$UID" ]; then # so there must be a matching gid local pass=false for user_gid in $(id -G); do if [ "$repo_path_gid" == "$user_gid" ]; then pass=true fi done if ! $pass; then continue # skip that repo since uid and gids don't match fi fi local shortdesc=$(cut -c 1-40 < "$repo_path/description") local permissions="owner=$(_getent passwd $repo_path_uid)" local color="$C_bg_green" if [ $(( 0$repo_path_mode & 070 )) -gt 0 ]; then # shared permissions="$permissions group=$(_getent group $repo_path_gid)" color="$C_bg_lblue" fi if [ $(( 0$repo_path_mode & 07 )) -gt 0 ]; then # public permissions="$permissions PUBLIC" color="$C_bg_magenta" fi printf "%s%-60s (%s) (%s)\n" "$color" "$repo_path$C_bg_default" "$permissions" "$shortdesc" done } _create_repo() { local label=$1 local shared=$2 local group=$3 local is_public=$4 read -p 'New repository name.git: ' local repo_name=$REPLY if ! `expr "$repo_name" : '.*\.git' >/dev/null`; then echo "Repository name must end with .git" return 1 fi local repo_dir=$BASEDIR/$USER/${repo_name} test \! -e "$repo_dir" || { echo "Repo $repo_dir already exist"; exit 1; } read -p 'Set Description: ' local repo_desc=$REPLY read -p "Create $label in $repo_dir Ok? (y/N)" test "$REPLY" == "y" || exit 1 mkdir -p "$repo_dir" git init -q --bare --shared=$shared "$repo_dir" chgrp -R "$group" "$repo_dir" echo "$repo_desc" > "$repo_dir/description" if $is_public; then touch "$repo_dir/PUBLIC" echo "created $repo_dir/PUBLIC to expose via cgit" fi echo "done creating $label in $repo_dir" echo "use 'git repo show $repo_dir' for details" return 0 } do_create_public() { _create_repo "public repository" "0664" "share" true return $? } do_create_shared() { _create_repo "group-writeable repository" "0660" "share" false return $? } do_create_private() { _create_repo "private repository" "0600" "$USER" false return $? } do_show() { for repo_path in $*; do if ! [ -f "$repo_path/HEAD" ]; then repo_path="$BASEDIR/$repo_path" fi if ! [ -f "$repo_path/HEAD" ]; then echo "Not a git repository: $repo_path" continue fi local repo_file_mode=$(stat --format %a "$repo_path") local repo_file_uid=$(stat --format %u "$repo_path") local repo_file_user=$(_getent passwd $repo_file_uid) local repo_file_gid=$(stat --format %g "$repo_path") local repo_file_group=$(_getent group $repo_file_gid) local repo_git_shared=$(GIT_DIR="$repo_path" git config --get core.sharedrepository) echo " Directory: $repo_path" echo " ✔ Permissions mode ${repo_file_mode} (uid=${repo_file_uid}/${repo_file_user} gid=${repo_file_gid}/${repo_file_group})" echo " git core.sharedrepository=$repo_git_shared" echo " ⚡ Clone read/write SSH: git clone 'ssh://localnet.cc${repo_path}'" if [ $(( 0$repo_file_mode & 07 )) -gt 0 ]; then echo " Clone read/- HTTP: git clone 'http://localnet.cc/cgit/cgit.cgi/${repo_path#$BASEDIR/}'" fi echo " Update remote: git remote set-url origin 'ssh://localnet.cc${repo_path}" echo " ☛ To update description execute: $EDITOR $repo_path/description" echo "" done } do_mirror() { local root_path=$(readlink -f "$1") for repo_path in $(_list_repo_dirs $root_path); do local is_mirror=$(GIT_DIR="$repo_path" git config --get remote.origin.mirror) if [ "$is_mirror" != "true" ]; then continue # skip non-mirror repo fi echo "Mirror $repo_path from $(GIT_DIR="$repo_path" git config --get remote.origin.url)" GIT_DIR="$repo_path" git fetch --force --prune origin done } do_make_public() { local repo_path=$1 test -f "$repo_path/HEAD" || { echo "$repo_path is not a git repository"; exit 1; } set -x git init -q --bare --shared=0664 "$repo_path" find "$repo_path" -type d -exec chown $USER:share \{\} \; find "$repo_path" -type d -exec chmod 0775 \{\} \; find "$repo_path" -type f -exec chmod 0664 \{\} \; touch "$repo_path/PUBLIC" set +x } do_make_shared() { local repo_path=$1 test -f "$repo_path/HEAD" || { echo "$repo_path is not a git repository"; exit 1; } set -x git init -q --bare --shared=0660 "$repo_path" find "$repo_path" -type d -exec chown $USER:share \{\} \; find "$repo_path" -type d -exec chmod 0770 \{\} \; find "$repo_path" -type f -exec chmod 0660 \{\} \; chgrp -R share "$repo_path" rm -f "$repo_path/PUBLIC" set +x } do_make_private() { local repo_path=$1 test -f "$repo_path/HEAD" || { echo "$repo_path is not a git repository"; exit 1; } set -x git init -q --bare --shared=0600 "$repo_path" find "$repo_path" -type d -exec chown $USER:$USER \{\} \; find "$repo_path" -type d -exec chmod 0700 \{\} \; find "$repo_path" -type f -exec chmod 0600 \{\} \; chgrp -R $USER "$repo_path" rm -f "$repo_path/PUBLIC" set +x } case "$1" in list) do_list "$2" ;; create-public) do_create_public ;; create-shared) do_create_shared ;; create-private) do_create_private ;; make-public) do_make_public "$2" ;; make-shared) do_make_shared "$2" ;; make-private) do_make_private "$2" ;; show) shift do_show $* ;; mirror) do_mirror "$2" ;; *) cat <] List infos (optional: only below ) show Show Commands for repo mirror Mirror git repositories under by looking for 'Mirror for https://..' in description" create-public Create new repository that is - Read/Write by Owner - Read/Write by 'share' group. - Read-Only through www via cgit create-shared Create new repository that is - Read/Write by Owner - Read/Write by 'share' group. create-private Create new repository that is - Read/Write by Owner only make-public Change permissions to public make-shared Change permissions to shared make-private Change permissions to private ☛ Base directory of repositories: $BASEDIR ☛ Color codes: ${C_bg_magenta}public${C_bg_default}, ${C_bg_lblue}shared${C_bg_default}, ${C_bg_green}private${C_bg_default} ☛ For limited ssh-access with current user configure ~/.ssh/authorized_keys with: command=\"git shell -c \\\"\$SSH_ORIGINAL_COMMAND\\\"\",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-rsa AAAAB3..... HERE ;; esac