Skip to content

feat: add shell completion support#227

Merged
david-waltermire merged 3 commits intometaschema-framework:developfrom
david-waltermire:feature/shell-completion
Jan 9, 2026
Merged

feat: add shell completion support#227
david-waltermire merged 3 commits intometaschema-framework:developfrom
david-waltermire:feature/shell-completion

Conversation

@david-waltermire
Copy link
Copy Markdown
Contributor

@david-waltermire david-waltermire commented Jan 8, 2026

Summary

  • Add shell completion support for bash and zsh shells
  • Register SPI-discovered commands including shell-completion from cli-processor

Usage

# Generate and source bash completion
oscal-cli shell-completion bash > ~/.oscal-completion.bash
source ~/.oscal-completion.bash

# Generate zsh completion
oscal-cli shell-completion zsh > ~/.zsh/completions/_oscal-cli

# Write directly to file
oscal-cli shell-completion bash --to ~/.oscal-completion.bash

Changes

  • Modified CLI.java to register SPI-discovered commands via CommandService
  • Added ShellCompletionTest.java with comprehensive tests

Dependencies

Test plan

  • All 311 oscal-cli tests pass
  • Shell completion tests verify bash/zsh generation
  • File output with --to option works correctly
  • Invalid shell types are handled gracefully

Closes #85

Summary by CodeRabbit

  • New Features

    • CLI now loads discovered/extendable commands at runtime, enabling plugin-style commands (including shell-completion).
    • Shell-completion command available to generate completion scripts for bash and zsh.
  • Tests

    • Added comprehensive tests for shell-completion covering help output, script generation, file output, error handling, and argument validation.

✏️ Tip: You can customize this high-level summary in your review settings.

Register SPI-discovered commands including shell-completion from
cli-processor. This enables users to generate bash and zsh completion
scripts for oscal-cli.

Usage:
  oscal-cli shell-completion bash > completion.bash
  oscal-cli shell-completion zsh > completion.zsh

Added comprehensive tests for:
- Bash and zsh completion script generation
- File output with --to option
- Invalid shell type handling
- Missing argument handling

Closes metaschema-framework#85
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Registers SPI-discovered commands into the CLI processor at runtime and adds tests exercising the shell-completion command (help, bash/zsh generation, file output, invalid args, missing args).

Changes

Cohort / File(s) Summary
CLI command registration
src/main/java/dev/metaschema/oscal/tools/cli/core/CLI.java
Imports CommandService and iterates CommandService.getInstance().getCommands() after built-in registration, injecting each discovered command via processor.addCommandHandler(...).
Shell completion tests
src/test/java/dev/metaschema/oscal/tools/cli/core/ShellCompletionTest.java
New JUnit5 test class with parameterized and individual tests covering help output, bash/zsh script generation, writing completions to files (with cleanup), invalid shell argument rejection, and missing-argument handling.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI_Main as CLI.main
  participant Processor
  participant CommandService
  participant SPI_Command as ShellCompletionCommand

  User->>CLI_Main: start with args
  CLI_Main->>Processor: register built-in commands
  CLI_Main->>CommandService: getInstance().getCommands()
  CommandService-->>CLI_Main: list of discovered commands
  CLI_Main->>Processor: addCommandHandler(discovered commands)
  CLI_Main->>Processor: process(args)
  Processor->>SPI_Command: invoke matching command
  SPI_Command-->>User: output (help/script/file/error)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 I nibble at code, then hop with glee,

SPI seeds commands for the CLI tree,
Bash and zsh twirl in a scripted dance,
Tests keep the completions in fine stance,
A rabbit cheers for each successful prance.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add shell completion support' clearly and concisely describes the primary change in the pull request, which adds shell completion functionality for bash and zsh.
Linked Issues check ✅ Passed The PR implements shell completion support for bash and zsh with a user-facing command, file output support via --to flag, and comprehensive test coverage as required by issue #85.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing shell completion support: CLI.java modification for SPI command registration and ShellCompletionTest.java for testing the new functionality.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
src/test/java/dev/metaschema/oscal/tools/cli/core/ShellCompletionTest.java (1)

78-99: Consider using try-finally or @AfterEach for reliable cleanup.

The test properly validates file output, but the cleanup at line 98 won't execute if an assertion fails. While this is acceptable for CI environments (target/ is typically cleaned), consider moving cleanup to @AfterEach or wrapping in try-finally for better test isolation.

♻️ Optional improvement for cleanup reliability
   @ParameterizedTest
   @ValueSource(strings = { "bash", "zsh" })
   void testShellCompletionToFile(String shell) throws IOException {
     Path outputPath = Path.of("target/completion." + shell);
     String[] args = { "shell-completion", shell, "--to", outputPath.toString() };

-    ExitStatus status = CLI.runCli(args);
-
-    assertAll(
-        () -> assertEquals(ExitCode.OK, status.getExitCode(),
-            "shell-completion " + shell + " --to file should return OK"),
-        () -> assertTrue(Files.exists(outputPath),
-            "Output file should exist"),
-        () -> {
-          String content = Files.readString(outputPath, StandardCharsets.UTF_8);
-          assertTrue(content.contains("oscal-cli"),
-              "Completion script should reference oscal-cli");
-        });
-
-    // Clean up
-    Files.deleteIfExists(outputPath);
+    try {
+      ExitStatus status = CLI.runCli(args);
+
+      assertAll(
+          () -> assertEquals(ExitCode.OK, status.getExitCode(),
+              "shell-completion " + shell + " --to file should return OK"),
+          () -> assertTrue(Files.exists(outputPath),
+              "Output file should exist"),
+          () -> {
+            String content = Files.readString(outputPath, StandardCharsets.UTF_8);
+            assertTrue(content.contains("oscal-cli"),
+                "Completion script should reference oscal-cli");
+          });
+    } finally {
+      // Clean up
+      Files.deleteIfExists(outputPath);
+    }
   }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4589db5 and bc7d028.

📒 Files selected for processing (2)
  • src/main/java/dev/metaschema/oscal/tools/cli/core/CLI.java
  • src/test/java/dev/metaschema/oscal/tools/cli/core/ShellCompletionTest.java
⏰ 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). (2)
  • GitHub Check: Website
  • GitHub Check: Code
🔇 Additional comments (8)
src/main/java/dev/metaschema/oscal/tools/cli/core/CLI.java (2)

10-10: LGTM!

The import is necessary for the SPI-discovered command registration.


84-86: SPI command registration is safe as-is - tests confirm functionality.

The tests in CLITest.java invoke CLI.runCli() multiple times successfully without error handling around CommandService.getInstance().getCommands(). The code follows the established pattern used elsewhere in the codebase (e.g., lines 72-83) where command registration has no defensive wrapping. SPI discovery is working as intended in the test suite.

The thread-safety concern relates to the external CommandService implementation in metaschema-framework, not this code. The CLI runs sequentially and calls getInstance() once during initialization, which is not a multi-threaded scenario requiring additional synchronization at this point.

src/test/java/dev/metaschema/oscal/tools/cli/core/ShellCompletionTest.java (6)

36-43: LGTM!

The test appropriately verifies that the help flag is recognized and returns a success exit code.


48-58: LGTM!

The test properly validates successful bash completion generation with both exit code and exception checks.


63-73: LGTM!

The test validates zsh completion generation with appropriate assertions.


104-112: LGTM!

The test appropriately validates error handling for unsupported shell types.


117-125: LGTM!

The test properly validates the requirement for the shell argument.


1-31: LGTM!

The test class is well-structured with clear documentation, appropriate imports, and comprehensive test coverage addressing issue #85.

Address CodeRabbit review feedback - wrap file output test in
try-finally to ensure cleanup runs even if assertions fail.
@david-waltermire
Copy link
Copy Markdown
Contributor Author

@coderabbitai Addressed the nitpick in commit faadf0d - wrapped file output test in try-finally to ensure cleanup runs even if assertions fail.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 8, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the commit details to verify the change
git show faadf0d --stat
echo "---"
git show faadf0d

Length of output: 2609


@david-waltermire: Thank you for addressing the feedback! I've verified commit faadf0d and the change looks great. The try-finally block ensures that the cleanup code in Files.deleteIfExists(outputPath) will execute regardless of whether the assertions pass or fail, which is exactly the right approach for test reliability. This prevents potential test file pollution in the target/ directory when tests fail.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 (2)
src/test/java/dev/metaschema/oscal/tools/cli/core/ShellCompletionTest.java (2)

81-81: Consider Maven build directory dependency.

The hardcoded "target/" path assumes Maven project structure. While this is standard for Maven projects and should work fine, be aware this might fail in non-standard build configurations or if the target directory doesn't exist before tests run.


93-95: Consider more thorough content validation.

The test only verifies that "oscal-cli" appears in the generated script. While this provides basic smoke-testing, you could optionally validate:

  • The script contains expected shell syntax patterns
  • Specific command names appear in completions
  • The script structure matches bash/zsh completion conventions

However, the current minimal validation may be sufficient depending on your testing strategy.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc7d028 and faadf0d.

📒 Files selected for processing (1)
  • src/test/java/dev/metaschema/oscal/tools/cli/core/ShellCompletionTest.java
⏰ 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: Website
🔇 Additional comments (6)
src/test/java/dev/metaschema/oscal/tools/cli/core/ShellCompletionTest.java (6)

25-31: Good documentation linking to issue #85.

The class-level documentation clearly references the related issue, which helps with traceability.


33-43: Well-structured help test.

The test appropriately verifies that help output returns a successful exit code with clear assertion messages.


45-73: Good use of assertAll for grouped assertions.

Both tests appropriately use assertAll to verify multiple conditions (exit code and absence of exceptions), which provides better test failure reporting.


75-101: Excellent cleanup pattern with try-finally.

The try-finally block ensures file cleanup occurs even if assertions fail, which prevents test pollution. This is a best practice for tests that create filesystem resources.


103-114: Good negative test case for invalid input.

Testing the error path with an unsupported shell type (fish) ensures the command fails gracefully with an appropriate exit code.


116-127: Appropriate edge case for missing arguments.

Testing the missing required argument scenario ensures proper validation and user feedback.

@david-waltermire david-waltermire merged commit 08ea1fe into metaschema-framework:develop Jan 9, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant