Skip to content

mender-docker-lifecycle-helper

GitHub Release PyPI - Version GitHub Actions Workflow Status codecov Dive Docker efficiency pre-commit Commitlint Conventional Commits Python Black

Management tool for Mender Docker application workloads in local and CI development iteration and release deployment processes.

Documentation

See the full documentation at https://rcwbr.github.io/mender-docker-lifecycle-helper/latest

Overview

mender-docker-lifecycle-helper demo

The tool is a wrapper around Mender's mender-artifact tool plus upload and deployment of its produced artifact via Mender server. It generates artifacts similarly to the app-gen tool, but with inference of artifact details from the execution context.

In other words, use mender-docker-lifecycle-helper if you are deploying Docker artifacts with Mender, but don't want to think about the version and dependency metadata to specify every time you run docker build and just want to see what it does live.

The artifact generation extracts the images and their versions from the provided Docker compose manifest, and compares these with the same from the previously-generated artifact to infer whether the images must be included in the artifact or if a dependency on previous artifact may be used instead. Specifically, each image in the manifest is included in the artifact only if the image or its version are changed relative to the artifact most recently uploaded from the environment (in the case of local development iteration) or relative to the manifest at the previous semantic version release Git tag of the repo.

The mechanism by which the previous semantic version tag of the repo may be inferred depends on the versioning model used. It is recommended to use the release-it-gh-workflow with the release-it-docker file bumper image, as this automates establishment of the required conditions, namely that releases trigger a bump of the VERSION file in the repo root as a commit to main, from which the tag is created. Also expected is that an artifact is published with version specified as the contents of VERSION file, on the commits that bump that version. It is recommended to automate this using the reusable workflow from this repo.

mender-docker-lifecycle-helper-launcher

The mender-docker-lifecycle-helper-launcher script is a convenience wrapper around the containerized tool for the appropriate volume mounts and run options. It may be used directly from this repo using wget:

mender-docker-lifecycle-helper-launcher usage

To run the mender-docker-lifecycle-helper-launcher script, and thus launch a Docker container of the tool, run:

wget -qO - https://raw.githubusercontent.com/rcwbr/mender-docker-lifecycle-helper/refs/tags/1.2.2/mender-docker-lifecycle-helper-launcher | bash -s -- --help

mender-docker-lifecycle-helper-setup

The mender-docker-lifecycle-helper-setup script sets up a local script equivalent to the launcher, thus "installing" the tool to the CLI and enabling shell autocompletion (thanks to Click). It may be used directly from this repo using wget:

mender-docker-lifecycle-helper-setup usage

To run the mender-docker-lifecycle-helper-setup script, and thus launch a Docker container of the tool, run the following command in the location in which the script should reside:

wget -qO - https://raw.githubusercontent.com/rcwbr/mender-docker-lifecycle-helper/refs/tags/1.2.2/mender-docker-lifecycle-helper-setup >mender-docker-lifecycle-helper && bash mender-docker-lifecycle-helper setup

This will download the setup script, which will unpack itself into the launcher script and the Zsh completion script. It will prompt to add to the ~/.zshrc:

Add the following to ~/.zshrc to complete setup:

. /your/download/path/mender-docker-lifecycle-helper_comp
export PATH=/your/download/path:$PATH

mender-docker-lifecycle-helper container

The mender-docker-lifecycle-helper tool is released as a Docker image under this repo. It may be launched directly as a container, as opposed to likewise via the launcher, for greater control.

mender-docker-lifecycle-helper container usage

To launch a mender-docker-lifecycle-helper container, run:

docker run \
    --rm \
    -it \
    --name mender-docker-lifecycle-helper \
    -e MENDER_PAT \
    -e MENDER_HELPER_CACHE_DIR=/mender-helper-cache \
    -e DOCKER_CONFIG_JSON="$(cat ~/.docker/config.json)" \
    -v mender-helper-cache:/mender-helper-cache \
    -v "$(pwd):$(pwd)" \
    -v /var/run/docker.sock:/var/run/docker.sock \
    ghcr.io/rcwbr/mender-docker-lifecycle-helper:1.2.2 \
    --help

mender-docker-lifecycle-helper core

The core implementation of the mender-docker-lifecycle-helper tool is a Python CLI script that wraps Mender's mender-artifact tool and requests to the Mender Server Management API. For details of the context provided to mender-artifact, see mender-docker-lifecycle-helper - mender-artifact contexts.

mender-docker-lifecycle-helper core usage

To execute the core script directly, run the following from an environment in which the package is installed:

mender-docker-lifecycle-helper --help

mender-docker-lifecycle-helper inputs

