diff --git a/.env b/.env
index 93e626530f31129609801d8e4bb3e61003ad3362..786f93e2274be154065ab3b3f4e9f937c8a2c102 100644
--- a/.env
+++ b/.env
@@ -2,8 +2,7 @@ BUILDCACHE_OCI_HOST=""
 BUILDCACHE_OCI_PASSWORD=""
 BUILDCACHE_OCI_PROJECT=""
 BUILDCACHE_OCI_USERNAME=""
-
 CONCRETIZE_OCI_HOST=""
 CONCRETIZE_OCI_PASSWORD=""
 CONCRETIZE_OCI_PROJECT=""
-CONCRETIZE_OCI_USERNAME""
+CONCRETIZE_OCI_USERNAME=""
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8b5459c7205a666d4595a00b8f54f4bd79c64132..57a429a420966e04b8d53ac0645d8512310df367 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -39,5 +39,6 @@ testing-pytest-coverage:
     paths:
       - test-results.xml
       - .dedal.log
+      - .generate_cache.log
     expire_in: 1 week
 
diff --git a/README.md b/README.md
index dd68dcfa9fecfd80b6cf28a890a71a80c4bed9b1..733d8ff63a4f2aabb3634707b85c252e4cfb284d 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,13 @@
 # Dedal
 
-This repository provides functionalities to easily ```managed spack environments``` and ```helpers for the container image build flow```.
-
-This library runs only on different kinds of linux distribution operating systems.
-
-The lowest ```spack version``` compatible with this library is ```v0.23.0```.
-
-
-        This repository also provied CLI interface. For more informations, after installing this library, call dedal --help.
-
+This repository provides functionalities to easily ```managed spack environments``` and
+```helpers for the container image build flow```.
 
 **Setting up the needed environment variables**
-    The ````<checkout path>\dedal\.env```` file contains the environment variables required for OCI registry used for caching.
-    Ensure that you edit the ````<checkout path>\dedal\.env```` file to match your environment.
-    The following provides an explanation of the various environment variables:
-
+The ````<checkout path>\dedal\.env```` file contains the environment variables required for OCI registry used for
+caching.
+Ensure that you edit the ````<checkout path>\dedal\.env```` file to match your environment.
+The following provides an explanation of the various environment variables:
 
        # OCI Registry Configuration Sample for concretization caches
        # =============================
@@ -49,5 +42,101 @@ The lowest ```spack version``` compatible with this library is ```v0.23.0```.
        # The password used for authentication with the Docker registry.
        BUILDCACHE_OCI_HOST="###ACCESS_TOKEN###"
 
-For both concretization and binary caches, the cache version can be changed via the attributes ```cache_version_concretize``` and ```cache_version_build```. 
+For both concretization and binary caches, the cache version can be changed via the attributes
+```cache_version_concretize``` and ```cache_version_build```.
 The default values are ```v1```.
+
+Before using this library, the following tool must be installed on Linux distribution:
+
+````
+    apt install -y bzip2 ca-certificates g++ gcc gfortran git gzip lsb-release patch python3 python3-pip tar unzip xz-utils zstd
+````
+
+````
+    python3 -m pip install --upgrade pip setuptools wheel
+````
+
+# Dedal library installation
+
+```sh
+  pip install dedal
+```
+
+# Dedal CLI Commands
+
+The following commands are available in this CLI tool. You can view detailed explanations by using the `--help` option
+with any command.
+
+### 1. `dedal install-spack`
+
+Install spack in the install_dir folder.
+
+**Options:**
+
+- `--spack_version <TEXT>` : Specifies the Spack version to be installed (default: v0.23.0).
+- `--bashrc_path <TEXT>` : Defines the path to .bashrc.
+
+### 2. `dedal set-config`
+
+Sets configuration parameters for the session.
+
+**Options:**
+
+- `--use_cache`                     Enables cashing
+- `--use_spack_global`              Uses spack installed globally on the os
+- `--env_name <TEXT>`                 Environment name
+- `--env_path <TEXT>`                 Environment path to download locally
+- `--env_git_path <TEXT>`             Git path to download the environment
+- `--install_dir <TEXT>`              Install directory for installing spack;
+  spack environments and repositories are
+  stored here
+- `--upstream_instance <TEXT>`        Upstream instance for spack environment
+- `--system_name <TEXT>`              System name; it is used inside the spack
+  environment
+- `--concretization_dir <TEXT>`       Directory where the concretization caching
+  (spack.lock) will be downloaded
+- `--buildcache_dir <TEXT>`           Directory where the binary caching is
+  downloaded for the spack packages
+- `--gpg_name <TEXT>`                 Gpg name
+- `--gpg_mail <TEXT>`                 Gpg mail contact address
+- `--cache_version_concretize <TEXT>`
+  Cache version for concretizaion data
+- `--cache_version_build <TEXT>`      Cache version for binary caches data
+
+### 3. `dedal show-config`
+
+Show the current configuration.
+
+### 4. `dedal clear-config`
+
+Clears stored configuration
+
+### 5. `dedal add-spack-repo`
+
+Adds a spack repository to the spack environments.
+
+**Options:**
+
+- `--repo_name <TEXT>`  Repository name  [required]
+- `--path <TEXT>`       Repository path to download locally  [required]
+- `--git_path <TEXT>`   Git path to download the repository  [required]
+
+### 6. `dedal setup-spack-env`
+
+Setups a spack environment according to the given configuration.
+
+### 7. `dedal concretize`
+
+Spack concretization step.
+
+### 9. `dedal install-packages`
+
+Installs spack packages present in the spack environment defined in configuration.
+
+**Options:**
+
+- `--jobs <INTEGER>`  Number of parallel jobs for spack installation
+
+# Dedal's UML diagram
+
+![screenshot](dedal/docs/resources/dedal_UML.png)
\ No newline at end of file
diff --git a/dedal/bll/SpackManager.py b/dedal/bll/SpackManager.py
new file mode 100644
index 0000000000000000000000000000000000000000..e5fae221c7093bff86a4adf541e4664fda353dff
--- /dev/null
+++ b/dedal/bll/SpackManager.py
@@ -0,0 +1,35 @@
+import os
+from dedal.model.SpackDescriptor import SpackDescriptor
+from dedal.spack_factory.SpackOperationCreator import SpackOperationCreator
+from dedal.configuration.SpackConfig import SpackConfig
+
+
+class SpackManager:
+    """
+    This class defines the logic used by the CLI
+    """
+
+    def __init__(self, spack_config: SpackConfig = None, use_cache=False):
+        self._spack_config = spack_config
+        self._use_cache = use_cache
+
+    def _get_spack_operation(self):
+        return SpackOperationCreator.get_spack_operator(self._spack_config, self._use_cache)
+
+    def install_spack(self, version: str, bashrc_path=os.path.expanduser("~/.bashrc")):
+        self._get_spack_operation().install_spack(spack_version=f'v{version}', bashrc_path=bashrc_path)
+
+    def add_spack_repo(self, repo: SpackDescriptor):
+        """
+        After additional repo was added, setup_spack_env must be invoked
+        """
+        self._spack_config.add_repo(repo)
+
+    def setup_spack_env(self):
+        self._get_spack_operation().setup_spack_env()
+
+    def concretize_spack_env(self):
+        self._get_spack_operation().concretize_spack_env()
+
+    def install_packages(self, jobs: int):
+        self._get_spack_operation().install_packages(jobs=jobs)
diff --git a/dedal/bll/__init__.py b/dedal/bll/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/dedal/bll/cli_utils.py b/dedal/bll/cli_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..bfc74ed0262aa944c88d722a558a88fccaad0687
--- /dev/null
+++ b/dedal/bll/cli_utils.py
@@ -0,0 +1,23 @@
+import jsonpickle
+import os
+
+
+def save_config(spack_config_data, config_path: str):
+    """Save config to JSON file."""
+    with open(config_path, "w") as data_file:
+        data_file.write(jsonpickle.encode(spack_config_data))
+
+
+def load_config(config_path: str):
+    """Load config from JSON file."""
+    if os.path.exists(config_path):
+        with open(config_path, "r") as data_file:
+            data = jsonpickle.decode(data_file.read())
+            return data
+    return {}
+
+
+def clear_config(config_path: str):
+    """Delete the JSON config file."""
+    if os.path.exists(config_path):
+        os.remove(config_path)
diff --git a/dedal/cli/SpackManager.py b/dedal/cli/SpackManager.py
deleted file mode 100644
index 11eefce81a0458aa22b18e061a25bd6245e1c650..0000000000000000000000000000000000000000
--- a/dedal/cli/SpackManager.py
+++ /dev/null
@@ -1,2 +0,0 @@
-class SpackManager:
-    pass
diff --git a/dedal/cli/spack_manager_api.py b/dedal/cli/spack_manager_api.py
new file mode 100644
index 0000000000000000000000000000000000000000..497bce91c41112ed24309cec5904588572d352ca
--- /dev/null
+++ b/dedal/cli/spack_manager_api.py
@@ -0,0 +1,153 @@
+import os
+from pathlib import Path
+import click
+import jsonpickle
+
+from dedal.bll.SpackManager import SpackManager
+from dedal.bll.cli_utils import save_config, load_config
+from dedal.configuration.GpgConfig import GpgConfig
+from dedal.configuration.SpackConfig import SpackConfig
+from dedal.model.SpackDescriptor import SpackDescriptor
+from dedal.utils.utils import resolve_path
+
+SESSION_CONFIG_PATH = os.path.expanduser(f'~/tmp/dedal/dedal_session.json')
+os.makedirs(os.path.dirname(SESSION_CONFIG_PATH), exist_ok=True)
+
+
+@click.group()
+@click.pass_context
+def cli(ctx: click.Context):
+    config = load_config(SESSION_CONFIG_PATH)
+    if ctx.invoked_subcommand not in ['set-config', 'install-spack'] and not config:
+        click.echo('No configuration set. Use `set-config` first.')
+        ctx.exit(1)
+    if config:
+        config['env_path'] = resolve_path(config['env_path'])
+        env = SpackDescriptor(config['env_name'], config['env_path'], config['env_git_path'])
+        gpg = GpgConfig(config['gpg_name'], config['gpg_mail']) if config['gpg_name'] and config['gpg_mail'] else None
+        spack_config = SpackConfig(env=env, repos=None, install_dir=config['install_dir'],
+                                   upstream_instance=config['upstream_instance'],
+                                   concretization_dir=config['concretization_dir'],
+                                   buildcache_dir=config['buildcache_dir'],
+                                   system_name=config['system_name'], gpg=gpg,
+                                   use_spack_global=config['use_spack_global'])
+        ctx.obj = SpackManager(spack_config, use_cache=config['use_cache'])
+
+
+@cli.command()
+@click.option('--use_cache', is_flag=True, default=False, help='Enables cashing')
+@click.option('--use_spack_global', is_flag=True, default=False, help='Uses spack installed globally on the os')
+@click.option('--env_name', type=str, default=None, help='Environment name')
+@click.option('--env_path', type=str, default=None, help='Environment path to download locally')
+@click.option('--env_git_path', type=str, default=None, help='Git path to download the environment')
+@click.option('--install_dir', type=str,
+              help='Install directory for installing spack; spack environments and repositories are stored here')
+@click.option('--upstream_instance', type=str, default=None, help='Upstream instance for spack environment')
+@click.option('--system_name', type=str, default=None, help='System name; it is used inside the spack environment')
+@click.option('--concretization_dir', type=str, default=None,
+              help='Directory where the concretization caching (spack.lock) will be downloaded')
+@click.option('--buildcache_dir', type=str, default=None,
+              help='Directory where the binary caching is downloaded for the spack packages')
+@click.option('--gpg_name', type=str, default=None, help='Gpg name')
+@click.option('--gpg_mail', type=str, default=None, help='Gpg mail contact address')
+@click.option('--cache_version_concretize', type=str, default='v1', help='Cache version for concretizaion data')
+@click.option('--cache_version_build', type=str, default='v1', help='Cache version for binary caches data')
+def set_config(use_cache, env_name, env_path, env_git_path, install_dir, upstream_instance, system_name,
+               concretization_dir,
+               buildcache_dir, gpg_name, gpg_mail, use_spack_global, cache_version_concretize, cache_version_build):
+    """Sets configuration parameters for the session."""
+    spack_config_data = {
+        'use_cache': use_cache,
+        'env_name': env_name,
+        'env_path': env_path,
+        'env_git_path': env_git_path,
+        'install_dir': install_dir,
+        'upstream_instance': upstream_instance,
+        'system_name': system_name,
+        'concretization_dir': Path(concretization_dir) if concretization_dir else None,
+        'buildcache_dir': Path(buildcache_dir) if buildcache_dir else None,
+        'gpg_name': gpg_name,
+        'gpg_mail': gpg_mail,
+        'use_spack_global': use_spack_global,
+        'repos': [],
+        'cache_version_concretize': cache_version_concretize,
+        'cache_version_build': cache_version_build,
+    }
+    save_config(spack_config_data, SESSION_CONFIG_PATH)
+    click.echo('Configuration saved.')
+
+
+@click.command()
+def show_config():
+    """Show the current configuration."""
+    config = load_config(SESSION_CONFIG_PATH)
+    if config:
+        click.echo(jsonpickle.encode(config, indent=2))
+    else:
+        click.echo('No configuration set. Use `set-config` first.')
+
+
+@cli.command()
+@click.option('--spack_version', type=str, default='0.23.0', help='Specifies the Spack version to be installed (default: v0.23.0).')
+@click.option('--bashrc_path', type=str, default="~/.bashrc", help='Defines the path to .bashrc.')
+@click.pass_context
+def install_spack(ctx: click.Context, spack_version: str, bashrc_path: str):
+    """Install spack in the install_dir folder"""
+    bashrc_path = os.path.expanduser(bashrc_path)
+    if ctx.obj is None:
+        SpackManager().install_spack(spack_version, bashrc_path)
+    else:
+        ctx.obj.install_spack(spack_version, bashrc_path)
+
+
+@cli.command()
+@click.option('--repo_name', type=str, required=True, default=None, help='Repository name')
+@click.option('--path', type=str, required=True, default=None, help='Repository path to download locally')
+@click.option('--git_path', type=str, required=True, default=None, help='Git path to download the repository')
+def add_spack_repo(repo_name: str, path: str, git_path: str = None):
+    """Adds a spack repository to the spack environments. The setup command must be rerun."""
+    path = resolve_path(path)
+    repo = SpackDescriptor(repo_name, path, git_path)
+    config = load_config(SESSION_CONFIG_PATH)
+    config['repos'].append(repo)
+    save_config(config, SESSION_CONFIG_PATH)
+    click.echo('dedal setup_spack_env must be reran after each repo is added for the environment.')
+
+
+@cli.command()
+@click.pass_context
+def setup_spack_env(ctx: click.Context):
+    """Setups a spack environment according to the given configuration."""
+    ctx.obj.setup_spack_env()
+
+
+@cli.command()
+@click.pass_context
+def concretize(ctx: click.Context):
+    """Spack concretization step."""
+    ctx.obj.concretize_spack_env()
+
+
+@cli.command()
+@click.option('--jobs', type=int, default=2, help='Number of parallel jobs for spack installation')
+@click.pass_context
+def install_packages(ctx: click.Context, jobs):
+    """Installs spack packages present in the spack environment defined in configuration."""
+    ctx.obj.install_packages(jobs=jobs)
+
+
+@click.command()
+def clear_config():
+    """Clears stored configuration."""
+    if os.path.exists(SESSION_CONFIG_PATH):
+        os.remove(SESSION_CONFIG_PATH)
+        click.echo('Configuration cleared!')
+    else:
+        click.echo('No configuration to clear.')
+
+
+cli.add_command(show_config)
+cli.add_command(clear_config)
+
+if __name__ == '__main__':
+    cli()
diff --git a/dedal/configuration/SpackConfig.py b/dedal/configuration/SpackConfig.py
index 0d4706796ec13d6bf733c3d68459e455a6da53b7..d76783ec507bd1d0d71f4cf14cc068e831f6a357 100644
--- a/dedal/configuration/SpackConfig.py
+++ b/dedal/configuration/SpackConfig.py
@@ -1,31 +1,30 @@
 import os
 from pathlib import Path
-
 from dedal.configuration.GpgConfig import GpgConfig
 from dedal.model import SpackDescriptor
+from dedal.utils.utils import resolve_path
 
 
 class SpackConfig:
     def __init__(self, env: SpackDescriptor = None, repos: list[SpackDescriptor] = None,
                  install_dir=Path(os.getcwd()).resolve(), upstream_instance=None, system_name=None,
-                 concretization_dir: Path = None, buildcache_dir: Path = None, gpg: GpgConfig = None):
+                 concretization_dir: Path = None, buildcache_dir: Path = None, gpg: GpgConfig = None,
+                 use_spack_global=False, cache_version_concretize='v1',
+                 cache_version_build='v1'):
         self.env = env
         if repos is None:
             self.repos = []
         else:
             self.repos = repos
-        self.install_dir = install_dir
-        if self.install_dir:
-            os.makedirs(self.install_dir, exist_ok=True)
         self.upstream_instance = upstream_instance
         self.system_name = system_name
-        self.concretization_dir = concretization_dir
-        if self.concretization_dir:
-            os.makedirs(self.concretization_dir, exist_ok=True)
-        self.buildcache_dir = buildcache_dir
-        if self.buildcache_dir:
-            os.makedirs(self.buildcache_dir, exist_ok=True)
+        self.concretization_dir = concretization_dir if concretization_dir is None else resolve_path(concretization_dir)
+        self.buildcache_dir = buildcache_dir if buildcache_dir is None else resolve_path(buildcache_dir)
+        self.install_dir = resolve_path(install_dir)
         self.gpg = gpg
+        self.use_spack_global = use_spack_global
+        self.cache_version_concretize = cache_version_concretize
+        self.cache_version_build = cache_version_build
 
     def add_repo(self, repo: SpackDescriptor):
         if self.repos is None:
diff --git a/dedal/docs/resources/dedal_UML.png b/dedal/docs/resources/dedal_UML.png
new file mode 100644
index 0000000000000000000000000000000000000000..430554abd5420474a5f2c3681871d491faf5976d
Binary files /dev/null and b/dedal/docs/resources/dedal_UML.png differ
diff --git a/dedal/error_handling/exceptions.py b/dedal/error_handling/exceptions.py
index 0256f886ab0cf4b958ac12d59d6fcea2d5f568ec..4653e72f54481e2d86d90afc3ab103744243b37d 100644
--- a/dedal/error_handling/exceptions.py
+++ b/dedal/error_handling/exceptions.py
@@ -39,3 +39,8 @@ class SpackGpgException(BashCommandException):
     """
     To be thrown when the spack fails to create gpg keys
     """
+
+class SpackRepoException(BashCommandException):
+    """
+    To be thrown when the spack fails to add a repo
+    """
\ No newline at end of file
diff --git a/dedal/logger/logger_config.py b/dedal/logger/logger_config.py
deleted file mode 100644
index 3ca3b000fd171dde8ceafdc6731dfb02009e845c..0000000000000000000000000000000000000000
--- a/dedal/logger/logger_config.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import logging
-
-
-class LoggerConfig:
-    """
-        This class sets up logging with a file handler
-        and a stream handler, ensuring consistent
-        and formatted log messages.
-    """
-    def __init__(self, log_file):
-        self.log_file = log_file
-        self._configure_logger()
-
-    def _configure_logger(self):
-        formatter = logging.Formatter(
-            fmt='%(asctime)s - %(levelname)s - %(message)s',
-            datefmt='%Y-%m-%d %H:%M:%S'
-        )
-
-        file_handler = logging.FileHandler(self.log_file)
-        file_handler.setFormatter(formatter)
-
-        stream_handler = logging.StreamHandler()
-        stream_handler.setFormatter(formatter)
-
-        self.logger = logging.getLogger(__name__)
-        self.logger.setLevel(logging.DEBUG)
-
-        self.logger.addHandler(file_handler)
-        self.logger.addHandler(stream_handler)
-
-    def get_logger(self):
-        return self.logger
diff --git a/dedal/model/SpackDescriptor.py b/dedal/model/SpackDescriptor.py
index 70e484fb3d39e4333389682d14a32ac46c08a912..939164a0653cf8724e28b7adc6702a4b28d6bb52 100644
--- a/dedal/model/SpackDescriptor.py
+++ b/dedal/model/SpackDescriptor.py
@@ -7,7 +7,7 @@ class SpackDescriptor:
     Provides details about the spack environment
     """
 
-    def __init__(self, env_name: str, path: Path = Path(os.getcwd()).resolve(), git_path: str = None):
-        self.env_name = env_name
-        self.path = path
+    def __init__(self, name: str, path: Path = Path(os.getcwd()).resolve(), git_path: str = None):
+        self.name = name
+        self.path = path.resolve() if isinstance(path, Path) else Path(path).resolve()
         self.git_path = git_path
diff --git a/dedal/spack_factory/SpackOperation.py b/dedal/spack_factory/SpackOperation.py
index d37a5e0872267023f52d1048d9779894a3180fee..7814cc8b6b698ecda6c3dbacddc36c331bd1526f 100644
--- a/dedal/spack_factory/SpackOperation.py
+++ b/dedal/spack_factory/SpackOperation.py
@@ -7,7 +7,7 @@ from dedal.commands.command_runner import CommandRunner
 from dedal.commands.preconfigured_command_enum import PreconfiguredCommandEnum
 from dedal.configuration.SpackConfig import SpackConfig
 from dedal.error_handling.exceptions import BashCommandException, NoSpackEnvironmentException, \
-    SpackInstallPackagesException, SpackConcertizeException, SpackMirrorException, SpackGpgException
+    SpackInstallPackagesException, SpackConcertizeException, SpackMirrorException, SpackGpgException, SpackRepoException
 from dedal.logger.logger_builder import get_logger
 from dedal.tests.testing_variables import SPACK_VERSION
 from dedal.utils.utils import run_command, git_clone_repo, log_command, set_bashrc_variable
@@ -30,29 +30,40 @@ class SpackOperation:
 
     def __init__(self, spack_config: SpackConfig = SpackConfig(), logger=get_logger(__name__)):
         self.spack_config = spack_config
-        self.spack_config.install_dir.mkdir(parents=True, exist_ok=True)
+        self.spack_config.install_dir = spack_config.install_dir
+        os.makedirs(self.spack_config.install_dir, exist_ok=True)
         self.spack_dir = self.spack_config.install_dir / 'spack'
-        self.spack_setup_script = self.spack_dir / 'share' / 'spack' / 'setup-env.sh'
+
+        self.spack_setup_script = "" if self.spack_config.use_spack_global else f"source {self.spack_dir / 'share' / 'spack' / 'setup-env.sh'} &&"
         self.logger = logger
+        self.spack_config.concretization_dir = spack_config.concretization_dir
+        if self.spack_config.concretization_dir:
+            os.makedirs(self.spack_config.concretization_dir, exist_ok=True)
+        self.spack_config.buildcache_dir = spack_config.buildcache_dir
+        if self.spack_config.buildcache_dir:
+            os.makedirs(self.spack_config.buildcache_dir, exist_ok=True)
+        if self.spack_config.env and spack_config.env.name:
+            self.env_path: Path = spack_config.env.path / spack_config.env.name
+            self.spack_command_on_env = f'{self.spack_setup_script} spack env activate -p {self.env_path}'
+        else:
+            self.spack_command_on_env = self.spack_setup_script
         if self.spack_config.env and spack_config.env.path:
-            self.spack_config.env.path = spack_config.env.path.resolve()
+            self.spack_config.env.path = spack_config.env.path
             self.spack_config.env.path.mkdir(parents=True, exist_ok=True)
-            self.env_path = spack_config.env.path / spack_config.env.env_name
-            self.spack_command_on_env = f'source {self.spack_setup_script} && spack env activate -p {self.env_path}'
         self.command_runner = CommandRunner()
 
     def create_fetch_spack_environment(self):
         if self.spack_config.env.git_path:
-            git_clone_repo(self.spack_config.env.env_name, self.spack_config.env.path / self.spack_config.env.env_name,
+            git_clone_repo(self.spack_config.env.name, self.spack_config.env.path / self.spack_config.env.name,
                            self.spack_config.env.git_path,
                            logger=self.logger)
         else:
-            os.makedirs(self.spack_config.env.path / self.spack_config.env.env_name, exist_ok=True)
+            os.makedirs(self.spack_config.env.path / self.spack_config.env.name, exist_ok=True)
             run_command("bash", "-c",
-                        f'source {self.spack_setup_script} && spack env create -d {self.env_path}',
+                        f'{self.spack_setup_script} spack env create -d {self.env_path}',
                         check=True, logger=self.logger,
-                        info_msg=f"Created {self.spack_config.env.env_name} spack environment",
-                        exception_msg=f"Failed to create {self.spack_config.env.env_name} spack environment",
+                        info_msg=f"Created {self.spack_config.env.name} spack environment",
+                        exception_msg=f"Failed to create {self.spack_config.env.name} spack environment",
                         exception=BashCommandException)
 
     def setup_spack_env(self):
@@ -75,19 +86,19 @@ class SpackOperation:
         self.create_fetch_spack_environment()
         if self.spack_config.install_dir.exists():
             for repo in self.spack_config.repos:
-                repo_dir = self.spack_config.install_dir / repo.path / repo.env_name
-                git_clone_repo(repo.env_name, repo_dir, repo.git_path, logger=self.logger)
-                if not self.spack_repo_exists(repo.env_name):
-                    self.add_spack_repo(repo.path, repo.env_name)
-                    self.logger.debug(f'Added spack repository {repo.env_name}')
+                repo_dir = self.spack_config.install_dir / repo.path / repo.name
+                git_clone_repo(repo.name, repo_dir, repo.git_path, logger=self.logger)
+                if not self.spack_repo_exists(repo.name):
+                    self.add_spack_repo(repo.path, repo.name)
+                    self.logger.debug(f'Added spack repository {repo.name}')
                 else:
-                    self.logger.debug(f'Spack repository {repo.env_name} already added')
+                    self.logger.debug(f'Spack repository {repo.name} already added')
 
     def spack_repo_exists(self, repo_name: str) -> bool | None:
         """Check if the given Spack repository exists."""
         if self.spack_config.env is None:
             result = run_command("bash", "-c",
-                                 f'source {self.spack_setup_script} && spack repo list',
+                                 f'{self.spack_setup_script} spack repo list',
                                  check=True,
                                  capture_output=True, text=True, logger=self.logger,
                                  info_msg=f'Checking if {repo_name} exists')
@@ -112,7 +123,7 @@ class SpackOperation:
                              self.spack_command_on_env,
                              check=True,
                              capture_output=True, text=True, logger=self.logger,
-                             info_msg=f'Checking if environment {self.spack_config.env.env_name} exists')
+                             info_msg=f'Checking if environment {self.spack_config.env.name} exists')
         return result is not None
 
     @check_spack_env
@@ -121,9 +132,9 @@ class SpackOperation:
         run_command("bash", "-c",
                     f'{self.spack_command_on_env} && spack repo add {repo_path}/{repo_name}',
                     check=True, logger=self.logger,
-                    info_msg=f"Added {repo_name} to spack environment {self.spack_config.env.env_name}",
-                    exception_msg=f"Failed to add {repo_name} to spack environment {self.spack_config.env.env_name}",
-                    exception=BashCommandException)
+                    info_msg=f"Added {repo_name} to spack environment {self.spack_config.env.name}",
+                    exception_msg=f"Failed to add {repo_name} to spack environment {self.spack_config.env.name}",
+                    exception=SpackRepoException)
 
     @check_spack_env
     def get_compiler_version(self):
@@ -131,22 +142,22 @@ class SpackOperation:
                              f'{self.spack_command_on_env} && spack compiler list',
                              check=True, logger=self.logger,
                              capture_output=True, text=True,
-                             info_msg=f"Checking spack environment compiler version for {self.spack_config.env.env_name}",
-                             exception_msg=f"Failed to checking spack environment compiler version for {self.spack_config.env.env_name}",
+                             info_msg=f"Checking spack environment compiler version for {self.spack_config.env.name}",
+                             exception_msg=f"Failed to checking spack environment compiler version for {self.spack_config.env.name}",
                              exception=BashCommandException)
         # todo add error handling and tests
         if result.stdout is None:
-            self.logger.debug('No gcc found for {self.env.env_name}')
+            self.logger.debug(f'No gcc found for {self.spack_config.env.name}')
             return None
 
         # Find the first occurrence of a GCC compiler using regex
         match = re.search(r"gcc@([\d\.]+)", result.stdout)
         gcc_version = match.group(1)
-        self.logger.debug(f'Found gcc for {self.spack_config.env.env_name}: {gcc_version}')
+        self.logger.debug(f'Found gcc for {self.spack_config.env.name}: {gcc_version}')
         return gcc_version
 
     def get_spack_installed_version(self):
-        spack_version = run_command("bash", "-c", f'source {self.spack_setup_script} && spack --version',
+        spack_version = run_command("bash", "-c", f'{self.spack_setup_script} spack --version',
                                     capture_output=True, text=True, check=True,
                                     logger=self.logger,
                                     info_msg=f"Getting spack version",
@@ -162,18 +173,18 @@ class SpackOperation:
                     f'{self.spack_command_on_env} && spack concretize {force}',
                     check=True,
                     logger=self.logger,
-                    info_msg=f'Concertization step for {self.spack_config.env.env_name}',
-                    exception_msg=f'Failed the concertization step for {self.spack_config.env.env_name}',
+                    info_msg=f'Concertization step for {self.spack_config.env.name}',
+                    exception_msg=f'Failed the concertization step for {self.spack_config.env.name}',
                     exception=SpackConcertizeException)
 
     def create_gpg_keys(self):
         if self.spack_config.gpg:
             run_command("bash", "-c",
-                        f'source {self.spack_setup_script} && spack gpg init && spack gpg create {self.spack_config.gpg.name} {self.spack_config.gpg.mail}',
+                        f'{self.spack_setup_script} spack gpg init && spack gpg create {self.spack_config.gpg.name} {self.spack_config.gpg.mail}',
                         check=True,
                         logger=self.logger,
-                        info_msg=f'Created pgp keys for {self.spack_config.env.env_name}',
-                        exception_msg=f'Failed to create pgp keys mirror {self.spack_config.env.env_name}',
+                        info_msg=f'Created pgp keys for {self.spack_config.env.name}',
+                        exception_msg=f'Failed to create pgp keys mirror {self.spack_config.env.name}',
                         exception=SpackGpgException)
         else:
             raise SpackGpgException('No GPG configuration was defined is spack configuration')
@@ -279,7 +290,7 @@ class SpackOperation:
 
     def remove_mirror(self, mirror_name: str):
         run_command("bash", "-c",
-                    f'source {self.spack_setup_script} && spack mirror rm {mirror_name}',
+                    f'{self.spack_setup_script} spack mirror rm {mirror_name}',
                     check=True,
                     logger=self.logger,
                     info_msg=f'Removing mirror {mirror_name}',
@@ -292,18 +303,19 @@ class SpackOperation:
         fresh = '--fresh' if fresh else ''
         debug = '--debug' if debug else ''
         install_result = run_command("bash", "-c",
-                                     f'{self.spack_command_on_env} && spack {debug} install -v {signed} --j {jobs} {fresh}',
+                                     f'{self.spack_command_on_env} && spack {debug} install -v {signed} -j {jobs} {fresh}',
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.PIPE,
                                      text=True,
                                      logger=self.logger,
-                                     info_msg=f"Installing spack packages for {self.spack_config.env.env_name}",
-                                     exception_msg=f"Error installing spack packages for {self.spack_config.env.env_name}",
+                                     info_msg=f"Installing spack packages for {self.spack_config.env.name}",
+                                     exception_msg=f"Error installing spack packages for {self.spack_config.env.name}",
                                      exception=SpackInstallPackagesException)
         log_command(install_result, str(Path(os.getcwd()).resolve() / ".generate_cache.log"))
         return install_result
 
-    def install_spack(self, spack_version=f'v{SPACK_VERSION}', spack_repo='https://github.com/spack/spack'):
+    def install_spack(self, spack_version=f'v{SPACK_VERSION}', spack_repo='https://github.com/spack/spack',
+                      bashrc_path=os.path.expanduser("~/.bashrc")):
         try:
             user = os.getlogin()
         except OSError:
@@ -321,23 +333,24 @@ class SpackOperation:
         else:
             self.logger.debug("Spack already cloned.")
 
-        bashrc_path = os.path.expanduser("~/.bashrc")
         # ensure the file exists before opening it
         if not os.path.exists(bashrc_path):
             open(bashrc_path, "w").close()
         # add spack setup commands to .bashrc
         with open(bashrc_path, "a") as bashrc:
             bashrc.write(f'export PATH="{self.spack_dir}/bin:$PATH"\n')
-            bashrc.write(f"source {self.spack_setup_script}\n")
+            spack_setup_script = f"source {self.spack_dir / 'share' / 'spack' / 'setup-env.sh'}"
+            bashrc.write(f"{spack_setup_script}\n")
         self.logger.info("Added Spack PATH to .bashrc")
         if user:
             run_command("chown", "-R", f"{user}:{user}", self.spack_dir, check=True, logger=self.logger,
                         info_msg='Adding permissions to the logged in user')
-        run_command("bash", "-c", f"source {bashrc_path}", check=True, logger=self.logger, info_msg='Restart bash')
         self.logger.info("Spack install completed")
-        # Restart Bash after the installation ends
-        os.system("exec bash")
-
+        if self.spack_config.use_spack_global is True:
+            # Restart the bash only of the spack is used globally
+            self.logger.info('Restarting bash')
+            run_command("bash", "-c", f"source {bashrc_path}", check=True, logger=self.logger, info_msg='Restart bash')
+            os.system("exec bash")
         # Configure upstream Spack instance if specified
         if self.spack_config.upstream_instance:
             upstreams_yaml_path = os.path.join(self.spack_dir, "etc/spack/defaults/upstreams.yaml")
diff --git a/dedal/spack_factory/SpackOperationCreateCache.py b/dedal/spack_factory/SpackOperationCreateCache.py
new file mode 100644
index 0000000000000000000000000000000000000000..41cfc8454136fd0096f6dcf78c0df62de004c6e6
--- /dev/null
+++ b/dedal/spack_factory/SpackOperationCreateCache.py
@@ -0,0 +1,51 @@
+import os
+
+from dedal.error_handling.exceptions import NoSpackEnvironmentException
+
+from dedal.utils.utils import copy_to_tmp, copy_file
+from dedal.wrapper.spack_wrapper import check_spack_env
+from dedal.build_cache.BuildCacheManager import BuildCacheManager
+from dedal.configuration.SpackConfig import SpackConfig
+from dedal.logger.logger_builder import get_logger
+from dedal.spack_factory.SpackOperation import SpackOperation
+
+
+class SpackOperationCreateCache(SpackOperation):
+    """
+    This class creates caching for the concretization step and for the installation step.
+    """
+
+    def __init__(self, spack_config: SpackConfig = SpackConfig()):
+        super().__init__(spack_config, logger=get_logger(__name__))
+        self.cache_dependency = BuildCacheManager(os.environ.get('CONCRETIZE_OCI_HOST'),
+                                                  os.environ.get('CONCRETIZE_OCI_PROJECT'),
+                                                  os.environ.get('CONCRETIZE_OCI_USERNAME'),
+                                                  os.environ.get('CONCRETIZE_OCI_PASSWORD'),
+                                                  cache_version=spack_config.cache_version_concretize)
+        self.build_cache = BuildCacheManager(os.environ.get('BUILDCACHE_OCI_HOST'),
+                                             os.environ.get('BUILDCACHE_OCI_PROJECT'),
+                                             os.environ.get('BUILDCACHE_OCI_USERNAME'),
+                                             os.environ.get('BUILDCACHE_OCI_PASSWORD'),
+                                             cache_version=spack_config.cache_version_build)
+
+    @check_spack_env
+    def concretize_spack_env(self):
+        super().concretize_spack_env(force=True)
+        dependency_path = self.spack_config.env.path / self.spack_config.env.name / 'spack.lock'
+        copy_file(dependency_path, self.spack_config.concretization_dir, logger=self.logger)
+        self.cache_dependency.upload(self.spack_config.concretization_dir)
+        self.logger.info(f'Created new spack concretization for create cache: {self.spack_config.env.name}')
+
+    @check_spack_env
+    def install_packages(self, jobs: int = 2, debug=False):
+        signed = False
+        if self.spack_config.gpg:
+            signed = True
+            self.create_gpg_keys()
+        self.add_mirror('local_cache', self.spack_config.buildcache_dir, signed=signed, autopush=signed,
+                        global_mirror=False)
+        self.logger.info(f'Added mirror for {self.spack_config.env.name}')
+        super().install_packages(jobs=jobs, signed=signed, debug=debug, fresh=True)
+        self.logger.info(f'Installed spack packages for {self.spack_config.env.name}')
+        self.build_cache.upload(self.spack_config.buildcache_dir)
+        self.logger.info(f'Pushed spack packages for {self.spack_config.env.name}')
diff --git a/dedal/spack_factory/SpackOperationCreator.py b/dedal/spack_factory/SpackOperationCreator.py
index 54517a845bad14629c6019416f0e19581472991e..fdc929d34866dcb254a775e9e8f8a24ee893029d 100644
--- a/dedal/spack_factory/SpackOperationCreator.py
+++ b/dedal/spack_factory/SpackOperationCreator.py
@@ -1,14 +1,19 @@
 from dedal.configuration.SpackConfig import SpackConfig
 from dedal.spack_factory.SpackOperation import SpackOperation
+from dedal.spack_factory.SpackOperationCreateCache import SpackOperationCreateCache
 from dedal.spack_factory.SpackOperationUseCache import SpackOperationUseCache
 
 
 class SpackOperationCreator:
     @staticmethod
-    def get_spack_operator(spack_config: SpackConfig = None):
+    def get_spack_operator(spack_config: SpackConfig = None, use_cache: bool = False) -> SpackOperation:
         if spack_config is None:
-            return SpackOperation(SpackConfig())
+            return SpackOperation()
         elif spack_config.concretization_dir is None and spack_config.buildcache_dir is None:
             return SpackOperation(spack_config)
-        else:
+        elif (spack_config.concretization_dir and spack_config.buildcache_dir) and not use_cache:
+            return SpackOperationCreateCache(spack_config)
+        elif (spack_config.concretization_dir and spack_config.buildcache_dir) and use_cache:
             return SpackOperationUseCache(spack_config)
+        else:
+            return SpackOperation(SpackConfig())
diff --git a/dedal/spack_factory/SpackOperationUseCache.py b/dedal/spack_factory/SpackOperationUseCache.py
index ce247ccd93fea0a626cb69a231fec8ee0ae722b5..b2ccc12da3fb12a2d7f0731c3533bfb71c308512 100644
--- a/dedal/spack_factory/SpackOperationUseCache.py
+++ b/dedal/spack_factory/SpackOperationUseCache.py
@@ -1,10 +1,17 @@
 import os
 
+import subprocess
+from pathlib import Path
+
 from dedal.build_cache.BuildCacheManager import BuildCacheManager
 from dedal.configuration.SpackConfig import SpackConfig
 from dedal.error_handling.exceptions import NoSpackEnvironmentException
+from dedal.error_handling.exceptions import SpackInstallPackagesException
 from dedal.logger.logger_builder import get_logger
 from dedal.spack_factory.SpackOperation import SpackOperation
+from dedal.configuration.SpackConfig import SpackConfig
+from dedal.utils.utils import file_exists_and_not_empty, run_command, log_command, copy_to_tmp, copy_file
+from dedal.wrapper.spack_wrapper import check_spack_env
 
 
 class SpackOperationUseCache(SpackOperation):
@@ -12,19 +19,18 @@ class SpackOperationUseCache(SpackOperation):
     This class uses caching for the concretization step and for the installation step.
     """
 
-    def __init__(self, spack_config: SpackConfig = SpackConfig(), cache_version_concretize='v1',
-                 cache_version_build='v1'):
+    def __init__(self, spack_config: SpackConfig = SpackConfig()):
         super().__init__(spack_config, logger=get_logger(__name__))
         self.cache_dependency = BuildCacheManager(os.environ.get('CONCRETIZE_OCI_HOST'),
                                                   os.environ.get('CONCRETIZE_OCI_PROJECT'),
                                                   os.environ.get('CONCRETIZE_OCI_USERNAME'),
                                                   os.environ.get('CONCRETIZE_OCI_PASSWORD'),
-                                                  cache_version=cache_version_concretize)
+                                                  cache_version=spack_config.cache_version_concretize)
         self.build_cache = BuildCacheManager(os.environ.get('BUILDCACHE_OCI_HOST'),
                                              os.environ.get('BUILDCACHE_OCI_PROJECT'),
                                              os.environ.get('BUILDCACHE_OCI_USERNAME'),
                                              os.environ.get('BUILDCACHE_OCI_PASSWORD'),
-                                             cache_version=cache_version_build)
+                                             cache_version=spack_config.cache_version_build)
 
     def setup_spack_env(self) -> None:
         """Set up the spack environment for using the cache.
@@ -54,5 +60,35 @@ class SpackOperationUseCache(SpackOperation):
             self.logger.error("Error adding buildcache mirror: %s", e)
             raise
 
-    def concretize_spack_env(self, force=True):
-        pass
+    @check_spack_env
+    def concretize_spack_env(self):
+        concretization_redo = False
+        self.cache_dependency.download(self.spack_config.concretization_dir)
+        if file_exists_and_not_empty(self.spack_config.concretization_dir / 'spack.lock'):
+            concretization_file_path = self.env_path / 'spack.lock'
+            copy_file(self.spack_config.concretization_dir / 'spack.lock', self.env_path)
+            # redo the concretization step if spack.lock file was not downloaded from the cache
+            if not file_exists_and_not_empty(concretization_file_path):
+                super().concretize_spack_env(force=True)
+                concretization_redo = True
+        else:
+            # redo the concretization step if spack.lock file was not downloaded from the cache
+            super().concretize_spack_env(force=True)
+            concretization_redo = True
+        return concretization_redo
+
+    @check_spack_env
+    def install_packages(self, jobs: int, signed=True, debug=False):
+        signed = '' if signed else '--no-check-signature'
+        debug = '--debug' if debug else ''
+        install_result = run_command("bash", "-c",
+                                     f'{self.spack_command_on_env} && spack {debug} install -v --reuse {signed} -j {jobs}',
+                                     stdout=subprocess.PIPE,
+                                     stderr=subprocess.PIPE,
+                                     text=True,
+                                     logger=self.logger,
+                                     info_msg=f"Installing spack packages for {self.spack_config.env.name}",
+                                     exception_msg=f"Error installing spack packages for {self.spack_config.env.name}",
+                                     exception=SpackInstallPackagesException)
+        log_command(install_result, str(Path(os.getcwd()).resolve() / ".generate_cache.log"))
+        return install_result
diff --git a/dedal/tests/integration_tests/spack_create_cache_test.py b/dedal/tests/integration_tests/spack_create_cache_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..fcef47a89af9f0ae7bdb53828c5e6682dd3f094e
--- /dev/null
+++ b/dedal/tests/integration_tests/spack_create_cache_test.py
@@ -0,0 +1,58 @@
+from pathlib import Path
+
+import pytest
+
+from dedal.configuration.GpgConfig import GpgConfig
+from dedal.configuration.SpackConfig import SpackConfig
+
+from dedal.model.SpackDescriptor import SpackDescriptor
+from dedal.spack_factory.SpackOperationCreateCache import SpackOperationCreateCache
+from dedal.spack_factory.SpackOperationCreator import SpackOperationCreator
+from dedal.tests.testing_variables import test_spack_env_git, ebrains_spack_builds_git
+
+"""
+Before running those tests, the repositories where the caching is stored must be cleared after each run. 
+Ebrains Harbour does not support deletion via API, so the clean up must be done manually
+"""
+
+
+@pytest.mark.skip(
+    reason="Skipping until an OCI registry which supports via API deletion; Clean up for OCI registry repo must be added before this test.")
+def test_spack_create_cache_concretization(tmp_path):
+    install_dir = tmp_path
+    concretization_dir = install_dir / 'concretization'
+    buildcache_dir = install_dir / 'buildcache'
+    env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
+    repo = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
+    gpg = GpgConfig(gpg_name='test-gpg', gpg_mail='test@test.com')
+    config = SpackConfig(env=env, install_dir=install_dir, concretization_dir=concretization_dir,
+                         buildcache_dir=buildcache_dir, gpg=gpg)
+    config.add_repo(repo)
+    spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperationCreateCache)
+    spack_operation.install_spack()
+    spack_operation.setup_spack_env()
+    spack_operation.concretize_spack_env()
+    assert len(spack_operation.cache_dependency.list_tags()) > 0
+
+
+@pytest.mark.skip(
+    reason="Skipping until an OCI registry which supports via API deletion; Clean up for OCI registry repo must be added before this test.")
+def test_spack_create_cache_installation(tmp_path):
+    install_dir = tmp_path
+    concretization_dir = install_dir / 'concretization'
+    buildcache_dir = install_dir / 'buildcache'
+    env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
+    repo = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
+    gpg = GpgConfig(gpg_name='test-gpg', gpg_mail='test@test.com')
+    config = SpackConfig(env=env, install_dir=install_dir, concretization_dir=concretization_dir,
+                         buildcache_dir=buildcache_dir, gpg=gpg)
+    config.add_repo(repo)
+    spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperationCreateCache)
+    spack_operation.install_spack()
+    spack_operation.setup_spack_env()
+    spack_operation.concretize_spack_env()
+    assert len(spack_operation.cache_dependency.list_tags()) > 0
+    spack_operation.install_packages()
+    assert len(spack_operation.build_cache.list_tags()) > 0
diff --git a/dedal/tests/integration_tests/spack_from_cache_test.py b/dedal/tests/integration_tests/spack_from_cache_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..33f4483314b04e894c0bca3c75f259d58031d4f4
--- /dev/null
+++ b/dedal/tests/integration_tests/spack_from_cache_test.py
@@ -0,0 +1,42 @@
+import pytest
+from dedal.configuration.SpackConfig import SpackConfig
+from dedal.model.SpackDescriptor import SpackDescriptor
+from dedal.spack_factory.SpackOperationCreator import SpackOperationCreator
+from dedal.spack_factory.SpackOperationUseCache import SpackOperationUseCache
+from dedal.utils.utils import file_exists_and_not_empty
+from dedal.utils.variables import test_spack_env_git, ebrains_spack_builds_git
+
+
+def test_spack_from_cache_concretize(tmp_path):
+    install_dir = tmp_path
+    env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
+    repo = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
+    spack_config = SpackConfig(env, install_dir=install_dir, concretization_dir=install_dir / 'concretize',
+                               buildcache_dir=install_dir / 'buildcache')
+    spack_config.add_repo(repo)
+    spack_operation = SpackOperationCreator.get_spack_operator(spack_config, use_cache=True)
+    assert isinstance(spack_operation, SpackOperationUseCache)
+    spack_operation.install_spack()
+    spack_operation.setup_spack_env()
+    assert spack_operation.concretize_spack_env() == False
+    concretization_file_path = spack_operation.env_path / 'spack.lock'
+    assert file_exists_and_not_empty(concretization_file_path) == True
+
+
+@pytest.mark.skip(reason="Skipping test::test_spack_from_cache_install until all the functionalities in SpackOperationUseCache")
+def test_spack_from_cache_install(tmp_path):
+    install_dir = tmp_path
+    env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
+    repo = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
+    spack_config = SpackConfig(env, install_dir=install_dir, concretization_dir=install_dir / 'concretize',
+                               buildcache_dir=install_dir / 'buildcache')
+    spack_config.add_repo(repo)
+    spack_operation = SpackOperationCreator.get_spack_operator(spack_config, use_cache=True)
+    assert isinstance(spack_operation, SpackOperationUseCache)
+    spack_operation.install_spack()
+    spack_operation.setup_spack_env()
+    assert spack_operation.concretize_spack_env() == False
+    concretization_file_path = spack_operation.env_path / 'spack.lock'
+    assert file_exists_and_not_empty(concretization_file_path) == True
+    install_result = spack_operation.install_packages(jobs=2, signed=True, debug=False)
+    assert install_result.returncode == 0
diff --git a/dedal/tests/integration_tests/spack_from_scratch_test.py b/dedal/tests/integration_tests/spack_from_scratch_test.py
index 2fec80f743d72190d0b175191660250981f98255..7e8d900caffbdc7d3031778033c79a85b0a388cd 100644
--- a/dedal/tests/integration_tests/spack_from_scratch_test.py
+++ b/dedal/tests/integration_tests/spack_from_scratch_test.py
@@ -6,33 +6,28 @@ from dedal.spack_factory.SpackOperationCreator import SpackOperationCreator
 from dedal.model.SpackDescriptor import SpackDescriptor
 from dedal.tests.testing_variables import test_spack_env_git, ebrains_spack_builds_git
 from dedal.utils.utils import file_exists_and_not_empty
+from dedal.spack_factory.SpackOperation import SpackOperation
 
 
-def test_spack_repo_exists_1():
-    spack_operation = SpackOperationCreator.get_spack_operator()
-    spack_operation.install_spack()
-    assert spack_operation.spack_repo_exists('ebrains-spack-builds') == False
-
-
-def test_spack_repo_exists_2(tmp_path):
+def test_spack_repo_exists_1(tmp_path):
     install_dir = tmp_path
     env = SpackDescriptor('ebrains-spack-builds', install_dir)
     config = SpackConfig(env=env, install_dir=install_dir)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
     spack_operation.install_spack()
     with pytest.raises(NoSpackEnvironmentException):
-        spack_operation.spack_repo_exists(env.env_name)
+        spack_operation.spack_repo_exists(env.name)
 
 
-def test_spack_repo_exists_3(tmp_path):
+def test_spack_repo_exists_2(tmp_path):
     install_dir = tmp_path
     env = SpackDescriptor('ebrains-spack-builds', install_dir)
     config = SpackConfig(env=env, install_dir=install_dir)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
-    print(spack_operation.get_spack_installed_version())
     spack_operation.setup_spack_env()
-    assert spack_operation.spack_repo_exists(env.env_name) == False
+    assert spack_operation.spack_repo_exists(env.name) == False
 
 
 def test_spack_from_scratch_setup_1(tmp_path):
@@ -40,9 +35,10 @@ def test_spack_from_scratch_setup_1(tmp_path):
     env = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
     config = SpackConfig(env=env, system_name='ebrainslab', install_dir=install_dir)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
-    assert spack_operation.spack_repo_exists(env.env_name) == False
+    assert spack_operation.spack_repo_exists(env.name) == False
 
 
 def test_spack_from_scratch_setup_2(tmp_path):
@@ -53,9 +49,10 @@ def test_spack_from_scratch_setup_2(tmp_path):
     config.add_repo(repo)
     config.add_repo(repo)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
-    assert spack_operation.spack_repo_exists(env.env_name) == True
+    assert spack_operation.spack_repo_exists(env.name) == True
 
 
 def test_spack_from_scratch_setup_3(tmp_path):
@@ -66,6 +63,7 @@ def test_spack_from_scratch_setup_3(tmp_path):
     config.add_repo(repo)
     config.add_repo(repo)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     with pytest.raises(BashCommandException):
         spack_operation.setup_spack_env()
@@ -76,6 +74,7 @@ def test_spack_from_scratch_setup_4(tmp_path):
     env = SpackDescriptor('new_env2', install_dir)
     config = SpackConfig(env=env, install_dir=install_dir)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
     assert spack_operation.spack_env_exists() == True
@@ -87,10 +86,13 @@ def test_spack_not_a_valid_repo():
     config = SpackConfig(env=env, system_name='ebrainslab')
     config.add_repo(repo)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     with pytest.raises(BashCommandException):
-        spack_operation.add_spack_repo(repo.path, repo.env_name)
+        spack_operation.add_spack_repo(repo.path, repo.name)
 
 
+@pytest.mark.skip(
+    reason="Skipping the concretization step because it may freeze when numerous Spack packages are added to the environment.")
 def test_spack_from_scratch_concretize_1(tmp_path):
     install_dir = tmp_path
     env = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
@@ -99,6 +101,7 @@ def test_spack_from_scratch_concretize_1(tmp_path):
     config.add_repo(repo)
     config.add_repo(repo)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
@@ -107,6 +110,8 @@ def test_spack_from_scratch_concretize_1(tmp_path):
     assert file_exists_and_not_empty(concretization_file_path) == True
 
 
+@pytest.mark.skip(
+    reason="Skipping the concretization step because it may freeze when numerous Spack packages are added to the environment.")
 def test_spack_from_scratch_concretize_2(tmp_path):
     install_dir = tmp_path
     env = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
@@ -115,6 +120,7 @@ def test_spack_from_scratch_concretize_2(tmp_path):
     config.add_repo(repo)
     config.add_repo(repo)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
     spack_operation.concretize_spack_env(force=False)
@@ -130,6 +136,7 @@ def test_spack_from_scratch_concretize_3(tmp_path):
     config.add_repo(repo)
     config.add_repo(repo)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
     concretization_file_path = spack_operation.env_path / 'spack.lock'
@@ -141,6 +148,7 @@ def test_spack_from_scratch_concretize_4(tmp_path):
     env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
     config = SpackConfig(env=env, install_dir=install_dir)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
     spack_operation.concretize_spack_env(force=False)
@@ -153,6 +161,7 @@ def test_spack_from_scratch_concretize_5(tmp_path):
     env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
     config = SpackConfig(env=env, install_dir=install_dir)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
     spack_operation.concretize_spack_env(force=True)
@@ -167,6 +176,7 @@ def test_spack_from_scratch_concretize_6(tmp_path):
     config = SpackConfig(env=env, install_dir=install_dir)
     config.add_repo(repo)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
     spack_operation.concretize_spack_env(force=False)
@@ -181,6 +191,7 @@ def test_spack_from_scratch_concretize_7(tmp_path):
     config = SpackConfig(env=env)
     config.add_repo(repo)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
     spack_operation.concretize_spack_env(force=True)
@@ -195,6 +206,7 @@ def test_spack_from_scratch_install(tmp_path):
     config = SpackConfig(env=env)
     config.add_repo(repo)
     spack_operation = SpackOperationCreator.get_spack_operator(config)
+    assert isinstance(spack_operation, SpackOperation)
     spack_operation.install_spack()
     spack_operation.setup_spack_env()
     spack_operation.concretize_spack_env(force=True)
diff --git a/dedal/tests/integration_tests/spack_install_test.py b/dedal/tests/integration_tests/spack_install_test.py
index 564d5c6aa2138e815cd7d092215a4f2eee8816f6..0c6cf1273cf57b5ccb82e4c4cd79fc913aebeaaf 100644
--- a/dedal/tests/integration_tests/spack_install_test.py
+++ b/dedal/tests/integration_tests/spack_install_test.py
@@ -1,12 +1,12 @@
-import pytest
+from dedal.configuration.SpackConfig import SpackConfig
 from dedal.spack_factory.SpackOperation import SpackOperation
 from dedal.tests.testing_variables import SPACK_VERSION
 
 
-# run this test first so that spack is installed only once for all the tests
-@pytest.mark.run(order=1)
-def test_spack_install_scratch():
-    spack_operation = SpackOperation()
+def test_spack_install_scratch(tmp_path):
+    install_dir = tmp_path
+    spack_config = SpackConfig(install_dir=install_dir)
+    spack_operation = SpackOperation(spack_config)
     spack_operation.install_spack(spack_version=f'v{SPACK_VERSION}')
     installed_spack_version = spack_operation.get_spack_installed_version()
     assert SPACK_VERSION == installed_spack_version
diff --git a/dedal/tests/integration_tests/spack_operation_creator_test.py b/dedal/tests/integration_tests/spack_operation_creator_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..226184b00a5c7136c97f5ef12761ac44c71286a1
--- /dev/null
+++ b/dedal/tests/integration_tests/spack_operation_creator_test.py
@@ -0,0 +1,50 @@
+from dedal.spack_factory.SpackOperationCreateCache import SpackOperationCreateCache
+
+from dedal.configuration.SpackConfig import SpackConfig
+from dedal.model.SpackDescriptor import SpackDescriptor
+from dedal.spack_factory.SpackOperation import SpackOperation
+from dedal.spack_factory.SpackOperationCreator import SpackOperationCreator
+from dedal.spack_factory.SpackOperationUseCache import SpackOperationUseCache
+from dedal.tests.testing_variables import ebrains_spack_builds_git, test_spack_env_git
+
+
+def test_spack_creator_scratch_1(tmp_path):
+    install_dir = tmp_path
+    env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
+    repo = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
+    spack_config = SpackConfig(env, install_dir=install_dir)
+    spack_config.add_repo(repo)
+    spack_operation = SpackOperationCreator.get_spack_operator(spack_config)
+    assert isinstance(spack_operation, SpackOperation)
+
+
+def test_spack_creator_scratch_2(tmp_path):
+    spack_config = None
+    spack_operation = SpackOperationCreator.get_spack_operator(spack_config)
+    assert isinstance(spack_operation, SpackOperation)
+
+
+def test_spack_creator_scratch_3():
+    spack_config = SpackConfig()
+    spack_operation = SpackOperationCreator.get_spack_operator(spack_config)
+    assert isinstance(spack_operation, SpackOperation)
+
+
+def test_spack_creator_create_cache(tmp_path):
+    install_dir = tmp_path
+    env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
+    repo = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
+    spack_config = SpackConfig(env, install_dir=install_dir, concretization_dir=install_dir, buildcache_dir=install_dir)
+    spack_config.add_repo(repo)
+    spack_operation = SpackOperationCreator.get_spack_operator(spack_config)
+    assert isinstance(spack_operation, SpackOperationCreateCache)
+
+
+def test_spack_creator_use_cache(tmp_path):
+    install_dir = tmp_path
+    env = SpackDescriptor('test-spack-env', install_dir, test_spack_env_git)
+    repo = SpackDescriptor('ebrains-spack-builds', install_dir, ebrains_spack_builds_git)
+    spack_config = SpackConfig(env, install_dir=install_dir, concretization_dir=install_dir, buildcache_dir=install_dir)
+    spack_config.add_repo(repo)
+    spack_operation = SpackOperationCreator.get_spack_operator(spack_config, use_cache=True)
+    assert isinstance(spack_operation, SpackOperationUseCache)
diff --git a/dedal/tests/unit_tests/spack_manager_api_test.py b/dedal/tests/unit_tests/spack_manager_api_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d32a56d11dbe8b0d617f750fa599440ee838980
--- /dev/null
+++ b/dedal/tests/unit_tests/spack_manager_api_test.py
@@ -0,0 +1,183 @@
+import os
+
+import pytest
+from unittest.mock import patch, MagicMock
+from click.testing import CliRunner
+from dedal.cli.spack_manager_api import show_config, clear_config, install_spack, add_spack_repo, install_packages, \
+    setup_spack_env, concretize, set_config
+from dedal.model.SpackDescriptor import SpackDescriptor
+
+
+@pytest.fixture
+def runner():
+    return CliRunner()
+
+
+@pytest.fixture
+def mocked_session_path():
+    return '/mocked/tmp/session.json'
+
+
+@pytest.fixture
+def mock_spack_manager():
+    mock_spack_manager = MagicMock()
+    mock_spack_manager.install_spack = MagicMock()
+    mock_spack_manager.add_spack_repo = MagicMock()
+    mock_spack_manager.setup_spack_env = MagicMock()
+    mock_spack_manager.concretize_spack_env = MagicMock()
+    mock_spack_manager.install_packages = MagicMock()
+    return mock_spack_manager
+
+
+@pytest.fixture
+def mock_load_config():
+    with patch('dedal.cli.spack_manager_api.load_config') as mock_load:
+        yield mock_load
+
+
+@pytest.fixture
+def mock_save_config():
+    with patch('dedal.cli.spack_manager_api.save_config') as mock_save:
+        yield mock_save
+
+
+@pytest.fixture
+def mock_clear_config():
+    with patch('dedal.cli.spack_manager_api.clear_config') as mock_clear:
+        yield mock_clear
+
+
+def test_show_config_no_config(runner, mock_load_config):
+    mock_load_config.return_value = None
+    result = runner.invoke(show_config)
+    assert 'No configuration set. Use `set-config` first.' in result.output
+
+
+def test_show_config_with_config(runner, mock_load_config):
+    """Test the show_config command when config is present."""
+    mock_load_config.return_value = {"key": "value"}
+    result = runner.invoke(show_config)
+    assert result.exit_code == 0
+    assert '"key": "value"' in result.output
+
+
+def test_clear_config(runner, mock_clear_config):
+    """Test the clear_config command."""
+    with patch('os.path.exists', return_value=True), patch('os.remove') as mock_remove:
+        result = runner.invoke(clear_config)
+        assert 'Configuration cleared!' in result.output
+        mock_remove.assert_called_once()
+
+
+def test_install_spack_no_context_1(runner, mock_spack_manager):
+    """Test install_spack with no context, using SpackManager."""
+    with patch('dedal.cli.spack_manager_api.SpackManager', return_value=mock_spack_manager):
+        result = runner.invoke(install_spack, ['--spack_version', '0.24.0'])
+    mock_spack_manager.install_spack.assert_called_once_with('0.24.0', os.path.expanduser("~/.bashrc"))
+    assert result.exit_code == 0
+
+
+def test_install_spack_no_context_2(runner, mock_spack_manager):
+    """Test install_spack with no context, using SpackManager and the default value for spack_version."""
+    with patch('dedal.cli.spack_manager_api.SpackManager', return_value=mock_spack_manager):
+        result = runner.invoke(install_spack)
+    mock_spack_manager.install_spack.assert_called_once_with('0.23.0', os.path.expanduser("~/.bashrc"))
+    assert result.exit_code == 0
+
+
+def test_install_spack_with_mocked_context_1(runner, mock_spack_manager):
+    """Test install_spack with a mocked context, using ctx.obj as SpackManager."""
+    result = runner.invoke(install_spack, ['--spack_version', '0.24.0', '--bashrc_path', '/home/.bahsrc'], obj=mock_spack_manager)
+    mock_spack_manager.install_spack.assert_called_once_with('0.24.0', '/home/.bahsrc')
+    assert result.exit_code == 0
+
+
+def test_install_spack_with_mocked_context_2(runner, mock_spack_manager):
+    """Test install_spack with a mocked context, using ctx.obj as SpackManager and the default value for spack_version."""
+    result = runner.invoke(install_spack, obj=mock_spack_manager)
+    mock_spack_manager.install_spack.assert_called_once_with('0.23.0', os.path.expanduser("~/.bashrc"))
+    assert result.exit_code == 0
+
+
+def test_setup_spack_env(runner, mock_spack_manager):
+    """Test setup_spack_env with a mocked context, using ctx.obj as SpackManager."""
+    result = runner.invoke(setup_spack_env, obj=mock_spack_manager)
+    mock_spack_manager.setup_spack_env.assert_called_once_with()
+    assert result.exit_code == 0
+
+
+def test_concretize(runner, mock_spack_manager):
+    """Test install_spack with a mocked context, using ctx.obj as SpackManager."""
+    result = runner.invoke(concretize, obj=mock_spack_manager)
+    mock_spack_manager.concretize_spack_env.assert_called_once_with()
+    assert result.exit_code == 0
+
+
+def test_install_packages_1(runner, mock_spack_manager):
+    """Test install_packages with a mocked context, using ctx.obj as SpackManager."""
+    result = runner.invoke(install_packages, obj=mock_spack_manager)
+    mock_spack_manager.install_packages.assert_called_once_with(jobs=2)
+    assert result.exit_code == 0
+
+
+def test_install_packages(runner, mock_spack_manager):
+    """Test install_packages with a mocked context, using ctx.obj as SpackManager."""
+    result = runner.invoke(install_packages, ['--jobs', 3], obj=mock_spack_manager)
+    mock_spack_manager.install_packages.assert_called_once_with(jobs=3)
+    assert result.exit_code == 0
+
+
+@patch('dedal.cli.spack_manager_api.resolve_path')
+@patch('dedal.cli.spack_manager_api.SpackDescriptor')
+def test_add_spack_repo(mock_spack_descriptor, mock_resolve_path, mock_load_config, mock_save_config,
+                        mocked_session_path, runner):
+    """Test adding a spack repository with mocks."""
+    expected_config = {'repos': [SpackDescriptor(name='test-repo')]}
+    repo_name = 'test-repo'
+    path = '/path'
+    git_path = 'https://example.com/repo.git'
+    mock_resolve_path.return_value = '/resolved/path'
+    mock_load_config.return_value = expected_config
+    mock_repo_instance = MagicMock()
+    mock_spack_descriptor.return_value = mock_repo_instance
+
+    with patch('dedal.cli.spack_manager_api.SESSION_CONFIG_PATH', mocked_session_path):
+        result = runner.invoke(add_spack_repo, ['--repo_name', repo_name, '--path', path, '--git_path', git_path])
+
+    assert result.exit_code == 0
+    assert 'dedal setup_spack_env must be reran after each repo is added' in result.output
+    mock_resolve_path.assert_called_once_with(path)
+    mock_spack_descriptor.assert_called_once_with(repo_name, '/resolved/path', git_path)
+    assert mock_repo_instance in expected_config['repos']
+    mock_save_config.assert_called_once_with(expected_config, mocked_session_path)
+
+
+def test_set_config(runner, mock_save_config, mocked_session_path):
+    """Test set_config."""
+    with patch('dedal.cli.spack_manager_api.SESSION_CONFIG_PATH', mocked_session_path):
+        result = runner.invoke(set_config, ['--env_name', 'test', '--system_name', 'sys'])
+
+    expected_config = {
+        'use_cache': False,
+        'env_name': 'test',
+        'env_path': None,
+        'env_git_path': None,
+        'install_dir': None,
+        'upstream_instance': None,
+        'system_name': 'sys',
+        'concretization_dir': None,
+        'buildcache_dir': None,
+        'gpg_name': None,
+        'gpg_mail': None,
+        'use_spack_global': False,
+        'repos': [],
+        'cache_version_concretize': 'v1',
+        'cache_version_build': 'v1',
+    }
+
+    mock_save_config.assert_called_once()
+    saved_config, saved_path = mock_save_config.call_args[0]
+    assert saved_path == mocked_session_path
+    assert saved_config == expected_config
+    assert result.exit_code == 0
+    assert 'Configuration saved.' in result.output
diff --git a/dedal/utils/bootstrap.sh b/dedal/utils/bootstrap.sh
index 58be94b97dd188f07ce0da8abfc4e2c57ec1210d..9cd2e1e11b4ab82fd301cc86179de7ba373f5d03 100644
--- a/dedal/utils/bootstrap.sh
+++ b/dedal/utils/bootstrap.sh
@@ -1,6 +1,11 @@
-# Minimal prerequisites for installing the dedal_library
+# Minimal prerequisites for installing the esd_library
 # pip must be installed on the OS
 echo "Bootstrapping..."
+set -euo pipefail
+shopt -s inherit_errexit 2>/dev/null
+export DEBIAN_FRONTEND=noninteractive
 apt update
-apt install -y bzip2 ca-certificates g++ gcc gfortran git gzip lsb-release patch python3 python3-pip tar unzip xz-utils zstd
+apt install -o DPkg::Options::=--force-confold -y -q --reinstall \
+                  bzip2 ca-certificates g++ gcc make gfortran git gzip lsb-release \
+                  patch python3 python3-pip tar unzip xz-utils zstd gnupg2 vim curl rsync
 python3 -m pip install --upgrade pip setuptools wheel
diff --git a/dedal/utils/utils.py b/dedal/utils/utils.py
index 9fc82ad520b4a22715c819d3ca11042b88b87c7f..2f7c48479fb3c0825b06f80e3cc2c70046e4affc 100644
--- a/dedal/utils/utils.py
+++ b/dedal/utils/utils.py
@@ -2,6 +2,7 @@ import logging
 import os
 import shutil
 import subprocess
+import tempfile
 from pathlib import Path
 
 from dedal.error_handling.exceptions import BashCommandException
@@ -78,6 +79,21 @@ def log_command(results, log_file: str):
         log_file.write(results.stderr)
 
 
+def copy_to_tmp(file_path: Path) -> Path:
+    """
+    Creates a temporary directory and copies the given file into it.
+
+    :param file_path: Path to the file that needs to be copied.
+    :return: Path to the copied file inside the temporary directory.
+    """
+    if not file_path.is_file():
+        raise FileNotFoundError(f"File not found: {file_path}")
+    tmp_dir = Path(tempfile.mkdtemp())
+    tmp_file_path = tmp_dir / file_path.name
+    shutil.copy(file_path, tmp_file_path)
+    return tmp_file_path
+
+
 def set_bashrc_variable(var_name: str, value: str, bashrc_path: str = os.path.expanduser("~/.bashrc"),
                         logger: logging = logging.getLogger(__name__)):
     """Update or add an environment variable in ~/.bashrc."""
@@ -98,3 +114,43 @@ def set_bashrc_variable(var_name: str, value: str, bashrc_path: str = os.path.ex
         logger.info(f"Updated {bashrc_path} with: export {var_name}={value}")
     with open(bashrc_path, "w") as file:
         file.writelines(lines)
+
+
+def copy_file(src: Path, dst: Path, logger: logging = logging.getLogger(__name__)) -> None:
+    """
+    Copy a file from src to dest.
+    """
+    if not os.path.exists(src):
+        raise FileNotFoundError(f"Source file '{src}' does not exist.")
+    src.resolve().as_posix()
+    dst.resolve().as_posix()
+    os.makedirs(os.path.dirname(dst), exist_ok=True)
+    shutil.copy2(src, dst)
+    logger.debug(f"File copied from '{src}' to '{dst}'")
+
+
+def delete_file(file_path: str, logger: logging = logging.getLogger(__name__)) -> bool:
+    """
+    Deletes a file at the given path. Returns True if successful, False if the file doesn't exist.
+    """
+    try:
+        os.remove(file_path)
+        logger.debug(f"File '{file_path}' deleted.")
+        return True
+    except FileNotFoundError:
+        logger.error(f"File not found: {file_path}")
+        return False
+    except PermissionError:
+        logger.error(f"Permission denied: {file_path}")
+        return False
+    except Exception as e:
+        logger.error(f"Error deleting file {file_path}: {e}")
+        return False
+
+
+def resolve_path(path: str):
+    if path is None:
+        path = Path(os.getcwd()).resolve()
+    else:
+        path = Path(path).resolve()
+    return path
diff --git a/dedal/utils/variables.py b/dedal/utils/variables.py
new file mode 100644
index 0000000000000000000000000000000000000000..553ccf97992ca2dcd06970f82fdbbb26f5f1db23
--- /dev/null
+++ b/dedal/utils/variables.py
@@ -0,0 +1,5 @@
+import os
+
+SPACK_ENV_ACCESS_TOKEN = os.getenv("SPACK_ENV_ACCESS_TOKEN")
+test_spack_env_git = f'https://oauth2:{SPACK_ENV_ACCESS_TOKEN}@gitlab.ebrains.eu/ri/projects-and-initiatives/virtualbraintwin/tools/test-spack-env.git'
+ebrains_spack_builds_git = 'https://gitlab.ebrains.eu/ri/tech-hub/platform/esd/ebrains-spack-builds.git'
diff --git a/pyproject.toml b/pyproject.toml
index c8d849e0c4bfd139081cc4c4a3b1a7a7ddfa7e6f..c7ea27622c32e267ecf0559f1f457656a2f60216 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,8 +17,13 @@ dependencies = [
     "oras",
     "spack",
     "ruamel.yaml",
+    "click",
+    "jsonpickle",
 ]
 
+[project.scripts]
+dedal = "dedal.cli.spack_manager_api:cli"
+
 [tool.setuptools.data-files]
 "dedal" = ["dedal/logger/logging.conf"]