From ce01b8f8f343a59fb4fbea6c867df83fb35c6b3c Mon Sep 17 00:00:00 2001
From: Sylvester Joosten <sylvester.joosten@gmail.com>
Date: Tue, 18 May 2021 16:17:37 +0000
Subject: [PATCH] Resolve "Simplify and consolidate container build"

New container names are jug_dev and jug_xl (dressed container)
---
 .gitlab-ci.yml                              | 324 ++++++++++++++++----
 README.md                                   |   7 +-
 containers/build_and_deploy.yml.in          |  62 ----
 containers/builder/Dockerfile               |  82 -----
 containers/builder/config.env               |  11 -
 containers/debian/Dockerfile                |  66 ++++
 containers/{release => debian}/bashrc       |   0
 containers/jug/Dockerfile.dev               | 208 +++++++++++++
 containers/jug/Dockerfile.xl                |  68 ++++
 containers/{release => jug}/eic-shell       |   0
 containers/release/Dockerfile.in            |  82 -----
 containers/release/config.env               |  11 -
 containers/release/configure_dockerfile.sh  |  26 --
 containers/release/eic.def.in               |  18 --
 gitlab-ci/cleanup_registry.sh               |  88 ++++++
 gitlab-ci/configure.sh                      |  68 ----
 gitlab-ci/docker/Makefile                   |  90 ------
 gitlab-ci/docker/version-stable.sh          |   6 -
 gitlab-ci/docker/version.sh                 |   5 -
 gitlab-ci/docker_login.sh                   | 109 +++++++
 gitlab-ci/docker_push.sh                    | 132 ++++++++
 gitlab-ci/singularity/build.sh              | 176 -----------
 install.py                                  | 137 ++++++---
 containers/builder/spack.yaml => spack.yaml |   0
 24 files changed, 1024 insertions(+), 752 deletions(-)
 delete mode 100644 containers/build_and_deploy.yml.in
 delete mode 100644 containers/builder/Dockerfile
 delete mode 100644 containers/builder/config.env
 create mode 100644 containers/debian/Dockerfile
 rename containers/{release => debian}/bashrc (100%)
 create mode 100644 containers/jug/Dockerfile.dev
 create mode 100644 containers/jug/Dockerfile.xl
 rename containers/{release => jug}/eic-shell (100%)
 delete mode 100644 containers/release/Dockerfile.in
 delete mode 100644 containers/release/config.env
 delete mode 100644 containers/release/configure_dockerfile.sh
 delete mode 100644 containers/release/eic.def.in
 create mode 100755 gitlab-ci/cleanup_registry.sh
 delete mode 100755 gitlab-ci/configure.sh
 delete mode 100644 gitlab-ci/docker/Makefile
 delete mode 100644 gitlab-ci/docker/version-stable.sh
 delete mode 100644 gitlab-ci/docker/version.sh
 create mode 100755 gitlab-ci/docker_login.sh
 create mode 100755 gitlab-ci/docker_push.sh
 delete mode 100644 gitlab-ci/singularity/build.sh
 rename containers/builder/spack.yaml => spack.yaml (100%)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 964163fc1..fd953c541 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,81 +1,281 @@
 image: eicweb.phy.anl.gov:4567/containers/image_recipes/ubuntu_dind:latest
 
+variables:
+  ## Application versions used for the main release ## note: nightly builds will always use the master branch
+  JUGGLER_VERSION: "v1.5.0"
+  NPDET_VERSION: "master"
+  EICD_VERSION: "master"
+  DETECTOR_VERSION: "master"
+  IP6_VERSION: "master"
+  ACCELERATOR_VERSION: "master"
+
+  ## Spack github version
+  SPACK_VERSION: "63915de99b6d4bc7c8b8a22dc45e5c3dd7ee4f26"
+
+  ## We need to enable Docker Buildkit to use cache mounts and better
+  ## build performance overal
+  DOCKER_BUILDKIT: 1
+
+  ## Dockerhub registry
+  DH_REGISTRY: eicweb
+  DH_PUSH: 1
+
+  ## TLS error resiliency: number of retries and second wait between tries 
+  ## (wait time is doubled with each attempt)
+  DOCKER_NTRIES: 5
+  DOCKER_WAIT_TIME: 5
+
+  ## By default this is not a nightly build, unless the CI says so
+  NIGHTLY: 0
+
 stages:
   - config
-  - build
-  - deploy
+  - build:base      ## base OS image
+  - build:dev       ## naked dev container image
+  - build:release   ## dressed release container image
+  - deploy          ## build/deploy singularity images
+  - finalize
 
-## make note if we cannot use caching for one of the stages
-## by touching files in .ci_env
 default:
   tags:
     - silicon
-  artifacts:
-    paths: 
-      - .ci-env
-detect_changes:builder:
-  stage: .pre
+  before_script:
+    - ./gitlab-ci/docker_login.sh -u $DH_REGISTRY -p $DH_EICWEB_TOKEN
+                                  -n $DOCKER_NTRIES -t $DOCKER_WAIT_TIME
+    - ./gitlab-ci/docker_login.sh --ci -n $DOCKER_NTRIES -t $DOCKER_WAIT_TIME
+  after_script:
+    - docker logout
+    - docker logout ${CI_REGISTRY}
+
+## only run CI for in the following cases:
+## master, stable branch, release tag, MR event and nightly builds
+## not that nightly builds got from the master branch, but with "NIGHTLY" set to
+## 1 which triggers a slightly different workflow
+workflow:
   rules:
-    - changes:
-        - containers/builder/spack.yaml
-        - spack/packages/**/*
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+    - if: '$CI_COMMIT_BRANCH == "master"'
+    - if: '$CI_COMMIT_BRANCH =~ /v[0-9]+\.[0-9]+-stable/'
+    - if: '$CI_COMMIT_TAG =~ /v[0-9]+\.[0-9]+\.[0-9]+/'
+
+## plan:
+##  Workflows:
+##  - master      --> config + all build stages + singularity
+##  - <nightly>   --> config + build:release only + singularity
+##  - v3.0-stable --> config + all build stages + singularity
+##  - v3.0.0      --> config + all build stages + singularity
+##  - MR          --> config + all build stages
+##
+##  Container images tags
+##  - master      --> testing
+##  - <nightly>   --> nightly
+##  - v3.0-stable --> 3.0-stable
+##  - v3.0.0      --> 3.0-stable, 3.0.0
+##  - MR          --> unstable (on all registries)
+##                --> unstable-mr-XXX (on eicweb only, untag at end of pipeline)
+##  - all other   --> do nothing
+##
+## caching strategy for dispatch to multiple nodes and to avoid
+##  --> try this strategy: https://medium.com/titansoft-engineering/docker-build-cache-sharing-on-multi-hosts-with-buildkit-and-buildx-eb8f7005918e
+##  (first try with buildx didn't pan out, let's try again later)
+
+version:
+  stage: config
   script:
-    - mkdir -p .ci-env
-    - touch .ci-env/builder-nc
-detect_changes:release:
-  stage: .pre
+    - |
+      VERSION=`head -n1 VERSION`
+      STABLE=${VERSION%.*}-stable
+      TESTING="testing"
+      UNSTABLE="unstable"
+    ## determine appropriate major docker tag for this scenario
+    - |
+      ## internal tag used for the CI. Also temporarily tagged
+      ## on eicweb to communicate between jobs (removed in cleanup job)
+      INTERNAL_TAG="testing-$VERSION"
+      ## main export tag, optional secondary export tag,
+      EXPORT_TAG=${TESTING}
+      EXPORT_TAG2=
+      if [ "x${CI_PIPELINE_SOURCE}" == "xmerge_request_event" ]; then
+        INTERNAL_TAG="unstable-mr-${CI_MERGE_REQUEST_ID}"
+        EXPORT_TAG=$UNSTABLE
+        EXPORT_TAG2=
+      elif [ "$CI_COMMIT_TAG" = "v${VERSION}" ]; then
+        INTERNAL_TAG="stable-br-${VERSION}"
+        EXPORT_TAG=${STABLE}
+        EXPORT_TAG2=${VERSION}
+      elif [ "$CI_COMMIT_BRANCH" == "v${STABLE}" ]; then
+        INTERNAL_TAG="stable-tag-${VERSION}"
+        EXPORT_TAG=${STABLE}
+        EXPORT_TAG2=
+      elif [ "$NIGHTLY" != "0" ]; then
+        INTERNAL_TAG="nightly-${VERSION}"
+        EXPORT_TAG="nightly"
+        EXPORT_TAG2=
+      fi
+      echo "INTERNAL_TAG=$INTERNAL_TAG" >> build.env
+      echo "EXPORT_TAG=$EXPORT_TAG" >> build.env
+      echo "EXPORT_TAG2=$EXPORT_TAG2" >> build.env
+      echo "PIPELINE_TMP_TAG=$PIPELINE_TMP_TAG" >> build.env
+      cat build.env
+
+  artifacts:
+    reports:
+      dotenv: build.env
+
+## base job for all build jobs. Dependent jobs are expected to set
+## the BUILD_IMAGE environment variable, and take care of the actual
+## docker build during the "script" step
+## note that the nightly builds use a different pipeline
+.build:
   rules:
-    - changes:
-        - containers/release/configure_release.sh
+    - if: '$NIGHTLY != "0"'
+      when: never
+    - when: on_success
+  ## cookie-cutter docker push code, to be included at the
+  ## end of the regular job scripts
   script:
-    - mkdir -p .ci-env
-    - touch .ci-env/release-nc
+    - ./gitlab-ci/docker_push.sh -i ${BUILD_IMAGE} -l ${INTERNAL_TAG} 
+                                 -n $DOCKER_NTRIES -t $DOCKER_WAIT_TIME
+                                 ${EXPORT_TAG} ${EXPORT_TAG2}
+    - ./gitlab-ci/docker_push.sh -i ${BUILD_IMAGE} -l ${INTERNAL_TAG} 
+                                 -n $DOCKER_NTRIES -t $DOCKER_WAIT_TIME
+                                 ${INTERNAL_TAG} --eicweb
 
-## Init our job for our desired branches/tags/events
-init:
-  stage: config
+## Images:
+## debian_base --> jug_dev  --> jug_xl
+##             ---------------> jug_sim
+##             ---------------> jug_ml
+
+debian_base:default:
+  extends: .build
+  stage: build:base
+  needs:
+    - version
+  variables:
+    BUILD_IMAGE: "debian_base"
   script:
-    - ./gitlab-ci/configure.sh containers/build_and_deploy.yml.in
-  artifacts:
-    paths:
-      - build_and_deploy.yml
-
-## Quick debug build for new spack packages
-#run:quick_build:
-#  image: eicweb.phy.anl.gov:4567/containers/eic_container/eic_builder:latest
-#  stage: build
-#  script:
-#    - rm -r /opt/spack/np-spack && cp -r spack /opt/spack/np-spack
-#    - source /opt/spack/share/spack/setup-env.sh
-#    - spack env activate /opt/spack-environment
-#    - spack add madx && spack install
-
-## Dispatch if we ran the previous stage
-run:default:
-  stage: build
-  trigger:
-    include: 
-      - artifact: build_and_deploy.yml
-        job: init
-    strategy: depend
-
-singularity:
-  stage: deploy
-  needs: ["run:default"]
+    - docker build -t ${CI_REGISTRY_IMAGE}/${BUILD_IMAGE}:${INTERNAL_TAG} 
+                   containers/debian
+    - !reference [.build, script]
+
+jug_dev:default:
+  extends: .build
+  stage: build:dev
+  needs:
+    - version
+    - debian_base:default
+  variables:
+    BUILD_IMAGE: "jug_dev"
+  script:
+    ## calculate a hash based on the spack.yaml file and the spack directory
+    ## and use this spack as a docker variable to force a rebuild when there
+    ## is a change (versus rerun from cache)
+    - PACKAGE_HASH=$(tar cf - spack* | sha1sum | head -c40)
+    - echo "PACKAGE_HASH= ${PACKAGE_HASH}"
+    ## move spacke directory and spack.yaml into the container build directory
+    - cp -r spack containers/jug
+    - cp -r spack.yaml containers/jug/spack/spack.yaml
+    ## now build our image
+    - docker build -t ${CI_REGISTRY_IMAGE}/${BUILD_IMAGE}:${INTERNAL_TAG} 
+                   -f containers/jug/Dockerfile.dev
+                   --build-arg SPACK_VERSION=${SPACK_VERSION}
+                   --build-arg CACHE_BUST=${PACKAGE_HASH}
+                   --build-arg INTERNAL_TAG=${INTERNAL_TAG}
+                   containers/jug
+    - !reference [.build, script]
+
+jug_xl:default:
+  extends: .build
+  stage: build:release
+  needs:
+    - version
+    - jug_dev:default
+  variables:
+    BUILD_IMAGE: "jug_xl"
+  script:
+    - docker build -t ${CI_REGISTRY_IMAGE}/${BUILD_IMAGE}:${INTERNAL_TAG} 
+                   -f containers/jug/Dockerfile.xl
+                   --build-arg INTERNAL_TAG=${INTERNAL_TAG}
+                   --build-arg JUGGLER_VERSION=${JUGGLER_VERSION}
+                   --build-arg NPDET_VERSION=${NPDET_VERSION}
+                   --build-arg EICD_VERSION=${EICD_VERSION}
+                   --build-arg DETECTOR_VERSION=${DETECTOR_VERSION}
+                   --build-arg IP6_VERSION=${IP6_VERSION}
+                   --build-arg ACCELERATOR_VERSION=${ACCELERATOR_VERSION}
+                   containers/jug
+    - !reference [.build, script]
+
+jug_xl:nightly:
+  extends: .build
+  stage: build:release
   rules:
-    - if: '$CI_COMMIT_BRANCH == "master"'
-      when: on_success
-    - if: '$CI_COMMIT_BRANCH =~ /v[0-9]+\.[0-9]+-stable/'
-      when: on_success
-    - if: '$CI_COMMIT_TAG =~ /v[0-9]+\.[0-9]+\.[0-9]+/'
-      when: on_success
+    - if: '$NIGHTLY != "0"'
+      when: always
+    - when: never
+  needs:
+    - version
+  variables:
+    BUILD_IMAGE: "jug_xl"
   script:
-     - mkdir -p build 
-     - cd build
-     - ../gitlab-ci/configure.sh ../containers/release/eic.def.in
-     - /bin/bash ../gitlab-ci/singularity/build.sh eic.def
+    - docker build -t ${CI_REGISTRY_IMAGE}/${BUILD_IMAGE}:${INTERNAL_TAG} 
+                   -f containers/jug/Dockerfile.xl
+                   --build-arg INTERNAL_TAG="testing"
+                   containers/jug
+    - !reference [.build, script]
+
+.singularity:
+  rules:
+    - if: '$NIGHTLY != "0"'
+      when: never
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+      when: never
+    - when: on_success
   artifacts:
       expire_in: 90 days
       paths:
-        - build/eic.sif
-        - build/eic.def
+        - build/${BUILD_IMAGE}.sif
+  script:
+    - mkdir build
+    - singularity pull build/${BUILD_IMAGE}.sif docker://${CI_REGISTRY_IMAGE}/${BUILD_IMAGE}:${INTERNAL_TAG}
+
+jug_dev:singularity:default:
+  extends: .singularity
+  stage: deploy
+  needs: 
+    - version
+    - jug_dev:default
+  variables:
+    BUILD_IMAGE: "jug_dev"
+
+jug_xl:singularity:default:
+  extends: .singularity
+  stage: deploy
+  needs: 
+    - version
+    - jug_xl:default
+  variables:
+    BUILD_IMAGE: "jug_xl"
+
+jug_xl:singularity:nightly:
+  extends: .singularity
+  stage: deploy
+  rules:
+    - if: '$NIGHTLY != "0"'
+      when: always
+    - when: never
+  needs: 
+    - version
+    - jug_xl:nightly
+  variables:
+    BUILD_IMAGE: "jug_xl"
+
+cleanup:
+  stage: finalize
+  dependencies:
+    - version
+  script:
+    ## remove the pipeline specific export from eicweb if needed
+    - echo "Cleaning up pipeline specific docker tags if needed"
+    - ./gitlab-ci/cleanup_registry.sh -i debian_base -r 66 ${INTERNAL_TAG}
+    - ./gitlab-ci/cleanup_registry.sh -i jug_dev -r 68 ${INTERNAL_TAG}
+    - ./gitlab-ci/cleanup_registry.sh -i jug_xl -r 69 ${INTERNAL_TAG}
diff --git a/README.md b/README.md
index 1ba7d9ca0..593418651 100644
--- a/README.md
+++ b/README.md
@@ -15,9 +15,9 @@ cd eic_container
    modeuefile will be installed to `$PREFIX/../../etc/modulefiles`. 
    You can use the `-v` flag to select the version you want to install, or omit the 
    flag if you want to install the master build. The recommended stable 
-   release version is `v2.8.0`.
+   release version is `v2.9.2`.
 ```bash
-./install.py -v 2.8.0 <PREFIX>
+./install.py -v 2.9.2 <PREFIX>
 ```
 
    Available flags:
@@ -118,11 +118,12 @@ Included software:
     - podio@0.13
     - geant4@10.7.1
     - dd4hep@1.16.1
-    - acts@5.00.0
+    - acts@8.01.0
     - gaudi@34.0
     - dawn@3.91a
     - dawncut@1.54a
     - opencascade
+    - madx@5.06.1
   - The singularity build exports the following applications:
     - eic_shell: a development shell in the image
     - container_dev: same as EIC shell
diff --git a/containers/build_and_deploy.yml.in b/containers/build_and_deploy.yml.in
deleted file mode 100644
index 52c2aa4a6..000000000
--- a/containers/build_and_deploy.yml.in
+++ /dev/null
@@ -1,62 +0,0 @@
-image: eicweb.phy.anl.gov:4567/containers/image_recipes/ubuntu_dind:latest
-
-stages:
-  - build
-  - config
-  - package
-
-## variables:
-##   - TARGET_XXX: docker build target (including cache modifier) 
-##     (stable, stable-cached, unstable, unstable-cached)
-##   - TAG: main docker tag to be used internally 
-##     (e.g. 2.5-stable/unstable/<version>)
-##   - PUBLISH: docker publish directives
-variables:
-  TARGET_BUILDER: @TARGET_BUILDER@
-  TARGET_RELEASE: @TARGET_RELEASE@
-  TAG: @TAG@
-  PUBLISH: "@PUBLISH@"
-
-default:
-  tags:
-    - silicon
-
-builder:
-  stage: build
-  script:
-    - cp -r gitlab-ci/docker/* containers/builder
-    - cp -r spack containers/builder/np-spack
-    - cd containers/builder
-    - head Dockerfile
-    - make login
-    - echo "Creating builder image for $TARGET_BUILDER"
-    - make $TARGET_BUILDER
-    - echo "Publishing image $PUBLISH"
-    - make $PUBLISH
-
-config:
-  image: eicweb.phy.anl.gov:4567/containers/eic_container/eic_builder:$TAG
-  stage: config
-  needs: ["builder"]
-  script:
-    - bash containers/release/configure_dockerfile.sh $TAG
-  artifacts:
-    paths:
-      - config
-      
-release:
-  stage: package
-  needs: ["config"]
-  script:
-    - cp -r gitlab-ci/docker/* containers/release
-    - cp config/Dockerfile containers/release/Dockerfile
-    - cp config/eic-env.sh containers/release/eic-env.sh
-    - cd containers/release
-    - make login
-    - echo "Creating release image for $TARGET_RELEASE"
-    - make $TARGET_RELEASE
-    - echo "Publishing image $PUBLISH"
-    - make $PUBLISH
-  artifacts:
-    paths:
-      - config
diff --git a/containers/builder/Dockerfile b/containers/builder/Dockerfile
deleted file mode 100644
index 7d79f814e..000000000
--- a/containers/builder/Dockerfile
+++ /dev/null
@@ -1,82 +0,0 @@
-#syntax=docker/dockerfile:1.2
-
-# Builder with Argonne EIC software
-#
-FROM eicweb.phy.anl.gov:4567/containers/image_recipes/debian_spack:snapshot-20210408
-
-LABEL maintainer="Sylvester Joosten <sjoosten@anl.gov>" \
-      name="eic_builder" \
-      group="eic_builder" \
-      march="native" \
-      basedist="debian" \
-      base="debian"
-
-ENV DOCKERFILE_BASE=debian            \
-    DOCKERFILE_DISTRO=debian          \
-    DOCKERFILE_DISTRO_VERSION=20210408-testing \
-    SPACK_ROOT=/opt/spack             \
-    DEBIAN_FRONTEND=noninteractive    \
-    CURRENTLY_BUILDING_DOCKER_IMAGE=1 \
-    container=docker
-
-
-## install ghostview/ghostscript needed by some of the tools
-#RUN apt-get -yqq update \
-# && apt-get -yqq install --no-install-recommends \
-#        ghostscript \
-#        gv \
-# && rm -rf /var/lib/apt/lists/*
-
-## Setup our environment definition
-COPY spack.yaml /opt/spack-environment/spack.yaml
-
-## Ensure an up-to-date custom package list
-## TODO: We should just remove this from the upstream container
-##       and only initialize the custom packages here for more
-##       transparency
-RUN rm -rf $SPACK_ROOT/np-spack \
- && echo "repos:" > $SPACK_ROOT/etc/spack/repos.yaml \
- && echo " - $SPACK_ROOT/np-spack" >> $SPACK_ROOT/etc/spack/repos.yaml
-COPY np-spack $SPACK_ROOT/np-spack
-
-## Install the software, no garbage collection at this stage
-## as this is a raw builder image
-RUN \
-    cd /opt/spack-environment \
- && spack env activate . \
- && spack install -j64 \
- && spack clean -a
-
-## extra post-spack steps
-## Including some small fixes:
-##   - Somehow PODIO env isn't automatically set, 
-##   - and Gaudi likes BINARY_TAG to be set
-RUN cd /opt/spack-environment \
- && echo -n ""                                        \
- && echo "Grabbing environment info"                  \
- && spack env activate --sh -d . > /etc/profile.d/z10_spack_environment.sh \
- && sed -i "s?LD_LIBRARY_PATH=?&/lib/x86_64-linux-gnu:?" /etc/profile.d/z10_spack_environment.sh \
- && cd /opt/spack-environment                         \
- && echo -n ""                                        \
- && echo "Add extra environment variables for Podio and Gaudi" \
- && spack env activate .                              \
- && export PODIO=`spack find -p podio | grep software | awk '{print $2}'` \
- && echo "export PODIO=${PODIO};" >> /etc/profile.d/z10_spack_environment.sh \
- && echo "export BINARY_TAG=x86_64-linux-gcc9-opt" >> /etc/profile.d/z10_spack_environment.sh \
- && cd /opt/spack-environment && spack env activate . \
- && echo -n ""                                        \
- && echo "Installing additional python packages"      \
- && pip install --trusted-host pypi.org               \
-                --trusted-host files.pythonhosted.org \
-                --no-cache-dir                        \
-        ipython matplotlib scipy yapf pandas          \
-        jupyter jupyterlab uproot pyunfold seaborn    \
- && echo -n ""                                        \
- && echo "Executing cmake patch for dd4hep 16.1"      \ 
- && sed -i "s/FIND_PACKAGE(Python/#&/" /usr/local/cmake/DD4hepBuild.cmake
-
-## make sure we have the entrypoints setup correctly
-ENTRYPOINT []
-CMD ["bash", "--rcfile", "/etc/profile", "-l"]
-USER 0
-WORKDIR /
diff --git a/containers/builder/config.env b/containers/builder/config.env
deleted file mode 100644
index fea4ef6c4..000000000
--- a/containers/builder/config.env
+++ /dev/null
@@ -1,11 +0,0 @@
-REG_HOST  ?= eicweb.phy.anl.gov
-REG_PORT  ?= 4567
-REG_NAME  ?= $(REG_HOST):$(REG_PORT)
-REG_URL   ?= https://$(REG_HOST)
-
-APP_NAME     = eic_builder
-REPO_NAME    = eic_builder
-
-GL_GROUP     = eic_container
-GL_REG_GROUP = containers/eic_container
-GL_REG_NAME  = $(REG_NAME)
diff --git a/containers/debian/Dockerfile b/containers/debian/Dockerfile
new file mode 100644
index 000000000..417e939f0
--- /dev/null
+++ b/containers/debian/Dockerfile
@@ -0,0 +1,66 @@
+#syntax=docker/dockerfile:1.2
+
+# Minimal container based on Debian Testing for up-to-date packages. 
+# Very lightweight container with a minimal build environment
+
+FROM  amd64/debian:testing-20210408-slim
+LABEL maintainer="Sylvester Joosten <sjoosten@anl.gov>" \
+      name="debian_base" \
+      march="amd64"
+
+COPY bashrc /root/.bashrc
+
+ENV CLICOLOR_FORCE=1                                                    \
+    LANGUAGE=en_US.UTF-8                                                \
+    LANG=en_US.UTF-8                                                    \
+    LC_ALL=en_US.UTF-8
+
+## Install additional packages. Remove the auto-cleanup functionality
+## for docker, as we're using the new buildkit cache instead.
+## We also install gitlab-runner, from the buster package (as bullseye is not available atm)
+RUN --mount=type=cache,target=/var/cache/apt                            \
+    rm -f /etc/apt/apt.conf.d/docker-clean                              \
+ && ls /var/cache/apt/*                                                 \
+ && apt-get -yqq update                                                 \
+ && apt-get -yqq install --no-install-recommends                        \
+        ca-certificates                                                 \
+        curl                                                            \
+        file                                                            \
+        build-essential                                                 \
+        g++-10                                                          \
+        gcc-10                                                          \
+        gfortran-10                                                     \
+        git                                                             \
+        gnupg2                                                          \
+        iproute2                                                        \
+        locales                                                         \
+        lua-posix                                                       \
+        make                                                            \
+        unzip                                                           \
+        nano                                                            \
+        vim-nox                                                         \
+        less                                                            \
+        clang-format                                                    \
+        openssh-client                                                  \
+        wget                                                            \
+        ghostscript                                                     \
+        gv                                                              \
+        poppler-utils                                                   \
+        parallel                                                        \
+ && localedef -i en_US -f UTF-8 en_US.UTF-8                             \
+ && update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100  \
+ && update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100  \
+ && update-alternatives --install /usr/bin/gfortran gfortran            \
+                                  /usr/bin/gfortran-10 100              \
+ && cc --version                                                        \
+ && curl -L                                                             \
+    "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" \
+    | bash                                                              \
+ && sed -i "s/bullseye/buster/"                                         \
+           /etc/apt/sources.list.d/runner_gitlab-runner.list            \
+ && apt-get -yqq update                                                 \
+ && apt-get -yqq install --no-install-recommends                        \
+        gitlab-runner                                                   \
+ && apt-get -yqq autoremove                                             \
+ && rm -rf /var/lib/apt/lists/*                                         
+
diff --git a/containers/release/bashrc b/containers/debian/bashrc
similarity index 100%
rename from containers/release/bashrc
rename to containers/debian/bashrc
diff --git a/containers/jug/Dockerfile.dev b/containers/jug/Dockerfile.dev
new file mode 100644
index 000000000..900c9dc4a
--- /dev/null
+++ b/containers/jug/Dockerfile.dev
@@ -0,0 +1,208 @@
+#syntax=docker/dockerfile:1.2
+ARG INTERNAL_TAG="testing" 
+
+## ========================================================================================
+## STAGE1: spack builder image
+## EIC builder image with spack
+## ========================================================================================
+FROM eicweb.phy.anl.gov:4567/containers/eic_container/debian_base:${INTERNAL_TAG} as builder
+
+## instal some extra spack dependencies
+RUN --mount=type=cache,target=/var/cache/apt                            \
+    rm -f /etc/apt/apt.conf.d/docker-clean                              \
+ && apt-get -yqq update                                                 \
+ && apt-get -yqq install --no-install-recommends                        \
+        python3                                                         \
+        python3-pip                                                     \
+        python3-setuptools                                              \
+        tcl                                                             \
+        uuid-dev                                                        \
+        libfcgi-dev                                                     \
+        x11proto-xext-dev                                               \
+ && pip3 install boto3                                                  \ 
+ && rm -rf /var/lib/apt/lists/*
+
+## Setup spack
+## parts:
+ENV SPACK_ROOT=/opt/spack
+ARG SPACK_VERSION="develop"
+RUN echo "Part 1: regular spack install (as in containerize)"           \
+ && git clone https://github.com/spack/spack.git /tmp/spack-staging     \
+ && cd /tmp/spack-staging && git checkout $SPACK_VERSION && cd -        \
+ && mkdir -p $SPACK_ROOT/opt/spack                                      \
+ && cp -r /tmp/spack-staging/bin $SPACK_ROOT/bin                        \
+ && cp -r /tmp/spack-staging/etc $SPACK_ROOT/etc                        \
+ && cp -r /tmp/spack-staging/lib $SPACK_ROOT/lib                        \
+ && cp -r /tmp/spack-staging/share $SPACK_ROOT/share                    \
+ && cp -r /tmp/spack-staging/var $SPACK_ROOT/var                        \
+ && cp -r /tmp/spack-staging/.git $SPACK_ROOT/.git                      \
+ && rm -rf /tmp/spack-staging                                           \
+ && echo 'export LD_LIBRARY_PATH=/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH'\ 
+        >> $SPACK_ROOT/share/setup-env.sh                               \
+ && ln -s $SPACK_ROOT/share/spack/docker/entrypoint.bash                \
+          /usr/sbin/docker-shell                                        \
+ && ln -s $SPACK_ROOT/share/spack/docker/entrypoint.bash                \
+          /usr/sbin/interactive-shell                                   \
+ && ln -s $SPACK_ROOT/share/spack/docker/entrypoint.bash                \
+          /usr/sbin/spack-env                                           \
+ && echo "Part 2: Set target to generic x86_64"                         \
+ && echo "packages:" > $SPACK_ROOT/etc/spack/packages.yaml              \
+ && echo "  all:" >> $SPACK_ROOT/etc/spack/packages.yaml                \
+ && echo "    target: [x86_64]" >> $SPACK_ROOT/etc/spack/packages.yaml  \
+ && cat $SPACK_ROOT/etc/spack/packages.yaml                             \
+ && echo "Part 3: Set config to allow use of more cores for builds"     \
+ && echo "(and some other settings)"                                    \
+ && echo "config:" > $SPACK_ROOT/etc/spack/config.yaml                  \
+ && echo "  suppress_gpg_warnings: true"                                \
+        >> $SPACK_ROOT/etc/spack/config.yaml                            \
+ && echo "  build_jobs: 64" >> $SPACK_ROOT/etc/spack/config.yaml        \
+ && echo "  install_tree:" >> $SPACK_ROOT/etc/spack/config.yaml         \
+ && echo "    root: /opt/software" >> $SPACK_ROOT/etc/spack/config.yaml \
+ && cat $SPACK_ROOT/etc/spack/config.yaml
+
+SHELL ["docker-shell"]
+
+## Setup spack buildcache mirrors, including an internal
+## spack mirror using the docker build cache, and
+## a backup mirror on the internal B010 network
+RUN --mount=type=cache,target=/var/cache/spack-mirror                   \
+    export PATH=$PATH:$SPACK_ROOT/bin                                   \
+ && wget 10.10.241.24/spack-mirror/sodium.pub --no-check-certificate    \
+ && spack gpg trust sodium.pub                                          \
+ && spack mirror add silicon http://10.10.241.24/spack-mirror           \
+ && spack mirror add docker /var/cache/spack-mirror                     \
+ && spack mirror list
+
+## Setup our custom environment and package overrides
+COPY spack $SPACK_ROOT/eic-spack
+RUN echo "repos:" > $SPACK_ROOT/etc/spack/repos.yaml                    \
+ && echo " - $SPACK_ROOT/eic-spack" >> $SPACK_ROOT/etc/spack/repos.yaml \
+ && mkdir /opt/spack-environment                                        \
+ && mv $SPACK_ROOT/eic-spack/spack.yaml /opt/spack-environment/spack.yaml
+
+## This variable will change whenevery either spack.yaml or our spack package
+## overrides change, triggering a rebuild
+ARG CACHE_BUST="hash"
+## Now execute the main build (or fetch from cache if possible)
+## note, no-check-signature is needed to allow the quicker signature-less
+## packages from the internal (docker) buildcache
+RUN --mount=type=cache,target=/var/cache/spack-mirror                   \
+    cd /opt/spack-environment                                           \
+ && ls /var/cache/spack-mirror                                          \
+ && spack env activate .                                                \
+ && spack install -j64 --no-check-signature                             \
+ && spack clean -a
+
+## Update the local build cache if needed. Consists of 3 steps:
+## 1. Remove the B010 network buildcache
+## 2. Get a list of all packages, and compare with what is already on
+##    the buildcache (using package hash)
+## 3. Add packages that need to be added to buildcache if any
+RUN --mount=type=cache,target=/var/cache/spack-mirror                   \
+    spack buildcache list --allarch --long                              \
+     | grep -v -e '---'                                                 \
+     | sed "s/@.\+//"                                                   \
+     | sort > tmp.buildcache.txt                                        \
+ && spack find --no-groups --long                                       \
+     | tail -n +2                                                       \
+     | grep -v "==>"                                                    \
+     | sed "s/@.\+//"                                                   \
+     | sort > tmp.manifest.txt                                          \
+ && comm -23 tmp.manifest.txt tmp.buildcache.txt                        \
+     > tmp.needsupdating.txt                                            \
+ && if [ $(wc -l < tmp.needsupdating.txt) -ge 1 ]; then                 \
+     cat tmp.needsupdating.txt                                          \
+        | awk '{print($2);}'                                            \
+        | tr '\n' ' '                                                   \
+        | xargs spack buildcache create -uaf -d /var/cache/spack-mirror \
+     && spack buildcache update-index -d /var/cache/spack-mirror;       \
+    fi                                                                  \
+ && rm tmp.manifest.txt                                                 \
+ && rm tmp.buildcache.txt                                               \
+ && rm tmp.needsupdating.txt
+
+## extra post-spack steps
+## Including some small fixes:
+##   - Somehow PODIO env isn't automatically set, 
+##   - and Gaudi likes BINARY_TAG to be set
+RUN cd /opt/spack-environment                                           \
+ && echo -n ""                                                          \
+ && echo "Grabbing environment info"                                    \
+ && spack env activate --sh -d .                                        \
+        > /etc/profile.d/z10_spack_environment.sh                       \
+ && sed -i "s?LD_LIBRARY_PATH=?&/lib/x86_64-linux-gnu:?"                \
+        /etc/profile.d/z10_spack_environment.sh                         \
+ && cd /opt/spack-environment                                           \
+ && echo -n ""                                                          \
+ && echo "Add extra environment variables for Podio and Gaudi"          \
+ && spack env activate .                                                \
+ && export PODIO=`spack find -p podio                                   \
+        | grep software                                                 \
+        | awk '{print $2}'`                                             \
+ && echo "export PODIO=${PODIO};"                                       \
+        >> /etc/profile.d/z10_spack_environment.sh                      \
+ && echo "export BINARY_TAG=x86_64-linux-gcc9-opt"                      \
+        >> /etc/profile.d/z10_spack_environment.sh                      \
+ && cd /opt/spack-environment && spack env activate .                   \
+ && echo -n ""                                                          \
+ && echo "Installing additional python packages"                        \
+ && pip install --trusted-host pypi.org                                 \
+                --trusted-host files.pythonhosted.org                   \
+                --no-cache-dir                                          \
+        ipython matplotlib scipy yapf pandas                            \
+        jupyter jupyterlab uproot pyunfold seaborn                      \
+ && echo -n ""                                                          \
+ && echo "Executing cmake patch for dd4hep 16.1"                        \                
+ && sed -i "s/FIND_PACKAGE(Python/#&/" /usr/local/cmake/DD4hepBuild.cmake
+
+## make sure we have the entrypoints setup correctly
+ENTRYPOINT []
+CMD ["bash", "--rcfile", "/etc/profile", "-l"]
+USER 0
+WORKDIR /
+
+## ========================================================================================
+## STAGE 2: staging image with unnecessariy packages removed and stripped binaries
+## ========================================================================================
+FROM builder as staging
+
+RUN cd /opt/spack-environment && spack env activate . && spack gc -y
+# Strip all the binaries
+# This reduces the image by factor of x2, so worth the effort
+# note that we do not strip python libraries as can cause issues in some cases
+RUN find -L /usr/local/*                                                \
+         -type d -name site-packages -prune -false -o                   \
+         -type f -not -name "zdll.lib"                                  \
+         -exec readlink -f '{}' \;                                      \
+      | xargs file -i                                                   \
+      | grep 'charset=binary'                                           \
+      | grep 'x-executable\|x-archive\|x-sharedlib'                     \
+      | awk -F: '{print $1}' | xargs strip -s
+
+## ========================================================================================
+## STAGE 3
+## Lean target image
+## ========================================================================================
+FROM eicweb.phy.anl.gov:4567/containers/eic_container/debian_base:${INTERNAL_TAG}
+
+LABEL maintainer="Sylvester Joosten <sjoosten@anl.gov>" \
+      name="jug_xl" \
+      march="amd64"
+
+## copy over everything we need from staging in a single layer :-)
+RUN --mount=from=staging,target=/staging                                \
+    rm -rf /usr/local                                                   \
+ && cp -r /staging/opt/software /opt/software                           \
+ && cp -r /staging/usr/local /usr/local                                 \
+ && cp /staging/etc/profile.d/z10_spack_environment.sh /etc/eic-env.sh  \
+ && sed -i '/MANPATH/ s/;$/:;/' /etc/eic-env.sh                         \
+ && cp /etc/eic-env.sh /etc/profile.d/z10_eic-env.sh
+
+COPY eic-shell /usr/local/bin/eic-shell
+
+## make sure we have the entrypoints setup correctly
+ENTRYPOINT []
+CMD ["bash", "--rcfile", "/etc/profile", "-l"]
+USER 0
+WORKDIR /
+SHELL ["/usr/local/bin/eic-shell"]
diff --git a/containers/jug/Dockerfile.xl b/containers/jug/Dockerfile.xl
new file mode 100644
index 000000000..6a6e3fa7c
--- /dev/null
+++ b/containers/jug/Dockerfile.xl
@@ -0,0 +1,68 @@
+#syntax=docker/dockerfile:1.2
+ARG INTERNAL_TAG="testing" 
+
+## ========================================================================================
+## STAGE1: spack builder image
+## EIC builder image with spack
+## ========================================================================================
+FROM eicweb.phy.anl.gov:4567/containers/eic_container/jug_dev:${INTERNAL_TAG}
+
+ARG JUGGLER_VERSION="master"
+ARG NPDET_VERSION="master"
+ARG EICD_VERSION="master"
+
+## cachebust that can change values to trigger a rebuild, useful for nightly builds
+## off the master
+ARG CACHEBUST=1
+RUN cd /tmp                                                                     \
+ && echo "INSTALLING NPDET"                                                     \
+ && git clone -b ${NPDET_VERSION}  https://eicweb.phy.anl.gov/EIC/NPDet.git     \
+ && cmake -B build -S NPDet -DCMAKE_CXX_STANDARD=17                             \
+ && cmake --build build -j12 -- install                                         \
+ && rm -rf build NPDet                                                          \
+ && echo "INSTALLING EICD"                                                      \
+ && git clone -b ${EICD_VERSION}  https://eicweb.phy.anl.gov/EIC/eicd.git       \
+ && cmake -B build -S eicd -DCMAKE_CXX_STANDARD=17                              \  
+ && cmake --build build -j12 -- install                                         \
+ && rm -rf build eicd                                                           \
+ && echo "INSTALLING JUGGLER"                                                   \
+ && git clone -b ${JUGGLER_VERSION} https://eicweb.phy.anl.gov/EIC/juggler.git  \
+ && cmake -B build -S juggler -DCMAKE_CXX_STANDARD=17                           \      
+ && cmake --build build -j12 -- install                                         \
+ && rm -rf build juggler
+
+## also install detector/ip geometries into opt
+## FIXME: need to add proper compact file install directly to the reference detector
+##        build
+ARG DETECTOR_VERSION="master"
+ARG IP6_VERSION="master"
+ARG ACCELERATOR_VERSION="master"
+RUN cd /tmp                                                                     \
+ && DETECTOR_PREFIX=/opt/detector                                               \
+ && DETECTOR_DATA=$DETECTOR_PREFIX/share/reference_detector                     \
+ && mkdir -p /opt/detector/share/reference_detector                             \
+ && echo "INSTALLING REFERENCE DETECTOR"                                        \
+ && git clone -b ${DETECTOR_VERSION}                                            \
+                https://eicweb.phy.anl.gov/EIC/detectors/reference_detector.git \
+ && cmake -B build -S reference_detector -DCMAKE_CXX_STANDARD=17                \
+          -DCMAKE_INSTALL_PREFIX=${DETECTOR_PREFIX}                             \
+ && cmake --build build -j12 -- install                                         \
+ && cp -r reference_detector/compact                                            \
+          reference_detector/reference_detector.xml                             \
+          ${DETECTOR_DATA}                                                      \
+ && rm -rf build reference_detector                                             \
+ && echo "INSTALLING ACCELERATOR GEOMETRY"                                      \
+ && git clone -b ${ACCELERATOR_VERSION}                                         \
+                https://eicweb.phy.anl.gov/EIC/detectors/accelerator.git        \
+ && cp -r accelerator/eic                                                       \
+          ${DETECTOR_DATA}                                                      \
+ && rm -rf accelerator                                                          \
+ && echo "INSTALLING IP6 GEOMETRY"                                              \
+ && git clone -b ${IP6_VERSION}                                                 \
+                https://eicweb.phy.anl.gov/EIC/detectors/ip6.git                \
+ && cmake -B build -S ip6 -DCMAKE_CXX_STANDARD=17                               \
+          -DCMAKE_INSTALL_PREFIX=${DETECTOR_PREFIX}                             \
+ && cmake --build build -j12 -- install                                         \
+ && cp -r ip6/ip6                                                               \
+          ${DETECTOR_DATA}                                                      \
+ && rm -rf build ip6
diff --git a/containers/release/eic-shell b/containers/jug/eic-shell
similarity index 100%
rename from containers/release/eic-shell
rename to containers/jug/eic-shell
diff --git a/containers/release/Dockerfile.in b/containers/release/Dockerfile.in
deleted file mode 100644
index 3565c98c9..000000000
--- a/containers/release/Dockerfile.in
+++ /dev/null
@@ -1,82 +0,0 @@
-# Release container for Argonne EIC software
-# Copy over relevant files from builder
-#
-FROM eicweb.phy.anl.gov:4567/containers/eic_container/eic_builder:@TAG@ as builder
-RUN cd /opt/spack-environment && spack env activate . && spack gc -y
-
-# Strip all the binaries
-# This reduces the image size from 3.2GB --> 1.7GB so worth the time
-RUN find -L /usr/local/* \
-         -type d -name site-packages -prune -false -o \
-         -type f -not -name "zdll.lib" \
-         -exec readlink -f '{}' \; | \
-    xargs file -i | \
-    grep 'charset=binary' | \
-    grep 'x-executable\|x-archive\|x-sharedlib' | \
-    awk -F: '{print $1}' | xargs strip -s
-
-FROM eicweb.phy.anl.gov:4567/containers/image_recipes/debian_minimal:testing-20210408
-LABEL maintainer="Sylvester Joosten <sjoosten@anl.gov>" \
-      name="eic" \
-      group="eic" \
-      march="native" \
-      basedist="debian" \
-      base="debian"
-
-## @ENV@ will be automatically expanded to auto-load the 
-## runtime environment
-ENV DOCKERFILE_DISTRO=debian          \
-    DOCKERFILE_DISTRO_VERSION=20210408-testing   \
-    DEBIAN_FRONTEND=noninteractive    \
-@ENV@
-
-## poppler-utils for pdftoppm, is this is right now not supported by the
-## Spack version
-RUN apt-get -yqq update \
- && apt-get -yqq install --no-install-recommends \
-        ghostscript \
-        gv \
-        poppler-utils \
-        parallel \
- && apt-get -yqq autoremove \
- && rm -rf /var/lib/apt/lists/*
-
-## Copy over files from builder
-COPY --from=builder /opt/spack-environment /opt/spack-environment
-COPY --from=builder /opt/software /opt/software
-RUN rm -rf /usr/local
-COPY --from=builder /usr/local /usr/local
-
-## install gitlab-runner FIXME: needs to be consolidated with other installs
-## installing package from buster (stable) instead of bullseye (testing)
-RUN curl -L \
-    "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | \
-    bash \
- && sed -i "s/bullseye/buster/" /etc/apt/sources.list.d/runner_gitlab-runner.list \
- && apt-get update \
- && apt-get -yqq install --no-install-recommends gitlab-runner \
- && apt-get -yqq autoremove \
- && rm -rf /var/lib/apt/lists/*
-
-## eic-shell and environment scripts. Not strictly needed anymore now we auto-load
-## the environment but still provided for backward compatibility (and documentation).
-COPY eic-shell /usr/local/bin/eic-shell
-COPY eic-env.sh /etc/eic-env.sh
-COPY eic-env.sh /etc/profile.d/z10_eic-env.s
-
-## also make sure we have the older container_dev command available
-COPY eic-shell /usr/local/bin/container_dev
-
-## also make sure we have the older container_dev command available
-COPY eic-shell /usr/local/bin/container_dev
-
-## Setup fresh bashrc, useful for singularity
-COPY bashrc /etc/bash.bashrc
-COPY bashrc /root/.bashrc
-
-## make sure we have the entrypoints setup correctly
-ENTRYPOINT []
-CMD ["bash", "--rcfile", "/etc/profile", "-l"]
-USER 0
-WORKDIR /
-SHELL ["/usr/local/bin/eic-shell"]
diff --git a/containers/release/config.env b/containers/release/config.env
deleted file mode 100644
index 3249f5afb..000000000
--- a/containers/release/config.env
+++ /dev/null
@@ -1,11 +0,0 @@
-REG_HOST  ?= eicweb.phy.anl.gov
-REG_PORT  ?= 4567
-REG_NAME  ?= $(REG_HOST):$(REG_PORT)
-REG_URL   ?= https://$(REG_HOST)
-
-APP_NAME     = eic
-REPO_NAME    = eic
-
-GL_GROUP     = eic_container
-GL_REG_GROUP = containers/eic_container
-GL_REG_NAME  = $(REG_NAME)
diff --git a/containers/release/configure_dockerfile.sh b/containers/release/configure_dockerfile.sh
deleted file mode 100644
index 8af0a6899..000000000
--- a/containers/release/configure_dockerfile.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-## Extract a list of the variable definitions needed
-## for this environment from the builder image, and 
-## then configure the release Dockerfile to set these
-## variables
-
-mkdir -p config
-
-cp /etc/profile.d/z10_spack_environment.sh config/spack-env.sh
-
-export TAG=$1
-
-## Spack sets the man-path, which stops bash from using the default man-path
-## We can fix this by appending a trailing colon to MANPATH
-sed -i '/MANPATH/ s/;$/:;/' config/spack-env.sh
-
-## Extract the desired environment variables as defined by spack
-grep export config/spack-env.sh | \
-  sed 's/export /    /' | \
-  sed 's/;$/ \\/' > config/eic-env.sh
-
-## create our release Dockerfile
-sed '/^@ENV@/r config/eic-env.sh' containers/release/Dockerfile.in | \
-  sed '/^@ENV@/d' | \
-  sed "s/@TAG@/$TAG/" > config/Dockerfile
diff --git a/containers/release/eic.def.in b/containers/release/eic.def.in
deleted file mode 100644
index 5f1f5b182..000000000
--- a/containers/release/eic.def.in
+++ /dev/null
@@ -1,18 +0,0 @@
-Bootstrap: docker
-From: eicweb.phy.anl.gov:4567/containers/eic_container/eic:@TAG@
-
-%help
-  EIC software container.
-  Tools:
-     - eic_shell   : Bash shell in this container
-     - root        : Root shell in the container
-     - ipython     : Python shell in the container
-
-%labels
-  Maintainer "Whitney Armstrong, Sylvester Joosten"
-  Version v2.x
-
-%post -c /bin/bash
-  echo "  -------------------------------------------------"
-  echo "  ===> Image setup complete"
-  echo "  -------------------------------------------------"
diff --git a/gitlab-ci/cleanup_registry.sh b/gitlab-ci/cleanup_registry.sh
new file mode 100755
index 000000000..8c58bfc81
--- /dev/null
+++ b/gitlab-ci/cleanup_registry.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+## Remove a specific docker tag from the eicweb registry
+
+function print_the_help {
+  echo "USAGE:    -i image -r reg_id tag "
+  echo "ARGUMENTS:"
+  echo "          -i,--image      Registry image name"
+  echo "          -r,--reg-id     Registry image ID (integer number)"
+  echo "          -h,--help       Print this message"
+  echo "  positional              Tag to remove from registry"
+  echo ""
+  echo "  Remove a specific docker tag from the eicweb registry, if present on the
+  registry."
+  echo ""
+  echo "EXAMPLE: ./cleanup_registry -i debian_base -r 66 unstable"
+  exit
+}
+
+IMAGE=
+REG_ID=
+TAG=
+
+while [ $# -gt 0 ]; do
+  key=$1
+  case $key in
+    -i|--image)
+      IMAGE=$2
+      shift
+      shift
+      ;;
+    -r|--reg-id)
+      REG_ID=$2
+      shift
+      shift
+      ;;
+    -h|--help)
+      print_the_help
+      exit 0
+      ;;
+    -*)
+      echo "ERROR: unknown flag: $key"
+      echo "use --help for more info"
+      exit 1
+      ;;
+    *)
+      if [ ! -z ${TAG} ]; then
+        echo "ERROR: multiple positional arguments specified"
+        echo "use --help for more info"
+        exit 1
+      fi
+      TAG=$1
+      shift
+      ;;
+  esac
+done
+
+if [ -z $IMAGE ]; then
+  echo "ERROR: no image name given, please use -i <IMAGE>"
+  print_the_help
+  exit 1
+fi
+if [ -z $REG_ID ]; then
+  echo "ERROR: no image id given, please use -r <REG_ID>"
+  print_the_help
+  exit 1
+fi
+if [ -z $TAG ]; then
+  echo "ERROR: no tag given, please specify a single tag (positional argument)"
+  print_the_help
+  exit 1
+fi
+
+echo "Cleaning up eicweb registry for ${IMAGE}:${TAG}"
+
+IMAGE_EXISTS= 
+docker manifest inspect \
+  eicweb.phy.anl.gov:4567/containers/eic_container/${IMAGE}:${TAG} \
+    > /dev/null \
+  && IMAGE_EXISTS=1 \
+  || echo "Image not found, no cleanup needed"
+
+if [ ! -z ${IMAGE_EXISTS} ]; then
+  curl --request DELETE --header "PRIVATE-TOKEN: ${REG_CLEANUP_TOKEN}" \
+    ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/registry/repositories/${REG_ID}/tags/${TAG} \
+  && echo "Image removed" \
+  || echo "Error trying to remove image"
+fi
diff --git a/gitlab-ci/configure.sh b/gitlab-ci/configure.sh
deleted file mode 100755
index 9d99e8bf6..000000000
--- a/gitlab-ci/configure.sh
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/bin/bash
-
-## Configure a CI file based on a template file (first and only
-## argument to this script).
-
-## Known variables that will be substituted:
-## @TAG@             - docker tag used for this build
-## @TARGET_BUILDER@  - docker Makefile target for the builder image
-## @TARGET_RELEASE@  - docker Makefile target for the release image
-## @PUBLISH@         - docker push Makefile target
-
-TEMPLATE_FILE=$1
-OUTPUT_FILE=`basename ${TEMPLATE_FILE} .in`
-
-echo "Configuring CI file: ${TEMPLATE_FILE}"
-echo "Output will be written to: ${OUTPUT_FILE}"
-
-VERSION=`head -n1 VERSION`
-STABLE="${VERSION%.*}-stable"
-
-## Figure out which scenario we are running:
-##  - master
-##  - stable
-##  - tag
-##  - unstable (default)
-TARGET=""
-TAG=""
-PUBLISH=""
-if [ "$CI_COMMIT_BRANCH" = "master" ]; then
-  TARGET="stable"
-	TAG="latest"
-	PUBLISH="publish-latest publish-stable"
-elif [ "$CI_COMMIT_TAG" = "v${VERSION}" ]; then
-  TARGET="stable"
-	TAG=$VERSION
-	PUBLISH="publish-version"
-elif [ "$CI_COMMIT_BRANCH" = "v${STABLE}" ]; then
-  TARGET="stable"
-	TAG=${STABLE}
-	PUBLISH="publish-stable"
-else
-  TARGET="unstable"
-	TAG="unstable"
-	PUBLISH="publish-unstable"
-fi
-
-TARGET_BUILDER=$TARGET
-TARGET_RELEASE=$TARGET
-
-if [ ! -f .ci-env/builder-nc ]; then
-  echo "Can use cached build directive for builder"
-  TARGET_BUILDER="${TARGET_BUILDER}-cached"
-else
-  echo "No-cache required for builder"
-fi
-if [ ! -f .ci-env/release-nc ]; then
-  echo "Can use cached build directive for release"
-  TARGET_RELEASE="${TARGET_RELEASE}-cached"
-else
-  echo "No-cache required for release"
-fi
-
-sed "s/@TAG@/$TAG/g" $TEMPLATE_FILE | \
-  sed "s/@TARGET_BUILDER@/$TARGET_BUILDER/g" | \
-	sed "s/@TARGET_RELEASE@/$TARGET_RELEASE/g" | \
-	sed "s/@PUBLISH@/$PUBLISH/g" > ${OUTPUT_FILE}
-
-echo "Done"
diff --git a/gitlab-ci/docker/Makefile b/gitlab-ci/docker/Makefile
deleted file mode 100644
index d70b8478e..000000000
--- a/gitlab-ci/docker/Makefile
+++ /dev/null
@@ -1,90 +0,0 @@
-# import config.
-# You can change the default config with `make cnf="config_special.env" build`
-cnf ?= config.env
-include $(cnf)
-# exports variables in config.env as environment variables
-export $(shell sed 's/=.*//' $(cnf))
-
-
-# import deploy config
-# You can change the default deploy config with `make cnf="deploy_special.env" release`
-
-SHELL = bash
-
-## Get our version tag and stable version tag
-VERSION=$(shell bash version.sh)
-STABLE=$(shell bash version-stable.sh)
-
-# help will output the help for each task
-# thanks to https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
-.PHONY: help
-
-help: ## This help.
-	@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
-
-.DEFAULT_GOAL := help
-
-# ==========================================================================
-#
-build: ## build the image
-	DOCKER_BUILDKIT=1 docker build -t $(APP_NAME):$(VERSION) .
-build-unstable: ## build the image
-	DOCKER_BUILDKIT=1 docker build -t $(APP_NAME):$(VERSION)-unstable .
-
-build-nc: ## Build the container without caching (from scratch)
-	DOCKER_BUILDKIT=1 docker build --no-cache -t $(APP_NAME):$(VERSION) .
-build-unstable-nc: ## Build the container without caching (from scratch)
-	DOCKER_BUILDKIT=1 docker build --no-cache -t $(APP_NAME):$(VERSION)-unstable .
-
-# ==========================================================================
-#
-login: ## Auto login to AWS-ECR unsing aws-cli
-	docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
-	echo "Login COMPLETE"
-
-stable: build-nc
-stable-cached: build
-unstable: build-unstable-nc 
-unstable-cached: build-unstable 
-
-publish: login publish-latest publish-version #publish-version ## Publish the `{version}` ans `latest` tagged containers to ECR
-	@echo "Publishing done"
-
-
-publish-stable: login 
-	@echo 'publish $(STABLE) to $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME)'
-	docker tag $(APP_NAME):$(VERSION) $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):$(STABLE)
-	docker push $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):$(STABLE)
-	docker rmi $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):$(STABLE) || true
-
-publish-latest: login 
-	echo 'publish latest to $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME)'
-	docker tag $(APP_NAME):$(VERSION) $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):latest
-	docker push $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):latest
-	docker rmi $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):latest || true
-
-publish-version: login 
-	@echo 'publish $(STABLE) to $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME)'
-	docker tag $(APP_NAME):$(VERSION) $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):$(VERSION)
-	docker push $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):$(VERSION)
-	docker rmi $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):$(VERSION) || true
-
-publish-unstable: login 
-	@echo 'publish unstable to $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME)'
-	docker tag $(APP_NAME):$(VERSION)-unstable $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):unstable
-	docker push $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):unstable
-	docker rmi $(REG_NAME)/$(GL_REG_GROUP)/$(APP_NAME):unstable || true
-
-version: ## Output the current version
-	@echo $(VERSION)
-
-# ==========================================================================
-#
-info: ## Output the current version
-	@echo 'VERSION      = $(VERSION)     '
-	@echo 'STABLE       = $(STABLE)      '
-	@echo 'REG_NAME     = $(REG_NAME)    '
-	@echo 'APP_NAME     = $(APP_NAME)    '
-	@echo 'GL_GROUP     = $(GL_GROUP)    '
-	@echo 'GL_REG_GROUP = $(GL_REG_GROUP)'
-	@echo 'GL_REG_NAME  = $(GL_REG_NAME) '
diff --git a/gitlab-ci/docker/version-stable.sh b/gitlab-ci/docker/version-stable.sh
deleted file mode 100644
index 973c1bcc3..000000000
--- a/gitlab-ci/docker/version-stable.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-
-## print the stable version tag for this version to the console
-
-VERSION=`head -n1 ../../VERSION`
-echo "${VERSION%.*}-stable"
diff --git a/gitlab-ci/docker/version.sh b/gitlab-ci/docker/version.sh
deleted file mode 100644
index cfc89a3eb..000000000
--- a/gitlab-ci/docker/version.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-## print the full version number to the console
-
-head -n1 ../../VERSION
diff --git a/gitlab-ci/docker_login.sh b/gitlab-ci/docker_login.sh
new file mode 100755
index 000000000..e295710fc
--- /dev/null
+++ b/gitlab-ci/docker_login.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+
+## Resilient docker login with multiple progressive retries in case of TLS issues
+
+function print_the_help {
+  echo "USAGE:  -u user -p -password [...] [registry]"
+  echo "ARGUMENTS:"
+  echo "          -u,--user       Input image name (e.g., eic_base)"
+  echo "          -p,--password   Input tag (eg., v3.0.0)"
+  echo "          --ci            Login to internal CI registry; -u, -p and positional are ignored"
+  echo "          -t,--time       Time interval (in seconds) between attempts"
+  echo "                          (doubled each time), default: 5"
+  echo "          -n,--n-attempts Number of attempts, default: 5"
+  echo "          -h,--help       Print this message"
+  echo "  positional       Registry name (will use dockerhub if none given)"
+  echo ""
+  echo "  Login to registry."
+  echo ""
+  echo "EXAMPLE: ./docker_login.sh -u \$CI_REGISTRY_USER -p \$CI_REGISTRY_PASSWORD \$CI_REGISTRY" 
+  exit
+}
+
+REGISTRY=
+REG_USER=
+REG_PASSWORD=
+NTRIES=5
+TIME=5
+CI=
+
+while [ $# -gt 0 ]; do
+  key=$1
+  case $key in
+    -u|--user)
+      REG_USER=$2
+      shift
+      shift
+      ;;
+    -p|--password)
+      REG_PASSWORD=$2
+      shift
+      shift
+      ;;
+    --ci)
+      CI=1
+      shift
+      ;;
+    -t|--time)
+      TIME=$2
+      shift
+      shift
+      ;;
+    -n|--n-attempts)
+      NTRIES=$2
+      shift
+      shift
+      ;;
+    -h|--help)
+      print_the_help
+      exit 0
+      ;;
+    -*)
+      echo "ERROR: unknown flag: $key"
+      echo "use --help for more info"
+      exit 1
+      ;;
+    *)
+      REGISTRY+="$1"
+      shift
+      ;;
+  esac
+done
+
+## only sanizize input if not in CI registry mode
+if [ -z $CI ]; then
+  if [ -z $REG_USER ]; then
+    echo "ERROR: no username given, please use -u <USER>"
+    print_the_help
+    exit 1
+  fi
+  if [ -z $REG_PASSWORD ]; then
+    echo "ERROR: no password given, please use -p <PASSWORD>"
+    print_the_help
+    exit 1
+  fi
+else
+  REGISTRY=$CI_REGISTRY
+  REG_USER=$CI_REGISTRY_USER
+fi
+
+while [ $NTRIES != 0 ]; do
+  if [ -z $CI ]; then
+    echo $REG_PASSWORD | docker login -u $REG_USER --password-stdin \
+      && break \
+      || echo "Login failed, retrying in $TIME seconds..."
+  else
+    echo $CI_REGISTRY_PASSWORD | docker login -u $REG_USER \
+                                              --password-stdin $REGISTRY \
+      && break \
+      || echo "Login failed, retrying in $TIME seconds..."
+  fi
+  sleep ${TIME}s
+  TIME=$((TIME * 2))
+  NTRIES=$((NTRIES - 1))
+done
+if [ $NTRIES = 0 ]; then
+  echo "Failed to login to registry $REGISTRY"
+  exit 1
+fi
+echo "Docker login to $REGISTRY successful"
diff --git a/gitlab-ci/docker_push.sh b/gitlab-ci/docker_push.sh
new file mode 100755
index 000000000..e77444d92
--- /dev/null
+++ b/gitlab-ci/docker_push.sh
@@ -0,0 +1,132 @@
+#!/bin/bash
+
+## Generic script to execute docker push from the input tag to all export tags
+## on both dockerhub and eicweb if set
+
+function print_the_help {
+  echo "USAGE:    -i image -l input_tag export_tag [export_tag2 ...]"
+  echo "ARGUMENTS:"
+  echo "          -i,--image      Input image name (e.g., eic_base)"
+  echo "          -l,--label      Input tag (eg., v3.0.0)"
+  echo "          -t,--time       Time interval (in seconds) between attempts"
+  echo "                          (doubled each time), default: 5"
+  echo "          -n,--n-attempts Number of attempts, default: 5"
+  echo "          -h,--help       Print this message"
+  echo "          --eicweb        Publish to $CI_REGISTRY only"
+  echo "  positional              At least one export tag (e.g., v3.0-stable)"
+  echo ""
+  echo "  Execute docker push from image:input_tag to REGISTRY/image:export_tag for"
+  echo "  for all export tags. Will push to $CI_REGISTRY, and optionally also to"
+  echo "  Dockerhub if the DH_PUSH environment variable is set"
+  echo ""
+  echo "EXAMPLE: ./docker_push.sh -i eic_base -l 3.0.0 3.0.0 3.0-stable"
+  exit
+}
+
+IMAGE=
+INPUT_TAG=
+EXPORT_TAGS=()
+NTRIES=5
+TIME=5
+DO_DH=${DH_PUSH}
+
+while [ $# -gt 0 ]; do
+  key=$1
+  case $key in
+    -i|--image)
+      IMAGE=$2
+      shift
+      shift
+      ;;
+    -l|--label)
+      INPUT_TAG=$2
+      shift
+      shift
+      ;;
+    -t|--time)
+      TIME=$2
+      shift
+      shift
+      ;;
+    -n|--n-attempts)
+      NTRIES=$2
+      shift
+      shift
+      ;;
+    --eicweb)
+      DO_DH=0
+      shift
+      ;;
+    -h|--help)
+      print_the_help
+      exit 0
+      ;;
+    -*)
+      echo "ERROR: unknown flag: $key"
+      echo "use --help for more info"
+      exit 1
+      ;;
+    *)
+      EXPORT_TAGS+=("$1")
+      shift
+      ;;
+  esac
+done
+
+if [ -z $IMAGE ]; then
+  echo "ERROR: no image name given, please use -i <IMAGE>"
+  print_the_help
+  exit 1
+fi
+if [ -z $INPUT_TAG ]; then
+  echo "ERROR: no input_Tag given, please use -t <INPUT_TAG>"
+  print_the_help
+  exit 1
+fi
+if [ ${#EXPORT_TAGS[@]} -eq 0 ]; then
+  echo "ERROR: need at least one export tag (positional argument)"
+  print_the_help
+  exit 1
+fi
+
+function retry_push () {
+  INPUT=$1
+  DESTINATION=$2
+  time=$TIME
+  ntries=$NTRIES
+  echo "Pushing ${INPUT} to ${DESTINATION}"
+  if [ ${INPUT} != ${DESTINATION} ]; then
+    echo docker tag ${INPUT} ${DESTINATION}
+    docker tag ${INPUT} ${DESTINATION}
+  fi
+  while [ $ntries != 0 ]; do
+    echo docker push ${DESTINATION} 
+    docker push ${DESTINATION} && break \
+      || echo "Docker push failed, retrying in $time seconds..."
+    sleep ${time}s
+    time=$((time * 2))
+    ntries=$((ntries - 1))
+  done
+  if [ ${INPUT} != ${DESTINATION} ]; then
+    echo docker rmi ${DESTINATION}
+    docker rmi ${DESTINATION}
+  fi
+  if [ $ntries = 0 ]; then
+    echo "Failed to push $INPUT to $DESTINATION"
+    exit 1
+  fi
+}
+
+#echo "IMAGE: ${IMAGE}"
+#echo "INPUT_TAG: ${INPUT_TAG}"
+#echo "EXPORT_TAGS: ${EXPORT_TAGS}"
+#echo "DH_PUSH: ${DH_PUSH}"
+#echo "DO_DH: ${DO_DH}"
+
+export INPUT=$CI_REGISTRY_IMAGE/${IMAGE}:${INPUT_TAG}
+for TAG in ${EXPORT_TAGS[@]}; do
+  retry_push $INPUT $CI_REGISTRY_IMAGE/${IMAGE}:${TAG}
+  if [ ${DO_DH} != 0 ]; then
+    retry_push $INPUT $DH_REGISTRY/${IMAGE}:${TAG}
+  fi
+done
diff --git a/gitlab-ci/singularity/build.sh b/gitlab-ci/singularity/build.sh
deleted file mode 100644
index 4509e96b0..000000000
--- a/gitlab-ci/singularity/build.sh
+++ /dev/null
@@ -1,176 +0,0 @@
-#!/bin/bash
-
-# build.sh will build a Singularity container. It's not overly complicated.
-#
-# USAGE: build.sh --uri collection-name/container-name --cli registry Singularity
-#        build.sh --uri collection-name/container-name --cli registry
-#        build.sh Singularity
-
-# Copyright (C) 2017-2018 Vanessa Sochat.
-
-# This program is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
-# License for more details.
-
-# You should have received a copy of the GNU Affero General Public License
-# along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-set -o errexit
-set -o nounset
-
-function usage() {
-
-    echo "USAGE: build [recipe] [options]"
-    echo ""
-    echo "OPTIONS:
-
-          Image Format
-              --uri   -u    if uploading, a uri to give to sregistry
-              --cli   -c    the sregistry client to use (if uploading)
-              --help  -h    show this help and exit
-              "
-}
-
-# --- Option processing --------------------------------------------------------
-
-uri=""
-cli=""
-tag=""
-
-while true; do
-    case ${1:-} in
-        -h|--help|help)
-            usage
-            exit 0
-        ;;
-        -u|-uri)
-            shift
-            uri="${1:-}"
-            shift
-        ;;
-        -t|--tag)
-            shift
-            tag="${1:-}"
-            shift
-        ;;
-        -c|--cli)
-            shift
-            cli="${1:-}"
-            shift
-        ;;
-        \?) printf "illegal option: -%s\n" "${1:-}" >&2
-            usage
-            exit 1
-        ;;
-        -*)
-            printf "illegal option: -%s\n" "${1:-}" >&2
-            usage
-            exit 1
-        ;;
-        *)
-            break;
-        ;;
-    esac
-done
-
-################################################################################
-### Recipe File ################################################################
-################################################################################
-
-
-if [ $# == 0 ] ; then
-    recipe="Singularity"
-else
-    recipe=$1
-fi
-
-echo ""
-echo "Image Recipe: ${recipe}"
-
-
-################################################################################
-### Storage Client #############################################################
-################################################################################
-
-is_valid_client () {
-  local e match="$1"
-  shift
-  for e; do [[ "$e" == "$match" ]] && return 0; done
-  return 1
-}
-
-# Test if client is valid
-
-clients=("google-storage" "registry" "globus" "dropbox" "google-drive")
-
-if [ "${cli}" != "" ]; then
-    is_valid_client "${cli}" "${clients[@]}"
-    if [ $? -ne 0 ]; then
-        echo "${cli} is not a valid choice! Choose from ${clients[@]}";
-        exit 1
-    fi
-    echo "Storage Client: ${cli}"
-else
-    echo "Storage Client: none"
-fi
-
-
-################################################################################
-### Build! #####################################################################
-################################################################################
-
-# Continue if the image recipe is found
-
-if [ -f "$recipe" ]; then
-
-    imagefile="`basename ${recipe} .def`.sif"
-
-    echo "Creating $imagefile using $recipe..."
-    
-   singularity build $imagefile $recipe
-
-    # If the image is successfully built, test it and upload (examples)
-  
-    if [ -f "${imagefile}" ]; then
-
-        # Example testing using run (you could also use test command)
-
-        echo "Testing the image... Marco!"
-        singularity exec $imagefile echo "Polo!"
-
-        # Example sregistry commands to push to endpoints
-
-        if [ "${cli}" != "" ]; then
-
-            # If the uri isn't provided, he gets a robot name
-            if [ "${uri}" == "" ]; then
-                uri=$(python -c "from sregistry.logger.namer import RobotNamer; bot=RobotNamer(); print(bot.generate())")
-            fi
-
-            # If a tag is provided, add to uri
-            if [ "${tag}" != "" ]; then
-                uri="${uri}:${tag}"
-            fi
-
-            echo "Pushing ${uri} to ${cli}://"
-            echo "SREGISTRY_CLIENT=${cli} sregistry push --name ${uri} ${imagefile}"
-            SREGISTRY_CLIENT="${cli}" sregistry push --name "${uri}" "${imagefile}"
-
-        else
-            echo "Skipping upload. Image $imagefile is finished!"
-        fi
-
-    fi
-
-else
-
-    echo "Singularity recipe ${recipe} not found!"
-    exit 1
-
-fi
diff --git a/install.py b/install.py
index 945be95e4..671dce228 100755
--- a/install.py
+++ b/install.py
@@ -21,32 +21,57 @@ from install import make_launcher, make_modulefile
 from install.util import smart_mkdir, project_version, InvalidArgumentError
 
 ## Gitlab group and project/program name. 
-GROUP_NAME='containers'
-PROJECT_NAME='eic_container'
-IMAGE_ROOT='eic'
+DEFAULT_IMG='eic'
+DEFAULT_VERSION='2.9.2'
 
-PROGRAMS = ['eic-shell',
-            'container_dev',
-            #'root', 
-            'ipython']
+SHORTCUTS = ['eic-shell']
 
 ## URL for the current container (git tag will be filled in by the script)
-CONTAINER_URL = r'https://eicweb.phy.anl.gov/api/v4/projects/290/jobs/artifacts/{version}/raw/build/{img}.sif?job=singularity'
-#api/v4/projects/1/jobs/artifacts/master/raw/some/release/file.pdf
+## components:
+##  - {ref}:
+##      - branch/tag --> git branch or tag
+##      - MR XX      --> refs/merge-requests/XX/head
+##      - nightly    --> just use fallback singularity pull
+##  - {img}: image name
+##  - {job}: the CI job that built the artifact
+CONTAINER_URL = r'hhttps://eicweb.phy.anl.gov/api/v4/projects/290/jobs/artifacts/{ref}/raw/build/{img}.sif?job={job}'
+
+## Docker ref is used as fallback in case regular artifact download fails
+## The components are:
+## - {img}: image name
+## - {tag}: docker tag associated with image
+##      - master        --> testing
+##      - branch/tag    --> branch/tag without leading v
+##      - MR XX         --> unstable (may be incorrect if multiple MRs active)
+##      - nightly       --> nightly
+DOCKER_REF = r'docker://eicweb/{img}:{tag}'
 
 ## Singularity bind directive
 BIND_DIRECTIVE= '-B {0}:{0}'
 
+class UnknownVersionError(Exception):
+    pass
+class ContainerDownloadError(Exception):
+    pass
+
 if __name__ == "__main__":
     parser = argparse.ArgumentParser()
     parser.add_argument(
             'prefix',
             help='Install prefix. This is where the container will be deployed.')
+    parser.add_argument(
+            '-c', '--container',
+            dest='container',
+            default=DEFAULT_IMG,
+            help='(opt.) Container to install. '
+                 'D: {} (will migrate to jug_xl for v3.0).'.format(DEFAULT_IMG))
     parser.add_argument(
             '-v', '--version',
             dest='version',
-            default=project_version(),
-            help='(opt.) project version. Default: current version (in repo).')
+#            default=project_version(),
+            default=DEFAULT_VERSION,
+            help='(opt.) project version. '
+                 'D: {}. For MRs, use mr-XXX.'.format(DEFAULT_VERSION))
     parser.add_argument(
             '-f', '--force',
             action='store_true',
@@ -60,24 +85,12 @@ if __name__ == "__main__":
     parser.add_argument(
             '-m', '--module-path',
             dest='module_path',
-            help='(opt.) Root module path where you want to install a modulefile. D: <prefix>/../../etc/modulefiles')
-    parser.add_argument(
-            '-l', '--local',
-            action='store_true',
-            dest='local',
-            help='Local deploy, will not install the modulefiles (you will have to run'
-                  'the launchers scripts from their relative paths).')
-    ## deprecated, we should just make sure the release image is good enough
-    ## builder singularity image will most likely be removed from the CI
-    ## in a future release
-    #parser.add_argument(
-            #'--install-builder',
-            #dest='builder',
-            #help='(opt.) Install fat builder image, instead of normal slim image')
+            help='(opt.) Root module path to install a modulefile. '
+                 'D: Do not install a modulefile')
 
     args = parser.parse_args()
 
-    print('Deploying', PROJECT_NAME, 'version', args.version)
+    print('Deploying', args.container, 'version', args.version)
 
     ## Check if our bind paths are valid
     bind_directive = ''
@@ -103,35 +116,50 @@ if __name__ == "__main__":
     ## master branch: latest/master --> git master and local stable
     ## for other branches --> git <BRANCH> and local unstable
 
-    version_local = None
-    version_repo = None
-    if args.version in ('master', 'latest'):
-        version_local = 'latest'
-        version_repo = 'master'
+    version_docker = None
+    version_gitlab = None
+    build_job = '{}:singularity:default'.format(args.container)
+    if args.version in ('master', 'testing'):
+        version_docker = 'testing'
+        version_gitlab = 'master'
     elif re.search('[0-9]+\.[0-9]+\.[0-9]|[0-9]+\.[0-9]-stable', args.version) is not None:
-        version_local = args.version
-        version_repo = args.version
-        if version_local[0] == 'v':
-            version_local = version_local[1:]
-        if version_repo[0].isdigit():
-            version_repo = 'v{}'.format(args.version)
+        version_docker = args.version
+        version_gitlab = args.version
+        if version_docker[0] == 'v':
+            version_docker = version_docker[1:]
+        if version_gitlab[0].isdigit():
+            version_gitlab = 'v{}'.format(args.version)
+    elif args.version[:3] == 'mr-':
+        version_docker = 'unstable'
+        version_gitlab = 'refs/merge-requests/{}/head'.format(args.version[3:])
+    elif args.version == 'nightly':
+        version_docker = 'nightly'
+        version_gitlab = 'master'
+        build_job = '{}:singularity:nightly'.format(args.container)
     else:
-        version_local = 'unstable'
-        version_repo = args.version
+        ## fixme add proper error handling
+        print('Unknown requested version:', args.version)
+        raise UnknownVersionError()
+
+    ## when working with the old container, the build job is just 'singularity'
+    if args.container == 'eic':
+        build_job = 'singularity'
 
     ## Create our install prefix if needed and ensure it is writable
     args.prefix = os.path.abspath(args.prefix)
     if not args.module_path:
-        args.module_path = os.path.abspath('{}/../../etc/modulefiles'.format(args.prefix))
+        deploy_local=True
+    else:
+        deploy_local=False
     print('Install prefix:', args.prefix)
     print('Creating install prefix if needed...')
     bindir = '{}/bin'.format(args.prefix)
     libdir = '{}/lib'.format(args.prefix)
     libexecdir = '{}/libexec'.format(args.prefix)
     root_prefix = os.path.abspath('{}/..'.format(args.prefix))
-    moduledir = '{}/{}'.format(args.module_path, PROJECT_NAME)
     dirs = [bindir, libdir, libexecdir]
-    if not args.local:
+    if not deploy_local:
+        moduledir = '{}/{}'.format(args.module_path, args.container)
         dirs.append(moduledir)
     for dir in dirs:
         print(' -', dir)
@@ -142,27 +170,36 @@ if __name__ == "__main__":
 
     ## Get the container
     ## We want to slightly modify our version specifier: if it leads with a 'v' drop the v
-    img = IMAGE_ROOT
+    img = args.container
     ## Builder SIF is not built anymore, deprecated
     #if args.builder:
         #img += "_builder"
-    container = '{}/{}.sif.{}'.format(libdir, img, version_local)
+    container = '{}/{}.sif.{}'.format(libdir, img, version_docker)
     if not os.path.exists(container) or args.force:
-        url = CONTAINER_URL.format(group=GROUP_NAME, project=PROJECT_NAME,
-                version=version_repo, img=img)
+        url = CONTAINER_URL.format(ref=version_gitlab, img=img, job=build_job)
         print('Downloading container from:', url)
         print('Destination:', container)
-        urllib.request.urlretrieve(url, container)
+        try:
+            urllib.request.urlretrieve(url, container)
+        except:
+            print('WARNING: failed to retrieve container artifact')
+            print('Attempting alternative download from docker registry')
+            cmd = ['singularity pull', '--force', container, DOCKER_REF.format(img=img, tag=version_docker)]
+            cmd = ' '.join(cmd)
+            print('Executing:', cmd)
+            err = os.system(cmd)
+            if err:
+                raise ContainerDownloadError()
     else:
         print('WARNING: Container found at', container)
         print(' ---> run with -f to force a re-download')
 
-    if not args.local:
-        make_modulefile(PROJECT_NAME, version_local, moduledir, bindir)
+    if not deploy_local:
+        make_modulefile(args.container, version_docker, moduledir, bindir)
 
     ## configure the application launchers
     print('Configuring applications launchers: ')
-    for prog in PROGRAMS:
+    for prog in SHORTCUTS:
         app = prog
         exe = prog
         if type(prog) == tuple:
diff --git a/containers/builder/spack.yaml b/spack.yaml
similarity index 100%
rename from containers/builder/spack.yaml
rename to spack.yaml
-- 
GitLab