gout_gemini (3992B)
1 #!/bin/sh -e 2 # 3 # post-receive.d hook: generate Gemini pages for a repo, plus the repo index. 4 # 5 # The twin of gout_html. The post-receive dispatcher feeds the same stdin to 6 # every hook in post-receive.d, so the two run independently and neither needs 7 # to know about the other. 8 # 9 # Output is served by agate as a virtual host, which is why the root below is 10 # content/<hostname>/ and why agate must be started with BOTH hostnames: 11 # agate_flags="--hostname chris.bracken.jp --hostname git.bracken.jp ..." 12 # agate_content="/var/gemini/content" 13 # agate only resolves the per-hostname subdirectory when given more than one 14 # --hostname. 15 # 16 # Install: 17 # /home/git/git_infra/post-receive.d/gout_gemini (this file, chmod +x) 18 # /home/git/<repo>.git/hooks/post-receive.d/gout_gemini -> symlink 19 20 export LC_CTYPE="en_US.UTF-8" 21 22 # agate reads these as a different user (1965) in a different jail, so the 23 # world bits have to be set. 24 umask 022 25 26 # Root directory into which gout will write pages. 27 gmi_root="/var/gemini/content/git.bracken.jp" 28 29 # Hooks are called from the repo directory. 30 repo_dir=$(pwd) 31 32 # The gout cache file. 33 # This is an optimisation to avoid regenerating all pages on each push. 34 # 35 # It must NOT be shared with gout_html: the cache holds log entries already 36 # rendered in the output format, so one cache per format. 37 cachefile="${repo_dir}/.gmicache" 38 39 # The default branch of the repository. 40 default_branch=$(git symbolic-ref HEAD 2>/dev/null || echo "refs/heads/main") 41 42 # The directory under which all repos are located. 43 # Repos under this directory will be added to the repo index page. 44 repos_root=$(dirname "$repo_dir") 45 46 # The user-friendly name of the repository. 47 repo_name=$(basename "$repo_dir" '.git') 48 49 # Directory into which repo pages will be written. 50 repo_gmi_dir="${gmi_root}/${repo_name}" 51 52 # Detect --force pushes. 53 cd "${repo_dir}" || exit 1 54 force=0 55 while read -r old new ref; do 56 # Ignore force pushes to non-default branches. 57 [ "${ref}" = "${default_branch}" ] || continue 58 59 [ "${old}" = "0000000000000000000000000000000000000000" ] && continue 60 [ "${new}" = "0000000000000000000000000000000000000000" ] && continue 61 62 hasrevs=$(git rev-list "$old" "^$new" | sed 1q) 63 if [ -n "$hasrevs" ]; then 64 echo "[gout] Force push detected on ${ref}: cleaning gemini cache" 65 force=1 66 break 67 fi 68 done 69 70 # If there was a --force push, delete all existing pages. The lack of a cache 71 # file will trigger a full rebuild below. 72 if [ "$force" = "1" ]; then 73 rm -f "$cachefile" 74 rm -rf "${repo_gmi_dir}/commit" "${repo_gmi_dir}/file" 75 fi 76 77 # Build the pages. If $cachefile is not present, a full rebuild is performed. 78 if [ -e "${repo_dir}/git-daemon-export-ok" ]; then 79 # Change to the directory where we will output pages. 80 mkdir -p "$repo_gmi_dir" 81 cd "$repo_gmi_dir" || exit 1 82 83 # Generate the index page. 84 # Any repo containing a gout-no-index file will not be indexed. 85 # 86 # gout_index -M emits relative links (=> repo/log.gmi), and takes no -m: the 87 # Mastodon verification link is an HTML-only concept. 88 echo "[gout] Building gemini repo index" 89 find "${repos_root}/." -maxdepth 1 \ 90 -type d \ 91 -name "*.git" \ 92 -exec test -e "{}/git-daemon-export-ok" \; \ 93 -exec test ! -e "{}/gout-no-index" \; \ 94 -print \ 95 | sort -f \ 96 | sed -e 's/"/"\\""/g' -e 's/.*/"&"/' \ 97 | xargs gout_index -M \ 98 > "${gmi_root}/index.gmi" 99 100 # Rebuild the pages for the repo (including for unlisted repos). 101 echo "[gout] Building gemini pages" 102 gout -M -c "$cachefile" "$repo_dir" 103 104 # use log as index page: agate looks for index.gmi when a directory is 105 # requested, and answers "Directory index disabled" if there is none. 106 echo "[gout] Linking gemini index" 107 ln -sf log.gmi index.gmi 108 else 109 echo "[gout] Not building gemini pages: private repo" 110 fi