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


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

Цель:
Доработать SberTrack-реализацию так, чтобы она стала ближе к уже существующей Jira-реализации:

1. Не создавать дубли сущностей.
2. Уметь находить существующие сущности.
3. Уметь обновлять существующие сущности, если это поддержано API.
4. Использовать cache как дополнительную защиту.
5. Сохранять безопасный режим по умолчанию.
6. Разобраться и аккуратно реализовать/отложить blocker на основании найденных endpoint-ов и документации.
7. Не ломать уже рабочее создание release/epic/story/task/bug/links.

ВАЖНО:
Перед изменениями обязательно сделай backup файлов:
- src/sbertrack_api.py
- src/sbertrack_planner.py
- src/unit_cache.py
- src/main.py
- src/yaml_reader.py

Backup сделать с суффиксом:

.ensure_update_blocker.backup

Например:
src/sbertrack_api.py.ensure_update_blocker.backup

Жёсткие ограничения:
- Не делай git push.
- Не выводи секреты.
- Не выводи полный .env.
- Не делай реальные POST/PATCH/PUT/DELETE без явного включения sbertrack_write_enabled=true.
- По умолчанию sbertrack_write_enabled должен оставаться false.
- После любых реальных действий sbertrack_write_enabled должен быть возвращён в false.
- PATCH/PUT/DELETE пока не выполнять реально, даже если endpoint найден. Для них только dry-run/план.
- Не трогай blocker реальным POST, пока не будет точно понятно, что это за сущность.
- Не переписывай проект с нуля.
- Работай точечно в существующей архитектуре.

---

# Контекст текущей реализации

В проекте уже работает создание сущностей SberTrack:

- Release
- Epic
- Story
- Task
- Bug
- Links

Подтверждённые рабочие endpoint-ы:

- POST /rest/api/unit/v2/release/create
- POST /rest/api/unit/v2/epic/create
- POST /rest/api/unit/v2/story/create
- POST /rest/api/unit/v2/task/create
- POST /rest/api/unit/v2/bug/create
- POST /rest/api/unit/v1/link

Подтверждённая логика связей:

- Story -> Release: type=release
- Task -> Release: type=release
- Epic -> Release: type=release
- Story -> Epic: type=decomposition
- Task -> Story: type=decomposition
- Release -> Bug: type=containsbug

Текущая проблема:
Сейчас создание работает, но нет полноценного Jira-like ensure/update:

- нет поиска существующей сущности в SberTrack по summary;
- нет update существующей сущности;
- cache работает только локально;
- если cache пустой, есть риск дублей;
- blocker отключён/postponed, потому что endpoint POST /rest/api/unit/v2/blocker/create возвращал HTTP 500.

---

# Внутренняя документация, которую мы нашли

Есть Python-библиотека:

sber-devops-zis-sbertrack-python-api

Основной клиент:

from sbertrack_python_api import SbertrackClient

Есть прямые HTTP-методы:

client.get(path, **kwargs)
client.post(path, json_data=data)
client.put(path, json_data=data)
client.patch(path, json_data=data)
client.delete(path, **kwargs)

Есть пример получения unit:

sbertrack_client.unit.get(unit)

REST endpoint:

GET /rest/api/unit/v2/{unit_code}

Пример:

GET https://api.sbertrack.sberbank.ru/rest/api/unit/v2/PMKCORE-160

Также в документации “Создание задач в SberTrack. Генри v.2” есть MCP-инструменты:

issue.create — создать задачу
issue.getByKey — поиск задачи по номеру/ключу
issue.update — обновление задачи
issue.transition — перевод задачи в другой статус workflow
issue.findSuits — поиск типов задач в проекте
link.create — создать связь между задачами
link.types — типы доступных связей
history.list — история изменений задачи

---

# Часть 1. Сначала безопасно проверь 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__))"

Найди в исходниках пакета и в нашем проекте:

- unit.get
- unit.create
- unit.update
- getByKey
- issue.getByKey
- issue.update
- issue.create
- issue.findSuits
- issue.transition
- link.create
- link.types
- history.list
- unit.search
- unit.find
- search_units
- find_units
- get_unit
- update_unit
- find_by_summary
- summary
- title
- name
- query
- space
- suit
- taxonomy
- workflow
- swtr_task_tracker_plugin
- /rest/api/unit/v2
- /rest/api/unit/v2/{code}
- /rest/api/unit/v2/update
- /rest/api/unit/v2/search
- /rest/api/user/v1/search
- /rest/api/unit/v1/link
- blocker
- Blocker
- блокер
- Блокер
- blocks
- blocked
- blocking
- defect_type
- task_categories
- priority
- containsbug

После поиска выведи краткий отчёт:

1. Найден ли пакет sbertrack_python_api.
2. Где он лежит.
3. Какие методы точно можно использовать.
4. Какие endpoint-ы подтверждены.
5. Какие endpoint-ы не подтверждены.

---

# Часть 2. Реализовать безопасный UnitCache

Если файла src/unit_cache.py нет — создать.
Если есть — аккуратно доработать.

Нужен класс UnitCache.

Cache должен храниться локально в JSON-файлах:

.sbertrack_cache/units_{space}.json

Структура cache:

{
  "SPACE": {
    "unit_type": {
      "normalized_summary": "UNIT_CODE"
    }
  }
}

Нужно реализовать методы:

- __init__(cache_dir=".sbertrack_cache")
- normalize_summary(summary)
- load_cache(space)
- save_cache(space, data)
- get_unit_code(space, unit_type, summary)
- put_unit_code(space, unit_type, summary, code)
- clear_space(space)
- clear_all()
- show_cache_status(space=None)

Требования:
- normalize_summary должен быть case-insensitive.
- summary нужно trim-ить.
- cache не должен хранить payload-и с секретами.
- show_cache_status не должен выводить секреты.
- если cache-файл повреждён, не падать, а показать warning и начать с пустого cache.

---

# Часть 3. Доработать src/sbertrack_api.py

Добавить методы:

1. get_unit_by_code(code)

Логика:
- Выполняет GET /rest/api/unit/v2/{code}
- Это READ-операция, её можно выполнять даже при sbertrack_write_enabled=false.
- Возвращает response JSON.
- При 404 возвращает None.
- При ошибке пишет понятный warning.
- Секреты не выводит.

2. find_unit_by_summary(space, unit_type, summary)

Логика:
- Сначала проверить, есть ли подтверждённый endpoint/метод поиска из sbertrack_python_api.
- Если найден безопасный поиск по summary — использовать его.
- Если подтверждённого поиска нет — НЕ делать неподтверждённые POST/GET-запросы наугад.
- В таком случае метод должен:
  - проверить local cache;
  - если cache hit — проверить unit через get_unit_by_code;
  - если unit найден — вернуть его;
  - если cache miss — вернуть None и написать, что удалённый поиск по summary не подтверждён.

3. update_unit_by_code(code, payload)

Логика:
- Реально PATCH/PUT пока не выполнять.
- Реализовать dry-run поведение:
  - показать, какой code был бы обновлён;
  - показать sanitized payload;
  - показать предполагаемый endpoint, если он найден;
  - если endpoint не подтверждён — написать, что update требует уточнения.
- Реальный PATCH/PUT не отправлять.
- Даже если sbertrack_write_enabled=true, update пока не делать, пока endpoint официально не подтверждён.
- Вернуть структуру результата:
  {
    "updated": false,
    "dry_run": true,
    "reason": "...",
    "code": "...",
    "payload": sanitized_payload
  }

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

Логика:
- Определить space из аргумента или payload.
- Нормализовать summary.
- Сначала проверить local cache.
- Если cache hit:
  - выполнить get_unit_by_code(code);
  - если unit существует:
    - выполнить update_unit_by_code(code, payload) в dry-run;
    - вернуть существующий code;
  - если unit не найден:
    - удалить/игнорировать битый cache entry.
- Если cache miss:
  - попытаться find_unit_by_summary(space, unit_type, summary).
  - если найден unit:
    - сохранить code в cache;
    - выполнить update_unit_by_code(code, payload) в dry-run;
    - вернуть code.
  - если не найден:
    - если sbertrack_write_enabled=false:
      - не создавать;
      - вернуть dry-run план создания.
    - если sbertrack_write_enabled=true:
      - создать через существующий create_release/create_epic/create_story/create_task/create_bug;
      - получить code из response;
      - сохранить code в cache;
      - вернуть code.

Важно:
- ensure не должен создавать дубль, если есть cache hit и get_unit_by_code подтверждает существование.
- Если нет удалённого поиска по summary и cache пустой, честно написать, что дубль невозможно исключить на 100%.
- Все payload-и выводить только sanitized.
- Не выводить token/password/full .env.

5. create_link(source, destination, link_type)

Если уже есть — не ломать.
Доработать:
- endpoint POST /rest/api/unit/v1/link
- payload:
  {
    "source": source,
    "destination": destination,
    "type": link_type
  }
- При sbertrack_write_enabled=false — только dry-run.
- При true — реальный POST.
- Успешным считать HTTP 200/201 и response true.
- Ошибки не должны ломать весь процесс, если сущности уже созданы; нужно писать warning и финальный статус.

6. link_types()

Если в sbertrack_python_api найден метод link.types или endpoint для типов связей:
- добавить read-only метод.
Если не найден:
- вернуть известные подтверждённые типы:
  - release
  - decomposition
  - containsbug
- написать, что список частичный.

---

# Часть 4. Доработать src/sbertrack_planner.py

Нужно сделать режим, похожий на Jira:

1. Добавить метод:

create_full_release_from_yaml_ensure(...)

или доработать существующий create_full_release_from_yaml так, чтобы он мог работать через ensure.

Логика:
- release создавать/находить через ensure_unit_by_summary
- epic создавать/находить через ensure_unit_by_summary
- story создавать/находить через ensure_unit_by_summary
- task создавать/находить через ensure_unit_by_summary
- bug создавать/находить через ensure_unit_by_summary
- links создавать только после того, как известны актуальные code всех сущностей

2. Сохранить старое рабочее поведение, если оно нужно.
Не ломать текущие actions.

3. Добавить safe planning method:

plan_full_release_safe(project, service, release_file=None)

Логика:
- Всегда dry-run.
- Игнорирует sbertrack_write_enabled.
- Не создаёт unit.
- Не обновляет cache.
- Показывает:
  - какой YAML найден;
  - какие сущности будут созданы/найдены;
  - какие связи будут созданы;
  - какие cache hit/cache miss;
  - какие потенциальные дубли возможны;
  - какие поля отсутствуют.

4. Добавить show_cache_status(space=None)

Вызвать UnitCache.show_cache_status.

---

# Часть 5. Доработать src/main.py

Добавить actions:

1. sbertrack_cache_status

Пример запуска:

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

Назначение:
- показать cache status;
- не делать POST/PATCH/PUT/DELETE.

2. sbertrack_plan_full_release_safe

Пример:

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

Назначение:
- безопасный dry-run;
- не учитывать sbertrack_write_enabled;
- не создавать сущности;
- не обновлять cache.

3. sbertrack_create_full_release_from_yaml_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:
- только dry-run;
- никаких POST.

При sbertrack_write_enabled=true:
- create только для отсутствующих сущностей;
- cache update после create;
- links create после получения code.

4. sbertrack_create_links_only

Если уже есть — оставить.
Если нет — добавить.

Пример:

python src/main.py --tracker sbertrack --action sbertrack_create_links_only \
  --release_code TST123456-29 \
  --epic_code TST123456-30 \
  --story_codes TST123456-31,TST123456-32 \
  --task_codes TST123456-33,TST123456-34,TST123456-35 \
  --bug_codes TST123456-36

---

# Часть 6. Blocker

Нужно аккуратно разобраться и не ломать.

1. Найти все упоминания blocker в проекте.

Искать:
- blocker
- Blocker
- блокер
- Блокер
- blocking
- blocked
- priority=blocker
- blocker/create
- /rest/api/unit/v2/blocker/create
- defect_type
- task_categories
- category
- suit
- unit type

2. Определить, чем blocker является в SberTrack:

Проверить варианты:

A. Отдельный unit type:
POST /rest/api/unit/v2/blocker/create

B. Bug/defect с priority=blocker:
POST /rest/api/unit/v2/bug/create

C. Task с priority=blocker:
POST /rest/api/unit/v2/task/create

D. Link type:
POST /rest/api/unit/v1/link
type=blocks / blockedby / depends / depends_on / blocking

E. Workflow/status:
issue.transition

3. Не делать реальный POST blocker.

4. Если подтверждения отдельного endpoint-а нет:
- оставить blocker postponed;
- убрать попытки POST /rest/api/unit/v2/blocker/create;
- в отчёте честно написать, что blocker/create не подтверждён и раньше давал HTTP 500.

5. Если подтверждено, что blocker — это priority у task или bug:
- добавить только dry-run план;
- реальную реализацию пометить как TODO до подтверждения наставником.

6. Добавить в отчёт вопросы наставнику:

- В SberTrack blocker — это отдельный unit type или priority?
- Можно ли создавать blocker через API?
- Какой endpoint для blocker?
- Какой payload обязателен?
- Какой тип связи означает “блокирует/заблокировано”?
- Нужно ли blocker создавать как bug/defect?
- Нужно ли blocker связывать с release через containsbug или другим типом?
- Какие поля обязательны в space TST123456?
- Можно ли использовать sbertrack-python-api в project-manager-tools?
- Какой endpoint официально использовать для update unit?

---

# Часть 7. YAML

Проверь текущий YAML reader.

Нужно убедиться, что текущий YAML продолжает работать для:

projects/{project}/services/{service}/releases/release-1.0.0.yaml

Поддерживаемые поля:
- summary
- description
- space
- ci_code
- priority
- release_kind
- uat_start_date
- introduction_start_date
- epic
- stories[]
- tasks[]
- bugs[]

Не ломай текущий формат.

Если для blocker нужны новые поля, пока не внедряй их в основной YAML, а предложи отдельный пример в comments/TODO.

---

# Часть 8. Тесты и проверки

После реализации выполнить безопасные проверки.

1. Проверить syntax/import:

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

2. Проверить, что sbertrack_write_enabled=false:

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

Не выводить token/password/full .env.

3. Запустить cache status:

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

4. Запустить safe dry-run:

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

5. Запустить 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;
- выводится план;
- cache не ломается;
- secrets не выводятся.

6. Если есть уже созданные ID из прошлого теста:
- release: TST123456-29
- epic: TST123456-30
- stories: TST123456-31, TST123456-32
- tasks: TST123456-33, TST123456-34, TST123456-35
- bug: TST123456-36

Проверить создание связей dry-run:

python src/main.py --tracker sbertrack --action sbertrack_create_links_only \
  --release_code TST123456-29 \
  --epic_code TST123456-30 \
  --story_codes TST123456-31,TST123456-32 \
  --task_codes TST123456-33,TST123456-34,TST123456-35 \
  --bug_codes TST123456-36

При sbertrack_write_enabled=false POST не выполнять.

---

# Часть 9. Финальный отчёт

После выполнения выведи финальный отчёт строго по структуре:

# ФИНАЛЬНЫЙ ОТЧЁТ: SberTrack Jira-like ensure/update/cache + blocker

## 1. Какие файлы изменены

Таблица:

Файл | Что изменено | Backup создан

## 2. Что реализовано

Отдельно перечислить:

- UnitCache
- get_unit_by_code
- find_unit_by_summary
- update_unit_by_code dry-run
- ensure_unit_by_summary
- safe plan
- cache status
- create links only
- blocker research / postponed

## 3. Что теперь работает как в Jira

Таблица:

Механизм | Jira | SberTrack после доработки | Отличия

Обязательно сравнить:

- ensure by summary
- cache
- create
- update
- links
- dry-run
- duplicate protection

## 4. Что пока НЕ полностью как в Jira

Честно указать:

- если нет подтверждённого поиска по summary;
- если update пока только dry-run;
- если cache локальный;
- если blocker не подтверждён;
- если нельзя 100% исключить дубли при пустом cache.

## 5. Blocker

Ответить:

- где был blocker в коде;
- какой endpoint раньше использовался;
- почему HTTP 500;
- какая модель blocker наиболее вероятна;
- что нужно уточнить у наставника;
- что сейчас безопасно сделано.

## 6. Подтверждение безопасности

Подтвердить:

- sbertrack_write_enabled=false
- POST не выполнялись, если не было явного включения
- PATCH не выполнялись
- PUT не выполнялись
- DELETE не выполнялись
- git push не выполнялся
- секреты не выводились
- полный .env не выводился

## 7. Команды для проверки

Вывести готовые команды:

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

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

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

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

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

python src/main.py --tracker sbertrack --action sbertrack_create_links_only \
  --release_code TST123456-29 \
  --epic_code TST123456-30 \
  --story_codes TST123456-31,TST123456-32 \
  --task_codes TST123456-33,TST123456-34,TST123456-35 \
  --bug_codes TST123456-36