Skip to content

Instantly share code, notes, and snippets.

@benekastah
Created May 12, 2020 19:34
Show Gist options
  • Save benekastah/072bbdab1e7b3e41ef6e1c5a4090d402 to your computer and use it in GitHub Desktop.
Save benekastah/072bbdab1e7b3e41ef6e1c5a4090d402 to your computer and use it in GitHub Desktop.
An automated pull request checklist
[alias]
# Pushes your branch to a remote branch of the same name
up = !git push -u origin "$(git rev-parse --abbrev-ref HEAD)"
# open a pull request, echos the url to the pr.
# Requires the `hub` command: https://github.com/github/hub.
pr = "!f(){ \
checklist -r .pr_checklist || exit 1; \
git up; \
hub pull-request -m \"$(git log -1 --pretty=format:'%B')\" --edit -b master -h \"$(git rev-parse --abbrev-ref HEAD)\"; \
}; f"
# This is a comment and will be ignored. Blank lines are also ignored.
# This can be placed in your repo directory, or in your home directory.
Did you review it?
Did you test it?
A final question?
#!/bin/bash
# See http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'
SCRIPT_NAME="$(basename $0)"
CHECKLIST_NAME=
REQUIRE_INTERACTIVE=1
REQUIRE_CHECKLIST=0
VERBOSE=0
usage() {
echo "Usage: $SCRIPT_NAME [--required|-r] [--noinput] [--help|-h] CHECKLIST" >&2
echo >&2
echo $'\t'"CHECKLIST"$'\t'"Required. The name of the checklist file. $SCRIPT_NAME will" >&2
echo $'\t\t\t'"search for this file in the current directory and up through" >&2
echo $'\t\t\t'"parent directories until it is found." >&2
echo $'\t'"--required|-r"$'\t'"In required mode, the command fails if no checklist is found" >&2
echo $'\t'"--noinput"$'\t'"Allows this command to be used in scripts" >&2
echo $'\t'"--help|-h"$'\t'"Prints this message and exits" >&2
exit 1
}
usage-error() {
echo "$1" >&2
usage
}
info-msg() {
if [[ "$VERBOSE" -gt 0 ]]; then
echo "$1" >&2
fi
}
yesno() {
read -p "$1 (y/n) " response
case "$response" in
y*|Y*) ;;
*) exit 1
esac
}
find-up() {
# https://unix.stackexchange.com/a/35265
# Arguments are the same as the find utility, but it searches up to the
# root dir instead of down into subdirectories.
curpath="$1"
shift 1
while [[ "$curpath" != / ]]; do
find "$curpath" -maxdepth 1 -mindepth 1 "$@"
# Try to use realpath first, falling back to readlink if needed
curpath="$(realpath -s "$curpath"/.. || readlink -f "$curpath"/..)"
done
}
# Get args
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--noinput)
REQUIRE_INTERACTIVE=0
shift
;;
--required|-r)
REQUIRE_CHECKLIST=1
shift
;;
--help|-h)
usage
;;
--verbose|-v)
VERBOSE=1
shift
;;
*)
if [ -z "$CHECKLIST_NAME" ]; then
CHECKLIST_NAME="$1"
shift
else
usage-error "Unknown argument"
fi
;;
esac
done
if [ -z "$CHECKLIST_NAME" ]; then
usage-error 'No checklist name specified'
fi
# This disallows using this checklist in a script
if [ ! -t 0 ] && [[ "$REQUIRE_INTERACTIVE" -gt 0 ]]; then
echo 'This script must be run interactively' >&2
exit 1
fi
CHECKLIST_FILE="$(find-up . -type f -name "$CHECKLIST_NAME" | head -n 1)"
if [ -z "$CHECKLIST_FILE" ]; then
msg='No checklist file found'
if [[ "$REQUIRE_CHECKLIST" -gt 0 ]]; then
echo "$msg" >&2
else
info-msg "$msg"
fi
exit "$REQUIRE_CHECKLIST"
else
info-msg "Found checklist file: $CHECKLIST_FILE"
fi
# Set up IFS to split input on newlines only
OLDIFS="$IFS"; IFS=$'\n'
for item in $(cat "$CHECKLIST_FILE"); do
item="$(echo -n "$item")"
if [ -n "$item" ] && [[ "$item" != '#'* ]]; then
yesno "$item"
fi
done
# Put IFS back to the previous setting
IFS="$OLDIFS"
@benekastah
Copy link
Author

benekastah commented May 12, 2020

  • checklist needs to go in a directory somewhere in your $PATH.
  • .gitconfig can be found in each git repository, or at ~/.gitconfig for global config (that's what I use).
  • .pr_checklist can go in your home directory (to make that checklist global), or in a specific repository (applies only to that repo).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment