Skip to content

Instantly share code, notes, and snippets.

@jjoos
Created February 20, 2014 15:06
Show Gist options
  • Save jjoos/9115718 to your computer and use it in GitHub Desktop.
Save jjoos/9115718 to your computer and use it in GitHub Desktop.
[Foreman](https://github.com/ddollar/foreman) isn't supported anymore and there is a really annoying bug. When a process crashes not all other processes get killed properly. Since [foreman](https://github.com/ddollar/foreman) doesn't do too much, @Eamon rewrote it in bash. This is a rewrite inspired by his implementation. This script doesn't bre…
#!/usr/bin/env bash
# default values
env_file_location=".env"
procfile_location="Procfile"
# possibly overwrites $env_file_location
read_command_line_options() {
# read the options
TEMP=`getopt e:h $*`
eval set -- "$TEMP"
# extract options and their arguments into variables.
while true ; do
case "$1" in
-e|--env)
case "$2" in
"") shift 2 ;;
*) env_file_location=$2 ; shift 2 ;;
esac ;;
--) shift ; break ;;
*) echo "Internal error!" ; exit 1 ;;
esac
done
if [[ $1 != '' ]]; then procfile_location="$1"; fi
}
export_key_values_from_env_file() {
# set -a make sure that all asignments are exported automatically
set -a
source $env_file_location
set +a
}
# sets $longest_name, $names and $commands
read_procfile() {
pattern='([^:/]+)(/.*/)?:(.*)'
counter=0
longest_name=0
while read line
do
if [[ $line =~ $pattern ]]; then
names[$counter]=${BASH_REMATCH[1]}
# accept input for the sed s command in the form /<regex>/replacement/
# you should use double escapes in these patterns, keep the output if no
# pattern is provided.
if [[ ${BASH_REMATCH[2]} == '' ]]; then
replacements[$counter]='/(.*)/$1/';
else
replacements[$counter]=${BASH_REMATCH[2]}
fi
commands[$counter]=${BASH_REMATCH[3]}
if (( "${#names[counter]}" > "$longest_name" )); then
longest_name=${#names[counter]}
fi
counter=$(( counter + 1 ))
fi
done < $procfile_location
}
# called when an exit signal is trapped, kills the jobs in the current group
onexit() {
printf "Shutting down ...\n"
kill -SIGINT $(jobs) &> /dev/null
sleep 2
kill -SIGTERM $(jobs) &> /dev/null
sleep 2
kill -SIGKILL 0
}
# returs a function that pipes the jobs output and adds names and colours
format_output() {
declare -a colors=('1;31' '32' '36' '1;32' '1;33' '1;34' '1;35' '1;36' '35'
'31' '1;30' '34' '33')
# calculate how much whitespace should be added to align job output.
whitespace_length=$(( $longest_name - ${#names[counter]} ))
whitespace=`perl -e "print ' ' x $whitespace_length;"`
# Underline job names of error output
if [[ $1 == 'stderr' ]]; then error_modifier='4;'; fi
prefix=`echo -e "\e[$error_modifier${colors[counter]}m${names[counter]}\e[24m$whitespace| \e[0m"`
sed -u -e "s${replacements[counter]}g" -e "s/^/$prefix/"
}
read_command_line_options "$@"
export_key_values_from_env_file
read_procfile
# trap all exit signals to the onexit function
trap onexit SIGINT SIGTERM EXIT INT QUIT TERM
# loop through all process names
for counter in "${!names[@]}"; do
# execute the command and pipe the output through the formatter
{
{
eval "${commands[counter]}" || kill $$;
} 2>&1 1>&3 | format_output 'stderr' >&2; # stderr=>stdout stdin=>fd3 and stdout=>stderr
} 3>&1 | format_output 'stdout' & # fd3>stdout
# wait a miniscule amount of time (mitigating race conditions)
sleep 0.02
done
# wait for all the jobs to quit
wait
@searls
Copy link

searls commented Aug 5, 2014

Thanks for sharing this! Ultimately this one didn't work b/c OS X sed does not support the -u option. I chose to use Shoreman, because that one just worked, but this put me on the path. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment