8.2.6. Project Branching

A common use case is the analysis of VCS branches in a separate Dashboard project.

In software development a typical way of doing modifications is creating an exact copy of some state of the source code and then work on that, while other developers may be working on other copies of the source code. This way, no one interferes with the copies of the others and every copy has a well-defined purpose. Eventually, the changes of such a copy compared to its original are integrated back into some main or master copy.

Version Control Systems like Git assist with the maintenance and sharing of these copies and also with the process of integrating the changes that were done on such a copy. They have established the terms Branch for such copies and the term Merge for when the changes of such a copy are reintegrated into the main copy or branch.

Around these basic principles, several workflows have emerged, the most basic being that of so-called feature branches as illustrated here. These feature branches can live for quite some time before they are merged back and then finally deleted. Sometimes such branches are never merged back and continue to evolve on their own.

The life cycle of a branch, e.g. Feature_1

An Axivion Dashboard project only tracks one VCS Branch, however it may be desirable to set up an additional axivion_ci / Dashboard project corresponding to a Branch that starts out with a copy of the original project, as the erosion findings are the same when the source code is the same. Over time, both the Main and the Branch project evolve differently regarding erosion findings.

On the Branch project, one is typically only interested in findings introduced by code changes on the corresponding VCS branch. It also would be nice, to somehow see the relation between the Main and the Branch project and also to somehow be able to copy annotations that were created on issues of the branch project back to the corresponding issues on the main project after merging.

All of this is possible using tools and API we provide but it needs to be integrated into your automation system. Here you can see the basic steps and when they happen.

Where our tools/APIs should be invoked

Note

Before reading ahead you should be aware that calling Dashboard APIs from an automation server requires storing credentials in it, so you should make sure to have read the section on axivion_ci authentication.

8.2.6.1. Branch Creation

Ideally, right after the VCS branch is created (but also at some later point in time), you have to create the Dashboard branch project. If your analysis configuration is under version control, that may very well be branched already. However, it may also contain the Project.name in a hard-coded way. This value must match the Dashboard project name, and thus hard-coding that name is even dangerous, as we usually want a copied configuration to correspond with a different Dashboard project and using the same project name for two different branches will actually result in both branches appending their analysis results to the same database.

You thus may want to configure Project.name dynamically e.g. by setting it to MyProject-$(env:AXIVION_BRANCH_NAME) so your automation system can somehow derive that value from the VCS branch and supply it when invoking axivion_ci (see Git Branch and Commit Hash).

Note

See here for some thoughts on choosing a good project naming scheme and here for some inspiration on how to derive some environment variable values from git.

Caution

In case of /Results/Dashboard/database_mode=shared_database you also need to ensure the uniqueness of the path names of all the enabled artifacts. Relevant config options for this are

The names of the IR file and the RFG file as well of the Annotation Database (.dbx) (created by the Dashboard) are automatically aligned to the Database filename.

Once you have taken care that your analysis project name and artifacts are unique, it is time to branch the Dashboard project. The way of doing this is fundamentally different depending on whether you have /Results/Dashboard/database_mode set to managed_upload or shared_database.

Branch Project Creation with database_mode=shared_database

With database_mode set to shared_database, you are responsible for providing and configuring the storage locations of the analysis artifacts both in axivion_ci and the Dashboard. Consequently, you can create your branch project by copying the artifacts to a new name and registering the new database in the Dashboard.

Python (rfgscript) Snippet to create a branch project from an existing project with database_mode=shared_database
# Copyright (C) 2025 Axivion GmbH
# Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
# https://www.qt.io


import os
import shutil
import subprocess
from pathlib import Path

from axivion.dashboard import ApiToken, Dashboard

prj_main = os.environ['MAIN_PROJECT_NAME']
prj_feature = os.environ['FEATURE_PROJECT_NAME']
ci_side_databases_dir = Path(os.environ['DATABASES_DIR'])
dashboard_side_databases_dir = Path(os.environ['DASHBOARD_DATABASES_DIR'])


def artifact(
    project_name: str, suffix: str, db_root: Path = ci_side_databases_dir
) -> str:
    return os.fspath(db_root / Path(project_name).with_suffix(suffix))


# issue database + annotation database
shutil.copy2(artifact(prj_main, '.db'), artifact(prj_feature, '.db'))
shutil.copy2(artifact(prj_main, '.dbx'), artifact(prj_feature, '.dbx'))

# generated files folder (next 17 lines, remove if you do not use a ShadowVCS)
repo_lines = subprocess.check_output(
    ['cidbman', 'repository', 'list', artifact(prj_feature, '.db')]
).splitlines()
opts = (line.split(b';') for line in repo_lines)
shadow_dir_opt = next((opt for opt in opts if opt[1] == b'shadow_directory'), None)
if shadow_dir_opt is not None:
    shadow_dir = artifact(prj_feature, '.shadow')
    shutil.copytree(shadow_dir_opt[2].decode('utf-8'), shadow_dir)
    shadow_dir_def = subprocess.check_call(
        [
            'cidbman',
            'repository',
            'set',
            shadow_dir_opt[0],
            'shadow_directory=' + shadow_dir,
        ]
    )

with Dashboard('https://dashboard.example', auth=ApiToken(os.environ['TOKEN'])) as d:
    d.install_project(
        artifact(prj_feature, '.db', dashboard_side_databases_dir),
        prj_feature,
    )

The copying of the generated files folder can be omitted, if that feature is not enabled. Also you’ll likely not want to copy IR and RFG as those artifacts aren’t versioned and the first analysis of the branch project will overwrite them anyway.

Caution

The Continuous Integration Token Type is only usable for this if your Dashboard Server is running on at least 7.8.0. If it isn’t you’ll have to resort to the General Purpose Token and to grant the Configure Dashboard permission.

Branch Project Creation with shared_database=managed_upload

Caution

Requires your Dashboard Server to be on at least 7.8.0.

With database_mode set to managed_upload, no manual artifact copying is necessary, you just need to call the appropriate Dashboard API endpoint.

Python (rfgscript) Snippet to create a branch project from an existing project with shared_database=managed_upload
# Copyright (C) 2025 Axivion GmbH
# Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
# https://www.qt.io


import os

from axivion.dashboard import ApiToken, Dashboard, Project

prj_main = os.environ['MAIN_PROJECT_NAME']
prj_feature = os.environ['FEATURE_PROJECT_NAME']

with Dashboard('https://dashboard.example', auth=ApiToken(os.environ['TOKEN'])) as d:
    Project(d, prj_main).create_branch(name=prj_feature, version='latest')

Note

With version 7.8.3, the Dashboard Server API was extended to automatically check whether a branch project with given name (prj_feature in our example) already exists and whether its origin and analysis version match the values supplied in the API call (prj_main and 'latest', respectively, in our example). If such a project is found, it is returned instead of creating a new branch project—effectively making create_branch a NOOP on the server side. Also with Dashboard Server version 7.8.3, the version parameter became optional with latest being the default value. Moreover, omitting version in create_branch disables the version check for already existing branch projects. This might particularly be useful in scenarios where the branching API is called for every analysis run, regardless of whether the branch project to be created already exists or not.

8.2.6.2. Merging of Issue Annotations

When new changes to the main branch are integrated into your long-living feature branch you will likely want to also have the new issue annotations of the main branch in the annotation database of your feature branch. Likewise, when the end of life of your feature branch is reached and you want it integrated back into your main branch you will also want to merge the annotations unique to your feature branch back to main.

This can be achieved using the tool merge_issue_annotations and the process is the same regardless of what database_mode you use.

Example usage of merge_issue_annotations
merge_issue_annotations --url https://your-dashboard/axivion \
      --source_project prj_src --target_project prj_tgt

Note, that prj-src and prj-tgt need to be chosen in accordance with the annotation copy direction, i.e. when integrating new changes from the main branch into the feature branch, source is the main project and target the feature project and when integrating a feature branch into the main branch it is the other way round.

In order for the script to work properly, you need to set the environment variable AXIVION_USERNAME to the login name of a (technical) Dashboard User with at least the following permissions:

  • Log in

  • View

  • View Annotations

  • Low-Level Issue Annotation Control

All the permissions except for Log In are project specific, i.e. the technical user can be limited to a certain project namespace, e.g. Team1-*.

The environment variable AXIVION_PASSWORD must be set to an ApiToken of type General Purpose or Continuous Integration belonging to the user denoted by AXIVION_USERNAME. Providing the password in the environment variable is also possible but not recommended.

Caution

The Continuous Integration Token Type is only usable for this if your Dashboard Server is running on at least 7.8.0. If it isn’t you’ll have to resort to the General Purpose Token.

8.2.6.3. Branch Deletion

Once your feature branch is merged back into main and not needed any more, you likely want to delete the corresponding Dashboard project.

This may also be a good time to trigger the (final) invocation of merge_issue_annotations in order to sync your issue annotations. If you want to do that, you need to await one analysis run on the main branch (after the feature branch was integrated) and only then can do the last issue annotations sync and only afterwards delete the branch project.

Same as with creating the branch project, the procedure for removing it differs fundamentally, depending on which database_mode you use.

Branch Project Deletion with database_mode=shared_database

With database_mode set to shared_database, you are responsible for providing and configuring the storage locations of the analysis Artifacts both in axivion_ci and the Dashboard. The deletion of artifacts in this case thus requires deleting those files manually and invoking deregistration of the project from the Dashboard Server, unless Automated Cleanup for unmanaged projects is configured.

Python (rfgscript) Snippet to delete a branch project with database_mode=shared_database
# Copyright (C) 2025 Axivion GmbH
# Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
# https://www.qt.io


import os
import shutil
from pathlib import Path

from axivion.dashboard import ApiToken, Dashboard

prj_feature = os.environ['FEATURE_PROJECT_NAME']
ci_side_databases_dir = Path(os.environ['DATABASES_DIR'])


def artifact(
    project_name: str, suffix: str, db_root: Path = ci_side_databases_dir
) -> Path:
    return db_root / Path(project_name).with_suffix(suffix)


with Dashboard('https://dashboard.example', auth=ApiToken(os.environ['TOKEN'])) as d:
    d.uninstall_project(prj_feature)

artifact(prj_feature, '.db').unlink()  # issue database
artifact(prj_feature, '.dbx').unlink()  # annotation database
artifact(prj_feature, '.ir').unlink()  # IR file
artifact(prj_feature, '.rfg').unlink()  # RFG file
shutil.rmtree(artifact(prj_feature, '.shadow'))  # generated files folder

Caution

The Continuous Integration Token Type is only usable for this if your Dashboard Server is running on at least 7.8.0. If it isn’t you’ll have to resort to the General Purpose Token and to grant the Configure Dashboard permission.

Branch Project Deletion with database_mode=managed_upload

Caution

Requires your Dashboard Server to be on at least 7.8.0.

With database_mode set to managed_upload, deleting the branch project merely consists of calling the delete API endpoint:

Python (rfgscript) Snippet to delete a branch project with database_mode=managed_upload
# Copyright (C) 2025 Axivion GmbH
# Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group
# https://www.qt.io


import os

from axivion.dashboard import ApiToken, Dashboard, Project

prj_feature = os.environ['FEATURE_PROJECT_NAME']

with Dashboard('https://dashboard.example', auth=ApiToken(os.environ['TOKEN'])) as d:
    Project(d, prj_feature).delete()

8.2.6.4. Automated Cleanup of Branch Projects

The general idea of automated project cleanup is to periodically (every 24h) delete Dashboard projects (including their artifacts !) that are related to a non-existing VCS branch.

Note

Automated cleanup is currently limited to projects using the Git Version Control System and also requires that the branch option of the VCSIntegration Git can be reflected from workspace (i.e., is not in detached head state) or has to be manually set (see Git Branch and Commit Hash).

Note

This option may be dangerous if you want to merge back issue annotations before branch project deletion as it might be hard to ensure the four steps: VCS merge, axivion_ci (on main), merge_issue_annotations, and VCS branch deletion happen in exactly that order.

On the General Settings page of the Dashboard Server, you can enable the automated cleanup using the following options:

Trashing Projects

The Dashboard periodically (every 24h) scans for projects for which at least one of their VCS branches does not exist anymore and trashes them. This means that the projects are not permanently erased immediately but only removed from the Dashboard and marked for deletion. This is done by renaming the files associated to that project with a .trash suffix.

Deleting trashed Projects

The Dashboard periodically (every 24h) scans for projects that are marked for deletion (i.e., have the .trash suffix in their filename) and deletes them from disk.

  • Delete *.trash files from shared_database projects – This will only delete shared_database projects that are marked for deletion.

  • Delete *.trash files from managed_upload projects – This will only delete managed_upload projects that are marked for deletion.

Caution

Files deleted this way cannot be restored.