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.
Files changed (47) hide show
  1. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.github/workflows/release.yml +1 -1
  2. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/PKG-INFO +2 -2
  3. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/README.md +1 -1
  4. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/pyproject.toml +1 -1
  5. stackdoctor-0.1.2/stackdoctor/__init__.py +9 -0
  6. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/server.py +2 -9
  7. stackdoctor-0.1.2/tests/test_cli.py +47 -0
  8. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/uv.lock +1 -1
  9. stackdoctor-0.1.1/stackdoctor/__init__.py +0 -0
  10. stackdoctor-0.1.1/tests/test_cli.py +0 -19
  11. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.gitattributes +0 -0
  12. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.github/workflows/demo.yml +0 -0
  13. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.github/workflows/tests.yml +0 -0
  14. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/.gitignore +0 -0
  15. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/DEMO.md +0 -0
  16. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/LICENSE +0 -0
  17. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/.env.example +0 -0
  18. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/Dockerfile +0 -0
  19. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/api.py +0 -0
  20. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/enqueue.py +0 -0
  21. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/requirements.txt +0 -0
  22. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/app/tasks.py +0 -0
  23. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/break_worker.ps1 +0 -0
  24. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/break_worker.sh +0 -0
  25. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/docker-compose.yml +0 -0
  26. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/e2e.py +0 -0
  27. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/lock_table.ps1 +0 -0
  28. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/lock_table.sh +0 -0
  29. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/postgres/init.sql +0 -0
  30. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/reset.ps1 +0 -0
  31. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/reset.sh +0 -0
  32. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/slow_query.ps1 +0 -0
  33. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/demo/slow_query.sh +0 -0
  34. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/docs/images/diagnosis.png +0 -0
  35. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/__main__.py +0 -0
  36. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/__init__.py +0 -0
  37. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/celery.py +0 -0
  38. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/logs.py +0 -0
  39. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/postgres.py +0 -0
  40. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/checks/redis.py +0 -0
  41. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/config.py +0 -0
  42. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/diagnose.py +0 -0
  43. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/stackdoctor/safety.py +0 -0
  44. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/tests/test_diagnose.py +0 -0
  45. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/tests/test_integration.py +0 -0
  46. {stackdoctor-0.1.1 → stackdoctor-0.1.2}/tests/test_logs.py +0 -0
  47. {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, e.g. `git tag v0.1.0 && git push origin v0.1.0`.
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.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 v0.1.0 && git push origin v0.1.0`.
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 v0.1.0 && git push origin v0.1.0`.
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stackdoctor"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "Read-only MCP server that diagnoses a Python backend stack (Postgres, Celery, Redis, logs) with cross-system correlation."
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -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 {_version()}")
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)
@@ -1003,7 +1003,7 @@ wheels = [
1003
1003
 
1004
1004
  [[package]]
1005
1005
  name = "stackdoctor"
1006
- version = "0.1.1"
1006
+ version = "0.1.2"
1007
1007
  source = { editable = "." }
1008
1008
  dependencies = [
1009
1009
  { name = "celery" },
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