Skip to content
Open
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- `darnit serve` with no `--framework` now serves OpenSSF Baseline when it
is installed, the same default as `darnit audit`. It used to take the
first installed framework by name, which on a full `darnit-mcp` install
is `gittuf`, so the MCP entry that `darnit install` writes gave clients
the gittuf tools. With several frameworks installed and no baseline it
now asks for `--framework` instead of guessing.
- Installs without the development dependencies work again (`uv tool install`
from a checkout, and any install from a built wheel). Two things broke
them: `tree-sitter-language-pack` 1.6.3, the newest release the old
Expand Down
33 changes: 28 additions & 5 deletions packages/darnit/src/darnit/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,24 @@ def _emit_exit_summary(reason: str, exit_code: int) -> None:
harness_logger.info("harness: %s, exit %d", reason, int(exit_code))


DEFAULT_FRAMEWORK = "openssf-baseline"


def _default_framework(frameworks: list[str]) -> str | None:
"""Pick the framework `darnit serve` uses when none is named.

The same default as `darnit audit` (``load_effective_config_auto``):
OpenSSF Baseline when it is installed. Otherwise the only installed
framework. With several installed and no baseline there is no safe guess,
so the caller has to ask for ``--framework``.
"""
if DEFAULT_FRAMEWORK in frameworks:
return DEFAULT_FRAMEWORK
if len(frameworks) == 1:
return frameworks[0]
return None


def cmd_serve(args: argparse.Namespace) -> int:
"""Start the MCP server.

Expand Down Expand Up @@ -1147,14 +1165,19 @@ def cmd_serve(args: argparse.Namespace) -> int:
allowed = operator_config.config.plugins.allowed
if allowed:
frameworks = [f for f in frameworks if f in allowed]
if frameworks:
framework_name = frameworks[0] # Default to first available
else:
if not frameworks:
logger.error(
"No framework specified and none found. "
"Use 'darnit serve config.toml' or install a framework package."
)
return 1
framework_name = _default_framework(frameworks)
if framework_name is None:
logger.error(
f"No framework specified and several are installed: {', '.join(frameworks)}. "
"Choose one with --framework."
)
return 1

# Get framework path and use it as config
try:
Expand Down Expand Up @@ -1395,7 +1418,7 @@ def create_parser() -> argparse.ArgumentParser:
"Usage:\n"
" darnit serve config.toml # Use TOML config file\n"
" darnit serve --framework NAME # Use named framework\n"
" darnit serve # Auto-detect framework",
" darnit serve # OpenSSF Baseline, if installed",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
serve_parser.add_argument(
Expand All @@ -1405,7 +1428,7 @@ def create_parser() -> argparse.ArgumentParser:
)
serve_parser.add_argument(
"-f", "--framework",
help="Framework to use (default: auto-detect). Ignored if config file is provided.",
help="Framework to use (default: openssf-baseline if installed, else the only installed framework). Ignored if config file is provided.",
)
_add_operator_config_args(serve_parser)
serve_parser.set_defaults(func=cmd_serve)
Expand Down
48 changes: 48 additions & 0 deletions tests/darnit/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,54 @@ def test_install_preserves_existing_settings(tmp_path, monkeypatch):
assert "darnit" in data["mcpServers"]


@pytest.mark.parametrize(
("frameworks", "expected"),
[
(["gittuf", "openssf-baseline", "reproducibility"], "openssf-baseline"),
(["community-spec", "gittuf", "hello", "openssf-baseline"], "openssf-baseline"),
(["gittuf"], "gittuf"),
(["community-spec", "gittuf"], None),
],
)
def test_default_framework(frameworks, expected):
from darnit.cli import _default_framework

assert _default_framework(frameworks) == expected


def _serve_with(monkeypatch, frameworks):
"""Run `darnit serve` with a fake framework list; return (exit code, served names)."""
served = []

class _Server:
def run(self):
pass

def fake_create_server(path, **kwargs):
served.append(path)
return _Server()

monkeypatch.setattr("darnit.config.list_available_frameworks", lambda: list(frameworks))
monkeypatch.setattr("darnit.config.resolve_framework_path", lambda name: name)
monkeypatch.setattr("darnit.server.create_server", fake_create_server)
return main(["serve"]), served


def test_serve_defaults_to_the_baseline_not_the_first_name(monkeypatch):
code, served = _serve_with(monkeypatch, ["gittuf", "openssf-baseline", "reproducibility"])

assert code == 0
assert served == ["openssf-baseline"]


def test_serve_refuses_to_guess_between_other_frameworks(monkeypatch, caplog):
code, served = _serve_with(monkeypatch, ["community-spec", "gittuf"])

assert code == 1
assert served == []
assert any("--framework" in record.message for record in caplog.records)


class TestCreateParser:
"""Tests for CLI argument parsing."""

Expand Down
Loading