#!/usr/bin/env bash BASEDIR=$HOME/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 -path "/home/*/git/*" -not -path '*.git/*' -name '*.git' 2>/dev/null } do_list() { if [ -n "$1" ]; then local root_path=$(readlink -f "$1") else local root_path=/home 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 -f 'local repo_path_uid=%u repo_path_gid=%g repo_path_mode=%Lp' "$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 # and is it marked as shared ? local repo_git_shared=$(GIT_DIR="$repo_path" git config --get core.sharedrepository) local repo_name="~${repo_path#/home/}" local shortdesc=$(cut -c 1-40 < "$repo_path/description") if [ $(( 0$repo_path_mode & 07 )) -gt 0 ]; then # public local shared="($C_bg_magenta$repo_path_mode$C_bg_default git.shared=${repo_git_shared} gid=$repo_path_gid/$(_getent group $repo_path_gid))" elif [ $(( 0$repo_path_mode & 070 )) -gt 0 ]; then # shared local shared="($C_bg_lblue$repo_path_mode$C_bg_default git.shared=${repo_git_shared} gid=$repo_path_gid/$(_getent group $repo_path_gid))" else local shared="($C_bg_green$repo_path_mode$C_bg_default)" fi printf "%-60s %s (%s)\n" "$repo_name" "$shared" "$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/${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 --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 echo "Not a git repository: $repo_path" continue fi echo "========== Actions on: $repo_path" local repo_file_mode=$(stat -f %Lp "$repo_path") local repo_file_uid=$(stat -f %u "$repo_path") local repo_file_user=$(_getent passwd $repo_file_uid) local repo_file_gid=$(stat -f %g "$repo_path") local repo_file_group=$(_getent group $repo_file_gid) echo " ✔ Permissions mode ${repo_file_mode} (uid=${repo_file_uid}/${repo_file_user} gid=${repo_file_gid}/${repo_file_group})" echo " ⚡ To clone from remote execute: " echo " git clone 'ssh://localnet.cc/~${repo_path##/home/}'" if [ $(( 0$repo_file_mode & 07 )) -gt 0 ]; then echo " git clone 'http://localnet.cc/cgit/cgit.cgi/${repo_path##/home/}' (read-only)" fi echo " ☛ To update description execute: $EDITOR $repo_path/description" echo " ☛ For limited ssh-access with current user configure ~/.ssh/authorized_keys with:" echo " command=\"git shell -c \\\"\$SSH_ORIGINAL_COMMAND\\\"\",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-rsa AAAAB3....." done } do_mirror() { local root_path=$(readlink -f "$1") for repo_path in $(_list_repo_dirs $root_path); do local mirror_url=$(sed -n -e 's/^Mirror for \(https.*\)$/\1/p' < $repo_path/description) #todo 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 \{\} \; 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 \{\} \; 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 HERE ;; esac