Skip to content

Generalize Boost/C++ patch pipeline behind config and plugin points (#69)#73

Merged
wpak-ai merged 3 commits into
developfrom
feat/generalize-patch-pipeline
Jul 9, 2026
Merged

Generalize Boost/C++ patch pipeline behind config and plugin points (#69)#73
wpak-ai merged 3 commits into
developfrom
feat/generalize-patch-pipeline

Conversation

@bradjin8

@bradjin8 bradjin8 commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Lift Boost/Capy-specific patch literals (boost-source/boost-root, Patch Boost, capy-root, b2-workflow, libs/$module, etc.) into patches.project (PatchProjectConfig) so other C++/GitHub Actions projects can retarget the existing steps without code changes.
  • Add patch_registry.py and patches.extra_steps so custom patch steps can register via the localci.patch_steps entry-point group instead of editing PATCH_STEP_REGISTRY.
  • Make repo_full_name and native_image_prefix configurable via ProjectConfig, wired through the orchestrator into ActCommandBuilder (replaces hardcoded cppalliance/capy and capy- defaults).
  • Boost.Capy backward compatibility is preserved: default .localci.yml behaviour is unchanged and existing patcher fixtures still pass.

Closes #69

Test plan

  • pytest — 593 tests pass
  • pre-commit run -a — ruff, format, mypy, yaml hooks pass
  • Existing patcher fixtures (cli/tests/fixtures/patcher/) patch identically with default config
  • New generic_cpp.yml fixture patches correctly with custom patches.project literals
  • Plugin step registration covered via extra_steps + registry monkeypatch test
  • ActCommandBuilder tests for configurable repo_full_name and native_image_prefix (x86 / linux/386 gating)

Summary by CodeRabbit

  • New Features

    • Added configurable workflow patching, including project-specific settings and support for custom patch steps.
    • Expanded the local CI guide with a new workflow patch pipeline section and examples.
  • Bug Fixes

    • Improved workflow patching for different project layouts and naming conventions.
    • Updated job handling so 32-bit container behavior is applied more consistently based on image naming.

@bradjin8 bradjin8 self-assigned this Jul 8, 2026
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR generalizes the previously Boost.Capy-specific patch pipeline. PatchesConfig gains project (a PatchProjectConfig/ProjectConfig) and extra_steps fields, patch steps read literals from project config instead of hardcoded strings, a new patch_registry module supports plugin-based step registration via entry points, and ActCommandBuilder/OrchestratorConfig accept configurable repo_full_name/native_image_prefix. Tests, fixtures, and docs are updated accordingly.

Changes

Configurable Patch Pipeline and Project Settings

Layer / File(s) Summary
Project config and patch registry
cli/localci/core/config.py, cli/localci/core/patch_registry.py, cli/localci/core/patch_pipeline.py
Adds PatchProjectConfig, ProjectConfig, extra_steps validation, is_step_enabled/resolved_order, and a new get_patch_step_registry() supporting built-in + entry-point plugin steps; PatchPipeline.from_config now resolves steps and order via the registry and config.
Project-driven patch step rewrites
cli/localci/core/patch_steps.py
B2SourceCacheStep, RestoreCapyTimestampsStep, CapyCopyPreservationStep, and B2BootstrapSkipStep now use project-config literals (paths, markers, filenames) instead of hardcoded Boost/Capy strings.
Repo and image prefix defaults
cli/localci/core/command_builder.py, cli/localci/core/orchestrator.py
ActCommandBuilder and OrchestratorConfig accept configurable repo_full_name/native_image_prefix, defaulting from new config constants; x86 386-arch logic now checks the configured prefix instead of hardcoded "capy-".
Fixtures, validation, and docs
cli/tests/fixtures/patcher/generic_cpp.yml, cli/tests/test_patch_pipeline.py, cli/tests/test_executor.py, cli/tests/test_patcher_patches.py, cli/Usage Guide.md, README.md
Adds a generic C++ workflow fixture, tests for project-driven patching, plugin registration, extra-step validation, command-builder defaults, updated skip-warning text, and documentation for the new patch pipeline/plugin system.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: henry0816191, wpak-ai

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.13% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: generalizing the Boost/C++ patch pipeline via config and plugin points.
Linked Issues check ✅ Passed The changes lift Boost literals into config, add plugin registration, keep defaults/backward compatibility, and make repo/image defaults overridable.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes are present; the docs, fixtures, and tests all support the generalized patch-pipeline work.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/generalize-patch-pipeline

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
cli/localci/core/patch_registry.py (1)

27-32: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

No error handling around ep.load().

If a registered plugin's entry point fails to import (bad package, missing dependency), get_patch_step_registry() raises unhandled, breaking PatchesConfig validation (and thus the whole CLI) even for unrelated config changes. Consider catching and logging load failures per entry point so one broken plugin doesn't take down config parsing.

🤖 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 `@cli/localci/core/patch_registry.py` around lines 27 - 32, The
get_patch_step_registry() entry point loading path currently calls ep.load()
without protection, so one broken plugin can crash PatchesConfig validation and
the CLI. Update the registry-building loop in get_patch_step_registry() to catch
import/load failures for each entry point, log the failure with the entry point
name and error details, and continue processing the remaining plugins so
unrelated config parsing still succeeds.
cli/localci/core/config.py (1)

290-295: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Default literals duplicated across three files.

ProjectConfig.repo_full_name/native_image_prefix defaults ("cppalliance/capy"/"capy-") are re-declared as fallback literals in orchestrator.py's OrchestratorConfig.from_config (getattr(project, "repo_full_name", "cppalliance/capy")) and in command_builder.py's ActCommandBuilder.__init__ (self.repo_full_name = repo_full_name or "cppalliance/capy"). Three independent copies of the same magic strings risk drifting if the default ever changes.

♻️ Suggested consolidation
+DEFAULT_REPO_FULL_NAME = "cppalliance/capy"
+DEFAULT_NATIVE_IMAGE_PREFIX = "capy-"
+
 class ProjectConfig(BaseModel):
     """Project identity and image conventions for act command building."""
 
-    repo_full_name: str = "cppalliance/capy"
-    native_image_prefix: str = "capy-"
+    repo_full_name: str = DEFAULT_REPO_FULL_NAME
+    native_image_prefix: str = DEFAULT_NATIVE_IMAGE_PREFIX

Then import DEFAULT_REPO_FULL_NAME/DEFAULT_NATIVE_IMAGE_PREFIX in orchestrator.py and command_builder.py instead of re-typing the literals.

🤖 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 `@cli/localci/core/config.py` around lines 290 - 295, The default repo/image
strings are duplicated as magic literals across ProjectConfig,
OrchestratorConfig.from_config, and ActCommandBuilder.__init__, so consolidate
them into shared defaults and reuse those symbols instead of retyping
"cppalliance/capy" and "capy-". Update the fallback logic in orchestrator.py and
command_builder.py to reference the existing ProjectConfig defaults or imported
DEFAULT_REPO_FULL_NAME/DEFAULT_NATIVE_IMAGE_PREFIX so all three places stay in
sync if the defaults change.
cli/localci/core/patch_steps.py (1)

171-195: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Prefix-only match for workspace copy line is looser than necessary.

dest_prefix = dest.split("/")[0] only checks the first path segment (e.g. "libs"/"vendor") is present in the line, rather than the full configured dest string (e.g. "libs/$module"). Since the fixture/tests show dest appears verbatim in the workflow source, matching the full string would be equally simple and more precise, avoiding accidental matches on unrelated lines that happen to contain the prefix substring.

♻️ Suggested tightening
-        dest_prefix = dest.split("/")[0]
         for i, line in enumerate(ctx.lines):
-            if marker in line and dest_prefix in line:
+            if marker in line and dest in line:
🤖 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 `@cli/localci/core/patch_steps.py` around lines 171 - 195, The workspace copy
replacement in apply() is using a prefix-only check via dest_prefix, which is
too broad and can match unrelated lines. Update the matching logic in
PatchSteps.apply to look for the full configured proj.workspace_libs_copy_dest
string together with the marker instead of splitting dest, so the correct
workspace copy line is targeted precisely. Keep the replacement behavior
unchanged; only tighten the condition used to locate the line.
🤖 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 `@cli/localci/core/config.py`:
- Around line 290-295: The default repo/image strings are duplicated as magic
literals across ProjectConfig, OrchestratorConfig.from_config, and
ActCommandBuilder.__init__, so consolidate them into shared defaults and reuse
those symbols instead of retyping "cppalliance/capy" and "capy-". Update the
fallback logic in orchestrator.py and command_builder.py to reference the
existing ProjectConfig defaults or imported
DEFAULT_REPO_FULL_NAME/DEFAULT_NATIVE_IMAGE_PREFIX so all three places stay in
sync if the defaults change.

In `@cli/localci/core/patch_registry.py`:
- Around line 27-32: The get_patch_step_registry() entry point loading path
currently calls ep.load() without protection, so one broken plugin can crash
PatchesConfig validation and the CLI. Update the registry-building loop in
get_patch_step_registry() to catch import/load failures for each entry point,
log the failure with the entry point name and error details, and continue
processing the remaining plugins so unrelated config parsing still succeeds.

In `@cli/localci/core/patch_steps.py`:
- Around line 171-195: The workspace copy replacement in apply() is using a
prefix-only check via dest_prefix, which is too broad and can match unrelated
lines. Update the matching logic in PatchSteps.apply to look for the full
configured proj.workspace_libs_copy_dest string together with the marker instead
of splitting dest, so the correct workspace copy line is targeted precisely.
Keep the replacement behavior unchanged; only tighten the condition used to
locate the line.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2b33c2e6-abe7-4ef9-b7c1-2f3943ea1864

📥 Commits

Reviewing files that changed from the base of the PR and between 1fb8bd3 and 7a84d27.

📒 Files selected for processing (11)
  • .pre-commit-config.yaml
  • cli/localci/core/command_builder.py
  • cli/localci/core/config.py
  • cli/localci/core/orchestrator.py
  • cli/localci/core/patch_pipeline.py
  • cli/localci/core/patch_registry.py
  • cli/localci/core/patch_steps.py
  • cli/tests/fixtures/patcher/generic_cpp.yml
  • cli/tests/test_executor.py
  • cli/tests/test_patch_pipeline.py
  • cli/tests/test_patcher_patches.py

Comment thread cli/localci/core/patch_pipeline.py Outdated
Comment thread cli/localci/core/patch_registry.py
@bradjin8 bradjin8 requested a review from henry0816191 July 9, 2026 17:20
@henry0816191 henry0816191 requested a review from wpak-ai July 9, 2026 17:37
@bradjin8 bradjin8 force-pushed the feat/generalize-patch-pipeline branch from c3a5c06 to bf1b8b2 Compare July 9, 2026 18:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
cli/localci/core/patch_steps.py (2)

184-184: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

C++ source extensions are hardcoded in the file-stat scan.

The find filter is fixed to *.cpp/*.hpp/*.h/*.ipp. For projects using other extensions (e.g. .cc, .cxx, .hh, .tcc), incremental-build stat records will be incomplete, silently defeating the timestamp-restore optimization. Given the generalization objective, consider making the extension set project-configurable.

🤖 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 `@cli/localci/core/patch_steps.py` at line 184, The file-stat scan in
patch_steps.py hardcodes only a small set of C++ extensions, so incremental
build stat records will miss files like .cc, .cxx, .hh, and .tcc. Update the
logic that generates the find command in the patch-building flow to use a
configurable extension list instead of the fixed filter, and thread that
configuration through the existing patch steps/helpers so projects can define
their own source/header extensions.

205-236: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Generalization is incomplete: step title and b2 binary name remain hardcoded.

Unlike RestoreCapyTimestampsStep, which now sources its title from proj.restore_timestamps_step_title, this step still hardcodes "Skip b2 bootstrap" (the idempotency marker at Line 216 and the inserted title at Line 226) and the cached-binary name b2 at Line 228. That keeps Boost/Capy-specific literals baked into the step despite the PR goal of lifting them into project config. Consider parameterizing these via PatchProjectConfig for consistency with the other steps.

🤖 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 `@cli/localci/core/patch_steps.py` around lines 205 - 236, The new step in
patch_steps.py still hardcodes Boost-specific literals instead of using project
configuration. Update the logic in Skip b2 bootstrap insertion to source the
step title/idempotency marker and the cached binary name from PatchProjectConfig
via _project_settings(ctx), similar to RestoreCapyTimestampsStep using
proj.restore_timestamps_step_title. Replace the fixed “Skip b2 bootstrap” and
“b2” references in the step construction and already_patched check with
config-backed values so the behavior stays project-agnostic and consistent
across workflows.
🤖 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 `@cli/localci/core/patch_steps.py`:
- Line 184: The file-stat scan in patch_steps.py hardcodes only a small set of
C++ extensions, so incremental build stat records will miss files like .cc,
.cxx, .hh, and .tcc. Update the logic that generates the find command in the
patch-building flow to use a configurable extension list instead of the fixed
filter, and thread that configuration through the existing patch steps/helpers
so projects can define their own source/header extensions.
- Around line 205-236: The new step in patch_steps.py still hardcodes
Boost-specific literals instead of using project configuration. Update the logic
in Skip b2 bootstrap insertion to source the step title/idempotency marker and
the cached binary name from PatchProjectConfig via _project_settings(ctx),
similar to RestoreCapyTimestampsStep using proj.restore_timestamps_step_title.
Replace the fixed “Skip b2 bootstrap” and “b2” references in the step
construction and already_patched check with config-backed values so the behavior
stays project-agnostic and consistent across workflows.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0d5b3402-7eb9-40ed-a488-dc3918014b8b

📥 Commits

Reviewing files that changed from the base of the PR and between c3a5c06 and bf1b8b2.

📒 Files selected for processing (12)
  • README.md
  • cli/Usage Guide.md
  • cli/localci/core/command_builder.py
  • cli/localci/core/config.py
  • cli/localci/core/orchestrator.py
  • cli/localci/core/patch_pipeline.py
  • cli/localci/core/patch_registry.py
  • cli/localci/core/patch_steps.py
  • cli/tests/fixtures/patcher/generic_cpp.yml
  • cli/tests/test_executor.py
  • cli/tests/test_patch_pipeline.py
  • cli/tests/test_patcher_patches.py
🚧 Files skipped from review as they are similar to previous changes (11)
  • README.md
  • cli/localci/core/patch_registry.py
  • cli/tests/fixtures/patcher/generic_cpp.yml
  • cli/localci/core/patch_pipeline.py
  • cli/tests/test_executor.py
  • cli/Usage Guide.md
  • cli/localci/core/command_builder.py
  • cli/tests/test_patcher_patches.py
  • cli/localci/core/orchestrator.py
  • cli/tests/test_patch_pipeline.py
  • cli/localci/core/config.py

@wpak-ai wpak-ai merged commit b95310f into develop Jul 9, 2026
7 checks passed
@wpak-ai wpak-ai deleted the feat/generalize-patch-pipeline branch July 9, 2026 18:18
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.

Generalize the Boost/C++-specific patch pipeline behind config/plugin points

3 participants