Налаштування Git Submodules для спільних бібліотек

Коли спільний код живе в кількох репозиторіях, команди непомітно розходяться версіями, і збірка починає ламатися на рівному місці. Ми налаштовуємо підмодулі під ключ: фіксуємо версії спільних бібліотек, описуємо порядок оновлення і передаємо команді зрозумілий регламент. Розсинхрон версій зникає, і збірка стає передбачуваною.
Часті запитання

Налаштування Git Submodules для спільних бібліотек

Та сама бібліотека — парсер конфігів, клієнт внутрішнього API, набір компонентів інтерфейсу — живе копіями в кількох репозиторіях. Поки копії розходяться повільно, цього ніхто не помічає.

Варто випустити виправлення в одній із них, і версії перестають збігатися: у сервісі авторизації вже нова сигнатура виклику, у білінгу — стара.

Далі спрацьовує ефект «у кого свіжіше». Інженер, який першим підтягнув зміни, отримує падіння збірки на чужому коді, а решта продовжують збиратися на застарілій копії й проблеми не бачать.

Розсинхрон версій спливає на стенді або, що гірше, на проді — і шукати його починають не там, де він виник: спочатку у своєму коді, потім у залежностях, потім у середовищі збірки.

Налаштування Git Submodules прибирає саму причину. Спільна бібліотека залишається окремим репозиторієм, а проєкт зберігає не копію її коду, а посилання на конкретний коміт.

У батьківському репозиторії видно, яка версія бібліотеки зафіксована, оновлення стає усвідомленою дією з окремим записом в історії, а не побічним наслідком клонування. Одна бібліотека в репозиторіях перестає бути кількома різними бібліотеками.

Нижче розбираємо, як це виглядає на практиці: що потрапляє в .gitmodules, як вибудувати порядок оновлення, щоб підняття версії в одному проєкті не ламало збірку в сусідньому, і які перевірки поставити в автоматичну збірку, щоб розходження ловилося до злиття гілки.

Що змінюється у збірці, коли версії спільного коду під контролем

Підмодуль зберігає в батьківському репозиторії не «останню версію» бібліотеки, а вказівник на конкретну ревізію. Клонування з ініціалізацією підмодулів дає рівно той код, який проходив приймальні перевірки, — і на машині розробника, і на складальному агенті.

Тому відтворювана збірка перестає залежати від того, коли і в якому порядку хтось синхронізував залежності вручну.

Раніше спільний код жив копіями: бібліотеку переносили в кожен проєкт, правили прямо на місці й забували повернути виправлення назад. Дрейф накопичувався, і однакові на вигляд збірки відрізнялися поведінкою.

Git з підмодулями прибирає саму можливість такої різниці: у батьківському репозиторії лежить посилання, а тіло бібліотеки — лише в її власному репозиторії.

Фіксація версії бібліотеки перетворює оновлення на явну дію: змінюється вказівник, зміна потрапляє в історію й проходить рев'ю як звичайна правка. Відкат — повернення вказівника на попередню ревізію, без перезбирання інших частин проєкту.

Єдине джерело коду прибирає ручні правки чужого коду: виправлення вноситься в репозиторій бібліотеки й приходить у всі проєкти під час оновлення підмодуля.

Час збірки проєкту скорочується завдяки тому, що зникає ручна синхронізація версій і повторні розбори розходжень. Крок ініціалізації підмодулів на агенті займає секунди, а результат детермінований: однаковий вхід — однаковий артефакт.

Заодно зникає цілий клас відмов на кшталт «на моїй машині працює» — розходженню просто нізвідки взятися.

Три способи повторного використання спільного коду: порівняння підходів

Спосіб повторного використання спільного коду — це передусім питання про те, хто і як часто змінює бібліотеку.

Якщо правки йдуть з одного проєкту й релізяться разом із ним, розносити код по репозиторіях сенсу немає; якщо бібліотеку споживають п'ять команд із різним темпом релізів — потрібен контракт версії, а не спільна папка.

Варіант Коли підходить Обмеження
Підмодулі або єдиний репозиторій Кілька репозиторіїв, бібліотека живе окремо, потрібна жорстка прив'язка проєкту до конкретної ревізії Посилання на коміт замість гілки: оновлення лише явною командою, застаріле посилання не ламає збірку, а тихо тягне старий код
Реєстр пакетів Бібліотеку споживають різні проєкти, у них свій цикл релізів і потрібні версії з діапазонами сумісності Потребує реєстру, правил публікації та семантичного версіонування; правка стає доступною лише після випуску нової версії
Копія залежності в дереві Рідкісні оновлення й вимога збиратися без звернення до зовнішніх джерел Та сама бібліотека розходиться за версіями між проєктами, локальні патчі губляться під час наступного оновлення

Окремо про обмеження підмодулів, які найчастіше спливають уже в процесі. Робоча копія після клонування порожня, доки не виконано рекурсивний обхід — це регулярно ламає збірку в системах безперервної інтеграції, якщо крок не прописаний явно.

