Apertis stores the source of all the shipped packages in GitLab and uses a GitLab CI pipeline to manage the workflows and automate many of the tasks that are required to release a package, thus for many tasks such as adding or updating packages, interaction is only required with GitLab, with automation taking care of the rest of the process.

The software components used in Apertis images are packaged in the deb packaging format, as used by Debian. It is preferred, where possible, to add additional packages from Debian as covered by the contribution policy.

## Adding New Packages from Debian

This is the process to import a new package from Debian to Apertis:

• Check out the apertis-infrastructure repository or use the SDK image, which comes with the required tools installed.

• Python gitlab credentials are required to run the scripts. Set these up to have access to the Apertis Gitlab server, see python-gitlab docs.

For example, write to the default user configuration file ~/.python-gitlab.cfg:

[global]
default = apertis
ssl_verify = true
timeout = 5

[apertis]
url = https://gitlab.apertis.org/
private_token = $your_gitlab_private_token api_version = 4  • Invoke import-debian-package to import the new debian package: $ import-debian-package --upstream buster --downstream apertis/v2021 --component target --package hello --push-remote


To fetch a specific version, add the --version option:

import-debian-package --upstream buster --downstream apertis/v2021 --component target --package hello --push-remote --version 2.10-2  • The argument to --component reflects the repository component it is part of (for instance, target); it will be stored in debian/apertis/component. • Multiple downstream branches can be specified, in which case all of them will be updated to point to the newly imported package version. • The Apertis version of the package will have a local suffix (apertis0) appended. • The argument --push-remote will create the remote repository (if it doesn’t exist) and push all branches and tags. Then it will take care of applying the default settings and triggering the CI pipeline on all downstream branches. ## Adding New Packages from Upstream Sources There are likely to be instances where it is desirable to import the latest version of a piece of software into Apertis directly from it’s original authors or maintainers. This may be as a result of the software in question not being packaged by Apertis’ default upstream distribution, Debian, or their being a mismatch between the desired version in the upstream distribution and what is required for a specific goal. For example, the Apertis release flow stipulates that each Apertis release should include the latest mainline kernel LTS release. Due to Debian’s release cadence being approximately half that of Apertis’, there are 2 Apertis releases for each Debian stable release and no guarantees that the kernel’s packaged for Debian’s current stable or in progress releases will align with Apertis’ requirements. As a result it is expected that Apertis will occasionally need to take its kernel from the mainline kernel LTS tree to satisfy it’s requirements. Unless the imported package can be used as-is without any modification, there will be a patch series that potentially needs tweaking to apply after the update. The workflow set out below takes this into consideration. Below we use the process of importing GNU Hello application as an example. • Using the Gitlab Web UI, create an empty project in your personal area, for example called hello. • Download the latest release tarball from upstream. At the time of writing this is hello-2.10.tar.gz for GNU Hello. • Extract the tarball and enter the extracted folder  tar xf hello-2.10.tar.gz
$cd hello-2.10  • Create a new git repository and commit the whole source tree; this will create the upstream/apertis branch containing the untouched upstream source code: $ git init
$git checkout -b upstream/apertis$ git add .
$git commit -s -m "Initial commit of GNU Hello 2.10"  • Commit the original tarball to the pristine-lfs branch: $ pristine-lfs commit ../hello-2.10.tar.gz

• Create the packaging branch for the Apertis version you’re targeting, for example if you are targeting Apertis v2021, create the apertis/v2021 branch:

$APERTIS_RELEASE=v2021$ git checkout -b apertis/${APERTIS_RELEASE}  • Add packaging files and commit them to the packaging branch. • Ensure that the package builds for you locally: $ gbp buildpackage -uc -us --git-debian-branch=apertis/${APERTIS_RELEASE} --git-upstream-tree=upstream/apertis  • Add the Gitlab project you created earlier as the origin git remote (replacing <username> with your GitLab username): $ GITLAB_USER=<username>
$git remote add origin git@gitlab.apertis.org:${GITLAB_USER}/hello.git

• Push your branches to the repository:

## Updating Components to a New Debian Release

The CI/CD pipeline can also be used to pull in a new release of Debian. If the needed debian/$RELEASE and upstream/$RELEASE branches don’t exist (for example, there are debian/buster and upstream/buster branches but no debian/bullseye and upstream/bullseye), the Gitlab Web UI can be used to create them:

• Navigate to the Branches page and click on the New branch button.
• Enter the new branch name as upstream/$RELEASE, where $RELEASE is the new release name, for example upstream/bullseye.
• In the Create from field select the previous Debian release. For example, the release that precedes Debian Bullseye is Buster, so pick upstream/buster.
• Click the Create branch button.
• Repeat the steps for debian/$RELEASE. From there, a new pipeline should have been automatically triggered on the new debian/$RELEASE branch. This pipeline will import the new package version from Debian and both upstream/$RELEASE and debian/$RELEASE branches will be updated. The pipeline will also create a new branch called proposed-updates/debian/$RELEASE/... as well as new merge request based on this branch and targeting the current Apertis development branch (e.g. apertis/v2024dev1). If the pipeline is not automatically triggered when the new debian/$RELEASE is created, then please refer to the Updating Components from Debian paragraph to manually trigger it.

In some situations manual intervention may be required to tweak the packaging, patches or the automated merge machinery may ask to PLEASE SUMMARIZE remaining Apertis Changes. When this happens:

• Check out the proposed update branch.
• If asked to summarize the changes, edit the changelog to list all the downstream changes the package still ships compared to the newly merged upstream package and their reason, describing the purpose of each downstream patch and of any other change shown by git diff against the debian/* branch.
• Where other issues are found, either modify the packaging data or add/modify the Apertis patch series to address the issue.
• Amend the merge commit.
• Force-push to the proposed update branch
• If changes beyond summarizing the changelog are made, the update branch should be reviewed before it is merged.

Moving from a debian/ branch to another can sometimes lead to a merge conflict in the merge-updates job running on the debian/$RELEASE branch. This results in a failure of the prepare-build-env job from the child pipeline running on proposed-updates/debian/$RELEASE/... branch. Thus, the proposed-updates/debian/$RELEASE/... branch contains upstream sources together with the debian folder but not Apertis changes. This is easily recognizable by the error message: cat: debian/apertis/component: No such file or directory. In this case, the current Apertis development branch (e.g. apertis/v2024dev1) needs to be manually merged into the new proposed-updates/debian/$RELEASE/... branch and merge conflicts need to be manually resolved.

## Updating from Debian Development Repositories

This is another scenario, wherein the user may need updates which are not yet released into the Upstream Distributions’ package repositories.

For example, for Apertis, we may need a very newer version of a package, which may not yet have been released into any of Debian development releases (Unstable, Testing). In such cases, where the changes may only be available in the packaging repositories. We need to take extra care when pulling in such updates as they will not have been through the same levels of testing as packages taken from stable or even testing releases.

• Clone the remote git packaging repository from Debian.

$PACKAGE=hello$ DEBIAN_REPO=https://salsa.debian.org/debian/${PACKAGE}.git$ DEBIAN_BRANCH=debian/master
$git clone -b${DEBIAN_BRANCH} ${DEBIAN_REPO}${PACKAGE}-debian
$cd${PACKAGE}-debian

• Generate a source package out of the packaging repository

$gbp buildpackage -S --no-sign --git-debian-branch=${DEBIAN_BRANCH}


If successful, this will give us a proper source packages in the parent directory

• Clone the Apertis git packaging repository for the same component

$cd ..$ GITLAB_GROUP=pkg
$git clone git@gitlab.apertis.org:${GITLAB_GROUP}/${PACKAGE}$ cd ${PACKAGE}  • If the required debian/ and upstream/ branches don’t yet exist in your repository, create them based on the version that you have for instance. For instance, if you currently have a version of Apertis based on Debian Buster and wish to use the updated package in Bullseye: $ CURRENT_UPSTREAM=buster
$DESIRED_UPSTREAM=unreleased$ git push origin origin/upstream/${CURRENT_UPSTREAM}:refs/heads/upstream/${DESIRED_UPSTREAM}
$git push origin origin/debian/${CURRENT_UPSTREAM}:refs/heads/debian/${DESIRED_UPSTREAM}  • Ensure you have the required branches locally: $ git branch debian/${DESIRED_UPSTREAM} origin/debian/${DESIRED_UPSTREAM}
$git branch upstream/${DESIRED_UPSTREAM} origin/upstream/${DESIRED_UPSTREAM}  • Run gbp import-dsc to update the debian/ and upstream/ branches. $ NEW_DSC=$(ls -t ../*.dsc | head -n 1)$ GBP_CONF_FILES=/dev/null gbp import-dsc --author-is-committer --author-date-is-committer-date \
--upstream-branch=upstream/${DESIRED_UPSTREAM} --debian-branch=debian/${DESIRED_UPSTREAM} \
--debian-tag='debian/%(version)s' --no-sign-tags --no-pristine-tar ${NEW_DSC}  • Use the pristine-lfs tool to import the source package generated from the Debian repository into Apertis packaging repository. $ pristine-lfs import-dsc ${NEW_DSC}  • Ensure that the changes to these branches are pushed to your remote origin. It is important that these branches are pushed and in sync with the default remote. $ git push origin --follow-tags pristine-lfs debian/${DESIRED_UPSTREAM} upstream/${DESIRED_UPSTREAM}

• The upstream changes now need to be merged into the apertis branch. We will do this via a development branch that can be pushed for review.

$APERTIS_RELEASE=v2021$ PROPOSED_BRANCH=proposed-updates/${GITLAB_GROUP}/${APERTIS_RELEASE}-update-from-${DESIRED_UPSTREAM}$ git checkout -b ${PROPOSED_BRANCH} apertis/${APERTIS_RELEASE}
$apertis-pkg-merge-updates --upstream=debian/${DESIRED_UPSTREAM} --downstream=${PROPOSED_BRANCH}  • If the merge fails, fix the conflicts and continue $ git add ...
$git merge --continue  • Create a new changelog entry for the new version $ DEBEMAIL="User Name <user@example.com>" dch -D apertis --local +apertis --no-auto-nmu ""

• Check debian/changelog and update it if needed by listing all the remaining Apertis changes then commit the changelog.

$DEBIAN_VERSION=$(dpkg-parsechangelog -S Version)
$git commit -s -m "Release${PACKAGE} version ${DEBIAN_VERSION}" debian/changelog  • Push your changes for review $ git push origin ${PROPOSED_BRANCH}:${PROPOSED_BRANCH}


Follow the instructions provided by the above command to create a merge request

## Updating New Version of Packages from Upstream Sources

There are likely to be instances where it is desirable to update the latest version of a piece of software into Apertis directly from it’s original authors or maintainers. This may be as a result of the software in question not being packaged by Apertis’ default upstream distribution, Debian, or there being a mismatch between the desired version in the upstream distribution and what is required for a specific goal.

For example, the Apertis release flow stipulates that each Apertis release should include the latest mainline kernel LTS release. Due to Debian’s release cadence being approximately half that of Apertis’, there are 2 Apertis releases for each Debian stable release and no guarantees that the kernel packaged for Debian’s current stable or in progress releases will align with Apertis’ requirements. As a result it is expected that Apertis will occasionally need to take its kernel from the mainline kernel LTS tree to satisfy it’s requirements.

Unless the imported package can be used as-is without any modification, there will be a patch series that potentially needs tweaking to apply after the update. The workflow set out below takes this into consideration.

Below we use the process of updating GNU Hello application as an example.

• Clone the package git repository that needs to be updated with the new upstream source

$git clone https://git.apertis.org/pkg/hello$ cd hello

• Download the latest release tarball from upstream. At the time of writing this is hello-2.10.tar.gz for GNU Hello.

• Import the new upstream tarball into the git repository

$gbp import-orig ../hello-2.10.tar.gz --debian-branch=apertis/v2021 --upstream-branch=upstream/apertis --no-pristine-tar  • Commit the original tarball to the pristine-lfs branch: $ pristine-lfs commit ../hello-2.10.orig.tar.gz

• Update packaging files to adapt to new upstream changes and commit them to the packaging branch.

• Ensure that the package builds for you locally:

$gbp buildpackage -uc -us --git-debian-branch=apertis/${APERTIS_RELEASE} --git-upstream-tree=upstream/apertis

• Push your branches to the repository:

$git push --all --follow-tags origin  # Creating New Modifications The standard Contribution Process applies unchanged to packaging repositories, the Apertis development team and trusted contributors push changes to wip/ branches and get them landed to the apertis/* branches via merge requests. Other developers can fork packaging repositories into their personal space and send merge requests from there as documented in the development process guide. The only additional requirement imposed by the Debian packaging format is that changes outside of the debian/ folder are not allowed and would cause the source-building pipeline to fail. Check the Debian Packaging documentation to find how patches affecting code outside of the debian/ folder should be handled. ## The Debian Changelog and Releases Beyond informing developers and interested users of what has changed between successive releases of a component, the changelog controls the release process in a number of ways: • Package versioning: The changelog is the canonical location used by the Debian packaging process to determine the version number used for the binary packages. • Release control: The “distribution” field of a changelog entry controls whether the component is ready to be cut as a new release. It is therefore important to correctly handle the changelog file when creating modifications to components. The CI pipeline generates a source package locally for each commit pushed to the packaging repositories. You can download this package by browsing the pipeline artifacts. The generated sources are versioned to indicate that they are not yet suitable for release. With the “distribution” field set to UNRELEASED, package sources get uploaded to the :snapshots OBS project with the matching branch (that is, when targeting the apertis/v2021 branch of a component from the target suite, the source package will be uploaded to the apertis:v2021:target:snaphots project). It is advisable to update the debian/changelog file in a separate commit, as the very last step when you issue a release. Such changelog entries should be generated from the Git commit log. This can be achieved by using gbp dch. Writing good commit messages ensures that you don’t have to spend time editing the generated changelog entries. It is best to submit a separate merge request on GitLab for each bug or task. As performing a separate release between each merge request may not be wanted and to avoid churn in the case of rebases (in particular to ease the review process), it is recommended to leave the editing of debian/changelog to a dedicated merge request once all the other MRs have been landed. If you still wish to edit debian/changelog as you go along for some reason, make sure that the changelog entry you’re writing has the distribution field set to UNRELEASED and use gbp dch --auto --ignore-branch to ensure the formatting of the changelog is correct. ## Landing Downstream Changes to the Main Archive Once downstream changes to a package are deemed ready to be published in the Apertis main archive, a proper release needs to be issued. To achieve this create a merge request containing an update to the changelog. The description should contain details of all the changes since the last release. This can be easily achieved with gbp dch if the commits have been well written: $ APERTIS_RELEASE=v2021
$GBP_CONF_FILES=/dev/null gbp dch --release -D apertis --debian-branch=apertis/${APERTIS_RELEASE}


Ensure that the “distribution” field has been changed from UNRELEASED to apertis.

If making changes to more than one release, start with the most recent release branch where you want to land your changes. For instance, if you want to land changes to both the development and stable releases, submit a merge request for the development one first (for instance, apertis/v2020dev0) and then, once merged, create a merge request for the stable one (for instance, apertis/v2019-updates)

Once a merge request has been reviewed and landed, the CI pipeline will build-check the source package as usual. Since the distribution field is no longer UNRELEASED it will also:

• add a Git tag for the release version to the repository
• rebuild the release source package
• store the release sources in the pristine-lfs-source branch
• upload the release source package to the main project on OBS (for instance apertis:v2020dev0:target)

If the apertis/$RELEASE-updates or apertis/$RELEASE-security branches for published stable releases do not exist yet, they should be created from the GitLab web UI since their protected status makes pushing forbidden.

For trivial changes it is also possible to combine the release commit in the same MR as the changes. Again, developers need to be careful to ensure the changelog entries are kept up-to-date when the commit messages get changed via rebase.

## Backporting Updates or Security Fixes

Often downstream fixes, upstream updates or security fixes need to be applied to multiple active releases. Changes should be introduced in the most recent development release where they can be tested and regressions detected with little impact. Once the changes have been thoroughly tested, paying close attention to the QA test results, they can then be propagated to the more stable releases, where any mistake can impact the product teams using Apertis in the field.

For instance, once a fix is landed to apertis/v2021dev0 and no regressions are found in the subsequent QA test results, an MR should be create to land the changes to the relevant stable releases.

If there is no divergence between the packages in the different releases and the backport can be done with a fast-forward, an MR should be created to submit the changes from for instance apertis/v2021dev0 to apertis/v2020-updates or apertis/v2020-security. Choose the required destination depending on the nature and impact of the fix.

If package diverged across releases, a separate branch has to be created where the fixes are cherry-picked appropriately before creating the MR.

## Diverging Release Branches

Sometimes different downstream patches need to be applied in the different Apertis release branches. A clear case of that is the base-files package which ships the release name in /etc/os-release.

In such situation it is crucial to use different version identifiers in each branch: the version for a given package needs to be globally unique across the whole archive since uploading different package sources with the same name/version would lead to difficult to diagnose errors.

When targeting a specific release, ~${RELEASE}.${COUNTER} needs to be appended to the version identifier after the local build suffix:

Previous Version New release specific version (for v2020) Description
0.42+apertis0 0.42+apertis0~v2020.0 Increment Apertis release, append release specific identifier
0.42+apertis0~v2020.0 0.42+apertis0~v2020.1 Increment the release specific counter

This uses the fact that ~ in Debian package numbers sorts before anything. Adding ~ is necessary so that if a new upstream version 0.42.1 or a new non-release-specific downstream version 0.42+apertis1 is introduced, they will replace the release-specific package.

Note that dpkg considers 2020.0 to be newer than 2020pre.0, so the Apertis release identifiers can be used with no modification. If in doubt, check with dpkg --compare-versions:

$dpkg --compare-versions 0.42+apertis0~2020pre.0 '<<' 0.42+apertis0~2020.0 && echo true || echo false true$ dpkg --compare-versions 0.42+apertis1 '<<' 0.42+apertis0~2020.0 && echo true || echo false
false


## Normalizing Line Endings in Git

Under certain project repositories, usually the ones which are developed upstream in both Windows and Unix environments, line endings can be a peculiar source of frustration. This is usually seen when cloning out a fresh repository (like: bind9) and notice that the checked out repository reports many modified files.

As an example:

$git clone git@gitlab.apertis.org:pkg/bind9 Cloning into 'bind9'... remote: Enumerating objects: 10627, done. remote: Counting objects: 100% (986/986), done. remote: Compressing objects: 100% (750/750), done. remote: Total 10627 (delta 337), reused 426 (delta 221), pack-reused 9641 Receiving objects: 100% (10627/10627), 13.99 MiB | 952.00 KiB/s, done. Resolving deltas: 100% (5960/5960), done.$ cd bind9/

$git status On branch apertis/v2023dev1 Your branch is up to date with 'origin/apertis/v2023dev1'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: bin/check/win32/checkconf.vcxproj.filters.in modified: bin/check/win32/checkconf.vcxproj.in modified: bin/check/win32/checkconf.vcxproj.user modified: bin/check/win32/checktool.vcxproj.filters.in modified: bin/check/win32/checktool.vcxproj.in modified: bin/check/win32/checktool.vcxproj.user modified: bin/check/win32/checkzone.vcxproj.filters.in modified: bin/check/win32/checkzone.vcxproj.in modified: bin/check/win32/checkzone.vcxproj.user modified: bin/confgen/win32/confgentool.vcxproj.filters.in modified: bin/confgen/win32/confgentool.vcxproj.in modified: bin/confgen/win32/confgentool.vcxproj.user ..... snipped .....  A quick fix for such repositories is to add the below mentioned settings to the cloned repository’s git attributes $ echo "* -text -eol -crlf -ident -filter -working-tree-encoding -export-subst" >> .git/info/attributes

\$ git status
On branch apertis/v2023dev1
Your branch is up to date with 'origin/apertis/v2023dev1'.

nothing to commit, working tree clean


As merge requests to components are submitted, the CI pipeline performs license scans on the package. The scans are performed on all files in the package, not just the new submission. The pipeline fails or emits a warning (depending on the configuration) if it finds files with unknown or unclear licensing terms, or files under licenses not allowed in the package. When such a situation arises, it is the responsibility of the submitter to perform the review of the license scan results and make updates to the package if necessary.

When the license scan mistakenly identifies a file as being under an incorrect license, or fails to process it correctly, there are three ways to fix this:

1. Specify the correct copyright and the license in debian/apertis/copyright.yml. The format of the file is specified in the Dpkg::Copyright::Scanner manpage. In short, it’s a YAML file mapping paths to their licensing information:

  1 2 3 4 5 6 7 8 9 10  debian: copyright: 2015, Marcel license: Expat src/: copyright: 2016, Joe license: Expat .*/NOTICE: skip: 1 src/garbled/: 'override-copyright': 2016 Marcel MeXzigue 

Patterns follow the Perl regular expression rules.

Please also verify debian/copyright specifies the correct license, and if it doesn’t, submit a patch to Debian.

The license statement should be used when the license scan fails to determine a license (reporting it as as UNKNOWN), while override-license should be used when it detects an incorrect license declared inside a file.

It is important to note that ci-license-scan tries to resolve unknown licenses with metadata from different sources, in some cases this leads to files appearing with an incorrect license. However, since the license is not declared inside the file, but filled from other sources, using the license keyword is enough.

Another common issue that falls into this type are files under debian/patches which inherit the license of debian folder. This is usually an upstream bug in debian/copyright which should be reported since the expectation is that patches respect the license of the file they patch. However, since upstream could take some time to fix the issue, an entry for debian/patches should be added with a license keyword pointing to the correct information.

2. Add the file to the list of ignored files, debian/apertis/copyright.whitelist. This file is formatted the same way as gitignore, please refer to the gitignore manpage for more information.

This approach is useful in cases where a file has a license unsuitable for Apertis but which won’t affect the license of the generated image. A common example of this situation are the tests that run at build time, which will not be part of the binary package.

Another example for this type of exceptions are files that are not meant to be installed in images such as

• Documentation, which is excluded thanks to dpkg exclusions
• Sample programs or tools part of -dev packages which are used as build dependencies
3. If the file is under a license not suitable for Apertis, it can be removed from the package by either repackaging the tarball or patching it out, in which case the scanner will not take it into account.

The license scanner will store the automatically generated copyright report file under debian/apertis/copyright, updating the merge request when necessary.

## Custom Pipelines

When using the packaging pipeline, developers cannot put their CI/CD automation in .gitlab-ci.yml anymore, as the CI config path points to the ci-package-builder definition.

However, developers can put their jobs in the debian/apertis/local-gitlab-ci.yml file and have them executed in a child pipeline whenever the main packaging pipeline is executed. This is specially handy to run tests before the actual packaging process begins.

The packaging pipeline will automatically rebuild the packages on OBS when a new pipeline is triggered. This is enabled by default since users may not have OBS access. To disable this behaviour, set the CI/CD environment variable OBS_REBUILD_DISABLED (i.e. to any value) when triggering a new pipeline.