Changeset 462 for trunk


Ignore:
Timestamp:
10/10/2009 01:12:56 (3 years ago)
Author:
ccowart
Message:

Reworked the way in which confman handles opening an EDITOR for
recipe editing and log message capturing.

This implements a reasonable template for recipe files providing better
context clues to the user. See #83.

This also allows recipes to be created non-interactively. After adding
the -R flag to confman recipe create, it became obvious that unless
the svn logs could also be captured with something like -m or -F
(similar to actual svn client behavior), that things wouldn't be totally
non-interactive. A lot of rewriting of log handling has happened, touching
both the install and commit functions in addition to the recipe editor.
See #84.

Due to the large extent of these changes, let's make sure it gets a good
round of testing in several environments before we clear it to be merged
into the 1.9 branch.

The referenced tickets will remain open until the merge has happened.

Location:
trunk
Files:
1 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Makefile.am

    r457 r462  
    1515sysconf_DATA = confman.conf.sample 
    1616sysconfdefaults_DATA = confman.conf 
     17dist_sysconfdefaults_DATA = recipe.template 
    1718bashcomp_DATA = confman_completion 
    1819man_MANS = confman.8 confman.conf.5 
  • trunk/confman.8.in

    r460 r462  
    3636.Op Fl v | Cm version 
    3737.Nm 
    38 .Ar command Oo Ar command_opts Oc Op Ar command_args 
     38.Oo Ar flags Oc Ar command Oo Ar command_opts Oc Op Ar command_args 
    3939.Sh DESCRIPTION 
    4040The 
     
    9696operations can bring the system up-to-date. 
    9797.El 
    98 .\" TODO 
     98.Ss FLAGS 
     99The following flags can be specified before the commands: 
     100.Bl -tag -width indent 
     101.It Fl h Op Ar command  
     102Synonymous to the 
     103.Ar help 
     104command below. 
     105.It Fl v 
     106Causes 
     107.Nm 
     108to print the version string and exit. 
     109.It Fl m Ar message 
     110The string in the 
     111.Ar message 
     112argument will be used instead of prompting the user for a log message for 
     113.Cm commit 
     114and 
     115.Cm install 
     116operations. 
     117.It Fl F Ar filename 
     118Like 
     119.Fl m , 
     120except the log's contents are read from 
     121.Ar filename 
     122instead of the command line. 
     123.El 
    99124.Ss COMMANDS 
    100125The following commands are supported by  
     
    184209.Brq Cm get | list 
    185210.It Cm recipe  
    186 .Brq Cm create | edit | print | remove | set  
     211.Brq Cm create [ Fl R Ar filename ] | Cm edit | print | remove | set  
    187212.Ar recipe  
    188213.Bl -tag -width indent 
    189214.It Cm create 
    190215Create a recipe named  
    191 .Ar recipe 
     216.Ar recipe.  
     217Use -R to specify a file with the recipe's contents to bypass interactive  
     218mode. 
    192219.It Cm edit 
    193220Open 
  • trunk/confman.conf.5.in

    r438 r462  
    137137.It CONF_WARNING 
    138138This is the first line of confman banners for managed files. 
     139.It LOG_TEMPLATE 
     140If defined, the contents of this file will be the default log message visible 
     141in a user's editor for repository commits. This can be useful if you use 
     142hooks to log commits into an external issue tracker and you want to remind 
     143your users to fill in the ticket number. 
     144.It RECIPE_TEMPLATE 
     145When new recipes are created, the contents of this file will be used. The 
     146string  
     147.Ar __NAME__  
     148will be replaced with the name of the recipe. This defaults to  
     149@sysconfdefaultsdir@/recipe.template.  
    139150.It DEFAULT_MODE_DIRECTORY 
    140151Defaults to "0555". When new directories are created in 
  • trunk/confman.conf.in

    r435 r462  
    4545# The template for log messages 
    4646LOG_TEMPLATE="/dev/null" 
     47 
     48# The template for new recipes 
     49RECIPE_TEMPLATE="@sysconfdefaultsdir@/recipe.template" 
    4750 
    4851# The warning message for all files 
  • trunk/confman.in

    r454 r462  
    111111} 
    112112 
     113# Either by prompting or respecting -m/-F, returns a filename on stdout 
     114# with a log message in it 
     115# $1 contains a file with the output of svn status to display in interactive 
     116# log messages 
     117#   Returns 1 if an abort was requested by the user 
     118#   Returns 0 if the returned filename is good for a commit 
     119function get_log { 
     120    local status="$1" 
     121    local finished=false 
     122    local ignoreline="--This line, and those below, will be ignored--" 
     123    local orig_log logfile 
     124 
     125    # local seems to return true, overwriting the return of the nested 
     126    # call for && evaluation 
     127    logfile=$(conf_log_message) && finished=true 
     128 
     129    if $finished ; then 
     130        echo "$logfile" 
     131        return 0 
     132    fi 
     133 
     134    orig_log=$(conf_tmp_file) 
     135    printf "\n$ignoreline\n" >> "$logfile" 
     136    cat "$status" >> "$logfile" 
     137    cp "$logfile" "$orig_log" 
     138 
     139    # I apologize for the redirect, but stdout is being written into a 
     140    # variable, and at least vim (probably other editors too) gets cranky when 
     141    # stdout doesn't go to the user. Luckily, the fork in the common section 
     142    # leaves a copy of the original stdout in fd 3. 
     143    ${EDITOR} "$logfile" >&3 
     144 
     145    while diff "$logfile" "$orig_log" >/dev/null 2>&1 ; do 
     146        echo "Log message unchanged. (E)dit / (a)bort / (c)ontinue?" 
     147        read response 
     148        case "$response" in 
     149            [cC]*)      break;; 
     150            [aA]*)      return 1;; 
     151            ""|[eE]*)   ${EDITOR} "$logfile";; 
     152        esac 
     153    done 
     154    rm -f "$orig_log" 
     155    ${sed_cmd} -i '' -e "/${ignoreline}/,\$d" "$logfile" 2>/dev/null 
     156    echo "$logfile" 
     157    return 0 
     158} 
     159 
     160# Arguments are full paths to files within $WORK_PATH to check via svn status. 
     161#   Returns 1 if the files are unchanged 
     162#   Returns 0 if the files are changed and prints a filename to stdout 
     163#     with status info suitable for log message footers 
     164function get_status { 
     165    local status=$(conf_tmp_file) 
     166    local ABS_WORK=$(${readlink_cmd} -m "$WORK_PATH") 
     167 
     168    # Using a subshell to preserve CWD 
     169    ( 
     170        cd "$WORK_PATH" 
     171        until [ -z "$1" ] ; do 
     172            @SVN@ status "${1#${ABS_WORK}/}" >> "$status" 
     173            shift 
     174        done 
     175    ) 
     176 
     177    if [ $(wc -l < "$status") -eq 0 ] ; then 
     178        rm -f "$status" 
     179        return 1 
     180    fi 
     181 
     182    echo "$status" 
     183    return 0 
     184} 
     185 
    113186function commit { 
    114187    if [ -z "$*" ] ; then 
     
    116189        conf_lock_system 
    117190        conf_lock_wcopy 
    118         local msg=$(conf_tmp_file) 
    119         local orig_msg=$(conf_tmp_file) 
    120         local status=$(conf_tmp_file) 
    121         local modules=$(conf_get_recipe | tr '\n' ',')  
    122         local ignoreline="--This line, and those below, will be ignored--" 
    123         # Moved up per Ian's request. 
     191        local msg status modules module modpath 
     192        local nocommit=true 
     193 
     194        for module in $(conf_get_recipe) ; do 
     195            modpath=$(${readlink_cmd} -m ${WORK_PATH}/${module}) 
     196            modules=${modules:+${modules} }${modpath} 
     197        done 
     198 
    124199        $SUDO ${SUDO:+-v} 
    125         cat $LOG_TEMPLATE > $msg 
    126         printf "\n${ignoreline}\n" >> $msg 
    127         conf_get_recipe | tr ' ' '\n' | xargs -I % @SVN@ status \ 
    128             ${WORK_PATH}/% >> $status 
    129                 cat $status >> $msg 
    130         cp $msg $orig_msg 
    131                 if [ $(wc -l < $status) -gt 0 ] ; then 
    132                 ${EDITOR} $msg 
    133             if diff $msg $orig_msg >/dev/null 2>&1 ; then 
    134                 echo "Log message unchanged. (a)bort / (C)ontinue?" 
    135                 read response 
    136                 if [ "${response}" = "a" ] ; then 
    137                     conf_unlock_wcopy 
    138                     conf_unlock_system 
    139                     return 1 
    140                 fi 
    141             fi 
    142                 fi 
    143                 rm -f $status $orig_msg 
    144         ${sed_cmd} -i '' -e '/'"$ignoreline"'/,$d' $msg 2>/dev/null 
     200        conf_update_tree || conf_cleanExit 
     201 
     202        if status=$(get_status $modules) ; then 
     203            nocommit=false 
     204            msg=$(get_log "$status") || conf_cleanExit 
     205            rm -f "$status" 
     206        fi 
     207 
    145208        $SUDO ${SUDO:+-v} 
    146209 
    147         #Changed this from just update to fix locking issue - arjun 
    148         conf_update_tree || conf_cleanExit 
    149  
    150210        echo "Commit operation started" >&2 
    151         conf_commit "$(conf_get_recipe)" $msg || return $? 
     211        $nocommit || conf_commit "$(conf_get_recipe)" $msg || return $? 
    152212        for layer in $(conf_get_recipe) ; do 
    153213            echo "Rolling on $layer..." 
     
    178238        conf_lock_wcopy 
    179239        conf_lock_system 
    180         local msg=$(conf_tmp_file) 
    181         local orig_msg=$(conf_tmp_file) 
    182         local status=$(conf_tmp_file) 
    183         local modules=$(conf_get_recipe | tr '\n' ',') 
    184         local ignoreline="---Everything after this line will be ignored---" 
    185         local files 
    186         # Moved up per Ian's request. 
    187         cat $LOG_TEMPLATE > $msg 
    188         printf "\n${ignoreline}\n" >> $msg 
     240        local msg status files file 
     241        local nocommit=true 
     242 
    189243        for file in "$@"; do 
    190             if [ -L "$file" ]; then 
    191                 files="$files `${readlink_cmd} -m $file`" 
    192             fi 
    193             files="$files $file" 
    194         done 
    195         @SVN@ status $files >> $status 
    196         cat $status >> $msg 
    197         cp $msg $orig_msg 
    198         if [ $(wc -l < $status) -gt 0 ] ; then 
    199             ${EDITOR} $msg 
    200             if diff $msg $orig_msg >/dev/null 2>&1 ; then 
    201                 echo "Log message unchanged. (a)bort / (C)ontinue?" 
    202                 read response 
    203                 if [ "${response}" = "a" ] ; then 
    204                     conf_unlock_wcopy 
    205                     conf_unlock_system 
    206                     return 1 
    207                 fi 
    208             fi 
    209         fi 
    210         rm $status $orig_msg 
    211         ${sed_cmd} -i '' -e '/'"$ignoreline"'/,$d' $msg 2>/dev/null 
     244            files="${files:+${files} }$(${readlink_cmd} -m "$file")" 
     245        done 
     246 
     247 
    212248        $SUDO ${SUDO:+-v} 
    213         #changed from update to conf_update_tree to implement locking - arjun 
    214249        conf_update_tree || conf_cleanExit 
    215250 
     251        if status=$(get_status $files) ; then 
     252            nocommit=false 
     253            msg=$(get_log "$status") || conf_cleanExit 
     254            rm -f "$status" 
     255        fi 
     256 
    216257        echo "Installation operation started." >&2 
    217         conf_commit_file "$msg" "$files" || return $? 
     258        $nocommit || conf_commit_file "$msg" "$files" || return $? 
    218259        for layer in $(conf_get_recipe) ; do 
    219260            conf_install $layer "$@" 
     
    896937    if [ -z "$1" ] ; then print_usage 1 ; fi 
    897938    local recipe="$1" 
    898     local recipe_file=$(conf_tmp_file) 
    899     cat "$(conf_recipe_dir)/${recipe}" > "$recipe_file" 
    900     $EDITOR "$recipe_file" 
    901     for module in $(conf_get_recipe ${recipe_file}) ; do 
    902         if ! [ -d "${WORK_PATH}/${module}" ] ; then 
    903             echo "Error: recipe references non-existent module $module" >&2 
     939    local new_recipe_file=$(conf_tmp_file) 
     940    local recipe_file=$(conf_recipe_dir)/${recipe} 
     941    local repeat msg status 
     942 
     943    cat "$recipe_file" > "$new_recipe_file" 
     944    $EDITOR "$new_recipe_file" 
     945    until conf_recipe_verify "$new_recipe_file" ; do 
     946        echo -n "(E)dit / (c)ancel? " 
     947        read repeat 
     948        case "$repeat" in 
     949            [Cc]*)  return 1;;  
     950            *)      $EDITOR "$new_recipe_file";; 
     951        esac 
     952    done 
     953 
     954    cat "$new_recipe_file" > "$recipe_file" 
     955 
     956    if status=$(get_status "${recipe_file#${WORK_PATH}/}") ; then 
     957        msg=$(get_log "$status") || return 1 
     958        conf_commit_recipe "$recipe" "$msg" 
     959    fi 
     960    rm -f "$new_recipe_file" 
     961} 
     962 
     963function recipe_create { 
     964    local recipe recipe_file opt OPTIND msg status 
     965    local recipe_file 
     966    while getopts ":R:" opt ; do 
     967        case $opt in 
     968            R)  new_recipe_file="$OPTARG" ;; 
     969            *)  echo "Invalid option '-${OPTARG}' for recipe create." >&4 
     970                print_usage 1  
     971                ;; 
     972        esac 
     973    done 
     974 
     975    eval recipe=\$$OPTIND 
     976    recipe_file=$(conf_recipe_dir)/${recipe} 
     977 
     978    if [ -z "$recipe" ] ; then  
     979        print_usage 1  
     980    fi 
     981 
     982    conf_recipe_create "$recipe" || return 1 
     983 
     984    if [ -z "$new_recipe_file" ] ; then 
     985        if ! recipe_edit "$recipe" ; then 
     986            conf_rm_file --force "${new_recipe_file}" 
    904987            return 1 
    905988        fi 
    906     done 
    907     conf_update_recipe "$recipe" "$recipe_file" 
    908     rm -f "$recipe_file" 
    909 } 
    910  
    911 function recipe_create { 
    912     if [ -z "$1" ] ; then print_usage 1 ; fi 
    913     local recipe="$1" 
    914     conf_recipe_create "$recipe" 
    915     recipe_edit "$recipe" 
     989        return 0 
     990    fi 
     991 
     992    if conf_recipe_verify "$new_recipe_file" ; then 
     993        cp "$new_recipe_file" "$recipe_file" 
     994        if status=$(get_status "${recipe_file#${WORK_PATH}/}") ; then 
     995            msg=$(get_log "$status") || return 1 
     996            conf_commit_recipe "$recipe" "$msg" 
     997        fi 
     998        return 0 
     999    else 
     1000        conf_rm_file --force "${recipe_file}" 
     1001        return 1 
     1002    fi 
    9161003} 
    9171004 
     
    9731060 
    9741061# Debug mode? 
    975 while getopts "hdv-" opt ; do 
     1062while getopts ":h:dvm:F:-" opt ; do 
    9761063        case $opt in 
    977                 d) DEBUG="true" ; shift ;; 
    978         h) shift ; print_help "$@" ; conf_cleanExit 0 ;; 
     1064                d) DEBUG="true" ;; 
     1065        h) print_help "$OPTARG" ; conf_cleanExit 0 ;; 
    9791066        v) print_version ; conf_cleanExit 0 ;; 
    980                 *) print_usage 1 ;; 
     1067        m) LOG_MESSAGE="$OPTARG"; LOG_MESSAGE_SET="true" ;; 
     1068        F) LOG_FILE="$OPTARG" ;; 
     1069                *) echo "Invalid options '-${OPTARG}'." >&4 
     1070           print_usage 1  
     1071           ;; 
    9811072        esac 
    9821073done 
    9831074 
     1075# Argument checking 
     1076if [ -n "$LOG_FILE" ] && ! [ -r "$LOG_FILE" ] ; then 
     1077    echo "Log file $LOG_FILE does not exist or is not readable." >&4 
     1078    conf_cleanExit 1 
     1079fi 
     1080 
     1081if $LOG_MESSAGE_SET && [ -n "$LOG_FILE" ] ; then 
     1082    echo "Cannot specify both a message with -m and a file with -F." >&4 
     1083    conf_cleanExit 1 
     1084fi 
     1085 
    9841086# Dispatch the subcommand. This must happen last and in the global context. 
    985 subcommand=$1 
    986 shift 
     1087eval subcommand=\$$OPTIND 
     1088shift $OPTIND 
     1089 
    9871090trap "conf_interrupt_trap" HUP INT QUIT TERM 
    9881091case $subcommand in 
  • trunk/confmancommon.sh.in

    r437 r462  
    4040# we re-spawn confman inside a pipe. 
    4141if (true >&5) 2>/dev/null ; then 
    42         exec 4>&2 2>&5 >&3- 
     42        exec 4>&2 2>&5 >&3 
    4343else 
    4444        exec 3>&1 
  • trunk/confmandoc.sh

    r453 r462  
    224224 
    225225Usage: 
    226   $MYNAME commit 
     226    $MYNAME [ -F filename | -m message ] commit 
    227227 
    228228The commit command causes your changes to take effect. This means that 
     
    238238ORDER OF APPEARANCE. 
    239239 
     240If -F or -m are used, the log message will be read from the filename or 
     241the commandline, respectively, instead of prompting the user. 
     242 
    240243EOF 
    241244;; 
     
    257260 
    258261Usage: 
    259   $MYNAME install workingfile [ workingfile ... ] 
     262    $MYNAME [-F filename | -m message] install workingfile [workingfile [...]] 
    260263 
    261264The install subcommand allows you to roll out a single file from your working 
    262265copy. 
     266 
     267If -F or -m are used, the log message will be read from the filename or 
     268the commandline, respectively, instead of prompting the user. 
    263269 
    264270EOF 
  • trunk/confmanlib.sh.in

    r460 r462  
    3232WCOPY_DIRTY="false" 
    3333CONF_EXPORT="false" 
     34LOG_MESSAGE_SET="false" 
    3435 
    3536VERSION='@VERSION@' 
     
    698699 
    699700function conf_rm_file { 
    700         @SVN@ rm $* 
     701        @SVN@ rm "$@" 
    701702} 
    702703 
     
    963964        return 1 
    964965    fi 
    965     touch "$recipe_file" 
     966    ${sed_cmd} -e "s:__NAME__:${recipe}:" $RECIPE_TEMPLATE > "$recipe_file" 
    966967    @SVN@ add "$recipe_file" 
    967968} 
    968969 
    969 function conf_update_recipe { 
     970function conf_recipe_verify { 
     971    local recipe_file="$1" 
     972    local module 
     973    for module in $(conf_get_recipe ${recipe_file}) ; do 
     974        if ! [ -d "${WORK_PATH}/${module}" ] ; then 
     975            echo "Error: recipe references non-existent module $module" >&2 
     976            return 1 
     977        fi 
     978    done 
     979    return 0 
     980} 
     981 
     982function conf_commit_recipe { 
    970983    local recipe="$1" 
    971     local recipe_file="$2" 
    972     cat "$recipe_file" > "$(conf_recipe_dir)/${recipe}" 
    973     @SVN@ commit "$(conf_recipe_dir)/${recipe}" 
     984    local logfile="$2" 
     985    @SVN@ commit -F "$logfile" "$(conf_recipe_dir)/${recipe}" 
    974986} 
    975987 
     
    10151027} 
    10161028 
     1029# Implements logic for selecting logfile based on -m/-F or interactive mode 
     1030#   Returns 0 if the log is "complete", 1 if the log needs editing 
     1031#   Prints filename containing the log on stdout 
     1032function conf_log_message { 
     1033    local logfile 
     1034 
     1035    if [ -n "$LOG_FILE" ] ; then 
     1036        echo "$LOG_FILE" 
     1037        return 0 
     1038    fi 
     1039     
     1040    logfile=$(conf_tmp_file) 
     1041    echo "$logfile" 
     1042 
     1043    if $LOG_MESSAGE_SET ; then 
     1044        echo "$LOG_MESSAGE" > "$logfile" 
     1045    else 
     1046        cat "$LOG_TEMPLATE" > "$logfile" 
     1047        return 1 
     1048    fi 
     1049    return 0 
     1050} 
     1051 
    10171052# Because an exit doesn't help much when things are in subshells, we 
    10181053# provide a nice, easy way to halt execution. The result is the clean 
Note: See TracChangeset for help on using the changeset viewer.