Усередині підмодуля репозиторій перебуває в стані відокремленої гілки, і правка, зроблена «на місці», виїжджає комітом у гілку, про яку інженер не думав. Тому вибір варіанта варто закріпити в правилах: де припустимо змінювати версію посилання і хто робить рев'ю цієї зміни.

Порядок налаштування й перевірки підмодулів у проєкті

Підмодуль фіксує не гілку, а конкретний коміт зовнішнього репозиторію, тому «просто додати» його недостатньо. Без аудиту залежностей і єдиної схеми гілок збірка відтворюється лише в автора правок: решта отримують іншу ревізію спільної бібліотеки.

Шість кроків: від інвентаризації до передачі

  1. Аудит залежностей. Інвентаризуємо спільні бібліотеки: що підключено копіюванням, що вкладеним репозиторієм, що вручну. Для кожної фіксуємо власника, частоту правок і кількість проєктів-споживачів.

  2. Схема гілок. Домовляємося, звідки береться ревізія: релізні споживачі дивляться на гілку супроводу, активна розробка — на основну. У підмодулі закріплено тег або коміт, а не кінець гілки.

  3. Підключення. Прописуємо шлях і спосіб отримання кожного підмодуля: чиста копія репозиторію має розгортатися однією командою, без ручних доустановлень.

  4. Регламент оновлення підмодулів. Оновлення йде окремою зміною із зазначенням причини, проходить рев'ю й потрапляє в основну гілку разом із правками коду. Автоматичне «підтягнути останнє» виключено.

  5. Перевірка відтворюваності. На чистому середовищі збірка йде лише із зафіксованих ревізій, нічого не підтягується повз оголошені джерела; розходження видно до випуску, а не після.

  6. Перевірка перед релізом і передача. Перед випуском звіряємо ревізії з реєстром залежностей і проганяємо збірку з чистої копії — розходження блокує випуск. Віддаємо карту залежностей, правила оновлення й порядок відкатів.

Що передаємо команді після впровадження

Після впровадження в команди залишається не «налаштований репозиторій», а комплект, за яким новий інженер піднімає середовище за один прохід, а оновлення спільної бібліотеки розходиться по проєктах без ручних правок у кожному.

Нижче — склад цього комплекту й те, що в ньому перевірено на живих збірках, а не описано «з пам'яті».

  • Конфігурації підмодулів — файл зі шляхами, адресами й гілкою для кожного підключення: зафіксовано тег або гілку, увімкнено часткове клонування, задано правила ігнорування локальних змін. Команда бачить, яке підключення публічне, а яке закрите.
  • Карта залежностей — таблиця «проєкт → бібліотека → зафіксована версія → власник». За нею за хвилину знаходиться, які проєкти зачепить правка в спільній бібліотеці й кого потрібно попередити.
  • Політика оновлення — хто рухає вказівник версії, за якою подією (випуск бібліотеки, закриття уразливості) і що має позеленіти в перевірках до злиття.
  • Права доступу — матриця «роль → читання/запис» для кожного сховища: підряднику видається читання бібліотеки без права змінювати версії в основному проєкті.
  • Інструкція для розробників — короткі сценарії: первинне розгортання, додавання підключення, переведення проєкту на нову версію, типові помилки та їхні ознаки.
  • Порядок відкату — як повернути попередню версію бібліотеки в одному проєкті й як відкотити масове оновлення, якщо перевірки виявили регрес.
  • Регламент перевірок у збірці — правила, які не дають злити зміну з «плаваючою» версією або незакритими конфліктами в підключеннях.

Комплект лежить поруч із кодом і оновлюється разом із ним: конфігурації — у репозиторії, карта й політика — у спільній документації команди. Опишіть вашу схему репозиторіїв — повернемося з оцінкою та планом впровадження.

Кейс: прибрали розсинхрон версій у трьох командах

Як було: спільна бібліотека в трьох копіях

Три продуктові команди тримали спільний модуль — типи, клієнт зовнішнього сервісу, шар логування. Кожна копіювала його у свій репозиторій і правила під себе, тому до релізу версії розходилися, і падало вже в конвеєрі.

Кожна четверта збірка зупинялася через незбіг версії бібліотеки, а на ручне зведення правок ішло до шести годин на тиждень на команду: порівняти три копії, перенести чужу правку, не зламати локальні доопрацювання.

Що змінили

Бібліотеку винесли в окремий репозиторій, підключили як Git Submodules і зафіксували в кожному продукті не гілку, а конкретний коміт — збірка стала відтворюваною.

Оновлення йде окремою задачею: інженер піднімає вказівник на нову ревізію, проганяє перевірки, і лише потім це йде в основну гілку. Ламкі зміни публікуються в мажорній гілці, продукти переїжджають на неї по черзі, а не всі одразу.

Метрики після

Частка збірок, що падали через версію бібліотеки, знизилася з чверті до поодиноких випадків — зазвичай це явний конфлікт, видимий на кроці перевірки. Ручне зведення правок зникло: правка живе в одному місці й приїжджає в продукти через оновлення вказівника.

Час збірки продукту скоротився, бо спільна частина більше не перезбирається всередині кожного репозиторію.

Чим підмодуль відрізняється від пакета і що робити при розростанні?

Підмодуль — це жорстке посилання на конкретний коміт чужого репозиторію, записане в дереві проєкту. Git не вирішує за вас, чи сумісні версії: вказівник оновлюють вручну й фіксують окремим комітом, а транзитивні залежності бібліотеки в дерево не приїжджають.

Пакет влаштований інакше — це опублікований артефакт із версією, і менеджер залежностей сам збирає узгоджений набір.

Межа проходить за ритмом випуску й кількістю споживачів. Поки бібліотека змінюється разом із застосунком, а споживач один, підмодуль дешевший: вихідні видно в дереві, правки йдуть в одному середовищі, проміжні стани не потрібно версіонувати.

Як тільки з'являється другий-третій споживач із незалежним графіком, зворотна сумісність і нумерація версій стають обов'язковими — це і є сигнал, що потрібен пакет.

Репозиторій, що розрісся, видає себе часом збірки й чергами на злиття. Повний перенос за один крок майже завжди ламає збірку: спочатку бібліотеку виносять в окремий репозиторій і публікують як пакет, а підмодуль залишають перехідним мостом для тих, хто ще не перейшов.

Споживачів переводять по одному, звіряючи контракт, і лише після цього видаляють посилання й файл .gitmodules.

Окремо про межі відповідальності: підмодуль дає всім однакові права на один репозиторій, тому команди блокують одна одну. Пакет розділяє ролі — одна команда випускає версії, решта лише підключає їх.

Перед міграцією фіксують політику версіонування й стабільний контракт, інакше часткова міграція розтягнеться на невизначений строк.

Технічні сумніви: гілки, права, відкати й оновлення

Підмодуль фіксується в батьківському репозиторії посиланням на конкретний коміт спільної бібліотеки, а не на гілку.

Саме ця властивість визначає, як поводиться проєкт під час перемикання гілок, відкатів і розмежування доступу, — і саме навколо неї виникає більшість сумнівів перед тим, як погодитися на таку схему.

Загальний принцип: ініціалізація підмодулів — явна операція (submodule update --init --recursive), а не побічний ефект клонування. Поки її не виконали, каталоги бібліотек залишаються порожніми, і збірка падає одразу й зрозуміло, а не «тихо» виїжджає на застарілому коді.

Що станеться під час перемикання гілки?

Робоче дерево підмодуля саме не переміщується — після перемикання гілки батьківського проєкту потрібно синхронізувати посилання, інакше зберете стару бібліотеку з новим кодом.

Ми закриваємо це хук-скриптом: синхронізація виконується автоматично, а розходження фіксованого посилання й фактичного стану ловиться перевіркою в безперервній інтеграції до збірки.

Як обмежити права підрядників на спільний код?

Доступ до репозиторію бібліотеки видається окремо від доступу до проєкту й лише на читання — через окремий ключ або токен обмеженої дії.

Підрядник збирає й налагоджує зі спільною бібліотекою, але не може переписати її для себе; зміни проходять через власника репозиторію й обов'язкову перевірку.

Як відкотити версію бібліотеки без перезбирання всього?

Посилання на підмодуль — це один рядок в історії батьківського проєкту. Повернення до попереднього коміту бібліотеки робиться правкою цього посилання й фіксується окремою зміною: решта коду не чіпається.

Ми тримаємо політику так, щоб відкат займав хвилини, а не перезбирання всіх сервісів, і перевіряємо його тестами перед викаткою.

Обговоримо задачу: розберемо схему й повернемося з планом

Щоб повернутися з оцінкою та планом впровадження, нам потрібна не абстрактна постановка, а фактична схема репозиторіїв: де лежать спільні бібліотеки, хто і як підключає їх зараз, які гілки вважаються стабільними.

За цими вхідними даними ми рахуємо обсяг робіт і пропонуємо порядок переведення на Git Submodules — із точками відкату й без зупинки поточних випусків.

Що корисно описати на дзвінку або надіслати заздалегідь:

  • список репозиторіїв та їхніх власників: застосунки, сервіси, спільні бібліотеки, допоміжні утиліти;
  • поточний спосіб підключення спільних модулів: копіювання коду, окремий пакет, вкладені репозиторії, скрипти синхронізації;
  • схему розгалуження й правила випуску: які гілки довгоживучі, як проходять правки й як фіксуються версії;
  • вимоги до доступів: яким командам потрібне читання бібліотек, кому дозволено змінювати вказівник на конкретний коміт;
  • правила оновлення: як часто підтягувати зміни спільної бібліотеки в сервіси й хто ухвалює рішення про відкат;
  • набір перевірок на збірці: що запускається під час зміни вкладеного модуля і що блокує влиття;
  • обмеження середовищ: закриті мережі, дзеркала репозиторіїв, особливості збірки на боці замовника.

З цим набором ми фіксуємо схему репозиторіїв, розбиваємо роботи за етапами й надсилаємо план впровадження з критеріями готовності на кожному кроці. Опишіть задачу — повернемося з оцінкою та пропозицією щодо порядку дій.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1393
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1368
  • Розробка логотипу компанії B2B Advance
    Розробка логотипу компанії B2B Advance
    815
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1139