Repository: archlinux/archinstall

Issue:
Default linux kernel is hardcoded in systemd-boot entry
## Summary

Pull request #315 added the ability to select a different kernel other than the default, but it seems to result in an unbootable system, since the code that generates the systemd-boot entry is hardcoded to load the default kernel:

https://github.com/archlinux/archinstall/blob/7b1096bfa6a5d042b38341b68f857dbe4f150bb3/archinstall/lib/installer.py#L471

## How to reproduce

I wrote a JSON config for archinstall with the following key in it:

    "kernels": ["linux-zen"],

After installing and rebooting, systemd-boot showed no entries available to boot. I dropped to an UEFI shell and found that the generated entry was trying to load the default linux kernel instead of linux-zen:

    # Created by: archinstall
    # Created on: 2021-05-30_21-00-01
    title Arch Linux
    linux /vmlinuz-linux
    initrd /initramfs-linux.img
    options root=PARTUUID=e6393745-d916-4176-a7c3-315b3457092c rw intel_pstate=no_hwp

The linux-zen files were present in the EFI system partition, and manually editing the entry to add the -zen suffix made the system bootable.

## Installation log

[install.log](https://github.com/archlinux/archinstall/files/6566917/install.log)



Discussion:

Resolving patch:
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -40,6 +40,7 @@ def __init__(self, target, *, base_packages=None, kernels=None):
 			base_packages = __packages__[:3]
 		if kernels is None:
 			kernels = ['linux']
+		self.kernels = kernels
 		self.target = target
 		self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S')
 		self.milliseconds = int(str(time.time()).split('.')[1])
@@ -53,6 +54,7 @@ def __init__(self, target, *, base_packages=None, kernels=None):
 		for kernel in kernels:
 			self.base_packages.append(kernel)
 
+
 		self.post_base_install = []
 
 		storage['session'] = self
@@ -453,45 +455,42 @@ def add_bootloader(self, bootloader='systemd-bootctl'):
 			with open(f'{self.target}/boot/loader/loader.conf', 'w') as loader:
 				for line in loader_data:
 					if line[:8] == 'default ':
-						loader.write(f'default {self.init_time}\n')
+						loader.write(f'default {self.init_time}_{self.kernels[0]}\n')
 					elif line[:8] == '#timeout' and 'timeout 5' not in loader_data:
 						# We add in the default timeout to support dual-boot
 						loader.write(f"{line[1:]}\n")
 					else:
 						loader.write(f"{line}\n")
 
-			# For some reason, blkid and /dev/disk/by-uuid are not getting along well.
-			# And blkid is wrong in terms of LUKS.
-			# UUID = sys_command('blkid -s PARTUUID -o value {drive}{partition_2}'.format(**args)).decode('UTF-8').strip()
-			# Setup the loader entry
-			with open(f'{self.target}/boot/loader/entries/{self.init_time}.conf', 'w') as entry:
-				entry.write('# Created by: archinstall\n')
-				entry.write(f'# Created on: {self.init_time}\n')
-				entry.write('title Arch Linux\n')
-				entry.write('linux /vmlinuz-linux\n')
-				if not is_vm():
-					vendor = cpu_vendor()
-					if vendor == "AuthenticAMD":
-						entry.write("initrd /amd-ucode.img\n")
-					elif vendor == "GenuineIntel":
-						entry.write("initrd /intel-ucode.img\n")
+			for kernel in self.kernels:
+				# Setup the loader entry
+				with open(f'{self.target}/boot/loader/entries/{self.init_time}_{kernel}.conf', 'w') as entry:
+					entry.write('# Created by: archinstall\n')
+					entry.write(f'# Created on: {self.init_time}\n')
+					entry.write('title Arch Linux\n')
+					entry.write(f"linux /vmlinuz-{kernel}\n") # Issue: hardcoded
+					if not is_vm():
+						vendor = cpu_vendor()
+						if vendor == "AuthenticAMD":
+							entry.write("initrd /amd-ucode.img\n")
+						elif vendor == "GenuineIntel":
+							entry.write("initrd /intel-ucode.img\n")
+						else:
+							self.log("unknow cpu vendor, not adding ucode to systemd-boot config")
+					entry.write(f"initrd /initramfs-{kernel}.img\n")
+					# blkid doesn't trigger on loopback devices really well,
+					# so we'll use the old manual method until we get that sorted out.
+
+					if real_device := self.detect_encryption(root_partition):
+						# TODO: We need to detect if the encrypted device is a whole disk encryption,
+						#       or simply a partition encryption. Right now we assume it's a partition (and we always have)
+						log(f"Identifying root partition by PART-UUID on {real_device}: '{real_device.uuid}'.", level=logging.DEBUG)
+						entry.write(f'options cryptdevice=PARTUUID={real_device.uuid}:luksdev root=/dev/mapper/luksdev rw intel_pstate=no_hwp {" ".join(self.KERNEL_PARAMS)}\n')
 					else:
-						self.log("unknow cpu vendor, not adding ucode to systemd-boot config")
-				entry.write('initrd /initramfs-linux.img\n')
-				# blkid doesn't trigger on loopback devices really well,
-				# so we'll use the old manual method until we get that sorted out.
-
-				if real_device := self.detect_encryption(root_partition):
-					# TODO: We need to detect if the encrypted device is a whole disk encryption,
-					#       or simply a partition encryption. Right now we assume it's a partition (and we always have)
-					log(f"Identifying root partition by PART-UUID on {real_device}: '{real_device.uuid}'.", level=logging.DEBUG)
-					entry.write(f'options cryptdevice=PARTUUID={real_device.uuid}:luksdev root=/dev/mapper/luksdev rw intel_pstate=no_hwp {" ".join(self.KERNEL_PARAMS)}\n')
-				else:
-					log(f"Identifying root partition by PART-UUID on {root_partition}, looking for '{root_partition.uuid}'.", level=logging.DEBUG)
-					entry.write(f'options root=PARTUUID={root_partition.uuid} rw intel_pstate=no_hwp {" ".join(self.KERNEL_PARAMS)}\n')
+						log(f"Identifying root partition by PART-UUID on {root_partition}, looking for '{root_partition.uuid}'.", level=logging.DEBUG)
+						entry.write(f'options root=PARTUUID={root_partition.uuid} rw intel_pstate=no_hwp {" ".join(self.KERNEL_PARAMS)}\n')
 
-				self.helper_flags['bootloader'] = bootloader
-				return True
+					self.helper_flags['bootloader'] = bootloader
 
 		elif bootloader == "grub-install":
 			self.pacstrap('grub')
@@ -509,10 +508,11 @@ def add_bootloader(self, bootloader='systemd-bootctl'):
 				o = b''.join(SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --target=i386-pc /dev/{root_device}'))
 				SysCommand('/usr/bin/arch-chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg')
 				self.helper_flags['bootloader'] = True
-				return True
 		else:
 			raise RequirementError(f"Unknown (or not yet implemented) bootloader requested: {bootloader}")
 
+		return True
+
 	def add_additional_packages(self, *packages):
 		return self.pacstrap(*packages)
 
diff --git a/archinstall/lib/plugins.py b/archinstall/lib/plugins.py
--- a/archinstall/lib/plugins.py
+++ b/archinstall/lib/plugins.py
@@ -16,7 +16,7 @@
 # 1: List archinstall.plugin definitions
 # 2: Load the plugin entrypoint
 # 3: Initiate the plugin and store it as .name in plugins
-for plugin_definition in metadata.entry_points()['archinstall.plugin']:
+for plugin_definition in metadata.entry_points().get('archinstall.plugin', []):
 	plugin_entrypoint = plugin_definition.load()
 	try:
 		plugins[plugin_definition.name] = plugin_entrypoint()


Test patch: