Skip to content

Log external command invocations - #26

Merged
leynos merged 2 commits into
mainfrom
codex/log-full-commandline-for-external-calls
Nov 5, 2025
Merged

Log external command invocations#26
leynos merged 2 commits into
mainfrom
codex/log-full-commandline-for-external-calls

Conversation

@leynos

@leynos leynos commented Nov 4, 2025

Copy link
Copy Markdown
Owner

Summary

  • log each publish pre-flight command invocation before execution
  • add a shared helper to render command lines and apply it to cargo metadata loading
  • cover the new logging with unit tests for publish and workspace metadata modules

Testing

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

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

Summary by Sourcery

Log external commands before execution by introducing a shared helper and applying it to cargo metadata loading and publish operations.

New Features:

  • Log external command invocation in load_cargo_metadata
  • Log external command invocation in publish command helper

Enhancements:

  • Add format_command and log_command_invocation helpers in lading.utils.process for consistent command-line logging

Tests:

  • Add unit tests to verify logging of cargo metadata command execution
  • Add unit tests to verify logging of publish command execution

Summary by CodeRabbit

  • Chores

    • Improved logging for external command invocations, now consistently including command text and optional working directory to aid debugging and traceability.
    • Standardized how metadata-related tool invocations are logged across the workspace.
  • Tests

    • Added unit tests verifying command formatting and that command invocation logs include or omit cwd as appropriate, including handling of empty commands.

@sourcery-ai

sourcery-ai Bot commented Nov 4, 2025

Copy link
Copy Markdown

Reviewer's Guide

The PR introduces a shared helper for formatting and logging external command invocations, applies it to both the workspace metadata loader and the publish command, refactors cargo metadata command composition into constants, and adds unit tests to verify the new logging behavior.

Sequence diagram for logging external command invocation in publish command

sequenceDiagram
    participant LOGGER
    participant Publish
    participant log_command_invocation
    participant ExternalCommand
    Publish->>log_command_invocation: log_command_invocation(LOGGER, command, cwd)
    log_command_invocation->>LOGGER: info("Running external command: ...")
    Publish->>ExternalCommand: execute(command, cwd)
Loading

Sequence diagram for logging external command invocation in workspace metadata loading

sequenceDiagram
    participant LOGGER
    participant WorkspaceMetadata
    participant log_command_invocation
    participant Cargo
    WorkspaceMetadata->>log_command_invocation: log_command_invocation(LOGGER, command, root_path)
    log_command_invocation->>LOGGER: info("Running external command: ...")
    WorkspaceMetadata->>Cargo: run(command, cwd=root_path)
Loading

Class diagram for new process helpers and logging integration

classDiagram
    class log_command_invocation {
        +log_command_invocation(logger, command, cwd)
    }
    class format_command {
        +format_command(command)
    }
    class workspace_metadata {
        +load_cargo_metadata(workspace_root)
    }
    class commands_publish {
        +_invoke(command, cwd)
    }
    log_command_invocation <|-- format_command
    workspace_metadata ..> log_command_invocation : uses
    commands_publish ..> log_command_invocation : uses
Loading

File-Level Changes

Change Details Files
Introduce process helpers for command formatting and logging
  • add format_command to render shell-style commands
  • add log_command_invocation to log commands with optional cwd
lading/utils/process.py
Integrate logging and command constants into workspace metadata loader
  • define _CARGO_PROGRAM, _CARGO_METADATA_ARGS, _CARGO_METADATA_COMMAND constants
  • add LOGGER for metadata module
  • update _ensure_command to use new constants
  • invoke log_command_invocation before running cargo metadata
  • refactor _CmdMoxCommand to use constants for command and args
lading/workspace/metadata.py
Log publish command invocations before execution
  • add LOGGER for publish module
  • insert log_command_invocation call in _invoke
lading/commands/publish.py
Add unit tests for logging of external commands
  • test logging in load_cargo_metadata via caplog
  • add publish command logging tests in test_command_logging.py
tests/unit/test_workspace_metadata.py
tests/unit/publish/test_command_logging.py

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 Nov 4, 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

