Skip to content

Commit 7305255

Browse files
committed
Merge branch 'master' into network-mandatory-selection
# Conflicts: # archinstall/lib/configuration.py
2 parents 3b275a7 + 08cba23 commit 7305255

29 files changed

Lines changed: 1438 additions & 714 deletions

File tree

.github/workflows/translation-check.yaml

Lines changed: 0 additions & 28 deletions
This file was deleted.

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
default_stages: ['pre-commit']
22
repos:
33
- repo: https://github.com/astral-sh/ruff-pre-commit
4-
rev: v0.15.11
4+
rev: v0.15.12
55
hooks:
66
# fix unused imports and sort them
77
- id: ruff
@@ -31,7 +31,7 @@ repos:
3131
args: [--config=.flake8]
3232
fail_fast: true
3333
- repo: https://github.com/pre-commit/mirrors-mypy
34-
rev: v1.20.1
34+
rev: v1.20.2
3535
hooks:
3636
- id: mypy
3737
args: [

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ This can be done by installing `pacman -S arch-install-scripts util-linux` local
179179
# losetup --partscan --show ./testimage.img
180180
# pip install --upgrade archinstall
181181
# python -m archinstall --script guided
182-
# qemu-system-x86_64 -enable-kvm -machine q35,accel=kvm -device intel-iommu -cpu host -m 4096 -boot order=d -drive file=./testimage.img,format=raw -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF.4m.fd -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF.4m.fd
182+
# qemu-system-x86_64 -enable-kvm -machine q35,accel=kvm -device intel-iommu -cpu host -m 4096 -boot order=d -drive file=./testimage.img,format=raw -drive if=pflash,format=raw,readonly,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd -drive if=pflash,format=raw,readonly,file=/usr/share/edk2/x64/OVMF_VARS.4m.fd
183183

184184
This will create a *20 GB* `testimage.img` and create a loop device which we can use to format and install to.<br>
185185
`archinstall` is installed and executed in [guided mode](#docs-todo). Once the installation is complete, ~~you can use qemu/kvm to boot the test media.~~<br>
@@ -199,8 +199,8 @@ You may want to boot an ISO image in a VM to test `archinstall` in there.
199199
qemu-system-x86_64 -enable-kvm \
200200
-machine q35,accel=kvm -device intel-iommu \
201201
-cpu host -m 4096 -boot order=d \
202-
-drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF.4m.fd \
203-
-drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF.4m.fd \
202+
-drive if=pflash,format=raw,readonly,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd \
203+
-drive if=pflash,format=raw,readonly,file=/usr/share/edk2/x64/OVMF_VARS.4m.fd \
204204
-drive file=./archlinux-2025.12.01-x86_64.iso,format=raw
205205
```
206206

@@ -209,8 +209,8 @@ HINT: For espeakup support
209209
qemu-system-x86_64 -enable-kvm \
210210
-machine q35,accel=kvm -device intel-iommu \
211211
-cpu host -m 4096 -boot order=d \
212-
-drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF.4m.fd \
213-
-drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF.4m.fd \
212+
-drive if=pflash,format=raw,readonly,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd \
213+
-drive if=pflash,format=raw,readonly,file=/usr/share/edk2/x64/OVMF_VARS.4m.fd \
214214
-drive file=./archlinux-2025.12.01-x86_64.iso,format=raw \
215215
-device intel-hda -device hda-duplex,audiodev=snd0 \
216216
-audiodev pa,id=snd0,server=/run/user/1000/pulse/native
@@ -219,6 +219,10 @@ qemu-system-x86_64 -enable-kvm \
219219

220220
# FAQ
221221

222+
## AUR
223+
224+
`archinstall` will not offer or bundle AUR helpers or AUR packages due to a current consensus. This is not any individual developers decision. The reasons and discussions for this stance on the topic can be found on our mailing list thread: [(optional) AUR helper in archinstall](https://lists.archlinux.org/archives/list/arch-dev-public@lists.archlinux.org/thread/VYOULH2GOJLFM2BXOFLWH3D754YXFPSL/).
225+
222226
## Keyring out-of-date
223227
For a description of the problem see https://archinstall.archlinux.page/help/known_issues.html#keyring-is-out-of-date-2213 and discussion in issue https://github.com/archlinux/archinstall/issues/2213.
224228

archinstall/default_profiles/desktops/budgie.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def __init__(self) -> None:
99
'Budgie',
1010
ProfileType.DesktopEnv,
1111
support_gfx_driver=True,
12-
display_server=DisplayServerType.Xorg,
12+
display_server=DisplayServerType.Wayland,
1313
)
1414

1515
@property
@@ -18,12 +18,12 @@ def packages(self) -> list[str]:
1818
return [
1919
'materia-gtk-theme',
2020
'budgie',
21-
'mate-terminal',
22-
'nemo',
21+
'konsole',
22+
'dolphin',
2323
'papirus-icon-theme',
2424
]
2525

2626
@property
2727
@override
2828
def default_greeter_type(self) -> GreeterType:
29-
return GreeterType.LightdmSlick
29+
return GreeterType.Sddm

archinstall/lib/args.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from archinstall.lib.models.locale import LocaleConfiguration
2222
from archinstall.lib.models.mirrors import MirrorConfiguration
2323
from archinstall.lib.models.network import NetworkConfiguration
24+
from archinstall.lib.models.package_types import DEFAULT_KERNEL
2425
from archinstall.lib.models.packages import Repository
2526
from archinstall.lib.models.pacman import PacmanConfiguration
2627
from archinstall.lib.models.profile import ProfileConfiguration
@@ -71,7 +72,7 @@ class ArchConfig:
7172
auth_config: AuthenticationConfiguration | None = None
7273
swap: ZramConfiguration | None = None
7374
hostname: str = 'archlinux'
74-
kernels: list[str] = field(default_factory=lambda: ['linux'])
75+
kernels: list[str] = field(default_factory=lambda: [DEFAULT_KERNEL.value])
7576
ntp: bool = True
7677
packages: list[str] = field(default_factory=list)
7778
pacman_config: PacmanConfiguration = field(default_factory=PacmanConfiguration.default)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from dataclasses import dataclass
2+
from enum import Enum, auto
3+
from pathlib import Path
4+
5+
from archinstall.lib.hardware import SysInfo
6+
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
7+
from archinstall.lib.models.device import DiskLayoutConfiguration
8+
9+
10+
class BootloaderValidationFailureKind(Enum):
11+
LimineNonFatBoot = auto()
12+
LimineLayout = auto()
13+
BootloaderRequiresUefi = auto()
14+
EfistubNonFatBoot = auto()
15+
16+
17+
@dataclass(frozen=True)
18+
class BootloaderValidationFailure:
19+
kind: BootloaderValidationFailureKind
20+
description: str
21+
22+
23+
def validate_bootloader_layout(
24+
bootloader_config: BootloaderConfiguration | None,
25+
disk_config: DiskLayoutConfiguration | None,
26+
) -> BootloaderValidationFailure | None:
27+
"""Validate bootloader configuration against disk layout.
28+
29+
Returns a failure with a human-readable description if the configuration
30+
would produce an unbootable system, or None if it is valid.
31+
"""
32+
if not (bootloader_config and disk_config):
33+
return None
34+
35+
bootloader = bootloader_config.bootloader
36+
37+
if bootloader == Bootloader.NO_BOOTLOADER:
38+
return None
39+
40+
if bootloader.is_uefi_only() and not SysInfo.has_uefi():
41+
return BootloaderValidationFailure(
42+
kind=BootloaderValidationFailureKind.BootloaderRequiresUefi,
43+
description=f'{bootloader.value} requires a UEFI system.',
44+
)
45+
46+
boot_part = next(
47+
(p for m in disk_config.device_modifications if (p := m.get_boot_partition())),
48+
None,
49+
)
50+
51+
if bootloader == Bootloader.Efistub:
52+
# The UEFI firmware reads the kernel directly from the boot partition,
53+
# which must be FAT.
54+
if boot_part and (boot_part.fs_type is None or not boot_part.fs_type.is_fat()):
55+
return BootloaderValidationFailure(
56+
kind=BootloaderValidationFailureKind.EfistubNonFatBoot,
57+
description='Efistub does not support booting with a non-FAT boot partition.',
58+
)
59+
60+
if bootloader == Bootloader.Limine:
61+
# Limine reads its config and kernels from the boot partition, which
62+
# must be FAT.
63+
if boot_part and (boot_part.fs_type is None or not boot_part.fs_type.is_fat()):
64+
return BootloaderValidationFailure(
65+
kind=BootloaderValidationFailureKind.LimineNonFatBoot,
66+
description='Limine does not support booting with a non-FAT boot partition.',
67+
)
68+
69+
# When the ESP is the boot partition but mounted outside /boot and
70+
# UKI is disabled, kernels end up on the root filesystem which
71+
# Limine cannot access.
72+
if not bootloader_config.uki:
73+
efi_part = next(
74+
(p for m in disk_config.device_modifications if (p := m.get_efi_partition())),
75+
None,
76+
)
77+
if efi_part and efi_part == boot_part and efi_part.mountpoint != Path('/boot'):
78+
return BootloaderValidationFailure(
79+
kind=BootloaderValidationFailureKind.LimineLayout,
80+
description=(
81+
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_part.mountpoint}, '
82+
'enable UKI or add a separate /boot partition to install Limine.'
83+
),
84+
)
85+
86+
return None

archinstall/lib/command.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ def __init__(
4444
self._trace_log_pos = 0
4545
self.poll_object = epoll()
4646
self.child_fd: int | None = None
47-
self.started: float | None = None
48-
self.ended: float | None = None
47+
self.started = False
48+
self.ended = False
4949
self.remove_vt100_escape_codes_from_lines: bool = remove_vt100_escape_codes_from_lines
5050

5151
def __contains__(self, key: bytes) -> bool:
@@ -117,7 +117,7 @@ def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseExceptio
117117
def is_alive(self) -> bool:
118118
self.poll()
119119

120-
if self.started and self.ended is None:
120+
if self.started and not self.ended:
121121
return True
122122

123123
return False
@@ -173,11 +173,11 @@ def poll(self) -> None:
173173
self.peak(output)
174174
self._trace_log += output
175175
except OSError:
176-
self.ended = time.time()
176+
self.ended = True
177177
break
178178

179179
if self.ended or (not got_output and not _pid_exists(self.pid)):
180-
self.ended = time.time()
180+
self.ended = True
181181
try:
182182
wait_status = os.waitpid(self.pid, 0)[1]
183183
self.exit_code = os.waitstatus_to_exitcode(wait_status)
@@ -215,7 +215,7 @@ def execute(self) -> bool:
215215
# Only parent process moves back to the original working directory
216216
os.chdir(old_dir)
217217

218-
self.started = time.time()
218+
self.started = True
219219
self.poll_object.register(self.child_fd, EPOLLIN | EPOLLHUP)
220220

221221
return True

archinstall/lib/configuration.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from archinstall.lib.crypt import encrypt
1111
from archinstall.lib.menu.helpers import Confirmation, Selection
1212
from archinstall.lib.menu.util import get_password, prompt_dir
13+
from archinstall.lib.models.bootloader import Bootloader
1314
from archinstall.lib.models.network import NetworkConfiguration
1415
from archinstall.lib.output import debug, logger, warn
1516
from archinstall.lib.translationhandler import tr
@@ -59,6 +60,70 @@ def write_debug(self) -> None:
5960
debug(' -- Chosen configuration --')
6061
debug(self.user_config_to_json())
6162

63+
def as_summary(self) -> str:
64+
"""
65+
Render a concise two-column summary of the current configuration.
66+
67+
The left column holds section labels, the right column holds values.
68+
Column width adapts to the longest translated label so translations
69+
do not break the alignment. Rows whose underlying config is not set
70+
are skipped.
71+
72+
Returns an empty string if nothing meaningful to show.
73+
"""
74+
rows: list[tuple[str, str]] = []
75+
76+
disk_config = self._config.disk_config
77+
if disk_config and disk_config.device_modifications:
78+
disk_parts: list[str] = []
79+
for mod in disk_config.device_modifications:
80+
path = str(mod.device_path)
81+
root_part = mod.get_root_partition()
82+
flags: list[str] = []
83+
if root_part and root_part.fs_type:
84+
flags.append(root_part.fs_type.value)
85+
if disk_config.disk_encryption:
86+
flags.append(tr('LUKS'))
87+
disk_parts.append(f'{path} ({" + ".join(flags)})' if flags else path)
88+
rows.append((tr('Disks'), ', '.join(disk_parts)))
89+
90+
bl_config = self._config.bootloader_config
91+
if bl_config and bl_config.bootloader != Bootloader.NO_BOOTLOADER:
92+
rows.append((tr('Bootloader'), bl_config.bootloader.value))
93+
94+
kernels = self._config.kernels
95+
if kernels:
96+
rows.append((tr('Kernel'), ', '.join(kernels)))
97+
98+
profile_config = self._config.profile_config
99+
if profile_config and profile_config.profile:
100+
names = profile_config.profile.current_selection_names()
101+
rows.append((tr('Profile'), ', '.join(names) if names else profile_config.profile.name))
102+
if profile_config.greeter:
103+
rows.append((tr('Greeter'), profile_config.greeter.value))
104+
105+
packages = self._config.packages
106+
if packages:
107+
rows.append((tr('Packages'), str(len(packages))))
108+
109+
net_config = self._config.network_config
110+
if isinstance(net_config, NetworkConfiguration):
111+
rows.append((tr('Network'), net_config.type.display_msg()))
112+
113+
locale_config = self._config.locale_config
114+
if locale_config:
115+
rows.append((tr('Locale'), locale_config.sys_lang))
116+
117+
tz = self._config.timezone
118+
if tz:
119+
rows.append((tr('Timezone'), tz))
120+
121+
if not rows:
122+
return ''
123+
124+
label_width = max(len(label) for label, _ in rows) + 2
125+
return '\n'.join(f'{label:<{label_width}}{value}' for label, value in rows)
126+
62127
async def confirm_config(self, show_install_warnings: bool = False) -> bool:
63128
header = f'{tr("The specified configuration will be applied")}. '
64129
header += tr('Would you like to continue?') + '\n'

archinstall/lib/disk/device_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ def format(
250250
case FilesystemType.EXT2 | FilesystemType.EXT3 | FilesystemType.EXT4:
251251
# Force create
252252
options.append('-F')
253-
case FilesystemType.FAT12 | FilesystemType.FAT16 | FilesystemType.FAT32:
253+
case _ if fs_type.is_fat():
254254
mkfs_type = 'fat'
255255
# Set FAT size
256256
options.extend(('-F', fs_type.value.removeprefix(mkfs_type)))

archinstall/lib/general/system_menu.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,24 @@
33
from archinstall.lib.hardware import GfxDriver, SysInfo
44
from archinstall.lib.menu.helpers import Confirmation, Selection
55
from archinstall.lib.models.application import ZramAlgorithm, ZramConfiguration
6+
from archinstall.lib.models.package_types import DEFAULT_KERNEL, Kernel
67
from archinstall.lib.translationhandler import tr
78
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
89
from archinstall.tui.ui.result import ResultType
910

1011

11-
async def select_kernel(preset: list[str] = []) -> list[str]:
12+
async def select_kernel(preset: list[Kernel] = []) -> list[Kernel]:
1213
"""
1314
Asks the user to select a kernel for system.
1415
1516
:return: The string as a selected kernel
1617
:rtype: string
1718
"""
18-
kernels = ['linux', 'linux-lts', 'linux-zen', 'linux-hardened']
19-
default_kernel = 'linux'
19+
group = MenuItemGroup.from_enum(Kernel, sort_items=True, preset=preset)
20+
group.set_default_by_value(DEFAULT_KERNEL)
21+
group.set_focus_by_value(DEFAULT_KERNEL)
2022

21-
items = [MenuItem(k, value=k) for k in kernels]
22-
23-
group = MenuItemGroup(items, sort_items=True)
24-
group.set_default_by_value(default_kernel)
25-
group.set_focus_by_value(default_kernel)
26-
group.set_selected_by_value(preset)
27-
28-
result = await Selection[str](
23+
result = await Selection[Kernel](
2924
group,
3025
header=tr('Select which kernel(s) to install'),
3126
allow_skip=True,

0 commit comments

Comments
 (0)