The environment variable inputs to the mender-docker-lifecycle-helper tool are as follows:

Variable Default Effect
DOCKER_CONFIG_JSON N/A Configuration in the ~/.docker/config.json format, used to configure and authenticate Docker operations (if provided)
MENDER_PAT N/A Mender Server Personal Access Token used to authenticate the artifact upload and deployment creation.
MENDER_HELPER_CACHE_DIR ${XDG_CACHE_HOME}/mender-docker-lifecycle-helper or ~/.cache/mender-docker-lifecycle-helper The cache dir to which the metadata for the previously uploaded artifact is saved. May be overridden by the --cache-dir flag.

The CLI flag inputs are as follows:

Flag Default Effect
-a, --artifact-filename None Name of the artifact file to create. Default: <manifest-name>-<previous-version>+<current repo commit SHA>+<UUID>.mender
--cache-dir ${XDG_CACHE_HOME}/mender-docker-lifecycle-helper if defined, else ~/.cache/mender-docker-lifecycle-helper The cache dir to which the metadata for the previously uploaded artifact is saved. Overrides the MENDER_HELPER_CACHE_DIR variable.
--no-cache False Skip reading previous artifact info from cache and always read from the repo at the previous version.
--delta True Generate the artifact as an update artifact, if applicable.
--device-type N/A Device type for the artifact (required).
--device-group None Device group to which to deploy the artifact, or skip deployment if not defined.
-l, --log-level INFO Set logging level. One of "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL".
--manifest-name <dirname of repo containing manifest_file>-<dirname directly containing manifest_file> The application/software name for the artifact.
--mender-host https://hosted.mender.io Mender host URL for artifact upload and deployment.
--platform N/A Platform with which the artifact is compatible (required, e.g., linux/arm/v7).
--previous-version Read from VERSION file Repo ref from which to read image names and versions for comparison to the current state.
--release False Create the artifact for a release, using the current value of the VERSION file as the artifact version and the value of the VERSION file at the previous commit as the --previous-version.
-f, --service-file None Image file to extract and use to override the image for the specified service in the manifest_file, as --service-file <SERVICE NAME> <IMAGE FILE> (can be specified multiple times).
-i, --service-image None Image name override for the specified service in the manifest_file, as --service-image <SERVICE NAME> <IMAGE NAME> (can be specified multiple times).
-v, --verbose 0 Increase verbosity by one level (see --log-level). Can be specified multiple times (e.g., -vv).
[manifest_file] N/A (Required) Path to the manifest file for which to generate the artifact (e.g., docker-compose.yaml).

Additionally, the --version flag is available to display the tool version, and --help to display usage.

mender-docker-lifecycle-helper outputs

The outputs of the tool are inferred from the execution context, with the intention of producing the optimal artifact for that context. To that purpose, the version and depends for each artifact are set according to the following conditions:

Constant attributes:

Attribute Value
Software name <name of the repository dir>-<name of the dir containing manifest_file>
Base version <value of VERSION file>

Conditional attributes:

Condition Depends Software Name Version
No cache present <software name>.version:<base version> <software name> <base version>+<Git commit short SHA>+<uuid>
Cache is present <software name>.version:<version from cache> <software name> <base version>+<Git commit short SHA>+<uuid>
--release true <software name>.version:<value of VERSION file at the previous commit> <software name> <base version>

In this way, each artifact is generated as a delta relative to the most recent artifact known to relate to the current context.

mender-docker-lifecycle-helper understanding delta artifacts

When the --delta flag is set to false, all images from the manifest are included in full in the artifact, and the artifact is configured to have no dependency on another artifact.

Otherwise, any image included in the artifact is included as a delta, and conditionally based on recent artifacts. The hashes of images are read from the local daemon image store, if available, or from its registry otherwise. If metadata from a previous helper local execution is available, then an image is included in the artifact if its current hash differs from that in the previous execution metadata, and the artifact is configured to depend on that created by the previous execution. If such metadata is not available, then an image is included in the artifact if its current hash differs from that of the image per the manifest as of the repo version in the VERSION file (or the --previous-version arg, if provided).

mender-docker-lifecycle-helper - mender-artifact contexts

The context (args and files) provided to the mender-artifact call is computed from several inputs to the mender-docker-lifecycle-helper invocation. These include the CLI options and environment variables (see inputs), as well as local files, including cache from previous executions of the tool and the repo VERSION file. This mapping is the core logic of the tool.

mender-artifact previous version conditions

The previous version of the artifact (used for delta artifact generation, see mender-artifact delta artifact conditions) is specified differently for certain contexts. The value is as specified in this table, in order of priority.

Condition Previous version value Purpose
--previous-version flag specified Flag value Custom delta artifact generation
--release flag set Contents of VERSION file at commit HEAD~1 On release commits (i.e. that bump the VERSION file contents), generate an artifact relative to the previous release
Otherwise Contents of the VERSION file in the current state For non-releases, generate an artifact relative to the most recent release
mender-artifact delta artifact conditions

When generating a delta artifact, the context provided to mender-artifact is described by this table:

mender-artifact input Cache matches Cache behind No cache
The images field of the file provided to --meta-data contains ID of <compose file>.<service 1>.image, ID of <compose file>.<service 2>.image... ID of <compose file>.<service 1>.image, ID of <compose file>.<service 2>.image... ID of <compose file>.<service 1>.image, ID of <compose file>.<service 2>.image...
The image hash in each <images>/sums-new.txt belongs to the services defined in this version of the Compose file Current repo state Current repo state Current repo state
The image ref in each <images>/url-new.txt belongs to the services defined in this version of the Compose file Current repo state Current repo state Current repo state
The image hash in each <images>/sums-current.txt belongs to the services defined in this version of the Compose file Latest cache state (same as current repo state) Latest cache state Repo state at previous version
The image ref in each <images>/url-current.txt belongs to the services defined in this version of the Compose file Latest cache state (same as current repo state) Latest cache state Repo state at previous version
--depends rootfs-image.<repo name><manifest name>.version: version ID Cache previous artifact version ID Cache previous artifact version ID Previous version

Alternative installation via PyPi

The mender-docker-lifecycle-helper core tool can be installed directly from PyPi using pip:

pip install mender-docker-lifecycle-helper

This method requires the following system packages to be available:

Package Version
mender-artifact 3.9.0
jq 1.6
tree 2.1.0
xdelta3 3.0.11
skopeo 1.22.0

Additionally, the following configuration must be present in /etc/containers/policy.json:

{
  "default": [
    {
      "type": "insecureAcceptAnything"
    }
  ],
  "transports": {
    "docker-daemon": {
      "": [
        {
          "type": "insecureAcceptAnything"
        }
      ]
    }
  }
}

mender-docker-lifecycle-helper GitHub Actions workflow

This repo provides a GitHub Actions reusable workflow to apply the containerized tool via CI. It expects semantic versioning via a VERSION file and Git tags, per the release-it-gh-workflow process, which informs its use of the --release arg.

It generates and (optionally) deploys Mender artifacts of the specified target. The artifact is generated as --release=false and --delta=true, unless the ref for the workflow is a tag, in which case one artifact is generated with --release=true --delta=true and one with --release=true --delta=false. In this way, each release introduces a new baseline artifact that devices may install from scratch, but iterations on branches are fast and lightweight.

mender-docker-lifecycle-helper GitHub Actions workflow usage

