Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# logpilot 🪵

<!-- badges: start -->
![Python Version](https://img.shields.io/badge/python-3.9%2B-blue)
![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)
![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-d7ff64.svg)
![Security: ruff](https://img.shields.io/badge/security-ruff%20S-orange)
<!-- badges: end -->

A Python CLI tool for analyzing logs using the GitHub Copilot SDK.

## Features
Expand Down
4 changes: 2 additions & 2 deletions logpilot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ def get_version() -> str:
version = data.get("project", {}).get("version")
if version:
return version
except Exception:
pass
except Exception: # noqa: S110
pass # Intentionally silent - version detection is optional
return "unknown"


Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ indent-width = 1
exclude = [".git", "__pycache__", ".venv", "venv", "env", "build", "dist"]

[tool.ruff.lint]
select = ["E", "F", "W", "I"]
select = ["E", "F", "W", "I", "S"]
ignore = []

[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["S101", "S603"] # Allow assert and subprocess in tests

[tool.ruff.format]
indent-style = "tab"

Expand Down
22 changes: 15 additions & 7 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@
def run_cli(args, env=None):
env = dict(os.environ) if env is None else env
env["LOGPILOT_MOCK_LLM"] = "1"
# Construct command with trusted values only
cmd = [sys.executable, "-m", "logpilot.cli"]
# Arguments are safe without quoting when shell=False
cmd.extend(args)
return subprocess.run(
[sys.executable, "-m", "logpilot.cli"] + args,
cmd,
capture_output=True,
text=True,
env=env,
shell=False,
)


Expand Down Expand Up @@ -225,11 +230,14 @@ def test_cli_analyze_basic(tmp_path, monkeypatch):
# Run the CLI analyze command with mock LLM enabled
env = dict(os.environ)
env["LOGPILOT_MOCK_LLM"] = "1"
# Construct command with trusted values only
cmd = [sys.executable, "-m", "logpilot.cli", "analyze", str(log_file)]
result = subprocess.run(
[sys.executable, "-m", "logpilot.cli", "analyze", str(log_file)],
cmd,
capture_output=True,
text=True,
env=env,
shell=False,
)
assert result.returncode == 0, f"stdout: {result.stdout}\nstderr: {result.stderr}"
assert "Something failed" in result.stdout or "critical" in result.stdout.lower()
Expand Down Expand Up @@ -334,7 +342,7 @@ def test_analyze_stdin_rejected(self):
runner = typer.testing.CliRunner()
result = runner.invoke(app, ["analyze", "-"])
assert result.exit_code != 0
output = (result.stdout or "") + (result.stderr or "")
output = result.output
assert "Error: stdin is not supported" in output

def test_analyze_with_max_tokens(self, tmp_path):
Expand Down Expand Up @@ -387,7 +395,7 @@ def test_cli_help_command():
"""Test CLI help command."""
result = run_cli_direct(["--help"])
assert result.exit_code == 0
output = (result.stdout or "") + (result.stderr or "")
output = result.stdout
assert "Logpilot CLI" in output or "Usage" in output


Expand All @@ -396,7 +404,7 @@ def test_cli_analyze_help(tmp_path):
"""Test analyze subcommand help."""
result = run_cli_direct(["analyze", "--help"])
assert result.exit_code == 0
output = (result.stdout or "") + (result.stderr or "")
output = result.stdout
assert "Analyze logs" in output or "analyze" in output.lower()


Expand Down Expand Up @@ -736,14 +744,14 @@ def test_analyze_empty_log_file(self, tmp_path):

result = run_cli_direct(["analyze", str(log_file)])
assert result.exit_code != 0
output = (result.stdout or "") + (result.stderr or "")
output = result.output
assert "No log entries" in output or "Error" in output

def test_analyze_stdin_not_supported(self):
"""Test that stdin input is rejected."""
result = run_cli_direct(["analyze", "-"])
assert result.exit_code != 0
output = (result.stdout or "") + (result.stderr or "")
output = result.output
assert "stdin is not supported" in output

def test_analyze_with_all_options(self, tmp_path):
Expand Down
3 changes: 2 additions & 1 deletion tests/test_llm_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import pytest

# noqa: S603, S607 - Suppress bandit warnings for test code with hardcoded strings
from logpilot.llm_client import LLMClient

# ============================================================================
Expand Down Expand Up @@ -102,7 +103,7 @@ def test_llm_client_via_subprocess():
"""
env = dict(os.environ)
env["LOGPILOT_MOCK_LLM"] = "1"
result = subprocess.run(
result = subprocess.run( # noqa: S603
[sys.executable, "-c", code],
capture_output=True,
text=True,
Expand Down