Macros

Overview

The cikit.macros package provides reusable building blocks for project-specific CI commands. Macros cover common tasks such as linting, testing, coverage generation, build execution, report generation, packaging, and documentation builds.

Run-bound macros operate on a CiRunEnvironment so generated metrics and artifacts belong to an explicit logical run. Project-specific command modules should primarily handle command-line arguments and orchestration while reusable tool integration remains inside the macros.

Dependency Model

cikit intentionally keeps its own runtime dependency set small. Tool-specific dependencies are not installed automatically just because a corresponding macro exists.

Projects must provide the tools required by the macros they use. Depending on the tool, this may mean adding a Python package to the project environment, installing a system executable, or providing a project-specific toolchain.

The current macro dependencies are:

Macro

Dependency

Notes

common

rsync

Required only by compare_dirs().

ruff

ruff

Required for linting and GitLab Code Quality output.

pylint

pylint

Required for normal Pylint execution.

pylint

pylint-gitlab

Required only for GitLab Code Quality output.

pytest

pytest

Required for normal test execution.

pytest

coverage

Required only when coverage_dir is used.

coverage

coverage

Required by pycoverage().

coverage

gcovr and a compatible gcov tool

Required by ccoverage(). The coverage tool must match the compiler used to build the instrumented code, e.g. gcov for GCC or llvm-cov gcov for Clang.

cmake

cmake >= 3.20 / ctest

Required for CMake builds, File API compiler metadata, and CTest execution.

cmake

Selected generator

For example, generator="Ninja" requires ninja.

platformio

pio

Provided by PlatformIO Core.

sphinx

sphinx-build

Provided by Sphinx.

reports

None

Uses cikit functionality and the Python standard library.

bundle

None

ZIP and TAR creation use the Python standard library.

Python-based tools can normally be kept in a project dependency group, for example:

[dependency-groups]
dev = [
    "ruff>=0.15",
    "pylint~=4.0",
    "pylint-gitlab~=2.1",
    "pytest>=9",
    "coverage>=7.14",
    "gcovr",
    "sphinx",
    "platformio",
]

System tools such as CMake, Ninja, or rsync should be installed by the development environment or CI image when the corresponding functionality is used.

Common Helpers

The common module contains shared helpers used by project commands and other macros.

Typical responsibilities include:

  • environment initialization

  • cikit data preparation

  • timeline creation

  • standard project badges

  • directory comparison

  • name and label resolution

A run-bound environment can be created with:

from cikit.macros.common import init_env

env = init_env(run_name="ruff")

Without run_name, init_env() returns a generic CiEnvironment.

data_prepare() initializes the configured cikit data backend for the current pipeline. chart_timeline() retrieves historical run data and returns a configurable TimelineChart before rendering.

Directory comparison is available through compare_dirs(). This helper executes rsync in dry-run mode and therefore requires the rsync executable.

Ruff

The ruff macro executes Ruff checks and integrates their results with cikit.

Dependencies

The ruff executable is required.

Features

  • JSON lint report generation

  • GitLab Code Quality output

  • cikit data updates

  • lint artifact registration

  • badge generation

  • RST report generation

  • timeline integration

Typical usage

from cikit.macros import ruff

ruff.ruff(env, ["src", "tests"])
ruff.badge(env)
ruff.write_rst(env)

GitLab Code Quality output can be generated separately with ruff.gitlab_code_quality(). Ruff provides the GitLab output format directly, so no additional reporter package is required.

Pylint

The pylint macro executes Pylint checks and converts the resulting diagnostics into cikit reports.

Dependencies

Normal operation requires pylint.

GitLab Code Quality output additionally requires pylint-gitlab because the macro uses its GitlabCodeClimateReporter reporter.

Features

  • JSON lint report generation

  • GitLab Code Quality output

  • cikit data updates

  • lint artifact registration

  • badge generation

  • RST report generation

  • timeline integration

  • rule-specific filtered reports

Typical usage

from pathlib import Path

from cikit.macros import pylint

pylint.pylint(env, ["src", "tests"], rcfile=Path(".pylintrc"))
pylint.badge(env)
pylint.write_rst(env)

Pytest

The pytest macro executes Python test suites and stores their JUnit results.

Dependencies

Normal test execution requires pytest.

When coverage_dir is provided, the test suite is executed through coverage run. This mode additionally requires coverage.

Features

  • pytest execution

  • JUnit XML generation

  • optional parallel coverage data collection

  • cikit data updates

  • XUnit artifact registration

Typical usage

from pathlib import Path

from cikit.macros import pytest

pytest.pytest(
    env,
    Path("tests/unittests"),
    test_alias="unittests",
)

To collect coverage data for later processing:

pytest.pytest(
    env,
    Path("tests/unittests"),
    test_alias="unittests",
    coverage_dir=Path("build/coverage"),
)

Coverage

The coverage macro generates coverage reports from previously collected coverage data.

Dependencies

The required tool depends on the coverage workflow:

  • pycoverage() requires coverage.

  • ccoverage() requires gcovr and a compatible gcov tool.

For GCC builds, gcovr uses gcov by default. The gcov version should match the GCC version used to compile the instrumented code. Clang-based builds can use the compatible llvm-cov gcov mode instead.

Features

  • Python coverage processing

  • C/C++ coverage processing with gcovr

  • Cobertura XML generation

  • HTML coverage reports

  • cikit data updates

  • artifact registration

  • coverage badges

  • RST report generation

  • timeline integration

Python coverage

Parallel coverage data generated by the pytest macro can be combined and rendered with:

from pathlib import Path

from cikit.macros import coverage

coverage.pycoverage(env, Path("build/coverage"))
coverage.badge(env)
coverage.write_rst(env)

C/C++ coverage

For C or C++ projects, gcovr can process coverage data from a build directory:

from pathlib import Path

from cikit.macros import coverage

coverage.ccoverage(env, Path("build"))

Generated HTML coverage output is registered as a folder artifact. Generated RST pages refer to registered artifacts rather than depending on the physical public output layout.

CMake

The cmake macro supports CMake configuration, builds, and CTest execution.

Dependencies

CMake 3.20 or newer providing cmake and ctest is required.

The selected CMake generator and project toolchain must also be available. For example, using generator="Ninja" requires the ninja executable. Compilers and linkers are project toolchain requirements rather than cikit dependencies.

Features

  • CMake configuration

  • optional clean configuration

  • CMake build execution

  • CMake version metadata

  • compiler metadata collection through the CMake File API

  • optional compile_commands.json generation and registration

  • optimization and LTO metadata from the compilation database

  • CTest execution

  • parallel execution

  • configure, build, and total build time metrics

  • compiled file counts

  • compiler warning and error counts

  • linker memory usage metrics

  • configure and build command logs

  • JUnit XML output

  • cikit data updates

  • artifact registration

  • RST build reports

Typical build usage

from pathlib import Path

from cikit.macros import cmake

build_dir = cmake.get_build_dir(env)

cmake.gather_cmake_meta(env)

cmake.cmake_configure(
    env,
    source_dir=Path("."),
    build_dir=build_dir,
    generator="Ninja",
    clean=True,
    compile_commands=True,
)

cmake.gather_compiler_meta(env, build_dir, "CXX")
cmake.cmake_build(env, build_dir)
cmake.write_rst(env)

CTest can also be executed independently on an already configured build directory:

cmake.ctest(
    env,
    build_dir,
    test_alias="ctest",
)

Build reports include the metrics and artifacts available for the current run. See Builds for the wider build and report workflow.

CMake and Compiler Metadata

CMake metadata can be collected independently from project configuration:

cmake.gather_cmake_meta(env)

This stores the version of the CMake executable as cmake_version.

After configuration, compiler metadata can be collected for a CMake language:

cmake.gather_compiler_meta(env, build_dir, "CXX")

The language must match a language enabled by the configured CMake project, for example C or CXX.

cikit uses the CMake File API toolchains-v1 reply generated during cmake_configure() and stores:

  • compiler

  • compiler_version

  • compiler_type

compiler_type contains the compiler ID reported by CMake, for example GNU or Clang.

When compile_commands=True is used and CMake generates the compilation database, it is registered as a METADATA artifact. During cmake_build(), recognized optimization and LTO options are exposed as optimization and lto metrics.

Missing, invalid, or unavailable toolchain metadata is reported as a CiToolsError. Compilation-database-derived metrics are optional when the database or recognized options are unavailable.

PlatformIO

The platformio macro builds PlatformIO projects and collects build and compiler metadata.

Dependencies

PlatformIO Core and its pio executable are required.

Platforms, frameworks, libraries, and toolchains configured through platformio.ini are managed by PlatformIO and are project requirements rather than direct cikit dependencies.

Features

  • compiler metadata collection

  • PlatformIO build execution

  • optional clean builds

  • build time metrics

  • compiled file counts

  • compiler warning and error counts

  • memory usage metrics

  • build command logs

  • cikit data updates

  • artifact registration

  • RST build reports

Typical usage

from pathlib import Path

from cikit.macros import platformio

platformio.gather_compiler_meta(env, project_dir=Path("."))
platformio.build(env, project_dir=Path("."), clean=True)
platformio.write_rst(env)

When project_dir is omitted, the project root from the environment is used.

gather_compiler_meta() asks PlatformIO for the resolved build environment and, when exactly one environment can be determined, stores available compiler information in the current run data:

  • compiler

  • compiler_version

  • compiler_type

The compiler executable is taken from PlatformIO metadata and queried for its version.

Reports

The reports module provides common report handling shared by test and build macros.

Dependencies

No additional tool dependency is required.

Features

  • shared JUnit result processing for pytest and CTest

  • XUnit artifact registration and result persistence

  • test result badges

  • test summary reports

  • build summary reports with available tool, compiler, build, and timing metadata

  • timeline integration

  • memory usage tables

  • build artifact and bundle tables with download links, file sizes, and SHA-256 hashes

  • collapsible command log integration

  • RST artifact registration

Typical test report usage:

from cikit.macros import reports

reports.badge(env)
reports.write_test_report(env)

Typical build report usage:

from cikit.macros import reports

reports.write_build_report(env, report_title="Build Report")

Build-specific macros such as cmake and platformio expose their own write_rst() wrappers and use the generic build report internally.

Bundle

The bundle macro creates distributable archives from a file or directory and registers the generated archive as a bundle artifact.

Dependencies

No additional dependency is required. ZIP, TAR, and TAR.GZ archives are created with the Python standard library.

Typical usage

from pathlib import Path

from cikit.macros import bundle

bundle.create(
    env,
    "firmware-package.zip",
    Path("build/release"),
)

Supported target formats are:

  • .zip

  • .tar

  • .tar.gz

Directory contents are placed directly in the archive root.

Sphinx

The sphinx macro prepares registered cikit artifacts for documentation and builds the final HTML documentation.

Dependencies

Sphinx and its sphinx-build executable are required.

Themes and additional Sphinx extensions used by the target documentation must be installed by the consuming project. They are not dependencies of the cikit Sphinx macro itself.

Features

  • generated RST report integration

  • badge integration

  • static artifact integration

  • folder artifact integration

  • HTML documentation builds

  • HTML folder artifact registration

  • cikit Sphinx extension integration

Typical usage

from pathlib import Path

from cikit.macros import sphinx

docs_dir = Path("docs")

sphinx.prepare(env, docs_dir)
html_artifact = sphinx.sphinx(env, docs_dir)

prepare() rebuilds the generated portions of the Sphinx source tree from registered cikit artifacts. RST artifacts are copied to docs/_generated while other artifact types are placed below docs/_static according to the common Sphinx artifact layout.

The documentation itself is built in a temporary directory. The completed HTML tree is then registered as a folder artifact.

See Sphinx Documentation Build for the complete documentation build workflow and Sphinx Extensions for the cikit-specific roles and directives.

Artifacts and Cikit Data

Most run-bound macros update the current run data and register generated artifacts. This allows independent pipeline steps to exchange reports and generated outputs without depending on fixed filesystem paths.

Typical artifact types produced by macros include:

  • badges

  • timeline charts

  • JUnit and Cobertura XML reports

  • lint reports

  • generated RST pages

  • HTML report folders

  • command logs

  • direct build outputs

  • bundles

Artifacts can later be consumed by reporting and Sphinx macros. Run-specific report helpers only include artifacts associated with the current logical run.

For the complete artifact model, see Artifacts. For persistent run data and timeline history, see Cikit Data.

Using Macros from Project Commands

Macros are intended to be orchestrated from project-local command modules loaded by cikit.ci. Command modules define project policy; macros provide reusable implementation.

A lint command can remain small:

from cikit.macros import common, pylint

env = common.init_env(run_name="pylint")

pylint.pylint(env, ["src", "tests"])
pylint.badge(env)
pylint.write_rst(env)

A CMake build command can compose several build helpers:

from pathlib import Path

from cikit.macros import cmake, common

env = common.init_env(run_name="build")
build_dir = cmake.get_build_dir(env)

cmake.gather_cmake_meta(env)

cmake.cmake_configure(
    env,
    source_dir=Path("."),
    build_dir=build_dir,
    generator="Ninja",
    clean=True,
)
cmake.gather_compiler_meta(env, build_dir, "CXX")
cmake.cmake_build(env, build_dir)
cmake.write_rst(env)

This separation keeps command-line handling and workflow policy inside the project while the reusable implementation remains in cikit.macros.

See Also