docs: добавить руководство для контрибьюторов (CONTRIBUTING.md)

This commit is contained in:
qwen.ai[bot] 2026-08-12 13:19:34 +00:00
parent 2c547bd763
commit ad19f76249

View file

@ -1,20 +1,20 @@
# Руководство для контрибьюторов netsleuth # Руководство для контрибьюторов Netsleuth
Спасибо за интерес к проекту netsleuth! Это руководство поможет вам начать вносить свой вклад. Спасибо за интерес к проекту! Это руководство поможет вам начать вносить свой вклад.
## 🚀 Быстрый старт ## 🚀 Быстрый старт
### 1. Установка зависимостей ### 1. Настройка окружения
```bash ```bash
# Клонируйте репозиторий # Клонируйте репозиторий
git clone https://github.com/netsleuth/netsleuth.git git clone https://github.com/egorovandrey/netsleuth.git
cd netsleuth cd netsleuth
# Установите uv (менеджер пакетов) # Установите uv (если ещё не установлен)
curl -LsSf https://astral.sh/uv/install.sh | sh curl -LsSf https://astral.sh/uv/install.sh | sh
# Синхронизируйте зависимости # Создайте виртуальное окружение и установите зависимости
uv sync --extra tcptrace --extra ndt7 uv sync --extra tcptrace --extra ndt7
# Установите pre-commit хуки # Установите pre-commit хуки
@ -28,10 +28,10 @@ uv run pre-commit install
uv run pytest uv run pytest
# Тесты с покрытием # Тесты с покрытием
uv run pytest --cov=src/netsleuth --cov-report=term-missing uv run pytest --cov=src/netsleuth --cov-report=html
# Конкретный тест # Конкретный тест
uv run pytest tests/test_cli.py -v uv run pytest tests/test_exporter.py -v
``` ```
### 3. Линтинг и форматирование ### 3. Линтинг и форматирование
@ -40,290 +40,114 @@ uv run pytest tests/test_cli.py -v
# Проверка стиля # Проверка стиля
uv run flake8 src/netsleuth uv run flake8 src/netsleuth
# Авто-форматирование (ruff) # Форматирование кода
uv run ruff format src/netsleuth uv run black src/netsleuth tests
# Проверка типов # Проверка типов
uv run mypy src/netsleuth uv run mypy src/netsleuth --ignore-missing-imports
``` ```
--- ## 📝 Как добавить новый источник геоданных
## 📁 Структура проекта 1. Создайте новый файл в `src/netsleuth/geo/` (например, `new_provider.py`)
2. Реализуйте интерфейс:
```
netsleuth/
├── src/netsleuth/ # Исходный код
│ ├── cli.py # CLI интерфейс (typer)
│ ├── config.py # Конфигурация и настройки
│ ├── models.py # Датаклассы и модели данных
│ ├── bgp.py # BGP и ASN информация
│ ├── geoip.py # Геолокация IP
│ ├── latency.py # Измерение задержек
│ ├── path.py # Traceroute и анализ пути
│ ├── speed.py # Speedtest и bufferbloat
│ ├── reputation.py # Проверка IP репутации
│ ├── exporter.py # Генерация отчётов
│ └── interpret.py # Интерпретация результатов
├── tests/ # Тесты
├── .github/workflows/ # CI/CD пайплайны
└── docs/ # Документация
```
---
## 🔧 Как добавить новый источник геоданных
### Пример: Добавление GeoIP провайдера
1. **Создайте функцию в `geoip.py`:**
```python ```python
async def fetch_geolocation_new_provider(ip: str) -> dict[str, Any]: from typing import Optional
"""Получает геоданные от нового провайдера. from dataclasses import dataclass
Args: @dataclass
ip: IP адрес для поиска class GeoResult:
country: Optional[str] = None
city: Optional[str] = None
asn: Optional[int] = None
org: Optional[str] = None
Returns: async def fetch_geo(ip: str) -> Optional[GeoResult]:
Dict с ключами: country, city, org, asn """Получить геоданные для IP."""
# Ваша реализация
Raises: pass
httpx.HTTPError: Если API недоступен
"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.newprovider.com/geo/{ip}",
timeout=5.0
)
response.raise_for_status()
return parse_geolocation_response(response.json())
``` ```
2. **Добавьте парсер ответа:** 3. Добавьте импорт в `src/netsleuth/geo/__init__.py`
4. Напишите тесты в `tests/test_new_provider.py`
5. Обновите документацию
```python ## 🧪 Как написать тест для новой пробы
def parse_geolocation_response(payload: dict) -> dict[str, Any]:
"""Извлекает поля из ответа API."""
return {
"country": payload.get("country_name"),
"city": payload.get("city"),
"org": payload.get("org"),
"asn": payload.get("asn"),
}
```
3. **Интегрируйте в основной пайплайн:**
В `cli.py` найдите `gather_identity()` и добавьте вызов:
```python
geo_new = await fetch_geolocation_new_provider(ip)
```
4. **Напишите тест:**
Создайте `tests/test_geoip_new_provider.py`:
```python ```python
import pytest import pytest
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_fetch_geolocation_new_provider(httpx_mock): async def test_my_probe():
httpx_mock.add_response(
url="https://api.newprovider.com/geo/8.8.8.8",
json={"country_name": "US", "city": "Mountain View"}
)
from netsleuth.geoip import fetch_geolocation_new_provider
result = await fetch_geolocation_new_provider("8.8.8.8")
assert result["country"] == "US"
assert result["city"] == "Mountain View"
```
---
## 🧪 Как написать тест для новой пробы
### Шаблон теста
```python
"""Тесты для модуля <module_name>."""
import pytest
from unittest.mock import patch, MagicMock
from netsleuth.<module> import <function_name>
class TestFunctionName:
"""Тесты для <function_name>."""
@pytest.mark.asyncio
async def test_success_case(self, httpx_mock):
"""Тестирует успешный сценарий."""
# Arrange # Arrange
httpx_mock.add_response(...) with patch('netsleuth.my_probe.httpx.get') as mock_get:
mock_get.return_value = {"status": "ok"}
# Act # Act
result = await <function_name>(...) result = await run_probe("8.8.8.8")
# Assert # Assert
assert result.expected_field == "expected_value" assert result.status == "ok"
mock_get.assert_called_once()
@pytest.mark.asyncio
async def test_error_handling(self):
"""Тестирует обработку ошибок."""
with pytest.raises(ExpectedError):
await <function_name>(invalid_input)
@pytest.mark.asyncio
async def test_edge_case_empty_input(self):
"""Тестирует граничный случай."""
result = await <function_name>("")
assert result is None
``` ```
### Мокирование HTTP запросов **Важно:**
- Все HTTP-запросы должны быть замокированы
```python - Используйте фикстуры из `tests/conftest.py`
@pytest.mark.asyncio - Добавляйте тесты на граничные случаи
async def test_with_httpx_mock(httpx_mock):
"""httpx_mock автоматически мокирует httpx запросы."""
# Добавляем ожидаемый запрос
httpx_mock.add_response(
url="https://api.example.com/data",
method="GET",
status_code=200,
json={"key": "value"}
)
# Вызываем функцию
result = await fetch_data()
# Проверяем что запрос был сделан
request = httpx_mock.get_request()
assert request.url == "https://api.example.com/data"
```
---
## ✅ Чеклист перед Pull Request ## ✅ Чеклист перед Pull Request
Перед отправкой PR убедитесь: - [ ] Код отформатирован (`black src/netsleuth tests`)
- [ ] Нет нарушений flake8 (`flake8 src/netsleuth`)
- [ ] Все тесты проходят: `uv run pytest` - [ ] Все тесты проходят (`pytest`)
- [ ] Покрытие не уменьшилось (минимум 60%) - [ ] Покрытие не уменьшилось (минимум 60%)
- [ ] Линтер не находит ошибок: `uv run flake8 src/netsleuth`
- [ ] Типизация корректна: `uv run mypy src/netsleuth`
- [ ] Код отформатирован: `uv run ruff format src/netsleuth`
- [ ] Добавлены docstrings для публичных функций - [ ] Добавлены docstrings для публичных функций
- [ ] Добавлены тесты для нового функционала
- [ ] Обновлена документация (если нужно) - [ ] Обновлена документация (если нужно)
- [ ] Pre-commit хуки проходят: `uv run pre-commit run --all-files` - [ ] Pre-commit хуки пройдены
--- ## 🏗️ Архитектура проекта
## 📝 Стиль кода ```
src/netsleuth/
### Именование ├── cli.py # Точка входа, CLI аргументы
├── config.py # Конфигурация и настройки
- **Функции**: `snake_case` (`fetch_geolocation`) ├── models.py # Датаклассы для данных
- **Классы**: `PascalCase` (`SpeedResult`) ├── probes/ # Модули проб (speed, latency, etc.)
- **Константы**: `UPPER_CASE` (`MAX_RETRIES`) ├── geo/ # Геоданные и ASN lookup
- **Приватные функции**: `_prefix` (`_parse_response`) ├── reputation.py # Проверка по чёрным спискам
├── interpret.py # Интерпретация результатов
### Docstrings └── exporter.py # Экспорт в Markdown/JSON
Используйте Google style:
```python
def calculate_latency(samples: list[float]) -> float:
"""Вычисляет среднюю задержку из списка семплов.
Args:
samples: Список задержек в миллисекундах
Returns:
Средняя задержка, или 0.0 если список пуст
Raises:
ValueError: Если есть отрицательные значения
"""
if not samples:
return 0.0
if any(s < 0 for s in samples):
raise ValueError("Latency cannot be negative")
return sum(samples) / len(samples)
``` ```
### Type hints Основная статья: [`ARCHITECTURE.ru.md`](ARCHITECTURE.ru.md)
Все публичные функции должны иметь аннотации типов: ## 🐛 Сообщение об ошибках
```python При создании issue укажите:
from typing import Any, Optional 1. Версию Python (`python --version`)
2. Версию netsleuth (`netsleuth --version`)
3. ОС и версию
4. Команду запуска
5. Полный вывод ошибки
6. Скриншот (если применимо)
def process_ip(ip: str, timeout: float = 5.0) -> Optional[dict[str, Any]]: ## 💡 Идеи для улучшений
...
``` Ищем помощь в следующих областях:
- [ ] Поддержка новых протоколов (QUIC, HTTP/3)
- [ ] Графический интерфейс (TUI/GUI)
- [ ] Интеграция с Prometheus/Grafana
- [ ] Поддержка IPv6-only хостов
- [ ] Локализация на другие языки
## 📞 Контакты
- GitHub Issues: [egorovandrey/netsleuth/issues](https://github.com/egorovandrey/netsleuth/issues)
- Telegram: @your_channel (если есть)
--- ---
## 🐛 Отладка **Спасибо за ваш вклад!** 🎉
### Логирование
Включите дебаг логирование:
```bash
netsleuth <target> --watch --verbose
```
### Отладка тестов
```bash
# Запустить с выводом print()
uv run pytest -s tests/test_file.py::test_name
# Остановиться на первой ошибке
uv run pytest -x tests/
# Запустить последний упавший тест
uv run pytest --lf
```
---
## 📞 Вопросы и поддержка
- **GitHub Issues**: Для багов и фич https://github.com/netsleuth/netsleuth/issues
- **Discussions**: Для вопросов https://github.com/netsleuth/netsleuth/discussions
---
## 🎯 Идеи для контрибьюшена
### Начинающим
- [ ] Добавить docstrings к функциям без документации
- [ ] Исправить опечатки в README
- [ ] Добавить тесты для существующих функций
- [ ] Улучшить сообщения об ошибках
### Продвинутым
- [ ] Добавить новый источник геоданных
- [ ] Реализовать кэширование API запросов
- [ ] Оптимизировать производительность NetsetIndex
- [ ] Добавить поддержку IPv6 в traceroute
- [ ] Создать GUI для результатов
---
Спасибо за ваш вклад! 🎉