Package Version in the Device Configuration

Since Karabo 2.10, Devices packages are able to set the classVersion property in their configuration from helper functions. The karabo packaging system installs devices from git repositories. This section will document the implementation in the various APIs. The git version will be encoded in the device’s classVersion property.

Rationale

The classVersion property is set from the framework as a string formatted as

PackageName-git_tag

This implementation allows long-term storage of the installed packages in the archiving system for reproducibility. It also allows to spot if a package is not deployed with a tagged version on a live system.

Known Issues

The C++ API and the Python APIs do not use the same path to define the PackageName string:

  • In the case of the C++ API, the PackageName is set to the directory name of the root folder of the git repository.
  • In the case of the Python APIs, the PackageName is set to the name of the python module where the device class is defined.

In the case of packages created using the command karabo new, this distinction does not have consequences.

The complete code traceability would need to expose the url of the origin repository. Due to the fact that for the European XFEL scope, the PackageName and version are sufficient and that the uri could be easily spoofed by custom network configurations. This feature is not implemented.

C++ API

The karabo script, currently supports installink c++ dependencies only through a Makefile. In the package’s Makefile, add the following target:

PACKAGE_NAME=$(shell basename -s .git `git remote -v | grep fetch | head -n1 | awk '{ print $$2 }' `)

src/version.hh: .git/HEAD .git/index .git/refs/tags
    @echo "// WARNING: This file is auto generated by the Makefile." > $@
    @echo "#ifndef PACKAGE_VERSION" >> $@
    # Note that --dirty can be fooled: Build once when clean and then change source files - or vice a versa...
    @echo "#define PACKAGE_VERSION \"$(PACKAGE_NAME)-$(shell git describe --tags --match "*.*.*" --dirty --always )\"" >> $@
    @echo "#endif" >> $@

Make sure to add the src/version.hh file as a dependency to the library file target. In case of devices generated from Karabo’s template, this target dependency is sufficient:

.build-pre: src/version.hh

In the file where the device class is defined, include the src/version.hh file and pass the PACKAGE_VERSION define to the KARABO_CLASSINFO macro.

#include "version.hh"  // provides PACKAGE_VERSION
...

KARABO_CLASSINFO(DeviceClassName, "DeviceClassName", PACKAGE_VERSION)

The transient file src/version.hh should not be included in the git repository and be added to the .gitignore file.

Python

For Python packages, Karabo uses the setuptools_scm package to define a path where the version is saved. In the setup.py file, the following definition should be added:

ROOT_FOLDER = dirname(realpath(__file__))
VERSION_FILE_PATH = join(ROOT_FOLDER, 'src', 'deviceClassModule', '_version.py')

try:
    from karabo.packaging.versioning import device_scm_version
    scm_version = device_scm_version(ROOT_FOLDER, VERSION_FILE_PATH)
except ImportError:
    # compatibility with karabo versions earlier than 2.10
    scm_version = {'write_to': VERSION_FILE_PATH}

This conditional import definition allows compatibility with versions of Karabo earlier than 2.10.

In the setup function call, one should add the import:

setup(name='DeviceClassName',
      use_scm_version=scm_version,
      ...

Bound API

In the file where the device’s class is defined, one should import:

from ._version import version as deviceVersion

and decorate the device class with the decorator

@KARABO_CLASSINFO("DeviceClassName", deviceVersion)

Middlelayer API

In the file where the device’s class is defined, one should import:

from ._version import version as deviceVersion

and add the class attribute __version__ to the class:

class DeviceClassName(Device):
    # provide version for classVersion property
    __version__ = deviceVersion