Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NF: SINGULARITY_CMD=shell to record (bash) history+result of interactive sessions #9

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions binds/HOME/.bashrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export PS1="\[\e[33m\]\u\[\e[m\]:\[\e[35m\]\s\[\e[m\]\[\e[37m\]:\[\e[m\]\[\e[36m\]\w\[\e[m\]\n\\$ "

if [ ! -z "$PS1" ]; then
# include a marker into PS1 that we are in the singularity image
# Since we are using git-annex for images, SINGULARITY_NAME would
Expand All @@ -19,6 +21,9 @@ if [ ! -z "$PS1" ]; then
export PS1="singularity:$_name > $PS1"
fi
fi
# Add new line before prompt to reduce clutter
PS1="\n$PS1"


# USER variable might not be defined in sanitized environment
# but could be needed by some tools, e.g. FSL. See
Expand Down
135 changes: 133 additions & 2 deletions scripts/singularity_cmd
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,154 @@

set -eu

# ANSI colors
black='\e[0;30m'
Black='\e[1;30m'
red='\e[0;31m'
Red='\e[1;31m'
green='\e[0;32m'
Green='\e[1;32m'
yellow='\e[0;33m'
Yellow='\e[1;33m'
blue='\e[0;34m'
Blue='\e[1;34m'
cyan='\e[0;36m'
Cyan='\e[1;36m'
white='\e[0;37m'
White='\e[1;37m'
NC='\e[0m' #no color

function msg() {
color="$1"; shift;
pref="$1: "; shift;

for l in "$@"; do
printf "${color}${pref}${l}$NC\n" >&2
pref=" "
done
}

function info() {
: # echo -e "I: $@" >&2
msg "$green" "I" "$@"
}

function warning() {
msg "$red" "W" "$@"
}

function error() {
msg "$Red" "E" "$@"
exit 1
}

#info "I am here" "and there"
#warning "one"
#error "one"
#error "very" "long one"
#exit 0

function has_changes() {
git status -s | grep -q .
}

function singularity_version() {
singularity --version | sed -e 's,^[^0-9]*,,g'
}

# https://stackoverflow.com/a/24067243
function version_gt() {
test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1";
}

thisdir=$(dirname $0| xargs readlink -f)
updir=$(dirname "$thisdir")

cmd="${SINGULARITY_CMD:-$1}"; shift

# We might need to expand list of arguments
args=("$@")

#
# Pass other useful variables inside the container
#
if [ ! -z "${DATALAD_CONTAINER_NAME:-}" ]; then
export SINGULARITYENV_DATALAD_CONTAINER_NAME="$DATALAD_CONTAINER_NAME"
fi

#
# Prepare bind mounts
#

# singularity bind mounts system /tmp, which might result in side-effects
# Create a dedicated temporary directory to be removed upon completion
tmpdir=$(mktemp -d --suffix=singtmp)
info "created temp dir $tmpdir"
trap "rm -fr '$tmpdir' && info 'removed temp dir $tmpdir'" exit

singularity "$cmd" -e -c -W "$tmpdir" -H "$updir/binds/HOME" -B $PWD --pwd "$PWD" "$@"
#
# Prepare for storing bash history in cmd='shell' mode
#
# Will be non-empty if some post-run handling is needed
FINAL_BASH_HISTORY=
TEMP_BASH_HISTORY_LOCAL=
if [ "$cmd" = "shell" ]; then
# should be outside of $tmpdir so we could copy it there before
# trap cleans things up
histstamp=$(git describe --always)-$(date -Iseconds)
TEMP_BASH_HISTORY_LOCAL=$(mktemp -t bash_history.$histstamp.XXXXXXXXX)
TEMP_BASH_HISTORY_FILENAME=$(basename $TEMP_BASH_HISTORY_LOCAL)
TEMP_BASH_HISTORY="$tmpdir/tmp/$TEMP_BASH_HISTORY_FILENAME"
# singularity 2.x seems to mess with HISTFILE - cannot pass through!
if version_gt 3 "$(singularity_version)"; then
error "Can manipulate bash history only with singularity >= 3"
fi
# Expose it to singularity environment
export SINGULARITYENV_HISTFILE="/tmp/$TEMP_BASH_HISTORY_FILENAME"
# We will copy it only if it was clean and new changes emerged
# Handle (save) protocol of interactive sessions
if ! has_changes ; then
# TODO: place at the top of the dataset!?
FINAL_BASH_HISTORY=".repronim/bash_histories/$histstamp"
# TODO: cleanup TEMP_BASH_HISTORY in case of crash?
else
warning "uncomitted changes present, 'shell' mode will NOT commit bash history." \
"You will find stored history at $TEMP_BASH_HISTORY_LOCAL"
fi
if [ "$#" -gt 1 ]; then
error "for 'shell' mode - do not provide any custom command. Got options: $@"
fi
cmd="exec"
args+=(bash)
fi

#
# The actual invocation
#
singularity "$cmd" -e -c -W "$tmpdir" -H "$updir/binds/HOME" -B "$PWD" --pwd "$PWD" "${args[@]}"


#
# Handle possible digital objects to save/be added to be saved
#
if [ ! -z "$FINAL_BASH_HISTORY" ]; then
if ! has_changes ; then
# TODO: someone might want to just record his wonderings around, so
# might be worth an option to force saving history only
info "no changes to the tree detected. Bash history will not be saved." \
"You will find stored history at $TEMP_BASH_HISTORY_LOCAL"
else
mkdir -p "$(dirname $FINAL_BASH_HISTORY)"
mv "$TEMP_BASH_HISTORY" "$FINAL_BASH_HISTORY"
# due to https://github.com/datalad/datalad/issues/3421 saving entire directory of histories
datalad save \
-m "[REPRONIM/CONTAINERS]: bash history for the interactive session

Actual changes might (or not, depending on the invocation) get committed in the next commit" \
"$(dirname $FINAL_BASH_HISTORY)"
fi
fi

if [ ! -z "$TEMP_BASH_HISTORY_LOCAL" ] && [ -e "$TEMP_BASH_HISTORY" ]; then
# So we did create it but did not move to be saved, so let's expose locally before it is wiped out
mv "$TEMP_BASH_HISTORY" "$TEMP_BASH_HISTORY_LOCAL"
fi