Download
77 lines 3.1 KiBBashModified 2026-10-28executable raw
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
#!/bin/sh
# post-receive hook for Repo Renderer. contrib/new-repo links each
# repository's hooks/post-receive to this one file.
#
# A push does two things, each only if the repository's git config asks
# for it:
#
#   repo-render.config   the renderer's config: queue a job for each branch
#                        and tag pushed, and start repo-render kick, which
#                        renders them
#   repo-render.program  the repo-render to run (default: repo-render on
#                        PATH)
#   repo-render.mirror   a git URL to push to, one per mirror: the default
#                        branch, forced, and every tag. Never --mirror,
#                        which would delete what only the mirror has.
#                        ssh runs as core.sshCommand says, if set, and
#                        otherwise never prompts, gives up on a
#                        connection that hangs, and checks host keys
#                        against ssh's known_hosts files.
#
# It must return in milliseconds, so the render and the mirror pushes run
# in the background. Nothing from the client is trusted except ref names
# and SHAs, which enqueue and the renderer both validate. Mirror failures
# go to syslog (logger -t repo-render-mirror), and the next push retries.

# The repository's name comes from its directory on disk, which the
# client can't influence.
repo=$(basename "$(cd "${GIT_DIR:-.}" && pwd -P)" .git)
case "$repo" in
	""|.*|*[!A-Za-z0-9._-]*) exit 0 ;;
esac

# Run in the background below, as "post-receive mirror".
if [ "${1-}" = mirror ]; then
	branch=$(git symbolic-ref --short HEAD) || exit 1
	if [ -z "$(git config core.sshCommand)" ]; then
		GIT_SSH_COMMAND="ssh -o BatchMode=yes -o StrictHostKeyChecking=yes -o ConnectTimeout=30 -o ServerAliveInterval=15 -o ServerAliveCountMax=4"
		export GIT_SSH_COMMAND
	fi
	git config --get-all repo-render.mirror | while read -r url; do
		if git push --no-verify --quiet -- "$url" "+refs/heads/$branch:refs/heads/$branch" "refs/tags/*:refs/tags/*" </dev/null 2>&1; then
			echo "$repo: mirrored to $url"
		else
			echo "$repo: mirror to $url failed (the next push retries)"
		fi
	done
	exit 0
fi

program=$(git config repo-render.program) || program="repo-render"
config=$(git config repo-render.config) || config=""

pushed=""
while read -r old new ref; do
	case "$ref" in
		refs/heads/*|refs/tags/*)
			pushed=1
			# enqueue locks against the renderer claiming the queue, so
			# no job is lost to a push that races a running kick.
			if [ -n "$config" ]; then
				"$program" --config "$config" enqueue "$repo" "$ref" "$old" "$new"
			fi
			;;
	esac
done
[ -n "$pushed" ] || exit 0

if [ -n "$config" ]; then
	nohup "$program" --config "$config" kick </dev/null >/dev/null 2>&1 &
fi
if git config --get-all repo-render.mirror >/dev/null; then
	# nohup on the shell, so that both ends of the pipe ignore hangups.
	# shellcheck disable=SC2016 # $0 expands in that shell
	nohup sh -c 'if command -v logger >/dev/null 2>&1; then "$0" mirror 2>&1 | logger -t repo-render-mirror; else "$0" mirror; fi' "$0" \
		</dev/null >/dev/null 2>&1 &
fi
exit 0