[devel] Hey, I've got a hasher in my pocket!

Paul Wolneykien manowar на altlinux.org
Пн Дек 8 00:41:37 MSK 2014


   Всем привет,

   Речь пойдёт не совсем о «карманах» [1], а скорее о «кармашках» -- на 
большее пока не замахиваюсь. Но тем не менее. Есть у меня 
производственная необходимость получить в руки примерно такой вот 
инструмент:

     $ # Открываем карман для сборки пакетов в ~/pocket:

     $ pocket init debian:stable pocketserver:a-product-repo

     $ # В качестве источника пакетов при этом подключаются два
     $ # репозитория: один -- дистрибутив "stable" из репозитория
     $ # "Debian", а второй -- опубликованный на узле "pocketserver"
     $ # репозиторий с микро-дистрибутивом "a-product-repo".
     $ # Предполагается, что на pocketserver публикуются стабильные
     $ # версии микро-дистрибутивов, т.е. таких наборов пакетов,
     $ # которые дополняют базовый дистрибутив ОС "продуктовыми"
     $ # функциями.

     $ # Система догадывается, что собирать мы будем *.deb-пакеты.
     $ # Либо потому, что мы работаем под Debian, либо потому, что
     $ # первым источником пакетов у нас идёт "debian". Для особо
     $ # тяжёлых случаев предусмотрена опция --flavour. Предполагается,
     $ # что можно будет сказать --flavour=altlinux и --flavour=debian.

     $ # Карман открыт, будем собирать туда пакеты. Примерно так:

     $ git clone srv:somelib.git
     $ cd somelib
     $ vim ...
     $ pocket build

     $ # Допустим, что оно не собралось или собралось как-то не так.
     $ # Чиним. Примерно вот так:

     $ pocket sync
     $ vim ...
     $ pocket sync

     $ # Предполагается, что по команде "sync" машина сама понимает
     $ # в каком направлении копировать файлы: сперва из чрута в
     $ # текущую, а затем наоборот (т.к. файлы в текущей теперь
     $ # немного новее). Ошибка поправлена -- снова собираем. Точнее
     $ # "дособираем" -- продолжаем процесс сборки с того места,
     $ # где он прервался:

     $ pocket build

     $ # Будем считать, что на этот раз всё прошло успешно -- пакет
     $ # собрался. Но мы хотим удостовериться и поэтому пересобираем
     $ # его с нуля:

     $ pocket rebuild

     $ # Ура! Пакет пересобрался вчистую, т.е. с разворотом чрута
     $ # с нуля. И лежит somelib_newver.deb в этом самом кармане
     $ # (~/pocket).

     $ # Повторяем процесс для someprog, которая основана на somelib:

     $ cd ..; git clone srv:someprog.git; cd someprog; pocket build

     $ # Естественно, что someprog у нас собирается с новой версией
     $ # somelib, т.е. карман является дополнительным и самым
     $ # приоритетным источником пакетов для самого себя. И теперь
     $ # у нас в кармане два свежесобранных пакета: somelib_newver.deb
     $ # и someprog_newver.deb. И теперь надо бы проверить, что мы
     $ # там такого насобирали, работает ли оно вообще? Для этого
     $ # публикуем карман под именем a-product-YYYYDDMM:

     $ pocket publish /pockets/a-product-YYYYDDMM --mirror
     Mirroring http://pocketserver/a-product-repo ...
     Merge-in local packages:
     Replacing somelib: oldver -> newver
     Replacing someprog: oldver -> newver
     Your pocket is available at http://myhost/pockets/a-product-YYYYDDMM

     $ # Может быть даже "pocket open", посмотрим. По этой команде
     $ # карман принимает вид полноценного репозитория, доступного
     $ # снаружи как http://myhost/pockets/a-product-YYYYDDMM и
     $ # пригодного для `apt-get update/install`. Здесь важно отметить,
     $ # что опция `--mirror` привела к тому, что по указанному адресу
     $ # опубликовано не только содержимое самого кармана, но и его
     $ # целевого репозитория, каковым является "a-product-repo" с узла
     $ # "pocketserver". Но зеркалится целевой репозиторий не как был,
     $ # а с изменениями: с новыми, свежесобранными версиями somelib и
     $ # someprog. (Кстати, а как можно было бы таким образом ещё и
     $ # удалять пакеты, т.е. делать так, чтобы в новой копии целевого
     $ # репозитория они отсутствовали? Можно через Conflicts/Obsoletes,
     $ # наверное. Но явный способ удаления тоже нужен. Ещё не придумал
     $ # как.)

     $ # Тестируем новую версию "a-product-repo" на узле "testhost":

     $ ssh root на testhost
     [root на testhost ~]# # Вписываем опубликованный a-product-YYYYDDMM
     [root на testhost ~]# # в sources.list:
     [root на testhost ~]# vim /etc/apt/sources.list
     [root на testhost ~]# # Обновляемся и проводим тестирование:
     [root на testhost ~]# apt-get update
     [root на testhost ~]# apt-get dist-upgrade
     [root на testhost ~]# someprog --test
     OK
     [root на testhost ~]# exit

     $ # Будем считать, что тестирование завершилось удачно. А это
     $ # значит, что можно включать новые сборки в стабильный
     $ # репозиторий на pocketserver:

     $ pocket commit -m "New versions of somelib and someprog"
     New build task #00101 at pocketserver

     $ # Предполагаем, что система опять догадалась, что целевым
     $ # репозиторием у нас является "a-pocket-repo", а сборочный
     $ # сервер "pocketserver" обладает каким-то стандартным
     $ # интерфейсом для засылки пакетов на сборку. Изменения
     $ # отправляются пачкой: после обработки сборочного задания
     $ # репозиторий на pocketserver должен прийти в соответствие
     $ # с недавно опубликованным "a-product-YYYYDDMM" (если конечно
     $ # не было других изменений от других пользователей).

   Кроме функций, включённых в воображаемую демонстрацию выше, было бы
хорошо иметь ещё и `pocket snapshot`, которая позволяла бы 
"замораживать" различные версии кармана на протяжении сеанса работы с 
ним, и затем публиковать, а также отправлять на сборку такие именованные 
версии. На мой взгляд, это было крайне удобно для тестирования, когда 
приходится собирать несколько версий одного и того же пакета, например, 
с различным вариантом фикса, и уже на месте выяснять, какая же версия 
оказалась лучше. В связи с этим вторая штука, которой не нашлось места в 
воображаемой сессии -- это автоподнятие релизов. Каждая сборка пакета в 
карман попадает в него с обновлённой версией релиза, причём релиз 
временно принимает в этом случае форму oldrel-YYYY-DD-MM-n, где n -- 
собственно автоинкрементное поле, а перед ним -- дата, перед ней -- 
старый номер релиза. Это, опять же, удобно при тестировании -- можно 
отмечать, какая именно сборка оказалась удачной. И `apt-get 
dist-upgrade` обновит пакет без специальных пинков. А при отправке 
задания на сборочный сервер этот дополнительный хвост к номеру релиза 
убирается.

   А ещё `pocket shell`, конечно.


   Примерно так. За основу взял hasher-priv, который без вопросов 
собрался и работает на Debian, за что ldv@ отдельное большое спасибо! А 
дальше, видимо, make, потому как "система рецептов" показалась мне для 
решения этой задачи очень даже уместной.

   На сегодняшний день добился того, что базовая система ставится в чрут 
посредством debootstrap, более-менее стандартным образом, но под 
управлением hasher-priv. И в этот чрут потом можно сходить через 
`hasher-priv chrootuid1 fakeroot ...`. В качестве статических утилит для 
взлёта была использована busybox-static. Как оказалось, в Debian она 
очень кстати умеет кое-что из dpkg.

   Исходный код:

http://git.altlinux.org/people/manowar/private/pocket.git


     Павел.

   P.S. Вероятно это -- очередной велосипед. Но я вот что-то не нашёл 
пока способа сборки в изолированном окружении, такого же простого как 
hasher. :-) Почему-то всем этим штукам или нужен root, или какие-нибудь 
спецправа на супервизор. А ещё, конечно, хочется разных вышеописанных 
вкусностей. Чтобы workflow был не сервероцентричным, а юзероцентричным! 
Может быть даже межпользовательски-дружелюбным. :-)

---
[1] http://www.altlinux.org/Pockets


Подробная информация о списке рассылки Devel