stackdoctor 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.github/workflows/release.yml +1 -1
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/PKG-INFO +2 -2
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/README.md +1 -1
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/pyproject.toml +1 -1
- stackdoctor-0.1.2/stackdoctor/__init__.py +9 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/server.py +2 -9
- stackdoctor-0.1.2/tests/test_cli.py +47 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/uv.lock +1 -1
- stackdoctor-0.1.1/stackdoctor/__init__.py +0 -0
- stackdoctor-0.1.1/tests/test_cli.py +0 -19
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.gitattributes +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.github/workflows/demo.yml +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.github/workflows/tests.yml +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.gitignore +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/DEMO.md +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/LICENSE +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/.env.example +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/Dockerfile +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/api.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/enqueue.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/requirements.txt +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/tasks.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/break_worker.ps1 +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/break_worker.sh +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/docker-compose.yml +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/e2e.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/lock_table.ps1 +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/lock_table.sh +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/postgres/init.sql +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/reset.ps1 +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/reset.sh +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/slow_query.ps1 +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/slow_query.sh +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/docs/images/diagnosis.png +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/__main__.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/__init__.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/celery.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/logs.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/postgres.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/redis.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/config.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/diagnose.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/safety.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/tests/test_diagnose.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/tests/test_integration.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/tests/test_logs.py +0 -0
- {stackdoctor-0.1.1 → stackdoctor-0.1.2}/tests/test_safety.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
name: release
|
|
2
2
|
|
|
3
3
|
# Publishes to PyPI with trusted publishing (OIDC), no API token.
|
|
4
|
-
# Triggered by pushing a version tag that matches pyproject.toml
|
|
4
|
+
# Triggered by pushing a version tag that matches pyproject.toml: git tag "v$(uv version --short)" && git push origin --tags
|
|
5
5
|
|
|
6
6
|
on:
|
|
7
7
|
push:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stackdoctor
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Read-only MCP server that diagnoses a Python backend stack (Postgres, Celery, Redis, logs) with cross-system correlation.
|
|
5
5
|
Project-URL: Homepage, https://github.com/lepri89/stackdoctor
|
|
6
6
|
Project-URL: Issues, https://github.com/lepri89/stackdoctor/issues
|
|
@@ -339,7 +339,7 @@ Releases go to PyPI from GitHub Actions with
|
|
|
339
339
|
`lepri89`, repository `stackdoctor`, workflow `release.yml` and environment `pypi`. In GitHub, create
|
|
340
340
|
an environment named `pypi` (Settings → Environments); adding yourself as a required reviewer is a good idea.
|
|
341
341
|
2. Bump `version` in `pyproject.toml`, commit, then tag and push:
|
|
342
|
-
`git tag
|
|
342
|
+
`git tag "v$(uv version --short)" && git push origin "v$(uv version --short)"`.
|
|
343
343
|
The workflow checks that the tag matches the version, runs the tests, builds, and publishes.
|
|
344
344
|
|
|
345
345
|
## License
|
|
@@ -321,7 +321,7 @@ Releases go to PyPI from GitHub Actions with
|
|
|
321
321
|
`lepri89`, repository `stackdoctor`, workflow `release.yml` and environment `pypi`. In GitHub, create
|
|
322
322
|
an environment named `pypi` (Settings → Environments); adding yourself as a required reviewer is a good idea.
|
|
323
323
|
2. Bump `version` in `pyproject.toml`, commit, then tag and push:
|
|
324
|
-
`git tag
|
|
324
|
+
`git tag "v$(uv version --short)" && git push origin "v$(uv version --short)"`.
|
|
325
325
|
The workflow checks that the tag matches the version, runs the tests, builds, and publishes.
|
|
326
326
|
|
|
327
327
|
## License
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""stackdoctor: read-only MCP server that diagnoses a Python backend stack."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
# pyproject.toml is the single source of truth; this reads the installed package's metadata.
|
|
7
|
+
__version__ = version("stackdoctor")
|
|
8
|
+
except PackageNotFoundError: # running from a source tree that isn't installed
|
|
9
|
+
__version__ = "0.0.0+unknown"
|
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
6
|
import functools
|
|
7
|
-
import importlib.metadata
|
|
8
7
|
import inspect
|
|
9
8
|
import logging
|
|
10
9
|
import sys
|
|
@@ -12,6 +11,7 @@ import sys
|
|
|
12
11
|
from mcp.server.mcpserver import MCPServer
|
|
13
12
|
from mcp.types import ToolAnnotations
|
|
14
13
|
|
|
14
|
+
from . import __version__
|
|
15
15
|
from . import diagnose as diag
|
|
16
16
|
from .checks import celery, logs, postgres, redis
|
|
17
17
|
from .config import get_config
|
|
@@ -181,7 +181,7 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
181
181
|
epilog=USAGE_EPILOG,
|
|
182
182
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
183
183
|
)
|
|
184
|
-
parser.add_argument("--version", action="version", version=f"stackdoctor {
|
|
184
|
+
parser.add_argument("--version", action="version", version=f"stackdoctor {__version__}")
|
|
185
185
|
parser.parse_args(argv) # --help / --version print and exit here
|
|
186
186
|
|
|
187
187
|
logging.basicConfig(level=logging.WARNING, stream=sys.stderr) # stdout is the MCP channel
|
|
@@ -189,12 +189,5 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
189
189
|
mcp.run("stdio")
|
|
190
190
|
|
|
191
191
|
|
|
192
|
-
def _version() -> str:
|
|
193
|
-
try:
|
|
194
|
-
return importlib.metadata.version("stackdoctor")
|
|
195
|
-
except importlib.metadata.PackageNotFoundError:
|
|
196
|
-
return "unknown"
|
|
197
|
-
|
|
198
|
-
|
|
199
192
|
if __name__ == "__main__":
|
|
200
193
|
main()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from stackdoctor import server
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.parametrize("flag,expected", [("--help", "usage: stackdoctor"), ("--version", "stackdoctor ")])
|
|
7
|
+
def test_flags_print_and_exit_without_starting_server(flag, expected, capsys, monkeypatch):
|
|
8
|
+
monkeypatch.setattr(server.mcp, "run", lambda *a, **k: pytest.fail("server must not start"))
|
|
9
|
+
with pytest.raises(SystemExit) as exit_info:
|
|
10
|
+
server.main([flag])
|
|
11
|
+
assert exit_info.value.code == 0
|
|
12
|
+
assert expected in capsys.readouterr().out
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_unknown_flag_is_an_error(monkeypatch):
|
|
16
|
+
monkeypatch.setattr(server.mcp, "run", lambda *a, **k: pytest.fail("server must not start"))
|
|
17
|
+
with pytest.raises(SystemExit) as exit_info:
|
|
18
|
+
server.main(["--nope"])
|
|
19
|
+
assert exit_info.value.code == 2
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_version_matches_pyproject(capsys):
|
|
23
|
+
"""pyproject.toml is the single source of truth for the version."""
|
|
24
|
+
import tomllib
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
pyproject = tomllib.loads((Path(__file__).parents[1] / "pyproject.toml").read_text())
|
|
28
|
+
with pytest.raises(SystemExit):
|
|
29
|
+
server.main(["--version"])
|
|
30
|
+
assert capsys.readouterr().out.strip() == f"stackdoctor {pyproject['project']['version']}"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_version_fallback_when_not_installed(monkeypatch):
|
|
34
|
+
import importlib
|
|
35
|
+
import importlib.metadata
|
|
36
|
+
|
|
37
|
+
import stackdoctor
|
|
38
|
+
|
|
39
|
+
def missing(name):
|
|
40
|
+
raise importlib.metadata.PackageNotFoundError(name)
|
|
41
|
+
|
|
42
|
+
monkeypatch.setattr(importlib.metadata, "version", missing)
|
|
43
|
+
try:
|
|
44
|
+
assert importlib.reload(stackdoctor).__version__ == "0.0.0+unknown"
|
|
45
|
+
finally:
|
|
46
|
+
monkeypatch.undo()
|
|
47
|
+
importlib.reload(stackdoctor)
|
|
File without changes
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
from stackdoctor import server
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@pytest.mark.parametrize("flag,expected", [("--help", "usage: stackdoctor"), ("--version", "stackdoctor ")])
|
|
7
|
-
def test_flags_print_and_exit_without_starting_server(flag, expected, capsys, monkeypatch):
|
|
8
|
-
monkeypatch.setattr(server.mcp, "run", lambda *a, **k: pytest.fail("server must not start"))
|
|
9
|
-
with pytest.raises(SystemExit) as exit_info:
|
|
10
|
-
server.main([flag])
|
|
11
|
-
assert exit_info.value.code == 0
|
|
12
|
-
assert expected in capsys.readouterr().out
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_unknown_flag_is_an_error(monkeypatch):
|
|
16
|
-
monkeypatch.setattr(server.mcp, "run", lambda *a, **k: pytest.fail("server must not start"))
|
|
17
|
-
with pytest.raises(SystemExit) as exit_info:
|
|
18
|
-
server.main(["--nope"])
|
|
19
|
-
assert exit_info.value.code == 2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|