#!/bin/sh # Drops the cached pages a push makes stale, so new commits show up right away # instead of once the cache-*-ttl minutes have passed. It only matters when # cache-size in cgitrc is above 0, which turns the cache on. # # cgit writes the request each slot answers at the head of the slot file, so # the hook reads it back and drops only this repository's pages and the # repository index, whose idle column the push changes. Other repositories # keep their cached pages. The cached repository scan goes too, which is what # makes a brand new repository appear without waiting out cache-scan-ttl. # # The hook needs the url cgit serves this repository at. A repository that # scan-path finds is served at its path below scan_path, as set below, so # match that and remove_suffix to cgitrc. One that cgitrc names with repo.url # gives its url with "git config hooks.cgiturl ". With no url to go on, # the whole cache goes, as the safe choice. # # The path below is the default cache-root, so a cgitrc that changes the # setting needs the same change here. The web server owns the cache root, so # the pushing user needs write access to it. Nothing here complains or exits # nonzero when that access is missing, so the pusher is not shown an error # over a stale page. # # To install the hook, copy or link it to hooks/post-receive in each # repository. Git runs one post-receive per repository, so fold this together # with the age-file hook to have both. set -eu cache_root=/var/cache/cgit scan_path=/var/lib/git remove_suffix=0 test -d "$cache_root" || exit 0 url=$(git config --get hooks.cgiturl || :) scan_root=$(cd "$scan_path" 2>/dev/null && pwd -P) || scan_root= if test -z "$url" && test -n "$scan_root"; then git_dir=$(cd "$(git rev-parse --git-dir)" && pwd -P) case $git_dir in "$scan_root"/?*) url=${git_dir#"$scan_root"/} url=${url%/.git} if test "$remove_suffix" = 1; then url=${url%.git} url=${url%/} fi ;; esac fi # Takes one part of a slot's key off the front of $rest into $part. cgit # writes each part behind its length and a bar, so no value can be read as # another. take() { len=${rest%%"|"*} case $len in "" | *[!0-9]*) return 1 ;; esac rest=${rest#*"|"} test "${#rest}" -ge "$len" || return 1 pattern= while test "${#pattern}" -lt "$len"; do pattern="$pattern?" done part=${rest%"${rest#$pattern}"} rest=${rest#$pattern} } # Whether the page a slot holds goes stale with this push: one of this # repository's, by path or by the url and r parameters, or the index. A key # that does not read as cgit's counts as stale, since dropping a page costs # only a refill. stale() { rest=$1 take && take && take || return 0 path=$part take || return 0 case $path in "/$url" | "/$url/"*) return 0 ;; "" | "/") ;; *) return 1 ;; esac case "&$part&" in *"&url=$url&"* | *"&url=$url/"* | *"&r=$url&"*) return 0 ;; *"&url=&"* | *"&url=/&"*) ;; *"&url="* | *"&r="*) return 1 ;; esac return 0 } # The lock files belong to writers still working, so they stay. for slot in "$cache_root"/*; do case $slot in *.lock) continue ;; esac test -f "$slot" || continue case $slot in */rc-*) rm -f "$slot" 2>/dev/null || : continue ;; esac if test -z "$url" || dd if="$slot" bs=4096 count=1 2>/dev/null | tr '\0' '\n' | { IFS= read -r key || :; stale "$key"; } then rm -f "$slot" 2>/dev/null || : fi done exit 0