Environment

Overview

The environment is the central runtime context used by cikit commands and macros. It combines resolved project configuration with metadata provided by the active CI provider and exposes both through one stable interface.

cikit uses two environment layers:

  • CiEnvironment provides resolved configuration, provider metadata, path handling, and command argument expansion.

  • CiRunEnvironment extends CiEnvironment with a logical run, cikit data access, timeline queries, and artifact management.

This separation keeps generic configuration independent from operations that belong to a specific logical run.

CiEnvironment

CiEnvironment resolves the configured EnvVar definitions once during construction and stores the resulting values as a stable environment snapshot.

Typical usage:

from cikit.env import CiEnvironment, get_env_vars

env = CiEnvironment(get_env_vars())

project_root = env["PROJECT_ROOT"]
public_dir = env["PUBLIC_DIR"]

Required values are accessed with env[key]. Missing required values raise a cikit error. Optional access is available through env.get():

base_url = env.get("CIKIT_DATA_BASE_URL")

The environment also owns the active provider object and exposes commonly used pipeline and Git metadata through convenience properties.

Variable Resolution

Environment variables are described by EnvVar definitions. A definition contains the variable name, documentation, an optional default value, a target type, and an optional secret flag.

Example:

EnvVar(
    "PUBLIC_DIR",
    "Base directory for all generated CI artifacts.",
    "${PROJECT_ROOT}/public",
    value_type=Path,
)

Variables are resolved in declaration order.

For every variable, cikit performs the following steps:

  1. Use the value from the operating system environment when it is explicitly set.

  2. Otherwise use the configured default value.

  3. Evaluate callable defaults at resolution time.

  4. Expand ${VAR} placeholders using variables that have already been resolved.

  5. Convert the final value using the configured value_type.

The declaration order is therefore significant. A variable may reference a previously resolved variable, but not one that appears later in the definition list.

For example:

PROJECT_ROOT=/workspace/project
PUBLIC_DIR=${PROJECT_ROOT}/public

results in:

PUBLIC_DIR=/workspace/project/public

Standard Variables

The default cikit environment currently defines:

Variable

Purpose

PROJECT_NAME

Logical project name resolved from project metadata.

PROJECT_FILE

Path to the active project metadata file.

PYTHON_VERSION

Active Python major and minor version.

EXEC_TOOL

Optional command prefix used to execute project tools. Defaults to uv run.

PROJECT_ROOT

Root directory of the current project.

PUBLIC_DIR

Base directory for persistent generated output.

CIKIT_DATA_DIR

Directory containing local cikit data and run-related metadata.

CIKIT_DATA_BASE_URL

Base URL of the optional remote cikit data service.

CIKIT_DATA_API_TOKEN

Authentication token for the optional remote cikit data service.

Directories such as badges, rst, or artifacts are not separate environment variables. Artifact destinations are derived from PUBLIC_DIR and the registered artifact type.

Secret Values

An EnvVar can be marked as secret. Secret values remain available for actual command execution but are masked in user-facing environment output.

For example, env.as_dict() masks secrets by default:

CIKIT_DATA_API_TOKEN = ***

Command argument expansion follows the same principle. cikit keeps one fully-resolved argument vector for process execution and a second display-safe representation with secret values masked.

This allows commands to use credentials without exposing them in reports or error messages.

Provider Metadata

Provider metadata is separate from the resolved environment variables.

The active provider is available through:

env.provider

CiEnvironment exposes commonly used provider values directly:

env.pipeline_id
env.pipeline_alias
env.branch_name
env.commit_sha
env.commit_short_sha
env.commit_title
env.commit_message
env.commit_author
env.commit_timestamp

The active provider is detected automatically by CiEnvironment unless a provider instance is passed explicitly to the constructor.

This gives commands and macros the same interface when running locally, in GitLab CI, or in Azure DevOps.

Provider-specific behavior and metadata are described in Providers.

Path Resolution

resolve_path() expands environment placeholders and resolves relative paths against PROJECT_ROOT.

report_file = env.resolve_path("${PUBLIC_DIR}/artifacts/report.xml")
source_dir = env.resolve_path("src")

Absolute paths are preserved. Relative paths become project-relative paths.

Unknown template variables raise an error instead of silently producing an invalid filesystem path.

Resolved Path values in the environment should normally be absolute. If environment initialization leaves relative Path values, cikit emits a warning because their behavior would depend on the current working directory.

Command Arguments

The environment can also resolve command arguments through env.argv().

The command definition is expanded using the environment snapshot, and EXEC_TOOL is prepended when requested by the command.

Conceptually:

command definition
     |
     +-- environment expansion
     |
     +-- optional EXEC_TOOL prefix
     |
     +-- executable argv
     +-- masked display argv

This keeps subprocess execution and logging based on the same resolved configuration while preventing secret values from leaking into user-facing output.

CiRunEnvironment

Most actual CI commands operate on CiRunEnvironment.

CiRunEnvironment inherits all behavior from CiEnvironment and adds a logical run context:

from cikit.env import CiRunEnvironment, get_env_vars

env = CiRunEnvironment(
    get_env_vars(),
    run_name="build",
)

The logical run name is available through:

env.name

A logical run identifies related cikit data and artifacts. Different commands or workflow stages may therefore use different run names while sharing the same project and provider environment.

Run Data

CiRunEnvironment binds a RunContext to the logical run.

Run data can be stored through:

env.data_set("build_time", 4.2)

env.data_update({
    "warnings": 0,
    "errors": 0,
})

The locally cached data of the current run is available through:

data = env.data

Historical data can be queried with:

from cikit.data import RefKey

timeline = env.timeline(
    ["build_time", "warnings"],
    ref_key=RefKey.INDEX_DESC,
    limit=20,
)

The environment only provides the run-bound access layer. Cache behavior, remote synchronization, addressing, and timeline semantics are described in Cikit Data.

Artifact Access

CiRunEnvironment also owns an ArtifactRegistry.

Generated files can be copied into their persistent public location and registered for the current run:

from cikit.artifacts import ArtifactType

artifact = env.copy_artifact(
    "build/firmware.bin",
    ArtifactType.BUILD,
)

Artifacts can be queried across runs:

artifacts = env.get_artifacts(artifact_type=ArtifactType.BUILD)

or restricted to the active logical run:

artifacts = env.get_run_artifacts(ArtifactType.BUILD)

The artifact registry is the integration mechanism used by independent cikit features. Producers register artifacts, while consumers such as reports or the Sphinx integration query the artifact types they support.

Artifact types, storage, lookup behavior, and persistent registry semantics are described in Artifacts.

Output Directory Preparation

The standard project output root is:

public/

prepare_dirs() clears the configured PUBLIC_DIR and recreates the required base directories:

public/
`-- cikit_data/

Additional artifact directories are created on demand when artifacts are copied and registered. Depending on the artifact type, they are placed below directories such as:

public/
|-- artifacts/
|-- badges/
`-- rst/

This keeps directory layout decisions inside the artifact layer instead of exposing a separate environment variable for every output category.

Environment and Run Responsibilities

The two environment layers have intentionally different responsibilities:

Capability

CiEnvironment

CiRunEnvironment

Resolved configuration

Yes

Yes

Provider metadata

Yes

Yes

Path resolution

Yes

Yes

Command argument expansion

Yes

Yes

Logical run name

No

Yes

cikit run data

No

Yes

Timeline queries

No

Yes

Artifact registry

No

Yes

As a rule, infrastructure that only needs project configuration or provider metadata can operate on CiEnvironment. Commands and macros that create metrics, timelines, reports, or artifacts normally require CiRunEnvironment.

See Also