food-market/deploy/swagger-diff.sh
nns 72d0a71307
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
docs(s24): docs cross-check + auto-gen + onboarding + test gap-fill (8/8 ✓)
1. Docs cross-check — обновил performance-baseline.md (Sprint 18/20/23
   фиксы), secrets.md (16 новых env-vars из Sprint 20+ — Authentication
   Google/Microsoft, Monitoring, Cleanup, Hangfire:Cron, Telegram,
   Maintenance, App, Storage, PUBLIC_GA_ID/YM_ID).

2. Auto-gen api-reference — ApiReferenceDocsJob (Hangfire weekly вс
   05:30 UTC) + Python-эквивалент `/tmp/gen-api-ref.py` для commit
   actual snapshot. docs/api-reference.md = 195 endpoints, 57 controllers.

3. Coverage gap-fill — Sprint18To23FeaturesTests.cs (16 Facts):
   - bulk-update + cross-tenant isolation
   - UserPresets CRUD
   - inline-edit price PATCH
   - CSV import 2 строки транзакцией
   - OrgExport create + list isolation
   - 1C-CSV import с русскими заголовками
   - audit-log export CSV streaming + BOM check
   - MoySklad sync-status stub
   - SSO providers + 503 unconfigured + 400 unknown provider
   - bug-001 NUL byte → 400
   - bug-004 tiny price → 400
   - export CSV BOM
   Покрывает все новые контроллеры Sprint 18-23 + regression-protect
   для критичных багов.

4. Contract tests — deploy/swagger-diff.sh: pull /swagger/v1/swagger.json
   с двух URL, diff endpoints+schemas через python3. Exit 0/1/2 для
   blue-green safety gate. Multi-path auto-detect.

5. docs/error-codes.md — каталог HTTP-кодов API (200-503) + humanizeError
   pattern для фронта + retry-policy таблица.

6. docs/glossary.md — 50+ доменных терминов (Tenant/Organization/Stock/
   StockMovement/RetailSale/Counterparty/Owner/Employee/Role/Permission/
   advisory lock/Serializable/…) с ссылками на code-сущности.

7. docs/ONBOARDING.md — first 3 days для нового разработчика
   (install → запуск → структура → первый PR + FAQ).

8. README.md — обновил под текущее состояние: React 19, Sprint-history
   1-24, ссылки на ключевые docs, корректный 5-min quick start.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 02:15:56 +05:00

128 lines
4.5 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# Sprint 24: контракт-тест — diff /openapi.json между двумя
# окружениями. Используется ПЕРЕД blue-green деплоем чтобы понять что
# меняется в публичном API и не сломать клиентов (Web admin, POS WPF,
# партнёрские интеграции).
#
# Usage:
# deploy/swagger-diff.sh [--from URL] [--to URL]
#
# Default:
# from = https://admin.food-market.kz (prod)
# to = https://test.admin.food-market.kz (stage)
#
# Что показывает:
# - removed endpoints (path+method) — BREAKING ⚠️
# - added endpoints — NEW (нормально)
# - changed request/response schemas — нужен ручной обзор
#
# Без зависимости от swagger-diff CLI: парсим JSON через python3.
#
# Exit codes:
# 0 — изменений нет ИЛИ только additions
# 1 — есть removed (BREAKING) или changed schemas
# 2 — ошибка получения swagger.json
set -uo pipefail
FROM_URL="${FM_SWAGGER_FROM:-https://admin.food-market.kz}"
TO_URL="${FM_SWAGGER_TO:-https://test.admin.food-market.kz}"
while [[ $# -gt 0 ]]; do
case "$1" in
--from) FROM_URL="$2"; shift 2 ;;
--to) TO_URL="$2"; shift 2 ;;
--help|-h) grep -E '^#( |$)' "$0" | sed 's/^# \?//'; exit 0 ;;
*) echo "Unknown: $1" >&2; exit 2 ;;
esac
done
TMP=$(mktemp -d)
trap "rm -rf $TMP" EXIT
# Пытаемся несколько канонических путей: Swashbuckle default + alt-routes.
fetch_swagger() {
local base="$1" out="$2"
for path in /swagger/v1/swagger.json /v1/swagger.json /api/v1/swagger.json; do
if curl -fsS --max-time 30 "$base$path" -o "$out" 2>/dev/null; then
# Должен быть JSON, не HTML (фронт SPA отдаёт index.html на unknown path).
if python3 -c 'import json,sys; json.load(open(sys.argv[1]))' "$out" 2>/dev/null; then
echo " found at $path" >&2
return 0
fi
fi
done
return 1
}
echo "Fetching from $FROM_URL" >&2
fetch_swagger "$FROM_URL" "$TMP/from.json" \
|| { echo "FAIL: $FROM_URL не отдаёт swagger.json. Проверьте IncludeSwagger=true в appsettings или ASPNETCORE_ENVIRONMENT=Development." >&2; exit 2; }
echo "Fetching from $TO_URL" >&2
fetch_swagger "$TO_URL" "$TMP/to.json" \
|| { echo "FAIL: $TO_URL не отдаёт swagger.json." >&2; exit 2; }
python3 - <<PY
import json, sys
def endpoints(s):
out = set()
for path, methods in s.get('paths', {}).items():
for method, op in methods.items():
if method.lower() in {'get','post','put','patch','delete','head','options'}:
out.add(f"{method.upper()} {path}")
return out
def schemas(s):
return set(s.get('components', {}).get('schemas', {}).keys())
with open('$TMP/from.json') as f: src = json.load(f)
with open('$TMP/to.json') as f: dst = json.load(f)
e_src, e_dst = endpoints(src), endpoints(dst)
s_src, s_dst = schemas(src), schemas(dst)
added_ep = sorted(e_dst - e_src)
removed_ep = sorted(e_src - e_dst)
added_sc = sorted(s_dst - s_src)
removed_sc = sorted(s_src - s_dst)
print(f"=== Swagger diff: $FROM_URL → $TO_URL ===")
print(f"endpoints: from={len(e_src)} to={len(e_dst)} added={len(added_ep)} removed={len(removed_ep)}")
print(f"schemas: from={len(s_src)} to={len(s_dst)} added={len(added_sc)} removed={len(removed_sc)}")
print()
if added_ep:
print("### Added endpoints (новые, нормально):")
for e in added_ep: print(f" + {e}")
print()
if removed_ep:
print("### ⚠️ REMOVED endpoints (BREAKING для клиентов!):")
for e in removed_ep: print(f" - {e}")
print()
if added_sc:
print(f"### Added schemas: {len(added_sc)} (показано первые 20)")
for s in added_sc[:20]: print(f" + {s}")
print()
if removed_sc:
print(f"### ⚠️ REMOVED schemas: {len(removed_sc)}")
for s in removed_sc[:20]: print(f" - {s}")
print()
# Изменения операций (опц.): сравнить parameters/responses для shared endpoint'ов.
# Пока — высокоуровневое diff'a достаточно для blue-green safety check.
# Exit code
if removed_ep or removed_sc:
print("RESULT: BREAKING changes detected.")
sys.exit(1)
elif not added_ep and not added_sc:
print("RESULT: schemas identical.")
sys.exit(0)
else:
print("RESULT: только additions, безопасно деплоить.")
sys.exit(0)
PY