Skip to content

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

Merged
mangelajo merged 2 commits into
mainfrom
ci-test-infra
Jun 22, 2026
Merged

CI: per-package test logs, parallel runs, and uboot OOM fix#815
mangelajo merged 2 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

@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.

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.

Comment thread python/pyproject.toml Outdated
[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.

@mangelajo mangelajo enabled auto-merge (squash) June 22, 2026 06:41
mangelajo and others added 2 commits June 22, 2026 09:18
- Add LOGS_DIR support to Makefile: tee-based log capture, .failed
  marker files, and test-report target for failure summary
- Add PYTHONUNBUFFERED=1 for live output through tee pipeline
- Upload per-package test logs as GitHub Actions artifacts
- Exclude test files from diff-cover check
- Install rpm2cpio and cpio in Linux CI dependencies

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rpmfile decompresses the entire 572MB CPIO payload into memory. Use
rpm2cpio | cpio for streaming extraction with rpmfile as fallback.
Also adds download progress logging and a 120s timeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
exit 1
fi
uv run diff-cover $coverage_files --compare-branch=origin/${{ github.base_ref }} --fail-under=80 --exclude '*_pb2.py' '*_pb2_grpc.py' '**/conftest.py'
uv run diff-cover $coverage_files --compare-branch=origin/${{ github.base_ref }} --fail-under=80 --exclude '*_pb2.py' '*_pb2_grpc.py' '**/conftest.py' '**/test_*.py' '**/*_test.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.

We were considering test and test files as part of our coverage, who tests the test problem?

@mangelajo mangelajo merged commit 861e681 into main Jun 22, 2026
31 checks passed
@mangelajo mangelajo deleted the ci-test-infra branch June 22, 2026 09:05
evakhoni pushed a commit to evakhoni/jumpstarter that referenced this pull request Jun 24, 2026
…ter-dev#815)

- **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.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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