Загрузка данных


Работаем в project-manager-tools.

Контекст:
Финальная текущая версия уже проверена на рабочей сети.

Сейчас работает:
- создание Release через POST /rest/api/unit/v2/release/create
- создание Epic через POST /rest/api/unit/v2/epic/create
- создание Story через POST /rest/api/unit/v2/story/create
- создание Task через POST /rest/api/unit/v2/task/create
- создание Bug через POST /rest/api/unit/v2/bug/create
- создание связей через POST /rest/api/unit/v1/link
- links работают с HTTP 201
- dry-run режим работает
- sbertrack_write_enabled возвращается в false
- UnitCache уже создан и хранит created IDs локально
- action sbertrack_cache_status работает
- action sbertrack_plan_full_release_safe работает
- action sbertrack_create_full_release_from_yaml_ensure уже добавлен
- blocker НЕ трогать на этом этапе

Текущая цель:
Доделать Jira-like поведение по порядку:

ЭТАП 1 — PATCH/update по code.
ЭТАП 2 — remote search по summary, если cache пустой.
ЭТАП 3 — ensure_unit_by_summary должен работать по схеме:
cache -> remote search -> update/create.

Важно:
Сейчас blocker не реализуем и не тестируем.
Не делать реальный blocker POST.
Не делать git push.
Не выводить секреты.
Не выводить полный .env.
Не ломать уже работающее создание release/epic/story/task/bug/link.
Все реальные write-запросы только после отдельного подтверждения.
Сначала dry-run и диагностика.

Перед изменениями:
1. Создай backup файлов:
   - src/sbertrack_api.py
   - src/sbertrack_planner.py
   - src/unit_cache.py
   - src/main.py

2. Проверь, что:
   - sbertrack_write_enabled=false
   - секреты не выводятся
   - git status не обязателен, потому что папка может быть не git repo

Что нужно сделать:

==================================================
1. Найти подтверждённый endpoint для update
==================================================

Проверь в текущем проекте и доступных локальных источниках, есть ли уже упоминания:

- update_unit_by_code
- unit.update
- issue.update
- client.patch
- client.put
- PATCH /rest/api/unit/v2
- PATCH /rest/api/unit/v2/{code}
- PATCH /rest/api/unit/v2/update
- PATCH /rest/api/unit/v2/update/{code}
- PUT /rest/api/unit/v2/{code}
- /rest/api/unit/v2/update
- /rest/api/unit/v2/{unit_code}

Также проверь, установлен ли пакет sbertrack_python_api:

python -c "import sbertrack_python_api; print(sbertrack_python_api.__file__)"

Если установлен, найди исходники:

python -c "import sbertrack_python_api, os; print(os.path.dirname(sbertrack_python_api.__file__))"

По исходникам пакета безопасно поискать:

- class SbertrackClient
- def patch
- def put
- unit.update
- issue.update
- update(
- /rest/api/unit/v2/update
- /rest/api/unit/v2/{code}
- /rest/api/unit/v2

Ничего не выполнять через POST/PATCH/PUT/DELETE на этапе поиска.

Нужно вывести:
- найден ли update endpoint;
- найден ли готовый метод в sbertrack_python_api;
- какой payload ожидается;
- можно ли безопасно подключить его в наш проект;
- если endpoint не подтверждён, update оставить только dry-run stub.

==================================================
2. Реализовать update_unit_by_code
==================================================

В src/sbertrack_api.py добавить или доработать метод:

update_unit_by_code(code, payload)

Требования:
- Если sbertrack_write_enabled=false:
  - НЕ выполнять PATCH/PUT.
  - Вывести dry-run информацию:
    - code
    - endpoint, который был бы использован
    - sanitized payload
  - Вернуть структуру результата вида:
    {
      "dry_run": True,
      "code": code,
      "updated": False,
      "reason": "sbertrack_write_enabled=false"
    }

- Если sbertrack_write_enabled=true:
  - выполнять только подтверждённый endpoint.
  - Если endpoint не подтверждён — не выполнять реальный PATCH/PUT, а вернуть RuntimeError или понятное сообщение:
    "update endpoint is not confirmed"
  - Не выводить секреты.
  - Не выводить полный .env.
  - Обработать HTTP status и response body.
  - После ошибки не оставлять write_enabled=true.

Важно:
Если endpoint update не подтверждён документацией или кодом sbertrack_python_api, НЕ придумывать endpoint.
Лучше оставить update в dry-run режиме и явно написать, что нужен endpoint от наставника.

==================================================
3. Найти remote search по summary
==================================================

Нужно выяснить, можно ли искать unit в SberTrack по summary/name/title/query.

Проверить в текущем проекте, документации и sbertrack_python_api:

- unit.search
- unit.find
- unit.find_9
- search_units
- find_units
- issue.getByKey
- issue.find
- query
- title
- name
- summary
- TQL
- filter
- /rest/api/unit/v2/search
- /rest/api/unit/v2/find
- /rest/api/unit/v2/all
- /rest/api/unit/v2
- /rest/api/user/v1/search
- POST search
- client.post(...search...)
- client.get(...query...)

Нужно найти:
- есть ли endpoint/метод поиска unit по пространству и summary;
- можно ли фильтровать по space/project;
- можно ли фильтровать по unit type;
- можно ли искать через TQL;
- какой payload нужен;
- какой response возвращается.

Реальные POST/GET поиска можно делать только если это read-only операция.
Если GET/POST search безопасный и не создаёт сущности — можно выполнить тестовый read-only запрос.
Но:
- не выводить токены;
- не выводить полный .env;
- не делать create/update/delete.

==================================================
4. Реализовать find_unit_by_summary
==================================================

В src/sbertrack_api.py добавить или доработать метод:

find_unit_by_summary(space, unit_type, summary)

Логика:
1. Сначала нормализовать summary:
   - trim
   - lower
   - схлопнуть повторные пробелы

2. Сначала проверить UnitCache:
   - если найден code, дополнительно попробовать get_unit_by_code(code)
   - если get_unit_by_code успешен, вернуть найденный unit/code
   - если get_unit_by_code недоступен или ошибка, не падать, а перейти к remote search

3. Если cache miss:
   - выполнить remote search, если endpoint подтверждён
   - искать по space + unit_type + summary
   - если найдено ровно одно совпадение — вернуть code
   - если найдено несколько совпадений — вывести предупреждение и выбрать самое точное совпадение по нормализованному summary, если оно одно
   - если точного совпадения нет — вернуть None
   - если endpoint не подтверждён — вернуть None и явно написать, что remote search не подтверждён

4. Никакого auto-create внутри find_unit_by_summary делать нельзя.

==================================================
5. Реализовать ensure_unit_by_summary
==================================================

В src/sbertrack_api.py добавить или доработать метод:

ensure_unit_by_summary(unit_type, summary, payload, space=None)

Логика как в Jira:
1. Проверить cache.
2. Если cache hit:
   - проверить get_unit_by_code(code), если метод доступен.
   - если unit существует:
     - вызвать update_unit_by_code(code, payload)
     - вернуть code.
3. Если cache miss:
   - выполнить remote search по summary.
   - если найден существующий unit:
     - сохранить code в cache.
     - вызвать update_unit_by_code(code, payload), если update подтверждён.
     - вернуть code.
4. Если ничего не найдено:
   - создать новый unit через существующие рабочие create_release/create_epic/create_story/create_task/create_bug.
   - сохранить created code в cache.
   - вернуть code.

Важное ограничение:
- Если sbertrack_write_enabled=false:
  - create не выполняется.
  - update не выполняется.
  - показывается план действий.
- Если sbertrack_write_enabled=true:
  - create выполняется только для реально отсутствующих сущностей.
  - update выполняется только если endpoint update подтверждён.
  - если update endpoint не подтверждён, существующую сущность не обновлять, только вернуть warning.

==================================================
6. Обновить planner
==================================================

В src/sbertrack_planner.py:

Доработать create_full_release_from_yaml_ensure так, чтобы он не создавал дубли:

- Release:
  ensure_unit_by_summary("release", summary, release_payload, space)

- Epic:
  ensure_unit_by_summary("epic", epic_summary, epic_payload, space)

- Story:
  ensure_unit_by_summary("story", story_summary, story_payload, space)

- Task:
  ensure_unit_by_summary("task", task_summary, task_payload, space)

- Bug:
  ensure_unit_by_summary("bug", bug_summary, bug_payload, space)

Связи создавать после получения актуальных code.

Перед созданием связи:
- не создавать связь, если source или destination отсутствует;
- желательно использовать существующую безопасную проверку, чтобы не падать на дублях;
- если SberTrack возвращает "already exists" / HTTP 570, считать это ожидаемым предупреждением, а не критической ошибкой.

==================================================
7. Обновить main.py
==================================================

Добавить/проверить actions:

1. sbertrack_cache_status
   - read-only
   - показывает cache

2. sbertrack_plan_full_release_safe
   - всегда dry-run
   - ничего не создаёт
   - показывает план

3. sbertrack_create_full_release_from_yaml_ensure
   - использует ensure pattern
   - при sbertrack_write_enabled=false только dry-run
   - при true создаёт только отсутствующее
   - update выполняет только если endpoint подтверждён

4. Добавить отдельный безопасный action для проверки поиска:
   sbertrack_find_unit_by_summary

Пример параметров:
   --unit_type release
   --summary "testic"
   --space TST123456

Он должен:
- сначала смотреть cache;
- потом remote search, если подтверждён;
- ничего не создавать;
- ничего не обновлять;
- вывести найденный code или "not found".

==================================================
8. Тесты
==================================================

Выполнить только безопасные команды сначала:

1. Проверка синтаксиса:

python -m py_compile src/sbertrack_api.py src/sbertrack_planner.py src/unit_cache.py src/main.py src/yaml_reader.py

2. Проверка .env без секретов:

grep -E 'sbertrack_write_enabled|sbertrack_project_key|sbertrack_base_url' .env

3. Cache status:

python src/main.py --tracker sbertrack --action sbertrack_cache_status --project luch --service luch-pprb

4. Safe plan:

python src/main.py --tracker sbertrack --action sbertrack_plan_full_release_safe --project luch --service luch-pprb

5. Find by summary dry/read-only:

python src/main.py --tracker sbertrack --action sbertrack_find_unit_by_summary --project luch --service luch-pprb --unit_type release --summary "testic" --space TST123456

6. Ensure dry-run:

python src/main.py --tracker sbertrack --action sbertrack_create_full_release_from_yaml_ensure --project luch --service luch-pprb

Если всё успешно и sbertrack_write_enabled=false, реальных POST/PATCH/PUT/DELETE быть не должно.

Реальный запуск делать только после отдельного подтверждения.

==================================================
9. Финальный отчёт
==================================================

В конце выведи финальный отчёт:

## 1. Какие файлы изменены
Таблица:
Файл | Что изменено | Backup создан

## 2. Update/PATCH
- endpoint найден: да/нет
- метод библиотеки найден: да/нет
- реальный PATCH/PUT выполнялся: да/нет
- если нет — почему

## 3. Remote search по summary
- endpoint найден: да/нет
- метод библиотеки найден: да/нет
- read-only поиск выполнялся: да/нет
- найден ли testic по summary: да/нет
- какой code найден

## 4. Ensure pattern
Покажи итоговую логику:
cache -> remote search -> update/create

## 5. Проверка безопасности
Подтверди:
- sbertrack_write_enabled=false в конце
- секреты не выводились
- полный .env не выводился
- POST без подтверждения не выполнялся
- PATCH/PUT/DELETE без подтверждения не выполнялись
- git push не выполнялся
- blocker не трогался

## 6. Что осталось
Отдельно перечисли:
- что осталось уточнить у наставника по PATCH/update
- что осталось уточнить по remote search/TQL
- что осталось уточнить по blocker