putnam hill moments a place for my stuff

pre-push

a pre-push git hook that examines files being pushed and aborts by exitting non-zero if necessary
#!/usr/bin/env bash

# A local hook script to detect if a rendered jekyll draft is about to be pushed.
#
# Called by "git push" after it has checked the remote status, but before anything
# has been pushed. If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> <local oid> <remote ref> <remote oid>

# print meta data
meta_name_value_pairs() {
	local f="$1" elem val IFS=$' \t\n>/'
	while read -d '<' elem val; do
		test "$elem" == '/head' && return
		test "$elem" == 'meta' && {
			echo $val  # echo without quotes to strip $IFS characters
		}
	done < "$f"
}

# look for name="jekyll-type" draft in the meta content
jekyll_draft_found() {
	local file content
	if [ $# -gt 0 ]; then # process args on the commandline
		while [ $# -gt 0 ]; do
			file="$1"
			shift
			while read content; do
				[[ "$content" =~ ^name\s*=\s*\"jekyll-type\" ]] && {
					content="${content##*=}"
					content="${content//\"/}"
					test "$content" == 'draft' && \
						echo "$file is a draft" && return 0
				}
			done < <(meta_name_value_pairs "$file")
		done
	fi
	return 1
}

while read local_ref local_oid remote_ref remote_oid; do
	declare -A tags
	declare -a missing_tag_files

	while read -r file; do
		case "$file" in
			_site/*.html)
				# check for rendered drafts that are about to be pushed
				jekyll_draft_found "$file" && {
					echo -n 'Continue to push rendered draft(s)? '
					read answer < /dev/tty
					[[ "$answer" = [Nn]* ]] && exit 1 || echo 'Pushing drafts'
				}
				;;
			*.md)
				# collect all tags from front matter and store as
				# a slugified key in the bash associative array
				while read -r tag; do
					tags["$tag"]=1
				done < <(collections/_bin/_get-tags "$file")
				;;
		esac
	done < <(git diff --name-only $local_oid $remote_oid)

	# make sure each tag has a tags/ page
	for tag in "${!tags[@]}"; do
		test -e "collections/_tag/$tag.md" || missing_tag_files+=("$tag")
	done
	test ${#missing_tag_files[@]} -gt 0 && {
		echo -n 'missing tag file'
		test ${#missing_tag_files[@]} -gt 1 && echo -n 's'
		echo ':'
		for tag in "${missing_tag_files[@]}"; do
			echo -e "\ttags/$tag/index.html"
		done
		echo -n 'Create missing tag file'
		test ${#missing_tag_files[@]} -gt 1 && echo -n 's'
		echo -n '? '
		read answer < /dev/tty
		[[ "$answer" = [Yy]* ]] && collections/_bin/_gen-tags "${missing_tag_files[@]}"
		exit 1
	}
done

exit 0