Skip to content

ci: build container images for all active Fedora releases#29

Open
thrix wants to merge 1 commit intomainfrom
build-all-fedora-releases
Open

ci: build container images for all active Fedora releases#29
thrix wants to merge 1 commit intomainfrom
build-all-fedora-releases

Conversation

@thrix
Copy link
Owner

@thrix thrix commented Mar 14, 2026

Summary

Add automated CI container builds via Testing Farm covering all active Fedora releases plus rawhide.

  • scripts/generate-build-fmf.py — queries Bodhi API for current Fedora releases, renders tmt/build.fmf from a Jinja2 template, and keeps the container.yml matrix in sync automatically
  • tmt/build.fmf.j2 / tmt/build.fmf — fmf plan covering F42, F43, and rawhide; each version is a separate plan section (/tmt/build/f42, etc.)
  • .github/workflows/container.yml — matrix build (one Testing Farm request per version) triggered on push to main, PRs, and weekly; PR builds push to pr-<number>-<version> tags to avoid collisions with stable tags
  • .github/workflows/refresh-build-fmf.yml — weekly regeneration of tmt/build.fmf and container.yml matrix via Bodhi API, opens a PR if anything changed
  • Makefile — adds generate/build-fmf target; updates default FEDORA_VERSION from 42 → 43
  • pyproject.toml — adds jinja2 and requests dependencies

Prerequisites

Add TESTING_FARM_API_TOKEN secret to the repository settings. GITHUB_TOKEN is used automatically for ghcr.io push auth.

Assisted-by: Claude Code

Summary by Sourcery

Automate building and refreshing container images for all active Fedora releases using Testing Farm and tmt plans.

New Features:

  • Introduce a Testing Farm-based container build workflow that builds and pushes images for multiple Fedora versions and rawhide on pushes, pull requests, and a weekly schedule.
  • Add a scheduled workflow to regenerate tmt build plans and the CI matrix from the current set of active Fedora releases and open a pull request with changes.
  • Add a script to query the Bodhi API for active Fedora releases and generate tmt/build.fmf and the GitHub Actions matrix dynamically from a template.

Enhancements:

  • Update the default Fedora toolbox version used by the Makefile from 42 to 43 and add a make target to regenerate tmt/build.fmf.
  • Define a tmt plan template and generated plan for building and pushing container images per active Fedora release and rawhide.
  • Add Jinja2 and Requests as project dependencies to support dynamic generation of CI configuration.

Build:

  • Set up automated container image builds for all active Fedora releases and rawhide via Testing Farm, driven by tmt plans and GitHub Actions.

CI:

  • Configure a matrix-based GitHub Actions workflow to schedule Testing Farm container builds per Fedora version and rawhide and tag images appropriately for main and pull request builds.
  • Add a GitHub Actions workflow to periodically refresh the Fedora build configuration and create pull requests with any updates.

Add automated CI container builds via Testing Farm covering all active
Fedora releases plus rawhide, triggered on push to main, PRs, and
weekly schedule.

- Add scripts/generate-build-fmf.py to query the Bodhi API for current
  Fedora releases and render tmt/build.fmf from a Jinja2 template; also
  keeps the container.yml matrix in sync automatically
- Add tmt/build.fmf.j2 template and generated tmt/build.fmf covering
  F42, F43, and rawhide
- Add .github/workflows/container.yml: matrix build per version via
  testing-farm CLI; PR builds push to pr-<number>-<version> tags to
  avoid collisions with stable tags
- Add .github/workflows/refresh-build-fmf.yml: weekly regeneration of
  tmt/build.fmf and container.yml matrix, opens a PR if changed
- Add generate/build-fmf Makefile target
- Update default FEDORA_VERSION from 42 to 43
- Add jinja2 and requests dependencies to pyproject.toml

Requires TESTING_FARM_API_TOKEN secret to be set in the repository.

Assisted-by: Claude Code
Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
@sourcery-ai
Copy link

sourcery-ai bot commented Mar 14, 2026

