The Alpiq BESS org keeps its GitLab CI in a shared component repo, not in each project’s .gitlab-ci.yml. Consumer pipelines (e.g. bess-optimization) are ~40 lines of include: component: + inputs:; every job body lives in the component library. Discovered 2026-08-05 by inspecting GitLab directly.

For Agents

Repo path: alpiq_cicd/sales-and-origination/flexible-assets/bess/poc/bess-os-ci-components Tags: v1.0.0 .. v1.4.0 (semantic-release via .releaserc.json) Components live at templates/<name>/template.yml; consumed as component: $CI_SERVER_FQDN/<path>/<name>@v1.x.0 This is the Python/data-science side of BESS CI. The Rust mando pipeline does NOT use it — mando hand-rolls its jobs.

Component inventory

ComponentRole
mr-jira-checkMR gate: Jira key present/valid
python-setup”Dynamic Env Variable Setup” — derives all version/ECR/release vars, exports via dotenv
python-testpytest job (junit XML, no docker services)
python-docker-publishbuild + push image to ECR
release-notesrelease metadata / changelog
simulator-pipelinesimulator child pipeline

Each takes a spec: inputs: block, so consumers pass parameters instead of overriding YAML. python-test inputs seen in the wild: stage, pre_test_script, extra_apt_packages, timeout.

How the data-driven test CI actually works

This is the machinery behind the cheap 7.4-min/54-case and 13.8-min/68-case suites.

python-setup — the dotenv broadcast pattern

One job derives everything version-shaped from the branch name + pyproject.toml, then hands it to every downstream job:

  • PROJECT_VERSION derivation by ref:
    • main / release/*BASE_VERSION+<pipelineIID>.<shortsha>
    • developBASE_VERSION-dev+<pipelineIID>.<shortsha>
    • feature/* / bugfix/* → ref-slug suffixed
  • ECR repository name and release metadata derived the same way
  • Everything exported through:
artifacts:
  reports:
    dotenv: build.env

Downstream jobs inherit the vars through needs: — no repeated shell, no duplicated version logic. This is the single most reusable idea in the repo.

python-test — why the suites are cheap

The suites are fast because nothing is compiled and nothing is containerized

  • Plain runner, NO services: — zero docker sidecars. bess-optimization runs with DB_DISABLED=true, so the tests are pure data-in/data-out.
  • poetry install from the Nexus PyPI mirror pulls a prebuilt py-mando wheel — zero Rust compilation in the test job. That is the whole cost story.
  • pre_test_script input is the extension hook (e.g. installing the Gurobi license).
  • pytest --junitxml, then junitparser merge, published with:
artifacts:
  when: always
  reports:
    junit: <merged>.xml

when: always is load-bearing: a failing suite still uploads its report, so GitLab renders per-case failures in the MR widget instead of just “job failed”.

Branch rules

Consistent across components: MR events, develop, rc/*, main, release/*. Feature branches generally do not run the publish-shaped jobs (cf. the optional: true gotcha in be-1595-publish-docker-dev-feature-branch-test-need-2026-06-16 and the rules-override hatch in be-1595-arrow-consumer-lock-script-2026-06-24).

Versioned pinning

Consumers pin @v1.0.0-style refs, so a component bump is an explicit consumer commit. The repo itself is released by semantic-release (.releaserc.json), which is where the v1.0.0..v1.4.0 tag range comes from.

Relevance to mando-cli’s e2e job

mando-cli-e2e-harness-2026-08-04 needs a CI job, planned to live in mando’s pipeline. It is a hybrid of two shapes:

Borrowed fromWhat
bess-os-ci-components / python-testconsume a prebuilt artifact rather than compiling; artifacts: when: always + reports: junit: so failures are readable in the MR
mando’s .integration-test-linuxservices: sidecars — the e2e harness needs wiremock + postgres, which the pure data-driven Python suites deliberately have none of

The component library cannot be copied wholesale for e2e

python-test is explicitly service-less. The e2e harness’s whole point is a live stack (mando up → migrate → seed → flow run --bundle → verify), so the sidecar half must come from mando’s own integration job. Only the artifact-consumption and junit-always halves transfer.

Long-term option: package the e2e job as a new bess-os-ci-components component (e.g. mando-e2e) with spec: inputs: for suite path, image tag, and timeout — so other BESS repos can run mando e2e suites without copy-pasting job YAML. Not decided.