grm

git repo manager for self-hosted git servers
git clone git://sink.krj.st/grm
Log | Files | Refs | README | LICENSE

grm (7722B)


      1 #!/bin/sh
      2 # grm: git repo manager for self-hosted git servers
      3 
      4 #---------------+----------+-----------------#
      5 #               |  config  |                 #
      6 #               +----------+                 #
      7 
      8 # root directory of git repositories
      9 # GRM_REPOS_ROOT="/home/git"
     10 
     11 # default owner
     12 # GRM_OWNER="yourname"
     13 
     14 # default url prefix (without ending slash)
     15 # GRM_URL_PREFIX="git://git.domain.tld"
     16 
     17 # path of the post-receive hooks for stagit
     18 # GRM_POSTRECV_HOOK="/home/git/.post-receive"
     19 
     20 # root directory of stagit web pages
     21 # STAGIT_WEB_ROOT="/srv/git"
     22 
     23 #                                            #
     24 #                                            #
     25 #--------------------------------------------#
     26 
     27 # for stagit
     28 export LC_CTYPE="en_US.UTF-8"
     29 
     30 prog_name="${0##*/}"
     31 repos_root=${GRM_REPOS_ROOT:-/home/git}
     32 web_root=${STAGIT_WEB_ROOT:-/srv/git}
     33 
     34 recompile_repo() {
     35   echo "[$1] recompiling stagit pages..."
     36   repo_dir="${repos_root}/${1}.git"
     37   repo_web_dir="${web_root}/${repo_name}"
     38   cachefile="${repo_dir}/.htmlcache"
     39 
     40   [ -d "$repo_dir" ] || { echo "[$1] repo not found"; return 1; }
     41   mkdir -p "$repo_web_dir"
     42 
     43   cd "${repo_web_dir:?}"             && \
     44   rm -f "$cachefile"                 && \
     45   rm -rf "commit" "file"             && \
     46   stagit -c "$cachefile" "$repo_dir" && \
     47   ln -sf log.html index.html         && \
     48   echo "[$1] done!"
     49 }
     50 
     51 rebuild_index() {
     52   echo "[index] rebuilding index..."
     53   mkdir -p "${web_root}" || return 1;
     54   # 1. find all directories in $repos_root ending with .git
     55   # 2. filter all the public repos (with git-daemon-export-ok)
     56   # 3. exclude any repo marked with stagit-no-index
     57   # 4. sort the result
     58   # 5. hack for posix compatibility
     59   # 6. run stagit-index on the result
     60   # 7. export result to index.html
     61   find "${repos_root}/." ! -name . -prune     \
     62     -type d -name "*.git"                     \
     63     -exec test -e "{}/git-daemon-export-ok" \;\
     64     -exec test ! -e "{}/stagit-no-index" \;   \
     65     -print                                    \
     66     | sort -f                                 \
     67     | sed -e 's/"/"\\""/g' -e 's/.*/"&"/'     \
     68     | xargs stagit-index                      \
     69     > "${web_root}/index.html" && \
     70   echo "[index] done!"
     71 }
     72 
     73 RED="\033[91m"
     74 GREEN="\033[92m"
     75 YELLOW="\033[93m"
     76 BLUE="\033[94m"
     77 RESET="\033[0m"
     78 
     79 grm_new() {
     80   set -e
     81   default_owner=${GRM_OWNER:-$(logname)}
     82   url_prefix=${GRM_URL_PREFIX:-git://$(hostname -f)}
     83   postrecv_path=${GRM_POSTRECV_HOOK:-/usr/local/share/doc/stagit/post-receive}
     84   default_desc="a work in progress"
     85 
     86   printf "%b\n> " "${BLUE}repo name${RESET}"
     87   read -r repo_name
     88   [ -z "$repo_name" ] && \
     89     { echo "no repo name given, exiting..."; exit 1; }
     90 
     91   # now we have the complete path of the repo
     92   repo_path="$repos_root/${repo_name}.git"
     93   [ -e "$repo_path" ] && \
     94     { echo "repository already exists"; exit 1; }
     95 
     96   printf "%b\n> " "${YELLOW}visibility:
     97   1) public\n  2) private\n  3) unlisted (hide from index)
     98 ${BLUE}enter index [default: ${GREEN}1${BLUE}]${RESET}"
     99   read -r visibility
    100 
    101   case $visibility in
    102      1|public) exported=1 ;;
    103      2|private) ;;
    104      3|unlisted) exported=1; hidden=1 ;;
    105      *) printf "%b\n" "${YELLOW}visibility defaults to ${GREEN}public${RESET}"
    106         exported=1 ;;
    107   esac
    108 
    109   printf "%b%s%b\n> " "${BLUE}description [${GREEN}" "$default_desc" "${BLUE}]${RESET}"
    110   read -r repo_desc
    111   repo_desc=${repo_desc:-$default_desc}
    112 
    113   printf "%b%s%b\n> " "${BLUE}owner [${GREEN}" "$default_owner" "${BLUE}]${RESET}"
    114   read -r owner
    115   owner=${owner:-$default_owner}
    116 
    117   printf "%b%s%b\n> " \
    118     "${BLUE}clone url [${GREEN}" "$url_prefix/$repo_name" "${BLUE}]${RESET}"
    119   read -r clone_url
    120   clone_url=${clone_url:-$url_prefix/$repo_name}
    121 
    122   # start creating repo
    123   git init --bare "$repo_path"
    124 
    125   echo "writing stagit metadata..."
    126   printf "%s\n" "$repo_desc" > "$repo_path/description"
    127   printf "%s\n" "$owner" > "$repo_path/owner"
    128   printf "%s\n" "$clone_url" > "$repo_path/url"
    129 
    130   echo "setting visibility..."
    131   [ "$exported" = "1" ] && : >> "$repo_path/git-daemon-export-ok"
    132   [ "$hidden" = "1" ] && : >> "$repo_path/stagit-no-index"
    133 
    134   echo "installing stagit post-receive hook..."
    135   ln -sf "$postrecv_path" "$repo_path/hooks/post-receive"
    136 
    137   echo "done!"
    138 }
    139 
    140 grm_remove() {
    141   [ $# -gt 0 ] || { echo "no repo name given, exiting..."; exit 1; }
    142 
    143   for repo in "$@"
    144   do
    145     printf "remove %s? [y/N] " "$repo"
    146     read -r resp
    147     if echo "$resp" | grep -iq "^y$"; then
    148       rm -rf "${repos_root:?}/${repo:?}.git" || continue;
    149       rm -rf "${web_root:?}/${repo:?}" || continue;
    150     fi
    151   done
    152   # only rebuild index if stagit exists
    153   command -v stagit-index >/dev/null && rebuild_index &
    154   wait
    155 }
    156 
    157 grm_list() {
    158   case "$1" in
    159     public)
    160       find "${repos_root}/." ! -name . -prune        \
    161         -type d -name "*.git"                        \
    162         -exec test -e "{}/git-daemon-export-ok" \;   \
    163         -exec test ! -e "{}/stagit-no-index" \;      \
    164         -exec basename {} '.git' \; ;;
    165     private)
    166       find "${repos_root}/." ! -name . -prune        \
    167         -type d -name "*.git"                        \
    168         -exec test ! -e "{}/git-daemon-export-ok" \; \
    169         -exec basename {} '.git' \; ;;
    170     hidden|unlisted)
    171       find "${repos_root}/." ! -name . -prune        \
    172         -type d -name "*.git"                        \
    173         -exec test -e "{}/stagit-no-index" \;        \
    174         -exec basename {} '.git' \; ;;
    175     *)
    176       find "${repos_root}/." ! -name . -prune \
    177         -type d -name "*.git"                 \
    178         -exec basename {} '.git' \; ;;
    179   esac
    180 }
    181 
    182 grm_recompile() {
    183   for repo_name in "$@"
    184   do
    185     recompile_repo "$repo_name" &
    186   done
    187   rebuild_index &
    188   wait
    189   echo "recompilation done!"
    190 }
    191 
    192 grm_recompileall() {
    193   grm_list public \
    194     | sed -e 's/"/"\\""/g' -e 's/.*/"&"/' \
    195     | xargs "$0" rc
    196 }
    197 
    198 grm_info() {
    199   [ -z "$1" ] && { echo "no repo name given, exiting..."; exit 1; }
    200 
    201   repo_name=$1
    202   repo_dir="${repos_root}/${repo_name}.git"
    203 
    204   [ -d "$repo_dir" ] || { echo "can't find repo named $repo_name"; exit 1; }
    205   echo "name: $repo_name"
    206   printf "visibility: "
    207 
    208   if [ -e "${repo_dir}/git-daemon-export-ok" ]; then
    209     if [ -e "${repo_dir}/stagit-no-index" ]; then
    210       printf "%b\n" "${YELLOW}unlisted${RESET}"
    211     else
    212       printf "%b\n" "${GREEN}public${RESET}"
    213     fi
    214   else
    215     printf "%b\n" "${RED}private${RESET}"
    216   fi
    217 
    218   [ -f "${repo_dir}/description" ] && \
    219     echo "description: $(cat "${repo_dir}/description")"
    220 
    221   [ -f "${repo_dir}/owner" ] && \
    222     echo "owner: $(cat "${repo_dir}/owner")"
    223 
    224   [ -f "${repo_dir}/url" ] && \
    225     echo "url: $(cat "${repo_dir}/url")"
    226 }
    227 
    228 show_help() {
    229   cat << EOF
    230 usage: $prog_name <command> [<args>]
    231 
    232 Git repo manager, manage git repositories on self-hosted git servers.
    233 
    234 If you have created a 'git' user for managing git repositories, this
    235 script should be run as:
    236     $ doas -u git -- $prog_name <command> [<args>]
    237 or
    238     $ sudo -u git -- $prog_name <command> [<args>]
    239 
    240 commands:
    241     new                  create a new repo
    242     info repo_name       display metadata of the repo
    243     ls                   list all repos
    244     ls public            list public repos
    245     ls private           list private repos
    246     ls unlisted          list unlisted (hidden) repos
    247     rm repo1 [repo2..]   remove repos
    248     rc                   recompile stagit index
    249     rc repo1 [repo2..]   recompile stagit pages for repos,
    250                          and recompile index
    251     rca                  recompile all public repos
    252     help                 show help
    253 EOF
    254 }
    255 
    256 # parse subcommands
    257 case "$1" in
    258   new) cmd=new;;
    259   ls|list) cmd=list;;
    260   rm|remove) cmd=remove;;
    261   rc|recompile) cmd=recompile;;
    262   rca|recompileall) cmd=recompileall;;
    263   info) cmd=info;;
    264   *) { show_help; exit; };;
    265 esac
    266 
    267 shift
    268 grm_"$cmd" "$@"