Custom Local Repository (Русский)
Contents
Новый метод (repo-add)
В pacman 3 появился новый скрипт repo-add, который делает генерацию собственного локального репозитория еще проще. Используйте repo-add --help для больших подробностей по его использованию.
При помощи этого скрипта очень легко поддерживать состояние вашей базы данных в актуальном состоянии. Просто сохраните все пакеты, которые вы хотите положить в свой репозиторий, в одной папке, перейдите в эту папку, и запустите следующую команду:
repo-add /path/to/repo.db.tar.gz *.pkg.tar.xz
где 'repo' - это имя вашего собственного репозитория. Последний аргумент добавляет все pkg.tar.xz файлы к вашему репозиторию, так что будьте внимательны, если вы имеете несколько разных версий одного и того же пакета в вашей папке. Неизвестно какая версия попадет в репозиторий, но попадет только одна!
Для добавления нового пакета (и удаления старого, если он есть) просто запустите:
repo-add /path/to/repo.db.tar.gz packagetoadd-1.0-1-i686.pkg.tar.xz
Если есть пакет, который вы не хотите видеть в репозитории, читайте repo-remove --help.
Старый метод (свой репозиторий из дерева ABS)
Этот документ описывает то, как создать ваш собственный репозиторий из индивидуализированного дерева ABS, содержащего только те PKGBUILD'ы которые вы хотите включить в ваш репозиторий. Это полезно для создания локального репозитория или особого репозитория для пакетов, которых нет в официальных репозиториях.
Основано на: https://bbs.archlinux.org/viewtopic.php?p=29474
Выполните gensync и прочитайте опции командной строки.
Коротко: параметрами являются корни PKGBUILD'ов, сортированные в подкаталогах (например, как дерево ABS), предполагаемые имя и местоположение файла базы данных и директория, содержащая бинарные пакеты.
Создайте дерево ABS. Вы можете сделать это, используя команду abs, если вы хотите создать копию обычного дерева ABS или создать дерево вручную. Придерживайтесь следующего правила: каждый PKGBUILD находится в своей собственной директории (официальный ли это PKGBUILD или тот, который вы создали сами). Если вы изменяете официальный ABS репозиторий, удалите любые abs директории, которые вы не хотите включать в конечный репозиторий.
Сохраните все пакеты, для которых вы хотите создать репозиторий, в особый каталог (например, /home/arch/i686/core). Вы можете создать пакет, используя makepkg, или скачать их с помощью pacman'а, это зависит от ситуации.
Выполните gensync, подставляя необходимые параметры. Например:
gensync /var/abs /home/arch/i686/core/core.db.tar.gz /home/arch/i686/core
построит репозиторий для репозитория core, если он расположен в /home/arch/i686/core. Имя файла db.tar.gz - это имя репозитория, который вы создаёте. Обычно используют такое же имя для и директории, которая хранит пакеты.
Используйте
tar -tzf core.db.tar.gz || less
для того чтобы удостовериться, что база содержит верные пакеты.
Быстрый способ (свой репозиторий из директории с пакетами)
Не работает пакетами с суффиксами -i686/-x86_64
Если у вас есть директория с большим количеством пакетом (core/extra/testing/selfbuild/etc) и вы хотите включить их в один репозиторий .
(Мне нужно было пересобрать установочный диск для того чтобы положить всё, что нужно на компьютер без интернет соединения). Вы можете создать core.db.tar.gz из этой директории, используя маленький скрипт (не очень элегантно, зато работает)
#!/usr/bin/perl
use strict;
unless (defined $ARGV[0] && defined $ARGV[1]){
print STDERR "\nUsage: gendb <DIR> <NAME>\n\n";
exit 1;
}
`rm -rf /tmp/pkgdb`;
opendir(DIR, $ARGV[0]) || die "Can't open dir $ARGV[0]: $!";
while(my $file=readdir(DIR)){
if($file =~ /^(.+)\.pkg.tar.gz$/){
my $pkg = $1;
my @filedata=stat($ARGV[0]."/".$file);
my %info = ();
my $i;
print STDERR "Processing $file\n";
`mkdir -p /tmp/pkgdb/$pkg`;
`rm -f /tmp/PKGINFO ; tar -O -xzf $ARGV[0]/$file .PKGINFO > /tmp/PKGINFO`;
open(INPUT, "/tmp/PKGINFO") || die "can't open PKGINFO file: $!";
while(<INPUT>)
{
chomp;
if( /^([a-z]+)\s?[=]\s?(.+)$/ )
{
my $attr = $1;
my $value = $2;
$info{$attr} .= $value." ";
}
}
close(INPUT);
open(OUT, "> /tmp/pkgdb/$pkg/desc") || die "can't open desc file: $!";
print OUT "\%NAME\%\n".$info{"pkgname"}."\n\n";
print OUT "\%VERSION\%\n".$info{"pkgver"}."\n\n";
print OUT "\%DESC\%\n".$info{"pkgdesc"}."\n\n";
print OUT "\%CSIZE\%\n".$filedata[7]."\n\n";
print OUT "\%MD5SUM\%\n";
close(OUT);
`md5sum $ARGV[0]/$file | cut -f1 -d' ' >> /tmp/pkgdb/$pkg/desc`;
open(OUT, ">> /tmp/pkgdb/$pkg/desc") || die "can't open desc file: $!";
print OUT "\n\%REPLACES\%\n";
foreach $i (split(' ',$info{"replaces"})){ print OUT $i."\n";}
print OUT "\n";
close(OUT);
open(OUT, "> /tmp/pkgdb/$pkg/depends") || die "can't open depends file: $!";
print OUT "\%DEPENDS\%\n";
foreach $i (split(' ',$info{"depend"})){ print OUT $i."\n";}
print OUT "\n";
print OUT "\%CONFLICTS\%\n";
foreach $i (split(' ',$info{"conflict"})){ print OUT $i."\n";}
print OUT "\n";
print OUT "\%PROVIDES\%\n";
foreach $i (split(' ',$info{"provides"})){ print OUT $i."\n";}
print OUT "\n";
close(OUT);
}
}
closedir DIR;
`cd /tmp/pkgdb ; tar -czf $ARGV[1].db.tar.gz *`;
`cp /tmp/pkgdb/$ARGV[1].db.tar.gz $ARGV[0]/`
Поместите содержимое скрипта в файл gendb и укажите, что он исполняемый:
chmod +x gendb
После этого скриптом уже можно пользоваться и натравливать на папку с пакетами в таком стиле:
./gendb /путь/к/папке название_репозитория
Результат будет храниться в /путь/к/папке/название_репозитория.db.tar.gz.
Другой быстрый способ, скрипт на Python
Этот скрипт также создаёт репозиторий из директории, содержащей пакеты, не нуждаясь в ABS. Он также показывает неудовлетворённые зависимости (хотя на этот момент игнорирует номера версий) и может фильтровать их в довольно примитивным образом, игнорируя определённое множество (например, пакеты в /var/lib/pacman/local).
#!/usr/bin/env python
# gen_repo.py - build a repository db file from a set of packages
#
# Author: gradgrind <mt.42-at-web.de>
#
# This file is part of the larch project.
#
# larch is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# larch is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with larch; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
#----------------------------------------------------------------------------
#
import os
import os.path
import shutil
import sys
import tarfile
from types import *
import re
# Regex to remove version comparison from package dependency
onlyname = re.compile("[^=><]+")
def create_db(dbname, packagesdir, dep_ignore_list):
os.chdir(packagesdir)
# Open tar archive for db
dbtar = tarfile.open(dbname + ".db.tar.gz", "w:gz")
# Get a list of packages
packages = filter(lambda s: s.endswith(".pkg.tar.gz"), os.listdir("."))
packages.sort()
# Make a dict for keeping track of dependencies
dep_dict = {}
if os.path.isdir(dbname): shutil.rmtree(dbname)
os.mkdir(dbname, 0755)
for p in packages:
pkg_dict = get_pkg_info(p)
pkg_name = pkg_dict["pkgname"]
pkg_dbname = pkg_name + "-" + pkg_dict["pkgver"]
pkg_dir = os.path.join(dbname, pkg_dbname)
os.mkdir(pkg_dir, 0755)
mkdesc(pkg_dir, pkg_dict)
mkdepends(pkg_dir, pkg_dict)
# Add dependency info to dependency dict
for d in pkg_dict["depend"]:
# But also need to cater for versioning!!!
# I will just ignore it here ...
d = onlyname.match(d).group()
if d not in dep_dict:
dep_dict[d] = [False]
dep_dict[d].append(pkg_name)
# Mark packages provided by this one
for p in (pkg_dict["provides"] + [pkg_name]):
if p in dep_dict:
dep_dict[p][0] = True
else:
dep_dict[p] = [True]
dbtar.add(pkg_dir, pkg_dbname)
# Mark packages in ignore list
for p in dep_ignore_list:
if p in dep_dict:
dep_dict[p][0] = True
# Close db tar archive
dbtar.close()
# Remove directory structure
shutil.rmtree(dbname)
# Now display unstaisfied dependencies
# Should add the possibility of declaring a list of packages
# available (e.g. the base set, or all those on the live CD ..."
print "-------------\nUnsatisfied dependencies:"
for d, r in dep_dict.iteritems():
if not r[0]:
print " ", d, "- needed by: ",
for p in r[1:]:
print p, " ",
print ""
def get_pkg_info(pkg):
tf = tarfile.open(pkg, "r:gz")
pkginfo = tf.extractfile(".PKGINFO")
pkg_dict = {# the first ones go to 'desc'
"pkgname" : None,
"pkgver" : None,
"pkgdesc" : None,
# from here they are optional, and can occur more than once
"group" : [],
"replaces" : [],
# the rest go to 'depends'
"depend" : [],
"conflict" : [],
"provides" : [],
}
while True:
l = pkginfo.readline().strip()
if not l: break
if l[0] == "#": continue
split3 = l.split(None, 2)
while len(split3) < 3: split3.append(None)
key, eq, value = split3
if key not in pkg_dict: continue
val = pkg_dict[key]
if val == None:
pkg_dict[key] = value
continue
if not isinstance(val, ListType):
print "Unexpected situation ...\n key [oldvalue] <- newvalue"
print key, "[%s]" % val, "<-", value
sys.exit(1)
pkg_dict[key].append(value)
pkginfo.close()
pkg_dict["md5sum"] = md5sum(pkg)
pkg_dict["csize"] = str(os.path.getsize(pkg))
return pkg_dict
def mkdesc(pkg_dir, pkg_dict):
f = open(os.path.join(pkg_dir, "desc"), "w")
f.write("%NAME%\n" + pkg_dict["pkgname"] + "\n\n")
f.write("%VERSION%\n" + pkg_dict["pkgver"] + "\n\n")
f.write("%DESC%\n" + pkg_dict["pkgdesc"] + "\n\n")
f.write("%CSIZE%\n" + pkg_dict["csize"] + "\n\n")
f.write("%MD5SUM%\n" + pkg_dict["md5sum"] + "\n\n")
groups = pkg_dict["group"]
if groups:
f.write("%GROUPS%\n")
for g in groups:
f.write(g + "\n")
f.write("\n")
replaces = pkg_dict["replaces"]
if replaces:
f.write("%REPLACES%\n")
for r in replaces:
f.write(r + "\n")
f.write("\n")
f.close()
def mkdepends(pkg_dir, pkg_dict):
f = open(os.path.join(pkg_dir, "depends"), "w")
depends = pkg_dict["depend"]
if depends:
f.write("%DEPENDS%\n")
for d in depends:
f.write(d + "\n")
f.write("\n")
conflicts = pkg_dict["conflict"]
if conflicts:
f.write("%CONFLICTS%\n")
for c in conflicts:
f.write(c + "\n")
f.write("\n")
provides = pkg_dict["provides"]
if provides:
f.write("%PROVIDES%\n")
for p in provides:
f.write(p + "\n")
f.write("\n")
def md5sum(filepath):
f = file(filepath, 'rb')
m = hashlib.md5()
while True:
d = f.read(8192)
if not d:
break
m.update(d)
f.close()
return m.hexdigest()
def cat(path):
"""Python version of 'cat'"""
fp = open(path, "r")
op = "".join(fp)
fp.close()
return op
def usage():
print """
genrepo.py package-dir [repo-name] [-- ignore-list]
Generate a pacman db file for the packages in package-dir.
If repo-name is given, this will be used as the name for the repository,
otherwise the name of the directory containing the packages will be used.
All dependencies of the packages in the repository will be listed to
standard output, but a list of packages not to be included in this list
can be specified:
ignore-list should be either a file containing the names of packages
not to be listed as dependencies (separated by space or newline), or a
directory containing 'package directories', like /var/abs/base or
/var/lib/pacman/local
"""
sys.exit(1)
if __name__ == "__main__":
if os.getuid() != 0:
print "Must be root to run this"
sys.exit(1)
if len(sys.argv) < 2:
usage()
pkgdir = sys.argv[1]
if len(sys.argv) == 2 or sys.argv[2] == "--":
dbname = os.path.basename(os.path.abspath(pkgdir))
i = 2
else:
dbname = sys.argv[2]
i = 3
if len(sys.argv) == i:
ignore_list = []
elif len(sys.argv) == i+2 and sys.argv[i] == "--":
ignore_list = sys.argv[i+1]
else:
usage()
if not os.path.isdir(pkgdir):
print "\n1st argument must be a directory"
sys.exit(1)
print "\nCreating pacman database (%s.db.tar.gz) file in %s" % (dbname, pkgdir)
i = raw_input("Do you want to do this? [Y/n] ").strip()
if i and i[0] not in "Yy":
print " -> Not creating package db"
sys.exit(0)
if ignore_list:
# Get list of packages to be ignored in dependency list
if os.path.isfile(ignore_list):
# A simple file containing the names of packages to ignore
# separated by space or newline.
ignore_list = cat(ignore_list).split()
elif os.path.isdir(ignore_list):
# A directory containing packages or package-directories (like in abs)
l = os.listdir(ignore_list)
# See if there are packages in this directory
lp = filter(lambda s: s.endswith(".pkg.tar.gz"), l)
if lp:
l = map(lambda s: s.replace(".pkg.tar.gz", ""), lp)
re1 = re.compile(r"(.+)-[^-]+?-\d+")
ignore_list = []
for f in l:
m = re1.match(f)
if m:
ignore_list.append(m.group(1))
else:
print "!!! Invalid ignore-list"
usage()
create_db(dbname, pkgdir, ignore_list)
Заключительная часть
При желании вы можете добавить репозиторий (директорию, содержащую пакеты и файл db.tar.gz) на ftp (или nfs) сервер машины.
Добавьте репозиторий в ваш pacman.conf. Именем репозитория должно быть имя файла db.tar.gz. Вы можете сослаться непосредственно на него, используя синтаксис file:// или обратиться по ftp: ~ftp://localhost/path/to/directory
Не забудьте добавить ваш собственный репозиторий в наш список неофициальных пользовательских репозиториев для того чтобы другие пользователи могли найти и установить ваши пакеты!