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" "$@"