ci: build container images for all active Fedora releases#29
ci: build container images for all active Fedora releases#29
Conversation
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>
Reviewer's GuideAdds 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 FarmsequenceDiagram
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
Sequence diagram for weekly refresh of tmt/build.fmf and workflow matrixsequenceDiagram
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
Flow diagram for generate-build-fmf.py generation and sync processflowchart 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"]
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The
update_container_ymlfunction assumes the matrix markers always exist and usesstr.index, which will raise a genericValueErrorif 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 ofbase64 -w0may be GNU-specific; to make the build more portable and robust against differentbase64implementations, consider using a POSIX-compatible pattern likebase64 | 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| 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) |
There was a problem hiding this comment.
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.
| 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) |
mfocko
left a comment
There was a problem hiding this comment.
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?
| 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) |
There was a problem hiding this comment.
| @@ -0,0 +1,35 @@ | |||
| summary: Build and push nix-toolbox container images for all active Fedora releases. | |||
There was a problem hiding this comment.
I'd add this to .gitattributes:
+tmt/build.fmf linguist-generated| echo "value=${{ matrix.version }}" >> $GITHUB_OUTPUT | ||
| fi | ||
|
|
||
| - name: Schedule build via Testing Farm |
There was a problem hiding this comment.
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…
| - version: "42" | ||
| plan: /tmt/build/f42 | ||
| - version: "43" | ||
| plan: /tmt/build/f43 | ||
| - version: rawhide | ||
| plan: /tmt/build/rawhide |
There was a problem hiding this comment.
It feels like this should be templated too
| commit-message: | | ||
| tmt: regenerate build.fmf for current Fedora releases | ||
|
|
||
| Assisted-by: Claude Code |
There was a problem hiding this comment.
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 |
| 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) |
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, renderstmt/build.fmffrom a Jinja2 template, and keeps thecontainer.ymlmatrix in sync automaticallytmt/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 tomain, PRs, and weekly; PR builds push topr-<number>-<version>tags to avoid collisions with stable tags.github/workflows/refresh-build-fmf.yml— weekly regeneration oftmt/build.fmfandcontainer.ymlmatrix via Bodhi API, opens a PR if anything changedMakefile— addsgenerate/build-fmftarget; updates defaultFEDORA_VERSIONfrom 42 → 43pyproject.toml— addsjinja2andrequestsdependenciesPrerequisites
Add
TESTING_FARM_API_TOKENsecret to the repository settings.GITHUB_TOKENis 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:
Enhancements:
Build:
CI: