В то время, как фанаты экзотики на хабре активно пьют чашки с Java, принимают дозы F# и вкалывают себе прочий Haskell, мы с вами учились собирать их творения в deb-пакеты. За прошедшее с предыдущей части время у кого-то наверняка уже накопилось несколько готовых пакетов, а в официальный репозиторий Debian и Ubuntu мы их пока помещать даже не пробовали. Поэтому настало время подумать, каким бы образом организовать всё накопленное богатство в один большой красивый репозиторий, который не стыдно будет предложить для использования и другим пользователям.
(Части 1, 2 и 3)
Для этих целей мы с вами рассмотрим две программы (просто потому, что для постижения дао мне хватило именно их) — это apt-ftparchive из стандартного пакета apt-utils и пакет reprepro.
Давайте для начала посмотрим, как устроен нормальный репозиторий. Репозитории бывают двух видов — уродские и косые (или в оригинале — flat — плоские, их мы рассматривать не будем) и нормальные иерархические. Вот иерархические мы и будем учиться создавать. Итак, нормальный репозиторий состоит из двух каталогов: pool и dists.
pool/
В этом каталоге в произвольной иерархии могут лежать пакеты. Я говорю «в произвольной», потому что вы можете действительно их раскладывать как угодно, но на практике обычно реализуется следующая иерархия:
Здесь «main» — это название секции, к которой мы относим наш пакет (о секциях читайте в первой статье), дальше идёт первая буква названия src-пакета и затем каталог, названный по имени src-пакета. В этом каталоге и будет лежать src-пакет и все собранные из него бинарные пакеты. Для многочисленных библиотек, у которых названия пакетов поголовно начинаются на «lib», вместо буквы «l» используется отдельное «liba», где «a» — первая буква названия после «lib».
dists/
Этот каталог организован гораздо сложнее. Первым делом мы в нём должны обнаружить каталог с названием репозитория — в убунту это различные «intrepid», «intrepid-proposed», «jaunty» и т.п., в дебиане это — «etch», «etch-proposed-updates», «lenny» а также их синонимы — «stable», «testing» и др. — они как правило являются симлинками на каноническое название. Таким образом можно хранить в одном месте пакеты сразу для нескольких репозиториев.
dists/unstable/[1]
Внутри этого каталога можно обнаружить следующие файлы:
Помимо этого в этом же каталоге мы обнаружим каталоги с названиями, соответствующими названиям секций репозитория — main, contrib, non-free или main, universe, multiverse, restricted или теми, которые определите вы сами.
dists/unstable/main/
В каждой секции (не только main, разумеется) можно наблюдать набор каталогов следующего вида:
dists/unstable/main/binary-i386/
Тут хранится файл Packages (а также Packages.gz и Packages.bz2), содержащий описания бинарных пакетов соответствующей архитектуры. По сути он представляет собой соединенные воедино control-файлы из всех бинарных пакетов репозитория для данной архитектуры. Часто здесь еще можно обнаружить файл Release, который, в отличие от такого же файла в dists/unstable/, не содержит в себе хэшей файлов, а хранит только описания репозитория для данного каталога.
dists/unstable/main/source/
Здесь лежит всё то же самое, что и в binary-arch, за исключением того, что вместо слова Packages используется слово Sources.
Разумеется, все эти файлы можно писать ручками, но я бы вам не советовал. Особенно, если у вас есть хотя бы 3-4 пакета. Поэтому мы воспользуемся волшебной утилитой apt-ftparchive из пакета apt-utils и dpkg-scanpackages из пакета dpkg-dev.
Итак, допустим, у нас есть созданный нами в предыдущей серии некий src-пакет qutim, и собранные из него бинарные пакеты qutim и qutim-dev. Собирали мы их под две архитектуры, поэтому теперь мы имеем следующий набор файлов:
Первые три файла, как мы помним, являются src-пакетом, а оставшиеся — это собранные бинарные пакеты. Еще у нас имеются файлы qutim_0.1.99.138-1_i386.changes и qutim_0.1.99.138-1_amd64.changes, сгенерированные при сборке пакета.
Давайте соберем из них минимально приличный репозиторий.
Итак, создадим каталог с начальной структурой и скопируем в него пакеты:
Как видите, в размещении самих пакетов в репозитории нет вообще ничего сложного. Немножко сложнее будет сгенерировать набор описаний для этих пакетов:
Здесь мы генерируем для каждой архитектуры и для src-пакетов описания, после чего помещаем их в соответствующий файл. Заметьте, что мы генерируем по три файла — Packages, Packages.gz и Packages.bz2 (аналогично с Source). В этом в принципе нет необходимости, но мы будем культурными людьми и сгенерируем все три — выбирайте любой по душе. Опция -a указывает каждый раз, пакеты для какой архитектуры выбирать из всего набора в pool/main. Как я уже говорил, пакет qutim-dev при этом попадёт
Теоретически, и генерацию описания бинарных пакетов можно было сделать при помощи apt-ftparchive, но я так и не нашёл у него опции, которая выделяет пакеты определенной архитектуры, вместо того чтобы пихать в вывод всё подряд.
Теперь нам необходимо сгенерировать файл Release:
Описание всё равно приходится писать руками, apt-ftparchive делать это не умеет.
И последний шаг — подпишем наш репозиторий:
Вуаля, наш репозиторий можно использовать. Добавим в /etc/apt/sources.list строки:
Обновляем список пакетов — и репозиторием можно пользоваться.
Разумеется, это не очень удобно — перегенерировать файлы описаний придётся каждый раз при обновлении набора пакетов. Конечно, всё это автоматизируется скриптами, но всё равно так неинтересно. Поэтому мы перейдём ко второй используемой программе, которая сделает всё за нас.
Эта программа — просто рай для тех, кто не хочет заморачиваться над структурой репозитория вообще, а хочет один раз настроить и пользоваться. Поэтому мы сносим наш доморощенный репозиторий и ставим пакет reprepro:
Теперь создаём в каталоге rep/conf файл distributions, в нём мы будем описывать наш репозиторий:
Codename должен содержать название репозитория (lenny, hardy, etch, jaunty), а Suite — синоним для создания символических ссылок (stable, unstable). Параметры Version, Origin, Label и Description особой смысловой нагрузки не несут и просто копируются в файл Release. Параметр SignWith указывает на необходимость подписать репозиторий, создав файл Release.gpg. DebIndices, DscIndices и Contents — указывают на необходимость создания файлов Packages, Sources и Contents-arch — в простом и в архивированном виде.
Теперь мы можем добавить наши пакеты:
ключ -b указывает reprepro, где лежит наш репозиторий, команда --ask-passphrase сообщает, что наш gpg-ключ запаролен и при подписывании дистрибутива неплохо бы спросить пароль, ключ -C сообщает, что мы включаем пакет в секцию main. Здесь надо отметить один интересный момент. Команда include принимает в качестве аргумента название репозитория, в который включить файл и имя .changes файла, созданного при сборке src-пакета. При этом в репозиторий добавляется src-пакет и собранные бинарные пакеты под данную архитектуру. И тут у нас возникает проблема — разработчики reprepro наотрез отказываются добавлять в репозиторий пакеты уже существующей в нём версии. Поэтому если мы теперь попробуем указать .changes файл для другой архитектуры — то получим ошибку — у нас уже добавлен не только src-пакет, но еще и пакет qutim-dev, собранный для всех архитектур. Для этого случая есть команда includedeb, которая позволит указать только deb-файл с отличающейся архитектурой.
Просмотрите теперь содержимое каталога rep. Подкаталог conf содержит конфигурацию репозитория (подробнее о ней можно почитать в `man reprepro`), каталог db — его внутреннюю базу данных. А вот каталоги pool и dists стали сильно отличаться от того, что мы создавали при помощи apt-ftparchive и стали похожи на ту структуру, которую я описывал в начале. Поскольку адрес репозитория у нас в sources.list уже есть, набираем `sudo apt-get update`и пользуемся :)
Ну и напоследок, если вы хотите более подробно изучить структуру репозитория, рекомендую изучить официальный репозиторий Ubuntu. Желающие, конечно, могут ознакомиться и с репозиторием Debian, но сразу предупреждаю, что он устроен куда сложнее.
*[1] здесь и далее вместо абстрактного названия каталога я буду использовать конкретные примеры
(Части 1, 2 и 3)
Для этих целей мы с вами рассмотрим две программы (просто потому, что для постижения дао мне хватило именно их) — это apt-ftparchive из стандартного пакета apt-utils и пакет reprepro.
Структура репозитория
Давайте для начала посмотрим, как устроен нормальный репозиторий. Репозитории бывают двух видов — уродские и косые (или в оригинале — flat — плоские, их мы рассматривать не будем) и нормальные иерархические. Вот иерархические мы и будем учиться создавать. Итак, нормальный репозиторий состоит из двух каталогов: pool и dists.
pool/
В этом каталоге в произвольной иерархии могут лежать пакеты. Я говорю «в произвольной», потому что вы можете действительно их раскладывать как угодно, но на практике обычно реализуется следующая иерархия:
pool/main/q/qutim/
Здесь «main» — это название секции, к которой мы относим наш пакет (о секциях читайте в первой статье), дальше идёт первая буква названия src-пакета и затем каталог, названный по имени src-пакета. В этом каталоге и будет лежать src-пакет и все собранные из него бинарные пакеты. Для многочисленных библиотек, у которых названия пакетов поголовно начинаются на «lib», вместо буквы «l» используется отдельное «liba», где «a» — первая буква названия после «lib».
dists/
Этот каталог организован гораздо сложнее. Первым делом мы в нём должны обнаружить каталог с названием репозитория — в убунту это различные «intrepid», «intrepid-proposed», «jaunty» и т.п., в дебиане это — «etch», «etch-proposed-updates», «lenny» а также их синонимы — «stable», «testing» и др. — они как правило являются симлинками на каноническое название. Таким образом можно хранить в одном месте пакеты сразу для нескольких репозиториев.
dists/unstable/[1]
Внутри этого каталога можно обнаружить следующие файлы:
- Contents-arch — файл, в котором содержится список всех файлов из всех пакетов. Вместо «arch» подставляется название архитектуры пакета, а на конце может быть еще ".gz" и ".bz2" (если файл соответствующим образом сжат). Он необязательный и используется в основном утилитами типа apt-file для поиска, в каком пакете можно найти требуемый файл. Пример названия — Contents-amd64.gz
- Release — это файл, в котором содержится краткое описание репозитория и хэши всех вспомогательных файлов, которые содержатся в dists/.
- Release.gpg — это gpg-подпись файла Release, заверяющая создание файла определенным автором. Оба файла являются необязательными, но при их отсутствии APT гарантированно будет ругаться на неаутентифицированный репозиторий.
Помимо этого в этом же каталоге мы обнаружим каталоги с названиями, соответствующими названиям секций репозитория — main, contrib, non-free или main, universe, multiverse, restricted или теми, которые определите вы сами.
dists/unstable/main/
В каждой секции (не только main, разумеется) можно наблюдать набор каталогов следующего вида:
- binary-arch — каталог для описания пакетов соответствующей архитектуры, например, binary-amd64 — для архитектуры amd64. Пакеты с архитектурой all (такие, как dev-пакеты) будут упомянуты в каталоге для каждой архитектуры.
- source — в этом каталоге описываются src-пакеты
- i18n — этот каталог используется редко и в основном только в официальных репозиториях — в нем находятся файлы с переводом описаний пакетов на другие языки. Я не буду описывать этот каталог, поскольку он не является таким уж действительно важным элементом, если вы захотите его использовать — вы всегда можете посмотреть в репозитории убунту
dists/unstable/main/binary-i386/
Тут хранится файл Packages (а также Packages.gz и Packages.bz2), содержащий описания бинарных пакетов соответствующей архитектуры. По сути он представляет собой соединенные воедино control-файлы из всех бинарных пакетов репозитория для данной архитектуры. Часто здесь еще можно обнаружить файл Release, который, в отличие от такого же файла в dists/unstable/, не содержит в себе хэшей файлов, а хранит только описания репозитория для данного каталога.
dists/unstable/main/source/
Здесь лежит всё то же самое, что и в binary-arch, за исключением того, что вместо слова Packages используется слово Sources.
apt-ftparchive
Разумеется, все эти файлы можно писать ручками, но я бы вам не советовал. Особенно, если у вас есть хотя бы 3-4 пакета. Поэтому мы воспользуемся волшебной утилитой apt-ftparchive из пакета apt-utils и dpkg-scanpackages из пакета dpkg-dev.
Итак, допустим, у нас есть созданный нами в предыдущей серии некий src-пакет qutim, и собранные из него бинарные пакеты qutim и qutim-dev. Собирали мы их под две архитектуры, поэтому теперь мы имеем следующий набор файлов:
qutim_0.1.99.138.orig.tar.gz
qutim_0.1.99.138-1.diff.gz
qutim_0.1.99.138-1.dsc
qutim_0.1.99.138-1_amd64.deb
qutim_0.1.99.138-1_i386.deb
qutim-dev_0.1.99.138-1_all.deb
Первые три файла, как мы помним, являются src-пакетом, а оставшиеся — это собранные бинарные пакеты. Еще у нас имеются файлы qutim_0.1.99.138-1_i386.changes и qutim_0.1.99.138-1_amd64.changes, сгенерированные при сборке пакета.
Давайте соберем из них минимально приличный репозиторий.
Итак, создадим каталог с начальной структурой и скопируем в него пакеты:
$ mkdir -p rep/dists
$ mkdir -p rep/pool/main
$ cp qutim_0.1.99.138.orig.tar.gz qutim_0.1.99.138-1.diff.gz qutim_0.1.99.138-1.dsc qutim_0.1.99.138-1_amd64.deb qutim_0.1.99.138-1_i386.deb qutim-dev_0.1.99.138-1_all.deb rep/pool/main/
$ cd rep
Как видите, в размещении самих пакетов в репозитории нет вообще ничего сложного. Немножко сложнее будет сгенерировать набор описаний для этих пакетов:
$ mkdir -p dists/unstable/main/binary-amd64
$ mkdir dists/unstable/main/binary-i386
$ mkdir dists/unstable/main/source
$ dpkg-scanpackages -a amd64 pool/main >dists/unstable/main/binary-amd64/Packages 2>/dev/null
$ dpkg-scanpackages -a amd64 pool/main | gzip -c9 >dists/unstable/main/binary-amd64/Packages.gz 2>/dev/null
$ dpkg-scanpackages -a amd64 pool/main | bzip2 -c9 >dists/unstable/main/binary-amd64/Packages.bz2 2>/dev/null
$ dpkg-scanpackages -a i386 pool/main >dists/unstable/main/binary-i386/Packages 2>/dev/null
$ dpkg-scanpackages -a i386 pool/main | gzip -c9 > dists/unstable/main/binary-i386/Packages.gz 2>/dev/null
$ dpkg-scanpackages -a i386 pool/main | bzip2 -c9 > dists/unstable/main/binary-i386/Packages.bz2 2>/dev/null
$ apt-ftparchive sources pool > dists/unstable/main/source/Sources 2>/dev/null
$ apt-ftparchive sources pool | gzip -c9 > dists/unstable/main/source/Sources.gz 2>/dev/null
$ apt-ftparchive sources pool | bzip2 -c9 > dists/unstable/main/source/Sources.bz2 2>/dev/null
Здесь мы генерируем для каждой архитектуры и для src-пакетов описания, после чего помещаем их в соответствующий файл. Заметьте, что мы генерируем по три файла — Packages, Packages.gz и Packages.bz2 (аналогично с Source). В этом в принципе нет необходимости, но мы будем культурными людьми и сгенерируем все три — выбирайте любой по душе. Опция -a указывает каждый раз, пакеты для какой архитектуры выбирать из всего набора в pool/main. Как я уже говорил, пакет qutim-dev при этом попадёт
Теоретически, и генерацию описания бинарных пакетов можно было сделать при помощи apt-ftparchive, но я так и не нашёл у него опции, которая выделяет пакеты определенной архитектуры, вместо того чтобы пихать в вывод всё подряд.
Теперь нам необходимо сгенерировать файл Release:
$ echo "Archive: unstable" > dists/unstable/Release
$ echo "Suite: unstable" >> dists/unstable/Release
$ echo "Components: main" >> dists/unstable/Release
$ echo "Origin: qutim.org" >> dists/unstable/Release
$ echo "Label: qutim.org Debian repository" >> dists/unstable/Release
$ echo "Architectures: amd64 i386" >> dists/unstable/Release
$ echo "Description: Debian qutIM unstable" >> dists/unstable/Release
$ apt-ftparchive release dists/unstable >> dists/unstable/Release
Описание всё равно приходится писать руками, apt-ftparchive делать это не умеет.
И последний шаг — подпишем наш репозиторий:
$ gpg -abs -o dists/unstable/Release.gpg dists/unstable/Release
Вуаля, наш репозиторий можно использовать. Добавим в /etc/apt/sources.list строки:
deb file:///path/to/rep unstable main
deb-src file:///path/to/rep unstable main
Обновляем список пакетов — и репозиторием можно пользоваться.
Разумеется, это не очень удобно — перегенерировать файлы описаний придётся каждый раз при обновлении набора пакетов. Конечно, всё это автоматизируется скриптами, но всё равно так неинтересно. Поэтому мы перейдём ко второй используемой программе, которая сделает всё за нас.
reprepro
Эта программа — просто рай для тех, кто не хочет заморачиваться над структурой репозитория вообще, а хочет один раз настроить и пользоваться. Поэтому мы сносим наш доморощенный репозиторий и ставим пакет reprepro:
$ cd .. && rm -rf rep/*
$ sudo apt-get install reprepro
$ mkdir rep/conf
Теперь создаём в каталоге rep/conf файл distributions, в нём мы будем описывать наш репозиторий:
Codename: lenny
Suite: unstable
Version: unstable
Origin: qutim.org
Label: qutim.org Debian Repository
Description: qutim.org Debian repository
Architectures: source i386 amd64
Components: main
SignWith: default
DebIndices: Packages Release . .gz .bz2
DscIndices: Sources Release . .gz .bz2
Contents: . .gz .bz2
Codename должен содержать название репозитория (lenny, hardy, etch, jaunty), а Suite — синоним для создания символических ссылок (stable, unstable). Параметры Version, Origin, Label и Description особой смысловой нагрузки не несут и просто копируются в файл Release. Параметр SignWith указывает на необходимость подписать репозиторий, создав файл Release.gpg. DebIndices, DscIndices и Contents — указывают на необходимость создания файлов Packages, Sources и Contents-arch — в простом и в архивированном виде.
Теперь мы можем добавить наши пакеты:
$ reprepro -b rep/ createsymlinks
$ reprepro -b rep/ --ask-passphrase -C main include unstable qutim_0.1.99.138-1_amd64.changes
$ reprepro -b rep/ --ask-passphrase -C main includedeb unstable qutim_0.1.99.138-1_i386.deb
ключ -b указывает reprepro, где лежит наш репозиторий, команда --ask-passphrase сообщает, что наш gpg-ключ запаролен и при подписывании дистрибутива неплохо бы спросить пароль, ключ -C сообщает, что мы включаем пакет в секцию main. Здесь надо отметить один интересный момент. Команда include принимает в качестве аргумента название репозитория, в который включить файл и имя .changes файла, созданного при сборке src-пакета. При этом в репозиторий добавляется src-пакет и собранные бинарные пакеты под данную архитектуру. И тут у нас возникает проблема — разработчики reprepro наотрез отказываются добавлять в репозиторий пакеты уже существующей в нём версии. Поэтому если мы теперь попробуем указать .changes файл для другой архитектуры — то получим ошибку — у нас уже добавлен не только src-пакет, но еще и пакет qutim-dev, собранный для всех архитектур. Для этого случая есть команда includedeb, которая позволит указать только deb-файл с отличающейся архитектурой.
Просмотрите теперь содержимое каталога rep. Подкаталог conf содержит конфигурацию репозитория (подробнее о ней можно почитать в `man reprepro`), каталог db — его внутреннюю базу данных. А вот каталоги pool и dists стали сильно отличаться от того, что мы создавали при помощи apt-ftparchive и стали похожи на ту структуру, которую я описывал в начале. Поскольку адрес репозитория у нас в sources.list уже есть, набираем `sudo apt-get update`и пользуемся :)
Ну и напоследок, если вы хотите более подробно изучить структуру репозитория, рекомендую изучить официальный репозиторий Ubuntu. Желающие, конечно, могут ознакомиться и с репозиторием Debian, но сразу предупреждаю, что он устроен куда сложнее.
*[1] здесь и далее вместо абстрактного названия каталога я буду использовать конкретные примеры