# HG changeset patch # User Tero Marttila # Date 1332245531 -7200 # Node ID b68b8615c51284f5ade85b666fcab68470be0131 # Parent c486df8ea68ad9353de49053a0b1ccd5749ec1ab update: split out code into lib/update.foo diff -r c486df8ea68a -r b68b8615c512 bin/update --- a/bin/update Tue Mar 20 14:00:33 2012 +0200 +++ b/bin/update Tue Mar 20 14:12:11 2012 +0200 @@ -3,11 +3,7 @@ set -ue - -### Paths -ROOT=$(pwd) - -# resolve $0 +# resolve $0 -> bin/update self=$0 while [ -L $self ]; do tgt=$(readlink $self) @@ -22,747 +18,54 @@ # Our bin dir, with scripts BIN=$(dirname $self) -# Data files +# code root +CODE=$(dirname $BIN) + + +LIB=$CODE/lib + +## Data paths +# absolute path to data files; can be changed using -d +ROOT=$(pwd) + DATA=settings ZONES=zones SERIALS=$DATA + +# hg repo to commit REPO=$DATA -# hide files under repo in diff output.. +## Settings used in lib +# Hide files under repo in commit diff output.. REPO_HIDE='*.serial' -# Script/data args +# XXX: hosts data input charset? PROCESS_ARGS='--input-charset latin-1' -# external progs +# External bins NAMED_CHECKZONE=/usr/sbin/named-checkzone HG=/usr/bin/hg RNDC=/usr/sbin/rndc + +# Path to rndc key, must be readable to run.. RNDC_KEY=/etc/bind/rndc.key -### Command-line argument handling - -IS_TTY= - -## Options -LOG_ERROR=y -LOG_WARN=y -LOG=y -LOG_FORCE=y -LOG_UPDATE=y -LOG_NOOP=y -LOG_SKIP= -LOG_DEBUG= -LOG_CMD= - -UPDATE_FORCE= -UPDATE_NOOP= -UPDATE_DIFF= - -SERIAL_NOOP= -SERIAL_FORCE= - -COMMIT_SKIP= -COMMIT_FORCE= -COMMIT_MSG=' ' - -DEPLOY_SKIP= - -## Output command-line argument help. -function help_args { - local prog=$1 - - cat <&2 -} - -# Output message to stderr, optionally with given color, if TTY. -function log_color { - local code=$1; shift - - if [ $IS_TTY ]; then - echo $'\e['${code}'m'"$*"$'\e[00m' >&2 - else - echo "$*" >&2 - fi -} - -## Log at various log-levels - -function log_error { - [ $LOG_ERROR ] && log_color '31' "$*" -} - -function log_warn { - [ $LOG_WARN ] && log_color '33' "$*" || true -} - -# plain -function log { - [ $LOG ] && log_msg "$*" || true -} - -function log_force { - [ $LOG_FORCE ] && log_color '2;33' " $*" || true -} - -function log_update { - [ $LOG_UPDATE ] && log_color '36' " $*" || true -} - -function log_noop { - [ $LOG_NOOP ] && log_color '2;34' " $*" || true -} - -function log_skip { - [ $LOG_SKIP ] && log_color '1;34' " $*" || true -} - -function log_debug { - [ $LOG_DEBUG ] && log_color 32 " $*" || true -} - -function log_cmd { - [ $LOG_CMD ] && log_color 35 " \$ $*" || true -} - -# Output stacktrace, broken. -function log_stack { - local level=1 - - while info=$(caller $level); do - echo $info | read line sub file - - log_msg "$file:$lineno $sub()" - - level=$(($level + 1)) - done -} - -# Output calling function's name. -function func_caller { - caller 1 | cut -d ' ' -f 2 -} - -### High-level logging output -# Log with func_caller at log_debug -function debug { - printf -v prefix "%s" $(func_caller) - - log_debug "$prefix: $*" -} - -# Log with func_caller at log_error and exit, intended for internal errors... -function fail { - log_error "$(func_caller): $*" - - exit 2 -} - -# Log at log_error and exit -function die { - log_error "$*" - exit 1 -} - -### Command execution -## Execute command, possibly logging its execution. -# -# cmd $cmd... -# -# Fails if the command returns an error exit code. -function cmd { - log_cmd "$@" - - "$@" || die "Failed" -} - -function indent () { - local indent=$1; shift - - log_cmd "$@" - - "$@" | sed "s/^/$indent/" - - return ${PIPESTATUS[0]} -} - - -### FS utils -# Create dir in $ROOT if not exists. -function ensure_dir { - local dir=$1 - - if [ ! -d $ROOT/$dir ]; then - log_warn "Creating output dir: $dir" - cmd mkdir $ROOT/$dir - fi -} - -## Output absolute path from $ROOT: -# -# abspath $path -# -function abspath () { - local path=$1 - - echo "$ROOT/$path" -} - -### HG wrappers -# Run `hg ...` within $REPO. -function hg { - local repo=$REPO - - cmd $HG -R $ROOT/$repo "$@" -} - -# Does the repo have local modifications? -function hg_modified { - hg id | grep -q '+' -} - -# Output possible -u flag for commit. -function hg_user { - if [ ${SUDO_USER:-} ]; then - echo '-u' "$SUDO_USER" - - elif [ $HOME ] && [ -e $HOME/.hgrc ]; then - debug "using .hgrc user" - echo '' - - else - echo '-u' "$USER" - fi -} - -# Show changes in repo -function hg_diff { - hg diff -X "$REPO/$REPO_HIDE" -} - -## Commit changes in repo, with given message: -# -# hg_commit $msg -# -function hg_commit { - local msg=$1 - local user_opt=$(hg_user) - - debug "$user_opt: $msg" - hg commit $user_opt -m "$msg" -} - - -### Dependency-based updates - -## Compare the given output file with all given source files: -# -# check_update $out ${deps[@]} && do_update $out ... || ... -# -# Returns true if the output file needs to be updated. -function check_update { - # target - local out=$1; shift - - debug "$out" - - # need update? - local update= - - if [ ${#@} == 0 ]; then - debug " update: unknown deps" - update=y - - elif [ ! -e $out ]; then - debug " update: dest missing" - update=y - - elif [ $UPDATE_FORCE ]; then - debug " update: forced" - update=y - fi - - # check deps - for dep in "$@"; do - # don't bother checking if already figured out - [ $update ] && continue - - # check - if [ ! -e $ROOT/$dep ]; then - fail "$dst: Missing source: $dep" - - elif [ $ROOT/$out -ot $ROOT/$dep ]; then - debug " update: $dep" - update=y - else - debug " check: $dep" - fi - done - - [ ! $update ] && debug " up-to-date" - - # return - [ $update ] -} - -## Generate updated output file from given command's stdout: -# -# do_update $out $BIN/cmd --args -# -# Writes output to a temporary .new file, optionally shows a diff of changes, and commits -# the new version to $out (unless noop'd). -function do_update { - local out=$1; shift - local tmp=$out.new +## Library includes +# Command-line argument handling +source $LIB/update.args - debug "$out" - cmd "$@" > $ROOT/$tmp - - # compare - if [ -e $ROOT/$out ] && [ $UPDATE_DIFF ]; then - debug " changes:" - - # terse - indent " " diff --unified=1 $ROOT/$out $ROOT/$tmp || true - fi - - # deploy - if [ $UPDATE_NOOP ]; then - # cleanup - debug " no-op" - - cmd rm $ROOT/$tmp - else - # commit - debug " deploy" - - cmd mv $ROOT/$tmp $ROOT/$out - fi -} - -## Look for a link target: -# -# find_link $lnk $tgt... -# -# Outputs the first given target to exist, skipping any that are the same as the given $lnk. -# If no $tgt matches, outputs the last one, or '-'. -function choose_link { - local lnk=$1; shift - local tgt=- - - for tgt in "$@"; do - [ $tgt != $out ] && [ -e $ROOT/$tgt ] && break - done - - echo $tgt -} - - -## Compare symlink to target: -# -# check_link $lnk $tgt && do_link $lnk $tgt || ... -# -# Tests if the symlink exists, and the target matches. -# Fails if the target does not exist. -function check_link { - local lnk=$1 - local tgt=$2 - - [ ! -e $ROOT/$tgt ] && fail "$tgt: target does not exist" - - [ ! -e $ROOT/$lnk ] || [ $(readlink $ROOT/$lnk) != $ROOT/$tgt ] -} - -## Update symlink to point to target: -# -# do_link $lnk $tgt -# -function do_link { - local lnk=$1 - local tgt=$2 - - cmd ln -sf $ROOT/$tgt $ROOT/$lnk -} - -## Update .serial number: -# -# do_update_serial $serial -# -# Shows old/new serial on debug. -function do_update_serial { - local serial=$1 - - # read - local old=$(test -e $ROOT/$serial && cat $ROOT/$serial || echo '') - - - cmd $BIN/update-serial $ROOT/$serial - - # read - local new=$(cat $ROOT/$serial) - - debug " $old -> $new" -} - -## Perform `hg commit` for $DATA -function do_commit { - local msg=$1 - - indent " " hg_diff - - hg_commit "$msg" -} - -### Hosts -## Update hosts from verbatim from input zone data: -# -# copy_hosts $ZONES/$zone $DATA/$base -# -# Writes updated zone to $zone, deps on $base. -function copy_hosts { - local zone=$1 - local base=$2 - - if check_update $zone $base; then - log_update "Copying hosts $zone <- $base..." - - do_update $zone \ - cat $ROOT/$base - else - log_skip "Copying hosts $zone <- $base: not changed" - fi -} - -## Generate hosts from input zone data using $BIN/process-zone: -# -# update_hosts $ZONES/$zone $DATA/$base -# -# Writes process-zone'd data to $zone, deps on $base. -function update_hosts { - local zone=$1; shift - local base=$1; shift - - if check_update $zone $base; then - log_update "Generating hosts $zone <- $base..." - - do_update $zone \ - $BIN/process-zone $PROCESS_ARGS $ROOT/$base "$@" - else - log_skip "Generating hosts $zone <- $base: not changed" - fi -} - -## Generate new serial for zone using $BIN/update-serial, if the zone data has changed: -# -# update_serial $zone $deps... -# -# Supports SERIAL_FORCE/NOOP. -# Updates $SERIALS/$zone.serial. -function update_serial { - local zone=$1; shift - - local serial=$SERIALS/$zone.serial - - # test - if [ $SERIAL_FORCE ]; then - log_force "Updating $serial: forced" - - do_update_serial $serial - - elif ! check_update $serial "$@"; then - log_skip "Updating $serial: not changed" - - elif [ $SERIAL_NOOP ]; then - log_noop "Updating $serial: skipped" - - else - log_update "Updating $serial..." - - do_update_serial $serial - fi -} - -## Link serial for zone from given base-zone: -# -# link_serial $zone $base -function link_serial { - local zone=$1 - local base=$2 - - local lnk=$SERIALS/$zone.serial - local tgt=$SERIALS/$base.serial - - if check_link $lnk $tgt; then - log_update "Linking $lnk -> $tgt..." - - do_link $lnk $tgt +# Logging +source $LIB/update.logging - else - log_skip "Linking $lnk -> $tgt: not changed" - fi -} - -## Update zone file verbatim from source: -# -# copy_zone $view $zone [$base] -# -# Copies changed $DATA/$base zone data to $ZONES/$view/$zone. -function copy_zone { - local view=$1 - local zone=$2 - local base=${3:-$zone} - - local out=$ZONES/$view/$zone - local src=$DATA/$base - - if check_update $out $src; then - log_update "Copying $out <- $src..." - - do_update $out \ - cat $ROOT/$src - else - log_skip "Copying $out <- $src: not changed" - fi -} - -## Expand zone file from source using $BIN/expand-zone: -# -# update_zone $view $zone [$base] -# -# Processed $DATA/$base zone data through $BIN/expand-zone, writing output to $ZONES/$view/$zone. -function update_zone { - local view=$1 - local zone=$2 - local base=${3:-$zone} - - local out=$ZONES/$view/$zone - local src=$DATA/$base.zone - local lnk=$ZONES/$base - - local serial=$SERIALS/$base.serial - - if check_update $out $src $serial; then - log_update "Generating $out <- $src..." - - do_update $out \ - $BIN/expand-zone $ROOT/$src \ - --serial $ROOT/$serial \ - --expand zones=$(abspath $ZONES) \ - --expand view=$view - else - log_skip "Generating $out <- $src: not changed" - fi -} - -## Link zone file to ues given shared zone. -# -# link_zone $view $zone [$base] -# -# Looks for shared zone at: -# $ZONES/$view/$base -# $ZONES/common/$base -function link_zone { - local view=$1 - local zone=$2 - local base=${3:-$zone} - - local out=$ZONES/$view/$zone - local tgt=$(choose_link $out $ZONES/$view/$base $ZONES/common/$base) - - if check_link $out $tgt; then - log_update "Linking $out -> $tgt..." - - do_link $out $tgt - - else - log_skip "Linking $out -> $tgt: not changed" - fi -} - -## Test hosts zone for validity: -# -# check_hosts $DATA/$hosts --check-exempt ... -# -# Fails if the check fails. -function check_hosts { - local hosts=$1; shift 1 +# Utility functions +source $LIB/update.utils - local cmd=($BIN/process-zone $PROCESS_ARGS $ROOT/$hosts --check-hosts "$@") - - if "${cmd[@]}" -q; then - log_skip "Check $hosts: OK" - else - log_error " Check $hosts: Failed" - - indent " " "${cmd[@]}" - - exit 1 - fi -} - -## Test zone file for validity using named-checkzone: -# -# check_zone $view $zone $origin -# -# Uses the zonefile at $ZONES/$view/$zone, loading it with given initial $ORIGIN. -# Fails if the check fails. -function check_zone { - local view=$1 - local zone=$2 - local origin=$3 - - local src=$ZONES/$view/$zone - - local cmd=($NAMED_CHECKZONE $origin $ROOT/$src) - - # test - # XXX: checkzone is very specific about the order of arguments, -q must be first - if $NAMED_CHECKZONE -q $origin $ROOT/$src; then - log_skip "Check $src ($origin): OK" - else - log_error " Check $src ($origin): Failed:" - - indent " " "${cmd[@]}" - - exit 1 - fi -} +# Dependency-based updates +source $LIB/update.updates -## Load update zonefiles into bind: -# -# deploy_zones -# -# Invokes `rndc reload`, showing its output. -function deploy_zones { - local msg="Reload zones" - - if [ $DEPLOY_SKIP ]; then - log_skip "$msg: skipped" - - elif [ ! -r $RNDC_KEY ]; then - log_error " $msg: rndc: permission denied: $RNDC_KEY" - - else - log_update "$msg..." +# Operations +source $LIB/update.operations - # run - indent " rndc: " \ - $RNDC reload - fi -} -## Commit changes in $DATA to version control: -# -# commit_data -# -# Invokes `hg commit` in the $REPO, first showing the diff. -function commit_data { - local repo=$REPO - local commit_msg="$COMMIT_MSG" - - local msg="Commit changes in $repo" - - # operate? - if [ $COMMIT_FORCE ]; then - log_force "$msg..." - - do_commit "$commit_msg" - - elif ! hg_modified; then - log_skip "$msg: no changes" - - elif [ $COMMIT_SKIP ]; then - log_noop "$msg: skipped" - - else - log_update "$msg..." - - do_commit "$commit_msg" - fi -} ## Site settings, used as arguments to scripts # MX record to generate in hosts --forward-zone diff -r c486df8ea68a -r b68b8615c512 lib/update.args --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/update.args Tue Mar 20 14:12:11 2012 +0200 @@ -0,0 +1,118 @@ +# vim: set ft=sh : +# +# Command-line option handling + +# use color output? +IS_TTY= + + +## Options +LOG_ERROR=y +LOG_WARN=y +LOG=y +LOG_FORCE=y +LOG_UPDATE=y +LOG_NOOP=y +LOG_SKIP= +LOG_DEBUG= +LOG_CMD= + +UPDATE_FORCE= +UPDATE_NOOP= +UPDATE_DIFF= + +SERIAL_NOOP= +SERIAL_FORCE= + +COMMIT_SKIP= +COMMIT_FORCE= +COMMIT_MSG=' ' + +DEPLOY_SKIP= + +## Output command-line argument help. +function help_args { + local prog=$1 + + cat <&2 +} + +# Output message to stderr, optionally with given color, if TTY. +function log_color { + local code=$1; shift + + if [ $IS_TTY ]; then + echo $'\e['${code}'m'"$*"$'\e[00m' >&2 + else + echo "$*" >&2 + fi +} + +## Log at various log-levels + +function log_error { + [ $LOG_ERROR ] && log_color '31' "$*" +} + +function log_warn { + [ $LOG_WARN ] && log_color '33' "$*" || true +} + +# plain +function log { + [ $LOG ] && log_msg "$*" || true +} + +function log_force { + [ $LOG_FORCE ] && log_color '2;33' " $*" || true +} + +function log_update { + [ $LOG_UPDATE ] && log_color '36' " $*" || true +} + +function log_noop { + [ $LOG_NOOP ] && log_color '2;34' " $*" || true +} + +function log_skip { + [ $LOG_SKIP ] && log_color '1;34' " $*" || true +} + +function log_debug { + [ $LOG_DEBUG ] && log_color 32 " $*" || true +} + +function log_cmd { + [ $LOG_CMD ] && log_color 35 " \$ $*" || true +} + +# Output stacktrace, broken. +function log_stack { + local level=1 + + while info=$(caller $level); do + echo $info | read line sub file + + log_msg "$file:$lineno $sub()" + + level=$(($level + 1)) + done +} + +# Output calling function's name. +function func_caller { + caller 1 | cut -d ' ' -f 2 +} + +### High-level logging output +# Log with func_caller at log_debug +function debug { + printf -v prefix "%s" $(func_caller) + + log_debug "$prefix: $*" +} + +# Log with func_caller at log_error and exit, intended for internal errors... +function fail { + log_error "$(func_caller): $*" + + exit 2 +} + +# Log at log_error and exit +function die { + log_error "$*" + exit 1 +} + + diff -r c486df8ea68a -r b68b8615c512 lib/update.operations --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/update.operations Tue Mar 20 14:12:11 2012 +0200 @@ -0,0 +1,271 @@ +## vim: set ft=sh : +# +# Operations on zonefiles/hosts/whatever + +## Hosts +## Update hosts from verbatim from input zone data: +# +# copy_hosts $ZONES/$zone $DATA/$base +# +# Writes updated zone to $zone, deps on $base. +function copy_hosts { + local zone=$1 + local base=$2 + + if check_update $zone $base; then + log_update "Copying hosts $zone <- $base..." + + do_update $zone \ + cat $ROOT/$base + else + log_skip "Copying hosts $zone <- $base: not changed" + fi +} + +## Generate hosts from input zone data using $BIN/process-zone: +# +# update_hosts $ZONES/$zone $DATA/$base +# +# Writes process-zone'd data to $zone, deps on $base. +function update_hosts { + local zone=$1; shift + local base=$1; shift + + if check_update $zone $base; then + log_update "Generating hosts $zone <- $base..." + + do_update $zone \ + $BIN/process-zone $PROCESS_ARGS $ROOT/$base "$@" + else + log_skip "Generating hosts $zone <- $base: not changed" + fi +} + +## Generate new serial for zone using $BIN/update-serial, if the zone data has changed: +# +# update_serial $zone $deps... +# +# Supports SERIAL_FORCE/NOOP. +# Updates $SERIALS/$zone.serial. +function update_serial { + local zone=$1; shift + + local serial=$SERIALS/$zone.serial + + # test + if [ $SERIAL_FORCE ]; then + log_force "Updating $serial: forced" + + do_update_serial $serial + + elif ! check_update $serial "$@"; then + log_skip "Updating $serial: not changed" + + elif [ $SERIAL_NOOP ]; then + log_noop "Updating $serial: skipped" + + else + log_update "Updating $serial..." + + do_update_serial $serial + fi +} + +## Link serial for zone from given base-zone: +# +# link_serial $zone $base +function link_serial { + local zone=$1 + local base=$2 + + local lnk=$SERIALS/$zone.serial + local tgt=$SERIALS/$base.serial + + if check_link $lnk $tgt; then + log_update "Linking $lnk -> $tgt..." + + do_link $lnk $tgt + + else + log_skip "Linking $lnk -> $tgt: not changed" + fi +} + +## Update zone file verbatim from source: +# +# copy_zone $view $zone [$base] +# +# Copies changed $DATA/$base zone data to $ZONES/$view/$zone. +function copy_zone { + local view=$1 + local zone=$2 + local base=${3:-$zone} + + local out=$ZONES/$view/$zone + local src=$DATA/$base + + if check_update $out $src; then + log_update "Copying $out <- $src..." + + do_update $out \ + cat $ROOT/$src + else + log_skip "Copying $out <- $src: not changed" + fi +} + +## Expand zone file from source using $BIN/expand-zone: +# +# update_zone $view $zone [$base] +# +# Processed $DATA/$base zone data through $BIN/expand-zone, writing output to $ZONES/$view/$zone. +function update_zone { + local view=$1 + local zone=$2 + local base=${3:-$zone} + + local out=$ZONES/$view/$zone + local src=$DATA/$base.zone + local lnk=$ZONES/$base + + local serial=$SERIALS/$base.serial + + if check_update $out $src $serial; then + log_update "Generating $out <- $src..." + + do_update $out \ + $BIN/expand-zone $ROOT/$src \ + --serial $ROOT/$serial \ + --expand zones=$(abspath $ZONES) \ + --expand view=$view + else + log_skip "Generating $out <- $src: not changed" + fi +} + +## Link zone file to ues given shared zone. +# +# link_zone $view $zone [$base] +# +# Looks for shared zone at: +# $ZONES/$view/$base +# $ZONES/common/$base +function link_zone { + local view=$1 + local zone=$2 + local base=${3:-$zone} + + local out=$ZONES/$view/$zone + local tgt=$(choose_link $out $ZONES/$view/$base $ZONES/common/$base) + + if check_link $out $tgt; then + log_update "Linking $out -> $tgt..." + + do_link $out $tgt + + else + log_skip "Linking $out -> $tgt: not changed" + fi +} + +## Test hosts zone for validity: +# +# check_hosts $DATA/$hosts --check-exempt ... +# +# Fails if the check fails. +function check_hosts { + local hosts=$1; shift 1 + + local cmd=($BIN/process-zone $PROCESS_ARGS $ROOT/$hosts --check-hosts "$@") + + if "${cmd[@]}" -q; then + log_skip "Check $hosts: OK" + else + log_error " Check $hosts: Failed" + + indent " " "${cmd[@]}" + + exit 1 + fi +} + +## Test zone file for validity using named-checkzone: +# +# check_zone $view $zone $origin +# +# Uses the zonefile at $ZONES/$view/$zone, loading it with given initial $ORIGIN. +# Fails if the check fails. +function check_zone { + local view=$1 + local zone=$2 + local origin=$3 + + local src=$ZONES/$view/$zone + + local cmd=($NAMED_CHECKZONE $origin $ROOT/$src) + + # test + # XXX: checkzone is very specific about the order of arguments, -q must be first + if $NAMED_CHECKZONE -q $origin $ROOT/$src; then + log_skip "Check $src ($origin): OK" + else + log_error " Check $src ($origin): Failed:" + + indent " " "${cmd[@]}" + + exit 1 + fi +} + +## Load update zonefiles into bind: +# +# deploy_zones +# +# Invokes `rndc reload`, showing its output. +function deploy_zones { + local msg="Reload zones" + + if [ $DEPLOY_SKIP ]; then + log_skip "$msg: skipped" + + elif [ ! -r $RNDC_KEY ]; then + log_error " $msg: rndc: permission denied: $RNDC_KEY" + + else + log_update "$msg..." + + # run + indent " rndc: " \ + $RNDC reload + fi +} +## Commit changes in $DATA to version control: +# +# commit_data +# +# Invokes `hg commit` in the $REPO, first showing the diff. +function commit_data { + local repo=$REPO + local commit_msg="$COMMIT_MSG" + + local msg="Commit changes in $repo" + + # operate? + if [ $COMMIT_FORCE ]; then + log_force "$msg..." + + do_commit "$commit_msg" + + elif ! hg_modified; then + log_skip "$msg: no changes" + + elif [ $COMMIT_SKIP ]; then + log_noop "$msg: skipped" + + else + log_update "$msg..." + + do_commit "$commit_msg" + fi +} + diff -r c486df8ea68a -r b68b8615c512 lib/update.updates --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/update.updates Tue Mar 20 14:12:11 2012 +0200 @@ -0,0 +1,163 @@ +# vim: set ft=sh : +# +# Dependency-based updates + utils + +## Compare the given output file with all given source files: +# +# check_update $out ${deps[@]} && do_update $out ... || ... +# +# Returns true if the output file needs to be updated. +function check_update { + # target + local out=$1; shift + + debug "$out" + + # need update? + local update= + + if [ ${#@} == 0 ]; then + debug " update: unknown deps" + update=y + + elif [ ! -e $out ]; then + debug " update: dest missing" + update=y + + elif [ $UPDATE_FORCE ]; then + debug " update: forced" + update=y + fi + + # check deps + for dep in "$@"; do + # don't bother checking if already figured out + [ $update ] && continue + + # check + if [ ! -e $ROOT/$dep ]; then + fail "$dst: Missing source: $dep" + + elif [ $ROOT/$out -ot $ROOT/$dep ]; then + debug " update: $dep" + update=y + else + debug " check: $dep" + fi + done + + [ ! $update ] && debug " up-to-date" + + # return + [ $update ] +} + +## Generate updated output file from given command's stdout: +# +# do_update $out $BIN/cmd --args +# +# Writes output to a temporary .new file, optionally shows a diff of changes, and commits +# the new version to $out (unless noop'd). +function do_update { + local out=$1; shift + local tmp=$out.new + + debug "$out" + cmd "$@" > $ROOT/$tmp + + # compare + if [ -e $ROOT/$out ] && [ $UPDATE_DIFF ]; then + debug " changes:" + + # terse + indent " " diff --unified=1 $ROOT/$out $ROOT/$tmp || true + fi + + # deploy + if [ $UPDATE_NOOP ]; then + # cleanup + debug " no-op" + + cmd rm $ROOT/$tmp + else + # commit + debug " deploy" + + cmd mv $ROOT/$tmp $ROOT/$out + fi +} + +## Look for a link target: +# +# find_link $lnk $tgt... +# +# Outputs the first given target to exist, skipping any that are the same as the given $lnk. +# If no $tgt matches, outputs the last one, or '-'. +function choose_link { + local lnk=$1; shift + local tgt=- + + for tgt in "$@"; do + [ $tgt != $out ] && [ -e $ROOT/$tgt ] && break + done + + echo $tgt +} + + +## Compare symlink to target: +# +# check_link $lnk $tgt && do_link $lnk $tgt || ... +# +# Tests if the symlink exists, and the target matches. +# Fails if the target does not exist. +function check_link { + local lnk=$1 + local tgt=$2 + + [ ! -e $ROOT/$tgt ] && fail "$tgt: target does not exist" + + [ ! -e $ROOT/$lnk ] || [ $(readlink $ROOT/$lnk) != $ROOT/$tgt ] +} + +## Update symlink to point to target: +# +# do_link $lnk $tgt +# +function do_link { + local lnk=$1 + local tgt=$2 + + cmd ln -sf $ROOT/$tgt $ROOT/$lnk +} + +## Update .serial number: +# +# do_update_serial $serial +# +# Shows old/new serial on debug. +function do_update_serial { + local serial=$1 + + # read + local old=$(test -e $ROOT/$serial && cat $ROOT/$serial || echo '') + + + cmd $BIN/update-serial $ROOT/$serial + + # read + local new=$(cat $ROOT/$serial) + + debug " $old -> $new" +} + +## Perform `hg commit` for $DATA +function do_commit { + local msg=$1 + + indent " " hg_diff + + hg_commit "$msg" +} + + diff -r c486df8ea68a -r b68b8615c512 lib/update.utils --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/update.utils Tue Mar 20 14:12:11 2012 +0200 @@ -0,0 +1,94 @@ +# vim: set ft=sh : +# +# Utility functions + + +### Command execution +## Execute command, possibly logging its execution. +# +# cmd $cmd... +# +# Fails if the command returns an error exit code. +function cmd { + log_cmd "$@" + + "$@" || die "Failed" +} + +function indent () { + local indent=$1; shift + + log_cmd "$@" + + "$@" | sed "s/^/$indent/" + + return ${PIPESTATUS[0]} +} + + +### FS utils +# Create dir in $ROOT if not exists. +function ensure_dir { + local dir=$1 + + if [ ! -d $ROOT/$dir ]; then + log_warn "Creating output dir: $dir" + cmd mkdir $ROOT/$dir + fi +} + +## Output absolute path from $ROOT: +# +# abspath $path +# +function abspath () { + local path=$1 + + echo "$ROOT/$path" +} + +### HG wrappers +# Run `hg ...` within $REPO. +function hg { + local repo=$REPO + + cmd $HG -R $ROOT/$repo "$@" +} + +# Does the repo have local modifications? +function hg_modified { + hg id | grep -q '+' +} + +# Output possible -u flag for commit. +function hg_user { + if [ ${SUDO_USER:-} ]; then + echo '-u' "$SUDO_USER" + + elif [ $HOME ] && [ -e $HOME/.hgrc ]; then + debug "using .hgrc user" + echo '' + + else + echo '-u' "$USER" + fi +} + +# Show changes in repo +function hg_diff { + hg diff -X "$REPO/$REPO_HIDE" +} + +## Commit changes in repo, with given message: +# +# hg_commit $msg +# +function hg_commit { + local msg=$1 + local user_opt=$(hg_user) + + debug "$user_opt: $msg" + hg commit $user_opt -m "$msg" +} + +