To upload artifacts and trigger deployments on Mender Server, the workflow must be provided with a Mender Personal Access Token as an Actions Secret. To create the token, log on to Mender Server (e.g. https://hosted.mender.io/), and navigate to User > Settings > My profile > Personal access token management > Generate a token. Copy the generated token, then navigate to GitHub repository Settings > Security > Secrets and variables > Actions > New repository secret, and paste as the secret value. The name of the secret must be provided as the mender-pat-secret input to the workflow (see below).

Workflow basic usage:

on: push
jobs:
  mender-docker-lifecycle-helper:
    uses: 
      rcwbr/mender-docker-lifecycle-helper/.github/workflows/mender-docker-lifecycle-helper.yaml@1.2.2
    with:
      manifest-file: <path to docker-compose.yaml>
      device-type: <device type>
      platform: <platform>
    secrets:
      mender-pat-secret: ${{ secrets.<Actions secret name> }}

With the above configuration, the artifact will be uploaded but not deployed. To also deploy the artifact, specify a device group to target for the deployment:

on: push
jobs:
  mender-docker-lifecycle-helper:
    uses: rcwbr/mender-docker-lifecycle-helper/.github/workflows/mender-docker-lifecycle-helper.yaml@1.2.2
    with:
      device-group: <device group>
      ...

Most often, the tool should be integrated into the CI workflow such that it publishes an artifact including the image just generated by the workflow. To achieve this, the contents of a compose manifest in source may be overridden using the service-images input with a value set to the output of a previous job. For example, the CI automation for this repo builds an image in a job named build-docker-images, and uses output from that job as the service-images override for the artifact it generates

on: push
jobs:
  build-docker-images:
    ...
  mender-docker-lifecycle-helper:
    uses: rcwbr/mender-docker-lifecycle-helper/.github/workflows/mender-docker-lifecycle-helper.yaml@1.2.2
    with:
      ...
      service-images: ${{ format('["mdlh {0}"]', fromJSON(needs.build-docker-images.outputs.mender-docker-lifecycle-helper).uv-project['image.name']) }}

To be more precise, it overrides this way unless the workflow is for a Git tag (indicating a release). In that case, it does not override, as the .release-it.json configures automated version bump in the example compose file, meaning that the ref in that file at any release tag is already pointing to the same image version just built by the workflow. This configuration looks like this:

on: push
jobs:
  build-docker-images:
    ...
  mender-docker-lifecycle-helper:
    uses: rcwbr/mender-docker-lifecycle-helper/.github/workflows/mender-docker-lifecycle-helper.yaml@1.2.2
    with:
      ...
      service-images: ${{ github.ref_type == 'tag' && '' || format('["mdlh {0}"]', fromJSON(needs.build-docker-images.outputs.mender-docker-lifecycle-helper).uv-project['image.name']) }}

Similarly, the service-files input may be used to override service images with local image files (e.g., .tar files). This is useful when the workflow builds or downloads image files that should be included in the artifact:

on: push
jobs:
  build-docker-images:
    ...
  mender-docker-lifecycle-helper:
    uses: rcwbr/mender-docker-lifecycle-helper/.github/workflows/mender-docker-lifecycle-helper.yaml@1.2.2
    with:
      ...
      service-files: '["web /path/to/web-image.tar", "api /path/to/api-image.tar"]'

mender-docker-lifecycle-helper GitHub Actions workflow inputs

The full inputs for the workflow are as follows:

Input Required Default Type Effect
device-group '' string Device group to which to deploy the artifact, or skip deployment if not defined.
device-type N/A string Device type for the artifact.
manifest-file N/A string Path to the manifest file for which to generate the artifact (e.g., docker-compose.yaml).
mender-host '' string Mender host URL for artifact upload and deployment.
platform N/A string Platform with which the artifact is compatible (e.g., linux/arm/v7).
service-files '' string Image file overrides for services in the manifest_file, as a JSON array [" ", " ", ...].
service-images '' string Image name overrides for services in the manifest_file, as a JSON array [" ", " ", ...].
helper-image 'ghcr.io/rcwbr/mender-docker-lifecycle-helper:1.2.2' string Docker image to use as mender-docker-lifecycle-helper.
secrets.mender-pat-secret N/A secret Secret that contains the Mender server Personal Access Token to use for artifact upload and deployment.

Contributing

devcontainer

This repo contains a devcontainer definition in the .devcontainer folder. It leverages the devcontainer cache build tool and layers defined in the dockerfile-partials repo.

devcontainer basic usage

The devcontainer cache build tool requires authentication to the GitHub package registry, as a token stored as MENDER_DOCKER_LIFECYCLE_HELPER_DEVCONTAINER_INITIALIZE (see instructions).

devcontainer Codespaces usage

For use with Codespaces, the MENDER_DOCKER_LIFECYCLE_HELPER_DEVCONTAINER_INITIALIZE token (see devcontainer basic usage) must be stored as a Codespaces secret (see instructions), as must values for USER, and UID (see useradd Codespaces usage).

devcontainer pre-commit usage

By default, the devcontainer configures pre-commit hooks in the repository to ensure commits pass basic testing. This includes enforcing conventional commit messages as the standard for this repository, via commitlint.

CI/CD

This repo uses the release-it-gh-workflow, with the conventional-changelog image defined at any given ref, as its automation. It leverages the devcontainer-cache-build workflow to pre-generate devcontainer images, which are also used for the pre-commit workflow. Finally, a Dive Docker image efficiency analysis job first builds an image with all partials layers included, then analyses the storage efficiency of the resulting image.

Codecov

This repo uses Codecov for code coverage reporting, as uploaded by the CI/CD workflow. Access is configured using Codecov's GitHub App, and applying the Repository token value from Codecov as a Repository secret called CODECOV_TOKEN in Settings > Secrets and variables > Actions. Optionally, the coverage may be viewed using Codecov's browser extension.

In devcontainers (including Codespaces), the Coverage Gutters extension is enabled. It may be activated using the Coverage Gutters: Watch command, and have inputs populated using the Pytest task.

Settings

The GitHub repo settings for this repo are defined as code using the Probot settings GitHub App. Settings values are defined in the .github/settings.yml file. Enabling automation of settings via this file requires installing the app.

The settings applied are as recommended in the release-it-gh-workflow usage, including tag and branch protections, GitHub App and environment authentication, and required checks.