#!/bin/bash # sunrise-commit - Automates the Gentoo Sunrise Overlay commit process # Released into the public domain source /sbin/functions.sh BLUE=$BRACKET BOLD=$'\e[0;01m' DARKGREEN=$'\e[32m' GREEN=$GOOD LIGHTBLUE=$HILITE RED=$BAD YELLOW=$WARN commit_category="$(pwd | awk -F/ '{ print $(NF-1) }')" commit_package="$(pwd | awk -F/ '{ print $NF }')" current_svn_status=( ) cwd_is_ebuild_dir=0 items_added=( ) items_conflicted=( ) items_deleted=( ) items_ignored=( ) items_missing=( ) items_modified=( ) items_not_modified=( ) items_not_version_controlled=( ) items_obstructed=( ) items_replaced=( ) items_used_by_externals=( ) num_new_dirs=0 opt_changelog=0 opt_noformat=0 opt_norepoman=0 opt_noupdate=0 opt_quiet=0 opt_verbose=0 changelog_append() { if [[ $opt_changelog == 1 ]] ; then get_current_svn_status if [[ "${current_svn_status[*]}" =~ 'ChangeLog' ]] ; then echo "!!! Error: Only one ChangeLog entry should be made per commit, and you have" echo "!!! already modified your ChangeLog locally since your last commit. To keep the" echo "!!! pre-existing modifications please run sunrise-commit again without the -c" echo "!!! option. To discard the pre-existing modifications run:" echo "!!! svn revert ChangeLog" echo "!!!" echo -n "!!! " echo -n "${BOLD}Are you SURE you want to append to ChangeLog?${NORMAL} [${GREEN}No${NORMAL}/${RED}Yes${NORMAL}] " read choice echo case "$choice" in yes|Yes|YES) ;; n*|N*|"") echo "Aborting. Run sunrise-commit without the -c this time around." exit 1 ;; *) echo "Unrecognized response - assuming no." echo "Aborting. Run sunrise-commit without the -c this time around." exit 1 ;; esac fi ebegin "Appending/creating ChangeLog" [ -e $(dirname $0)/echangelog ] && ec="$(dirname $0)/echangelog" || ec="echangelog" $ec "$*" eend $? fi } create_digests() { if [[ $cwd_is_ebuild_dir == 1 ]] ; then ebegin "Digesting ebuilds" for i in *.ebuild ; do ebuild $i digest done eend $? fi } # Sort current changed items into arrays based on symbols in `svn status` # output. For now we're only concerned with the symbols in the first column of # the output. See `svn help status` for symbol definitions. # # Returns with exit status 1 if the current dir isn't under version control. get_current_svn_status() { local IFS_SAVED="$IFS" local item column_1 #column_2 column_3 column_4 column_5 column_6 IFS=$'\n' current_svn_status=( ) items_added=( ) items_conflicted=( ) items_deleted=( ) items_ignored=( ) items_missing=( ) items_modified=( ) items_not_modified=( ) items_not_version_controlled=( ) items_obstructed=( ) items_replaced=( ) items_used_by_externals=( ) for line in $(svn status 2>&1) ; do [[ "$line" =~ 'is not a working copy' ]] && return 1 current_svn_status[${#current_svn_status[*]}]=$line column_1=${line:0:1} #column_2=${line:1:1} #column_3=${line:2:1} #column_4=${line:3:1} #column_5=${line:4:1} #column_6=${line:5:1} item=${line:7} case $column_1 in ' ') items_not_modified[${#items_not_modified[*]}]=$item ;; A) items_added[${#items_added[*]}]=$item ;; C) items_conflicted[${#items_conflicted[*]}]=$item ;; D) items_deleted[${#items_deleted[*]}]=$item ;; I) items_ignored[${#items_ignored[*]}]=$item ;; M) items_modified[${#items_modified[*]}]=$item ;; R) items_replaced[${#items_replaced[*]}]=$item ;; X) items_used_by_externals[${#items_used_by_externals[*]}]=$item ;; !) items_missing[${#items_missing[*]}]=$item ;; ?) items_not_version_controlled[${#items_not_version_controlled[*]}]=$item ;; ~) items_obstructed[${#items_obstructed[*]}]=$item ;; esac done IFS="$IFS_SAVED" } repoman_check() { if [[ $opt_norepoman == 0 ]] ; then if [[ $cwd_is_ebuild_dir == 1 ]] ; then ebegin "Running repoman" export PORTDIR_OVERLAY="$(dirname $(dirname $(pwd)))" repoman eend $? fi fi } svn_add() { local num_unversioned_dirs=0 while ! get_current_svn_status ; do (( num_unversioned_dirs++ )) pushd .. >/dev/null done ebegin "Adding local changes to working copy" if [[ ${#items_not_version_controlled[*]} > 0 ]] ; then if [[ $opt_verbose == 1 ]] ; then svn add "${items_not_version_controlled[@]}" || set $? else svn add -q "${items_not_version_controlled[@]}" || set $? fi fi eend ${1:-0} num_new_dirs=$num_unversioned_dirs for (( i=num_unversioned_dirs ; i > 0 ; i-- )) ; do popd >/dev/null done return ${1:-0} } svn_commit() { local commit_message="$*" get_current_svn_status if [[ ${#current_svn_status[*]} == 0 ]] ; then echo "!!! Error: No working copy changes found in current directory. Aborting commit." exit 1 fi if [[ $opt_noformat == 0 ]] ; then if [[ $cwd_is_ebuild_dir == 1 ]] ; then commit_message="${commit_category}/${commit_package}: ${commit_message}" else commit_message="${commit_package}/${current_svn_status[0]:7}: ${commit_message}" fi fi for (( i=num_new_dirs ; i > 0 ; i-- )) ; do cd .. done echo echo "${DARKGREEN}The following local changes will be committed to the repository:${NORMAL}" echo get_current_svn_status for item in "${current_svn_status[@]}" ; do echo "$item" done echo echo "${DARKGREEN}The following commit message will be used:${NORMAL}" echo echo "$commit_message" if [[ $opt_quiet == 0 ]] ; then echo echo -n "${BOLD}Commit changes?${NORMAL} [${GREEN}Yes${NORMAL}/${RED}No${NORMAL}] " read choice echo case "$choice" in y*|Y*|"") ;; *) echo "Quitting." echo return 1 ;; esac fi ebegin "Committing working copy to repository" svn commit -m "$commit_message" eend $? } svn_up() { if [[ $opt_noupdate == 0 ]] ; then for (( i=num_new_dirs ; i > 0 ; i-- )) ; do pushd .. >/dev/null done ebegin "Updating working copy to latest version from repository" if [[ $opt_verbose == 1 ]] ; then svn update || set $? else svn update -q || set $? fi eend ${1:-0} for (( i=num_new_dirs ; i > 0 ; i-- )) ; do popd >/dev/null done get_current_svn_status if [[ ${#items_conflicted[*]} > 0 ]] ; then echo "!!! Error: Some local items have changes that conflict with the repository." echo "!!! Please contact the most recent committer(s) of these items and resolve the" echo "!!! conflicts manually before running sunrise-commit again:" for item in "${items_conflicted[@]}" ; do echo "!!!" echo "!!! item: ${item}" echo "!!! committer: $(svn info ${item} | sed -rn 's/Last Changed Author\: (.*)$/\1/p')" done exit 1 fi fi return ${1:-0} } usage() { cat << EOF ${BOLD}Usage:${NORMAL} ${LIGHTBLUE}sunrise-commit${NORMAL} [ ${GREEN}options${NORMAL} ] ${BLUE}message${NORMAL} ${GREEN}options${NORMAL}: ${BOLD}--changelog, -c${NORMAL} Create a ChangeLog entry using ${BLUE}message${NORMAL} ${BOLD}--help, -h${NORMAL} Show help ${BOLD}--noformat, -m${NORMAL} Disable automatic formatting of ${BLUE}message${NORMAL} ${BOLD}--norepoman, -p${NORMAL} Skip repoman check ${BOLD}--noupdate, -d${NORMAL} Don't update from repository before committing ${BOLD}--quiet, -q${NORMAL} Don't ask for confirmation ${BOLD}--verbose, -v${NORMAL} Show detailed information during commit ${BLUE}message${NORMAL}: Commit message describing changes and listing names/emails of anyone (other than the commiter) who contributed. EOF exit ${1:-0} } [[ -z "$1" ]] && usage 1 while [[ $# > 0 ]] ; do case "$1" in --changelog|-c) if [[ -z "$ECHANGELOG_USER" ]] ; then echo "!!! Error: --changelog option requires ECHANGELOG_USER to be set:" echo "!!! export ECHANGELOG_USER=\"Your Name \"" exit 1 fi opt_changelog=1 shift ;; --help|-h) usage ;; --noformat|-m) opt_noformat=1 shift ;; --norepoman|-p) opt_norepoman=1 shift ;; --noupdate|-d) opt_noupdate=1 shift ;; --quiet|-q) opt_quiet=1 shift ;; --verbose|-v) opt_verbose=1 shift ;; -*) echo "!!! Error: Unknown option ${1}. See: sunrise-commit -h" exit 1 ;; *) break ;; esac done if [[ -z "$*" ]] ; then echo "!!! Error: You must supply a commit message. See: sunrise-commit -h" exit 1 fi [[ "$(ls)" =~ '\.ebuild' ]] && cwd_is_ebuild_dir=1 pushd . >/dev/null while [[ "$(echo `svn status 2>&1`)" =~ 'A.+? \.' ]] ; do (( num_new_dirs++ )) cd .. done popd >/dev/null svn_up || exit $? [[ $cwd_is_ebuild_dir == 1 && ! -e metadata.xml ]] && cp ../../skel.metadata.xml metadata.xml >/dev/null 2>&1 svn_add || exit $? changelog_append "$*" || exit $? create_digests || exit $? svn_add || exit $? repoman_check || exit $? svn_commit "$*" || exit $?