summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYves Fischer <yvesf+git@xapek.org>2017-01-05 11:42:00 +0000
committerYves Fischer <yvesf+git@xapek.org>2017-01-05 11:42:00 +0000
commit3c73384598e99f2f755450a3a9e63569e259ef1d (patch)
treed708573445f61bab85ffc2034c6b8e77e51e7a53
downloadgit-repo-3c73384598e99f2f755450a3a9e63569e259ef1d.tar.gz
git-repo-3c73384598e99f2f755450a3a9e63569e259ef1d.zip
git-repo
-rw-r--r--Makefile8
-rwxr-xr-xgit-repo221
2 files changed, 229 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0cfad6f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+PREFIX=/usr/local
+
+info:
+ echo 'install'
+
+install:
+ cp git-repo $(PREFIX)/bin
+ chmod 0755 $(PREFIX)/bin/git-repo
diff --git a/git-repo b/git-repo
new file mode 100755
index 0000000..8bded4f
--- /dev/null
+++ b/git-repo
@@ -0,0 +1,221 @@
+#!/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 <<HERE
+Unknown subcommand: '$1'
+
+Subcommands of git repo:
+ list [<dir>] List infos (optional: only below <dir>)
+ show <dir...> Show Commands for repo <dir>
+ mirror <dir> Mirror git repositories under <dir> 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 <dir> Change permissions to public
+ make-shared <dir> Change permissions to shared
+ make-private <dir> Change permissions to private
+HERE
+ ;;
+esac
+