Reviewer's Guide

Adds automated CI container builds via Testing Farm for all active Fedora releases (discovered dynamically from Bodhi), including a generated tmt plan and GitHub matrix workflows that stay in sync via a Python generator script and a scheduled refresh job.

Sequence diagram for container build workflow via Testing Farm

sequenceDiagram
  actor Dev as Developer
  participant GH as GitHub
  participant WF as Workflow_container_yml
  participant JB as job_build
  participant TFCon as testing_farm_cli_container
  participant TF as Testing_Farm
  participant TMT as tmt_build_fmf
  participant GHCR as ghcr_io_registry

  Dev->>GH: push to main or open PR
  GH->>WF: trigger workflow on event
  WF->>JB: start job_build with matrix versions

  loop for each matrix_version
    JB->>JB: compute tag (pr_number_version or version)
    JB->>TFCon: run testing_farm request
    TFCon->>TF: submit request with plan from matrix
    TF->>TMT: load plan for version or rawhide
    TF->>TF: provision container build environment
    TF->>GHCR: push built image with computed tag
  end
Loading

Sequence diagram for weekly refresh of tmt/build.fmf and workflow matrix

sequenceDiagram
  participant Sched as GitHub_schedule
  participant WF as Workflow_refresh_build_fmf_yml
  participant JR as job_refresh
  participant Repo as Repository
  participant Py as generate_build_fmf_py
  participant Bodhi as Bodhi_API
  participant PRAction as create_pull_request_action

  Sched->>WF: cron trigger (weekly)
  WF->>JR: start job_refresh
  JR->>Repo: actions_checkout
  JR->>JR: setup Python and uv and make
  JR->>Py: make generate_build_fmf
  Py->>Bodhi: GET current releases
  Bodhi-->>Py: JSON releases list
  Py->>Py: derive versions and render template
  Py->>Repo: write tmt/build.fmf and update container.yml matrix
  JR->>PRAction: create_pull_request if changes
  PRAction->>Repo: open PR branch refresh_build_fmf
Loading

Flow diagram for generate-build-fmf.py generation and sync process

flowchart TD
  A["Start generate_build_fmf_py"] --> B["Call Bodhi_API for current releases"]
  B --> C{"HTTP request successful"}
  C -->|no| Z["Exit with error: no releases or request failed"]
  C -->|yes| D["Parse JSON and extract Fedora F versions"]
  D --> E["Sort versions list"]
  E --> F{"versions list empty"}
  F -->|yes| Z
  F -->|no| G["Read tmt/build_fmf_j2 template"]
  G --> H["Render template with versions"]
  H --> I["Write tmt/build_fmf"]
  I --> J["Read .github/workflows/container.yml"]
  J --> K["Locate matrix include block between MATRIX_START and MATRIX_END"]
  K --> L["Render matrix include with versions plus rawhide"]
  L --> M["Replace old matrix block with new matrix"]
  M --> N["Write updated container.yml"]
  N --> O["Finish generate_build_fmf_py"]
Loading

File-Level Changes

Change Details Files
Introduce Python generator script to derive active Fedora releases from Bodhi and use them to generate tmt build plans and update the GitHub Actions container matrix.
  • Add scripts/generate-build-fmf.py that calls the Bodhi API, extracts Fedora release numbers, renders tmt/build.fmf from a Jinja2 template, and rewrites the container.yml matrix include list.
  • Define constants and simple helpers to locate repo paths, identify the matrix block in container.yml, and render per-version entries including rawhide.
scripts/generate-build-fmf.py
Define templated and generated tmt build plans for container image builds across all active Fedora versions plus rawhide.
  • Create tmt/build.fmf.j2 template with common build configuration and per-version sub-plans that set FEDORA_VERSION and TARGET_IMAGE appropriately.
  • Add the generated tmt/build.fmf checked-in output corresponding to current active Fedora releases and rawhide.
tmt/build.fmf.j2
tmt/build.fmf
Add GitHub Actions workflow to trigger Testing Farm container builds using a version/plan matrix and safe image tagging for PRs vs main.
  • Create .github/workflows/container.yml with a matrix over Fedora versions and rawhide, using the Testing Farm CLI container to request builds with appropriate environment variables.
  • Compute image tags differently for PRs (pr--) and non-PR events (version), pushing to ghcr.io with auth derived from GITHUB_TOKEN.
  • Configure triggers for pushes to main, pull requests, a weekly schedule, and manual dispatch.
.github/workflows/container.yml
Add GitHub Actions workflow to periodically regenerate tmt/build.fmf and the container matrix and open an automated PR when they change.
  • Create .github/workflows/refresh-build-fmf.yml to run weekly or on demand, installing Python and uv, then invoking make generate/build-fmf.
  • Use peter-evans/create-pull-request to commit and open a PR containing regenerated tmt/build.fmf and container.yml matrix updates.
.github/workflows/refresh-build-fmf.yml
Extend project tooling to support build plan generation and update Fedora default version.
  • Add generate/build-fmf Makefile target that runs the generator script via uv.
  • Bump default FEDORA_VERSION from 42 to 43 for local tooling and builds.
Makefile
Declare new Python dependencies required by the generator script and refresh the lockfile.
  • Add jinja2 and requests to the runtime dependencies in pyproject.toml to support template rendering and HTTP calls to Bodhi.
  • Update uv.lock to capture the resolved dependency versions.
pyproject.toml
uv.lock

Possibly linked issues

  • #Build all Fedora releases: PR implements automated container builds for all active Fedora releases, directly addressing building all Fedora releases.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The update_container_yml function assumes the matrix markers always exist and uses str.index, which will raise a generic ValueError if they don’t; consider explicitly checking for the markers and failing with a clearer, more actionable error message.
  • In .github/workflows/container.yml, the use of base64 -w0 may be GNU-specific; to make the build more portable and robust against different base64 implementations, consider using a POSIX-compatible pattern like base64 | tr -d '\n' instead.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `update_container_yml` function assumes the matrix markers always exist and uses `str.index`, which will raise a generic `ValueError` if they don’t; consider explicitly checking for the markers and failing with a clearer, more actionable error message.
- In `.github/workflows/container.yml`, the use of `base64 -w0` may be GNU-specific; to make the build more portable and robust against different `base64` implementations, consider using a POSIX-compatible pattern like `base64 | tr -d '\n'` instead.

## Individual Comments

### Comment 1
<location path="scripts/generate-build-fmf.py" line_range="22-34" />
<code_context>
+MATRIX_END = "\n    container:\n"
+
+
+def get_active_fedora_versions() -> list[int]:
+    response = requests.get(BODHI_URL, timeout=30)
+    response.raise_for_status()
+    releases = response.json().get("releases", [])
+
+    versions = []
+    for release in releases:
+        name = release.get("name", "")
+        match = re.fullmatch(r"F(\d+)", name)
+        if match:
+            versions.append(int(match.group(1)))
+
+    return sorted(versions)
+
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Network/API failures from Bodhi will currently propagate as unhandled exceptions.

Any `requests`-level issue (DNS failure, timeout, connection error, non-JSON response) will currently surface as an unhandled exception. Consider catching `requests.exceptions.RequestException` (and possibly `ValueError` from `response.json()`) and exiting with a clear error message to `stderr`, similar to how you handle the `not versions` case, so CI logs are easier to interpret.

