diff --git a/src/ibek/globals.py b/src/ibek/globals.py index e458a38a4..90c799e18 100644 --- a/src/ibek/globals.py +++ b/src/ibek/globals.py @@ -124,9 +124,14 @@ def IOC_LIBS(self): @property def RUNTIME_DEBS(self): - """ibek-support list of declared libs""" + """ibek-support list of declared deb packages to install in runtime stage""" return self.SUPPORT / "configure" / "runtime_debs" + @property + def RUNTIME_FILES(self): + """ibek-support list of files to copy to the runtime stage""" + return self.SUPPORT / "configure" / "runtime_files_list" + # Folder containing templates for IOC src etc. TEMPLATES = Path(__file__).parent / "templates" diff --git a/src/ibek/ioc_cmds/assets.py b/src/ibek/ioc_cmds/assets.py index fb004e84c..b28d84e10 100644 --- a/src/ibek/ioc_cmds/assets.py +++ b/src/ibek/ioc_cmds/assets.py @@ -47,11 +47,11 @@ def extract_assets( """ if GLOBALS.STATIC_BUILD: - # static builds only need database files from support modules - asset_matches = "db" + # static builds only need database and .proto files from support modules + asset_matches = "db|*/protocol" else: - # dynamically linked builds need binaries - asset_matches = "bin|db|lib" + # dynamically linked builds need binaries and protocol files + asset_matches = "bin|db|lib|*/protocol" # chdir out of the folders we will move os.chdir(source) @@ -66,18 +66,21 @@ def extract_assets( Path.readlink( GLOBALS.IOC_FOLDER ).parent, # get contents of IOC folder and its source (parent) - Path("/venv"), # get the virtualenv - ] + list( - source.glob("ibek*") - ) # get ibek-support and related folders + GLOBALS.EPICS_ROOT.parent / "venv", # virtualenv is a peer to /epics + ] else: default_assets = [] # folder names with binary files in them binary = ["bin", "lib"] + # get the list of additional files supplied by add_runtime_files + if GLOBALS.RUNTIME_FILES.exists(): + with GLOBALS.RUNTIME_FILES.open("r") as f: + runtime_files = [Path(line.strip()) for line in f.readlines()] + # move the default assets and extras in their entirety - extra_files = default_assets + extras + extra_files = default_assets + extras + runtime_files for asset in extra_files: src = source / asset if src.exists(): diff --git a/src/ibek/support_cmds/commands.py b/src/ibek/support_cmds/commands.py index 939e56074..6a2d304e0 100644 --- a/src/ibek/support_cmds/commands.py +++ b/src/ibek/support_cmds/commands.py @@ -340,3 +340,17 @@ def generate_schema( typer.echo(Support.get_schema()) else: output.write_text(Support.get_schema()) + + +@support_cli.command() +def add_runtime_files( + files: List[str] = typer.Argument( + None, help="list of file or folders to add to the runtime image" + ), +): + """ + Adds to the list of folders or filesthat are copied over to the runtime + stage of the container build. + """ + files = files or [] + add_list_to_file(GLOBALS.RUNTIME_FILES, files) diff --git a/tests/conftest.py b/tests/conftest.py index 5aff5f951..6921e0bea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -63,15 +63,26 @@ def entity_factory(): def tmp_epics_root(samples: Path, tmp_path: Path, mocker: MockerFixture): # create an partially populated epics_root structure in a temporary folder epics = tmp_path / "epics" + epics_source = samples / "epics" epics.mkdir() - shutil.copytree(samples / "epics" / "pvi-defs", epics / "pvi-defs") - shutil.copytree(samples / "epics" / "support", epics / "support") - Path.mkdir(epics / "opi") - Path.mkdir(epics / "epics-base") - Path.mkdir(epics / "ioc/config", parents=True) - Path.mkdir(epics / "ibek-defs") - Path.mkdir(epics / "runtime") - + # a dummy venv for testing extract_assets + Path.mkdir(tmp_path / "venv") + + # create the minimal structure under epics root + files = epics_source.glob("*") + for f in files: + if f.is_dir(): + shutil.copytree(f, epics / f.name) + else: + shutil.copy(f, epics / f.name) + Path.mkdir(epics / "opi", exist_ok=True) + Path.mkdir(epics / "epics-base", exist_ok=True) + Path.mkdir(epics / "generic-source" / "ioc" / "config", parents=True) + (epics / "ioc").symlink_to(epics / "generic-source" / "ioc") + Path.mkdir(epics / "ibek-defs", exist_ok=True) + Path.mkdir(epics / "runtime", exist_ok=True) + + # patch the global EPICS_ROOT to point to this temporary folder mocker.patch.object(GLOBALS, "_EPICS_ROOT", epics) # this should not be needed - what gives? diff --git a/tests/samples/epics/runtime_file b/tests/samples/epics/runtime_file new file mode 100644 index 000000000..0c50f7a6a --- /dev/null +++ b/tests/samples/epics/runtime_file @@ -0,0 +1 @@ +dummy file for testing add_runtime_files \ No newline at end of file diff --git a/tests/samples/epics/runtime_folder/text1.txt b/tests/samples/epics/runtime_folder/text1.txt new file mode 100644 index 000000000..0c50f7a6a --- /dev/null +++ b/tests/samples/epics/runtime_folder/text1.txt @@ -0,0 +1 @@ +dummy file for testing add_runtime_files \ No newline at end of file diff --git a/tests/samples/epics/runtime_folder/text2.txt b/tests/samples/epics/runtime_folder/text2.txt new file mode 100644 index 000000000..0c50f7a6a --- /dev/null +++ b/tests/samples/epics/runtime_folder/text2.txt @@ -0,0 +1 @@ +dummy file for testing add_runtime_files \ No newline at end of file diff --git a/tests/test_unit.py b/tests/test_unit.py index f2e800ddf..248305a9f 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -3,15 +3,19 @@ """ import dataclasses +import shutil +from pathlib import Path import jinja2 import pytest from ibek.commands import semver_compare from ibek.ioc import id_to_entity +from ibek.ioc_cmds.assets import extract_assets from ibek.ioc_factory import IocFactory from ibek.parameters import IdParam, ObjectParam from ibek.support import EntityModel, Support +from ibek.support_cmds.commands import add_runtime_files from ibek.utils import UTILS @@ -89,3 +93,25 @@ def test_strict(): my_template = "{{ person.name ~ ' of age ' ~ person.height }}" with pytest.raises(jinja2.exceptions.UndefinedError): text = UTILS.render({"person": p}, my_template) + + +def test_extract_assets(tmp_epics_root: Path, samples: Path): + """ + Test the extract_assets function + """ + runtime_files = [ + str(tmp_epics_root / "runtime_file"), + str(tmp_epics_root / "runtime_folder"), + ] + add_runtime_files(runtime_files) + + dest = Path("/tmp/ibek_test_assests") + shutil.rmtree(dest, ignore_errors=True) + dest.mkdir() + + extract_assets(dest, tmp_epics_root, [], True) + new_epics_root = list(dest.glob("tmp/*/*/*/epics"))[0] + + assert Path.exists(new_epics_root / "runtime_file") + assert Path.exists(new_epics_root / "runtime_folder/text1.txt") + assert Path.exists(new_epics_root / "runtime_folder/text2.txt")