ELNBuildSync (EBS) automatically rebuilds Fedora Rawhide packages for Fedora ELN (Enterprise Linux Next). It listens for Koji tagging events on the Fedora Messaging Bus, batches work to account for side-tag merges, rebuilds packages in isolated Koji side-tags, and publishes consolidated Bodhi updates for testing.
When a package is tagged into Rawhide (after passing Fedora QA gating), EBS checks whether it is targeted for ELN or ELN Extras. Eligible packages are enqueued for the next rebuild batch. In order to ensure that all packages that need to be are processed as part of the same batch, batches are designed not to begin until a minimal lull timeout has passed.
While a batch is running, EBS stops accepting new Fedora Messages, re-queueing them for later processing. When the batch finishes, message processing resumes and a new batch can start. Batches never run in parallel: the next batch may depend on buildroots produced by the previous one.
The following describes what EBS does for each batch. The implementation
lives primarily in elnbuildsync/batching.py,
elnbuildsync/rebuildbatch.py, elnbuildsync/rebuildbatchslice.py, and
elnbuildsync/rebuildattempt.py.
| Step | What happens |
|---|---|
| Listen | listener.message_handler receives buildsys.tag messages. |
| Filter | config.is_eligible() applies include/exclude and mappings. |
| Queue | Eligible messages go into an in-memory message_queue. |
| Lull | batching.process_message_batch drains the queue after lull. |
| Serialize | If batching.running, new triggers are Nack'd until done. |
RebuildBatch creates a build side-tag derived from the ELN buildroot:
- Resolve the ELN target's parent and destination tags via Koji
(
kojihelpers.tags). - Create a new side-tag and tag most Rawhide builds into it (subject to
control.skip_tag—packages whose Rawhide builds must not enter the ELN buildroot, e.g. LLVM, OCAML,fedora-release). - Wait for Koji to regenerate the buildroot (
buildsys.repo.init/buildsys.repo.donemessages, with periodic tag polling as a fallback).
This approach reuses successful Rawhide builds in the buildroot so ELN rebuilds avoid most bootstrap and ordering problems.
Packages in the batch are grouped into batch slices by
control.ordering in the configuration (default order 1000; lower
numbers build first, e.g. llvm at 0).
For each slice, RebuildBatchSlice:
- Starts a rebuild attempt: all packages in the slice are submitted to Koji concurrently, using the SCM URL from the Rawhide build that triggered the tag (so dist-git drift after the Rawhide build does not change what gets built).
- Waits for tasks to complete via
buildsys.task.state.changemessages, with a periodiclistener.check_tasks()poll because Koji does not always emit AMQP events reliably. - Retries failures in new rebuild attempts until the failure count stops decreasing (the same set of failures twice in a row is treated as legitimate build breakage).
After all slices succeed or exhaust retries:
- Successful build NVRs are collected from task completion messages.
- For non-scratch builds, EBS creates a separate errata side-tag and tags only the new ELN builds into it (not the Rawhide builds used to seed the build side-tag).
BodhiClientsubmits one Bodhi update per errata tag (large batches may be split usingbodhi.batch_sizein configuration).- EBS waits for builds to appear in the configured
stable_tag, then removes the build side-tag.
Scratch builds (used in local testing) skip Bodhi submission and tagging.
The batch is marked complete in PostgreSQL, batching.running is cleared,
and queued Fedora Messages can be processed again.
EBS is a long-running Twisted application using the asyncio
reactor (elnbuildsync/daemon.py). Blocking or threaded work (Koji
calls, Bodhi submission, Git operations) is delegated via deferToThread
and related helpers; Fedora Messaging integrates through
fedora_messaging.api.twisted_consume.
┌─────────────────────────────────────────────────────────────────────────┐
│ elnbuildsync (daemon) │
├─────────────────────────────────────────────────────────────────────────┤
│ Fedora Messaging ──► listener.py ──► message_queue ──► batching.py │
│ ▲ │ │ │
│ │ ├── repo init/done ▼ │
│ │ ├── task state change RebuildBatch │
│ │ └── tag (trigger/await) │ │
│ │ ▼ │
│ HTTP :8080 ◄── web.py (status, trigger, OIDC) RebuildBatchSlice │
│ RebuildAttempt │
│ PostgreSQL ◄── db_models.py (batches, slices, tasks, sessions) │
│ Koji / Bodhi ◄── kojihelpers/ + bodhi-client │
│ Config YAML ◄── config.py (file, URL, or git checkout) │
└─────────────────────────────────────────────────────────────────────────┘
| Module | Responsibility |
|---|---|
daemon.py |
CLI entry; DB, schedulers, messaging, HTTP. |
listener.py |
Fedora Messages; task/tag waiters; polling. |
batching.py |
Message queue, lull timer, manual rebuild helper. |
rebuildbatch.py |
Side-tag lifecycle, slices, Bodhi updates. |
rebuildbatchslice.py |
Per-ordering slice execution and retries. |
rebuildattempt.py |
Koji build submission and per-task tracking. |
rebuildtask.py |
Individual rebuild task state. |
tagmessage.py |
Tag messages; SCM URLs and DB records. |
kojihelpers/ |
Koji connection pooling, tags, builds, errors. |
config.py |
YAML load/refresh; eligibility and ordering. |
db_models.py |
SQLAlchemy models for batches and sessions. |
web.py |
Health, status, /trigger, OIDC login/logout. |
status.py |
Periodic status page generation. |
cleanup.py |
Periodic cleanup of stale state. |
email.py |
SMTP notifications (e.g. build failures). |
auth.py |
OpenID Connect sessions for admin endpoints. |
When the daemon is running (including under tests/local_test_daemon.sh),
port 8080 exposes:
| Path | Purpose |
|---|---|
/alive, /startup |
Liveness / startup probes |
/status.html, /status.json |
Operational status |
/trigger |
Manually queue rebuilds (OIDC when configured) |
/login, /logout, /oidc/* |
OpenID Connect authentication flow |
Runtime behavior is driven by a YAML file (production: distrobuildsync-config
repository; local testing: tests/local_testconfig.yaml). Important sections:
configuration.koji: Koji profile, trigger tag, build target, stable tag, scratch/fail-fast flags.configuration.control:skip_tag,exclude,ordering, pause flag, status interval.configuration.bodhi: Maximum builds per Bodhi update (batch_size;0means no splitting).configuration.db: PostgreSQL connection settings.configuration.open_id_connect: OIDC settings for/trigger(tinystage in the sample test config).components: Autopackagelist resolver and per-package overrides.
| Path | Purpose |
|---|---|
Dockerfile / run.sh |
Container image and entrypoint. |
helm_charts/ |
Kubernetes/OpenShift deployment. |
requirements.txt |
Python dependencies |
Local development runs EBS in a Podman container against Fedora
Messaging, a PostgreSQL test database, and Koji (via Kerberos
credentials from your workstation). The primary workflow is
tests/local_test_daemon.sh.
Install development packages on a Fedora workstation:
sudo dnf install \
podman \
postgresql \
python3 \
python3-pip \
python3-devel \
koji \
krb5-workstation \
git \
rpm-develYou also need:
-
A Fedora account with permission to run builds against the Koji targets referenced in your test configuration (the sample config uses staging-oriented settings and
scratch_build: true). -
Kerberos credentials for Koji. EBS inside the container uses the host's KCM socket:
kinit your_fedora_username@FEDORAPROJECT.ORG koji hello
-
Test secrets under
tests/secrets/. At minimum:File Purpose tests/secrets/ebs_db_pwPostgreSQL password (one line) tests/secrets/ebs_smtp_pwSMTP password (one line) These may be overridden with
--db-pw-fileand--smtp-pw-filewhen callingtests/local_test_daemon.sh. -
Fedora Messaging certificates are vendored under
tests/fedora-messaging/(seetests/fedora-messaging/README.md).
Optionally, edit tests/local_testconfig.yaml for your environment (Koji
tags, OIDC client credentials for /trigger, package lists). The default
file points at tinystage for OIDC; register a client at
tiny-stage if you need
authenticated triggering. It can be disabled by setting
open_id_connect: false.
From the repository root:
./tests/local_test_daemon.sh --build-containerThis builds localhost/elnbuildsync:local_test_daemon from the project
Dockerfile. You only need to repeat this after dependency or image
changes (or pass --build-container again).
./tests/local_test_daemon.shThe script:
- Ensures Python dependencies are installed (
pip install -r requirements.txtand editable install of this tree). - Creates a Podman network
ebs_local_test. - Starts a temporary PostgreSQL 18 container (
temp_postgres) unless a persistent one is already reachable on port 5432. - Runs the EBS container with:
tests/local_testconfig.yamlmounted via--config-file- Secrets mounted at
/etc/elnbuildsync/ - Fedora Messaging staging config (
--environment stg, the default) - Port 8080 published for the web UI and APIs
- A short lull time of 5 seconds (production uses 60)
Logs are written to /tmp/elnbuildsync.log and echoed to the terminal.
Useful options:
./tests/local_test_daemon.sh --help
# More verbose logging
./tests/local_test_daemon.sh --log-level DEBUG
# Shorter or longer batch coalescing window
./tests/local_test_daemon.sh --lull-time 10
# Keep database data between runs
./tests/local_test_daemon.sh --persistent-db \
--persistent-db-path tests/persistent_db
# Use production Fedora Messaging broker config (instead of staging)
./tests/local_test_daemon.sh --environment prodWhen you stop the script (Ctrl+C), the ephemeral PostgreSQL container is
removed unless you used --persistent-db.
- Confirm
koji helloworks on the host before starting the daemon. - After startup, open http://localhost:8080/status.html.
- Watch
/tmp/elnbuildsync.logfor batch activity when Rawhide tag messages arrive on the staging broker, or use/trigger(with OIDC if configured) to queue specific components.
Install test dependencies and run pytest from the repository root:
pip install -e '.[test]'
pytestConfiguration parsing tests live in tests/test_parse_config.py; other
modules have targeted tests under tests/.
For deployment-style testing on OpenShift Local, use
tests/openshift_test_daemon.sh, which builds the image, pushes to a
cluster registry, and installs using the Helm chart. That path requires a
service keytab and is intended for integration testing rather than
day-to-day code changes.
Production deployments load configuration from the
elnbuildsync-config
git repository (see run.sh --config-url), mount database and SMTP secrets at
/etc/elnbuildsync/, and use a service keytab for
eln-buildsync@FEDORAPROJECT.ORG. See helm_charts/ for Kubernetes
resources.
GPL-3.0-or-later. See LICENSE.