diff --git a/Makefile b/Makefile
index 823c1e6c..ef224bad 100644
--- a/Makefile
+++ b/Makefile
@@ -132,6 +132,10 @@ build: clean tox
# update package version in spec file
cat package/python-kiwi-spec-template | sed -e s'@%%VERSION@${version}@' \
> dist/python-kiwi.spec
+ # update package version in PKGBUILD file
+ md5sums=$$(md5sum dist/python-kiwi.tar.gz | cut -d" " -f1); \
+ cat package/python-kiwi-pkgbuild-template | sed -e s'@%%VERSION@${version}@' \
+ -e s"@%%MD5SUM@$${md5sums}@" > dist/PKGBUILD
# provide rpm rpmlintrc
cp package/python-kiwi-rpmlintrc dist
diff --git a/kiwi/defaults.py b/kiwi/defaults.py
index d597e3bc..616a5b87 100644
--- a/kiwi/defaults.py
+++ b/kiwi/defaults.py
@@ -672,6 +672,7 @@ class Defaults:
"""
return [
'/usr/share/syslinux',
+ '/usr/lib/syslinux/bios',
'/usr/lib/syslinux/modules/bios',
'/usr/lib/ISOLINUX'
]
@@ -1470,6 +1471,8 @@ class Defaults:
return 'rpm'
elif package_manager in deb_based:
return 'dpkg'
+ elif package_manager == 'pacman':
+ return 'pacman'
def get(self, key):
"""
diff --git a/kiwi/package_manager/__init__.py b/kiwi/package_manager/__init__.py
index 2a1576de..4604ff88 100644
--- a/kiwi/package_manager/__init__.py
+++ b/kiwi/package_manager/__init__.py
@@ -21,6 +21,7 @@ import logging
from kiwi.package_manager.zypper import PackageManagerZypper
from kiwi.package_manager.apt import PackageManagerApt
from kiwi.package_manager.dnf import PackageManagerDnf
+from kiwi.package_manager.pacman import PackageManagerPacman
from kiwi.exceptions import (
KiwiPackageManagerSetupError
@@ -50,6 +51,8 @@ class PackageManager:
manager = PackageManagerDnf(repository, custom_args)
elif package_manager == 'apt-get':
manager = PackageManagerApt(repository, custom_args)
+ elif package_manager == 'pacman':
+ manager = PackageManagerPacman(repository, custom_args)
else:
raise KiwiPackageManagerSetupError(
'Support for package manager %s not implemented' %
diff --git a/kiwi/package_manager/pacman.py b/kiwi/package_manager/pacman.py
new file mode 100644
index 00000000..3fc82a2f
--- /dev/null
+++ b/kiwi/package_manager/pacman.py
@@ -0,0 +1,244 @@
+# Copyright (c) 2019 SUSE Linux GmbH. All rights reserved.
+#
+# This file is part of kiwi.
+#
+# kiwi 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 3 of the License, or
+# (at your option) any later version.
+#
+# kiwi 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 kiwi. If not, see
+#
+import re
+import logging
+
+# project
+from kiwi.command import Command
+from kiwi.package_manager.base import PackageManagerBase
+from kiwi.exceptions import KiwiRequestError
+from kiwi.path import Path
+
+log = logging.getLogger('kiwi')
+
+
+class PackageManagerPacman(PackageManagerBase):
+ """
+ **Implements base class for installation/deletion of
+ packages and collections using pacman**
+
+ :param list pacman_args: pacman arguments from repository runtime
+ configuration
+ :param dict command_env: pacman command environment from repository
+ runtime configuration
+ """
+ def post_init(self, custom_args=None):
+ """
+ Post initialization method
+
+ Store custom pacman arguments
+
+ :param list custom_args: custom pacman arguments
+ """
+ self.custom_args = custom_args
+ if not custom_args:
+ self.custom_args = []
+
+ runtime_config = self.repository.runtime_config()
+ self.pacman_args = runtime_config['pacman_args']
+ self.command_env = runtime_config['command_env']
+
+ def request_package(self, name):
+ """
+ Queue a package request
+
+ :param str name: package name
+ """
+ self.package_requests.append(name)
+
+ def request_collection(self, name):
+ """
+ Queue a collection request
+
+ Pacman does not distinguish the installation of pacakges
+ and collections. Because of that collections are listed together
+ with package requests and a warning message is shown.
+ :param str name: pacman group name
+ """
+ log.warning((
+ 'Pacman treats collection installations as regular packages.'
+ 'It is preferred to list them as regular packages'
+ ))
+ self.package_requests.append(name)
+
+ def request_product(self, name):
+ """
+ Queue a product request
+
+ There is no product definition in the Arch Linux repo data
+
+ :param str name: unused
+ """
+ pass
+
+ def request_package_exclusion(self, name):
+ """
+ Queue a package exclusion(skip) request
+
+ :param str name: package name
+ """
+ self.exclude_requests.append(name)
+
+ def process_install_requests_bootstrap(self):
+ """
+ Process package install requests for bootstrap phase (no chroot)
+
+ :return: process results in command type
+
+ :rtype: namedtuple
+ """
+ Command.run(
+ ['pacman'] + self.pacman_args + [
+ '--root', self.root_dir, '-Sy'
+ ]
+ )
+ bash_command = [
+ 'pacman'
+ ] + self.pacman_args + [
+ '--root', self.root_dir
+ ] + self.custom_args + [
+ '-S', '--needed',
+ '--overwrite', '{}/var/run'.format(self.root_dir)
+ ] + self.package_requests
+ self.cleanup_requests()
+ return Command.call(
+ ['bash', '-c', ' '.join(bash_command)], self.command_env
+ )
+
+ def process_install_requests(self):
+ """
+ Process package install requests for image phase (chroot)
+
+ :return: process results in command type
+
+ :rtype: namedtuple
+ """
+ chroot_pacman_args = Path.move_to_root(self.root_dir, self.pacman_args)
+ bash_command = [
+ 'chroot', self.root_dir, 'pacman'
+ ] + chroot_pacman_args + self.custom_args + [
+ '-S', '--needed'
+ ] + self.package_requests
+ self.cleanup_requests()
+ return Command.call(
+ ['bash', '-c', ' '.join(bash_command)], self.command_env
+ )
+
+ def process_delete_requests(self, force=False):
+ """
+ Process package delete requests (chroot)
+
+ :param bool force: force deletion: true|false
+
+ :raises KiwiRequestError: if none of the packages to delete is
+ installed
+ :return: process results in command type
+
+ :rtype: namedtuple
+ """
+ delete_items = []
+ for delete_item in self.package_requests:
+ try:
+ Command.run(['chroot', self.root_dir, 'pacman', '-Qi', delete_item])
+ delete_items.append(delete_item)
+ except Exception:
+ # ignore packages which are not installed
+ pass
+ if not delete_items:
+ raise KiwiRequestError(
+ 'None of the requested packages to delete are installed'
+ )
+ self.cleanup_requests()
+ chroot_pacman_args = Path.move_to_root(self.root_dir, self.pacman_args)
+ return Command.call(
+ [
+ 'chroot', self.root_dir, 'pacman'
+ ] + chroot_pacman_args + self.custom_args + [
+ '-Rdd' if force else '-Rs'
+ ] + delete_items,
+ self.command_env
+ )
+
+ def update(self):
+ """
+ Process package update requests (chroot)
+
+ :return: process results in command type
+
+ :rtype: namedtuple
+ """
+ chroot_pacman_args = Path.move_to_root(self.root_dir, self.pacman_args)
+ return Command.call(
+ [
+ 'chroot', self.root_dir, 'pacman'
+ ] + chroot_pacman_args + self.custom_args + [
+ '-Su'
+ ],
+ self.command_env
+ )
+
+ def process_only_required(self):
+ """
+ Setup package processing only for required packages.
+
+ This is the only option in pacman, nothing to do here.
+ """
+ pass
+
+ def process_plus_recommended(self):
+ """
+ Setup package processing to also include recommended dependencies.
+
+ There is no such concept of recommended dependencies in pacman.
+ """
+ pass
+
+ def match_package_installed(self, package_name, pacman_output):
+ """
+ Match expression to indicate a package has been installed
+
+ This match for the package to be installed in the output
+ of the pacman command is not 100% accurate. There might
+ be false positives due to sub package names starting with
+ the same base package name
+
+ :param list package_list: list of all packages
+ :param str log_line: dnf status line
+
+ :returns: match or None if there isn't any match
+
+ :rtype: match object, None
+ """
+ return re.match(
+ '.* installing ' + re.escape(package_name) + '.*', pacman_output
+ )
+
+ def match_package_deleted(self, package_name, pacman_output):
+ """
+ Match expression to indicate a package has been deleted
+
+ :param list package_list: list of all packages
+ :param str log_line: pacman status line
+
+ :returns: match or None if there isn't any match
+
+ :rtype: match object, None
+ """
+ return re.match(
+ '.* removing ' + re.escape(package_name) + '.*', pacman_output
+ )
diff --git a/kiwi/repository/__init__.py b/kiwi/repository/__init__.py
index decf9493..30b41ddc 100644
--- a/kiwi/repository/__init__.py
+++ b/kiwi/repository/__init__.py
@@ -19,6 +19,7 @@
from kiwi.repository.zypper import RepositoryZypper
from kiwi.repository.apt import RepositoryApt
from kiwi.repository.dnf import RepositoryDnf
+from kiwi.repository.pacman import RepositoryPacman
from kiwi.exceptions import (
KiwiRepositorySetupError
@@ -43,6 +44,8 @@ class Repository:
return RepositoryDnf(root_bind, custom_args)
elif package_manager == 'apt-get':
return RepositoryApt(root_bind, custom_args)
+ elif package_manager == 'pacman':
+ return RepositoryPacman(root_bind, custom_args)
else:
raise KiwiRepositorySetupError(
'Support for %s repository manager not implemented' %
diff --git a/kiwi/repository/pacman.py b/kiwi/repository/pacman.py
new file mode 100644
index 00000000..0cfe3d3d
--- /dev/null
+++ b/kiwi/repository/pacman.py
@@ -0,0 +1,247 @@
+# Copyright (c) 2019 SUSE Linux GmbH. All rights reserved.
+#
+# This file is part of kiwi.
+#
+# kiwi 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 3 of the License, or
+# (at your option) any later version.
+#
+# kiwi 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 kiwi. If not, see
+#
+import os
+from configparser import ConfigParser
+from tempfile import NamedTemporaryFile
+
+# project
+from kiwi.repository.base import RepositoryBase
+from kiwi.path import Path
+from kiwi.command import Command
+
+
+class RepositoryPacman(RepositoryBase):
+ """
+ **Implements repo handling for pacman package manager**
+
+ :param str shared_pacman_dir: shared directory between image root
+ and build system root
+ :param str runtime_pacman_config_file: pacman runtime config file name
+ :param list pacman_args: zypper caller args plus additional custom args
+ :param dict command_env: customized os.environ for zypper
+ """
+ def post_init(self, custom_args=None):
+ """
+ Post initialization method
+
+ Store custom pacman arguments and create runtime configuration
+ and environment
+
+ :param list custom_args: pacman arguments
+ """
+ self.custom_args = custom_args
+ self.check_signatures = False
+ self.repo_names = []
+ if not custom_args:
+ self.custom_args = []
+
+ self.runtime_pacman_config_file = NamedTemporaryFile(
+ dir=self.root_dir
+ )
+
+ if 'check_signatures' in self.custom_args:
+ self.custom_args.remove('check_signatures')
+ self.check_signatures = True
+
+ manager_base = self.shared_location + '/pacman'
+
+ self.shared_pacman_dir = {
+ 'cache-dir': manager_base + '/cache',
+ 'repos-dir': manager_base + '/repos'
+ }
+ Path.create(self.shared_pacman_dir['repos-dir'])
+
+ self.pacman_args = [
+ '--config', self.runtime_pacman_config_file.name,
+ '--noconfirm'
+ ]
+
+ self._write_runtime_config()
+
+ def use_default_location(self):
+ """
+ Setup pacman repository operations to store all data
+ in the default places
+ """
+ self.shared_pacman_dir['repos-dir'] = \
+ self.root_dir + '/etc/pacman.d'
+
+ def runtime_config(self):
+ """
+ pacman runtime configuration and environment
+ """
+ return {
+ 'pacman_args': self.pacman_args,
+ 'command_env': os.environ
+ }
+
+ def _add_repo_section(
+ self, config_parser, name, uri, repo_gpgcheck=None, pkg_gpgcheck=None
+ ):
+ config_parser.add_section(name)
+ if uri.startswith(os.sep) and os.path.exists(uri):
+ uri = 'file://{}'.format(uri)
+ config_parser.set(name, 'Server', uri)
+ config_parser.set(
+ name, 'SigLevel', '{0} {1}'.format(
+ 'Required' if pkg_gpgcheck else 'Never',
+ 'DatabaseRequired' if repo_gpgcheck else 'DatabaseNever'
+ )
+ )
+
+ def add_repo(
+ self, name, uri, repo_type=None,
+ prio=None, dist=None, components=None,
+ user=None, secret=None, credentials_file=None,
+ repo_gpgcheck=None, pkg_gpgcheck=None,
+ sourcetype=None
+ ):
+ """
+ Add pacman repository
+
+ :param str name: repository base file name
+ :param str uri: repository URI
+ :param repo_type: unused
+ :param int prio:
+ :param dist: unused
+ :param components: components to be used from this repository
+ :param user: unused
+ :param secret: unused
+ :param credentials_file: unused
+ :param bool repo_gpgcheck: enable database signature validation
+ :param bool pkg_gpgcheck: enable package signature validation
+ :param str sourcetype: unused
+ """
+ repo_file = '{0}/{1}.repo'.format(
+ self.shared_pacman_dir['repos-dir'], name
+ )
+ self.repo_names.append(name + '.repo')
+ repo_config = ConfigParser()
+ repo_config.optionxform = str
+ if not components:
+ self._add_repo_section(
+ repo_config, name, uri, repo_gpgcheck,
+ pkg_gpgcheck
+ )
+ else:
+ for component in components.split():
+ self._add_repo_section(
+ repo_config, component, uri, repo_gpgcheck,
+ pkg_gpgcheck
+ )
+
+ with open(repo_file, 'w') as config:
+ repo_config.write(config)
+
+ def import_trusted_keys(self, signing_keys):
+ """
+ zypper runtime configuration and environment
+ """
+ for signing_key in signing_keys:
+ if os.path.exists(signing_key):
+ Command.run(
+ ['pacman-key', '--recv-keys', signing_key]
+ )
+ else:
+ Command.run(
+ ['pacman-key', '--add', signing_key]
+ )
+
+ def delete_repo(self, name):
+ """
+ Delete pacman repository
+
+ :param str name: repository name
+ """
+ Path.wipe(
+ '{0}/{1}.repo'.format(self.shared_pacman_dir['repos-dir'], name)
+ )
+
+ def delete_all_repos(self):
+ """
+ Delete all pacman repositories
+ """
+ Path.wipe(self.shared_pacman_dir['repos-dir'])
+ Path.create(self.shared_pacman_dir['repos-dir'])
+
+ def delete_repo_cache(self, name):
+ """
+ Delete pacman repository cache
+
+ The method deletes these directories to cleanup the
+ cache information
+
+ :param str name: repository name
+ """
+ Path.wipe(self.shared_pacman_dir['cache-dir'])
+
+ def setup_package_database_configuration(self):
+ """
+ Creates the folder that will contain the package manager database
+ """
+ Path.create(os.sep.join([self.root_dir, 'var/lib/pacman']))
+
+ def cleanup_unused_repos(self):
+ """
+ Delete unused pacman repositories
+
+ Repository configurations which are not used for this build
+ must be removed otherwise they are taken into account for
+ the package installations
+ """
+ repos_dir = self.shared_pacman_dir['repos-dir']
+ repo_files = list(os.walk(repos_dir))[0][2]
+ for repo_file in repo_files:
+ if repo_file not in self.repo_names:
+ Path.wipe(repos_dir + '/' + repo_file)
+
+ def _write_runtime_config(self):
+ runtime_pacman_config = ConfigParser()
+ runtime_pacman_config.optionxform = str
+ runtime_pacman_config.add_section('options')
+
+ runtime_pacman_config.set(
+ 'options', 'Architecture', 'auto'
+ )
+ runtime_pacman_config.set(
+ 'options', 'CacheDir', self.shared_pacman_dir['cache-dir']
+ )
+
+ if self.check_signatures:
+ runtime_pacman_config.set(
+ 'options', 'SigLevel', 'Required DatabaseRequired'
+ )
+ runtime_pacman_config.set(
+ 'options', 'LocalFileSigLevel', 'Required DatabaseRequired'
+ )
+ else:
+ runtime_pacman_config.set(
+ 'options', 'SigLevel', 'Never DatabaseNever'
+ )
+ runtime_pacman_config.set(
+ 'options', 'LocalFileSigLevel', 'Never DatabaseNever'
+ )
+
+ runtime_pacman_config.set(
+ 'options', 'Include', '{0}/*.repo'.format(
+ self.shared_pacman_dir['repos-dir']
+ )
+ )
+
+ with open(self.runtime_pacman_config_file.name, 'w') as config:
+ runtime_pacman_config.write(config)
diff --git a/kiwi/schema/kiwi.rnc b/kiwi/schema/kiwi.rnc
index df57ccd9..de37825f 100644
--- a/kiwi/schema/kiwi.rnc
+++ b/kiwi/schema/kiwi.rnc
@@ -793,7 +793,7 @@ div {
#
div {
k.packagemanager.content =
- "apt-get" | "zypper" | "yum" | "dnf"
+ "apt-get" | "zypper" | "yum" | "dnf" | "pacman"
k.packagemanager.attlist = empty
k.packagemanager =
## Name of the Package Manager
diff --git a/kiwi/schema/kiwi.rng b/kiwi/schema/kiwi.rng
index e4db2d7a..39c9adfd 100644
--- a/kiwi/schema/kiwi.rng
+++ b/kiwi/schema/kiwi.rng
@@ -1228,6 +1228,7 @@ the device is looked up in /dev/disk/by-* and /dev/mapper/*
zypper
yum
dnf
+ pacman
diff --git a/kiwi/system/setup.py b/kiwi/system/setup.py
index f3b0d3ac..50c5a4da 100644
--- a/kiwi/system/setup.py
+++ b/kiwi/system/setup.py
@@ -499,6 +499,9 @@ class SystemSetup:
elif packager == 'dpkg':
self._export_deb_package_list(filename)
return filename
+ elif packager == 'pacman':
+ self._export_pacman_package_list(filename)
+ return filename
def export_package_verification(self, target_dir):
"""
@@ -1016,6 +1019,19 @@ class SystemSetup:
)
packages.write(os.linesep)
+ def _export_pacman_package_list(self, filename):
+ log.info('Export pacman packages metadata')
+ query_call = Command.run(['pacman', '-Qe'])
+ with open(filename, 'w') as packages:
+ for line in query_call.output.splitlines():
+ package, _, version_release = line.partition(' ')
+ version, _, release = version_release.partition('-')
+ packages.writelines([
+ '{0}|None|{1}|{2}|None|None|None{3}'.format(
+ package, version, release, os.linesep
+ )
+ ])
+
def _export_rpm_package_verification(self, filename):
log.info('Export rpm verification metadata')
dbpath_option = [
diff --git a/package/python-kiwi-pkgbuild-template b/package/python-kiwi-pkgbuild-template
new file mode 100644
index 00000000..a5176755
--- /dev/null
+++ b/package/python-kiwi-pkgbuild-template
@@ -0,0 +1,69 @@
+# Maintainer: Marcus Schaefer
+# Maintainer: David Cassany
+
+pkgname=('python-kiwi' 'kiwi-man-pages' 'dracut-kiwi-lib' 'dracut-kiwi-oem-repart' 'dracut-kiwi-oem-dump' 'dracut-kiwi-live' 'dracut-kiwi-overlay')
+arch=(x86_64)
+pkgver=%%VERSION
+pkgrel=0
+pkgdesc="KIWI - Appliance Builder Next Generation"
+url="https://github.com/SUSE/kiwi/tarball/master"
+license=('GPL3')
+makedepends=(python-setuptools gcc shadow grep)
+provides=(kiwi-ng kiwi)
+source=("${pkgname}.tar.gz")
+changelog="${pkgname}.changes"
+md5sums=('%%MD5SUM')
+
+
+build() {
+ cd kiwi-${pkgver}
+ python setup.py build
+}
+
+package_python-kiwi(){
+ depends=(python-docopt python-future python-lxml python-requests python-setuptools python-six python-pyxattr python-yaml grub qemu squashfs-tools gptfdisk pacman e2fsprogs xfsprogs btrfs-progs libisoburn lvm2 mtools parted multipath-tools rsync tar shadow kiwi-man-pages)
+ cd kiwi-${pkgver}
+ python setup.py install --root="${pkgdir}/" --optimize=1 --skip-build
+ ln -sr "${pkgdir}/usr/bin/kiwi-ng" "${pkgdir}/usr/bin/kiwi"
+ install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
+}
+
+package_kiwi-man-pages(){
+ cd kiwi-${pkgver}
+ make buildroot="${pkgdir}/" docdir="/usr/share/doc/${pkgname}" install_package_docs
+}
+
+package_dracut-kiwi-lib(){
+ depends=(cryptsetup btrfs-progs gptfdisk coreutils e2fsprogs grep lvm2 mdadm parted util-linux xfsprogs dialog curl xz device-mapper dracut)
+ cd kiwi-${pkgver}
+ install -d -m 755 ${pkgdir}/usr/lib/dracut/modules.d/99kiwi-lib
+ cp -a dracut/modules.d/99kiwi-lib ${pkgdir}/usr/lib/dracut/modules.d/
+}
+
+package_dracut-kiwi-oem-repart(){
+ depends=(dracut-kiwi-lib)
+ cd kiwi-${pkgver}
+ install -d -m 755 ${pkgdir}/usr/lib/dracut/modules.d/90kiwi-repart
+ cp -a dracut/modules.d/90kiwi-repart ${pkgdir}/usr/lib/dracut/modules.d/
+}
+
+package_dracut-kiwi-oem-dump(){
+ depends=(dracut-kiwi-lib multipath-tools)
+ cd kiwi-${pkgver}
+ install -d -m 755 ${pkgdir}/usr/lib/dracut/modules.d/90kiwi-dump
+ cp -a dracut/modules.d/90kiwi-dump ${pkgdir}/usr/lib/dracut/modules.d/
+}
+
+package_dracut-kiwi-live(){
+ depends=(dracut dialog xfsprogs e2fsprogs util-linux device-mapper libisoburn parted)
+ cd kiwi-${pkgver}
+ install -d -m 755 ${pkgdir}/usr/lib/dracut/modules.d/90kiwi-live
+ cp -a dracut/modules.d/90kiwi-live ${pkgdir}/usr/lib/dracut/modules.d/
+}
+
+package_dracut-kiwi-overlay(){
+ depends=(dracut util-linux)
+ cd kiwi-${pkgver}
+ install -d -m 755 ${pkgdir}/usr/lib/dracut/modules.d/90kiwi-overlay
+ cp -a dracut/modules.d/90kiwi-overlay ${pkgdir}/usr/lib/dracut/modules.d/
+}
diff --git a/test/unit/iso_tools/base_test.py b/test/unit/iso_tools/base_test.py
index dabe0283..d70057a8 100644
--- a/test/unit/iso_tools/base_test.py
+++ b/test/unit/iso_tools/base_test.py
@@ -63,6 +63,10 @@ class TestIsoToolsBase:
'root_dir/usr/share/syslinux/isolinux.bin',
'root_dir/image/loader/'
),
+ call(
+ 'root_dir/usr/lib/syslinux/bios/isolinux.bin',
+ 'root_dir/image/loader/'
+ ),
call(
'root_dir/usr/lib/syslinux/modules/bios/isolinux.bin',
'root_dir/image/loader/'
@@ -75,6 +79,10 @@ class TestIsoToolsBase:
'root_dir/usr/share/syslinux/ldlinux.c32',
'root_dir/image/loader/'
),
+ call(
+ 'root_dir/usr/lib/syslinux/bios/ldlinux.c32',
+ 'root_dir/image/loader/'
+ ),
call(
'root_dir/usr/lib/syslinux/modules/bios/ldlinux.c32',
'root_dir/image/loader/'
@@ -87,6 +95,10 @@ class TestIsoToolsBase:
'root_dir/usr/share/syslinux/libcom32.c32',
'root_dir/image/loader/'
),
+ call(
+ 'root_dir/usr/lib/syslinux/bios/libcom32.c32',
+ 'root_dir/image/loader/'
+ ),
call(
'root_dir/usr/lib/syslinux/modules/bios/libcom32.c32',
'root_dir/image/loader/'
@@ -99,6 +111,10 @@ class TestIsoToolsBase:
'root_dir/usr/share/syslinux/libutil.c32',
'root_dir/image/loader/'
),
+ call(
+ 'root_dir/usr/lib/syslinux/bios/libutil.c32',
+ 'root_dir/image/loader/'
+ ),
call(
'root_dir/usr/lib/syslinux/modules/bios/libutil.c32',
'root_dir/image/loader/'
@@ -111,6 +127,10 @@ class TestIsoToolsBase:
'root_dir/usr/share/syslinux/gfxboot.c32',
'root_dir/image/loader/'
),
+ call(
+ 'root_dir/usr/lib/syslinux/bios/gfxboot.c32',
+ 'root_dir/image/loader/'
+ ),
call(
'root_dir/usr/lib/syslinux/modules/bios/gfxboot.c32',
'root_dir/image/loader/'
@@ -123,6 +143,10 @@ class TestIsoToolsBase:
'root_dir/usr/share/syslinux/gfxboot.com',
'root_dir/image/loader/'
),
+ call(
+ 'root_dir/usr/lib/syslinux/bios/gfxboot.com',
+ 'root_dir/image/loader/'
+ ),
call(
'root_dir/usr/lib/syslinux/modules/bios/gfxboot.com',
'root_dir/image/loader/'
@@ -135,6 +159,10 @@ class TestIsoToolsBase:
'root_dir/usr/share/syslinux/menu.c32',
'root_dir/image/loader/'
),
+ call(
+ 'root_dir/usr/lib/syslinux/bios/menu.c32',
+ 'root_dir/image/loader/'
+ ),
call(
'root_dir/usr/lib/syslinux/modules/bios/menu.c32',
'root_dir/image/loader/'
@@ -147,6 +175,10 @@ class TestIsoToolsBase:
'root_dir/usr/share/syslinux/chain.c32',
'root_dir/image/loader/'
),
+ call(
+ 'root_dir/usr/lib/syslinux/bios/chain.c32',
+ 'root_dir/image/loader/'
+ ),
call(
'root_dir/usr/lib/syslinux/modules/bios/chain.c32',
'root_dir/image/loader/'
@@ -159,6 +191,10 @@ class TestIsoToolsBase:
'root_dir/usr/share/syslinux/mboot.c32',
'root_dir/image/loader/'
),
+ call(
+ 'root_dir/usr/lib/syslinux/bios/mboot.c32',
+ 'root_dir/image/loader/'
+ ),
call(
'root_dir/usr/lib/syslinux/modules/bios/mboot.c32',
'root_dir/image/loader/'
diff --git a/test/unit/iso_tools/xorriso_test.py b/test/unit/iso_tools/xorriso_test.py
index a2df0762..95035165 100644
--- a/test/unit/iso_tools/xorriso_test.py
+++ b/test/unit/iso_tools/xorriso_test.py
@@ -37,6 +37,7 @@ class TestIsoToolsXorrIso:
mock_which.assert_called_once_with(
'isohdpfx.bin', [
'/usr/share/syslinux',
+ '/usr/lib/syslinux/bios',
'/usr/lib/syslinux/modules/bios',
'/usr/lib/ISOLINUX'
]
diff --git a/test/unit/package_manager/init_test.py b/test/unit/package_manager/init_test.py
index 5f3ebbc7..f3d852f0 100644
--- a/test/unit/package_manager/init_test.py
+++ b/test/unit/package_manager/init_test.py
@@ -36,3 +36,9 @@ class TestPackageManager:
repository = Mock()
PackageManager(repository, 'apt-get')
mock_manager.assert_called_once_with(repository, None)
+
+ @patch('kiwi.package_manager.PackageManagerPacman')
+ def test_manager_pacman(self, mock_manager):
+ repository = Mock()
+ PackageManager(repository, 'pacman')
+ mock_manager.assert_called_once_with(repository, None)
diff --git a/test/unit/package_manager/pacman_test.py b/test/unit/package_manager/pacman_test.py
new file mode 100644
index 00000000..9aceff41
--- /dev/null
+++ b/test/unit/package_manager/pacman_test.py
@@ -0,0 +1,137 @@
+from mock import patch, call
+from pytest import raises
+import mock
+
+from kiwi.package_manager.pacman import PackageManagerPacman
+
+from kiwi.exceptions import KiwiRequestError
+
+
+class TestPackageManagerPacman:
+ def setup(self):
+ repository = mock.Mock()
+ repository.root_dir = '/root-dir'
+
+ repository.runtime_config = mock.Mock(
+ return_value={
+ 'pacman_args': [
+ '--config', '/root-dir/pacman.conf', '-y', '--noconfirm'
+ ],
+ 'command_env': ['env']
+ }
+ )
+ self.manager = PackageManagerPacman(repository)
+
+ def test_request_package(self):
+ self.manager.request_package('name')
+ assert self.manager.package_requests == ['name']
+
+ def test_request_collection(self):
+ self.manager.request_collection('name')
+ assert self.manager.collection_requests == []
+ assert self.manager.package_requests == ['name']
+
+ def test_request_product(self):
+ self.manager.request_product('name')
+ assert self.manager.product_requests == []
+
+ def test_request_package_exclusion(self):
+ self.manager.request_package_exclusion('name')
+ assert self.manager.exclude_requests == ['name']
+
+ @patch('kiwi.command.Command.call')
+ @patch('kiwi.command.Command.run')
+ def test_process_install_requests_bootstrap(self, mock_run, mock_call):
+ self.manager.request_package('vim')
+ self.manager.request_collection('collection')
+ self.manager.process_install_requests_bootstrap()
+ mock_run.call_args_list == [
+ call(['rm', '-r', '-f', '/root-dir/var/run']),
+ call([
+ 'pacman', '--config', '/root-dir/pacman.conf', '-y',
+ '--noconfirm', '--needed', '--root', '/root-dir', '-Sy'
+ ])
+ ]
+ mock_call.assert_called_once_with(
+ [
+ 'bash', '-c',
+ 'pacman --config /root-dir/pacman.conf -y --noconfirm '
+ '--root /root-dir -S --needed --overwrite /root-dir/var/run '
+ 'vim collection'
+ ], ['env']
+ )
+
+ @patch('kiwi.command.Command.call')
+ def test_process_install_requests(self, mock_call):
+ self.manager.request_package('vim')
+ self.manager.request_collection('collection')
+ self.manager.request_package_exclusion('skipme')
+ self.manager.process_install_requests()
+ mock_call.assert_called_once_with(
+ [
+ 'bash', '-c',
+ 'chroot /root-dir pacman --config /pacman.conf -y '
+ '--noconfirm -S --needed vim collection'
+ ], ['env']
+ )
+
+ @patch('kiwi.command.Command.call')
+ @patch('kiwi.command.Command.run')
+ def test_process_delete_requests_force(self, mock_run, mock_call):
+ self.manager.request_package('vim')
+ self.manager.process_delete_requests(True)
+ mock_call.assert_called_once_with(
+ [
+ 'chroot', '/root-dir', 'pacman', '--config', '/pacman.conf',
+ '-y', '--noconfirm', '-Rdd', 'vim'
+ ], ['env']
+ )
+
+ @patch('kiwi.command.Command.call')
+ @patch('kiwi.command.Command.run')
+ def test_process_delete_requests_no_force(self, mock_run, mock_call):
+ self.manager.request_package('vim')
+ self.manager.process_delete_requests()
+ mock_call.assert_called_once_with(
+ [
+ 'chroot', '/root-dir', 'pacman', '--config', '/pacman.conf',
+ '-y', '--noconfirm', '-Rs', 'vim'
+ ], ['env']
+ )
+
+ @patch('kiwi.command.Command.run')
+ @patch('kiwi.command.Command.call')
+ def test_process_delete_requests_package_missing(
+ self, mock_call, mock_run
+ ):
+ mock_run.side_effect = Exception
+ self.manager.request_package('vim')
+ with raises(KiwiRequestError):
+ self.manager.process_delete_requests()
+ mock_run.assert_called_once_with(
+ ['chroot', '/root-dir', 'pacman', '-Qi', 'vim']
+ )
+
+ def test_process_only_required(self):
+ self.manager.process_only_required()
+ assert self.manager.custom_args == []
+
+ def test_process_plus_recommended(self):
+ self.manager.process_plus_recommended()
+ assert self.manager.custom_args == []
+
+ @patch('kiwi.command.Command.call')
+ def test_update(self, mock_call):
+ self.manager.update()
+ mock_call.assert_called_once_with(
+ [
+ 'chroot', '/root-dir', 'pacman', '--config', '/pacman.conf',
+ '-y', '--noconfirm', '-Su'
+ ], ['env']
+ )
+
+ def test_match_package_installed(self):
+ assert self.manager.match_package_installed('foo', ' installing foo')
+
+ def test_match_package_deleted(self):
+ assert self.manager.match_package_deleted('foo', ' removing foo')
diff --git a/test/unit/repository/init_test.py b/test/unit/repository/init_test.py
index 343b404d..8299fa01 100644
--- a/test/unit/repository/init_test.py
+++ b/test/unit/repository/init_test.py
@@ -34,3 +34,9 @@ class TestRepository:
root_bind = mock.Mock()
Repository(root_bind, 'apt-get')
mock_manager.assert_called_once_with(root_bind, None)
+
+ @patch('kiwi.repository.RepositoryPacman')
+ def test_repository_pacman(self, mock_manager):
+ root_bind = mock.Mock()
+ Repository(root_bind, 'pacman')
+ mock_manager.assert_called_once_with(root_bind, None)
diff --git a/test/unit/repository/pacman_test.py b/test/unit/repository/pacman_test.py
new file mode 100644
index 00000000..83a1d3fe
--- /dev/null
+++ b/test/unit/repository/pacman_test.py
@@ -0,0 +1,191 @@
+
+from mock import (
+ patch, Mock, call, mock_open
+)
+
+import os
+
+from kiwi.repository.pacman import RepositoryPacman
+
+
+class TestRepositorPacman(object):
+ @patch('kiwi.repository.pacman.NamedTemporaryFile')
+ @patch('kiwi.repository.pacman.ConfigParser')
+ @patch('kiwi.repository.pacman.Path.create')
+ def setup(self, mock_path, mock_config, mock_temp):
+ runtime_pacman_config = Mock()
+ mock_config.return_value = runtime_pacman_config
+ tmpfile = Mock()
+ tmpfile.name = 'tmpfile'
+ mock_temp.return_value = tmpfile
+ root_bind = Mock()
+ root_bind.move_to_root = Mock(
+ return_value=['root-moved-arguments']
+ )
+ root_bind.root_dir = '../data'
+ root_bind.shared_location = '/shared-dir'
+ with patch('builtins.open', create=True):
+ self.repo = RepositoryPacman(root_bind)
+
+ assert runtime_pacman_config.set.call_args_list == [
+ call('options', 'Architecture', 'auto'),
+ call('options', 'CacheDir', '/shared-dir/pacman/cache'),
+ call('options', 'SigLevel', 'Never DatabaseNever'),
+ call('options', 'LocalFileSigLevel', 'Never DatabaseNever'),
+ call('options', 'Include', '/shared-dir/pacman/repos/*.repo')
+ ]
+
+ @patch('kiwi.repository.pacman.NamedTemporaryFile')
+ @patch('kiwi.repository.pacman.Path.create')
+ def test_post_init_with_custom_args(self, mock_path, mock_temp):
+ self.repo.post_init(['check_signatures'])
+ assert self.repo.custom_args == []
+ assert self.repo.check_signatures
+
+ def test_runtime_config(self):
+ assert self.repo.runtime_config()['pacman_args'] == \
+ self.repo.pacman_args
+ assert self.repo.runtime_config()['command_env'] == \
+ os.environ
+
+ @patch('kiwi.repository.pacman.ConfigParser')
+ @patch('os.path.exists')
+ def test_add_local_repo_with_components(
+ self, mock_exists, mock_config
+ ):
+ repo_config = Mock()
+ mock_config.return_value = repo_config
+ mock_exists.return_value = True
+
+ m_open = mock_open()
+ with patch('builtins.open', m_open, create=True):
+ self.repo.add_repo(
+ 'foo', '/some_uri', components='core extra',
+ repo_gpgcheck=False, pkg_gpgcheck=True
+ )
+ m_open.assert_called_once_with(
+ '/shared-dir/pacman/repos/foo.repo', 'w'
+ )
+
+ assert repo_config.add_section.call_args_list == [
+ call('core'), call('extra')
+ ]
+ assert repo_config.set.call_args_list == [
+ call('core', 'Server', 'file:///some_uri'),
+ call('core', 'SigLevel', 'Required DatabaseNever'),
+ call('extra', 'Server', 'file:///some_uri'),
+ call('extra', 'SigLevel', 'Required DatabaseNever'),
+ ]
+
+ @patch('kiwi.repository.pacman.ConfigParser')
+ @patch('os.path.exists')
+ def test_add_repo_with_components(
+ self, mock_exists, mock_config
+ ):
+ repo_config = Mock()
+ mock_config.return_value = repo_config
+ mock_exists.return_value = True
+
+ m_open = mock_open()
+ with patch('builtins.open', m_open, create=True):
+ self.repo.add_repo(
+ 'foo', 'some_uri', components='core extra',
+ repo_gpgcheck=False, pkg_gpgcheck=True
+ )
+ m_open.assert_called_once_with(
+ '/shared-dir/pacman/repos/foo.repo', 'w'
+ )
+
+ assert repo_config.add_section.call_args_list == [
+ call('core'), call('extra')
+ ]
+ assert repo_config.set.call_args_list == [
+ call('core', 'Server', 'some_uri'),
+ call('core', 'SigLevel', 'Required DatabaseNever'),
+ call('extra', 'Server', 'some_uri'),
+ call('extra', 'SigLevel', 'Required DatabaseNever'),
+ ]
+
+ @patch('kiwi.repository.pacman.ConfigParser')
+ @patch('os.path.exists')
+ def test_add_repo_without_components(
+ self, mock_exists, mock_config
+ ):
+ repo_config = Mock()
+ mock_config.return_value = repo_config
+ mock_exists.return_value = True
+
+ m_open = mock_open()
+ with patch('builtins.open', m_open, create=True):
+ self.repo.add_repo('foo', 'some_uri', pkg_gpgcheck=False)
+
+ m_open.assert_called_once_with(
+ '/shared-dir/pacman/repos/foo.repo', 'w'
+ )
+
+ assert repo_config.add_section.call_args_list == [
+ call('foo')
+ ]
+ assert repo_config.set.call_args_list == [
+ call('foo', 'Server', 'some_uri'),
+ call('foo', 'SigLevel', 'Never DatabaseNever'),
+ ]
+
+ @patch('kiwi.repository.pacman.Path.create')
+ def test_setup_package_database_configuration(self, mock_path_create):
+ self.repo.setup_package_database_configuration()
+ mock_path_create.assert_called_once_with(
+ '../data/var/lib/pacman'
+ )
+
+ @patch('kiwi.repository.pacman.os.path.exists')
+ @patch('kiwi.repository.pacman.Command.run')
+ def test_import_trusted_keys(self, mock_cmd, mock_exists):
+ exists = [True, False]
+ mock_exists.side_effect = lambda x: exists.pop()
+ signing_keys = ['key-file-a.asc', 'key-file-b_ID']
+ self.repo.import_trusted_keys(signing_keys)
+ assert mock_cmd.call_args_list == [
+ call(['pacman-key', '--add', 'key-file-a.asc']),
+ call(['pacman-key', '--recv-keys', 'key-file-b_ID'])
+ ]
+
+ @patch('kiwi.repository.pacman.Path')
+ def test_delete_repo(self, mock_path):
+ self.repo.delete_repo('foo')
+ mock_path.wipe.assert_called_once_with(
+ '/shared-dir/pacman/repos/foo.repo'
+ )
+
+ @patch('kiwi.repository.pacman.Path')
+ def test_delete_all_repos(self, mock_path):
+ self.repo.delete_all_repos()
+ mock_path.wipe.assert_called_once_with('/shared-dir/pacman/repos')
+ mock_path.create.assert_called_once_with('/shared-dir/pacman/repos')
+
+ @patch('kiwi.repository.pacman.Path.wipe')
+ @patch('kiwi.repository.pacman.os.walk')
+ def test_cleanup_unused_repos(self, mock_os_walk, mock_wipe):
+ mock_os_walk.return_value = [(
+ '/shared-dir/pacman/repos',
+ [],
+ ['foo.repo', 'bar.repo']
+ )]
+ self.repo.cleanup_unused_repos()
+ mock_os_walk.assert_called_once_with('/shared-dir/pacman/repos')
+ assert mock_wipe.call_args_list == [
+ call('/shared-dir/pacman/repos/foo.repo'),
+ call('/shared-dir/pacman/repos/bar.repo')
+ ]
+
+ def test_use_default_location(self):
+ self.repo.use_default_location()
+ assert self.repo.shared_pacman_dir['cache-dir'] == \
+ '/shared-dir/pacman/cache'
+ assert self.repo.shared_pacman_dir['repos-dir'] == \
+ '../data/etc/pacman.d'
+
+ @patch('kiwi.repository.pacman.Path')
+ def test_delete_repo_cache(self, mock_path):
+ self.repo.delete_repo_cache('foo')
+ mock_path.wipe.assert_called_once_with('/shared-dir/pacman/cache')
diff --git a/test/unit/system/setup_test.py b/test/unit/system/setup_test.py
index 5fb94ffd..ab5f4b02 100644
--- a/test/unit/system/setup_test.py
+++ b/test/unit/system/setup_test.py
@@ -924,6 +924,24 @@ class TestSystemSetup:
'-f', '${Package}|None|${Version}|None|${Architecture}|None|None\\n'
])
+ @patch('kiwi.system.setup.Command.run')
+ def test_export_package_list_pacman(self, mock_command):
+ command = Mock()
+ command.output = 'packages_data'
+ mock_command.return_value = command
+ self.xml_state.get_package_manager = Mock(
+ return_value='pacman'
+ )
+
+ with patch('builtins.open') as m_open:
+ result = self.setup.export_package_list('target_dir')
+ m_open.assert_called_once_with(
+ 'target_dir/some-image.x86_64-1.2.3.packages', 'w'
+ )
+
+ assert result == 'target_dir/some-image.x86_64-1.2.3.packages'
+ mock_command.assert_called_once_with(['pacman', '-Qe'])
+
@patch('kiwi.system.setup.Command.run')
@patch('kiwi.system.setup.RpmDataBase')
@patch('kiwi.system.setup.MountManager')