diff --git a/src/jira_hub/cli.py b/src/jira_hub/cli.py index de3a4a1..67c10b4 100644 --- a/src/jira_hub/cli.py +++ b/src/jira_hub/cli.py @@ -1,87 +1 @@ -"""Команды jira-hub: init (настройки из config.example.json), serve (HTTP), import (CSV-пакет), mcp (stdio), tokens, -agent publish (версия агента для самообновления).""" -import argparse -import json -import sys - -from . import db, importer, paths, projects, settings - - -def main(argv=None) -> int: - ap = argparse.ArgumentParser(prog="jira-hub", description="Мост Jira для АРМ КА: хаб") - sub = ap.add_subparsers(dest="cmd", required=True) - p = sub.add_parser("init", help="перенести настройки проекта из config.example.json, создать токены") - p.add_argument("--config", required=True) - p.add_argument("--force", action="store_true") - p = sub.add_parser("serve", help="запустить HTTP API") - p.add_argument("--host", default="0.0.0.0") - p.add_argument("--port", type=int) - p = sub.add_parser("import", help="импорт CSV-пакета admin/jira-sync в заметки") - p.add_argument("folder") - p = sub.add_parser("mcp", help="MCP-сервер по stdio") - p.add_argument("--project", help="метка проекта, к которому привязана сессия") - sub.add_parser("tokens", help="показать токены агента и клиента") - p = sub.add_parser("agent", help="версия агента для самообновления") - agent_sub = p.add_subparsers(dest="agent_cmd", required=True) - p = agent_sub.add_parser("publish", help="опубликовать agent.py (версия только вверх)") - p.add_argument("path") - args = ap.parse_args(argv) - - if args.cmd == "agent": - from . import release - try: - m = release.publish(paths.release_dir(), args.path) - except release.PublishError as e: - print(e, file=sys.stderr) - return 1 - print(f"опубликована v{m['version']}, sha256 {m['sha256'][:12]}…, {m['size']} байт") - return 0 - - if args.cmd == "serve": - from . import api - hub = api.Hub(paths.db_path()) - conn = hub.conn() - port = args.port or int(settings.get(conn, "port") or 8790) - conn.close() - api.serve(hub, args.host, port) - return 0 - if args.cmd == "mcp": - from . import mcp_server - mcp_server.run(args.project) - return 0 - - conn = db.connect(paths.db_path()) - settings.ensure_defaults(conn) - try: - if args.cmd == "init": - with open(args.config, encoding="utf-8") as f: - cfg = json.load(f) - try: - keys = settings.init_from_config(conn, cfg, force=args.force) - except RuntimeError as e: - print(e, file=sys.stderr) - return 1 - print("перенесено: " + ", ".join(keys)) - if projects.ensure_legacy(conn, cfg.get("sprints", {}).get("board_id")): - board = projects.get(conn, projects.LEGACY_LABEL)["board_id"] - print(f"проект {projects.LEGACY_LABEL} «{projects.LEGACY_NAME}», доска {board}") - _print_tokens(conn) - return 0 - if args.cmd == "import": - print(json.dumps(importer.import_package(conn, args.folder), ensure_ascii=False, indent=1)) - return 0 - if args.cmd == "tokens": - _print_tokens(conn) - return 0 - finally: - conn.close() - return 2 - - -def _print_tokens(conn) -> None: - tok = settings.tokens(conn) - print(f"агентский токен: {tok['agent']}\nклиентский токен: {tok['client']}\nбаза: {paths.db_path()}") - - -if __name__ == "__main__": - sys.exit(main()) +from .core.cli import main diff --git a/src/jira_hub/core/__init__.py b/src/jira_hub/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/jira_hub/api.py b/src/jira_hub/core/api.py similarity index 98% rename from src/jira_hub/api.py rename to src/jira_hub/core/api.py index 3e5644f..3118b98 100644 --- a/src/jira_hub/api.py +++ b/src/jira_hub/core/api.py @@ -15,8 +15,9 @@ from pathlib import Path from typing import NamedTuple from urllib.parse import parse_qs, quote, unquote, urlparse -from . import attachments, backup, db, files, history, mirror, projects, queue, relay, release, settings -from .commands import CommandError +from . import backup, db, files, projects, queue, relay, release, settings +from ..modules.jira import attachments, history, mirror +from ..modules.jira.commands import CommandError from .paths import backups_dir as default_backups_dir from .paths import secure_dir from .queue import ClaimedError @@ -24,7 +25,7 @@ from .queue import ClaimedError log = logging.getLogger(__name__) MAX_BODY = 16 * 1024 * 1024 # предел тела запроса, байт -WEB_APP = Path(__file__).parent / "web" / "app" # сборка веб-приложения (make web, не в git); читается на каждый запрос +WEB_APP = Path(__file__).resolve().parent.parent / "web" / "app" # сборка веб-приложения (make web, не в git); читается на каждый запрос ASSET_TYPES = {".js": "text/javascript; charset=utf-8", ".css": "text/css; charset=utf-8", ".svg": "image/svg+xml", ".png": "image/png", ".ico": "image/x-icon", ".woff2": "font/woff2", ".map": "application/json"} ASSET_CACHE = {"Cache-Control": "public, max-age=31536000, immutable"} # имена файлов сборки содержат хэш (R-3) diff --git a/src/jira_hub/backup.py b/src/jira_hub/core/backup.py similarity index 100% rename from src/jira_hub/backup.py rename to src/jira_hub/core/backup.py diff --git a/src/jira_hub/core/cli.py b/src/jira_hub/core/cli.py new file mode 100644 index 0000000..33a83d5 --- /dev/null +++ b/src/jira_hub/core/cli.py @@ -0,0 +1,88 @@ +"""Команды jira-hub: init (настройки из config.example.json), serve (HTTP), import (CSV-пакет), mcp (stdio), tokens, +agent publish (версия агента для самообновления).""" +import argparse +import json +import sys + +from . import db, paths, projects, settings +from ..modules.jira import importer + + +def main(argv=None) -> int: + ap = argparse.ArgumentParser(prog="jira-hub", description="Мост Jira для АРМ КА: хаб") + sub = ap.add_subparsers(dest="cmd", required=True) + p = sub.add_parser("init", help="перенести настройки проекта из config.example.json, создать токены") + p.add_argument("--config", required=True) + p.add_argument("--force", action="store_true") + p = sub.add_parser("serve", help="запустить HTTP API") + p.add_argument("--host", default="0.0.0.0") + p.add_argument("--port", type=int) + p = sub.add_parser("import", help="импорт CSV-пакета admin/jira-sync в заметки") + p.add_argument("folder") + p = sub.add_parser("mcp", help="MCP-сервер по stdio") + p.add_argument("--project", help="метка проекта, к которому привязана сессия") + sub.add_parser("tokens", help="показать токены агента и клиента") + p = sub.add_parser("agent", help="версия агента для самообновления") + agent_sub = p.add_subparsers(dest="agent_cmd", required=True) + p = agent_sub.add_parser("publish", help="опубликовать agent.py (версия только вверх)") + p.add_argument("path") + args = ap.parse_args(argv) + + if args.cmd == "agent": + from . import release + try: + m = release.publish(paths.release_dir(), args.path) + except release.PublishError as e: + print(e, file=sys.stderr) + return 1 + print(f"опубликована v{m['version']}, sha256 {m['sha256'][:12]}…, {m['size']} байт") + return 0 + + if args.cmd == "serve": + from . import api + hub = api.Hub(paths.db_path()) + conn = hub.conn() + port = args.port or int(settings.get(conn, "port") or 8790) + conn.close() + api.serve(hub, args.host, port) + return 0 + if args.cmd == "mcp": + from . import mcp_server + mcp_server.run(args.project) + return 0 + + conn = db.connect(paths.db_path()) + settings.ensure_defaults(conn) + try: + if args.cmd == "init": + with open(args.config, encoding="utf-8") as f: + cfg = json.load(f) + try: + keys = settings.init_from_config(conn, cfg, force=args.force) + except RuntimeError as e: + print(e, file=sys.stderr) + return 1 + print("перенесено: " + ", ".join(keys)) + if projects.ensure_legacy(conn, cfg.get("sprints", {}).get("board_id")): + board = projects.get(conn, projects.LEGACY_LABEL)["board_id"] + print(f"проект {projects.LEGACY_LABEL} «{projects.LEGACY_NAME}», доска {board}") + _print_tokens(conn) + return 0 + if args.cmd == "import": + print(json.dumps(importer.import_package(conn, args.folder), ensure_ascii=False, indent=1)) + return 0 + if args.cmd == "tokens": + _print_tokens(conn) + return 0 + finally: + conn.close() + return 2 + + +def _print_tokens(conn) -> None: + tok = settings.tokens(conn) + print(f"агентский токен: {tok['agent']}\nклиентский токен: {tok['client']}\nбаза: {paths.db_path()}") + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/jira_hub/client.py b/src/jira_hub/core/client.py similarity index 100% rename from src/jira_hub/client.py rename to src/jira_hub/core/client.py diff --git a/src/jira_hub/db.py b/src/jira_hub/core/db.py similarity index 100% rename from src/jira_hub/db.py rename to src/jira_hub/core/db.py diff --git a/src/jira_hub/files.py b/src/jira_hub/core/files.py similarity index 100% rename from src/jira_hub/files.py rename to src/jira_hub/core/files.py diff --git a/src/jira_hub/mcp_server.py b/src/jira_hub/core/mcp_server.py similarity index 99% rename from src/jira_hub/mcp_server.py rename to src/jira_hub/core/mcp_server.py index de4b6aa..1a6093c 100644 --- a/src/jira_hub/mcp_server.py +++ b/src/jira_hub/core/mcp_server.py @@ -15,9 +15,9 @@ from pathlib import Path from mcp.server.fastmcp import FastMCP from . import paths -from .attachments import fmt_size +from ..modules.jira.attachments import fmt_size from .client import HubClient, HubError, HubUnavailable -from .commands import describe, is_temp +from ..modules.jira.commands import describe, is_temp from .files import MAX_SIZE OFFLINE = "хаб не запущен: jira-hub serve" @@ -25,7 +25,7 @@ POLL_SECONDS = 5 AUTHOR = "claude" BOTH_FILE_AND_PATH = "у ensure_attachment либо path, либо file" NOT_BOUND = "сессия не привязана к проекту: запустите MCP с --project <метка> (список — projects)" -HUB_ROOT = Path(__file__).resolve().parents[2] # корень репозитория хаба, из которого загружен пакет (решение 19) +HUB_ROOT = Path(__file__).resolve().parents[3] # корень репозитория хаба, из которого загружен пакет (решение 19) PROJECT: str | None = None # привязка сессии, задаётся run(project) mcp = FastMCP("jira-hub") diff --git a/src/jira_hub/paths.py b/src/jira_hub/core/paths.py similarity index 100% rename from src/jira_hub/paths.py rename to src/jira_hub/core/paths.py diff --git a/src/jira_hub/projects.py b/src/jira_hub/core/projects.py similarity index 100% rename from src/jira_hub/projects.py rename to src/jira_hub/core/projects.py diff --git a/src/jira_hub/queue.py b/src/jira_hub/core/queue.py similarity index 99% rename from src/jira_hub/queue.py rename to src/jira_hub/core/queue.py index b6a4c5a..f160d3d 100644 --- a/src/jira_hub/queue.py +++ b/src/jira_hub/core/queue.py @@ -6,8 +6,9 @@ import logging from datetime import timedelta from . import backup as _backup # noqa: F401 (имя модуля занято параметром backup в claim) -from . import commands as C -from . import attachments, db, files, history, mirror, projects, settings +from ..modules.jira import commands as C +from . import db, files, projects, settings +from ..modules.jira import attachments, history, mirror log = logging.getLogger(__name__) diff --git a/src/jira_hub/relay.py b/src/jira_hub/core/relay.py similarity index 100% rename from src/jira_hub/relay.py rename to src/jira_hub/core/relay.py diff --git a/src/jira_hub/release.py b/src/jira_hub/core/release.py similarity index 100% rename from src/jira_hub/release.py rename to src/jira_hub/core/release.py diff --git a/src/jira_hub/settings.py b/src/jira_hub/core/settings.py similarity index 100% rename from src/jira_hub/settings.py rename to src/jira_hub/core/settings.py diff --git a/src/jira_hub/modules/__init__.py b/src/jira_hub/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/jira_hub/modules/jira/__init__.py b/src/jira_hub/modules/jira/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/jira_hub/attachments.py b/src/jira_hub/modules/jira/attachments.py similarity index 99% rename from src/jira_hub/attachments.py rename to src/jira_hub/modules/jira/attachments.py index a9cd322..633e8ee 100644 --- a/src/jira_hub/attachments.py +++ b/src/jira_hub/modules/jira/attachments.py @@ -3,7 +3,7 @@ базу и хранилище файлов, очередь и HTTP не знает.""" import re -from . import files +from ...core import files # lookahead: совпадения перекрываются, поэтому одиночный «!» ранее в тексте не съедает открывающий «!» настоящей ссылки; # после открывающего «!» пробела нет (как в Jira), иначе закрывающий «!» одной картинки породил бы ложное имя diff --git a/src/jira_hub/commands.py b/src/jira_hub/modules/jira/commands.py similarity index 100% rename from src/jira_hub/commands.py rename to src/jira_hub/modules/jira/commands.py diff --git a/src/jira_hub/history.py b/src/jira_hub/modules/jira/history.py similarity index 99% rename from src/jira_hub/history.py rename to src/jira_hub/modules/jira/history.py index 0637b57..a67e5cf 100644 --- a/src/jira_hub/history.py +++ b/src/jira_hub/modules/jira/history.py @@ -2,7 +2,7 @@ refresh. Пишет таблицу только приём журнала (POST /agent/history); API и MCP её только читают (spec data-model).""" from datetime import timedelta -from . import db, projects, settings +from ...core import db, projects, settings SINCE_MARGIN = timedelta(minutes=15) # расхождение часов Mac mini и Jira и длительность прогона (design #5) diff --git a/src/jira_hub/importer.py b/src/jira_hub/modules/jira/importer.py similarity index 98% rename from src/jira_hub/importer.py rename to src/jira_hub/modules/jira/importer.py index 7e99b50..a94bc07 100644 --- a/src/jira_hub/importer.py +++ b/src/jira_hub/modules/jira/importer.py @@ -4,7 +4,8 @@ import csv from pathlib import Path -from . import db, mirror, settings +from . import mirror +from ...core import db, settings def _rows(path: Path) -> list[dict]: diff --git a/src/jira_hub/mirror.py b/src/jira_hub/modules/jira/mirror.py similarity index 99% rename from src/jira_hub/mirror.py rename to src/jira_hub/modules/jira/mirror.py index 2ebcafe..6184cfc 100644 --- a/src/jira_hub/mirror.py +++ b/src/jira_hub/modules/jira/mirror.py @@ -2,7 +2,8 @@ Зеркало пишет только загрузка состояния от агента; заметки обновление зеркала не трогает.""" import json -from . import attachments, db, history +from . import attachments, history +from ...core import db ISSUE_COLUMNS = ["key", "id", "issue_type", "summary", "description", "status", "priority", "assignee", "reporter", "duedate", "planned_start", "planned_end", "epic_key", "labels", "sprint", "created", "jira_updated"] diff --git a/tests/conftest.py b/tests/conftest.py index b0065ce..d937127 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,7 @@ ResourceWarning «unclosed database» (hub-hygiene-d, решение 14; M-9/O-4 #4).""" import pytest -from jira_hub import db, projects +from jira_hub.core import db, projects @pytest.fixture diff --git a/tests/test_api.py b/tests/test_api.py index 66e6497..009af3a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -11,9 +11,9 @@ from datetime import timedelta import pytest -from jira_hub import api, db, files, projects, queue, release, settings -from jira_hub.client import HubClient, HubError -from jira_hub.commands import CommandError +from jira_hub.core import api, db, files, projects, queue, release, settings +from jira_hub.core.client import HubClient, HubError +from jira_hub.modules.jira.commands import CommandError @pytest.fixture @@ -157,7 +157,7 @@ def test_backup_created_on_first_write(hub, tmp_path): def test_client_unavailable(): - from jira_hub.client import HubUnavailable + from jira_hub.core.client import HubUnavailable with pytest.raises(HubUnavailable): HubClient("http://127.0.0.1:9", "t", timeout=1).get("/issues") @@ -1040,7 +1040,7 @@ ATT_NAME = "скрин 1.png" def _seed_attachment(hub, mime="image/png", size=3, name=ATT_NAME): conn = hub["hub"].conn() try: - from jira_hub import mirror + from jira_hub.modules.jira import mirror mirror.apply_state(conn, [{"key": "A-1", "summary": "т", "labels": [], "attachments": [ {"id": "31337", "filename": name, "size": size, "mime_type": mime, "created": "2026-10-20T10:00:00.000+0300"}]}], []) finally: diff --git a/tests/test_attachments.py b/tests/test_attachments.py index e1be1ed..a66812a 100644 --- a/tests/test_attachments.py +++ b/tests/test_attachments.py @@ -2,7 +2,8 @@ import json import pytest -from jira_hub import attachments, db, files, mirror +from jira_hub.core import db, files +from jira_hub.modules.jira import attachments, mirror def test_fmt_size(): diff --git a/tests/test_baseline.py b/tests/test_baseline.py index 6df8183..9313145 100644 --- a/tests/test_baseline.py +++ b/tests/test_baseline.py @@ -5,7 +5,7 @@ import json import os from pathlib import Path -from jira_hub import api, db, mcp_server, settings +from jira_hub.core import api, db, mcp_server, settings FIXTURES = Path(__file__).parent / "fixtures" / "baseline" diff --git a/tests/test_cli.py b/tests/test_cli.py index ec6e092..b2fe0ec 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,7 +2,7 @@ import json import os from pathlib import Path -from jira_hub import cli, paths, projects, release, settings +from jira_hub.core import cli, paths, projects, release, settings FIXTURE = Path(__file__).parent / "fixtures" / "config.example.json" @@ -67,7 +67,7 @@ def test_agent_publish_write_failure(tmp_path, monkeypatch, capsys): def test_mcp_project_option(monkeypatch): - from jira_hub import mcp_server + from jira_hub.core import mcp_server seen = [] monkeypatch.setattr(mcp_server, "run", lambda project=None: seen.append(project)) assert cli.main(["mcp", "--project", "AWPCA"]) == 0 diff --git a/tests/test_commands.py b/tests/test_commands.py index be46d17..f9d3b57 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1,7 +1,7 @@ import pytest -from jira_hub import commands, mirror -from jira_hub.commands import CommandError +from jira_hub.modules.jira import commands, mirror +from jira_hub.modules.jira.commands import CommandError NOW = "strftime('%Y-%m-%dT%H:%M:%S.000+0300', 'now')" diff --git a/tests/test_db.py b/tests/test_db.py index 904b4cf..95001f8 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -7,7 +7,7 @@ import threading import pytest -from jira_hub import backup, db, paths, projects, queue, settings +from jira_hub.core import backup, db, paths, projects, queue, settings def test_connect_creates_schema(open_db): @@ -154,7 +154,7 @@ def test_data_dir_is_private(tmp_path): def test_existing_backups_dir_made_private_on_start(tmp_path): - from jira_hub import api + from jira_hub.core import api backups = tmp_path / "backups" backups.mkdir() backups.chmod(0o755) diff --git a/tests/test_e2e_hub.py b/tests/test_e2e_hub.py index 16db23d..a473180 100644 --- a/tests/test_e2e_hub.py +++ b/tests/test_e2e_hub.py @@ -2,8 +2,8 @@ import threading import pytest -from jira_hub import api, settings -from jira_hub.client import HubClient, HubError +from jira_hub.core import api, settings +from jira_hub.core.client import HubClient, HubError @pytest.fixture diff --git a/tests/test_files.py b/tests/test_files.py index c7c978d..bbdf65a 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -7,7 +7,7 @@ import time import pytest -from jira_hub import db, files, settings +from jira_hub.core import db, files, settings ABC = hashlib.sha256(b"abc").hexdigest() # ba7816bf… DAY = 86400 diff --git a/tests/test_history.py b/tests/test_history.py index 770dd32..5ab7e6b 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -1,6 +1,6 @@ import pytest -from jira_hub import history +from jira_hub.modules.jira import history ITEM = {"history_id": "5001", "seq": 0, "key": "X", "created": "2026-10-22T09:00:00.000+0300", "author": "Петров Пётр", "field_id": "status", "field_name": "status", "from": "3", "from_string": "В работе", "to": "10001", @@ -29,7 +29,7 @@ def test_record_keeps_first_version(conn): def test_record_uses_field_settings(conn): - from jira_hub import settings + from jira_hub.core import settings settings.set_(conn, "field_ids", {"planned_end": "customfield_10201"}) history.record_changes(conn, [{**ITEM, "field_id": "customfield_10201", "field_name": "Плановое окончание"}]) assert conn.execute("SELECT field FROM issue_change").fetchone()["field"] == "planned_end" @@ -92,7 +92,7 @@ def test_feed_closed_in_two_hours(conn): # сце def test_feed_comment(conn): # «Комментарий в ленте» - from jira_hub import mirror + from jira_hub.modules.jira import mirror mirror.apply_state(conn, [{"key": "X", "labels": ["AWPCA"], "comments": [ {"id": "7", "author": "Иванов", "body": "Готово к проверке", "created": "2026-10-22T13:30:00.000+0300"}]}], []) got = history.feed(conn, "2026-10-22T10:00:00+00:00", "2026-10-22T12:00:00+00:00", history.DEFAULT_KINDS) @@ -163,7 +163,7 @@ def test_parse_kinds_unknown(): def test_issue_changes(conn): # «Задача с историей» - from jira_hub import mirror + from jira_hub.modules.jira import mirror _issue(conn, "X", ["AWPCA"]) _change(conn, "X", "1", "2026-10-21T11:00:00Z", "status", "a", "b") _change(conn, "X", "2", "2026-10-22T11:00:00Z", "description", "c", "d", field_id="description") diff --git a/tests/test_importer.py b/tests/test_importer.py index 00edce1..949919b 100644 --- a/tests/test_importer.py +++ b/tests/test_importer.py @@ -2,7 +2,8 @@ import csv import pytest -from jira_hub import importer, mirror, settings +from jira_hub.core import settings +from jira_hub.modules.jira import importer, mirror @pytest.fixture diff --git a/tests/test_layout.py b/tests/test_layout.py new file mode 100644 index 0000000..2671470 --- /dev/null +++ b/tests/test_layout.py @@ -0,0 +1,23 @@ +"""Раскладка пакетов: чистый импорт и пути, зависящие от места файла.""" +import subprocess +import sys +from pathlib import Path + +import jira_hub + + +def test_fresh_imports(): + r = subprocess.run( + [sys.executable, "-c", "import jira_hub.core.api, jira_hub.core.mcp_server, jira_hub.cli"], + capture_output=True, text=True) + assert r.returncode == 0, r.stderr + + +def test_web_app_path(): + from jira_hub.core import api + assert api.WEB_APP == Path(jira_hub.__file__).parent / "web" / "app" + + +def test_hub_root(): + from jira_hub.core import mcp_server + assert (mcp_server.HUB_ROOT / "pyproject.toml").is_file() diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 60fcb33..8dd0f61 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -9,8 +9,9 @@ from types import SimpleNamespace import pytest -from jira_hub import api, commands, files, mcp_server, settings -from jira_hub.client import HubClient +from jira_hub.core import api, files, mcp_server, settings +from jira_hub.modules.jira import commands +from jira_hub.core.client import HubClient @pytest.fixture @@ -811,7 +812,7 @@ def test_attachment_failed(downloads, monkeypatch): def test_attachment_413(downloads, monkeypatch): - from jira_hub.client import HubError + from jira_hub.core.client import HubError att = _att("5", "запись.mp4", 26214400) hub = FakeHub(_detail(key=ATT_KEY, attachments=[att]), fetch=HubError(413, "too_large", "большой")) _use(monkeypatch, hub) @@ -820,7 +821,7 @@ def test_attachment_413(downloads, monkeypatch): def test_attachment_409_passes_hub_text(downloads, monkeypatch): - from jira_hub.client import HubError + from jira_hub.core.client import HubError hub = FakeHub(_detail(key=ATT_KEY, attachments=[_att("5", "a.png", 1)]), fetch=HubError(409, "agent_too_old", "агент v9 не умеет скачивать вложения — нужен v14")) _use(monkeypatch, hub) diff --git a/tests/test_mirror.py b/tests/test_mirror.py index fa74ae0..fe33e17 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -1,6 +1,7 @@ import pytest -from jira_hub import db, mirror +from jira_hub.core import db +from jira_hub.modules.jira import mirror @pytest.fixture diff --git a/tests/test_projects.py b/tests/test_projects.py index 12ce18b..04f254c 100644 --- a/tests/test_projects.py +++ b/tests/test_projects.py @@ -1,6 +1,7 @@ import pytest -from jira_hub import mirror, projects, settings +from jira_hub.core import projects, settings +from jira_hub.modules.jira import mirror @pytest.fixture diff --git a/tests/test_queue.py b/tests/test_queue.py index 2d663dd..4249a37 100644 --- a/tests/test_queue.py +++ b/tests/test_queue.py @@ -2,9 +2,10 @@ import hashlib import pytest -from jira_hub import attachments, db, files, mirror, projects, queue, settings -from jira_hub.commands import CommandError -from jira_hub.queue import ClaimedError +from jira_hub.core import db, files, projects, queue, settings +from jira_hub.modules.jira import attachments, mirror +from jira_hub.modules.jira.commands import CommandError +from jira_hub.core.queue import ClaimedError LT = {"связанные": "ASUPR_Связанные"} @@ -207,7 +208,7 @@ def test_no_backup_for_refresh_batch(conn): def test_make_backup_rotates(tmp_path, open_db): - from jira_hub import backup + from jira_hub.core import backup conn = open_db(tmp_path / "hub.sqlite") for i in range(4): backup.make_backup(conn, tmp_path / "backups", f"b{i}", keep=2) @@ -218,7 +219,7 @@ def test_make_backup_rotates(tmp_path, open_db): def test_rotation_keeps_schema_migration_copy(tmp_path, open_db): """Копия базы перед миграцией схемы не участвует в ротации копий пакетов (M-1 финального ревью).""" - from jira_hub import backup + from jira_hub.core import backup conn = open_db(tmp_path / "hub.sqlite") backup.make_backup(conn, tmp_path / "backups", "schema-v1", keep=0) for i in range(4): diff --git a/tests/test_relay.py b/tests/test_relay.py index 4469904..9f38241 100644 --- a/tests/test_relay.py +++ b/tests/test_relay.py @@ -8,8 +8,8 @@ from datetime import datetime, timedelta, timezone import pytest -from jira_hub import api, relay, settings -from jira_hub.client import HubClient +from jira_hub.core import api, relay, settings +from jira_hub.core.client import HubClient @pytest.fixture diff --git a/tests/test_release.py b/tests/test_release.py index 5b5345d..ab46731 100644 --- a/tests/test_release.py +++ b/tests/test_release.py @@ -6,7 +6,7 @@ from pathlib import Path import pytest -from jira_hub import release +from jira_hub.core import release def agent_file(tmp_path, version, body="print('x')\n", name="agent.py"): diff --git a/tests/test_settings.py b/tests/test_settings.py index eb6d757..0444ca1 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -3,8 +3,8 @@ from pathlib import Path import pytest -from jira_hub import queue, settings -from jira_hub.commands import CommandError +from jira_hub.core import queue, settings +from jira_hub.modules.jira.commands import CommandError FIXTURE = Path(__file__).parent / "fixtures" / "config.example.json" diff --git a/tests/test_web_app.py b/tests/test_web_app.py index bfa7007..c4db10b 100644 --- a/tests/test_web_app.py +++ b/tests/test_web_app.py @@ -5,7 +5,7 @@ from urllib.parse import urlparse import pytest -from jira_hub import api +from jira_hub.core import api def _get(base, path): diff --git a/web/scripts/seed-hub.py b/web/scripts/seed-hub.py index 0d66620..2be91f5 100644 --- a/web/scripts/seed-hub.py +++ b/web/scripts/seed-hub.py @@ -5,7 +5,8 @@ import json import sys from pathlib import Path -from jira_hub import db, mirror, paths, settings +from jira_hub.core import db, paths, settings +from jira_hub.modules.jira import mirror snap = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) extras_path = Path(__file__).with_name("seed-extras.json")