forked from aws/aws-cdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
foreach.sh
executable file
·105 lines (91 loc) · 3.11 KB
/
foreach.sh
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
#!/bin/bash
# --------------------------------------------------------------------------------------------------
#
# this wonderful tool allows you to execute a command for all modules in this repo
# in topological order, but has the incredible property of being stateful.
# this means that if a command fails, you can fix the issue and resume from where
# you left off.
#
# to start a session, run:
# foreach.sh COMMAND
#
# this will execute "COMMAND" for each module in the repo (cwd will be the directory of the module).
# if a task fails, it will stop, and then to resume, simply run `foreach.sh` again (with or without the same command).
#
# to reset the session (either when all tasks finished or if you wish to run a different session), run:
# rm -f ~/.foreach.*
#
# this will effectively delete the state files.
#
# --------------------------------------------------------------------------------------------------
set -euo pipefail
scriptdir=$(cd $(dirname $0) && pwd)
statefile="${HOME}/.foreach.state"
commandfile="${HOME}/.foreach.command"
command_arg="${@:-}"
base=$PWD
function heading {
printf "\e[38;5;81m$@\e[0m\n"
}
function error {
printf "\e[91;5;81m$@\e[0m\n"
}
function success {
printf "\e[32;5;81m$@\e[0m\n"
}
if [[ "${1:-}" == "--reset" ]]; then
rm -f ~/.foreach.*
success "state cleared. you are free to start a new command."
exit 0
fi
if [ -f "${statefile}" ] && [ -f "${commandfile}" ]; then
command="$(cat ${commandfile})"
if [ ! -z "${command_arg}" ] && [ "${command}" != "${command_arg}" ]; then
error "error: there is still an active session for: \"${command}\". to reset:"
error " $0 --reset"
exit 1
fi
fi
if [ ! -f "${statefile}" ] && [ ! -f "${commandfile}" ]; then
if [ ! -z "${command_arg}" ]; then
command="${command_arg}"
success "starting new session"
node_modules/.bin/lerna ls --all --toposort -p > ${statefile}
echo "${command}" > ${commandfile}
else
error "no active session, use \"$(basename $0) COMMAND\" to start a new session"
exit 0
fi
fi
next="$(head -n1 ${statefile})"
if [ -z "${next}" ]; then
success "done (queue is empty). to reset:"
success " $0 --reset"
exit 0
fi
remaining=$(cat ${statefile} | wc -l | xargs -n1)
heading "---------------------------------------------------------------------------------"
heading "${next}: ${command} (${remaining} remaining)"
(
cd ${next}
# special-case "npm run" - skip any modules that simply don't have
# that script (similar to how "lerna run" behaves)
if [[ "${command}" == "npm run"* ]]; then
script="$(echo ${command} | cut -d" " -f3)"
exists=$(node -pe "require('./package.json').scripts.${script} || ''")
if [ -z "${exists}" ]; then
echo "skipping (no "${script}" script in package.json)"
exit 0
fi
fi
sh -c "${command}" &> /tmp/foreach.stdio || {
cd ${base}
cat /tmp/foreach.stdio | ${scriptdir}/path-prefix ${next}
error "error: last command failed. fix problem and resume by executing: $0"
error "directory: ${next}"
exit 1
}
)
tail -n +2 "${statefile}" > "${statefile}.tmp"
cp "${statefile}.tmp" "${statefile}"
exec $0