diff --git a/build-tests/arm/ubuntu/test-image-rpi/appliance.kiwi b/build-tests/arm/ubuntu/test-image-rpi/appliance.kiwi index 1c7a0e5072..9ddf8cbcf0 100644 --- a/build-tests/arm/ubuntu/test-image-rpi/appliance.kiwi +++ b/build-tests/arm/ubuntu/test-image-rpi/appliance.kiwi @@ -30,7 +30,7 @@ - + diff --git a/build-tests/x86/debian/test-image-live-disk/appliance.kiwi b/build-tests/x86/debian/test-image-live-disk/appliance.kiwi index da1162d59b..a99a3b7ed2 100644 --- a/build-tests/x86/debian/test-image-live-disk/appliance.kiwi +++ b/build-tests/x86/debian/test-image-live-disk/appliance.kiwi @@ -51,7 +51,7 @@ - + diff --git a/build-tests/x86/ubuntu/test-image-docker/appliance.kiwi b/build-tests/x86/ubuntu/test-image-docker/appliance.kiwi index fbd1f14c6f..726c12a075 100644 --- a/build-tests/x86/ubuntu/test-image-docker/appliance.kiwi +++ b/build-tests/x86/ubuntu/test-image-docker/appliance.kiwi @@ -23,7 +23,7 @@ - + diff --git a/build-tests/x86/ubuntu/test-image-live-disk/appliance.kiwi b/build-tests/x86/ubuntu/test-image-live-disk/appliance.kiwi index 48f9e63563..f897875403 100644 --- a/build-tests/x86/ubuntu/test-image-live-disk/appliance.kiwi +++ b/build-tests/x86/ubuntu/test-image-live-disk/appliance.kiwi @@ -51,7 +51,7 @@ - + diff --git a/kiwi/repository/apt.py b/kiwi/repository/apt.py index f981d53ffa..529f9d00a4 100644 --- a/kiwi/repository/apt.py +++ b/kiwi/repository/apt.py @@ -138,7 +138,8 @@ def add_repo( prio: int = None, dist: str = None, components: str = None, user: str = None, secret: str = None, credentials_file: str = None, repo_gpgcheck: bool = None, pkg_gpgcheck: bool = None, - sourcetype: str = None, customization_script: str = None + sourcetype: str = None, customization_script: str = None, + architectures: str = None ) -> None: """ Add apt_get repository @@ -157,6 +158,8 @@ def add_repo( :param str sourcetype: unused :param str customization_script: custom script called after the repo file was created + :param str architectures: + identifies which architectures are supported by this repository """ sources_file = '/'.join( [self.shared_apt_get_dir['sources-dir'], name + '.sources'] @@ -175,6 +178,10 @@ def add_repo( with open(sources_file, 'w') as repo: repo_details = 'Types: deb' + os.linesep repo_details += 'URIs: ' + uri + os.linesep + if architectures: + repo_details += 'Architectures: {}{}'.format( + architectures.replace(',', ' '), os.linesep + ) if not dist: # create a debian flat repository setup. We consider the # repository metadata to exist on the toplevel of the diff --git a/kiwi/repository/base.py b/kiwi/repository/base.py index 3af266aee1..22f03ed1b6 100644 --- a/kiwi/repository/base.py +++ b/kiwi/repository/base.py @@ -77,7 +77,7 @@ def add_repo( self, name: str, uri: str, repo_type: str, prio: int, dist: str, components: str, user: str, secret: str, credentials_file: str, repo_gpgcheck: bool, pkg_gpgcheck: bool, sourcetype: str, - customization_script: str = None + customization_script: str = None, architectures: str = None ) -> None: """ Add repository @@ -97,6 +97,7 @@ def add_repo( :param bool pkg_gpgcheck: unused :param str sourcetype: unused :param str customization_script: unused + :param str architectures: unused """ raise NotImplementedError diff --git a/kiwi/repository/dnf4.py b/kiwi/repository/dnf4.py index 542d8e1092..5136ccaff4 100644 --- a/kiwi/repository/dnf4.py +++ b/kiwi/repository/dnf4.py @@ -192,7 +192,8 @@ def add_repo( prio: int = None, dist: str = None, components: str = None, user: str = None, secret: str = None, credentials_file: str = None, repo_gpgcheck: bool = False, pkg_gpgcheck: bool = False, - sourcetype: str = None, customization_script: str = None + sourcetype: str = None, customization_script: str = None, + architectures: str = None ) -> None: """ Add dnf repository @@ -212,6 +213,7 @@ def add_repo( source type, one of 'baseurl', 'metalink' or 'mirrorlist' :param str customization_script: custom script called after the repo file was created + :param str architectures: unused """ repo_file = self.shared_dnf_dir['reposd-dir'] + '/' + name + '.repo' self.repo_names.append(name + '.repo') diff --git a/kiwi/repository/dnf5.py b/kiwi/repository/dnf5.py index a02b6f5554..45b8cef3db 100644 --- a/kiwi/repository/dnf5.py +++ b/kiwi/repository/dnf5.py @@ -192,7 +192,8 @@ def add_repo( prio: int = None, dist: str = None, components: str = None, user: str = None, secret: str = None, credentials_file: str = None, repo_gpgcheck: bool = False, pkg_gpgcheck: bool = False, - sourcetype: str = None, customization_script: str = None + sourcetype: str = None, customization_script: str = None, + architectures: str = None ) -> None: """ Add dnf repository @@ -212,6 +213,7 @@ def add_repo( source type, one of 'baseurl', 'metalink' or 'mirrorlist' :param str customization_script: custom script called after the repo file was created + :param str architectures: unused """ repo_file = self.shared_dnf_dir['reposd-dir'] + '/' + name + '.repo' self.repo_names.append(name + '.repo') diff --git a/kiwi/repository/pacman.py b/kiwi/repository/pacman.py index 85eb4bed3b..60946ab757 100644 --- a/kiwi/repository/pacman.py +++ b/kiwi/repository/pacman.py @@ -115,7 +115,8 @@ def add_repo( prio: int = None, dist: str = None, components: str = None, user: str = None, secret: str = None, credentials_file: str = None, repo_gpgcheck: bool = False, pkg_gpgcheck: bool = False, - sourcetype: str = None, customization_script: str = None + sourcetype: str = None, customization_script: str = None, + architectures: str = None ) -> None: """ Add pacman repository @@ -134,6 +135,7 @@ def add_repo( :param str sourcetype: unused :param str customization_script: custom script called after the repo file was created + :param str architectures: unused """ repo_file = '{0}/{1}.repo'.format( self.shared_pacman_dir['repos-dir'], name diff --git a/kiwi/repository/zypper.py b/kiwi/repository/zypper.py index 93a80b86c5..4fc4a117f5 100644 --- a/kiwi/repository/zypper.py +++ b/kiwi/repository/zypper.py @@ -252,7 +252,8 @@ def add_repo( prio: int = None, dist: str = None, components: str = None, user: str = None, secret: str = None, credentials_file: str = None, repo_gpgcheck: bool = False, pkg_gpgcheck: bool = False, - sourcetype: str = None, customization_script: str = None + sourcetype: str = None, customization_script: str = None, + architectures: str = None ) -> None: """ Add zypper repository @@ -271,6 +272,7 @@ def add_repo( :param str sourcetype: unused :param str customization_script: custom script called after the repo file was created + :param str architectures: unused """ if credentials_file: repo_secret = os.sep.join( diff --git a/kiwi/schema/kiwi.rnc b/kiwi/schema/kiwi.rnc index a6ddae0285..baec677ee9 100644 --- a/kiwi/schema/kiwi.rnc +++ b/kiwi/schema/kiwi.rnc @@ -36,7 +36,7 @@ volume-size-type = xsd:token {pattern = "(\d+|\d+M|\d+G|all)"} partition-size-type = xsd:token {pattern = "(\d+|\d+M|\d+G)"} vhd-tag-type = xsd:token {pattern = "[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}"} groups-list = xsd:token {pattern = "[a-zA-Z0-9_\-\.:]+(,[a-zA-Z0-9_\-\.:]+)*"} -arch-name = xsd:token {pattern = "(x86_64|i586|i686|ix86|aarch64|arm64|armv5el|armv5tel|armv6hl|armv6l|armv7hl|armv7l|ppc|ppc64|ppc64le|s390|s390x|riscv64)(,(x86_64|i586|i686|ix86|aarch64|arm64|armv5el|armv5tel|armv6hl|armv6l|armv7hl|armv7l|ppc|ppc64|ppc64le|s390|s390x|riscv64))*"} +arch-name = xsd:token {pattern = "(x86_64|i586|i686|ix86|aarch64|arm64|amd64|armv5el|armv5tel|armv6hl|armv6l|armv7hl|armv7l|ppc|ppc64|ppc64le|s390|s390x|riscv64)(,(x86_64|i586|i686|ix86|aarch64|arm64|amd64|armv5el|armv5tel|armv6hl|armv6l|armv7hl|armv7l|ppc|ppc64|ppc64le|s390|s390x|riscv64))*"} portnum-type = xsd:token {pattern = "(\d+|\d+/(udp|tcp))"} grub_console = xsd:token {pattern = "(none|console|gfxterm|serial|vga_text|mda_text|morse|spkmodem)( (none|console|serial|at_keyboard|usb_keyboard))*"} fs_attributes = xsd:token {pattern = "(no-copy-on-write|synchronous-updates)(,(no-copy-on-write|synchronous-updates))*"} @@ -1135,6 +1135,15 @@ div { attribute sourcetype { "baseurl" | "metalink" | "mirrorlist" } + k.repository.architectures.attribute = + ## Specifies for which architecture(s) this repository is + ## supposed to provide packages. Multiple architecture names + ## needs to be separated by a comma + attribute architectures { arch-name } + >> sch:pattern [ id = "architectures" is-a = "repo_type" + sch:param [ name = "attr" value = "architectures" ] + sch:param [ name = "types" value = "apt-deb" ] + ] k.repository.attlist = k.repository.type.attribute? & k.repository.profiles.attribute? & @@ -1152,7 +1161,8 @@ div { k.repository.package_gpgcheck.attribute? & k.repository.priority.attribute? & k.repository.password.attribute? & - k.repository.username.attribute? + k.repository.username.attribute? & + k.repository.architectures.attribute? k.repository = ## The Name of the Repository element repository { diff --git a/kiwi/schema/kiwi.rng b/kiwi/schema/kiwi.rng index b50bf6f790..ee4918e74b 100644 --- a/kiwi/schema/kiwi.rng +++ b/kiwi/schema/kiwi.rng @@ -83,7 +83,7 @@ - (x86_64|i586|i686|ix86|aarch64|arm64|armv5el|armv5tel|armv6hl|armv6l|armv7hl|armv7l|ppc|ppc64|ppc64le|s390|s390x|riscv64)(,(x86_64|i586|i686|ix86|aarch64|arm64|armv5el|armv5tel|armv6hl|armv6l|armv7hl|armv7l|ppc|ppc64|ppc64le|s390|s390x|riscv64))* + (x86_64|i586|i686|ix86|aarch64|arm64|amd64|armv5el|armv5tel|armv6hl|armv6l|armv7hl|armv7l|ppc|ppc64|ppc64le|s390|s390x|riscv64)(,(x86_64|i586|i686|ix86|aarch64|arm64|amd64|armv5el|armv5tel|armv6hl|armv6l|armv7hl|armv7l|ppc|ppc64|ppc64le|s390|s390x|riscv64))* @@ -1728,6 +1728,18 @@ be a simple repository url + + + Specifies for which architecture(s) this repository is +supposed to provide packages. Multiple architecture names +needs to be separated by a comma + + + + + + + @@ -1775,6 +1787,9 @@ be a simple repository url + + + diff --git a/kiwi/system/prepare.py b/kiwi/system/prepare.py index 50f7aa635b..7695adf601 100644 --- a/kiwi/system/prepare.py +++ b/kiwi/system/prepare.py @@ -158,6 +158,7 @@ def setup_repositories( for xml_repo in repository_sections: repo_type = xml_repo.get_type() repo_source = xml_repo.get_source().get_path() + repo_architectures = xml_repo.get_architectures() repo_user = xml_repo.get_username() repo_secret = xml_repo.get_password() repo_alias = xml_repo.get_alias() @@ -209,7 +210,8 @@ def setup_repositories( repo_type, repo_priority, repo_dist, repo_components, repo_user, repo_secret, uri.credentials_file_name(), repo_repository_gpgcheck, repo_package_gpgcheck, - repo_sourcetype, repo_customization_script + repo_sourcetype, repo_customization_script, + repo_architectures ) if clear_cache: repo.delete_repo_cache(repo_alias) diff --git a/kiwi/system/setup.py b/kiwi/system/setup.py index 6098fd3bb4..c43a9f75fc 100644 --- a/kiwi/system/setup.py +++ b/kiwi/system/setup.py @@ -149,6 +149,7 @@ def import_repositories_marked_as_imageinclude(self) -> None: for xml_repo in repository_sections: repo_type = xml_repo.get_type() repo_source = xml_repo.get_source().get_path() + repo_architectures = xml_repo.get_architectures() repo_user = xml_repo.get_username() repo_secret = xml_repo.get_password() repo_alias = xml_repo.get_alias() @@ -184,7 +185,8 @@ def import_repositories_marked_as_imageinclude(self) -> None: repo_type, repo_priority, repo_dist, repo_components, repo_user, repo_secret, uri.credentials_file_name(), repo_repository_gpgcheck, repo_package_gpgcheck, - repo_sourcetype, repo_customization_script + repo_sourcetype, repo_customization_script, + repo_architectures ) def import_cdroot_files(self, target_dir: str) -> None: diff --git a/kiwi/xml_parse.py b/kiwi/xml_parse.py index d6168ed838..61e9baac3e 100644 --- a/kiwi/xml_parse.py +++ b/kiwi/xml_parse.py @@ -2442,7 +2442,7 @@ class repository(k_source): """The Name of the Repository""" subclass = None superclass = k_source - def __init__(self, source=None, type_=None, profiles=None, arch=None, alias=None, sourcetype=None, components=None, distribution=None, imageinclude=None, imageonly=None, repository_gpgcheck=None, customize=None, package_gpgcheck=None, priority=None, password=None, username=None): + def __init__(self, source=None, type_=None, profiles=None, arch=None, alias=None, sourcetype=None, components=None, distribution=None, imageinclude=None, imageonly=None, repository_gpgcheck=None, customize=None, package_gpgcheck=None, priority=None, password=None, username=None, architectures=None): self.original_tagname_ = None super(repository, self).__init__(source, ) self.type_ = _cast(None, type_) @@ -2460,6 +2460,7 @@ def __init__(self, source=None, type_=None, profiles=None, arch=None, alias=None self.priority = _cast(int, priority) self.password = _cast(None, password) self.username = _cast(None, username) + self.architectures = _cast(None, architectures) def factory(*args_, **kwargs_): if CurrentSubclassModule_ is not None: subclass = getSubclassFromModule_( @@ -2501,6 +2502,8 @@ def get_password(self): return self.password def set_password(self, password): self.password = password def get_username(self): return self.username def set_username(self, username): self.username = username + def get_architectures(self): return self.architectures + def set_architectures(self, architectures): self.architectures = architectures def validate_arch_name(self, value): # Validate type arch-name, a restriction on xs:token. if value is not None and Validate_simpletypes_: @@ -2590,6 +2593,9 @@ def exportAttributes(self, outfile, level, already_processed, namespaceprefix_=' if self.username is not None and 'username' not in already_processed: already_processed.add('username') outfile.write(' username=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.username), input_name='username')), )) + if self.architectures is not None and 'architectures' not in already_processed: + already_processed.add('architectures') + outfile.write(' architectures=%s' % (quote_attrib(self.architectures), )) def exportChildren(self, outfile, level, namespaceprefix_='', name_='repository', fromsubclass_=False, pretty_print=True): super(repository, self).exportChildren(outfile, level, namespaceprefix_, name_, True, pretty_print=pretty_print) def build(self, node): @@ -2689,6 +2695,12 @@ def buildAttributes(self, node, attrs, already_processed): if value is not None and 'username' not in already_processed: already_processed.add('username') self.username = value + value = find_attr_value_('architectures', node) + if value is not None and 'architectures' not in already_processed: + already_processed.add('architectures') + self.architectures = value + self.architectures = ' '.join(self.architectures.split()) + self.validate_arch_name(self.architectures) # validate type arch-name super(repository, self).buildAttributes(node, attrs, already_processed) def buildChildren(self, child_, node, nodeName_, fromsubclass_=False): super(repository, self).buildChildren(child_, node, nodeName_, True) diff --git a/test/unit/repository/apt_test.py b/test/unit/repository/apt_test.py index 7f9bc49103..5928c4549e 100644 --- a/test/unit/repository/apt_test.py +++ b/test/unit/repository/apt_test.py @@ -151,11 +151,13 @@ def test_add_repo_distribution(self, mock_exists): mock_open.return_value = MagicMock(spec=io.IOBase) file_handle = mock_open.return_value.__enter__.return_value self.repo.add_repo( - 'foo', 'kiwi_iso_mount/uri', 'deb', None, 'xenial', 'a b' + 'foo', 'kiwi_iso_mount/uri', 'deb', None, 'xenial', 'a b', + architectures='amd64,arm64' ) file_handle.write.assert_called_once_with( 'Types: deb\n' 'URIs: file:/kiwi_iso_mount/uri\n' + 'Architectures: amd64 arm64\n' 'Suites: xenial\n' 'Components: a b\n' ) diff --git a/test/unit/system/prepare_test.py b/test/unit/system/prepare_test.py index d1aa572c19..48dc717038 100644 --- a/test/unit/system/prepare_test.py +++ b/test/unit/system/prepare_test.py @@ -287,12 +287,12 @@ def test_setup_repositories( call( 'uri-alias', 'uri', None, 42, None, None, None, None, 'credentials-file', None, None, - 'baseurl', None + 'baseurl', None, None ), call( 'uri-alias', 'uri', 'rpm-md', None, None, None, None, None, 'credentials-file', None, None, - None, '../data/script' + None, '../data/script', None ) ] assert repo.delete_repo_cache.call_args_list == [ diff --git a/test/unit/system/setup_test.py b/test/unit/system/setup_test.py index 4fe2d43e07..ab247f69a6 100644 --- a/test/unit/system/setup_test.py +++ b/test/unit/system/setup_test.py @@ -1678,7 +1678,7 @@ def test_import_repositories_marked_as_imageinclude( self.setup_with_real_xml.import_repositories_marked_as_imageinclude() assert repo.add_repo.call_args_list[0] == call( 'uri-alias', 'uri', 'rpm-md', None, None, None, None, None, - 'kiwiRepoCredentials', None, None, None, '../data/script' + 'kiwiRepoCredentials', None, None, None, '../data/script', None ) @patch('os.path.exists')