Skip to content

Implement Cargo manifest version bumping - #10

Merged
leynos merged 5 commits into
mainfrom
codex/implement-lading-bump-subcommand
Oct 14, 2025
Merged

Implement Cargo manifest version bumping#10
leynos merged 5 commits into
mainfrom
codex/implement-lading-bump-subcommand

Conversation

@leynos

@leynos leynos commented Oct 13, 2025

Copy link
Copy Markdown
Owner

Summary

  • add tomlkit-backed logic to update workspace and member Cargo manifests to the requested version
  • extend the lading bump CLI with a required version argument and add new unit and BDD coverage
  • update documentation and the roadmap to reflect the completed Step 2.1 work

Testing

  • make check-fmt
  • make typecheck
  • make lint
  • make test

https://chatgpt.com/codex/tasks/task_e_68ed70ec06648322b760bf9786254695

Summary by Sourcery

Implement full version bumping in the bump subcommand: accept a target version, update both workspace and crate Cargo.toml files using tomlkit, respect exclusions, and enhance documentation and tests to cover the new behavior.

New Features:

  • Require a version argument for the bump command
  • Implement manifest version propagation across the workspace and member crates
  • Use tomlkit to rewrite Cargo.toml files while preserving formatting and comments
  • Honor bump.exclude configuration to skip specified crates

Enhancements:

  • Report a concise summary of how many manifests were updated or if no changes were needed
  • Wire the version argument through the CLI dispatcher into the bump run function

Documentation:

  • Update the usage guide with bump command syntax and behavior
  • Mark Step 2.1 as complete in the roadmap and add implementation notes in the design doc

Tests:

  • Add unit tests for bump.run, covering version updates, exclusion logic, normalization, and fallback loading
  • Update CLI unit tests to assert the new version argument and return messages
  • Extend BDD tests to verify workspace and crate manifest versions are updated

Summary by CodeRabbit

  • New Features

    • bump now requires a semantic version argument, validates it before running, updates workspace and crate manifests (honoring configured excludes), and reports concise summaries or "No manifest changes required".
  • Documentation

    • Usage, roadmap, and design docs updated to describe the new versioned bump workflow and the deferred/config-driven documentation-handling approach; removed the previous doc_files knob.
  • Tests

    • BDD and unit tests expanded to cover version validation, manifest updates, exclusions, no-change scenarios, and CLI output.

@sourcery-ai

sourcery-ai Bot commented Oct 13, 2025

Copy link
Copy Markdown

Reviewer's Guide

Implements the bump command to propagate a specified version across workspace and crate Cargo.toml files using tomlkit, updates the CLI interface to accept a version argument, and revises tests and documentation to cover the new behavior.

Sequence diagram for the new bump command workflow

sequenceDiagram
    actor User
    participant CLI
    participant "bump.run()"
    participant "WorkspaceGraph"
    participant "tomlkit"
    User->>CLI: bump <version>
    CLI->>"bump.run()": run(workspace_root, target_version)
    "bump.run()"->>"WorkspaceGraph": load_workspace(root_path)
    "bump.run()"->>"tomlkit": _update_manifest(workspace_manifest, selectors, target_version)
    loop For each crate not in exclude
        "bump.run()"->>"tomlkit": _update_manifest(crate.manifest_path, ("package",), target_version)
    end
    "bump.run()"-->>CLI: Return summary message
    CLI-->>User: Print summary (e.g. "Updated version to ...")
Loading

File-Level Changes

Change Details Files
Implement bump command logic with tomlkit version updates
  • Introduce _update_manifest, _parse_manifest, _select_table, _assign_version and _value_matches helpers
  • Apply target_version to workspace and member tables, count changes and write back
  • Respect bump.exclude when iterating over workspace crates
lading/commands/bump.py
Extend CLI to require version argument and invoke bump logic
  • Add positional version parameter to bump subcommand
  • Use _run_with_context to call bump.run with version, configuration and workspace
lading/cli.py
Enhance unit and BDD tests for bump behavior
  • Update CLI unit tests to include version argument and verify dispatch
  • Add dedicated unit tests for manifest version updates and exclusion logic
  • Modify BDD steps and feature to invoke bump with version and assert manifest contents
tests/unit/test_cli.py
tests/unit/test_commands_placeholder.py
tests/unit/test_bump_command.py
tests/bdd/steps/test_cli_steps.py
tests/bdd/features/cli.feature
Revise documentation to describe bump workflow and roadmap status
  • Update usage-guide with bump syntax, behavior and output summary
  • Add implementation notes in design doc covering tomlkit usage and exclusion
  • Mark version bump task as completed in roadmap
docs/usage-guide.md
docs/lading-design.md
docs/roadmap.md

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

@coderabbitai

coderabbitai Bot commented Oct 13, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Implements a real bump command requiring a semantic version argument, validates it at the CLI layer before loading workspace/configuration, updates workspace and member Cargo.toml versions via tomlkit (honoring bump.exclude), removes bump.doc_files from configuration, and updates docs and tests to the new workflow and outputs.

Changes

Cohort / File(s) Summary of changes
Documentation
docs/lading-design.md, docs/roadmap.md, docs/usage-guide.md
Removed bump.doc_files references; documented manifest rewrite behavior using tomlkit; updated bump workflow to require a version argument and describe validation, summary outputs, and integration points for deferred doc updates.
CLI entrypoint
lading/cli.py
bump now requires a version argument (annotated VersionArgument), validates semantic-version format prior to loading workspace, and forwards normalized root and version into the runner.
Bump command implementation
lading/commands/bump.py
Replaced placeholder with real implementation: run(workspace_root, target_version, ...) parses/writes manifests using tomlkit, updates workspace.package and member crate versions (respecting bump.exclude), performs atomic writes only when needed, and returns a concise summary or no-change message.
Configuration model
lading/config.py
Removed doc_files field from BumpConfig and eliminated parsing/validation of bump.doc_files from configuration loading.
BDD tests
tests/bdd/features/cli.feature, tests/bdd/steps/test_cli_steps.py
Scenarios updated to invoke lading bump <version>; steps adapted to accept version and variadic CLI args, use tomlkit for manifest checks, assert exclusion behavior, invalid-version failure, and no-change reporting.
Unit tests (bump & CLI)
tests/unit/test_bump_command.py, tests/unit/test_cli.py, tests/unit/test_commands_placeholder.py
Added unit tests covering bump behavior (updates, exclusions, normalization, loader fallback), adapted CLI dispatch tests to new signature/return semantics, and removed the bump placeholder test.
Test fixtures & config tests
tests/conftest.py, tests/unit/test_config.py
Removed doc_files from minimal test configuration and deleted tests asserting bump.doc_files; adjusted fixtures and related assertions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant CLI as lading (CLI)
  participant Runner as CLI runner
  participant Bump as commands.bump.run
  participant Loader as Config/Workspace loaders
  participant FS as Cargo.toml files

  U->>CLI: lading bump <version> [--workspace-root]
  CLI->>CLI: validate version format (major.minor.patch, allow prerelease/build)
  CLI->>Runner: invoke bump with normalized root and version
  Runner->>Bump: run(workspace_root, target_version, configuration?, workspace?)

  alt configuration/workspace omitted
    Bump->>Loader: load configuration & workspace graph
    Loader-->>Bump: config, workspace
  end

  loop each selected manifest (workspace + non-excluded members)
    Bump->>FS: read manifest via tomlkit
    alt already target version
      Bump-->>Bump: skip write
    else update required
      Bump->>FS: write updated manifest atomically (preserve trivia/comments)
    end
  end

  Bump-->>Runner: return "Updated version to X in N manifest(s)." or "No manifest changes required"
  Runner-->>CLI: display summary to user
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I thump my paws: 1.2.3!
Across the burrowed crates, in sync they’ll be.
Exclusions nibbled, comments kept tight,
Toml leaves rustle—versions set right.
“Updated N,” I cheer — a hop, a bump, release tonight! 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Implement Cargo manifest version bumping” succinctly captures the core functionality introduced by the PR, focusing on the addition of version bumping support for Cargo manifests. It is clear, concise, and directly related to the main changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/implement-lading-bump-subcommand

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d6aa708 and 77c78ac.

📒 Files selected for processing (1)
  • lading/commands/bump.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (.rules/python-00.md)

**/*.py: Python files must use snake_case filenames (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants must use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single leading underscore
Use typing everywhere and maintain full static type coverage
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only usage
Avoid Any; prefer precise types (TypeVar, Protocol, Literal, Union); use typing.cast only when necessary with justification; use object for unknown opaque values
Be explicit with return types (e.g., -> None, -> str) for all public functions and methods
Favor immutability: prefer tuples to lists and MappingProxyType for read-only mappings; document third-party frozendict if used
Use # pyright: ignore sparingly and include an explanation comment when used
Avoid side effects at import time (no global state mutation or actions on import)
Use docstrings (NumPy format) for public functions, classes, and modules
Explain tricky code with inline comments

**/*.py: Prefer context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual try/finally blocks
Use contextlib.contextmanager (@contextmanager) to implement simple, linear setup/teardown context managers
Implement a class with enter and exit for context managers that require internal state or more complex lifecycle handling
For file I/O, prefer with open(...) as ... over open()/try/finally/close patterns
Choose @contextmanager when control flow is linear and no persistent state is needed
Choose a class-based context manager when there is internal state, lifecycle methods, re-entry, or advanced context needs

