# Custom Local Repository (Русский)

## Новый метод (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

Не забудьте добавить ваш собственный репозиторий в наш список неофициальных пользовательских репозиториев для того чтобы другие пользователи могли найти и установить ваши пакеты!

Categories:

- Package management (Русский)
- Русский