go-utils/sunrise-commit
2010-07-12 12:18:42 -04:00

380 lines
8.3 KiB
Bash
Executable File

#!/bin/sh
# sunrise-commit -- a helper script for Sunrise commiters.
# (c) 2010 Michał Górny <gentoo@mgorny.alt.pl>
# Released under the terms of the 3-clause BSD license.
# Few output helpers.
say() {
echo "${@}" >&2
}
die() {
say "${RED}${@}${RESET}"
exit 1
}
sayv() {
[ -n "${SC_VERBOSE}" ] && say "${GREEN}${@}${RESET}"
}
req() {
"$@" || die "$@ failed."
}
# POSIX compat.
local_supported() {
local test 2>/dev/null
}
local_supported || eval 'local() {
unset ${@}
}'
# See if we're in a repo, and what VCS are we using.
find_repo() {
svn info >/dev/null 2>&1
if [ ${?} -eq 0 ]; then
SC_VCS=svn
else
local remotes
remotes=$(git branch -r 2>/dev/null)
if [ ${?} -ne 127 -a ${?} -ne 128 ]; then
echo "${remotes}" | grep git-svn >/dev/null 2>&1
if [ ${?} -eq 0 ]; then
SC_VCS=git-svn
else
SC_VCS=git
fi
else
die 'Unable to find any familiar repository type (are you inside the repo?).'
fi
fi
sayv "Ok, we're in the ${SC_VCS} working tree. Let's see what I can do around here..."
}
is_whole_dir_removed() {
if [ ${SC_VCS} = svn ]; then
local flist
flist=$(find "${1}" \( -name '.svn' -prune -o ! -name "${1}" -print \))
[ -z "${flist}" ]
elif [ ${SC_VCS%-svn} = git ]; then
[ ! -e "${1}" ]
fi
}
# Check whether we're having a clean package removal.
is_package_removal() {
local fields list
[ -d profiles ] && fields=1-2 || fields=1
if [ ${SC_VCS%-svn} = git ]; then
list=$(git diff-index --relative --name-only --diff-filter=D HEAD)
elif [ ${SC_VCS} = svn ]; then
list=$(svn status -q | sed -n -e 's/^D //p')
fi
list=$(echo "${list}" | cut -d / -f ${fields} | sort | uniq)
# 1) We have to have any removes.
[ -z "${list}" ] && return 1
# Few more checks.
local dir olist
for dir in ${list}; do
# 2) These removes have to remove whole directories.
is_whole_dir_removed ${dir} && olist=${olist+${olist} }${dir}
done
[ -z "${olist}" ] && return 1
SC_CHANGE_LIST=${olist}
return 0
}
# Look around for ebuilds.
find_ebuilds() {
# POSIX is fun -- look for ebuilds in the current directory.
if [ -n "$(find \( -name '*.ebuild' -print -o ! -name '.' -prune \))" ]; then
local stripped
# Get CATEGORY and PN.
stripped=${PWD%/*}
stripped=${stripped%/*}
SC_CP=${PWD#${stripped}/}
SC_SCENARIO=ebuild-commit
sayv "We found ebuilds for ${SC_CP} here."
elif is_package_removal; then
local cplist category pkg rootprefix
# We can either have the category on the list or in PWD.
if [ -d profiles ]; then
category=
else
local stripped
stripped=${PWD%/*}
category=${PWD#${stripped}/}/
fi
SC_CP=
# Now we can have multiple packages around.
for pkg in ${SC_CHANGE_LIST}; do
if [ -z "${category}" ]; then
case ${pkg} in
eclass/*|licenses/*|local/*|profiles/*|scripts/*)
continue
;;
esac
fi
SC_CP=${SC_CP:+${SC_CP}, }${category}${pkg}
done
local root sdir
root=${category:+../}
for sdir in eclass licenses profiles; do
check_for_changes ${root}${sdir} >/dev/null && SC_CHANGE_LIST="${SC_CHANGE_LIST} ${root}${sdir}"
done
SC_SCENARIO=package-removal
sayv "We're removing ${SC_CP}."
else
die 'No familiar scenario found.'
fi
}
# VCS helpers.
check_for_changes() {
local output
if [ ${SC_VCS%-svn} = git ]; then
output=$(git diff-index --name-only --relative HEAD "$@")
elif [ ${SC_VCS} = svn ]; then
output=$(svn status "$@")
fi
[ -z "${output}" ] && return 1
# We do not care about user mangling ChangeLog, we will reset it anyway.
echo "${output}" | grep -v ChangeLog >/dev/null
}
vcs_reset() {
if [ ${SC_VCS%-svn} = git ]; then
req git reset -q HEAD "${1}"
git checkout -f "${1}" 2>/dev/null || req rm -f ChangeLog
elif [ ${SC_VCS} = svn ]; then
req rm -f ChangeLog
svn up ChangeLog >/dev/null 2>&1
fi
}
vcs_status() {
if [ ${SC_VCS%-svn} = git ]; then
git status -s ${1-.} "${@}"
elif [ ${SC_VCS} = svn ]; then
svn status "${@}"
fi
}
vcs_add() {
${SC_VCS%-svn} add "$@"
}
vcs_commit() {
local msg
msg=${1}
shift
if [ ${SC_VCS%-svn} = git ]; then
exec git commit -m "${msg}" ${1+-o} "${@}"
elif [ ${SC_VCS} = svn ]; then
exec svn commit -m "${msg}" "${@}"
fi
}
print_help() {
cat <<_EOH_
Synopsis:
sunrise-commit [options] [--] <commit message>
Options:
-?, -h, --help print this message,
-V, --version print version string,
-c, --changelog backwards compat (ignored),
-C, --nocolor disable colorful output,
-f, --force force repoman to proceed with the commit,
-t, --trivial trivial changes (do not add a ChangeLog entry),
-v, --verbose enable verbose output.
_EOH_
}
confirm() {
while true; do
local answ
printf '%s' "${WHITE}Commit changes?${RESET} [${BGREEN}Yes${RESET}/${RED}No${RESET}] ${GREEN}" >&2
read answ
printf '%s' "${RESET}"
case "${answ}" in
[yY]|[yY][eE]|[yY][eE][sS])
break
;;
[nN]|[nN][oO])
die 'Aborting.'
;;
*)
say "Your response '${answ}' not understood, try again."
esac
done
}
# Guess what!
main() {
local commitmsg force monochrome trivial
unset SC_VERBOSE
while [ ${#} -gt 0 ]; do
case "${1}" in
--help|-\?|-h)
print_help
exit 0
;;
--version|-V)
echo 'sunrise-commit 0.3'
exit 0
;;
-c|--changelog)
# Now changelog entries are implicit -- backwards compat.
;;
-C|--nocolor)
monochrome=1
;;
-f|--force)
force=1
;;
-t|--trivial)
trivial=1
;;
-v|--verbose)
SC_VERBOSE=1
;;
--)
shift
commitmsg=${commitmsg+${commitmsg} }${@}
break
;;
-*)
die "Unknown option: ${1}; see --help." >&2
;;
*)
commitmsg=${commitmsg+${commitmsg} }${1}
;;
esac
shift
done
if [ -n "${monochrome}" ]; then
RESET=
RED=
GREEN=
BGREEN=
YELLOW=
WHITE=
else
local esc
esc=$(printf '\033[')
RESET=${esc}0m
RED="${esc}1;31m"
GREEN=${esc}32m
BGREEN="${esc}1;32m"
YELLOW="${esc}1;33m"
WHITE="${esc}1;37m"
fi
[ -n "${commitmsg}" ] || die 'No commit message provided.'
find_repo
find_ebuilds
case ${SC_SCENARIO} in
ebuild-commit)
check_for_changes || die 'No changes found to commit.'
# With native git repos, we do not do ChangeLogs by default...
# ...at least unless they're already there.
if [ ${SC_VCS} != git -o -f ChangeLog ]; then
sayv 'Cleaning up the ChangeLog...'
vcs_reset ChangeLog
local bns
# Let's take a lucky guess bugnumbers consist of 4+ digits.
bns=$(echo "${commitmsg}" | grep -o -E '[0-9]{4,}')
# Creating a new ChangeLog? Let's take a look at the commit message.
if [ ! -f ChangeLog ]; then
[ -n "${trivial}" ] && die "Trivial change for an initial commit? You're joking, right?"
[ -z "${bns}" ] && die 'Please supply the bug number in the initial commit message!'
if [ ! -f metadata.xml ]; then
req cp ../../skel.metadata.xml metadata.xml
# Output similar to echangelog.
diff -dup /dev/null metadata.xml
req vcs_add metadata.xml
fi
fi
if [ -z "${trivial}" ]; then
sayv '...and appending to it.'
echangelog "${commitmsg}" || die 'Please correct the problems shown by echangelog.'
echo
fi
if [ -n "${bns}" ]; then
local bn cbn
for bn in ${bns}; do
cbn=#${WHITE}${bn}${NORMAL}
wget -q http://bugs.gentoo.org/show_bug.cgi?id=${bn} -O - | sed -n \
-e "s, *<title>Gentoo Bug \([0-9]*\) - \(.*\)</title>,Bug ${cbn}: ${BGREEN}\2${RESET},p" \
-e "s, *<title>Gentoo \(Invalid Bug ID\)</title>,Bug ${cbn}: ${YELLOW}!! \1${RESET},p"
done
echo
fi
fi
vcs_status
echo
# Do we have repoman new enough?
local old_repoman
repoman --version -a >/dev/null 2>&1
if [ $? -eq 2 ]; then
old_repoman=
#say "${GREEN}Please consider updating portage to newer version.${RESET}"
#say
req repoman manifest
if ! repoman full; then
[ -n "${force}" ] || die 'Please correct the problems shown by repoman.'
fi
confirm
fi
sayv "Now, let's let repoman do its job..."
exec repoman commit ${old_repoman--a} ${force+-f} -m "${SC_CP}: ${commitmsg}"
;;
package-removal)
vcs_status ${SC_CHANGE_LIST}
echo
say "Ready to commit ${WHITE}$(echo ${SC_CHANGE_LIST} | wc -w)${RESET} package removal(s), with commit message:"
say "${BGREEN}${SC_CP}: ${commitmsg}${RESET}"
confirm
vcs_commit "${SC_CP}: ${commitmsg}" ${SC_CHANGE_LIST}
;;
esac
}
main "${@}"