```suggestion
def get_active_fedora_versions() -> list[int]:
    try:
        response = requests.get(BODHI_URL, timeout=30)
        response.raise_for_status()
    except requests.exceptions.RequestException as exc:
        print(f"Failed to query Bodhi for active Fedora releases: {exc}", file=sys.stderr)
        sys.exit(1)

    try:
        releases = response.json().get("releases", [])
    except ValueError as exc:
        print(f"Failed to parse Bodhi response as JSON: {exc}", file=sys.stderr)
        sys.exit(1)

    versions: list[int] = []
    for release in releases:
        name = release.get("name", "")
        match = re.fullmatch(r"F(\d+)", name)
        if match:
            versions.append(int(match.group(1)))

    return sorted(versions)
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +22 to +34
def get_active_fedora_versions() -> list[int]:
response = requests.get(BODHI_URL, timeout=30)
response.raise_for_status()
releases = response.json().get("releases", [])

versions = []
for release in releases:
name = release.get("name", "")
match = re.fullmatch(r"F(\d+)", name)
if match:
versions.append(int(match.group(1)))

return sorted(versions)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Network/API failures from Bodhi will currently propagate as unhandled exceptions.

Any requests-level issue (DNS failure, timeout, connection error, non-JSON response) will currently surface as an unhandled exception. Consider catching requests.exceptions.RequestException (and possibly ValueError from response.json()) and exiting with a clear error message to stderr, similar to how you handle the not versions case, so CI logs are easier to interpret.

Suggested change
def get_active_fedora_versions() -> list[int]:
response = requests.get(BODHI_URL, timeout=30)
response.raise_for_status()
releases = response.json().get("releases", [])
versions = []
for release in releases:
name = release.get("name", "")
match = re.fullmatch(r"F(\d+)", name)
if match:
versions.append(int(match.group(1)))
return sorted(versions)
def get_active_fedora_versions() -> list[int]:
try:
response = requests.get(BODHI_URL, timeout=30)
response.raise_for_status()
except requests.exceptions.RequestException as exc:
print(f"Failed to query Bodhi for active Fedora releases: {exc}", file=sys.stderr)
sys.exit(1)
try:
releases = response.json().get("releases", [])
except ValueError as exc:
print(f"Failed to parse Bodhi response as JSON: {exc}", file=sys.stderr)
sys.exit(1)
versions: list[int] = []
for release in releases:
name = release.get("name", "")
match = re.fullmatch(r"F(\d+)", name)
if match:
versions.append(int(match.group(1)))
return sorted(versions)

Copy link

@mfocko mfocko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO tmt plan for building a container image is a pretty heavy hammer… is there any particular reason why you cannot use just Containerfile and GH Action workflow?

Comment on lines +22 to +34
def get_active_fedora_versions() -> list[int]:
response = requests.get(BODHI_URL, timeout=30)
response.raise_for_status()
releases = response.json().get("releases", [])

versions = []
for release in releases:
name = release.get("name", "")
match = re.fullmatch(r"F(\d+)", name)
if match:
versions.append(int(match.group(1)))

return sorted(versions)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -0,0 +1,35 @@
summary: Build and push nix-toolbox container images for all active Fedora releases.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add this to .gitattributes:

+tmt/build.fmf linguist-generated

echo "value=${{ matrix.version }}" >> $GITHUB_OUTPUT
fi

- name: Schedule build via Testing Farm
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a bit wasteful… Cause you basically occupy both GH Action worker and VM on Testing Farm… With the GH worker busy waiting for the Testing Farm to finish…

Comment on lines +24 to +29
- version: "42"
plan: /tmt/build/f42
- version: "43"
plan: /tmt/build/f43
- version: rawhide
plan: /tmt/build/rawhide
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like this should be templated too

commit-message: |
tmt: regenerate build.fmf for current Fedora releases

Assisted-by: Claude Code
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script for templating was assisted by Claude I assume. I doubt it's running in the GitHub workflow, is it?

body: |
Automated weekly refresh of `tmt/build.fmf` based on active Fedora releases from the Bodhi API.

Generated-by: Claude Code
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar here

Comment on lines +37 to +44
def render_matrix_include(versions: list[int]) -> str:
lines = [" include:\n"]
for version in versions:
lines.append(f' - version: "{version}"\n')
lines.append(f" plan: /tmt/build/f{version}\n")
lines.append(' - version: rawhide\n')
lines.append(' plan: /tmt/build/rawhide\n')
return "".join(lines)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh OK, so it is actually regenerated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants