forked from triggers/nii-project-2016
-
Notifications
You must be signed in to change notification settings - Fork 0
/
simple-defaults-for-bashsteps.source
147 lines (128 loc) · 5.39 KB
/
simple-defaults-for-bashsteps.source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# Sourcing this code defines and exports the following:
# reportfailed(), prev_cmd_failed(), $DATADIR,
# $starting_dependents, $starting_checks, and
# $skip_rest_if_already_done.
# It also exports this new framework which will hopefully
# soon replace some of the above:
# $starting_step
# $skip_step_if_already_done
# $starting_group
# $skip_group_if_unnecessary
# For any of these that are already defined, no change is made but the
# item is still exported. Therefore, simple subscripts steps that are
# not written to be called independently can assume these are already
# defined. (Steps that are written to be called independently will
# need to source this or some other file that defines the above exports.)
# In addition, it always sets CODEDIR, because this script should
# only be sourced by scripts that can be called independently, and
# therefore should set up their own environments. Note that if the
# calling script is started with a symbolic link, CODEDIR is set
# to the directory of the link, not final target of the link.
if ! declare -f reportfailed >/dev/null; then
reportfailed()
{
echo "Script failed...exiting. ($*)" 1>&2
exit 255
}
fi
export -f reportfailed
if ! declare -f prev_cmd_failed >/dev/null; then
prev_cmd_failed()
{
# this is needed because '( cmd1 ; cmd2 ; set -e ; cmd3 ; cmd4 ) || reportfailed'
# does not work because the || disables set -e, even inside the subshell!
# see http://unix.stackexchange.com/questions/65532/why-does-set-e-not-work-inside
# A workaround is to do '( cmd1 ; cmd2 ; set -e ; cmd3 ; cmd4 ) ; prev_cmd_failed'
(($? == 0)) || reportfailed "$*"
}
fi
export -f prev_cmd_failed
export CODEDIR="$(cd "$(dirname "$0")" && pwd -P)" || reportfailed
[ "$DATADIR" == "" ] && export DATADIR="$CODEDIR/output"
# put the current directory someplace unwritable to force use
# of the above variables
cd -P /proc/self
## Hints to understand the scripts that source this file:
## (1) Every step is put in its own process. The easiest way to do this
## is to use ( ), but calling other scripts is also possible for
## code reuse or readability reasons. (In addition, it is necessary
## to make sure the script terminates if *any* of the processes exit with
## an error. This is easy, but does take a little care in bash. See
## the comments in prev_cmd_failed.)
## (2) All steps start with commands that check whether the step has
## already been done. These commands should only check. They should
## not change any state.
## (3) The return code of the last "check" command is used to decide whether
## the rest of the step (i.e. the step's process) needs to be done.
## This should be done by inserting "$skip_step_if_already_done" at
## that point in the step.
## (4) An optional '$starting_step "Description of the step"' can appear
## at the start of the step.
## (5) Processes are also used to group steps into hierarchies.
## ((TODO: update the doc))
## Therefore, with minimal effort, it should be possible to take a
## the simplest easy-to-read sequential script and make it (at least
## somewhat) idempotent. In other words, it should be possible to
## run the script multiple times, and have it gracefully recover from
## failures and be able to retry efficiently after the point of failure.
## $starting_step and $skip_step_if_already_done are simple bash variables
## and can serve as hooks for controlling the script in useful ways.
## The setting below provide simple defaults that provide some log output
## and some idempotent behavior.
## The action of $starting_step as defined below is to only remember the title.
## It will be used by $skip_step_if_already_done.
## The action of $skip_step_if_already_done as defined below is to exit
## the process if the return code is 0. In addition it outputs a
## header line that reports the step's title and whether it is being
## done or skipped.
## If these variables are already defined, the code below leaves them
## unchanged. Therefore higher-level wrapper scripts can customize
## the behavior to provide more control, status updates, or debugging
## output.
## On the other extream, it is always be possible to set both variables
## to ":", which should (if everything is written correctly) turn the scripts
## back into a typical script that blindly runs from start to finish.
: ${starting_dependents:=default_set_title}
: ${starting_checks:=default_set_title}
: ${skip_rest_if_already_done:=default_skip_step} # exit (sub)process if return code is 0
export starting_dependents
export starting_checks
export skip_rest_if_already_done
# the new framework:
: ${starting_step:=default_set_title}
: ${starting_group:=default_set_title}
: ${skip_step_if_already_done:=default_skip_step}
: ${skip_group_if_unnecessary:=default_skip_group}
export starting_step
export starting_group
export skip_step_if_already_done
export skip_group_if_unnecessary
default_set_title()
{
[ "$*" != "" ] && step_title="$*"
}
export -f default_set_title
default_skip_step()
{
if (($? == 0)); then
echo "** Skipping step: $step_title"
step_title=""
exit 0
else
echo ; echo "** DOING STEP: $step_title"
step_title=""
fi
}
export -f default_skip_step
default_skip_group()
{
if (($? == 0)); then
echo "** Skipping group: $step_title"
step_title=""
exit 0
else
echo ; echo "** DOING GROUP: $step_title"
step_title=""
fi
}
export -f default_skip_group