-
Notifications
You must be signed in to change notification settings - Fork 100
Development‐General
- Мы форкаем, а не работаем напрямую с оригиналом, чтобы не было страшно что-то сломать (GitHub не даёт возможности за бесплатно защитить main).
- Мы стараемся поддерживать линейную историю -- для этого нужно использовать rebase при разрешении конфликтов.
- Мы придерживаемся trunk-based development.
-
Инициализация
- Заходим в https://github.com/Desbordante/desbordante-core → нажимаем Fork
- Клонируем к себе на ПК, создаём новую ветку
(Дальше с кодом можно делать что угодно, оригинальный репозиторий не будет затронут. В любом случае, при попытке push в Desbordante/desbordante-core будет permission denied)
git clone https://github.com/<Ваш ник>/desbordante-core git switch -c <new_feature>
-
Настроить fetch. Git на ПК не знает, что работает с форком — надо ему дать ссылку на оригинал.
git remote add upstream https://github.com/Desbordante/desbordante-core git remote -vПоследней строчкой глазами проверили, что есть два внешних репозитория: оригинал (upstream) и форк (origin)
-
Подтянуть изменения, если некий Программист успел своевременно поменять код в Desbordante/desbordante-core/main
git pull --rebase upstream mainПод капотом происходит:
git fetch upstream — узнать состояние upstream репы git checkout main — перейти в свой main git rebase upstream/main — ребазировать свой main на upstream/mainЭто нужно делать, чтобы поддерживать свой форк в синхронизации с оригиналом — иначе будут большие сложности с финальным добавлением алгоритма.
Можно настроить rebase по умолчанию, тогда можно будет писать просто
git pull:git config --global pull.rebase preserve -
Рабочий цикл (коммит-пуш)
... do stuff ... git add <done stuff> git commit git push origin <new_feature> -
Когда код собирается, работает и протестирован:
- Ещё раз подтянуть изменения, чтобы не было проблем при создании Pull Request (PR).
- Идти контрибьютить в оригинальный репо:
Desbordante/desbordante-core → Pull requests → Create pull request → compare across forks → будем добавлять <ваш форк> - new_feature в Desbordante/desbordante-core - main. - Прохождение CI
- Код-ревью
- Rebase and merge
Можно почитать здесь о том, как и зачем оформлять. Самое важное:
- Название коммита от его тела отделяется пустой строкой;
- В названии коммита должно быть около 50 символов или меньше;
- Название коммита пишется с большой буквы;
- Название коммита не должно заканчиваться точкой;
- Название коммита пишется в повелительном наклонении (imperative mood);
- В теле коммита строчки должны быть длиной не более 72 символов;
- В теле коммита необходимо описать какие и зачем были сделаны изменения, а не как.
То есть, текстовое сообщение к коммиту должно выглядеть примерно так:
Summarize changes in around 50 characters or less
Explain the problem that this commit is solving. Focus on why you
are making this change as opposed to how (the code explains that).
Are there side effects or other unintuitive consequences of this
change? Here's the place to explain them.
Особое внимание стоит обратить на то, что в теле коммита нужно делать упор на то какие и зачем были сделаны изменения, а не как (пункт 7). При этом тело коммита само по себе не обязательно, его можно опустить, если изменения и их цель очевидна из названия.
Пример оформления коммита:
Add lock to FDAlgorithm::registerFD
Each algorithm that supports parallelism (for now it is basically all
algorithms) has its own overrided thread-safe registerFD. To avoid such
code duplication add mutex to FDAlgorithm and lock it in registerFD.
This approach may slow down single-threaded algorithms a bit, but I
believe fd registration is not a bottleneck, so it won't be noticeable.
Оформление пул-реквестов похоже на оформление коммитов. В названии пул-реквеста необходимо кратко описать выжимку из того, что делают коммиты в нем. В описании нужно написать более подробно какие изменения принятый пул-реквест привнесет. Также следует использовать issues linking для issues, которые данный пул-реквест исправляет. Либо, если таких нет, то стоит описать, какую проблему пул-реквест решает (то есть зачем он был сделан).
Если во время ревью в написанном коде были замечены недочёты, они должны быть исправлены в уже существующих коммитах (если только по смыслу не нужно добавить новый), чтобы в истории было записано так, как будто с самого начала было сделано правильно. Для этого можно использовать git rebase -i.
Перед принятием PR необходимо посквошить коммиты, так чтобы их не было несколько десятков. Эти десятки обычно набираются при написании кода или же после нескольких раундов код-ревью и проверки через CI. Насколько сквошить? Это вопрос к вашему ощущению правильности - можно побить на несколько частей, например код, байндинги, примеры работы. Если делается валидатор и майнер в одном PR, то возможно имеет смысл разделить код на еще два коммита. В общем, думайте, спрашивайте. Но в любом случае должен получиться осмысленный набор коммитов, а не куча. Да, коммиты можно пересортировать перед squash.
Требования к оформлению бумаги (курсовой)
По умолчанию нужно следовать Google C++ style guide, за некоторыми исключениями.
Исключения из Google C++ style guide:
- Длина строки 100 символов;
- Разрешено использовать исключения (exceptions);
- Разрешены числовые встроенные типы (не только int, но и все остальные);
- Табы по 4 (влияет на форматирование
switch-caseи отступы для модификаторов доступа, смотри.clang-format); - Расширение у С++ файлов cpp (вместо cc);
- Разрешено
#pragma onceвместо стандартных include guards; - Файлы и директории именуются используя snake_case;
- Порядок секций модификаторов доступа в определении класса:
private,protected,public; - Наверняка много чего еще... (WIP)
Кроме того, запрещено объявлять и крайне нежелательно использовать любые переменные со статическим или потоковым временем хранения, которые могут быть изменены после инициализации — их сложно правильно использовать с subinterpreter'ами Python.
Что может понадобиться:
- model -- структуры данных для базового представления таблицы
- util -- более узкие структуры данных, на которые опираются алгоритмы
Что понадобится с меньшей вероятностью:
- algorithms -- основная логика алгоритмов
- core -- тут лежит более низкоуровневая логика Pyro
- caching -- относится к Pyro, нигде не используется
- custom -- тут ре-реализуются штуки из стандартной библиотеки для корректного сравнения с джавой
- parser -- парсер .csv
- logging -- тут лежит взятый снаружи логгер, его не надо трогать
Проект использует библиотеку spdlog. Все C++ логи перенаправляются в стандартный модуль logging Python.
- Логи — для отладки. Они не должны содержать информацию для конечного пользователя.
-
Производительность.
DEBUGиTRACEлоги полностью вырезаются из release-сборок на этапе компиляции. -
Форматирование. Используйте
{fmt}синтаксис вместо конкатенации строк.
Неправильно:
// Вызов std::to_string() и конкатенация выполняются всегда.
// Возможно неопределённое поведение, если id содержит {}
LOG_DEBUG("Error processing item " + std::to_string(id));Правильно:
// Форматирование происходит внутри spdlog только если уровень активен.
LOG_DEBUG("Error processing item {}", id);Используйте макросы LOG_<LEVEL>(...) в соответствии с назначением сообщения.
-
LOG_TRACE(...)- Назначение: Максимально детальная информация для отладки низкоуровневой логики (например, состояние на каждой итерации цикла).
-
LOG_DEBUG(...)- Назначение: Отслеживание шагов выполнения алгоритма (вход/выход из функций, значения ключевых переменных).
-
LOG_INFO(...)- Назначение: Высокоуровневые события, обозначающие прогресс (например, "Algorithm X started").
-
LOG_WARN(...)- Назначение: Некритичные ошибки, не прерывающие выполнение (например, пропущена некорректная строка в файле).
-
LOG_ERROR(...)-
Назначение: Критические ошибки, после которых корректная работа невозможна. Часто используется перед
throw.
-
Назначение: Критические ошибки, после которых корректная работа невозможна. Часто используется перед
-
LOG_CRITICAL(...)- Назначение: Критические ошибки, которые могут привести к аварийному завершению программы или повреждению данных (например, невозможность записать результат в файл).
Чтобы выводить пользовательские типы, нужно научить spdlog их форматировать.
Это наиболее простой и универсальный способ. spdlog автоматически подхватит этот оператор.
struct Point {
double x, y;
};
template <typename OStream>
OStream& operator<<(OStream& os, const Point& p) {
os << "(" << p.x << ", " << p.y << ")";
return os;
}
Point p = {1.0, 2.5};
LOG_INFO("Found optimal point: {}", p); // Выведет: Found optimal point: (1.0, 2.5)Этот способ более гибкий, но требует больше кода. Он может быть полезен для типов, для которых нельзя или нежелательно определять operator<<. Подробности можно найти в документации {fmt}.
В большинстве случаев стандартных макросов LOG_* достаточно. Если профилирование показывает, что подготовка данных для лога в горячем цикле является узким местом, можно добавить явную проверку compile-time уровня. И такие логи лучше сразу выносить в DEBUG и выше.
Пример:
// Этот блок кода будет полностью вырезан препроцессором в Release-сборках
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
std::string complex_debug_string;
for (const auto& item : huge_collection) {
complex_debug_string += item.serialize();
}
LOG_DEBUG("Complex state: {}", complex_debug_string);
#endifЭтот воркфлоу отвечает за автоматическую сборку бинарных .whl (wheels) пакетов для Linux и macOS с помощью cibuildwheel. Он гарантирует, что пользователи смогут устанавливать наш проект через pip без необходимости компиляции из исходников.
Полная матрица сборки колес всегда запускается в следующих случаях:
- При пуше в ветку
main. - Еженедельно по расписанию (Понедельник, 3:00 UTC).
- При публикации нового релиза.
Сборка не запускается. При создании PR или пуше в его ветку, статус проверки Wheel будет "Skipped".
Чтобы запустить полную матрицу сборки (все поддерживаемые версии Python для Linux и macOS) на вашем PR, выполните следующие действия:
- Откройте ваш Pull Request в интерфейсе GitHub.
- В разделе "Labels" справа найдите и добавьте метку
python-packaging-risk. Для этого нужно иметь как минимумtriageразрешение в репозитории. Если у вас его нет -- попросите кого-нибудь в чате.
После добавления метки воркфлоу автоматически запустится.
Если сборка уже запущена, но больше не нужна (например, вы заметили ошибку и хотите ее исправить), просто удалите метку python-packaging-risk. Тогда текущий запущенный процесс будет автоматически отменен.
Если вы добавляете опцию не стандартного типа, пометьте этот тип с помощью DESBORDANTE_EXPORT. Проект использует visibility=hidden, из-за чего некоторые типы нужно специально помечать.
Также это относится к типам исключений. За подробностями читайте https://gcc.gnu.org/wiki/Visibility#Problems_with_C.2B-.2B-_exceptions_.28please_read.21.29
Добавьте этот тип опций в py_to_any.cpp и opt_to_py.cpp. Последнее при возможности, если не получается --- спрашивайте. Первое обязательно для того, чтобы работали опции из Python.
Для истории, столкнулись и начали чинить тут: https://github.com/Desbordante/desbordante-core/pull/693