joyent-portal/bin/history-check
2016-10-17 17:56:01 +01:00

204 lines
3.7 KiB
Bash
Executable File

#! /usr/bin/env bash
#
# Prelude - make bash behave sanely
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
#
set -euo pipefail
IFS=$'\n\t'
#
# Globals
#
remember_git_start_and_end() {
HEAD="$(git rev-parse HEAD)"
ROOT="$(git log --pretty=format:%H | tail -n 1)"
}
#
# Utilities
#
die() {
local msg="$@"
[[ -z "${msg}" ]] || {
tput setaf 1 # red
tput bold
echo "${msg}"
tput sgr0 # reset
}
exit 1
}
error() {
local msg="$@"
echo -n '| '
tput setaf 1 # red
echo -n ' ✖'
tput sgr0 # reset
echo " ${msg}"
}
success() {
local msg="$@"
echo -n '| '
tput setaf 2 # green
echo -n ' ✓'
tput sgr0 # reset
echo " ${msg}"
}
log_commit() {
echo "$@"
}
# Check a command is present
ensure_command() {
local cmd="$1"
command -v "${cmd}" > /dev/null 2>&1 || {
die "Couldn't find required command: ${cmd}"
}
}
#
# Signal handling
#
cleanup() {
git reset --hard "${HEAD}" > /dev/null 2>&1
rm -f $$_commit_message
}
trap cleanup SIGHUP SIGINT SIGTERM
#
# Git helpers
#
# Go back one commit in history (first parent for merges)
step_back_one_commit() {
git reset --hard HEAD^ > /dev/null
log_commit "$(git rev-parse HEAD)"
}
current_commit_message() {
GIT_PAGER= git log --format=%B -n 1
}
current_commit_sha() {
git rev-parse HEAD
}
exit_if_not_git_repo() {
local gitroot="$(git rev-parse --show-toplevel 2> /dev/null)"
[[ "${gitroot}" == "" ]] && die 'Current directory is not in a repository'
return 0
}
#
# Checks
#
check_commit_message() {
local lineno=0
local length=0
local succeded=1
while read -r line ; do
let succeded=1
let lineno+=1
length=${#line}
[[ "${lineno}" -eq "1" ]] && {
[[ "${length}" -gt 50 ]] && {
error "Commit message: Subject line longer than 50 characters";
succeded=0
};
[[ ! "${line}" =~ ^[A-Z].*$ ]] && {
error "Commit message: Subject line not capitalised";
succeded=0
};
[[ "${line}" == *. ]] && {
error "Commit message: Subject line ended with a full stop";
succeded=0
};
}
[[ "${lineno}" -eq "2" ]] && [[ -n "${line}" ]] && {
error "Commit message: Subject line not separated by a blank line";
succeded=0;
};
[[ "${lineno}" -gt "1" ]] && [[ "${length}" -gt "72" ]] && {
error "Commit message: Body not wrapped at 72 characters";
succeded=0
};
done < $$_commit_message
[[ "${succeded}" -eq "1" ]] && success "Commit message"
return 0
}
run_checks() {
current_commit_message > $$_commit_message
check_commit_message
rm -f $$_commit_message
set +e
npm run lint > /dev/null 2>&1
if [[ "$?" -eq 0 ]]; then
success 'Lint'
else
error 'Lint: script did not exit successfully'
fi
npm test > /dev/null 2>&1
if [[ "$?" -eq 0 ]]; then
success 'Test'
else
error 'Test: script did not exit successfully'
fi
set -e
}
check_project() {
exit_if_not_git_repo
[[ -f './package.json' ]] || {
die 'This does not appear to be a node project'
}
[[ -z "$(json -f package.json 'scripts.lint')" ]] && {
die 'There is no lint script in the package.json'
}
[[ -z "$(json -f package.json 'scripts.test')" ]] && {
die 'There is no test script in the package.json'
}
return 0
}
traverse_history() {
while [[ "${ROOT}" != "$(current_commit_sha)" ]] ; do
run_checks
step_back_one_commit
done
}
#
# Main
#
ensure_command git
ensure_command tail
ensure_command npm
ensure_command json
check_project
remember_git_start_and_end
log_commit "HEAD: $(current_commit_sha)"
traverse_history
run_checks
log_commit "ROOT: $(current_commit_sha)"
cleanup
# vim: syntax=sh et ts=2 sts=2 sw=2