**/*.py: Exception classes must end with the suffix 'Error' (N818)
Prefer specific built-ins (e.g., TypeError, ValueError) or domain exceptions over raising Exception directly (TRY003/TRY004)
Preserve causal chains...

Files:

  • lading/commands/bump.py
🧬 Code graph analysis (1)
lading/commands/bump.py (4)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/config.py (2)
  • LadingConfig (82-103)
  • current_configuration (155-161)
lading/workspace/models.py (2)
  • WorkspaceGraph (43-52)
  • load_workspace (55-62)
lading/cli.py (1)
  • bump (192-207)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (1)
lading/commands/bump.py (1)

1-147: Excellent implementation with proper tomlkit integration.

The version bumping logic is well-structured and correctly handles:

  • Workspace and member manifest updates with configurable exclusion
  • Style-preserving TOML updates via tomlkit Item handling and trivia copying
  • Atomic writes with permission preservation (addressing past review feedback)
  • Defensive table traversal and type checking
  • Clear user-facing summary messages

The implementation properly addresses both previous review concerns (trivia preservation in _assign_version and permission preservation in _write_atomic_text). The approach of creating a new string() Item and copying _trivia (lines 111-118) correctly handles potential type changes while preserving formatting, and the defensive suppress(AttributeError) ensures robustness if trivia copying fails.

Type hints are comprehensive, docstrings follow NumPy format, and the code adheres to Python best practices throughout.


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

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

Hey there - I've reviewed your changes and found some issues that need to be addressed.

  • The BDD step definitions reference parse_toml but don’t import it—add from tomlkit import parse as parse_toml to avoid a NameError.
  • Add a unit or BDD test for the code path when all versions already match the target, asserting the “No manifest changes required; all versions already X” message.
  • Since bump.run no longer uses bump.doc_files, consider removing or repurposing that config field to reduce confusion.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The BDD step definitions reference `parse_toml` but don’t import it—add `from tomlkit import parse as parse_toml` to avoid a NameError.
- Add a unit or BDD test for the code path when all versions already match the target, asserting the “No manifest changes required; all versions already X” message.
- Since `bump.run` no longer uses `bump.doc_files`, consider removing or repurposing that config field to reduce confusion.

## Individual Comments

### Comment 1
<location> `lading/cli.py:171` </location>
<code_context>

 @app.command
 def bump(
+    version: str,
     workspace_root: WorkspaceRootOption | None = None,
 ) -> str:
</code_context>

<issue_to_address>
**suggestion:** No validation for version string format.

Consider adding input validation to ensure the version argument matches the expected format before processing.
</issue_to_address>

### Comment 2
<location> `lading/commands/bump.py:27` </location>
<code_context>
+)
+

 def run(
-    workspace_root: Path,
+    workspace_root: Path | str,
</code_context>

<issue_to_address>
**issue (review_instructions):** Add behavioural tests for the new bump command logic to ensure correct manifest version propagation.

The new implementation for the bump command updates manifest versions and respects exclusions. Behavioural tests are required to verify that the command correctly updates the intended files and produces the expected summary output.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural *and* unit tests are required.

</details>
</issue_to_address>

### Comment 3
<location> `lading/commands/bump.py:60` </location>
<code_context>
+    return f"Updated version to {target_version} in {changed} manifest(s)."
+
+
+def _update_manifest(
+    manifest_path: Path,
+    selectors: tuple[tuple[str, ...], ...],
</code_context>

<issue_to_address>
**issue (review_instructions):** Add unit tests for _update_manifest to verify correct version assignment and file writing.

Unit tests should cover cases where the manifest is updated, not updated (already matches), and where selectors do not match any table. This ensures the function behaves correctly in all scenarios.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural *and* unit tests are required.

</details>
</issue_to_address>

### Comment 4
<location> `lading/commands/bump.py:82` </location>
<code_context>
+    return parse_toml(content)
+
+
+def _select_table(
+    document: TOMLDocument | Table,
+    keys: tuple[str, ...],
</code_context>

<issue_to_address>
**issue (review_instructions):** Add unit tests for _select_table to ensure correct table selection and handling of missing keys.

Unit tests should verify that _select_table returns the correct table for valid selectors and None for invalid or missing keys.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural *and* unit tests are required.

</details>
</issue_to_address>

### Comment 5
<location> `lading/commands/bump.py:99` </location>
<code_context>
+    return current if isinstance(current, Table) else None
+
+
+def _assign_version(table: Table | None, target_version: str) -> bool:
+    """Update ``table['version']`` when ``table`` is present."""
+    if table is None:
</code_context>

<issue_to_address>
**issue (review_instructions):** Add unit tests for _assign_version to check version assignment logic.

Unit tests should cover cases where the table is None, the version matches, and the version needs updating.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural *and* unit tests are required.

</details>
</issue_to_address>

### Comment 6
<location> `lading/commands/bump.py:110` </location>
<code_context>
+    return True
+
+
+def _value_matches(value: object, expected: str) -> bool:
+    """Return ``True`` when ``value`` already equals ``expected``."""
+    sentinel = object()
</code_context>

<issue_to_address>
**issue (review_instructions):** Add unit tests for _value_matches to ensure correct comparison logic.

Unit tests should verify that _value_matches returns True when the value matches the expected string, including cases where the value has a 'value' attribute.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*`

**Instructions:**
For any new feature or change to an existing feature, both behavioural *and* unit tests are required.

</details>
</issue_to_address>

### Comment 7
<location> `lading/commands/bump.py:114-116` </location>
<code_context>
def _value_matches(value: object, expected: str) -> bool:
    """Return ``True`` when ``value`` already equals ``expected``."""
    sentinel = object()
    attribute = getattr(value, "value", sentinel)
    if attribute is not sentinel:
        return attribute == expected
    return value == expected

</code_context>

<issue_to_address>
**suggestion (code-quality):** We've found these issues:

- Lift code into else after jump in control flow ([`reintroduce-else`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/reintroduce-else/))
- Swap if/else branches ([`swap-if-else-branches`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/swap-if-else-branches/))
- Replace if statement with if expression ([`assign-if-exp`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/assign-if-exp/))

```suggestion
    return value == expected if attribute is sentinel else attribute == expected
```
</issue_to_address>

### Comment 8
<location> `tests/bdd/steps/test_cli_steps.py:105` </location>
<code_context>
def _run_cli(
    repo_root: Path,
    workspace_directory: Path,
    *command_args: str,
) -> dict[str, typ.Any]:
    command = [
        sys.executable,
        "-m",
        "lading.cli",
        "--workspace-root",
        str(workspace_directory),
    ]
    command.extend(command_args)
    completed = subprocess.run(  # noqa: S603
        command,
        check=False,
        cwd=str(repo_root),
        capture_output=True,
        text=True,
    )
    return {
        "returncode": completed.returncode,
        "stdout": completed.stdout,
        "stderr": completed.stderr,
        "workspace": workspace_directory.resolve(),
    }

</code_context>

<issue_to_address>
**suggestion (code-quality):** Merge extend into list declaration ([`merge-list-extend`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/merge-list-extend/))

```suggestion
        *command_args,
```
</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 thread lading/cli.py Outdated
Comment thread lading/commands/bump.py Outdated
Comment thread tests/bdd/steps/test_cli_steps.py Outdated

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
lading/cli.py (1)

170-173: Consider adding a Parameter annotation for better CLI help.

The version parameter lacks a Parameter annotation with help text. While cyclopts can infer basic help from the parameter name and docstring, an explicit annotation would improve consistency with workspace_root and provide clearer user guidance.

Consider applying this diff:

+_VERSION_PARAMETER = Parameter(
+    help="Target semantic version (e.g., 1.2.3) to set across workspace manifests.",
+)
+VersionArgument = typ.Annotated[str, _VERSION_PARAMETER]
+
 @app.command
 def bump(
-    version: str,
+    version: VersionArgument,
     workspace_root: WorkspaceRootOption | None = None,
 ) -> str:
lading/commands/bump.py (2)

82-96: Consider simplifying the final type check.

The function correctly navigates nested tables, but the final check on line 96 could be clearer. Since current is updated only when values are Table instances, the final isinstance check is needed only for the empty-keys edge case (where current remains a TOMLDocument).

Consider adding a comment explaining why the final check is necessary:

     current = next_value
-return current if isinstance(current, Table) else None
+# Empty keys tuple leaves current as TOMLDocument; verify it's a Table
+return current if isinstance(current, Table) else None

Alternatively, explicitly handle the empty-keys case at the start:

 def _select_table(
     document: TOMLDocument | Table,
     keys: tuple[str, ...],
 ) -> Table | None:
     """Return the nested table located by ``keys`` if it exists."""
+    if not keys:
+        return document if isinstance(document, Table) else None
     current: object = document

110-116: Consider using hasattr for clarity.

The sentinel pattern is correct but could be simplified with hasattr for slightly improved readability.

 def _value_matches(value: object, expected: str) -> bool:
     """Return ``True`` when ``value`` already equals ``expected``."""
-    sentinel = object()
-    attribute = getattr(value, "value", sentinel)
-    if attribute is not sentinel:
-        return attribute == expected
+    if hasattr(value, "value"):
+        return value.value == expected  # type: ignore[attr-defined]
     return value == expected
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0358d96 and 38b946c.

📒 Files selected for processing (10)
  • docs/lading-design.md (1 hunks)
  • docs/roadmap.md (1 hunks)
  • docs/usage-guide.md (2 hunks)
  • lading/cli.py (1 hunks)
  • lading/commands/bump.py (1 hunks)
  • tests/bdd/features/cli.feature (2 hunks)
  • tests/bdd/steps/test_cli_steps.py (5 hunks)
  • tests/unit/test_bump_command.py (1 hunks)
  • tests/unit/test_cli.py (6 hunks)
  • tests/unit/test_commands_placeholder.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py

📄 CodeRabbit inference engine (.rules/python-00.md)

**/*.py: Python files must use snake_case filenames (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants must use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single leading underscore
Use typing everywhere and maintain full static type coverage
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only usage
Avoid Any; prefer precise types (TypeVar, Protocol, Literal, Union); use typing.cast only when necessary with justification; use object for unknown opaque values
Be explicit with return types (e.g., -> None, -> str) for all public functions and methods
Favor immutability: prefer tuples to lists and MappingProxyType for read-only mappings; document third-party frozendict if used
Use # pyright: ignore sparingly and include an explanation comment when used
Avoid side effects at import time (no global state mutation or actions on import)
Use docstrings (NumPy format) for public functions, classes, and modules
Explain tricky code with inline comments

**/*.py: Prefer context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual try/finally blocks
Use contextlib.contextmanager (@contextmanager) to implement simple, linear setup/teardown context managers
Implement a class with enter and exit for context managers that require internal state or more complex lifecycle handling
For file I/O, prefer with open(...) as ... over open()/try/finally/close patterns
Choose @contextmanager when control flow is linear and no persistent state is needed
Choose a class-based context manager when there is internal state, lifecycle methods, re-entry, or advanced context needs

**/*.py: Exception classes must end with the suffix 'Error' (N818)
Prefer specific built-ins (e.g., TypeError, ValueError) or domain exceptions over raising Exception directly (TRY003/TRY004)
Preserve causal chains...

Files:

  • tests/unit/test_bump_command.py
  • lading/commands/bump.py
  • tests/unit/test_cli.py
  • lading/cli.py
  • tests/unit/test_commands_placeholder.py
  • tests/bdd/steps/test_cli_steps.py
{**/unittests/test_*.py,tests/**/*.py}

📄 CodeRabbit inference engine (.rules/python-00.md)

{**/unittests/test_*.py,tests/**/*.py}: Use pytest idioms: prefer fixtures over setup/teardown, parametrize broadly, avoid unnecessary mocks
Group related tests using classes with method names prefixed by test_
Write tests from a user's perspective: test public behaviour, not internals

Files:

  • tests/unit/test_bump_command.py
  • tests/unit/test_cli.py
  • tests/unit/test_commands_placeholder.py
  • tests/bdd/steps/test_cli_steps.py
tests/**/*.py

📄 CodeRabbit inference engine (.rules/python-exception-design-raising-handling-and-logging.md)

In tests, assert specific exception types (and optional message via regex) rather than using broad Exception (B017)

Files:

  • tests/unit/test_bump_command.py
  • tests/unit/test_cli.py
  • tests/unit/test_commands_placeholder.py
  • tests/bdd/steps/test_cli_steps.py
{README.md,docs/**}

📄 CodeRabbit inference engine (.rules/python-00.md)

Colocate documentation: keep README.md or a docs/ directory near reusable packages and include usage examples

Files:

  • docs/lading-design.md
  • docs/usage-guide.md
  • docs/roadmap.md
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use markdown files in docs/ as the knowledge base and source of truth for requirements, dependencies, and architectural decisions.
Proactively update relevant docs/ markdown when decisions, requirements, dependencies, or architecture change.

Files:

  • docs/lading-design.md
  • docs/usage-guide.md
  • docs/roadmap.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Markdown files must pass markdownlint.
Markdown files containing Mermaid diagrams must pass nixie validation.

Files:

  • docs/lading-design.md
  • docs/usage-guide.md
  • docs/roadmap.md
🧬 Code graph analysis (6)
tests/unit/test_bump_command.py (3)
lading/workspace/models.py (2)
  • WorkspaceCrate (30-40)
  • WorkspaceGraph (43-52)
lading/config.py (2)
  • LadingConfig (84-105)
  • BumpConfig (36-55)
lading/commands/bump.py (1)
  • run (27-57)
lading/commands/bump.py (4)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/config.py (2)
  • LadingConfig (84-105)
  • current_configuration (157-163)
lading/workspace/models.py (2)
  • WorkspaceGraph (43-52)
  • load_workspace (55-62)
lading/cli.py (1)
  • bump (170-184)
tests/unit/test_cli.py (3)
lading/cli.py (3)
  • main (112-147)
  • bump (170-184)
  • publish (188-193)
lading/config.py (1)
  • LadingConfig (84-105)
lading/workspace/models.py (1)
  • WorkspaceGraph (43-52)
lading/cli.py (2)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/commands/bump.py (1)
  • run (27-57)
tests/unit/test_commands_placeholder.py (3)
lading/cli.py (1)
  • publish (188-193)
lading/commands/bump.py (1)
  • run (27-57)
lading/commands/publish.py (1)
  • run (18-36)
tests/bdd/steps/test_cli_steps.py (1)
tests/conftest.py (1)
  • repo_root (16-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (25)
docs/roadmap.md (1)

89-89: LGTM! Step 2.1 completion accurately reflected.

The roadmap update correctly marks the workspace and member version update task as complete, aligning with the bump command implementation delivered in this PR.

docs/lading-design.md (1)

280-290: LGTM! Implementation notes accurately document Step 2.1 behavior.

The notes clearly describe the manifest rewriting approach (tomlkit for format preservation), exclusion handling, and user-facing output format, all of which align with the implementation in lading/commands/bump.py.

lading/cli.py (1)

176-184: LGTM! Lambda correctly adapts the runner signature.

The lambda wrapper properly captures version from the enclosing scope and passes it as the second positional argument to commands.bump.run, aligning with the updated signature that now requires target_version.

tests/unit/test_commands_placeholder.py (2)

11-11: LGTM! Correctly removed bump placeholder imports.

The import cleanup is appropriate now that bump has a full implementation with dedicated test coverage in tests/unit/test_bump_command.py.


50-96: LGTM! Test parametrization correctly scoped to publish only.

The parametrized tests now appropriately focus on the remaining placeholder (publish), and the dedicated test_bump_run_mentions_command has been removed since bump is no longer a placeholder.

tests/unit/test_cli.py (2)

23-31: LGTM! Test data structure clearly models version-driven behavior.

The rename from placeholder_text to return_value is more precise, and the new expected_version field enables validation of version propagation through the CLI to the bump command.


142-168: LGTM! Test assertions correctly validate version flow.

The test properly validates that:

  • For bump: workspace_root and version are passed as positional args, with configuration and workspace as kwargs
  • For publish: all three args are passed positionally by _run_with_context
  • The version value flows through to the expected destination
tests/bdd/features/cli.feature (2)

2-8: LGTM! BDD scenario accurately reflects version-driven bump behavior.

The updated scenario name, invocation with version argument, and new assertions for manifest version updates correctly capture the Step 2.1 implementation.


17-18: LGTM! Missing configuration scenario updated for version requirement.

The scenario correctly includes the version argument for the bump invocation.

tests/unit/test_bump_command.py (7)

18-28: LGTM! Helper creates clean workspace manifests.

The helper correctly writes a minimal workspace manifest with members list and workspace.package.version, suitable for testing version updates.


31-56: LGTM! Workspace graph builder is well-structured.

The helper creates a representative workspace with two member crates, each with its own manifest, properly setting up the test fixture for bump command validation.


59-65: LGTM! Version loader appropriately uses tomlkit.

Reading versions with tomlkit.parse aligns with how the implementation updates manifests, ensuring test assertions validate the actual file state.

Based on learnings


74-84: LGTM! Test validates core manifest update behavior.

The test correctly verifies that bump.run:

  • Updates the workspace manifest workspace.package.version
  • Updates each member crate's package.version
  • Returns an accurate summary message

87-96: LGTM! Test validates exclusion logic.

The test correctly verifies that crates in bump.exclude retain their original version while others are updated, ensuring the exclusion feature works as designed.


99-110: LGTM! Test validates workspace root normalization.

The test correctly verifies that relative workspace paths are resolved before applying updates, ensuring consistent behavior regardless of how the path is provided.


113-122: LGTM! Test validates configuration and workspace loading.

The test correctly verifies that when configuration and workspace are omitted, bump.run falls back to loading them via current_configuration() and load_workspace(), ensuring the command works in both programmatic and CLI contexts.

docs/usage-guide.md (1)

68-85: LGTM! Usage guide accurately documents bump behavior.

The updated documentation clearly explains:

  • Version is a required positional argument
  • What manifests get updated (workspace.package.version and member package.version)
  • Exclusion behavior via bump.exclude
  • The concise summary output format

All details align with the implementation in lading/commands/bump.py.

lading/commands/bump.py (3)

7-8: LGTM: tomlkit imports.

The import of parse and Table is appropriate for manipulating TOML while preserving formatting.

Based on learnings: tomlkit 0.13.3 provides style-preserving TOML parsing via its DOM API, which is exactly what's needed here.


21-24: LGTM: Workspace selectors correctly target Cargo manifest structures.

The selectors appropriately handle both standalone package sections and workspace-scoped package sections, aligning with Cargo's manifest schema.


42-57: LGTM: Main logic correctly updates manifests and respects exclusions.

The implementation properly counts changes, honors the exclusion list from configuration, and provides clear user feedback.

tests/bdd/steps/test_cli_steps.py (5)

16-17: LGTM: Test imports properly support BDD parsing and TOML validation.

The addition of parsers for parameterized steps and parse_toml for manifest verification is appropriate.


55-64: LGTM: Workspace manifest creation aligns with Cargo workspace format.

The test fixture correctly creates a workspace manifest with [workspace.package] version, matching Cargo's schema for workspace-level package metadata.


93-118: LGTM: CLI runner generalized to support multiple arguments.

The change from a single subcommand string to variadic *command_args improves flexibility for testing commands with parameters.


121-131: LGTM: Bump step correctly parses and passes version parameter.

The use of parsers.parse to extract the version from the step text and pass it through to _run_cli is correct.


143-172: LGTM: New assertion steps properly validate manifest updates.

The three new then steps correctly:

  • Verify the command output message contains the expected version
  • Parse and validate the workspace manifest [workspace.package].version
  • Parse and validate individual crate manifest [package].version

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos

leynos commented Oct 14, 2025

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following now been resolved?

  • The BDD step definitions reference parse_toml but don’t import it—add from tomlkit import parse as parse_toml to avoid a NameError.
  • Add a unit or BDD test for the code path when all versions already match the target, asserting the “No manifest changes required; all versions already X” message.
  • Since bump.run no longer uses bump.doc_files, consider removing or repurposing that config field to reduce confusion.

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

Actionable comments posted: 0

🧹 Nitpick comments (12)
docs/usage-guide.md (1)

67-75: Tighten grammar and wording in bump usage section.

A few minor fixes improve clarity and markdownlint style.

Apply this diff:

-`bump` synchronises manifest versions across the workspace. The command
-requires the target version as a positional argument and rejects inputs that do
-not match the `<major>.<minor>.<patch>` semantic version shape. All validation
-happens before the command loads workspace metadata so mistakes fail fast.
+`bump` synchronises manifest versions across the workspace. The command
+requires the target version as a positional argument and rejects inputs that do
+not match the `<major>.<minor>.<patch>` semantic version pattern. All validation
+happens before the command loads workspace metadata, so mistakes fail fast.
@@
-Running the command updates:
+Running the command updates:
@@
-`lading` prints a short summary such as
-`Updated version to 1.2.3 in 3 manifest(s).` so that release automation can
-assert the change without parsing files directly. When every manifest already
-records the requested version the CLI instead reports
+`lading` prints a short summary such as
+`Updated version to 1.2.3 in 3 manifest(s).`, so that release automation can
+assert the change without parsing files directly. When every manifest already
+records the requested version, the CLI instead reports
 `No manifest changes required; all versions already 1.2.3.`

Also applies to: 80-91

lading/cli.py (1)

170-181: Consider broader SemVer acceptance (pre-release/build).

Current regex only accepts X.Y.Z. If you plan to support SemVer 2.0 pre-release/build (e.g., 1.2.3-alpha.1+meta), expand the validator later.

Example pattern to consider when needed:

  • r'^\d+.\d+.\d+(?:-[0-9A-Za-z.-]+)?(?:+[0-9A-Za-z.-]+)?$'
docs/lading-design.md (1)

275-292: Minor grammar/punctuation improvements in Step 2.1 notes.

Polish for readability.

Apply:

-implementation updates both `[package]` and `[workspace.package]` sections in
-the workspace manifest when present.
+The implementation updates both `[package]` and `[workspace.package]` sections
+in the workspace manifest when present.
@@
-When no manifest requires changes the command reports a dedicated
+When no manifest requires changes, the command reports a dedicated
 "No manifest changes required" message instead of rewriting files.
@@
-Invalid formats raise a user-facing error without touching the file
-system.
+Invalid formats raise a user-facing error without touching the filesystem.
docs/roadmap.md (1)

89-96: Roadmap updates read well; small style nits optional.

Optional punctuation tweaks for consistency.

-  - **Completion Criteria:** An integration test confirms that running
+  - **Completion Criteria:** An integration test confirms that running
     `lading bump 1.2.3` on a fixture workspace results in all `package.version`
     fields being updated to `1.2.3`.
@@
-  - **Outcome:** The `bump` command updates version numbers within TOML code
-    fences in documentation files using new configuration introduced alongside
-    this feature.
+  - **Outcome:** The `bump` command updates version numbers within TOML code
+    fences in documentation files, using new configuration introduced alongside
+    this feature.

Also applies to: 124-127

tests/unit/test_bump_command.py (1)

74-85: Add a test for root [package] updates in the workspace manifest.

Current coverage verifies [workspace.package], but not a root [package] entry. Add a case where the root manifest contains [package] and ensure it updates to the target version.

I can draft the additional test if helpful.

tests/bdd/features/cli.feature (1)

1-16: Add a scenario for invalid version input.

Cover CLI validation by asserting the error and exit code for an invalid version (e.g., 1.2).

Example:

Scenario: Bumping with an invalid version fails fast
  Given a workspace directory with configuration
  And cargo metadata describes a sample workspace
  When I invoke lading bump 1.2 with that workspace
  Then the CLI exits with code 1
  And the stderr contains "Invalid version argument"

Also applies to: 23-26

lading/commands/bump.py (1)

60-74: Ensure UTF‑8 and prefer atomic writes to avoid partial manifests.

Be explicit about encoding and write atomically to prevent truncated files on failure.

Apply:

-    document = _parse_manifest(manifest_path)
+    document = _parse_manifest(manifest_path)
@@
-    if changed:
-        manifest_path.write_text(document.as_string())
+    if changed:
+        content = document.as_string()
+        # Atomic write
+        import os, tempfile
+        dirpath = manifest_path.parent
+        fd, tmp = tempfile.mkstemp(dir=dirpath, prefix=manifest_path.name + ".", text=True)
+        try:
+            with os.fdopen(fd, "w", encoding="utf-8", newline="") as fh:
+                fh.write(content)
+            os.replace(tmp, manifest_path)
+        finally:
+            try:
+                os.unlink(tmp)
+            except FileNotFoundError:
+                pass
     return changed

And make reads explicit UTF‑8:

-def _parse_manifest(manifest_path: Path) -> TOMLDocument:
+def _parse_manifest(manifest_path: Path) -> TOMLDocument:
@@
-    content = manifest_path.read_text()
+    content = manifest_path.read_text(encoding="utf-8")
     return parse_toml(content)

Also applies to: 76-80

tests/unit/test_cli.py (2)

153-167: Also assert configuration is passed to bump.run for parity.

Bump path verifies workspace/version; consider asserting configuration was provided too, like publish does.

Apply this diff to strengthen the assertion:

 if case.command_module is bump_command:
     workspace_root_arg, version_arg = captured_args
     assert workspace_root_arg == tmp_path.resolve()
     assert version_arg == case.expected_version
+    assert "configuration" in captured_kwargs
+    assert isinstance(captured_kwargs["configuration"], config_module.LadingConfig)
     workspace_model = captured_kwargs["workspace"]

266-282: Also assert configuration is wired through Cyclopts app.

You already validate workspace and version; add a check for configuration as well.

Apply this diff to add the assertion:

     def fake_run(
         workspace_root: Path,
         version: str,
         *,
         configuration: config_module.LadingConfig,
         workspace: WorkspaceGraph,
     ) -> str:
         assert workspace_root == tmp_path.resolve()
         assert version == "4.5.6"
+        assert isinstance(configuration, config_module.LadingConfig)
         assert workspace is graph
         return "bump summary"
tests/bdd/steps/test_cli_steps.py (3)

33-48: Add precondition checks for manifest existence.

These steps assume manifests exist (created by another Given). To make failures clearer if steps are reordered/missed, assert files exist before reading.

Apply this diff:

 def given_workspace_versions_match(
     workspace_directory: Path,
     version: str,
 ) -> None:
     """Ensure the workspace and member manifests record ``version``."""
     workspace_manifest = workspace_directory / "Cargo.toml"
+    if not workspace_manifest.exists():
+        raise AssertionError(f"Workspace manifest not found: {workspace_manifest}")
     workspace_document = parse_toml(workspace_manifest.read_text())
     workspace_document["workspace"]["package"]["version"] = version
     workspace_manifest.write_text(workspace_document.as_string())

     crate_manifest = workspace_directory / "crates" / "alpha" / "Cargo.toml"
+    if not crate_manifest.exists():
+        raise AssertionError(f"Crate manifest not found: {crate_manifest}")
     crate_document = parse_toml(crate_manifest.read_text())
     crate_document["package"]["version"] = version
     crate_manifest.write_text(crate_document.as_string())

70-79: Dedent embedded TOML to avoid leading whitespace.

Indentation in triple-quoted TOML can be fragile. Dedent before writing for clarity/portability.

Apply this diff:

-    workspace_manifest.write_text(
-        """
-        [workspace]
-        members = ["crates/alpha"]
-
-        [workspace.package]
-        version = "0.1.0"
-        """
-    )
+    workspace_manifest.write_text(
+        textwrap.dedent(
+            """
+            [workspace]
+            members = ["crates/alpha"]
+
+            [workspace.package]
+            version = "0.1.0"
+            """
+        ).lstrip()
+    )

And add the import at the top of the file:

import textwrap

158-175: Consider less brittle output assertions.

Exact full-line matches can break on minor phrasing tweaks. Matching key fragments reduces churn.

Apply this diff:

-    assert f"Updated version to {version}" in stdout
+    assert "Updated version to " in stdout and version in stdout
-    assert f"No manifest changes required; all versions already {version}." in stdout
+    assert "No manifest changes required" in stdout and f"already {version}" in stdout
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 38b946c and 7be496a.

📒 Files selected for processing (13)
  • docs/lading-design.md (2 hunks)
  • docs/roadmap.md (2 hunks)
  • docs/usage-guide.md (2 hunks)
  • lading/cli.py (2 hunks)
  • lading/commands/bump.py (1 hunks)
  • lading/config.py (1 hunks)
  • tests/bdd/features/cli.feature (2 hunks)
  • tests/bdd/steps/test_cli_steps.py (6 hunks)
  • tests/conftest.py (0 hunks)
  • tests/unit/test_bump_command.py (1 hunks)
  • tests/unit/test_cli.py (5 hunks)
  • tests/unit/test_commands_placeholder.py (3 hunks)
  • tests/unit/test_config.py (0 hunks)
💤 Files with no reviewable changes (2)
  • tests/unit/test_config.py
  • tests/conftest.py
✅ Files skipped from review due to trivial changes (1)
  • lading/config.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/test_commands_placeholder.py
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py

📄 CodeRabbit inference engine (.rules/python-00.md)

**/*.py: Python files must use snake_case filenames (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants must use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single leading underscore
Use typing everywhere and maintain full static type coverage
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only usage
Avoid Any; prefer precise types (TypeVar, Protocol, Literal, Union); use typing.cast only when necessary with justification; use object for unknown opaque values
Be explicit with return types (e.g., -> None, -> str) for all public functions and methods
Favor immutability: prefer tuples to lists and MappingProxyType for read-only mappings; document third-party frozendict if used
Use # pyright: ignore sparingly and include an explanation comment when used
Avoid side effects at import time (no global state mutation or actions on import)
Use docstrings (NumPy format) for public functions, classes, and modules
Explain tricky code with inline comments

**/*.py: Prefer context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual try/finally blocks
Use contextlib.contextmanager (@contextmanager) to implement simple, linear setup/teardown context managers
Implement a class with enter and exit for context managers that require internal state or more complex lifecycle handling
For file I/O, prefer with open(...) as ... over open()/try/finally/close patterns
Choose @contextmanager when control flow is linear and no persistent state is needed
Choose a class-based context manager when there is internal state, lifecycle methods, re-entry, or advanced context needs

**/*.py: Exception classes must end with the suffix 'Error' (N818)
Prefer specific built-ins (e.g., TypeError, ValueError) or domain exceptions over raising Exception directly (TRY003/TRY004)
Preserve causal chains...

Files:

  • tests/unit/test_cli.py
  • tests/unit/test_bump_command.py
  • tests/bdd/steps/test_cli_steps.py
  • lading/commands/bump.py
  • lading/cli.py
{**/unittests/test_*.py,tests/**/*.py}

📄 CodeRabbit inference engine (.rules/python-00.md)

{**/unittests/test_*.py,tests/**/*.py}: Use pytest idioms: prefer fixtures over setup/teardown, parametrize broadly, avoid unnecessary mocks
Group related tests using classes with method names prefixed by test_
Write tests from a user's perspective: test public behaviour, not internals

Files:

  • tests/unit/test_cli.py
  • tests/unit/test_bump_command.py
  • tests/bdd/steps/test_cli_steps.py
tests/**/*.py

📄 CodeRabbit inference engine (.rules/python-exception-design-raising-handling-and-logging.md)

In tests, assert specific exception types (and optional message via regex) rather than using broad Exception (B017)

Files:

  • tests/unit/test_cli.py
  • tests/unit/test_bump_command.py
  • tests/bdd/steps/test_cli_steps.py
{README.md,docs/**}

📄 CodeRabbit inference engine (.rules/python-00.md)

Colocate documentation: keep README.md or a docs/ directory near reusable packages and include usage examples

Files:

  • docs/lading-design.md
  • docs/usage-guide.md
  • docs/roadmap.md
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use markdown files in docs/ as the knowledge base and source of truth for requirements, dependencies, and architectural decisions.
Proactively update relevant docs/ markdown when decisions, requirements, dependencies, or architecture change.

Files:

  • docs/lading-design.md
  • docs/usage-guide.md
  • docs/roadmap.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Markdown files must pass markdownlint.
Markdown files containing Mermaid diagrams must pass nixie validation.

Files:

  • docs/lading-design.md
  • docs/usage-guide.md
  • docs/roadmap.md
🧬 Code graph analysis (5)
tests/unit/test_cli.py (3)
lading/cli.py (2)
  • main (113-148)
  • publish (203-208)
lading/config.py (1)
  • LadingConfig (82-103)
lading/workspace/models.py (1)
  • WorkspaceGraph (43-52)
tests/unit/test_bump_command.py (3)
lading/workspace/models.py (2)
  • WorkspaceCrate (30-40)
  • WorkspaceGraph (43-52)
lading/config.py (2)
  • LadingConfig (82-103)
  • BumpConfig (36-53)
lading/commands/bump.py (5)
  • run (27-57)
  • _update_manifest (60-73)
  • _select_table (82-96)
  • _assign_version (99-107)
  • _value_matches (110-114)
tests/bdd/steps/test_cli_steps.py (1)
tests/conftest.py (1)
  • repo_root (16-18)
lading/commands/bump.py (4)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/config.py (2)
  • LadingConfig (82-103)
  • current_configuration (155-161)
lading/workspace/models.py (2)
  • WorkspaceGraph (43-52)
  • load_workspace (55-62)
lading/cli.py (1)
  • bump (184-199)
lading/cli.py (2)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/commands/bump.py (1)
  • run (27-57)
🪛 LanguageTool
docs/lading-design.md

[grammar] ~282-~282: There might be a mistake here.
Context: ...version value during the update pass. - The command reports a concise summary (...

(QB_NEW_EN)


[grammar] ~286-~286: There might be a mistake here.
Context: ...red" message instead of rewriting files. - Version arguments are validated at the C...

(QB_NEW_EN)


[grammar] ~289-~289: There might be a mistake here.
Context: ...rror without touching the file system. - The bump.doc_files configuration knob ...

(QB_NEW_EN)

docs/usage-guide.md

[grammar] ~69-~69: There might be a mistake here.
Context: ...` semantic version shape. All validation happens before the command loads workspa...

(QB_NEW_EN)


[grammar] ~72-~72: There might be a mistake here.
Context: ...validation, bump touches the workspace Cargo.toml alongside every member crat...

(QB_NEW_EN)


[grammar] ~73-~73: There might be a mistake here.
Context: ...r crate unless the crate name appears in bump.exclude within lading.toml. ``...

(QB_NEW_EN)


[grammar] ~86-~86: There might be a mistake here.
Context: ... lading prints a short summary such as `Updated version to 1.2.3 in 3 manifest(...

(QB_NEW_EN)


[grammar] ~87-~87: There might be a mistake here.
Context: ...fest(s).` so that release automation can assert the change without parsing files ...

(QB_NEW_EN)


[grammar] ~88-~88: There might be a mistake here.
Context: ...es directly. When every manifest already records the requested version the CLI in...

(QB_NEW_EN)

docs/roadmap.md

[grammar] ~127-~127: There might be a mistake here.
Context: ...n introduced alongside this feature. - Completion Criteria: A test case with ...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (15)
lading/cli.py (2)

189-199: Command wiring and validation look solid.

Version is validated, workspace normalised, and run dispatched with configuration/workspace context.


151-168: No changes required: fallback uses existing load_configuration
config.load_configuration is implemented in lading/config.py and delegates to build_loader under the hood, so the exception branch is correct.

Likely an incorrect or invalid review comment.

lading/commands/bump.py (1)

21-24: Core bump logic and selectors look correct.

  • Updates both [package] and [workspace.package].
  • Honors bump.exclude.
  • Skips rewrites when versions already match and returns concise messages.

Also applies to: 43-58

tests/unit/test_cli.py (6)

29-31: Good extension of the test case model.

Adding return_value and optional expected_version makes the dispatch tests clearer and more flexible.


119-122: CLI args updated correctly for bump.

Including the version argument and workspace root here mirrors the new CLI contract well.


142-145: Clean call capture.

fake_run signature and capture of args/kwargs are simple and robust.


197-197: No issues.

Argument order updated to include version looks correct.


235-235: No issues.

Invocation adjusted to include version for exception path is consistent.


241-257: Great: explicit invalid-version test.

Ensures CLI validation catches non-SemVer inputs and prevents run() from being invoked.

tests/bdd/steps/test_cli_steps.py (6)

16-18: Needed imports look good.

pytest-bdd step helpers and tomlkit parser are correctly imported.


29-29: Config fixture content OK.

Minimal config includes publish.strip_patches; matches test expectations.


111-112: Varargs CLI wrapper is cleaner.

Switching _run_cli to accept *command_args simplifies all step definitions.

Also applies to: 119-119


136-147: Version threaded through the bump step.

BDD step captures and forwards the version correctly.


177-187: Workspace manifest verification looks good.

Using tomlkit.parse and reading workspace.package.version is appropriate.


189-199: Crate manifest verification looks good.

Straightforward check of package.version with tomlkit is correct.

@coderabbitai

coderabbitai Bot commented Oct 14, 2025

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Checking parse_toml import in BDD step definitions ==="
cat tests/bdd/steps/test_cli_steps.py | head -20

echo ""
echo "=== Searching for 'No manifest changes required' in test files ==="
rg -n "No manifest changes required" tests/

echo ""
echo "=== Searching for bump.doc_files references ==="
rg -n "doc_files" --type=py

Length of output: 891


🏁 Script executed:

#!/bin/bash

echo "=== Checking configuration module for bump config structure ==="
cat lading/config.py

echo ""
echo "=== Searching for any doc_files references in entire codebase ==="
rg -n "doc_files" .

echo ""
echo "=== Checking if there's a schema file ==="
fd -e toml -e yaml -e json config schema

Length of output: 7755


@leynos Yes, all three issues have been resolved:

  1. parse_toml import: The import is present in tests/bdd/steps/test_cli_steps.py at line 14:

    from tomlkit import parse as parse_toml
  2. Test for "No manifest changes required" message: Tests exist in both unit and BDD tests:

    • tests/unit/test_bump_command.py:136 - asserts "No manifest changes required; all versions already 0.1.0."
    • tests/bdd/steps/test_cli_steps.py:174 - asserts the same message format with the expected version
  3. bump.doc_files removal: The BumpConfig class in lading/config.py only contains the exclude field. The doc_files field has been removed. The design document (docs/lading-design.md:290) confirms this was intentional, noting: "The bump.doc_files configuration knob has been removed until documentation..."

All concerns have been addressed in the current implementation.

@leynos

leynos commented Oct 14, 2025

Copy link
Copy Markdown
Owner Author

@coderabbitai

Re:

 def _value_matches(value: object, expected: str) -> bool:
     """Return ``True`` when ``value`` already equals ``expected``."""
-    sentinel = object()
-    attribute = getattr(value, "value", sentinel)
-    if attribute is not sentinel:
-        return attribute == expected
+    if hasattr(value, "value"):
+        return value.value == expected  # type: ignore[attr-defined]
     return value == expected

Could we do the following?

class ValueWrapper(typ.Protocol):
    value: str
    
def _value_matches(value: object, expected: str) -> bool:
    match value
        case ValueWrapper(value=attribute):
            return attribute == expected
        case str():
            return value == expected
        case _:
            return False

I don't know if that is any clearer, but I tend to see chains of isinstance and hasattr as a code smell

@coderabbitai

coderabbitai Bot commented Oct 14, 2025

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Current implementation of _value_matches ==="
rg -n -A 10 "def _value_matches" lading/commands/bump.py

echo ""
echo "=== How _value_matches is called ==="
rg -n "_value_matches" lading/commands/bump.py

echo ""
echo "=== Check Python version requirements ==="
cat pyproject.toml | grep -A 5 "requires-python"

echo ""
echo "=== Look at the actual types being passed ==="
rg -n -B 5 -A 5 "tomlkit" lading/commands/bump.py | head -40

Length of output: 1681


@leynos, I appreciate the pattern-matching approach, but there are a few issues with the proposed implementation:

  1. Protocol matching limitation: Protocols can't be used directly in structural pattern matching the way you've written it. The case ValueWrapper(value=attribute): syntax won't work because Protocols don't support this kind of structural destructuring in match/case.

  2. Behavior change: The current implementation falls back to value == expected, but your proposal returns False in the default case, which changes the behavior for unexpected types.

  3. Type mismatch: The case str(): won't match tomlkit's string wrapper types that have a .value attribute.

Here's a cleaner alternative using a @runtime_checkable Protocol:

from typing import Protocol, runtime_checkable

@runtime_checkable
class ValueWrapper(Protocol):
    """Protocol for tomlkit value wrappers."""
    value: str

def _value_matches(value: object, expected: str) -> bool:
    """Return ``True`` when ``value`` already equals ``expected``."""
    if isinstance(value, ValueWrapper):
        return value.value == expected
    return value == expected

This approach:

  • Uses isinstance with a Protocol (cleaner than raw hasattr)
  • Preserves the original fallback behavior
  • Is more explicit about the duck-typing contract
  • Works correctly with tomlkit's wrapper types

The @runtime_checkable decorator allows the Protocol to be used with isinstance at runtime, which is exactly what we need here.

Would you like me to generate this change for you?

@coderabbitai

coderabbitai Bot commented Oct 14, 2025

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Current implementation of _value_matches in bump.py ==="
rg -n -A 10 "def _value_matches" lading/commands/bump.py

echo ""
echo "=== Check what tomlkit types have .value attribute ==="
rg -n "\.value" lading/commands/bump.py | head -20

echo ""
echo "=== Look for imports and type hints in bump.py ==="
head -30 lading/commands/bump.py

echo ""
echo "=== Check Python version requirements ==="
cat pyproject.toml | grep -A 5 "python"

Length of output: 1546


🏁 Script executed:

#!/bin/bash

echo "=== Search for tomlkit documentation or usage in codebase ==="
rg -n "from tomlkit" --type=py

echo ""
echo "=== Check if there are any type annotations that hint at what has .value ==="
rg -n "tomlkit" lading/commands/bump.py -A 3 -B 3

echo ""
echo "=== Look at how _value_matches is called ==="
rg -n "_value_matches" lading/commands/bump.py -B 3 -A 3

echo ""
echo "=== Check tomlkit imports across codebase ==="
rg -n "tomlkit" --type=py | grep -E "(String|Integer|Float|Item)" | head -20

Length of output: 3808


🏁 Script executed:

#!/bin/bash

echo "=== Look at how tomlkit items are used in crate_tools/bump_version.py ==="
cat crate_tools/bump_version.py | head -100

echo ""
echo "=== Check how version values are handled ==="
rg -n "version.*=" lading/commands/bump.py -B 2 -A 2

echo ""
echo "=== See if there are examples of tomlkit String usage ==="
rg -n "String|Integer" crate_tools/bump_version.py | head -20

Length of output: 3638


@leynos Your proposed solution has a few issues that prevent it from working:

  1. Protocols don't work with pattern matching at runtime - Protocols are primarily for static type checking. Pattern matching can't check Protocol conformance at runtime.
  2. Syntax error - Missing colon after match value
  3. Pattern matching on attributes - The ValueWrapper(value=attribute) syntax doesn't work as you'd expect with Protocols.

However, your concern about hasattr as a code smell is valid! Here are some cleaner alternatives:

Option 1: Use actual tomlkit types

from tomlkit.items import Item

def _value_matches(value: object, expected: str) -> bool:
    """Return ``True`` when ``value`` already equals ``expected``."""
    if isinstance(value, Item):
        return value.value == expected  # type: ignore[attr-defined]
    return value == expected

Option 2: Pattern matching with guard clauses

def _value_matches(value: object, expected: str) -> bool:
    """Return ``True`` when ``value`` already equals ``expected``."""
    match value:
        case str():
            return value == expected
        case _ if hasattr(value, "value"):
            return value.value == expected  # type: ignore[attr-defined]
        case _:
            return value == expected

Option 3: Protocol with isinstance (if you prefer the Protocol approach)

class ValueWrapper(typ.Protocol):
    @property
    def value(self) -> str: ...

def _value_matches(value: object, expected: str) -> bool:
    """Return ``True`` when ``value`` already equals ``expected``."""
    # Note: isinstance doesn't check Protocol conformance at runtime
    # unless you use @runtime_checkable, but that still requires hasattr checks
    if hasattr(value, "value"):
        return value.value == expected  # type: ignore[attr-defined]
    return value == expected

I'd recommend Option 1 - it's explicit, clean, and Item is the base class for all tomlkit value types that have a .value attribute. This avoids hasattr entirely and is more maintainable.

codescene-delta-analysis[bot]

This comment was marked as outdated.

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/roadmap.md (1)

121-131: Remove deprecated bump.doc_files references in documentation

  • docs/lading-design.md (lines 290–291): remove or update the mention of the removed bump.doc_files configuration knob to reflect its deprecation.
docs/usage-guide.md (1)

128-130: Fix cargo metadata flag spelling

Use the correct flag form without a space after --.

Apply:

-`cargo metadata -- format-version 1` using `plumbum`, and returns the parsed
+`cargo metadata --format-version 1` using `plumbum`, and returns the parsed
🧹 Nitpick comments (11)
docs/roadmap.md (1)

126-127: Minor grammar/whitespace tweak

Consider “using new configuration introduced alongside this feature.” (single space) for smoother phrasing.

Based on static analysis hints

tests/unit/test_bump_command.py (1)

158-175: Add a test to ensure inline comments and formatting are preserved

To lock-in tomlkit’s style-preserving intent, add a case where version has a trailing comment and assert it’s retained after bump.

Example:

def test_update_preserves_inline_comment(tmp_path: Path) -> None:
    manifest = tmp_path / "Cargo.toml"
    manifest.write_text('[package]\nversion = "0.1.0"  # keep me\n')
    changed = bump._update_manifest(manifest, (("package",),), "1.2.3")
    assert changed is True
    text = manifest.read_text()
    assert '# keep me' in text
    # and version actually updated
    doc = parse_toml(text)
    assert doc["package"]["version"] == "1.2.3"

This will catch regressions if the update path replaces the value node and drops trivia. Based on learnings

Also applies to: 196-207

lading/cli.py (1)

175-186: Confirm version policy (pre-release/build metadata)

Current regex rejects “1.2.3-alpha”/“1.2.3+build”. If that’s intentional, ignore. If not, expand the pattern.

Option:

-_VERSION_PATTERN = re.compile(r"^\d+\.\d+\.\d+$")
+_VERSION_PATTERN = re.compile(
+    r"^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$"
+)

Also consider including the bad value in the error for clarity.

lading/commands/bump.py (2)

114-120: Simplify _value_matches using tomlkit Item

Use isinstance against tomlkit.items.Item for clarity and to avoid direct getattribute.

-def _value_matches(value: object, expected: str) -> bool:
-    """Return ``True`` when ``value`` already equals ``expected``."""
-    if hasattr(value, "value"):
-        attribute = object.__getattribute__(value, "value")
-        return attribute == expected
-    return value == expected
+def _value_matches(value: object, expected: str) -> bool:
+    """Return ``True`` when ``value`` already equals ``expected``."""
+    if isinstance(value, Item):
+        return value.value == expected
+    return value == expected

11-13: Add Item import for type checks/mutation

Needed for the above changes.

-from tomlkit.items import Table
+from tomlkit.items import Table, Item
tests/bdd/features/cli.feature (1)

2-9: Optional: add an exclusion scenario

Consider a scenario verifying that a crate listed in bump.exclude is not updated via CLI end-to-end.

Example steps:

  • Given bump.exclude contains "alpha"
  • When I invoke lading bump 1.2.3 with that workspace
  • Then the crate "alpha" manifest version is "0.1.0"
  • And the crate "beta" manifest version is "1.2.3"
docs/usage-guide.md (2)

72-75: Clarify wording (“touches” → “updates”)

Slightly clearer phrasing.

-When the version string passes validation, `bump` touches the workspace
-`Cargo.toml` alongside every member crate unless the crate name appears in
-`bump.exclude` within `lading.toml`.
+When the version string passes validation, `bump` updates the workspace
+`Cargo.toml` and each member crate’s manifest, unless the crate name appears in
+`bump.exclude` within `lading.toml`.

86-91: Tighten summary sentence and punctuation

Minor readability improvement.

-`lading` prints a short summary such as
-`Updated version to 1.2.3 in 3 manifest(s).`, so that release automation can
-assert the change without parsing files directly. When every manifest already
-records the requested version, the CLI instead reports
-`No manifest changes required; all versions already 1.2.3.`
+`lading` prints a short summary, for example:
+`Updated version to 1.2.3 in 3 manifest(s).` This lets release automation
+assert the change without parsing files directly. When every manifest already
+records the requested version, the CLI instead reports:
+`No manifest changes required; all versions already 1.2.3.`
docs/lading-design.md (2)

266-271: Avoid using raw triple backticks inline

Inline “```toml” can be misinterpreted by Markdown renderers. Use descriptive text.

-    - For each matching file, scan for TOML code fences (```toml).
+    - For each matching file, scan for TOML fenced code blocks (three backticks + "toml").

283-291: Tighten Step 2.1 notes punctuation

Minor clarity/punctuation tweaks.

-- The command reports a concise summary (`Updated version to … in N
-  manifest(s).`) so callers can assert success without inspecting the file
-  system. When no manifest requires changes, the command reports a dedicated
-  "No manifest changes required" message instead of rewriting files.
+- The command reports a concise summary (`Updated version to … in N manifest(s).`)
+  so callers can assert success without inspecting the filesystem. When no
+  manifest requires changes, it reports a dedicated "No manifest changes required"
+  message instead of rewriting files.
tests/bdd/steps/test_cli_steps.py (1)

17-19: LGTM: BDD steps updated for versioned bump; add explicit UTF‑8 encoding for file IO

  • Steps correctly pass/verify versions and parse manifests via tomlkit.
  • Recommend explicit encoding="utf-8" in read_text/write_text for portability across locales. As per coding guidelines on file I/O, prefer explicit encodings.
-    config_path.write_text('[bump]\n\n[publish]\nstrip_patches = "all"\n')
+    config_path.write_text('[bump]\n\n[publish]\nstrip_patches = "all"\n', encoding="utf-8")

-    workspace_document = parse_toml(workspace_manifest.read_text())
+    workspace_document = parse_toml(workspace_manifest.read_text(encoding="utf-8"))
-    workspace_manifest.write_text(workspace_document.as_string())
+    workspace_manifest.write_text(workspace_document.as_string(), encoding="utf-8")

-    crate_document = parse_toml(crate_manifest.read_text())
+    crate_document = parse_toml(crate_manifest.read_text(encoding="utf-8"))
-    crate_manifest.write_text(crate_document.as_string())
+    crate_manifest.write_text(crate_document.as_string(), encoding="utf-8")

-    workspace_manifest.write_text(
+    workspace_manifest.write_text(
         textwrap.dedent(
             """
             [workspace]
             members = ["crates/alpha"]

             [workspace.package]
             version = "0.1.0"
             """
         ).lstrip()
-    )
+    , encoding="utf-8")

-    document = parse_toml(manifest_path.read_text())
+    document = parse_toml(manifest_path.read_text(encoding="utf-8"))
-    document = parse_toml(manifest_path.read_text())
+    document = parse_toml(manifest_path.read_text(encoding="utf-8"))

As per coding guidelines

Also applies to: 34-55, 77-88, 117-129, 145-156, 167-186, 200-221

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7be496a and 0c19cb1.

📒 Files selected for processing (9)
  • docs/lading-design.md (2 hunks)
  • docs/roadmap.md (2 hunks)
  • docs/usage-guide.md (2 hunks)
  • lading/cli.py (3 hunks)
  • lading/commands/bump.py (1 hunks)
  • tests/bdd/features/cli.feature (2 hunks)
  • tests/bdd/steps/test_cli_steps.py (7 hunks)
  • tests/unit/test_bump_command.py (1 hunks)
  • tests/unit/test_cli.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py

📄 CodeRabbit inference engine (.rules/python-00.md)

**/*.py: Python files must use snake_case filenames (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants must use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single leading underscore
Use typing everywhere and maintain full static type coverage
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only usage
Avoid Any; prefer precise types (TypeVar, Protocol, Literal, Union); use typing.cast only when necessary with justification; use object for unknown opaque values
Be explicit with return types (e.g., -> None, -> str) for all public functions and methods
Favor immutability: prefer tuples to lists and MappingProxyType for read-only mappings; document third-party frozendict if used
Use # pyright: ignore sparingly and include an explanation comment when used
Avoid side effects at import time (no global state mutation or actions on import)
Use docstrings (NumPy format) for public functions, classes, and modules
Explain tricky code with inline comments

**/*.py: Prefer context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual try/finally blocks
Use contextlib.contextmanager (@contextmanager) to implement simple, linear setup/teardown context managers
Implement a class with enter and exit for context managers that require internal state or more complex lifecycle handling
For file I/O, prefer with open(...) as ... over open()/try/finally/close patterns
Choose @contextmanager when control flow is linear and no persistent state is needed
Choose a class-based context manager when there is internal state, lifecycle methods, re-entry, or advanced context needs

**/*.py: Exception classes must end with the suffix 'Error' (N818)
Prefer specific built-ins (e.g., TypeError, ValueError) or domain exceptions over raising Exception directly (TRY003/TRY004)
Preserve causal chains...

Files:

  • tests/unit/test_cli.py
  • lading/cli.py
  • tests/unit/test_bump_command.py
  • tests/bdd/steps/test_cli_steps.py
  • lading/commands/bump.py
{**/unittests/test_*.py,tests/**/*.py}

📄 CodeRabbit inference engine (.rules/python-00.md)

{**/unittests/test_*.py,tests/**/*.py}: Use pytest idioms: prefer fixtures over setup/teardown, parametrize broadly, avoid unnecessary mocks
Group related tests using classes with method names prefixed by test_
Write tests from a user's perspective: test public behaviour, not internals

Files:

  • tests/unit/test_cli.py
  • tests/unit/test_bump_command.py
  • tests/bdd/steps/test_cli_steps.py
tests/**/*.py

📄 CodeRabbit inference engine (.rules/python-exception-design-raising-handling-and-logging.md)

In tests, assert specific exception types (and optional message via regex) rather than using broad Exception (B017)

Files:

  • tests/unit/test_cli.py
  • tests/unit/test_bump_command.py
  • tests/bdd/steps/test_cli_steps.py
{README.md,docs/**}

📄 CodeRabbit inference engine (.rules/python-00.md)

Colocate documentation: keep README.md or a docs/ directory near reusable packages and include usage examples

Files:

  • docs/lading-design.md
  • docs/usage-guide.md
  • docs/roadmap.md
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use markdown files in docs/ as the knowledge base and source of truth for requirements, dependencies, and architectural decisions.
Proactively update relevant docs/ markdown when decisions, requirements, dependencies, or architecture change.

Files:

  • docs/lading-design.md
  • docs/usage-guide.md
  • docs/roadmap.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Markdown files must pass markdownlint.
Markdown files containing Mermaid diagrams must pass nixie validation.

Files:

  • docs/lading-design.md
  • docs/usage-guide.md
  • docs/roadmap.md
🧬 Code graph analysis (5)
tests/unit/test_cli.py (3)
lading/cli.py (2)
  • main (118-153)
  • publish (208-213)
lading/config.py (1)
  • LadingConfig (82-103)
lading/workspace/models.py (1)
  • WorkspaceGraph (43-52)
lading/cli.py (2)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/commands/bump.py (1)
  • run (29-59)
tests/unit/test_bump_command.py (4)
lading/cli.py (2)
  • bump (189-204)
  • publish (208-213)
lading/workspace/models.py (2)
  • WorkspaceCrate (30-40)
  • WorkspaceGraph (43-52)
lading/config.py (2)
  • LadingConfig (82-103)
  • BumpConfig (36-53)
lading/commands/bump.py (5)
  • run (29-59)
  • _update_manifest (62-75)
  • _select_table (84-100)
  • _assign_version (103-111)
  • _value_matches (114-119)
tests/bdd/steps/test_cli_steps.py (1)
tests/conftest.py (1)
  • repo_root (16-18)
lading/commands/bump.py (4)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/config.py (2)
  • LadingConfig (82-103)
  • current_configuration (155-161)
lading/workspace/models.py (2)
  • WorkspaceGraph (43-52)
  • load_workspace (55-62)
lading/cli.py (1)
  • bump (189-204)
🪛 LanguageTool
docs/lading-design.md

[grammar] ~282-~282: There might be a mistake here.
Context: ...version value during the update pass. - The command reports a concise summary (...

(QB_NEW_EN)


[grammar] ~286-~286: There might be a mistake here.
Context: ...red" message instead of rewriting files. - Version arguments are validated at the C...

(QB_NEW_EN)


[grammar] ~289-~289: There might be a mistake here.
Context: ...error without touching the filesystem. - The bump.doc_files configuration knob ...

(QB_NEW_EN)

docs/usage-guide.md

[grammar] ~69-~69: There might be a mistake here.
Context: ...semantic version pattern. All validation happens before the command loads workspa...

(QB_NEW_EN)


[grammar] ~72-~72: There might be a mistake here.
Context: ...validation, bump touches the workspace Cargo.toml alongside every member crat...

(QB_NEW_EN)


[grammar] ~73-~73: There might be a mistake here.
Context: ...r crate unless the crate name appears in bump.exclude within lading.toml. ``...

(QB_NEW_EN)


[grammar] ~86-~86: There might be a mistake here.
Context: ... lading prints a short summary such as `Updated version to 1.2.3 in 3 manifest(...

(QB_NEW_EN)


[grammar] ~87-~87: There might be a mistake here.
Context: ...est(s).`, so that release automation can assert the change without parsing files ...

(QB_NEW_EN)


[grammar] ~88-~88: There might be a mistake here.
Context: ...es directly. When every manifest already records the requested version, the CLI i...

(QB_NEW_EN)

docs/roadmap.md

[grammar] ~127-~127: There might be a mistake here.
Context: ...n introduced alongside this feature. - Completion Criteria: A test case with ...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (13)
docs/roadmap.md (1)

89-95: Step 2.1 completion looks correct

Accurately reflects implemented manifest version propagation.

lading/cli.py (1)

188-205: CLI wiring for bump(version, …) looks solid

Validation then dispatch through _run_with_context to commands.bump.run is correct.

lading/commands/bump.py (2)

29-60: run(): version propagation and exclude handling LGTM

Workspace + root [package] updates, per-crate loop, and summary messaging behave as intended.


122-136: Atomic write implementation LGTM

mkstemp in target dir + replace() + cleanup is correct and cross-platform-safe for same-filesystem replacements.

tests/bdd/features/cli.feature (1)

2-9: BDD scenarios align with the new versioned bump flow

Covers happy path, invalid version, and no-change behavior; looks good.

Also applies to: 10-15, 16-22, 29-32

tests/unit/test_cli.py (5)

117-122: LGTM: dispatch wiring, argument capture, and output checks

  • Correctly validates bump signature (workspace_root, version) and ensures config/workspace in kwargs.
  • Publish path asserts expected positional args.
  • Output assertions target the printed return values.

Also applies to: 142-169, 156-165, 167-169


199-206: LGTM: missing configuration error path

Asserts exit code and error message precisely when config is absent.


243-259: LGTM: invalid version short-circuit

Good guard ensuring bump.run is not called for invalid versions; error surfaced via exit code and stderr.


269-285: LGTM: direct Cyclopts invocation test

Validates cli.app invocation path, propagating workspace and version to bump.run.


287-294: LGTM: workspace env context manager

Confirms env var set/cleared correctly during context lifetime.

tests/bdd/steps/test_cli_steps.py (3)

167-174: LGTM: message assertion is resilient

Checks for substrings rather than exact punctuation; robust to minor formatting changes.


176-186: LGTM: “no changes required” scenario

Verifies both the generic message and the “already ” detail.


224-232: LGTM: publish placeholder validation

Asserts workspace path, crate count, and strip patch strategy in output.

Comment thread lading/commands/bump.py

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0c19cb1 and d6aa708.

📒 Files selected for processing (8)
  • docs/lading-design.md (1 hunks)
  • docs/usage-guide.md (3 hunks)
  • lading/cli.py (3 hunks)
  • lading/commands/bump.py (1 hunks)
  • tests/bdd/features/cli.feature (2 hunks)
  • tests/bdd/steps/test_cli_steps.py (7 hunks)
  • tests/unit/test_bump_command.py (1 hunks)
  • tests/unit/test_cli.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py

📄 CodeRabbit inference engine (.rules/python-00.md)

**/*.py: Python files must use snake_case filenames (e.g., http_client.py, task_queue.py)
Classes must use PascalCase
Variables and functions must use snake_case
Module-level constants must use UPPER_SNAKE_CASE
Prefix non-exported helpers or internal APIs with a single leading underscore
Use typing everywhere and maintain full static type coverage
Use TypedDict or @DataClass for structured data; prefer @DataClass(slots=True) for internal-only usage
Avoid Any; prefer precise types (TypeVar, Protocol, Literal, Union); use typing.cast only when necessary with justification; use object for unknown opaque values
Be explicit with return types (e.g., -> None, -> str) for all public functions and methods
Favor immutability: prefer tuples to lists and MappingProxyType for read-only mappings; document third-party frozendict if used
Use # pyright: ignore sparingly and include an explanation comment when used
Avoid side effects at import time (no global state mutation or actions on import)
Use docstrings (NumPy format) for public functions, classes, and modules
Explain tricky code with inline comments

**/*.py: Prefer context managers to encapsulate setup/teardown for resources (files, locks, connections) instead of manual try/finally blocks
Use contextlib.contextmanager (@contextmanager) to implement simple, linear setup/teardown context managers
Implement a class with enter and exit for context managers that require internal state or more complex lifecycle handling
For file I/O, prefer with open(...) as ... over open()/try/finally/close patterns
Choose @contextmanager when control flow is linear and no persistent state is needed
Choose a class-based context manager when there is internal state, lifecycle methods, re-entry, or advanced context needs

**/*.py: Exception classes must end with the suffix 'Error' (N818)
Prefer specific built-ins (e.g., TypeError, ValueError) or domain exceptions over raising Exception directly (TRY003/TRY004)
Preserve causal chains...

Files:

  • tests/bdd/steps/test_cli_steps.py
  • lading/commands/bump.py
  • tests/unit/test_cli.py
  • tests/unit/test_bump_command.py
  • lading/cli.py
{**/unittests/test_*.py,tests/**/*.py}

📄 CodeRabbit inference engine (.rules/python-00.md)

{**/unittests/test_*.py,tests/**/*.py}: Use pytest idioms: prefer fixtures over setup/teardown, parametrize broadly, avoid unnecessary mocks
Group related tests using classes with method names prefixed by test_
Write tests from a user's perspective: test public behaviour, not internals

Files:

  • tests/bdd/steps/test_cli_steps.py
  • tests/unit/test_cli.py
  • tests/unit/test_bump_command.py
tests/**/*.py

📄 CodeRabbit inference engine (.rules/python-exception-design-raising-handling-and-logging.md)

In tests, assert specific exception types (and optional message via regex) rather than using broad Exception (B017)

Files:

  • tests/bdd/steps/test_cli_steps.py
  • tests/unit/test_cli.py
  • tests/unit/test_bump_command.py
{README.md,docs/**}

📄 CodeRabbit inference engine (.rules/python-00.md)

Colocate documentation: keep README.md or a docs/ directory near reusable packages and include usage examples

Files:

  • docs/usage-guide.md
  • docs/lading-design.md
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Use markdown files in docs/ as the knowledge base and source of truth for requirements, dependencies, and architectural decisions.
Proactively update relevant docs/ markdown when decisions, requirements, dependencies, or architecture change.

Files:

  • docs/usage-guide.md
  • docs/lading-design.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Markdown files must pass markdownlint.
Markdown files containing Mermaid diagrams must pass nixie validation.

Files:

  • docs/usage-guide.md
  • docs/lading-design.md
🧬 Code graph analysis (5)
tests/bdd/steps/test_cli_steps.py (2)
tests/helpers/workspace_helpers.py (1)
  • install_cargo_stub (15-38)
tests/conftest.py (1)
  • repo_root (16-18)
lading/commands/bump.py (4)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/config.py (2)
  • LadingConfig (82-103)
  • current_configuration (155-161)
lading/workspace/models.py (2)
  • WorkspaceGraph (43-52)
  • load_workspace (55-62)
lading/cli.py (1)
  • bump (192-207)
tests/unit/test_cli.py (6)
lading/cli.py (2)
  • main (118-153)
  • publish (211-216)
lading/config.py (1)
  • LadingConfig (82-103)
tests/unit/test_bump_command.py (1)
  • _make_workspace (31-56)
tests/unit/test_commands_placeholder.py (1)
  • _make_workspace (23-46)
crate_tools/unittests/test_publish_workspace_dependencies.py (1)
  • workspace_root (16-21)
lading/workspace/models.py (1)
  • WorkspaceGraph (43-52)
tests/unit/test_bump_command.py (4)
lading/cli.py (2)
  • bump (192-207)
  • publish (211-216)
lading/workspace/models.py (2)
  • WorkspaceCrate (30-40)
  • WorkspaceGraph (43-52)
lading/config.py (2)
  • LadingConfig (82-103)
  • BumpConfig (36-53)
lading/commands/bump.py (5)
  • run (30-60)
  • _update_manifest (63-76)
  • _select_table (85-101)
  • _assign_version (104-118)
  • _value_matches (121-125)
lading/cli.py (2)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/commands/bump.py (1)
  • run (30-60)
🪛 LanguageTool
docs/usage-guide.md

[grammar] ~67-~67: There might be a mistake here.
Context: ...rsions across the workspace. The command requires the target version as a positio...

(QB_NEW_EN)


[grammar] ~68-~68: There might be a mistake here.
Context: ...onal argument and rejects inputs that do not match the <major>.<minor>.<patch> ...

(QB_NEW_EN)


[grammar] ~69-~69: There might be a mistake here.
Context: ...semantic version pattern, while allowing optional pre-release and build metadata....

(QB_NEW_EN)


[grammar] ~70-~70: There might be a mistake here.
Context: ...adata. All validation happens before the command loads workspace metadata, so mis...

(QB_NEW_EN)


[grammar] ~73-~73: There might be a mistake here.
Context: ...validation, bump updates the workspace Cargo.toml and each member crate's man...

(QB_NEW_EN)


[grammar] ~74-~74: There might be a mistake here.
Context: ...nifest, unless the crate name appears in bump.exclude within lading.toml. ``...

(QB_NEW_EN)


[grammar] ~88-~88: There might be a mistake here.
Context: ...nifest(s).` This lets release automation assert the change without parsing files ...

(QB_NEW_EN)


[grammar] ~89-~89: There might be a mistake here.
Context: ...es directly. When every manifest already records the requested version, the CLI i...

(QB_NEW_EN)


[grammar] ~129-~129: There might be a mistake here.
Context: ... using plumbum, and returns the parsed JSON mapping. Any execution errors or in...

(QB_NEW_EN)

docs/lading-design.md

[grammar] ~282-~282: There might be a mistake here.
Context: ...version value during the update pass. - The command reports a concise summary (...

(QB_NEW_EN)


[grammar] ~286-~286: There might be a mistake here.
Context: ...d" message instead of rewriting files. - Version arguments are validated at the C...

(QB_NEW_EN)


[grammar] ~289-~289: There might be a mistake here.
Context: ...optional pre-release and build metadata. - The legacy bump.doc_files configuratio...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (5)
tests/bdd/features/cli.feature (1)

2-29: Scenarios match new CLI requirements.

All revised scenarios explicitly pass the target version and assert the new outputs; they mirror the CLI changes accurately. Nice coverage of happy path, invalid input, exclusions, and no-op cases.

docs/usage-guide.md (1)

67-91: Documentation update looks great.

The guide now clearly states the version argument requirement, validation rules, exclusion behavior, and output messages—exactly what users need post-change.

tests/unit/test_bump_command.py (1)

1-234: Excellent coverage in the new unit tests.

Great job exercising the full bump pipeline—workspace/member updates, exclusions, normalization, lazy loading, idempotence, and trivia preservation. This suite gives strong confidence the tomlkit workflow behaves as intended.

docs/lading-design.md (1)

265-293: Design notes stay aligned with implementation.

The updated Step 2.1 section accurately reflects the manifest rewrite strategy, exclusion handling, reporting, validation, and retirement of bump.doc_files. Thanks for keeping the design doc in sync.

lading/cli.py (1)

27-207: CLI validation and wiring are solid.

The semantic-version regex, annotated parameter, early validation, and dispatcher updates are cleanly integrated—exactly what's needed for the new bump workflow.

Comment thread lading/commands/bump.py
@leynos
leynos merged commit 079457f into main Oct 14, 2025
4 checks passed
@leynos
leynos deleted the codex/implement-lading-bump-subcommand branch October 14, 2025 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant