git_infra

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

grm (8516B)


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