diff --git a/.ci/build.bash b/.ci/build.bash
index 3b12342fa078c13cf9d0c912c120602fcecb6cc5..5caa35e1ac5f4f868b9c81a150a4b525d46d9094 100644
--- a/.ci/build.bash
+++ b/.ci/build.bash
@@ -6,31 +6,21 @@ set -x
 whoami
 env | sort
 
-if [ -f .ci/env ]; then
-    # add quotes to all vars (but do it once)
-    sudo sed -i -E 's/="*(.*[^"])"*$/="\1"/' .ci/env 
-    source '.ci/env'
-fi
-
 if [ -z ${PYTHON_VERSION_MAJOR_MINOR} ]; then
     export PYTHON_VERSION_MAJOR=$(python -c "import sys; print(sys.version_info.major)")
     export PYTHON_VERSION_MAJOR_MINOR=$(python -c "import sys; print('{}.{}'.format(sys.version_info.major, sys.version_info.minor))")
 fi
 
-# Force the build to happen in the build workspace, not in the container
-# This should be done at the beginning, but there are some circular dependencies
-# between Gazebo build artifacts (existing in the container) and nrp aliases/variables
-
+# Force the build to happen in the build workspace
 export HBP=$WORKSPACE
 cd $WORKSPACE
 export PYTHONPATH=
-source ${USER_SCRIPTS_DIR}/nrp_variables
 
 cd ${WORKSPACE}/${VIRTUAL_COACH_DIR}
 
 
 # Configure build
-export VIRTUAL_ENV_PATH=$VIRTUAL_ENV
+export VIRTUAL_ENV_PATH="${VIRTUAL_ENV}"
 export NRP_INSTALL_MODE=dev
 export PYTHONPATH=hbp_nrp_virtual_coach:${VIRTUAL_ENV_PATH}/lib/python${PYTHON_VERSION_MAJOR_MINOR}/site-packages:$PYTHONPATH
 
diff --git a/.ci/whl-package.bash b/.ci/whl-package.bash
index 221e552a5ab9430160e95bd186cbd679d82507ce..2b3e37698586bb4d7792ae3f8b2ffe13ae926462 100644
--- a/.ci/whl-package.bash
+++ b/.ci/whl-package.bash
@@ -2,11 +2,8 @@
 set -e
 
 # Build package
-git fetch --tags
-PYNRP_VERSION=$(git describe --tags --always)
-
-echo "${PYNRP_VERSION}"
-echo "VERSION = \"${PYNRP_VERSION}\"" > hbp_nrp_virtual_coach/pynrp/version.py
+export NRP_INSTALL_MODE=user
+make set-nrp-version
 
 pushd hbp_nrp_virtual_coach
 python3 setup.py sdist bdist_wheel
diff --git a/Jenkinsfile b/Jenkinsfile
index f1c50f96a0c62338a4f2672a9cf6540a74d52646..047ca08201281570e5b228534d37f2459845f3a9 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -24,7 +24,6 @@ pipeline {
     environment {
         USER_SCRIPTS_DIR = "user-scripts"
         ADMIN_SCRIPTS_DIR = "admin-scripts"
-        GAZEBO_ROS_DIR = "GazeboRosPackages"
         VIRTUAL_COACH_DIR = "VirtualCoach"
         // GIT_CHECKOUT_DIR is a dir of the main project (that was pushed)
         GIT_CHECKOUT_DIR = "${env.VIRTUAL_COACH_DIR}"
@@ -41,9 +40,11 @@ pipeline {
     agent {
         docker {
             label 'ci_label'
-
+            alwaysPull true
             // NEXUS_REGISTRY_IP and NEXUS_REGISTRY_PORT are Jenkins global variables
-            image "${env.NEXUS_REGISTRY_IP}:${env.NEXUS_REGISTRY_PORT}/nrp:${IMG_TAG}"
+            registryUrl "https://${env.NEXUS_REGISTRY_IP}:${env.NEXUS_REGISTRY_PORT}"
+            registryCredentialsId 'nexusadmin'
+            image "nrp:${IMG_TAG}"
             args '--entrypoint="" -u root --privileged'
         }
     }
@@ -68,28 +69,23 @@ pipeline {
 
                 // Checkout main project to GIT_CHECKOUT_DIR
                 dir(env.GIT_CHECKOUT_DIR) {
-                    checkout scm
+                    checkout([
+                        $class: "GitSCM",
+                        branches: scm.branches, 
+                        extensions: [
+                            [$class: 'CloneOption', noTags: false],
+                            [$class: 'LocalBranch', localBranch: "**"]
+                        ], 
+                        userRemoteConfigs: scm.userRemoteConfigs
+                    ])
                     sh 'chown -R "${USER}" ./'
                 }
 
                 // Clone all dependencies
                 cloneRepoTopic(env.USER_SCRIPTS_DIR,  'git@bitbucket.org:hbpneurorobotics/user-scripts.git',      env.TOPIC_BRANCH, env.DEFAULT_BRANCH,     '${USER}')
-                cloneRepoTopic(env.GAZEBO_ROS_DIR,    'git@bitbucket.org:hbpneurorobotics/gazeborospackages.git', env.TOPIC_BRANCH, env.DEFAULT_BRANCH,  '${USER}')
                 cloneRepoTopic(env.ADMIN_SCRIPTS_DIR, 'git@bitbucket.org:hbpneurorobotics/admin-scripts.git',     env.TOPIC_BRANCH, 'master',       '${USER}')
 
-            }
-        }
-
-        stage('Build GazeboRosPackages') {
-            steps {
-                bitbucketStatusNotify(buildState: 'INPROGRESS', buildName: 'Building GazeboRosPackages')
-                
-                // Use GazeboRosPackages build script
-                dir(env.GAZEBO_ROS_DIR){
-                    // Determine explicitly the shell as bash (needed for proper user-scripts operation)
-                    sh 'bash .ci/build.bash ${WORKSPACE}/${USER_SCRIPTS_DIR}/'
-                }
-                
+                sh "git config --global --add safe.directory '*'"
             }
         }
 
@@ -99,10 +95,7 @@ pipeline {
 
                 // Determine explicitly the shell as bash (needed for proper user-scripts operation)
                 dir(env.GIT_CHECKOUT_DIR){
-                    // this is a workaround to pass all env vars into script run by the other user (now we are root)
-                    sh 'env > .ci/env'
-                    sh 'sudo -H -u bbpnrsoa bash .ci/build.bash'
-
+                    sh 'bash .ci/build.bash'
                     // deliver artifacts
                     // Make true to false when the coverage reaches 85%-90%. This with turn off auto threshold for coverage
                     makeReports(true, env.CODE_COVERAGE_LINE)
@@ -115,8 +108,8 @@ pipeline {
                 bitbucketStatusNotify(buildState: 'INPROGRESS', buildName: 'Building pynrp')
 
                 dir(env.GIT_CHECKOUT_DIR){
-                    // tbuild package
-                    sh 'bash ./.ci/whl-package.bash'
+                    // build package
+                    sh "export HBP=${WORKSPACE} && bash ./.ci/whl-package.bash"
                     // upload package
                     withCredentials([usernamePassword(credentialsId: 'nexusadmin', usernameVariable: 'USER', passwordVariable: 'PASSWORD')])
                     {
diff --git a/Makefile b/Makefile
index 3e2838aecba22f715b2c6cd85f53f46673dbcf98..fe7b1d20de3e8a1fe6d1617c7c6d2739819e2af3 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ PYTHON_PIP_VERSION?=pip>=19
 python_version_full := $(shell python -c "import sys; maj, min = sys.version_info[:2]; print('{}.{}'.format(maj, min))" 2>&1)
 
 ifeq ($(NRP_INSTALL_MODE),user)
-        include user_makefile
+        include $(HBP)/user-scripts/config_files/user_makefile
 else
         CI_REPO?=git@bitbucket.org:hbpneurorobotics/admin-scripts.git
         CI_DIR?=$(HBP)/admin-scripts/ContinuousIntegration
diff --git a/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py b/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py
index d4cc2a9df225a721b9d16735ca088b93e20d933e..1ebf9974877ab328d0d7841554e7925d6be8f141 100644
--- a/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py
+++ b/hbp_nrp_virtual_coach/pynrp/tests/test_virtual_coach.py
@@ -29,6 +29,7 @@ from future import standard_library
 standard_library.install_aliases()
 from builtins import object
 from pynrp.virtual_coach import VirtualCoach
+import pynrp.version
 
 from unittest.mock import Mock, patch, MagicMock
 import unittest
@@ -149,6 +150,7 @@ class TestVirtualCoach(unittest.TestCase):
                                                       "sdf":"example_model_2.sdf",
                                                       "configPath":"example_model_2/model.config"}}
 
+
     def test_init_asserts_no_password(self):
         # invalid environment
         self.assertRaises(AssertionError, VirtualCoach, environment=True)
@@ -166,6 +168,14 @@ class TestVirtualCoach(unittest.TestCase):
     def test_no_login_credentials(self):
         self.assertRaises(Exception, VirtualCoach)
 
+    def test_version(self):
+        self.assertRegex(pynrp.version._get_version(), "^\d+.\d+.\d+")
+        self.assertRegex(pynrp.version.VERSION, "^\d+.\d+.\d+")
+
+    @patch('os.getenv', return_value="/non-existing-path")
+    def test_version_exception(self, mock_getenv):
+        self.assertRaises(RuntimeError, pynrp.version._get_version)
+
 
     @patch('pynrp.virtual_coach.VirtualCoach._VirtualCoach__get_storage_token')
     @patch('getpass.getpass', return_value='password')
diff --git a/hbp_nrp_virtual_coach/pynrp/version.py b/hbp_nrp_virtual_coach/pynrp/version.py
index d5d3104d2c2a29ea0c5501bda007849b39c99b00..006613be1f6ed150d46e0c0e8c6cd246ee89b433 100644
--- a/hbp_nrp_virtual_coach/pynrp/version.py
+++ b/hbp_nrp_virtual_coach/pynrp/version.py
@@ -1,2 +1,18 @@
-'''version string - generated by setVersion.sh'''
-VERSION = '3.2.1'
+'''version string - automatically calculated from the SCM (git)'''
+import subprocess
+import os
+from subprocess import CalledProcessError
+
+def _get_version():
+    try:
+        version = subprocess.run(['bash',
+                            f'{os.getenv("HBP")}/user-scripts/nrp_get_scm_version.sh',
+                            'get_scm_version'],
+                            stdout=subprocess.PIPE, check=True).stdout.decode('utf-8')
+    except CalledProcessError as e:
+        raise RuntimeError("The SCM version calculation script failed.\
+            Expected path: $HBP/user-scripts/nrp_get_scm_version.sh,\
+            check its existance.") from e
+    return version
+
+VERSION = _get_version()