Added a small command-formatting/logging utility and wired it into publish command and cargo-metadata loading so each external command invocation is logged (with optional cwd). New unit tests verify formatting and logged messages.

Changes

Cohort / File(s) Summary
New logging utility
lading/utils/process.py
Adds format_command(command: Sequence[str]) -> str and log_command_invocation(logger, command, cwd) with __all__ export; handles empty sequences and formats commands via shlex.join.
Publish command integration
lading/commands/publish.py
Introduces module-level LOGGER and calls log_command_invocation(LOGGER, command, cwd) at the start of _invoke to record each executed command.
Cargo metadata logging & constants
lading/workspace/metadata.py
Adds _CARGO_PROGRAM, _CARGO_METADATA_ARGS, _CARGO_METADATA_COMMAND, a module LOGGER, updates _ensure_command, _CmdMoxCommand.argv, and routes IPC/direct exec to use the new constants; logs metadata invocations with log_command_invocation.
Unit tests for logging
tests/unit/publish/test_command_logging.py, tests/unit/test_workspace_metadata.py, tests/unit/utils/test_process.py
Adds tests asserting command formatting, handling of empty commands, and that logs include the formatted command and optional cwd for both publish and cargo-metadata flows.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Publish as publish._invoke
    participant Logger as log_command_invocation
    participant Subproc as subprocess (run/invoke)
    Note over Publish,Logger `#f8f4e6`: New logging hook before execution
    Publish->>Logger: format & log command (+cwd)
    Logger-->>Publish: OK
    Publish->>Subproc: execute command
    Subproc-->>Publish: stdout/stderr/exit
Loading
sequenceDiagram
    autonumber
    participant Loader as load_cargo_metadata
    participant Logger as log_command_invocation
    participant CmdEns as _ensure_command / _CmdMoxCommand
    participant Subproc as subprocess / cmd-mox IPC
    Note over Loader,Logger `#eef7ff`: Use _CARGO_PROGRAM and _CARGO_METADATA_ARGS
    Loader->>CmdEns: determine command (program + args)
    CmdEns->>Loader: command tuple / argv
    Loader->>Logger: format & log cargo metadata invocation (+cwd)
    Logger-->>Loader: OK
    Loader->>Subproc: invoke cargo (direct or IPC)
    Subproc-->>Loader: metadata JSON / exit
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review logging messages for exact phrasing used in tests (string matching).
  • Inspect format_command handling of shell-quoting and empty sequences.
  • Verify _CARGO_METADATA_ARGS usage across direct and cmd-mox paths and that argv/IPC assembly is correct.
  • Check tests for brittle cwd/path formatting on different platforms.

Poem

🐇 I hop and I format each little call,

echoing commands down the log-lined hall.
With cwd in my pocket and quotes all neat,
I trumpet each run with a rhythmic beat.
Hooray for logs — tidy, true, and fleet!

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Log external command invocations' accurately and concisely summarizes the main objective of the PR, which is to add logging for external command invocations across multiple modules.
✨ 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/log-full-commandline-for-external-calls

📜 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 6772c60 and 5cee1d3.

📒 Files selected for processing (5)
  • lading/utils/process.py (1 hunks)
  • lading/workspace/metadata.py (5 hunks)
  • tests/unit/publish/test_command_logging.py (1 hunks)
  • tests/unit/test_workspace_metadata.py (3 hunks)
  • tests/unit/utils/test_process.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/test_workspace_metadata.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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/workspace/metadata.py
  • tests/unit/publish/test_command_logging.py
  • tests/unit/utils/test_process.py
  • lading/utils/process.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/publish/test_command_logging.py
  • tests/unit/utils/test_process.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/publish/test_command_logging.py
  • tests/unit/utils/test_process.py
🧬 Code graph analysis (3)
lading/workspace/metadata.py (2)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/utils/process.py (1)
  • log_command_invocation (30-40)
tests/unit/publish/test_command_logging.py (1)
lading/commands/publish.py (1)
  • _invoke (678-702)
tests/unit/utils/test_process.py (1)
lading/utils/process.py (2)
  • format_command (20-27)
  • log_command_invocation (30-40)
