Skip to content

CI: per-package test logs, parallel runs, and uboot OOM fix#815

Open
mangelajo wants to merge 8 commits into
mainfrom
ci-test-infra
Open

CI: per-package test logs, parallel runs, and uboot OOM fix#815
mangelajo wants to merge 8 commits into
mainfrom
ci-test-infra

Conversation

@mangelajo

Copy link
Copy Markdown
Member

Summary

  • Per-package test log capture: When LOGS_DIR is set, each pkg-test-% target captures stdout/stderr to a separate .log file via tee (visible during run). Failures are tracked with .failed markers based on exit code. A test-report target prints full logs for failed packages. Local dev behavior is unchanged.
  • fail-fast: false: All matrix jobs (runner × Python version) run to completion even when one fails.
  • Parallel test runs: make test -j4 runs package tests concurrently.
  • Log noise suppression: --log-level=CRITICAL --log-cli-level=CRITICAL in CI-only PYTEST_ADDOPTS.
  • Artifact upload: Per-package logs uploaded as GitHub Actions artifacts (7-day retention).
  • uboot test OOM fix: rpmfile decompresses the entire 31MB zstd RPM into 572MB of memory to extract a 1.6MB u-boot.bin. Under parallel runs on resource-constrained VMs this triggers the OOM killer. Now uses streaming rpm2cpio | cpio on Linux, falling back to rpmfile when unavailable.

Test plan

  • All matrix jobs complete (none cancelled by fail-fast)
  • Per-package .log files appear in uploaded artifacts
  • Failed packages show full output in the test-report section
  • uboot test passes without hanging on extraction
  • Local make test still works without LOGS_DIR

🤖 Generated with Claude Code

mangelajo and others added 2 commits June 19, 2026 19:55
Makefile: When LOGS_DIR is set, pkg-test-% captures stdout/stderr to
per-package log files via tee (visible during run), records failures
with .failed markers (exit-code based), and test-report prints full
logs for failed packages. Local dev behavior is unchanged.

CI: Set fail-fast: false so all matrix jobs complete, run tests with
-j4 parallelism, suppress log noise with --log-level/--log-cli-level
CRITICAL, and upload per-package logs as artifacts (7-day retention).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rpmfile decompresses the entire zstd CPIO payload (572MB) into memory
to extract a 1.6MB u-boot.bin. Under parallel test runs this triggers
the OOM killer. Use streaming rpm2cpio + cpio on Linux; fall back to
rpmfile when those tools are unavailable (macOS).

Also adds download timeout (120s), progress logging, and proper error
reporting to the fixture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds LOGS_DIR-driven log capture and a test-report target to python/Makefile, enabling per-package test logs and deferred failure reporting. The GitHub Actions workflow gains fail-fast: false, parallel make test -j4, and unconditional artifact upload of test logs. The U-Boot driver test fixture is hardened with streaming RPM download and dual-path extraction (rpm2cpio|cpio or rpmfile). Coverage patterns are updated to use recursive glob syntax.

Changes

CI Test Infrastructure: Log Capture and Parallel Execution

Layer / File(s) Summary
Makefile: LOGS_DIR log capture and test-report target
python/Makefile
pkg-test-% gains a LOGS_DIR branch that tees pytest output to a per-package log, writes a .failed marker on failure, and returns success to Make. New test-report target scans .failed markers, prints logs, and exits 1 on any failure. test conditionally depends on test-report; test-report added to .PHONY.
GitHub Actions: fail-fast, parallel make test, artifact upload
.github/workflows/python-tests.yaml
pytest-matrix strategy sets fail-fast: false. Linux Qemu installation step adds rpm2cpio and cpio packages. Test step runs make test -j4 with LOGS_DIR=${{ runner.temp }}/test-logs and expanded PYTEST_ADDOPTS. A new always()-gated step uploads the logs directory as a matrix-keyed artifact with 7-day retention.

U-Boot Driver Test Fixture Hardening

Layer / File(s) Summary
uboot\_image fixture: streaming download and dual-path RPM extraction
python/packages/jumpstarter-driver-uboot/jumpstarter_driver_uboot/driver_test.py
Adds shutil, subprocess imports and UBOOT_RPM_URL constant. Fixture downloads RPM with streaming + timeout + raise_for_status, extracts u-boot.bin via rpm2cpio|cpio subprocess pipeline when tools are on PATH, otherwise falls back to rpmfile, then yields the binary path.

Coverage Configuration

Layer / File(s) Summary
Coverage omit patterns
python/pyproject.toml
Coverage omit patterns updated from non-recursive filenames/globs (e.g., conftest.py, test_*.py) to recursive **/-prefixed patterns (e.g., **/conftest.py, **/test_*.py) to match files anywhere in the project tree.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • NickCao

Poem

🐇 Hoppity-hop through the CI lane,
Logs now captured when tests complain,
Four jobs in parallel, none left behind,
fail-fast: false — so gentle and kind.
The U-Boot fixture downloads with care,
A fallback for rpm2cpio not there! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately summarizes the three main changes: per-package test logs, parallel runs (fail-fast: false), and the uboot OOM fix.
Description check ✅ Passed The pull request description comprehensively explains all changes including per-package logging, parallel execution, artifact upload, and the uboot OOM fix with rationale.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci-test-infra

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
python/packages/jumpstarter-driver-uboot/jumpstarter_driver_uboot/driver_test.py (1)

40-46: 💤 Low value

Static analysis false positive, but consider defensive quoting.

The static analysis tools flagged this as command injection, but this is a false positive—rpm_path is constructed entirely from pytest's tmpdir_factory, not from user input or external requests. The path is deterministic and controlled.

That said, using shlex.quote() is a low-effort defensive practice that protects against edge cases (unusual temp paths, future refactoring):

🛡️ Defensive improvement
+import shlex
+
 ...
 
     if shutil.which("rpm2cpio") and shutil.which("cpio"):
         subprocess.run(
-            f"rpm2cpio {rpm_path} | cpio -idm --quiet ./usr/share/uboot/qemu_arm64/u-boot.bin",
+            f"rpm2cpio {shlex.quote(str(rpm_path))} | cpio -idm --quiet ./usr/share/uboot/qemu_arm64/u-boot.bin",
             shell=True, cwd=str(tmp_path), check=True,
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/packages/jumpstarter-driver-uboot/jumpstarter_driver_uboot/driver_test.py`
around lines 40 - 46, The subprocess.run command in the conditional block that
checks for rpm2cpio and cpio directly interpolates the rpm_path variable into
the shell command string without proper quoting, which could be vulnerable to
command injection in edge cases. Apply shlex.quote() around the rpm_path
variable when constructing the command string in the subprocess.run call to add
defensive quoting. Import shlex at the top of the file if not already present,
then wrap rpm_path with shlex.quote(str(rpm_path)) in the f-string command to
protect against special characters or unusual paths in the temporary directory.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@python/packages/jumpstarter-driver-uboot/jumpstarter_driver_uboot/driver_test.py`:
- Around line 40-46: The subprocess.run command in the conditional block that
checks for rpm2cpio and cpio directly interpolates the rpm_path variable into
the shell command string without proper quoting, which could be vulnerable to
command injection in edge cases. Apply shlex.quote() around the rpm_path
variable when constructing the command string in the subprocess.run call to add
defensive quoting. Import shlex at the top of the file if not already present,
then wrap rpm_path with shlex.quote(str(rpm_path)) in the f-string command to
protect against special characters or unusual paths in the temporary directory.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d5b90d84-67c6-4a0d-b779-cfd4c6cb63fb

📥 Commits

Reviewing files that changed from the base of the PR and between c6b0748 and 84a6cb7.

📒 Files selected for processing (3)
  • .github/workflows/python-tests.yaml
  • python/Makefile
  • python/packages/jumpstarter-driver-uboot/jumpstarter_driver_uboot/driver_test.py

working-directory: python
env:
PYTEST_ADDOPTS: "--cov-report=xml"
PYTEST_ADDOPTS: "--cov-report=xml --log-level=CRITICAL --log-cli-level=CRITICAL"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This reduces a lot of unnecessary noise on the tests without completely disabling logs.

for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)

with rpmfile.open(tmp_path / "uboot-images-armv8.rpm") as rpm:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this was extracting the RPM to RAM, then extracting the file, not very optimal, and in parallelization used a lot of ram.

mangelajo and others added 2 commits June 19, 2026 20:03
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…).st_size

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread .github/workflows/python-tests.yaml Outdated
if: needs.changes.outputs.should_run == 'true' || github.event_name == 'workflow_dispatch'
runs-on: ${{ matrix.runs-on }}
strategy:
fail-fast: false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

isnt it likely that an error in one matrix job will also surface on others hence failing fast kinda makes sense.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes and no, I wanted to try without it, but may be it's better to leave it on. For example I was having a lot of mac failures and that stopped the linux ones where I could not see if it was going to fail or not as well... or the other way around.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Putting it back, we should probably discuss that separately, or disable on a need basis.

from jumpstarter.common.utils import serve


UBOOT_RPM_URL = "https://kojipkgs.fedoraproject.org/packages/uboot-tools/2025.10/1.fc43/noarch/uboot-images-armv8-2025.10-1.fc43.noarch.rpm"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Again, this is not an image we include in the containers, or the product, It's just used to test a real uboot on QEMU and properly validate the uboot driver.

mangelajo and others added 4 commits June 19, 2026 23:31
Fix import sorting (ruff I001) and replace pytest.fail() with
raise AssertionError to satisfy ty's call-non-callable check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The except block (network failure) and rpmfile fallback (no rpm2cpio)
are not exercised in normal CI runs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The omit patterns lacked ** prefixes, so test files like
package/driver_test.py were measured for coverage. Add ** glob
prefixes so conftest.py, test_*.py, *_test.py, *_pb2.py, and
*_pb2_grpc.py are excluded at any depth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread python/pyproject.toml
[tool.coverage.run]
branch = true
omit = ["conftest.py", "test_*.py", "*_test.py", "*_pb2.py", "*_pb2_grpc.py"]
omit = ["**/conftest.py", "**/test_*.py", "**/*_test.py", "**/*_pb2.py", "**/*_pb2_grpc.py"]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Apparently coverage was complaining for my _test.py file increased python surface. Which unveiled that we probably have better coverage than we thought, and that this may need patters to pickup the subpackages test files. I am not sure if each subpackage may need replication of this.

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