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
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>
128 lines
4.5 KiB
Bash
Executable file
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
|