Pull to refresh

Comments 11

По неизвестной до сих пор причине этот код медленнее любой из предшествующих реализаций, причем в несколько раз.

У вас там 100 раз идет sh "echo" и 100 раз идет def stdout = sh ()

Контролируйте количество внешних программ, которые нужно запустить и выполнить. Это же каждый раз нужно запустить интерпретатор (новый процесс в системе, память под него выделить, внутри еще и cat, который тоже должен выполниться), итого 300 процессов запустить/остановить

Вопрос не совсем архитектурный в данном случае. Архитектурные проблемы в интерфейсе библиотеки мы устранили, как и было рассказано. И проявление проблемы как бы ушло, но не сама проблема.

Десятки секунд на банальное порождение ничего, по сути, не делающих процессов - это перебор. В том же Shell такой код будет работать существенно быстрее. Плагин sh, который используется в Jenkins откровенно долго работает сам по себе - вот этот момент мы не курили, обойдя его рефакторингом своего кода.

Помню еще момент по поводу логов и артефактов - если они большие, то после того как на нодах все сбилдится, копирует все на дженкинс мастер, и это может занимаьт и время и траффик. Если артефакты и логи большие, возможно можно этот момент переделать прямо в пайплайне.

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

Когда-то, давным давно я работал в Нокии в проекте Maemo. Тогда ещё не было Jenkins, и система сборки была своя. Количество репозиториев было значительно больше 500 и дело не ограничивалось Git'ом. Например, репозиторий браузера был на экзотическом Mercurial, и разработчики наотрез отказывались от переезда на Git, потому что их workflow был завязан на апстримовый репозиторий, который у Mozilla был тоже на Mercurial. Ну, и других VCS было в товарных количествах - Subversion, CVS, Darcs и даже deb-src.

В общем, чтобы всё было максимально гибко нельзя было полагаться на триггер билда по PR. Некоторые пакеты релизились вместе, и вместе должны были билдиться. Такое было редко, но случалось. Если их билдить по отдельности, то ни тот, ни другой билд не проходил. Ну, например, либы меняла API, и приложение в новой версии использовало новый API. С версионированием API в embedded никто заморачиваться не хотел, особенно на этапе разработки ещё незарелизинной платформы.

Отсюда вытекала необходимость сканирования всех репозиториев по крону на предмет не PR, а tag'ов с новой версией. Workflow в командах был очень разнообразный, но чаще было так: разработчик делал PR, его ревьювили, особо продвинутые команды запускали свой локальный CI для него. Если всё было хорошо, то его мержили и ставили tag новой версии. Вот эти tag'и уже триггерили общесистемный CI.

Для построения дерева билда, а точнее топологически отсортированного списка, тоже использовалась NetworkX. Но перестраивались не все новые пакеты со всеми зависимостями, а только новые пакеты с неизменившимися зависимостями первого уровня, чтобы убедиться, что API не сломался. Перестравивать все уровни зависимостей смысла нет, только если для обогрева атмосферы (это основная претензия к системам типа OBS или Yocto). Причём новые бинарники неизменившихся зависимостей в итоговый образ не включались. Это необходимо было для того, чтобы Software Updates были поменьше, и чтобы на этапе тестирования этих updates выявить забытые version bumps из-за сломанного ABI.

Полные ребилды делались только при апдейте тулчейнов.

Хорошее было время. Система получилась ну очень гибкая. Но сейчас, если бы начал всё делать заново, то скорее всего написал бы, что-нибудь на основе ServerlessWorkflow.

Полные ребилды делались только при апдейте тулчейнов.

Не наш вариант, поскольку и libc тоже обновляется. А от этого компонента зависят все остальные => полный ребилд.

Ну, если libc, то нужно почти всë ребилдить, конечно. Но она ведь далеко не на каждый PR обновляется. Это спасает.

Вопрос определения иерархий изменённых явно или косвенно компонентов мы решаем иначе. Поскольку вся иерархия обходится всегда в одном порядке (от пред-зависимостей к пост-зависимостям), обнаружение в таргет-ветке коммита, отличного от предыдущего билда, есть основание для отметки всего нижележащего поддерева как требующего пересборки.

Важно помечать именно все поддерево пост-зависимостей, т.к. произвольный компонент без коммитов не обязательно является неизменным. Он мог ранее компилироваться с другими сторонними хэдерами или внешними дефайнами. Т.к. фиксы вносим по мере появления необходимости во все компоненты, то и полагаться на гипотетическое отсутствие сайд эффектов крайне вредно для качества продукта.

Про апгрейд тулчейна Вы уже упоминали, но конкретно этот процесс у нас полностью детерминирован и случается планово.

Видимо, проблему апдейта устройства вы решаете перефлэшиванием образа целиком. В Маемо делали инкрементальные апдейты и проводили дополнительное тестирование на сломанную бинарную совместимость как раз из-за разных дефайнов, например.

Позже один из инженеров написал скрипт, который скрейпил билдсреду на предмет опций компилятора, линкера, еще чего-то, что сейчас входит в стандарт buildinfo с reproducible-builds.org и сохранял вместе с бинарниками. Это упростило обнаружение коллизий и сократило количество билдов.

Видимо, проблему апдейта устройства вы решаете перефлэшиванием образа целиком

Почти. Учитывая, что на борту у команды тестирования в единицу времени сразу несколько стабильных релизов разных годов выпуска и пачка актуальных nightly билдов, для контроля регрессий приходится начисто накатывать конкретный дистрибутив на весь тестовый парк. Основное отличие в том, что за счет микроядерности у нас загрузочный образ очень маленький (сетевой стек, sshd, файловая система и пара файловых утилит [fdisk, mkdir, cp, etc]) и грузится на всём парке по сети. Всё остальное просто заливается в переразмеченную с нуля корневую FS в виде тарболов. В соседней статье ребята небольшую вводную на этот счет делали.

Кроме пары почтенных девайсов, вроде старенького Ateml, апгрейд проходит не более 20 мин. в худшем случае. В среднем около 7 мин.

Позже один из инженеров ...

С трудом могу представить безболезненную реализацию кейса, когда, например, какой-то проект, вроде zlib, через свой генерируемый config.h внезапно декларирует/отключает какие-то фичи, а зависимый переконфигурируется динамически на основе детектирования именно этой фичи. И далее по цепочке.

Учитывая, что многие проекты живут на разных рельсах -- нативная система сборки, ванильный autotools, cmake, meson, qmake, pkg-config ... -- простым такой анализ не будет. И априорно без пересборки словить такие build-time отличия, имхо, сочинить интерпретатор для всех этих тулов.

А если и так и так пересобирать, то профит не совсем ясен. Коллизия всплывёт либо на этапе компиляции/линковки, либо уже на этапе стат. анализа/тестирования/фаззирования и прочего динамического анализа.

Необязательно анализировать систему сборки. Компайл-опции можно, например, из debuginfo взять, который в dwarf-формате. Как решался вопрос с динамическим хедером не знаю. Не исключено, что и не решался.

Но сейчас понимаю, что, может, я и наврал про зависимости только первого уровня. Возможно второй уровень тоже ребилдился. Давно дело было. Но точно помню, что экономия на времени билдов была существенная)

Sign up to leave a comment.