Skip to content

Instantly share code, notes, and snippets.

@evansd
Last active January 14, 2022 10:08
Show Gist options
  • Save evansd/faa2a35ea72332e7efa3e2f27b0ed0ea to your computer and use it in GitHub Desktop.
Save evansd/faa2a35ea72332e7efa3e2f27b0ed0ea to your computer and use it in GitHub Desktop.
Expects to be run from a `bin/` subdirectory within the main project directory
#!/bin/bash
set -euo pipefail
help_text="
Usage: bin/development-services [-h|--help] [-n|--no-attach]
Executes the commands listed in the 'Procfile.development' file, each in its
own pane of a new tmux session, and then attaches to that session.
If an appropriate tmux session already exists then we attach to it without
starting anything.
--no-attach: Don't attach to the session, just ensure it is started and
then wait for it to terminate
"
show_help=false
no_attach=false
unknown_arg=''
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
show_help=true;;
-n|--no-attach)
no_attach=true;;
*)
unknown_arg="$unknown_arg $1";;
esac
shift
done
if $show_help; then
echo "$help_text"
exit 0
fi
if [[ -n "$unknown_arg" ]]; then
echo "UNRECOGNISED ARGUMENT:$unknown_arg"
echo "$help_text"
exit 1
fi
# Change in the project root directory
unset CDPATH
cd "$( dirname "${BASH_SOURCE[0]}" )/.."
process_file="$PWD/Procfile.development"
# Default project data directory if not already set
: ${PROJECT_DATA_DIR:="$PWD/data"}
export PROJECT_DATA_DIR
socket_path="$PROJECT_DATA_DIR/tmux.socket"
# Check tmux is installed
if ! which tmux >/dev/null; then
echo "You must install tmux to use this script"
echo "Note: you can always run the commands listed in the '$process_file' file manually."
exit 1
fi
# tmux wrapper function which uses the correct socket file
_tmux() {
tmux -2 -S "$socket_path" "$@"
}
process_list="$(sed --regexp-extended --quiet 's/^([A-Za-z0-9_]+):\s*(.+)$/\1:\2/p' "$process_file")"
if ! _tmux has-session 2>/dev/null; then
echo "Starting new tmux session on $socket_path"
mkdir -p "$(dirname "$socket_path")"
_tmux new-session -d
# Show pane titles in their borders
_tmux set pane-border-status
# Enable mouse mode so clicking in a pane will select it
_tmux set mouse on
IFS=$'\n'
first=true
for process_definition in $process_list; do
# Extract substring up to first colon
process_name="${process_definition%%:*}"
# Extract substring after first colon
process_command="${process_definition#*:}"
if $first; then
first=false
else
# Split pane, giving 95% of remaining space to new pane so we have plenty
# of space to keep opening more panes (panes will be resized evenly at
# the end of the loop)
_tmux split-window -p 95
fi
echo "Starting service '$process_name': $process_command"
# Sets the current pane title to the process name using the required escape
# sequence. We use a tiny delay to avoid writing characters before the
# shell is ready (everything will still work if this happens, we may just
# get some extraneous characters appearing in the pane which looks a bit
# untidy). We use a leading space to avoid polluting bash history.
sleep 0.01
_tmux send-keys " printf '\\033]2;%s\\033\\\\' '$process_name'; clear" enter
sleep 0.01
# Run the process command
_tmux send-keys "$process_command" enter
done
# Resize all panes to be equal height
_tmux select-layout even-vertical
else
echo "Found existing tmux session on $socket_path"
fi
if ! $no_attach; then
echo "Attaching to session"
exec tmux -2 -S "$socket_path" attach-session
else
echo "Waiting for session to close"
while _tmux has-session 2>/dev/null; do
sleep 1
done
echo 'Session closed'
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment