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