Skip to content
Open
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
6 changes: 6 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Install Python dependencies
run: pip install jinja2

- name: Assemble standalone install.sh
run: python3 assemble.py . > install.sh.assembled && mv install.sh.assembled install.sh

- name: Setup Pages
uses: actions/configure-pages@v5

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.ipynb_checkpoints/
Untitled.ipynb
eic-shell
__pycache__/
80 changes: 80 additions & 0 deletions assemble.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""Assemble a standalone install.sh from the source install.sh and template files.

The source install.sh generates each launcher script in two steps:
1. A variable-expanding heredoc writes a preamble with install-time values.
2. ``tail -n +2 "$SCRIPT_DIR/TEMPLATE" >> OUTPUT`` appends the template body
(skipping its ``#!/bin/bash`` shebang, which the preamble already provides).

This assembler replaces step 2 with an equivalent inline heredoc so the result
is a self-contained install.sh suitable for distribution (e.g., via curl | bash).

Jinja2's FileSystemLoader is used to locate and read each template file cleanly.

Usage:
python3 assemble.py [SOURCE_DIR]

Output is written to stdout.
"""

import re
import sys
from pathlib import Path

from jinja2 import Environment, FileSystemLoader


def assemble(source_dir: Path) -> str:
env = Environment(
loader=FileSystemLoader(str(source_dir)),
keep_trailing_newline=True,
)

install_sh = (source_dir / 'install.sh').read_text()

# Remove the SCRIPT_DIR assignment line (not needed in the assembled version
# because all template file references are replaced by inline heredocs below).
install_sh = re.sub(r'SCRIPT_DIR=.*\n', '', install_sh, count=1)

# Replace each ``tail -n +2 "$SCRIPT_DIR/TEMPLATE" >> OUTPUT`` call with an
# inline heredoc containing the template body (all lines after the shebang).
def inline_template(match: re.Match) -> str:
indent = match.group('indent')
template_name = match.group('template')
output = match.group('output')

# Load the template file via Jinja2's FileSystemLoader.
template_source, _, _ = env.loader.get_source(env, template_name)
# Skip the first line (#!/bin/bash shebang); the preamble heredoc that
# precedes this call already provides the shebang in the assembled file.
parts = template_source.split('\n', 1)
template_body = parts[1].rstrip('\n') if len(parts) > 1 else ''

# Derive a safe heredoc closing marker from the template filename.
marker = template_name.upper().replace('-', '_').replace('.', '_')

return (
f"{indent}cat >> {output} << '{marker}'\n"
f"{template_body}\n"
f"{marker}"
)

install_sh = re.sub(
r'(?P<indent>[ \t]*)tail -n \+2 "\$SCRIPT_DIR/(?P<template>[^"]+)" >> (?P<output>\S+)',
inline_template,
install_sh,
)

Comment on lines +62 to +67
# Validate: no $SCRIPT_DIR references should remain after assembly.
if '$SCRIPT_DIR/' in install_sh:
raise RuntimeError(
'Assembled install.sh still contains $SCRIPT_DIR/ references; '
'the assembly pattern may have drifted from the source.'
)

return install_sh


if __name__ == '__main__':
source_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path('.')
sys.stdout.write(assemble(source_dir))
81 changes: 81 additions & 0 deletions eic-shell.docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/bin/bash

## eic-shell Docker launcher
## Install-time values are baked in by install.sh via a preamble prepended to this file.
## The defaults below allow running this file directly for testing; override by
## setting the corresponding environment variables before execution.
: "${CONTAINER:=eic_xl}"
: "${TMPDIR:=}"
: "${VERSION:=nightly}"
: "${PREFIX:=$PWD}"
: "${DISABLE_CVMFS_USAGE:=}"
: "${IMG:=eicweb/eic_xl:nightly}"
: "${PLATFORM_FLAG:=}"
: "${MOUNT:=}"

function print_the_help {
echo "USAGE: ./eic-shell [OPTIONS] [ -- COMMAND ]"
echo "OPTIONAL ARGUMENTS:"
echo " -u,--upgrade Upgrade the container to the latest version"
echo " --noX Disable X11 forwarding on macOS"
echo " -h,--help Print this message"
echo ""
echo " Start the eic-shell containerized software environment (Docker version)."
echo ""
echo "EXAMPLES: "
echo " - Start an interactive shell: ./eic-shell"
echo " - Upgrade the container: ./eic-shell --upgrade"
echo " - Execute a single command: ./eic-shell -- <COMMAND>"
echo ""
exit
}

UPGRADE=
NOX=
while [ $# -gt 0 ]; do
key=$1
case $key in
-u|--upgrade)
UPGRADE=1
shift
;;
--noX)
NOX=1
shift
;;
-h|--help)
print_the_help
exit 0
;;
--)
shift
break
;;
*)
echo "ERROR: unknown argument: $key"
echo "use --help for more info"
exit 1
;;
esac
done

if [ x${DISPLAY} == "x" ] ; then
echo "No X11 display detected, disabling X11"
NOX=1
fi

if [[ -n "$UPGRADE" ]]; then
echo "Upgrading eic-shell..."
docker pull ${IMG} || exit 1
echo "eic-shell upgrade successful"
exit 0
fi

if [ "$(uname -s)" = 'Darwin' ] && [[ -z "$NOX" ]]; then
nolisten=`defaults find nolisten_tcp | grep nolisten | head -n 1 | awk '{print $3}'|cut -b 1 `
[[ $nolisten -ne 0 ]] && echo "For X support: In XQuartz settings --> Security --> enable \"Allow connections from network clients\" and restart (should be only once)."
xhost +localhost
dispnum=`ps -e |grep Xquartz | grep listen | grep -v xinit |awk '{print $5}'`
XSTUFF="-e DISPLAY=host.docker.internal${dispnum} -v /tmp/.X11-unix:/tmp/.X11-unix"
fi
docker run ${PLATFORM_FLAG} ${MOUNT} ${XSTUFF} -w=$PWD -it --rm -e EIC_SHELL_PREFIX=${PREFIX}/local ${IMG} eic-shell "$@"
112 changes: 112 additions & 0 deletions eic-shell.singularity
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/bin/bash

## eic-shell Singularity launcher
## Install-time values are baked in by install.sh via a preamble prepended to this file.
## The defaults below allow running this file directly for testing; override by
## setting the corresponding environment variables before execution.
: "${ORGANIZATION:=eicweb}"
: "${CONTAINER:=eic_xl}"
: "${TMPDIR:=}"
: "${VERSION:=nightly}"
: "${PREFIX:=$PWD}"
: "${DISABLE_CVMFS_USAGE:=}"
: "${INSTALLED_SINGULARITY:=singularity}"
: "${BINDPATH:=}"
: "${INSTALLED_SIF:=}"

function print_the_help {
echo "USAGE: ./eic-shell [OPTIONS] [ -- COMMAND ]"
echo "OPTIONAL ARGUMENTS:"
echo " -u,--upgrade Upgrade the container to the latest version"
echo " -n,--no-cvmfs Disable check for local CVMFS when updating. (D: enabled)"
echo " -c,--container Container family (D: $CONTAINER) (requires cvmfs)"
echo " -v,--version Version to install (D: $VERSION) (requires cvmfs)"
Comment on lines +20 to +23
echo " -h,--help Print this message"
echo ""
echo " Start the eic-shell containerized software environment (Singularity version)."
echo ""
echo "ENVIRONMENT VARIABLES:"
echo " SINGULARITY Path to the singularity executable (D: $INSTALLED_SINGULARITY)"
echo " SINGULARITY_OPTIONS Additional options to pass to singularity exec (D: none)"
echo ""
echo "EXAMPLES: "
echo " - Start an interactive shell: ./eic-shell"
echo " - Upgrade the container: ./eic-shell --upgrade"
echo " - Use different version: ./eic-shell --version $(date +%y.%m).0-stable"
echo " - Execute a single command: ./eic-shell -- <COMMAND>"
echo " - Use custom singularity: SINGULARITY=/path/to/singularity ./eic-shell"
echo " - Pass singularity options: SINGULARITY_OPTIONS='--nv' ./eic-shell"
echo ""
exit
}

UPGRADE=

while [ $# -gt 0 ]; do
key=$1
case $key in
-u|--upgrade)
UPGRADE=1
shift
;;
-n|--no-cvmfs)
DISABLE_CVMFS_USAGE=true
shift
;;
-c|--container)
CONTAINER=${2?Missing argument. Use --help for more info.}
export SIF=/cvmfs/singularity.opensciencegrid.org/${ORGANIZATION}/${CONTAINER}:${VERSION}
shift
shift
;;
-v|--version)
VERSION=${2?Missing argument. Use --help for more info.}
export SIF=/cvmfs/singularity.opensciencegrid.org/${ORGANIZATION}/${CONTAINER}:${VERSION}
shift
shift
;;
-h|--help)
print_the_help
exit 0
;;
--)
shift
break
;;
*)
echo "ERROR: unknown argument: $key"
echo "use --help for more info"
exit 1
;;
esac
done

if [[ -n "$UPGRADE" ]]; then
echo "Upgrading eic-shell..."
if [ -z "$DISABLE_CVMFS_USAGE" -a -d /cvmfs/singularity.opensciencegrid.org/${ORGANIZATION}/${CONTAINER}:${VERSION} ]; then
echo ""
echo "Note: You cannot manually update the container as you are using the CVMFS version."
echo " The container will automatically update every 24 hours."
echo " You can override this by setting the '--no-cvmfs' flag, which will"
echo " instantiate a local version."
echo " This is only recommended for expert usage."
echo ""
echo "This will only upgrade the eic-shell script itself."
echo ""
fi
FLAGS="-p ${PREFIX} -v ${VERSION}"
if [[ -n "$TMPDIR" ]]; then
FLAGS="${FLAGS} -t ${TMPDIR}"
fi
if [[ -n "$DISABLE_CVMFS_USAGE" ]]; then
FLAGS="${FLAGS} --no-cvmfs"
fi
curl -L https://eic.github.io/eic-shell/install.sh \
| bash -s -- ${FLAGS}
echo "eic-shell upgrade successful"
exit 0
fi

export EIC_SHELL_PREFIX=${PREFIX}/local
export SINGULARITY_BINDPATH=${BINDPATH}
${SINGULARITY:-${INSTALLED_SINGULARITY}} exec ${SINGULARITY_OPTIONS:-} ${SIF:-${INSTALLED_SIF}} eic-shell "$@"
Loading