Diff options
Diffstat (limited to 'custom')
| -rwxr-xr-x | custom/hooks/post-receive.cgit-cache | 115 |
1 file changed, 104 insertions, 11 deletions
diff --git a/custom/hooks/post-receive.cgit-cache b/custom/hooks/post-receive.cgit-cache index b1d5c13..f1a4b40 100755 --- a/custom/hooks/post-receive.cgit-cache +++ b/custom/hooks/post-receive.cgit-cache @@ -1,14 +1,20 @@ #!/bin/sh -# Drops cgit's output cache after a push, so new commits show up right away +# 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 names each cache slot after a hash of the request url, so there is no -# way to expire one repository's pages on their own and this clears the lot. -# cgit refills a slot the next time something asks for it. The cached -# repository scan goes too, which is what makes a brand new repository appear -# without waiting out cache-scan-ttl. +# 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 <url>". 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 @@ -23,11 +29,98 @@ set -eu cache_root=/var/cache/cgit +scan_path=/var/lib/git +remove_suffix=0 test -d "$cache_root" || exit 0 -# Everything directly under the cache root is a cache file, so the only thing -# worth keeping is the lock files, which belong to writers still working. -# The "! -name . -prune" pair is how POSIX find stays out of subdirectories. -find "$cache_root"/. ! -name . -prune ! -name '*.lock' -type f \ - -exec rm -f {} + 2>/dev/null || 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 |