⏰ 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 (6)
tests/unit/publish/test_command_logging.py (2)

21-33: Good coverage for cwd logging.

The test exercises the INFO log with cwd and validates stdout/stderr, so the logging helper behavior is covered end-to-end.


35-45: Thanks for capturing the no-cwd case.

Asserting the absence of (cwd= ensures the formatter doesn't leak placeholder text when cwd is omitted.

tests/unit/utils/test_process.py (1)

65-99: Great validation of empty-command logging.

Recording both INFO and WARNING handlers demonstrates the helper emits the placeholder log and warning as intended.

lading/workspace/metadata.py (1)

104-112: Centralized logging looks solid.

Logging via log_command_invocation before execution keeps both direct and cmd-mox paths consistent while preserving the resolved argv fallback.

lading/utils/process.py (2)

20-27: Appreciate the defensive warning.

Returning "" while emitting a module warning keeps formatting side effects contained without hiding potential call-site bugs.


30-40: Helper keeps logging consistent.

The %s placeholders and <empty command> fallback give downstream callers uniform log output.


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.

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `lading/workspace/metadata.py:108-111` </location>
<code_context>
     """Execute ``cargo metadata`` and parse the resulting JSON payload."""
     command = _ensure_command()
     root_path = normalise_workspace_root(workspace_root)
+    log_command_invocation(LOGGER, _CARGO_METADATA_COMMAND, root_path)
     exit_code, stdout, stderr = command.run(retcode=None, cwd=str(root_path))
     stdout_text = _coerce_text(stdout)
</code_context>

<issue_to_address>
**suggestion:** Consider logging the actual command that will be executed, including resolved arguments.

If command arguments become dynamic in the future, ensure the log reflects the actual command object used for execution.

```suggestion
    command = _ensure_command()
    root_path = normalise_workspace_root(workspace_root)
    # Log the actual command object, including resolved arguments
    log_command_invocation(LOGGER, str(command), root_path)
    exit_code, stdout, stderr = command.run(retcode=None, cwd=str(root_path))
```
</issue_to_address>

### Comment 2
<location> `lading/workspace/metadata.py:94` </location>
<code_context>
     except CommandNotFound as exc:
         raise CargoExecutableNotFoundError from exc
-    return cargo["metadata", "--format-version", "1"]
+    return cargo[list(_CARGO_METADATA_ARGS)]


</code_context>

<issue_to_address>
**suggestion:** Passing a list to cargo[...] may be unnecessary if _CARGO_METADATA_ARGS is already a tuple.

Consider passing _CARGO_METADATA_ARGS directly to cargo[...] to avoid unnecessary conversion and improve code clarity.

```suggestion
    return cargo[_CARGO_METADATA_ARGS]
```
</issue_to_address>

### Comment 3
<location> `lading/utils/process.py:16-18` </location>
<code_context>
+    PathType = typ.Any
+
+
+def _command_as_tuple(command: typ.Sequence[str]) -> tuple[str, ...]:
+    """Return ``command`` as an immutable tuple of strings."""
+    return tuple(command)
+
+
</code_context>

<issue_to_address>
**suggestion:** The _command_as_tuple helper may be redundant.

You can remove _command_as_tuple and pass command directly to shlex.join, as it already accepts any sequence.

Suggested implementation:

```python

```

If `_command_as_tuple` is used elsewhere in the file, replace those usages by passing the `command` sequence directly to `shlex.join` or other functions that accept a sequence. For example, change `shlex.join(_command_as_tuple(command))` to `shlex.join(command)`.
</issue_to_address>

### Comment 4
<location> `lading/utils/process.py:21-26` </location>
<code_context>
+def format_command(command: typ.Sequence[str]) -> str:
+    """Return a shell-style representation of ``command`` for logging."""
+    command_tuple = _command_as_tuple(command)
+    if not command_tuple:
+        return ""
+    return shlex.join(command_tuple)
+
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Returning an empty string for an empty command may obscure logging intent.

Consider logging a warning or raising an exception when an empty command is received, as this likely indicates a mistake and may hinder debugging.

```suggestion
import logging

def format_command(command: typ.Sequence[str]) -> str:
    """Return a shell-style representation of ``command`` for logging."""
    command_tuple = _command_as_tuple(command)
    if not command_tuple:
        logging.warning("format_command received an empty command sequence. This may indicate a bug or misconfiguration.")
        return ""
    return shlex.join(command_tuple)
```
</issue_to_address>

### Comment 5
<location> `tests/unit/publish/test_command_logging.py:21-30` </location>
<code_context>
+    LogCaptureFixture = typ.Any
+
+
+def test_invoke_logs_command_with_cwd(
+    tmp_path: Path, caplog: LogCaptureFixture
+) -> None:
+    """``_invoke`` should log the command line and working directory."""
+    caplog.set_level(logging.INFO, logger="lading.commands.publish")
+    exit_code, stdout, stderr = publish._invoke(("echo", "hello"), cwd=tmp_path)
+
+    assert exit_code == 0
+    assert stdout.strip() == "hello"
+    assert stderr == ""
+    expected = f"Running external command: echo hello (cwd={tmp_path})"
+    assert expected in caplog.messages
</code_context>

<issue_to_address>
**suggestion (testing):** Consider adding a test for _invoke when cwd is None.

Please add a test verifying that when cwd is None, the log message does not include the (cwd=...) portion.
</issue_to_address>

### Comment 6
<location> `lading/utils/process.py:29` </location>
<code_context>
+    return shlex.join(command_tuple)
+
+
+def log_command_invocation(
+    logger: LoggerType,
+    command: typ.Sequence[str],
</code_context>

<issue_to_address>
**issue (review_instructions):** Add behavioural and unit tests for log_command_invocation and format_command.

You have added new process execution helpers, but there are no corresponding behavioural or unit tests verifying their functionality. Add tests to ensure correct logging and command formatting.

<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/utils/process.py:24-26` </location>
<code_context>
def format_command(command: typ.Sequence[str]) -> str:
    """Return a shell-style representation of ``command`` for logging."""
    command_tuple = _command_as_tuple(command)
    if not command_tuple:
        return ""
    return shlex.join(command_tuple)

</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/))
- Replace if statement with if expression ([`assign-if-exp`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/assign-if-exp/))

```suggestion
    return "" if not command_tuple else shlex.join(command_tuple)
```
</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/workspace/metadata.py
Comment thread lading/workspace/metadata.py Outdated
Comment thread lading/utils/process.py Outdated
Comment thread lading/utils/process.py Outdated
Comment thread tests/unit/publish/test_command_logging.py
Comment thread lading/utils/process.py Outdated
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: 0

🧹 Nitpick comments (1)
lading/utils/process.py (1)

16-18: Consider inlining this helper.

The _command_as_tuple function is only called once in format_command and simply wraps tuple(command). Inlining this conversion directly in format_command would reduce indirection without sacrificing clarity.

Apply this diff if you'd like to simplify:

-def _command_as_tuple(command: typ.Sequence[str]) -> tuple[str, ...]:
-    """Return ``command`` as an immutable tuple of strings."""
-    return tuple(command)
-
-
 def format_command(command: typ.Sequence[str]) -> str:
     """Return a shell-style representation of ``command`` for logging."""
-    command_tuple = _command_as_tuple(command)
+    command_tuple = tuple(command)
     if not command_tuple:
         return ""
     return shlex.join(command_tuple)
📜 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 ff0cc4f and 6772c60.

📒 Files selected for processing (5)
  • lading/commands/publish.py (4 hunks)
  • lading/utils/process.py (1 hunks)
  • lading/workspace/metadata.py (5 hunks)
  • tests/unit/publish/test_command_logging.py (1 hunks)
  • tests/unit/test_workspace_metadata.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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/workspace/metadata.py
  • lading/commands/publish.py
  • tests/unit/test_workspace_metadata.py
  • tests/unit/publish/test_command_logging.py
  • lading/utils/process.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_workspace_metadata.py
  • tests/unit/publish/test_command_logging.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_workspace_metadata.py
  • tests/unit/publish/test_command_logging.py
🧬 Code graph analysis (4)
lading/workspace/metadata.py (2)
lading/utils/path.py (1)
  • normalise_workspace_root (10-16)
lading/utils/process.py (1)
  • log_command_invocation (29-39)
lading/commands/publish.py (1)
lading/utils/process.py (1)
  • log_command_invocation (29-39)
tests/unit/test_workspace_metadata.py (1)
lading/workspace/metadata.py (2)
  • run (130-152)
  • load_cargo_metadata (104-122)
tests/unit/publish/test_command_logging.py (1)
lading/commands/publish.py (1)
  • _invoke (678-702)
⏰ 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/commands/publish.py (3)

7-7: LGTM: Imports added for logging functionality.

The logging import and the log_command_invocation utility import are correctly placed and support the new logging instrumentation.

Also applies to: 16-16


27-28: LGTM: Module logger follows best practices.

The module-level logger is correctly initialized using logging.getLogger(__name__), following the coding guidelines for avoiding the root logger.


682-682: LGTM: Command invocation logging correctly placed.

The logging call is appropriately positioned at the start of _invoke, ensuring all external command executions are traced before they run.

tests/unit/publish/test_command_logging.py (1)

21-32: LGTM: Comprehensive test for command logging.

The test thoroughly verifies that _invoke logs the command line and working directory, while also confirming the command executes correctly. The use of caplog and specific logger targeting is appropriate.

tests/unit/test_workspace_metadata.py (2)

7-7: LGTM: Logging import added for test.

The logging import supports the new test that verifies command logging behavior.


322-353: LGTM: Well-structured test for cargo metadata logging.

The test effectively verifies that load_cargo_metadata logs the cargo command with its working directory. The fake command approach isolates the logging behavior from actual cargo execution, and the assertion correctly accounts for path resolution.

lading/workspace/metadata.py (5)

6-6: LGTM: Logging imports correctly added.

The logging infrastructure imports are properly placed to support the new command logging functionality.

Also applies to: 14-14


78-83: LGTM: Well-structured cargo command constants.

The introduction of _CARGO_PROGRAM, _CARGO_METADATA_ARGS, and _CARGO_METADATA_COMMAND centralizes the cargo invocation details, making the code more maintainable. The module logger follows best practices.


91-91: LGTM: Constants correctly applied in command construction.

The use of _CARGO_PROGRAM and _CARGO_METADATA_ARGS constants eliminates hardcoded strings and ensures consistency across the module.

Also applies to: 94-94


110-110: LGTM: Cargo metadata logging correctly integrated.

The logging call is properly positioned to trace cargo metadata invocations before execution, using the normalized workspace root path for context.


128-128: LGTM: Constants consistently applied in cmd-mox path.

The cmd-mox command proxy correctly uses _CARGO_METADATA_ARGS and _CARGO_PROGRAM, ensuring consistent cargo invocation across both direct and IPC execution paths.

Also applies to: 146-146

lading/utils/process.py (4)

1-14: LGTM: Well-structured module header and type imports.

The module follows best practices with from __future__ import annotations and uses TYPE_CHECKING to avoid runtime import costs for type hints.


21-26: LGTM: Command formatting uses shell-safe representation.

The use of shlex.join ensures proper shell quoting and escaping for logged commands. The empty command guard is a good defensive practice.


29-39: LGTM: Logging function follows parameterized logging best practices.

The implementation correctly uses parameterized logging with %s placeholders rather than f-strings, adhering to the coding guidelines (LOG004). The conditional formatting for cwd is clear and appropriate.


42-42: LGTM: Public API correctly defined.

The __all__ export list appropriately includes only the public functions, keeping the private helper internal.

@codescene-delta-analysis codescene-delta-analysis 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.

Gates Passed
6 Quality Gates Passed

See analysis details in CodeScene

Absence of Expected Change Pattern

  • lading/lading/commands/publish.py is usually changed with: lading/tests/bdd/steps/test_publish_steps.py

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

@leynos
leynos merged commit 379723a into main Nov 5, 2025
4 checks passed
@leynos
leynos deleted the codex/log-full-commandline-for-external-calls branch November 5, 2025 13:57
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