Skip to content
Snippets Groups Projects
Select Git revision
  • master
  • tmp_qa-build-cms-flowsim-gpu_4d3163b7
  • qa-build-cms-flowsim-gpu protected
  • tmp_qa-build-cms-flowsim-gpu_762339eb
  • tmp_qa-build-cms-flowsim-gpu_1e7e9a4c
  • qa-build-gen-madgraph-gpu protected
  • qa-build-cms-hlt-gpu protected
  • qa-build-gen-pepper-gpu protected
  • qa-build-alice-o2-gpu protected
  • tmp_qa-build-igwn-et-rift-gpu_c868302a
  • qa-build-igwn-et-rift-gpu protected
  • qa-build-gen-pepper-ma protected
  • BMK-1697_b
  • BMK-1697
  • BMK-1680
  • BMK-1695
  • BMK-1674
  • cms-flowsim-add-multi-gpu-support
  • qa-build-cms-flowsim protected
  • BMK-1620
  • v_cms-flowsim-gpu-bmk_v0.2 protected
  • v_gen-madgraph-gpu-bmk_v0.1 protected
  • v_cms-flowsim-gpu-bmk_v0.1 protected
  • v_gen-pepper-gpu-bmk_v0.3 protected
  • v_cms-hlt-gpu-bmk_v0.1 protected
  • v_gen-pepper-gpu-bmk_v0.2 protected
  • v_alice-digi-reco-core-run3-ma-bmk_v3.0 protected
  • v_atlas-gen_sherpa-ma-bmk_v2.2 protected
  • v_juno-gen-sim-reco-bmk_v3.1 protected
  • v_juno-gen-sim-reco-bmk_v3.0 protected
  • v_atlas-sim_mt-ma-bmk_v2.1 protected
  • v_atlas-reco_mt-ma-bmk_v2.3 protected
  • v_atlas-reco_mt-ma-bmk_v2.2 protected
  • v_hello-world-c7-ma-bmk_v1.0 protected
  • v_atlas-kv-bmk_v2.0 protected
  • v_igwn-pe-bmk_v0.5 protected
  • v_juno-gen-sim-reco-bmk_v2.2 protected
  • v_alice-digi-reco-core-run3-ma-bmk_v2.2 protected
  • v_atlas-gen_sherpa-ma-bmk_v2.1 protected
  • v_atlas-reco_mt-ma-bmk_v2.1 protected
40 results

main.sh

  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    main.sh 40.93 KiB
    #!/bin/bash
    
    # Copyright 2019-2022 CERN. See the COPYRIGHT file at the top-level
    # directory of this distribution. For licensing information, see the
    # COPYING file at the top-level directory of this distribution.
    
    #set -e # immediate exit on error
    
    # Naming convention for environment variable in this script (BMK-118):
    # - All uppercase environment variables may be used across different functions
    #   > All variables starting with CI_ are set by the gitlab runner CI
    #   > All variables starting with CIENV_ are set in .gitlab-ci.sh or .gitlab-ci.yml
    #   > All variables starting with HEPWL_ are set in the spec file of each workload
    #   > All variables starting with MAIN_ are set in this main.sh from user inputs or other variables
    # - All lowercase environment variables are meant to remain local to their script/function
    
    function fail(){
      echo -e "\n------------------------\nFailing '$@'\n------------------------\n" >&2
      echo -e "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
      echo -e "\n[main.sh] finished (NOT OK) at $(date)\n"
      echo -e "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
      exit 1
    }
    
    function execute_command(){
      echo "`date` [execute_command] Executing '$@'"
      eval "$@"
      status=$?
      echo -e "`date` [execute_command] '$@'\nReturn code: ${status}"
      return ${status}
    }
    
    
    # Load the spec file and validate it against common naming conventions (BMK-135)
    # Input $1: path to the HEPWL spec file
    # [Eventually this could use readonly bash variables and check if any HEPWL_ variables are already defined?]
    function load_and_validate_specfile(){
      if [ "$1" == "" ] || [ "$2" != "" ]; then
        echo "Usage: ${FUNCNAME[0]} <hepwlSpecFile>"
        return 1
      fi
      hepwlSpecFile=$1
      echo "[${FUNCNAME[0]}] HEPWL SPEC FILE: ${hepwlSpecFile}"
      if [ ! -f ${hepwlSpecFile} ]; then
        echo "ERROR! spec file '${hepwlSpecFile}' not found"
        return 1
      fi
      echo "[${FUNCNAME[0]}] validate spec file name"
      spec=$(basename ${hepwlSpecFile})
      spec12="${spec%%.spec}"
      if [ "${spec}" == "${spec12}" ]; then
        echo "ERROR! spec file name does not end in .spec"
        return 1
      fi
      spec1=${spec12%%-*}
      spec2=${spec12#*-}
      echo "[${FUNCNAME[0]}] experiment: ${spec1}"
      echo "[${FUNCNAME[0]}] workload: ${spec2}"
      ###echo "[${FUNCNAME[0]}] set | grep ^HEPWL :"; set | grep ^HEPWL_
      echo "[${FUNCNAME[0]}] source ${hepwlSpecFile}"
      source ${hepwlSpecFile}
      ###echo "[${FUNCNAME[0]}] set | grep ^HEPWL :"; set | grep ^HEPWL_
      echo "[${FUNCNAME[0]}] validate HEPWL_ environment variables"
      if [ "${HEPWL_BMKOS}" == "" ]; then HEPWL_BMKOS="gitlab-registry.cern.ch/hep-benchmarks/hep-workloads/hep-workloads-base:slc6"; fi
      if [ "${HEPWL_BMKDIR}" != "${spec12}" ]; then
        echo "ERROR! Invalid HEPWL_BMKDIR=${HEPWL_BMKDIR}, expected ${spec12}"
        return 1
      elif [ "${HEPWL_BMKEXE}" != "${spec12}-bmk.sh" ]; then
        echo "ERROR! Invalid HEPWL_BMKEXE=${HEPWL_BMKEXE}, expected ${spec12}-bmk.sh"
        return 1
      elif [ "${HEPWL_DOCKERIMAGENAME}" != "${spec12}-bmk" ]; then
        echo "ERROR! Invalid HEPWL_DOCKERIMAGENAME=${HEPWL_DOCKERIMAGENAME}, expected ${spec12}-bmk"
        return 1
      fi
      # Define default CVMFS_HTTP_PROXY when not defined via .spec file
      # (needed because some WLs could not use as proxy the cern one)
      if [ "${HEPWL_CVMFS_PROXY}" == "" ]; then HEPWL_CVMFS_PROXY="http://ca-proxy.cern.ch:3128"; fi
      # Needed for cvmfs repositories not hosted at CERN (such as for Dune and Juno)
      if [ "${HEPWL_CVMFS_SERVER_URL}" == "" ]; then 
        HEPWL_CVMFS_SERVER_URL="http://cvmfs-stratum-zero-hpc.cern.ch/cvmfs/@fqrn@" # from /etc/cvmfs/domain.d/cern.ch.conf
      fi
      if [ "${HEPWL_CVMFS_KEYS_DIR}" == "" ]; then 
        HEPWL_CVMFS_KEYS_DIR="/etc/cvmfs/keys/cern.ch" # from /etc/cvmfs/domain.d/cern.ch.conf
      fi
      if [ ! -e "${HEPWL_CVMFS_KEYS_DIR}" ]; then
        echo "ERROR!  CVMFS_KEYS_DIR ${HEPWL_CVMFS_KEYS_DIR} does not exist"
        return 1
      fi
    
      echo "[${FUNCNAME[0]}] OK"
      return 0
    }
    
    
    # Validate json file: lint syntax (BMK-137)
    # [Eventually: check compliance to expected schema (BMK-134)]
    # Input $1: path to the json file
    function validate_jsonfile(){
      if [ "$1" == "" ] || [ "$2" != "" ]; then
        echo "Usage: ${FUNCNAME[0]} <jsonFile>"
        return 1
      fi
      jsonFile=$1
      echo "[${FUNCNAME[0]}] JSON FILE: ${jsonFile}"
      if [ ! -f ${jsonFile} ]; then
        echo "ERROR! json file '${jsonFile}' not found"
        return 1
      fi
      echo "[${FUNCNAME[0]}] lint json file syntax using jq"
      if ! jq '.' -c < ${jsonFile}; then
        echo "ERROR! json file '${jsonFile}' lint validation failed"
        return 1
      fi
      return 0
    }
    
    
    # Mount the cvmfs repos specified in $HEPWL_CVMFSREPOS
    # Input environment variable ${HEPWL_CVMFSREPOS}: cvmfs repos to mount
    # Input environment variable ${MAIN_CVMFSTRACESDIR}: directory containing the cvmfs trace files
    function mount_cvmfs(){
      echo "[mount_cvmfs] ................................................"
      echo "[mount_cvmfs] starting at $(date)"
      echo "[mount_cvmfs] current directory is $(pwd)"
      if [ "$HEPWL_CVMFSREPOS" == "" ]; then fail "HEPWL_CVMFSREPOS is not set"; fi
      echo "[mount_cvmfs] mounting cvmfs repositories $HEPWL_CVMFSREPOS"
      echo "NB: it's fine if there is a single error message such as 'Failed to get D-Bus connection: Operation not permitted'"
      if [ ! -e $MAIN_CVMFSTRACESDIR ]; then
        mkdir -p $MAIN_CVMFSTRACESDIR || fail "[mount_cvmfs] cannot create $MAIN_CVMFSTRACESDIR"
        chown cvmfs $MAIN_CVMFSTRACESDIR || fail "[mount_cvmfs] cannot chown $MAIN_CVMFSTRACESDIR"
      fi
      cat > /etc/cvmfs/default.local <<EOF
    CVMFS_REPOSITORIES=${HEPWL_CVMFSREPOS}
    CVMFS_QUOTA_LIMIT=6000
    CVMFS_CACHE_BASE=/scratch/cache/cvmfs2
    CVMFS_MOUNT_RW=yes
    CVMFS_HTTP_PROXY="${HEPWL_CVMFS_PROXY}"
    CVMFS_TRACEFILE=${MAIN_CVMFSTRACESDIR}/cvmfs-@fqrn@.trace.log
    EOF
      echo "@@@@@@@@@>>>>>>> CVMFS_HTTP_PROXY"
      cat /etc/cvmfs/default.local
      mkdir -p  /etc/cvmfs/config.d
      echo "export CMS_LOCAL_SITE=/cvmfs/cms.cern.ch/SITECONF/T0_CH_CERN" > /etc/cvmfs/config.d/cms.cern.ch.local
    
      # NEW OPTION 1 (BMK-145) - main.sh as a subprocess
      #if [ -e /cvmfs ]; then fail "/cvmfs already exists"; fi # 1-inception
      #ln -sf $CIENV_CVMFSVOLUME /cvmfs # replaces "docker run -v $CIENV_CVMFSVOLUME:/cvmfs:shared"? IT DOES NOT WORK...
      #if [ ! -d /cvmfs ]; then fail "/cvmfs does not exist"; fi # assume that /cvmfs has been created by now
    
      # OLD OPTION 2 (BMK-145) - main.sh as an entrypoint of docker run
      [ -d /cvmfs ] || fail "[mount_cvmfs] /cvmfs does not exist" # assume that /cvmfs has been created by now (e.g. by docker run -v $CIENV_CVMFSVOLUME:/cvmfs:shared)
    
      cvmfs_config setup nostart || fail "[mount_cvmfs] problem with cvmfs_config setup nostart" # this would create /cvmfs if it did not exist yet
      for repo in `echo ${HEPWL_CVMFSREPOS}| sed -e 's@,@ @g'`; do
        umount /cvmfs/$repo # OPTION 2 ONLY - NEEDED AT ALL?
        rm -rf /cvmfs/$repo # OPTION 2 ONLY - NEEDED AT ALL?
        mkdir /cvmfs/$repo # Assume that /cvmfs has been created by now
        mount -t cvmfs $repo /cvmfs/$repo
        echo "[mount_cvmfs] ls -l /cvmfs/$repo"
        ls -l /cvmfs/$repo
      done
      echo "[mount_cvmfs] finished at $(date)"
      return 0
    }
    
    
    # Unmount the cvmfs repos specified in $HEPWL_CVMFSREPOS
    # Input environment variable ${HEPWL_CVMFSREPOS}: cvmfs repos to unmount
    function unmount_cvmfs(){
      echo "[unmount_cvmfs] ................................................"
      echo "[unmount_cvmfs] starting at $(date)"
      echo "[unmount_cvmfs] current directory is $(pwd)"
      if [ "$HEPWL_CVMFSREPOS" == "" ]; then fail "HEPWL_CVMFSREPOS is not set"; fi
      echo "[unmount_cvmfs] unmounting cvmfs repositories $HEPWL_CVMFSREPOS"
      for repo in `echo ${HEPWL_CVMFSREPOS}| sed -e 's@,@ @g'`; do
        umount /cvmfs/$repo || echo "WARNING! Could not umount /cvmfs/$repo"
        rm -rf /cvmfs/$repo || echo "WARNING! Could not remove /cvmfs/$repo"
      done
      echo "[unmount_cvmfs] finished at $(date)"
      return 0
    }
    
    
    # Build a docker image
    # [This is called twice: to build the image with cvmfs (cvmfs-shrink) and without cvmfs (standalone image)]
    # Input $1: docker image, without registry prefix (image_name:image_tag)
    # Input environment variable ${CIENV_HEPWL_SPECFILE}: spec file for the image to be built
    # Input environment variable ${MAIN_HEPWLBUILDDIR}: build dir below CIENV_JOBDIR (copy of HEPWL spec dir)
    # Output: image stored in the local Docker image registry
    function build_docker_image(){
      echo "[build_docker_image] ................................................"
      echo "[build_docker_image] starting at $(date)"
      echo "[build_docker_image] current directory is $(pwd)"
      if [ "$1" == "" ]; then fail "[build_docker_image] No image name provided. Failing "; fi
      theimage=$1
      echo "[build_docker_image] " $theimage
      cd ${MAIN_HEPWLBUILDDIR}
      generate_Dockerfile -s $CIENV_HEPWL_SPECFILE -H ./common/Dockerfile.header -T ./common/Dockerfile.template || fail "[build_docker_image] generate_Dockerfile"
      cat Dockerfile
      echo -e "\n[build_docker_image] docker build of ${theimage} starting at $(date) from MAIN_HEPWLBUILDDIR=$(pwd)\n"
      docker build --squash -t "${theimage}" . || fail "[build_docker_image] docker build -t ${theimage} ."
      ###docker build --build-arg CACHEBUST=$(date +%s) -t "${theimage}" . || fail "[build_docker_image] docker build -t ${theimage} ."
      echo "[build_docker_image] finished at $(date)"
      return 0
    }
    
    function create_functional_dirs(){
      mkdir $MAIN_HEPWLBUILDDIR/cvmfs
      mkdir $MAIN_HEPWLBUILDDIR/cvmfs/.keepme
      mkdir $MAIN_HEPWLBUILDDIR/cvmfs.provenance
      mkdir $MAIN_HEPWLBUILDDIR/cvmfs.provenance/.keepme
      mkdir $MAIN_HEPWLBUILDDIR/build
      mkdir $MAIN_HEPWLBUILDDIR/build/.keepme
    }
    # Build the HEP workload image with cvmfs mounted, then run it and produce a cvmfs trace file
    # Input environment variable ${HEPWL_DOCKERIMAGENAME}: image name for the HEP workload
    # Input environment variable ${HEPWL_BMKOPTS}: options for the HEP workload script
    # Input environment variable ${HEPWL_BMKUSEGPU}: "1" if this is a GPU workload, "" otherwise
    # Input environment variable ${MAIN_HEPWLBUILDDIR}: build dir below CIENV_JOBDIR (copy of HEPWL spec dir)
    # Input environment variable ${CIENV_HEPWL_SPECFILE}: spec file for the image to be built
    # Input environment variable ${CIENV_JOBDIR}/results: host directory where results should be stored
    # Input environment variable ${CIENV_CVMFSVOLUME}: host directory where cvmfs is mounted?
    # Input environment variable ${MAIN_CVMFSTRACESDIR}: directory containing the cvmfs trace files
    # Output: status code for the success of the test and results in ${CIENV_JOBDIR}/results
    # Output: cvmfs trace stored in ${MAIN_CVMFSTRACESDIR}
    function run_docker_wl(){
      echo "[run_docker_wl] ................................................"
      echo "[run_docker_wl] starting at $(date)"
      echo "[run_docker_wl] current directory is $(pwd)"
      if [ -z "$HEPWL_BMKOPTS" ]; then HEPWL_BMKOPTS=""; fi
      echo "[run_docker_wl] HEPWL_BMKOPTS options: $HEPWL_BMKOPTS"
      if [ -z "$HEPWL_DOCKER_EXTRA_ARGS" ]; then HEPWL_DOCKER_EXTRA_ARGS=""; fi
      echo "[run_docker_wl] HEPWL_DOCKER_EXTRA_ARGS options: $HEPWL_DOCKER_EXTRA_ARGS"
      if [ -e $MAIN_HEPWLBUILDDIR/cvmfs ]; then
        echo "[run_docker_wl] removing cvmfs dir in $MAIN_HEPWLBUILDDIR/"
        rm -rf $MAIN_HEPWLBUILDDIR/cvmfs
      fi
      create_functional_dirs
      # [NB ./cvmfs here is empty, it only contains .keepme]
      # [NB ./build here is empty, it only contains .keepme]
      theimage="$HEPWL_DOCKERIMAGENAME:cvmfs-shrink"
      echo "[run_docker_wl] Building temporary docker image $theimage"
      build_docker_image $theimage
      strace=""
      ###strace="--cap-add SYS_PTRACE" # optionally add SYS_PTRACE capability to use strace (see https://github.com/moby/moby/issues/21051)
      echo -e "\n[run_docker_wl] Run WL in docker (to extract cvmfs) via execute_command - started"
    
      echo "[run_docker_wl] HEPWL_BMKUSEGPU=$HEPWL_BMKUSEGPU"
      if [ "$HEPWL_BMKUSEGPU" == "1" ]; then USEGPU="--gpus all"; else USEGPU=""; fi
    
      # NEW OPTION 1 (BMK-145) - main.sh as a subprocess
      #execute_command docker run ${strace} --rm -v $CIENV_JOBDIR/results:/results -v $MAIN_HEPWLBUILDDIR:$MAIN_HEPWLBUILDDIR -w $MAIN_HEPWLBUILDDIR -v /cvmfs:/cvmfs:shared $theimage $HEPWL_BMKOPTS -d || fail "[run_docker_wl] docker run $theimage"
    
      # OLD OPTION 2 (BMK-145) - main.sh as an entrypoint of docker run
      if [ "$CIENV_CCACHEDIR" != "" ]; then BIND_CCACHEDIR="-v $CIENV_CCACHEDIR:/ccache -e CCACHE_DIR=/ccache"; else BIND_CCACHEDIR=""; fi # expose CI ccache to internal software builds like those of madgraph (BMK-1030)
      (execute_command docker run "${HEPWL_DOCKER_EXTRA_ARGS}" ${strace} --rm $USEGPU $BIND_CCACHEDIR \
              -v $CIENV_JOBDIR/results:/results \
              -v $MAIN_HEPWLBUILDDIR:$MAIN_HEPWLBUILDDIR \
              -w $MAIN_HEPWLBUILDDIR \
              -v $CIENV_CVMFSVOLUME:/cvmfs:shared \
              $theimage $HEPWL_BMKOPTS -d )|| fail "[run_docker_wl] docker run $theimage"
    
      echo -e "[run_docker_wl] Run WL in docker (to extract cvmfs) via execute_command - completed\n"
      for acvmfs in `ls ${MAIN_CVMFSTRACESDIR} | sed -e 's@cvmfs-\(.*\).trace.log@\1@'`; do
        echo "[run_docker_wl] cvmfs flush trace for $acvmfs"
        if ! cvmfs_talk -i ${acvmfs} tracebuffer flush; then  # fix BMK-3 (see CVM-1682)
          fail "[run_docker_wl] cvmfs flush $acvmfs" # fix BMK-136 (flush was silently failing, leading to missing cvmfs files)
        fi
      done
      echo "[run_docker_wl] validate json file" # see BMK-137
      jsonFile=$(ls -1tr $CIENV_JOBDIR/results/*/${HEPWL_BMKDIR}_summary.json 1> >( head -1 ) ) || fail "[run_docker_wl] json summary file not found" # process substitution retains error code
      validate_jsonfile $jsonFile || fail "[run_docker_wl] validate json file"
      echo "[run_docker_wl] finished at $(date)"
      return 0
    }
    
    
    # Create a spec from the cvmfs trace file, then run shrinkwrap to create a cvmfs export
    # Input environment variable ${MAIN_HEPWLBUILDDIR}: build dir below CIENV_JOBDIR (copy of HEPWL spec dir)
    # Input environment variable ${HEPWL_EXTEND_<repo>_SPEC}: optional extra cvmfs spec file
    # Input environment variable ${CIENV_JOBDIR}: host directory where the cvmfs config should be stored
    # Input environment variable ${CIENV_JOBDIR}/results: host directory where results should be stored
    # Input environment variable ${MAIN_CVMFSTRACESDIR}: directory containing the cvmfs trace files
    # Input environment variable ${MAIN_CVMFSEXPORTDIR}: directory where the cvmfs export should be stored
    # Output: cvmfs export in ${MAIN_CVMFSEXPORTDIR}/cvmfs
    function run_shrinkwrap(){
      echo "[run_shrinkwrap] ................................................"
      echo "[run_shrinkwrap] starting at $(date)"
      echo "[run_shrinkwrap] current directory is $(pwd)"
      echo "MAIN_CVMFSEXPORTDIR $MAIN_CVMFSEXPORTDIR"
      if [ -e  ${MAIN_CVMFSEXPORTDIR} ]; then
        echo "[run_shrinkwrap] ${MAIN_CVMFSEXPORTDIR} already exists: will remove it"
        ###echo "[run_shrinkwrap] ${MAIN_CVMFSEXPORTDIR} already exists: remove it? (y/[n]) "
        ###read myansw
        ###if [ "$myansw" == "n" ]; then return 1; fi
        rm -rf ${MAIN_CVMFSEXPORTDIR}
      fi
      mkdir -p ${MAIN_CVMFSEXPORTDIR}/cvmfs
      cvmfs_shrink_conf=$CIENV_JOBDIR/generic_config.cern.ch.config # no need to export this
      cat > ${cvmfs_shrink_conf} <<EOF
    CVMFS_CACHE_BASE=/var/lib/cvmfs/shrinkwrap
    CVMFS_HTTP_PROXY=DIRECT
    CVMFS_KEYS_DIR=${HEPWL_CVMFS_KEYS_DIR}
    CVMFS_MOUNT_DIR=/cvmfs # from /etc/cvmfs/default.conf
    CVMFS_SERVER_URL=${HEPWL_CVMFS_SERVER_URL}
    CVMFS_SHARED_CACHE=no
    export CMS_LOCAL_SITE=T0_CH_CERN
    EOF
      for acvmfs in `ls ${MAIN_CVMFSTRACESDIR} | grep "\.log"`; do
        echo "[run_shrinkwrap] ... shrinking  " $acvmfs;
        specname=`echo $acvmfs | sed -e 's@trace\.log@spec\.txt@'` # example cvmfs-sft.cern.ch.spec.txt
        reponame=`echo $acvmfs | sed -e 's@cvmfs-\(.*\)\.trace\.log.*@\1@'`
        echo "[run_shrinkwrap] specname $specname"
        echo "[run_shrinkwrap] reponame $reponame"
        date
        echo "[run_shrinkwrap] python3 /usr/libexec/cvmfs/shrinkwrap/spec_builder.py --policy=exact ${MAIN_CVMFSTRACESDIR}/$acvmfs ${MAIN_CVMFSTRACESDIR}/$specname"
        if ! python3 /usr/libexec/cvmfs/shrinkwrap/spec_builder.py --policy=exact ${MAIN_CVMFSTRACESDIR}/$acvmfs ${MAIN_CVMFSTRACESDIR}/$specname; then
          fail "[run_shrinkwrap] spec_builder.py" # fix BMK-136 (spec_builder was silently failing, leading to missing cvmfs files)
        fi
        echo "[run_shrinkwrap] saving $specname on ${CIENV_JOBDIR}/results/$specname"
        cp ${MAIN_CVMFSTRACESDIR}/$specname ${CIENV_JOBDIR}/results/
        date
        trimname0=${reponame/.cern.ch}
        trimname=${trimname0/\-/\_} # make sure there are not - in the name, like for atlas-conddb
        trimname=${reponame/.cern.ch}
        spec_var=HEPWL_EXTEND_${trimname^^}_SPEC
    
        #Variable indirection in bash 4.4 threats now differently the empty variable
        #https://lists.gnu.org/archive/html/bug-bash/2016-11/msg00165.html
        if [ -v ${spec_var} ] && [ -e $MAIN_HEPWLBUILDDIR/${!spec_var} ]; then
          #BMK-904 include possibility to remove entries from the specname file
          # The entries starting with "-" will be removed from the specname file instead of being added
          for aline in `cat $MAIN_HEPWLBUILDDIR/${!spec_var} | grep  "^\-" | sed -e "s@^-@@"`; 
            do 
              nline=`printf '%q' "$aline"` # need to escape chars
              echo "[run_shrinkwrap] removing line ${nline} from ${MAIN_CVMFSTRACESDIR}/$specname"
              sed -i  "s@^${nline}\$@@" ${MAIN_CVMFSTRACESDIR}/$specname
          done
          echo "[run_shrinkwrap] appending custom paths to ${MAIN_CVMFSTRACESDIR}/$specname based on $MAIN_HEPWLBUILDDIR/${!spec_var}"
          echo " " >> ${MAIN_CVMFSTRACESDIR}/$specname
          cat $MAIN_HEPWLBUILDDIR/${!spec_var} | grep -v "^\-" >> ${MAIN_CVMFSTRACESDIR}/$specname
        fi
        echo "[run_shrinkwrap] cvmfs_shrinkwrap --repo $reponame --src-config ${cvmfs_shrink_conf} --spec-file ${MAIN_CVMFSTRACESDIR}/$specname --dest-base ${MAIN_CVMFSEXPORTDIR}/cvmfs/ -j 4"
        cvmfs_shrinkwrap --repo $reponame --src-config ${cvmfs_shrink_conf} --spec-file  ${MAIN_CVMFSTRACESDIR}/$specname --dest-base ${MAIN_CVMFSEXPORTDIR}/cvmfs/ -j 4 || fail "[run_shrinkwrap] cvmfs_shrinkwrap failed"
        date
      done
      echo "[run_shrinkwrap] finished at $(date)"
      return 0
    }
    
    
    # Move the cvmfs export from ${MAIN_CVMFSEXPORTDIR}/cvmfs to ${MAIN_HEPWLBUILDDIR}/cvmfs
    # Input environment variable ${MAIN_CVMFSEXPORTDIR}: directory where the cvmfs export has been stored
    # Input environment variable ${MAIN_HEPWLBUILDDIR}: build dir below CIENV_JOBDIR (copy of HEPWL spec dir)
    # Input environment variable ${MAIN_CVMFSTRACESDIR}: directory containing the cvmfs trace files
    # Input environment variable ${HEPWL_DOCKERIMAGETAG}: image tag for the standalone HEP workload
    # Output: cvmfs export in ${MAIN_HEPWLBUILDDIR}/cvmfs, to be copied into the docker image
    function copy_cvmfs(){
      echo "[copy_cvmfs] ................................................"
      echo "[copy_cvmfs] starting at $(date)"
      echo "[copy_cvmfs] current directory is $(pwd)"
      echo "[copy_cvmfs] remove ${MAIN_CVMFSEXPORTDIR}/cvmfs/.data" # this is a _very_ large (and useless?) directory produced by cvmfs_shrinkwrap
      ###rm -rf ${MAIN_CVMFSEXPORTDIR}/cvmfs/.data # this is slow, rsync is faster (https://unix.stackexchange.com/a/79656)
      cd ${MAIN_CVMFSEXPORTDIR}/cvmfs; mkdir EMPTYDIR; rsync -a --delete EMPTYDIR/ .data || fail "[copy_cvmfs] rsync"; cd - # NB for rsync, add a trailing "/" to the source and none to the target
      date
      if [ -e $MAIN_HEPWLBUILDDIR/cvmfs ]; then
        echo "[copy_cvmfs] removing cvmfs dir in $MAIN_HEPWLBUILDDIR/"
        rm -rf $MAIN_HEPWLBUILDDIR/cvmfs
      fi
      echo "[copy_cvmfs] mv ${MAIN_CVMFSEXPORTDIR}/cvmfs $MAIN_HEPWLBUILDDIR "
      mv ${MAIN_CVMFSEXPORTDIR}/cvmfs $MAIN_HEPWLBUILDDIR || fail "[copy_cvmfs] cannot mv ${MAIN_CVMFSEXPORTDIR}/cvmfs $MAIN_HEPWLBUILDDIR"
      # FIXME: if cms repo, need to copy by hand the SITECONF/local, because it's a sym link
      #if [ -e /cvmfs/cms.cern.ch/SITECONF/local ]; then
      #  cp -r -H /cvmfs/cms.cern.ch/SITECONF/local $MAIN_HEPWLBUILDDIR/cvmfs/cms.cern.ch/SITECONF/ || fail "[copy_cvmfs] cannot cp /cvmfs/cms.cern.ch/SITECONF/local" # BMK-15: do NOT use '.. && ( .. || fail )'
      #fi
      # FIXME: try to run CMS bmks without SITECONF/local (BMK-15)
    #  if [ -e $MAIN_HEPWLBUILDDIR/cvmfs/cms.cern.ch/SITECONF/local ]; then
    #    rm -rf $MAIN_HEPWLBUILDDIR/cvmfs/cms.cern.ch/SITECONF/local
    #  fi
      mkdir -p $MAIN_HEPWLBUILDDIR/cvmfs/cms.cern.ch/SITECONF/local/JobConfig # empty
      echo "[copy_cvmfs] /cvmfs contents copied to $MAIN_HEPWLBUILDDIR/cvmfs"
      ls -l $MAIN_HEPWLBUILDDIR/cvmfs
      if [[ ${HEPWL_DOCKERIMAGETAG} =~ ^v[0-9]*\.[0-9]*$ ]]; then # keep provenance info only in v[0-9]*\.[0-9]* production images (BMK-159)
        echo "[copy_cvmfs] move $MAIN_HEPWLBUILDDIR/cvmfs/.provenance to $MAIN_HEPWLBUILDDIR/cvmfs.provenance"
        mv $MAIN_HEPWLBUILDDIR/cvmfs/.provenance $MAIN_HEPWLBUILDDIR/cvmfs.provenance # move away CI-JOB-xxx-specific files for better caching (BMK-159)
      else
        echo "[copy_cvmfs] remove $MAIN_HEPWLBUILDDIR/cvmfs/.provenance"
        rm -rf $MAIN_HEPWLBUILDDIR/cvmfs/.provenance # completely remove CI-JOB-xxx-specific files for better caching in singularity (BMK-159)
      fi
      echo "[copy_cvmfs] finished at $(date)"
      return 0
    }
    
    
    # Move the build directory from ${MAIN_BUILDEXPORTDIR}/build to ${MAIN_HEPWLBUILDDIR}/build
    # Input environment variable ${MAIN_BUILDEXPORTDIR}: directory where the build export has been stored
    # Input environment variable ${MAIN_HEPWLBUILDDIR}: build dir below CIENV_JOBDIR (copy of HEPWL spec dir)
    # Input environment variable ${HEPWL_DOCKERIMAGETAG}: image tag for the standalone HEP workload
    # Output: build export in ${MAIN_HEPWLBUILDDIR}/build, to be copied into the docker image
    function copy_build(){
      echo "[copy_build] ................................................"
      echo "[copy_build] starting at $(date)"
      echo "[copy_build] current directory is $(pwd)"
      date
      if [ -e $MAIN_HEPWLBUILDDIR/build ]; then
        echo "[copy_build] $MAIN_HEPWLBUILDDIR/build exists"
        ls -al $MAIN_HEPWLBUILDDIR/build
        echo "[copy_build] removing build dir in $MAIN_HEPWLBUILDDIR/"
        rm -rf $MAIN_HEPWLBUILDDIR/build
      fi
      echo "MAIN_BUILDEXPORTDIR $MAIN_BUILDEXPORTDIR"
      if [ -d $MAIN_BUILDEXPORTDIR/build ]; then
        ###echo "[copy_build] mv ${MAIN_BUILDEXPORTDIR}/build $MAIN_HEPWLBUILDDIR "
        ###mv ${MAIN_BUILDEXPORTDIR}/build $MAIN_HEPWLBUILDDIR || fail "[copy_build] cannot mv ${MAIN_BUILDEXPORTDIR}/build $MAIN_HEPWLBUILDDIR"
        echo "[copy_build] cp -dpr ${MAIN_BUILDEXPORTDIR}/build $MAIN_HEPWLBUILDDIR/build "
        cp -dpr ${MAIN_BUILDEXPORTDIR}/build $MAIN_HEPWLBUILDDIR/build || fail "[copy_build] cannot cp -dpr ${MAIN_BUILDEXPORTDIR}/build $MAIN_HEPWLBUILDDIR/build"
        echo "[copy_build] build products copied to $MAIN_HEPWLBUILDDIR/build"
        ls -al $MAIN_HEPWLBUILDDIR/build
      else
        # NB: this should never happen (bmk-driver should have created an empty /build if none was found)
        echo "[copy_build] WARNING! ${MAIN_BUILDEXPORTDIR}/build not found: nothing to copy"
      fi
      echo "[copy_build] finished at $(date)"
      return 0
    }
    
    
    # Remove the cvmfs copy from ${MAIN_HEPWLBUILDDIR}/cvmfs
    function clean_cvmfs_copy(){
      echo "[clean_cvmfs_copy] ................................................"
      echo "[clean_cvmfs_copy] starting at $(date)"
      echo "[clean_cvmfs_copy] current directory is $(pwd)"
      echo "remove cvmfs copy $MAIN_HEPWLBUILDDIR/cvmfs"
      rm -rf "$MAIN_HEPWLBUILDDIR/cvmfs" || fail "[clean_cvmfs_copy] could not remove $MAIN_HEPWLBUILDDIR/cvmfs"
      echo "[clean_cvmfs_copy] finished at $(date)"
      return 0
    }
    
    
    # Build, test and publish the standalone HEP workload image with a local /cvmfs
    # Input environment variable ${HEPWL_DOCKERIMAGENAME}: image name for the HEP workload
    # Input environment variable ${HEPWL_DOCKERIMAGETAG}: image tag for the standalone HEP workload
    # Input environment variable ${CIENV_DOCKERREGISTRY}: registry where the image should be pushed
    # Input environment variable ${HEPWL_BMKOPTS}: options for the HEP workload script
    # Input environment variable ${CIENV_JOBDIR}/results: host directory where results should be stored
    # Output: standalone WL image stored in the local Docker image registry
    # Output: status code for the success of the test and results in ${CIENV_JOBDIR}/results
    # Output: standalone WL image stored in the remote Docker image registry
    function build_standalone_image(){
      echo "[build_standalone_image] ................................................"
      echo "[build_standalone_image] starting at $(date)"
      echo "[build_standalone_image] current directory is $(pwd)"
      theimage="${HEPWL_DOCKERIMAGENAME}":"${HEPWL_DOCKERIMAGETAG}"
      if [ "${CIENV_DOCKERREGISTRY}" != "" ]; then theimage="${CIENV_DOCKERREGISTRY}/${theimage}"; fi
      echo "[build_standalone_image] theimage: $theimage"
      # [NB ./cvmfs here contains the data copied by the function copy_cvmfs]
      # [NB ./build here contains the data copied by the function copy_build]
      build_docker_image "$theimage" || fail "[build_standalone_image] build_docker_image $theimage"
      echo "[build_standalone_image] finished at $(date)"
      return 0
    }
    
    
    # Test the standalone HEP workload image (in the local registry) with a local /cvmfs and test it
    # Input environment variable ${HEPWL_DOCKERIMAGENAME}: image name for the HEP workload
    # Input environment variable ${HEPWL_DOCKERIMAGETAG}: image tag for the standalone HEP workload
    # Input environment variable ${CIENV_DOCKERREGISTRY}: registry where the image should be pushed
    # Input environment variable ${HEPWL_BMKOPTS}: options for the HEP workload script
    # Input environment variable ${HEPWL_BMKUSEGPU}: "1" if this is a GPU workload, "" otherwise
    # Input environment variable ${CIENV_JOBDIR}/results: host directory where results should be stored
    # Output: status code for the success of the test and results in ${CIENV_JOBDIR}/results
    function test_standalone_image_docker(){
      echo "[test_standalone_image_docker] ................................................"
      echo "[test_standalone_image_docker] starting at $(date)"
      echo "[test_standalone_image_docker] current directory is $(pwd)"
      echo "[test_standalone_image_docker] HEPWL_BMKUSEGPU=$HEPWL_BMKUSEGPU"
      if [ "$HEPWL_BMKUSEGPU" == "1" ]; then USEGPU="--gpus all"; else USEGPU=""; fi
      theimage="${HEPWL_DOCKERIMAGENAME}":"${HEPWL_DOCKERIMAGETAG}"
      if [ "${CIENV_DOCKERREGISTRY}" != "" ]; then theimage="${CIENV_DOCKERREGISTRY}/${theimage}"; fi
      echo "[test_standalone_image_docker] theimage: $theimage"
      if [ -z "$HEPWL_BMKOPTS" ]; then HEPWL_BMKOPTS=""; fi
      if [ -z "$HEPWL_DOCKER_EXTRA_ARGS" ]; then HEPWL_DOCKER_EXTRA_ARGS=""; fi
      echo "[test_standalone_image_docker] HEPWL_DOCKER_EXTRA_ARGS options: $HEPWL_DOCKER_EXTRA_ARGS"
    
      strace=""
      net_conn="--network none" # by default run the test without network connectivity
      if [ "$MAIN_NETWORKCONN" == "1" ]; then net_conn=""; fi
      ###strace="--cap-add SYS_PTRACE" # optionally add SYS_PTRACE capability to use strace (see https://github.com/moby/moby/issues/21051)
      echo -e "\n[test_standalone_image_docker] Run WL in docker (to test the image) via execute_command - started"
      if ! execute_command docker run "${HEPWL_DOCKER_EXTRA_ARGS}" ${strace} ${net_conn} --rm $USEGPU -v $CIENV_JOBDIR/results:/results $theimage $HEPWL_BMKOPTS -d; then
        docker rmi -f $theimage # BMK-122
        fail "[test_standalone_image_docker] docker run $theimage"
      fi
      echo -e "[test_standalone_image_docker] Run WL in docker (to test the image) via execute_command - completed\n"
      echo "[test_standalone_image_docker] validate json file" # see BMK-137
      jsonFile=$(ls -1tr $CIENV_JOBDIR/results/*/${HEPWL_BMKDIR}_summary.json 1> >( head -1 ) ) || fail "[test_standalone_image_docker] json summary file not found" # process substitution retains error code
      validate_jsonfile $jsonFile || fail "[test_standalone_image_docker] validate json file"
    
      echo -e "\n[test_standalone_image_docker] Call the WL with --help"
      execute_command docker run ${net_conn} --rm $USEGPU $theimage --help || fail "[test_standalone_image_docker] help call failed"
    
      echo "[test_standalone_image_docker] finished at $(date)"
      return 0
    }
    
    
    # Publish the standalone HEP workload image with a local /cvmfs
    # Input environment variable ${HEPWL_DOCKERIMAGENAME}: image name for the HEP workload
    # Input environment variable ${HEPWL_DOCKERIMAGETAG}: image tag for the standalone HEP workload
    # Input environment variable ${CIENV_DOCKERREGISTRY}: registry where the image should be pushed
    # Input environment variable ${CIENV_SINGULARITYREGISTRY}: registry where the image should be pushed
    # Input environment variable ${CIENV_SINGULARITYROBOT}: robot user for the registry where the image should be pushed
    # Input environment variable ${CI_JOB_TOKEN}: authentication token for the gitlab registry
    # Input environment variable ${CI_SING_TOKEN}: authentication token for the HARBOR registry
    # Output: standalone WL image stored in the remote Docker image registry
    function publish_standalone_image(){
      echo "[publish_standalone_image] ................................................"
      echo "[publish_standalone_image] starting at $(date)"
      echo "[publish_standalone_image] current directory is $(pwd)"
      if [ "${CIENV_DOCKERREGISTRY}" == "" ]; then
        echo "[publish_standalone_image] WARNING: empty CIENV_DOCKERREGISTRY, no need to push to docker registry"
      else
        ARCH="$(uname -m)"
        theimage="${CIENV_DOCKERREGISTRY}/${HEPWL_DOCKERIMAGENAME}:${HEPWL_DOCKERIMAGETAG}"
        echo "[publish_standalone_image] theimage: $theimage"
        echo $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin gitlab-registry.cern.ch || fail "[publish_standalone_image] docker login"
        echo "[publish_standalone_image] docker push ${theimage}_${ARCH}"
        docker tag "${theimage}" "${theimage}_${ARCH}" || fail "[publish_standalone_image] docker tag ${theimage} ${theimage}_${ARCH}"
        docker push "${theimage}_${ARCH}" || fail "[publish_standalone_image] docker push ${theimage}_${ARCH}"
        if [[ ${HEPWL_DOCKERIMAGETAG} =~ ^v[0-9]*\.[0-9]*$ ]]; then # flag as latest only images that respect the format v[0-9]*\.[0-9]*
          theimage_latest=${CIENV_DOCKERREGISTRY}/${HEPWL_DOCKERIMAGENAME}:latest_${ARCH}
          docker tag "${theimage}" "${theimage_latest}" || fail "[publish_standalone_image] docker tag ${theimage} ${theimage_latest}"
          docker push "${theimage_latest}" || fail "[publish_standalone_image] docker push ${theimage_latest}"
          docker rmi "${theimage_latest}" # BMK-122 clean local tag latest to free up space
          # Tag the image also with the git commit sha id (BMK-319) 
          theimage_shacommit=${CIENV_DOCKERREGISTRY}/${HEPWL_DOCKERIMAGENAME}:${CI_COMMIT_SHORT_SHA}_${ARCH}
          docker tag "${theimage}" "${theimage_shacommit}" || fail "[publish_standalone_image] docker tag ${theimage} ${theimage_shacommit}"
          docker push "${theimage_shacommit}" || fail "[publish_standalone_image] docker push ${theimage_shacommit}"
          docker rmi "${theimage_shacommit}" # BMK-122 clean local tag latest to free up space
        fi
        theimage_cilatest=${HEPWL_DOCKERIMAGENAME}:cilatest_${ARCH} # create a local tag cilatest to allow caching (BMK-159)
        docker rmi "${theimage_cilatest}" || echo "No cached image ${theimage_cilatest} to be removed"  # Clean previous cache image (BMK-320)
        docker tag "${theimage}" "${theimage_cilatest}" || fail "[publish_standalone_image] docker tag ${theimage} ${theimage_cilatest}"
        echo "[publish_standalone_image] remove local image ${theimage}"
        docker rmi ${theimage} # BMK-122 clean local image to free up space (keep only cilatest tag to allow caching BMK-159)
      fi
      echo "[publish_standalone_image] finished at $(date)"
      return 0
    }
    
    
    # Before building a new image $HEPWL_DOCKERIMAGENAME:$HEPWL_DOCKERIMAGETAG, check that the required
    # tag HEPWL_DOCKERIMAGETAG is greater than the "latest" tag in the registry, otherwise fail
    # Input environment variable ${HEPWL_DOCKERIMAGENAME}: image name for the HEP workload
    # Input environment variable ${HEPWL_DOCKERIMAGETAG}: image tag for the standalone HEP workload
    # Input environment variable ${CIENV_DOCKERREGISTRY}: registry where the image should be pushed
    function check_tag_version(){
      echo "[check_tag_version] ................................................"
      echo "[check_tag_version] starting at $(date)"
      echo "[check_tag_version] current directory is $(pwd)"
      theimage=${CIENV_DOCKERREGISTRY}/${HEPWL_DOCKERIMAGENAME}
      if [[ ! ${HEPWL_DOCKERIMAGETAG} =~ ^v[0-9]*\.[0-9]*$ ]]; then
        echo "[check_tag_version] image tag ${HEPWL_DOCKERIMAGETAG} does NOT respect the format v[0-9]*\.[0-9]* "
        return 0
      fi
      echo "[check_tag_version] testing image ${theimage}:latest"
      res=`skopeo inspect docker://${theimage}:latest`
      echo ${res}
      oldsha=`echo ${res} | jq '.["Digest"]' | sed -e 's@"@@g'`
      tags=`echo ${res} | jq '.["RepoTags"][] | select (.!="latest")' | sed -e 's@"@@g'`
      echo "[check_tag_version] signature ${oldsha}"
      echo "[check_tag_version] tags ${tags} " | xargs
      oldtag=''
      for tag in ${tags}
      do
        testsha=`skopeo inspect docker://${theimage}:${tag} | jq '.["Digest"]' | sed -e 's@"@@g'`
        if [ "${testsha}" == "${oldsha}" ] && [[ ${tag} =~ ^v[0-9]*\.[0-9]*$ ]]; then
          oldtag=${tag}
          echo "[check_tag_version] tag 'latest' is currently associated to ${oldtag}"
          break;
        fi
      done
      if [ "${oldtag}" == "" ]; then
        echo "[check_tag_version] tag 'latest' is currently not associated to any tag"
      else
        oldtag=${oldtag/v/}
        newtag=${HEPWL_DOCKERIMAGETAG/v/}
        oldtagmajor=${oldtag%%.*}
        oldtagminor=${oldtag##*.}
        newtagmajor=${newtag%%.*}
        newtagminor=${newtag##*.}
        if [ $newtagmajor -lt $oldtagmajor ]; then
          echo -e "\n[check_tag_version] new image tag v${newtag} MUST be greater than tag v${oldtag} currently associated to ${theimage}:latest\n\n"
          return 1
        elif [ $newtagmajor -eq $oldtagmajor ] && [ $newtagminor -le $oldtagminor ]; then
          echo -e "\n[check_tag_version] new image tag v${newtag} MUST be greater than tag v${oldtag} currently associated to ${theimage}:latest\n\n"
          return 1
        fi
      fi
      echo "[check_tag_version] finished at $(date)"
      return 0
    }
    
    
    function execute_procedure(){
      echo "[execute_procedure] ................................................"
      echo "[execute_procedure] starting at $(date)"
      echo "[execute_procedure] current directory is $(pwd)"
      echo "[execute_procedure] option '$1'"
      case $1 in
        none)
          echo "Do not run any step of the procedure."
          ;;
        sleep)
          echo -e "\nSleep and stay alive just to expose cvmfs"
          echo "cvmfs mount point $CIENV_CVMFSVOLUME"
          echo "You can mount the available cvmfs as follow:"
          echo "export CVMFSDIR=$CIENV_CVMFSVOLUME "
          echo "docker run -v \$CVMFSDIR:/cvmfs:shared ..."
          sleep infinity
          ;;
        all)
          echo "Run the full sequence."
          check_tag_version || fail "[execute_procedure] $1: check_tag_version"
          if [ "$HEPWL_CVMFSREPOS" != "NONE" ];
            then
              echo "[execute_procedure] Running procedure to snapshot cmvfs via the following functions: run_docker_wl, run_shrinkwrap, copy_cvmfs, copy_build."
              run_docker_wl || fail "[execute_procedure] $1: run_docker_wl"
              run_shrinkwrap || fail "[execute_procedure] $1: run_shrinkwrap"
              copy_cvmfs || fail "[execute_procedure] $1: copy_cvmfs"
              copy_build || fail "[execute_procedure] $1: copy_build"
            else
              # Needed for the cases without cvmfs, to make available an empty dir
              echo "[execute_procedure] No cvmfs snapshotting needed. Skipping this step. Just run create_functional_dirs."
              create_functional_dirs
          fi
          build_standalone_image || fail "[execute_procedure] $1: build_standalone_image"
          clean_cvmfs_copy || fail "[execute_procedure] $1: clean_cvmfs_copy"
          test_standalone_image_docker || fail "[execute_procedure] $1: test_standalone_image_docker"
          #test_standalone_image_singularity || fail "[execute_procedure] $1: test_standalone_image_singularity"
          publish_standalone_image || fail "[execute_procedure] $1: publish_standalone_image"
          #announce_standalone_image || fail "[execute_procedure] $1: announce_standalone_image"
          ;;
        *)
          fail "[execute_procedure] Unknown option $1"
      esac
      echo "[execute_procedure] finished at $(date)"
      return 0
    }
    
    ### Main
    
    echo -e "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
    echo -e "\n[main.sh] starting at $(date)\n"
    echo -e "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
    
    echo -e "Current directory is $(pwd)\n"
    
    echo "CIENV_JOBDIR is ${CIENV_JOBDIR}" # within docker this may come from .env.file
    
    echo "CI_PROJECT_DIR is ${CI_PROJECT_DIR}" # within docker this may come from .env.file
    
    scriptDir=`dirname $0`
    echo "scriptDir is ${scriptDir}"
    
    # Load function generate_Dockerfile()
    . ${scriptDir}/generate_Dockerfile.sh || fail '[main.sh] . generate_Dockerfile.sh'
    
    # These are fixed params, to be moved in an etc file
    export MAIN_CVMFSTRACESDIR=$CIENV_JOBDIR/cvmfs-traces/
    export MAIN_CVMFSEXPORTDIR=$CIENV_JOBDIR/cvmfs-export/
    
    # Use /results to export any build products (BMK-779)
    # In practice, main.sh copies build results from /results/build into the standalone image's /bmk/build
    # In the first docker run, workloads should copy any build products into /results/build
    export MAIN_BUILDEXPORTDIR=${CIENV_JOBDIR}/results
    
    # [NB MAIN_NETWORKCONN, which was only used in main.sh, is now a local variable]
    MAIN_NETWORKCONN=0 # this can be overridden via the -n command line option
    
    # NEW OPTION 1 (BMK-145) - main.sh as a subprocess
    # Previously these environment variables were reset to default values here
    # To make this cleaner (BMK-145), it is now expected that they must have been set in advance
    #for var in CIENV_HEPWL_SPECFILE CIENV_BUILDEVENT CIENV_DOCKERREGISTRY CIENV_MOUNTCVMFS CIENV_JOBDIR MAIN_NETWORKCONN; do
    #  # Bash indirection ${!var}: see http://mywiki.wooledge.org/BashFAQ/006#Indirection
    #  if [ "${!var}" == "" ]; then echo "ERROR! $var is not set"; exit 1; fi
    #  echo "$var=${!var}"
    #done
    
    # Parameters to be passed by command line
    # 1: SPEC file path
    # 2: optional export CIENV_DOCKERREGISTRY gitlab-registry.cern.ch/giordano/hep-workloads
    while getopts "hds:r:e:mn" o; do
      case ${o} in
        s)
          if [ "$OPTARG" != "" ]; then export CIENV_HEPWL_SPECFILE="$OPTARG"; fi
          ;;
        e)
          if [ "$OPTARG" != "" ]; then export CIENV_BUILDEVENT="$OPTARG"; fi
          ;;
        r)
          if [ "$OPTARG" != "" ]; then export CIENV_DOCKERREGISTRY="$OPTARG"; fi
          ;;
        m)
          CIENV_MOUNTCVMFS=1
          ;;
        n)
          MAIN_NETWORKCONN=1
          ;;
        d)
          set -x # Enable debug printouts
          ;;
        h)
          echo "Usage: $0 -s <full path to HEP Workload Spec file> [-e <execution step (none|sleep|all): default is all>] [-r <docker registry>] [-d (enable debug printouts)] [-n (force external network connectivity: default is false)"
          fail "Invalid command line options"
          ;;
        \?)
          fail "Invalid command line options: ${o}"
          ;;
      esac
    done
    
    # OLD OPTION 2 (BMK-145) - main.sh as an entrypoint of docker run
    for var in CIENV_HEPWL_SPECFILE CIENV_BUILDEVENT CIENV_MOUNTCVMFS CIENV_JOBDIR MAIN_NETWORKCONN; do
      # Bash indirection ${!var}: see http://mywiki.wooledge.org/BashFAQ/006#Indirection
      if [ "${!var}" == "" ]; then echo "ERROR! $var is not set"; exit 1; fi
      echo "$var=${!var}"
    done
    # [NB CIENV_DOCKERREGISTRY can be empty in interactive builds from run_build.sh]
    echo "CIENV_DOCKERREGISTRY=$CIENV_DOCKERREGISTRY"
    echo "CIENV_SINGULARITYREGISTRY=${CIENV_SINGULARITYREGISTRY}"
    echo "CIENV_SINGULARITYROBOT=${CIENV_SINGULARITYROBOT}"
    
    echo -e "\n[main] Input parameters\n-------------"
    echo CIENV_HEPWL_SPECFILE "$CIENV_HEPWL_SPECFILE"
    echo CIENV_BUILDEVENT "$CIENV_BUILDEVENT"
    echo CIENV_DOCKERREGISTRY "$CIENV_DOCKERREGISTRY"
    echo CIENV_SINGULARITYREGISTRY "$CIENV_SINGULARITYREGISTRY"
    echo CIENV_SINGULARITYROBOT "$CIENV_SINGULARITYROBOT"
    echo CIENV_MOUNTCVMFS "$CIENV_MOUNTCVMFS"
    echo CIENV_JOBDIR "$CIENV_JOBDIR"
    echo CIENV_CVMFSVOLUME "$CIENV_CVMFSVOLUME"
    echo "-------------"
    
    env | grep "CI_" | sort
    
    if [ "$CIENV_BUILDEVENT" != "none" ] && [ "$CIENV_BUILDEVENT" != "sleep" ] && [ "$CIENV_BUILDEVENT" != "all" ]; then fail "Unknown option CIENV_BUILDEVENT=$CIENV_BUILDEVENT"; fi
    
    if [ "$CIENV_HEPWL_SPECFILE" == "" ]; then fail "SPEC file not specified"; fi
    
    if [ ! -f $CIENV_HEPWL_SPECFILE ]; then fail "SPEC file $CIENV_HEPWL_SPECFILE not found"; fi
    
    cp -dpr $(dirname "$CIENV_HEPWL_SPECFILE") $CIENV_JOBDIR/build-wl
    cp -dpr $(dirname "$CIENV_HEPWL_SPECFILE")/../../common $CIENV_JOBDIR/build-wl/common
    export MAIN_HEPWLBUILDDIR=$CIENV_JOBDIR/build-wl
    
    CIENV_HEPWL_SPECFILE=$CIENV_JOBDIR/build-wl/${CIENV_HEPWL_SPECFILE##*/}
    
    echo "----------- $CIENV_HEPWL_SPECFILE"
    cat "$CIENV_HEPWL_SPECFILE"
    echo "-----------"
    
    # This sets all environment variables starting with HEPWL_ used in this script (BMK-118)
    # It should always set HEPWL_BMKEXE, HEPWL_BMKOPTS, HEPWL_BMKDIR, HEPWL_BMKDESCRIPTION,
    # as well as HEPWL_DOCKERIMAGENAME, HEPWL_DOCKERIMAGETAG and HEPWL_CVMFSREPOS
    # It may optionally set also HEPWL_BMKOS, HEPWL_BMKANNOUNCE and HEPWL_EXTEND_<repo>_SPEC
    if ! load_and_validate_specfile "$CIENV_HEPWL_SPECFILE"; then fail "invalid SPEC file"; fi
    
    if [ "$CIENV_MOUNTCVMFS" -gt 0 ] && [ "$HEPWL_CVMFSREPOS" != "NONE" ]; then
      mount_cvmfs || fail '[main.sh] mount_cvmfs'
    else
      echo -e "\n[main.sh] INFO: there is not cvmfs repo to be mounted\n"
    fi
    
    execute_procedure "$CIENV_BUILDEVENT"
    
    if [ "$CIENV_MOUNTCVMFS" -gt 0 ] && [ "$HEPWL_CVMFSREPOS" != "NONE" ]; then
      unmount_cvmfs || fail '[main.sh] unmount_cvmfs'
    else
      echo -e "\n[main.sh] INFO: there is not cvmfs repo to be unmounted\n"
    fi
    
    echo -e "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
    echo -e "\n[main.sh] finished (OK) at $(date)\n"
    echo -e "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"