Как вносить изменения

Основной репозиторий — на GitLab. Merge-request’ы отправляются туда, не в апстрим SatNOGS и не на GitHub.

Полный текст руководства по вкладу, унаследованный от Libre Space Foundation (включая Developer’s Certificate of Origin и требование подписывать коммиты), — в CONTRIBUTING.md в корне репозитория.

Рабочий цикл

  1. Ветка от актуальной основной ветки.

  2. Изменения атомарными коммитами: одно логическое изменение — один коммит.

  3. Локально прогнать проверки:

    tox -e ruff
    tox -e deps,pytest
    tox -e docs
    npm run lint
    npm test
    
  4. Merge request с описанием мотивации, а не только содержания диффа.

  5. Отработать замечания ревью.

Хуки до коммита

Необязательно, но экономит круг по CI: те же проверки, но только по файлам в индексе.

pip install pre-commit
pre-commit install

Набор описан в .pre-commit-config.yaml: ruff check --fix, ruff format, eslint и stylelint. Версия ruff в хуке совпадает с версией в tox.ini — разъехавшиеся версии форматтера и дают «у меня проходило»: хук переформатирует файл по-своему, CI просит обратно. CI от хуков не зависит и гоняет тот же набор целиком.

Стиль кода

Правила фиксируются ruff.toml и проверяются tox -e ruff:

Правило

Значение

Длина строки

88

Отступ

4 пробела

Кавычки

двойные

Импорты

сортировка I: стандартная библиотека → сторонние → локальные network.*

Docstring’и

стиль Google

Форматирование применяется командой tox -e ruff-format.

Обязательные соглашения

Миграции. Существующие файлы в network/*/migrations/ не редактируются. Изменения схемы — только новыми миграциями через makemigrations.

Бэкфиллы. RunPython, который проходит по Observation, пишется батчами: таблица на проде — миллионы строк, а web не отвечает, пока идут миграции.

def fill(apps, schema_editor):
    Observation = apps.get_model("base", "Observation")
    batch = []
    for observation in Observation.objects.filter(field__isnull=True).iterator(
        chunk_size=5000
    ):
        observation.field = compute(observation)
        batch.append(observation)
        if len(batch) >= 5000:
            Observation.objects.bulk_update(batch, ["field"])
            batch.clear()
    if batch:
        Observation.objects.bulk_update(batch, ["field"])

Образец того, как не надо, — 0048_observation_snapshot: два безусловных UPDATE по всей таблице. Она уже выпущена, редактировать её нельзя (см. выше), и в CHANGELOG к 1.10.0 из-за неё стоит отдельное предупреждение «заложить время на миграции».

Squash миграций base не делаем: стенды и станции обновляются вразнобой, а squash ломает любую базу, которая остановилась на миграции внутри схлопнутого отрезка.

Секреты. Ничего не хардкодится. Только config("VAR_NAME", default="") через python-decouple, значение попадает в .env, а новая переменная — в env-dist и в Конфигурация.

Решённые вопросы. Прежде чем предлагать очевидное улучшение, загляните в Принятые решения: там записано, что уже пробовали и почему отказались.

Размещение кода. HTML-представления — в network/base/views/<домен>.py, эндпоинты API — в network/api/views.py классами DRF. См. Архитектура.

Локализация. Весь текст, видимый пользователю, — через gettext_lazy. Комментарии и docstring’и — на английском.

Сгенерированные файлы. Руками не правятся:

  • requirements.txt, requirements-dev.txt, constraints.txt — генерируются ./contrib/refresh-requirements.sh из setup.cfg;

  • soniks-network-api-client/ — генерируется openapi-generator в CI;

  • network/_version.py — versioneer.

Декодеры телеметрии

Декодеры (.ksy Kaitai Struct) живут в отдельном репозитории soniks-decoders, подключённом как submodule soniks-decoders/; пакет satnogs-decoders с PyPI больше не используется. Сгенерированный Python в репозитории не хранится: в образе .ksy компилируются стадией decoders Dockerfile, для разработки без Docker — cd soniks-decoders && ./contrib/docker-ksc.sh.

Новый декодер: MR в soniks-decoders (ветка soniks), затем в этом репозитории передвинуть пин и закоммитить:

git submodule update --remote soniks-decoders
git add soniks-decoders

Документация

Документация живёт в docs/, пишется на русском в MyST-Markdown, собирается с -W (любое предупреждение Sphinx — ошибка сборки).

tox -e docs
# или
pip install -r docs/requirements.txt && make -C docs html

Новая страница обязана попасть в какой-нибудь toctree в docs/index.md — иначе сборка упадёт. Ссылки между страницами — относительные, синтаксисом MyST: [](../tle.md) подставит заголовок целевой страницы.

Зависимости сборки — только в docs/requirements.txt, в setup.cfg они не входят.

Изменение поведения без изменения документации — незаконченное изменение. Особенно это касается новых настроек, задач Celery, management-команд и эндпоинтов API: для них есть готовые места в Конфигурация, Задачи Celery, Management-команды и Обзор эндпоинтов.