git_infra

Git infra scripts for git.bracken.jp
git clone https://git.bracken.jp/git_infra.git
Log | Files | Refs | LICENSE

grm (8513B)


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