Skip to content

Commit c8de0fe

Browse files
authored
Merge branch 'archlinux:master' into master
2 parents 82bc26c + 051352e commit c8de0fe

69 files changed

Lines changed: 2125 additions & 236 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

PKGBUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# Contributor: demostanis worlds <demostanis@protonmail.com>
66

77
pkgname=archinstall
8-
pkgver=3.0.14
8+
pkgver=3.0.15
99
pkgrel=1
1010
pkgdesc="Just another guided/automated Arch Linux installer with a twist"
1111
arch=(any)

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ The installer also doubles as a python library to install Arch Linux and manage
2323
# archinstall
2424
```
2525

26-
Alternative ways to install are `git clone` the repository or `pip install --upgrade archinstall`.
26+
Alternative ways to install are `git clone` the repository (and is better since you get the latest code regardless of [build date](https://archlinux.org/packages/?sort=&q=archinstall)) or `pip install --upgrade archinstall`.
2727

2828
## Running the [guided](https://github.com/archlinux/archinstall/blob/master/archinstall/scripts/guided.py) installer
2929

@@ -36,11 +36,11 @@ archinstall
3636

3737
```shell
3838
# cd archinstall-git
39-
# python -m archinstall
39+
# python -m archinstall $@
4040
```
4141

4242
#### Advanced
43-
Some additional options that most users do not need are hidden behind the `--advanced` flag.
43+
Some additional options that most users do not need are hidden behind the `--advanced` flag and all options/args can be consulted through `-h` or `--help`.
4444

4545
## Running from a declarative configuration file or URL
4646

@@ -162,7 +162,7 @@ To test this without a live ISO, the simplest approach is to use a local image a
162162
This can be done by installing `pacman -S arch-install-scripts util-linux` locally and doing the following:
163163

164164
# truncate -s 20G testimage.img
165-
# losetup --partscan --show --find ./testimage.img
165+
# losetup --partscan --show ./testimage.img
166166
# pip install --upgrade archinstall
167167
# python -m archinstall --script guided
168168
# 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
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import TYPE_CHECKING
2+
3+
from archinstall.lib.models.application import PowerManagement, PowerManagementConfiguration
4+
from archinstall.lib.output import debug
5+
6+
if TYPE_CHECKING:
7+
from archinstall.lib.installer import Installer
8+
9+
10+
class PowerManagementApp:
11+
@property
12+
def ppd_packages(self) -> list[str]:
13+
return [
14+
'power-profiles-daemon',
15+
]
16+
17+
@property
18+
def tuned_packages(self) -> list[str]:
19+
return [
20+
'tuned',
21+
'tuned-ppd',
22+
]
23+
24+
def install(
25+
self,
26+
install_session: 'Installer',
27+
power_management_config: PowerManagementConfiguration,
28+
) -> None:
29+
debug(f'Installing power management daemon: {power_management_config.power_management.value}')
30+
31+
match power_management_config.power_management:
32+
case PowerManagement.POWER_PROFILES_DAEMON:
33+
install_session.add_additional_packages(self.ppd_packages)
34+
case PowerManagement.TUNED:
35+
install_session.add_additional_packages(self.tuned_packages)

archinstall/default_profiles/desktop.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ def packages(self) -> list[str]:
3232
'wget',
3333
'iwd',
3434
'wireless_tools',
35-
'wpa_supplicant',
3635
'smartmontools',
3736
'xdg-utils',
3837
]

archinstall/lib/applications/application_handler.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from archinstall.applications.audio import AudioApp
44
from archinstall.applications.bluetooth import BluetoothApp
5+
from archinstall.applications.power_management import PowerManagementApp
56
from archinstall.applications.print_service import PrintServiceApp
67
from archinstall.lib.models import Audio
78
from archinstall.lib.models.application import ApplicationConfiguration
@@ -26,6 +27,12 @@ def install_applications(self, install_session: 'Installer', app_config: Applica
2627
users,
2728
)
2829

30+
if app_config.power_management_config:
31+
PowerManagementApp().install(
32+
install_session,
33+
app_config.power_management_config,
34+
)
35+
2936
if app_config.print_service_config and app_config.print_service_config.enabled:
3037
PrintServiceApp().install(install_session)
3138

archinstall/lib/applications/application_menu.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
from typing import override
22

3+
from archinstall.lib.hardware import SysInfo
34
from archinstall.lib.menu.abstract_menu import AbstractSubMenu
4-
from archinstall.lib.models.application import ApplicationConfiguration, Audio, AudioConfiguration, BluetoothConfiguration, PrintServiceConfiguration
5+
from archinstall.lib.models.application import (
6+
ApplicationConfiguration,
7+
Audio,
8+
AudioConfiguration,
9+
BluetoothConfiguration,
10+
PowerManagement,
11+
PowerManagementConfiguration,
12+
PrintServiceConfiguration,
13+
)
514
from archinstall.lib.translationhandler import tr
615
from archinstall.tui.curses_menu import SelectMenu
716
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
@@ -54,8 +63,21 @@ def _define_menu_options(self) -> list[MenuItem]:
5463
preview_action=self._prev_print_service,
5564
key='print_service_config',
5665
),
66+
MenuItem(
67+
text=tr('Power management'),
68+
action=select_power_management,
69+
preview_action=self._prev_power_management,
70+
enabled=SysInfo.has_battery(),
71+
key='power_management_config',
72+
),
5773
]
5874

75+
def _prev_power_management(self, item: MenuItem) -> str | None:
76+
if item.value is not None:
77+
config: PowerManagementConfiguration = item.value
78+
return f'{tr("Power management")}: {config.power_management.value}'
79+
return None
80+
5981
def _prev_bluetooth(self, item: MenuItem) -> str | None:
6082
if item.value is not None:
6183
bluetooth_config: BluetoothConfiguration = item.value
@@ -81,6 +103,29 @@ def _prev_print_service(self, item: MenuItem) -> str | None:
81103
return None
82104

83105

106+
def select_power_management(preset: PowerManagementConfiguration | None = None) -> PowerManagementConfiguration | None:
107+
group = MenuItemGroup.from_enum(PowerManagement)
108+
109+
if preset:
110+
group.set_focus_by_value(preset.power_management)
111+
112+
result = SelectMenu[PowerManagement](
113+
group,
114+
allow_skip=True,
115+
alignment=Alignment.CENTER,
116+
allow_reset=True,
117+
frame=FrameProperties.min(tr('Power management')),
118+
).run()
119+
120+
match result.type_:
121+
case ResultType.Skip:
122+
return preset
123+
case ResultType.Selection:
124+
return PowerManagementConfiguration(power_management=result.get_value())
125+
case ResultType.Reset:
126+
return None
127+
128+
84129
def select_bluetooth(preset: BluetoothConfiguration | None) -> BluetoothConfiguration | None:
85130
group = MenuItemGroup.yes_no()
86131
group.focus_item = MenuItem.no()

archinstall/lib/args.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from pydantic.dataclasses import dataclass as p_dataclass
1414

1515
from archinstall.lib.crypt import decrypt
16-
from archinstall.lib.models.application import ApplicationConfiguration
16+
from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration
1717
from archinstall.lib.models.authentication import AuthenticationConfiguration
1818
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
1919
from archinstall.lib.models.device import DiskEncryption, DiskLayoutConfiguration
@@ -67,12 +67,12 @@ class ArchConfig:
6767
bootloader_config: BootloaderConfiguration | None = None
6868
app_config: ApplicationConfiguration | None = None
6969
auth_config: AuthenticationConfiguration | None = None
70+
swap: ZramConfiguration | None = None
7071
hostname: str = 'archlinux'
7172
kernels: list[str] = field(default_factory=lambda: ['linux'])
7273
ntp: bool = True
7374
packages: list[str] = field(default_factory=list)
7475
parallel_downloads: int = 0
75-
swap: bool = True
7676
timezone: str = 'UTC'
7777
services: list[str] = field(default_factory=list)
7878
custom_commands: list[str] = field(default_factory=list)
@@ -185,7 +185,7 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> 'ArchConfi
185185
uki = args_config.get('uki', False)
186186
if uki and not bootloader.has_uki_support():
187187
uki = False
188-
arch_config.bootloader_config = BootloaderConfiguration(bootloader=bootloader, uki=uki, removable=False)
188+
arch_config.bootloader_config = BootloaderConfiguration(bootloader=bootloader, uki=uki, removable=True)
189189

190190
# deprecated: backwards compatibility
191191
audio_config_args = args_config.get('audio_config', None)
@@ -211,7 +211,9 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> 'ArchConfi
211211
if parallel_downloads := args_config.get('parallel_downloads', 0):
212212
arch_config.parallel_downloads = parallel_downloads
213213

214-
arch_config.swap = args_config.get('swap', True)
214+
swap_arg = args_config.get('swap')
215+
if swap_arg is not None:
216+
arch_config.swap = ZramConfiguration.parse_arg(swap_arg)
215217

216218
if timezone := args_config.get('timezone', 'UTC'):
217219
arch_config.timezone = timezone

archinstall/lib/bootloader/bootloader_menu.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ def _prev_uki(self, item: MenuItem) -> str | None:
8383

8484
def _prev_removable(self, item: MenuItem) -> str | None:
8585
if item.value:
86-
return tr('Will install to /EFI/BOOT/ (removable location)')
87-
return tr('Will install to standard location with NVRAM entry')
86+
return tr('Will install to /EFI/BOOT/ (removable location, safe default)')
87+
return tr('Will install to custom location with NVRAM entry')
8888

8989
@override
9090
def run(
@@ -114,6 +114,9 @@ def _select_bootloader(self, preset: Bootloader | None) -> Bootloader | None:
114114
removable_item.value = False
115115
self._bootloader_conf.removable = False
116116
else:
117+
if not removable_item.enabled:
118+
removable_item.value = True
119+
self._bootloader_conf.removable = True
117120
removable_item.enabled = True
118121

119122
return bootloader
@@ -147,18 +150,26 @@ def _select_removable(self, preset: bool) -> bool:
147150
+ '\n\n'
148151
+ tr('This installs the bootloader to /EFI/BOOT/BOOTX64.EFI (or similar) which is useful for:')
149152
+ '\n\n • '
153+
+ tr('Firmware that does not properly support NVRAM boot entries like most MSI motherboards,')
154+
+ '\n '
155+
+ tr('most Apple Macs, many laptops...')
156+
+ '\n • '
150157
+ tr('USB drives or other portable external media.')
151158
+ '\n • '
152159
+ tr('Systems where you want the disk to be bootable on any computer.')
153-
+ '\n • '
154-
+ tr('Firmware that does not properly support NVRAM boot entries.')
155160
+ '\n\n'
156161
+ tr(
157162
textwrap.dedent(
158163
"""\
159-
This is NOT recommended if none of the above apply, as it makes installing multiple
160-
EFI bootloaders on the same disk more challenging, and it overwrites whatever bootloader
161-
was previously installed on the default removable media search location, if any.
164+
If you do not know what this means, LEAVE THIS OPTION ENABLED, as it is the safe default.
165+
166+
It is suggested to disable this if none of the above apply, as it makes installing multiple
167+
EFI bootloaders on the same disk easier, and it will not overwrite whatever bootloader
168+
was previously installed at the default removable media search location, if any.
169+
170+
It may also make the installation more resilient in case of dual-booting with Windows,
171+
as Windows is known to sometimes erase or replace the bootloader installed at the removable
172+
location.
162173
"""
163174
)
164175
)

archinstall/lib/disk/device_handler.py

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -422,11 +422,18 @@ def _lvm_info_with_retry(
422422
cmd: str,
423423
info_type: Literal['lv', 'vg', 'pvseg'],
424424
) -> LvmVolumeInfo | LvmGroupInfo | LvmPVInfo | None:
425-
while True:
425+
# Retry for up to 5 mins
426+
max_retries = 100
427+
for attempt in range(max_retries):
426428
try:
427429
return self._lvm_info(cmd, info_type)
428430
except ValueError:
429-
time.sleep(3)
431+
if attempt < max_retries - 1:
432+
debug(f'LVM info query failed (attempt {attempt + 1}/{max_retries}), retrying in 3 seconds...')
433+
time.sleep(3)
434+
435+
debug(f'LVM info query failed after {max_retries} attempts')
436+
return None
430437

431438
def lvm_vol_info(self, lv_name: str) -> LvmVolumeInfo | None:
432439
cmd = f'lvs --reportformat json --unit B -S lv_name={lv_name}'
@@ -457,8 +464,22 @@ def lvm_export_vg(self, vg: LvmVolumeGroup) -> None:
457464
SysCommand(cmd)
458465

459466
def lvm_import_vg(self, vg: LvmVolumeGroup) -> None:
460-
cmd = f'vgimport {vg.name}'
467+
# Check if the VG is actually exported before trying to import it
468+
check_cmd = f'vgs --noheadings -o vg_exported {vg.name}'
469+
470+
try:
471+
result = SysCommand(check_cmd)
472+
is_exported = result.decode().strip() == 'exported'
473+
except SysCallError:
474+
# VG might not exist yet, skip import
475+
debug(f'Volume group {vg.name} not found, skipping import')
476+
return
461477

478+
if not is_exported:
479+
debug(f'Volume group {vg.name} is already active (not exported), skipping import')
480+
return
481+
482+
cmd = f'vgimport {vg.name}'
462483
debug(f'vgimport: {cmd}')
463484
SysCommand(cmd)
464485

@@ -470,22 +491,25 @@ def lvm_vol_reduce(self, vol_path: Path, amount: Size) -> None:
470491
SysCommand(cmd)
471492

472493
def lvm_pv_create(self, pvs: Iterable[Path]) -> None:
473-
cmd = 'pvcreate ' + ' '.join([str(pv) for pv in pvs])
494+
pvs_str = ' '.join([str(pv) for pv in pvs])
495+
# Signatures are already wiped by wipefs, -f is just for safety
496+
cmd = f'pvcreate -f --yes {pvs_str}'
497+
# note flags used in scripting
474498
debug(f'Creating LVM PVS: {cmd}')
499+
SysCommand(cmd)
475500

476-
worker = SysCommandWorker(cmd)
477-
worker.poll()
478-
worker.write(b'y\n', line_ending=False)
501+
# Sync with udev to ensure the PVs are visible
502+
self.udev_sync()
479503

480504
def lvm_vg_create(self, pvs: Iterable[Path], vg_name: str) -> None:
481505
pvs_str = ' '.join([str(pv) for pv in pvs])
482-
cmd = f'vgcreate --yes {vg_name} {pvs_str}'
506+
cmd = f'vgcreate --yes --force {vg_name} {pvs_str}'
483507

484508
debug(f'Creating LVM group: {cmd}')
509+
SysCommand(cmd)
485510

486-
worker = SysCommandWorker(cmd)
487-
worker.poll()
488-
worker.write(b'y\n', line_ending=False)
511+
# Sync with udev to ensure the VG is visible
512+
self.udev_sync()
489513

490514
def lvm_vol_create(self, vg_name: str, volume: LvmVolume, offset: Size | None = None) -> None:
491515
if offset is not None:
@@ -729,6 +753,17 @@ def partition(
729753

730754
disk.commit()
731755

756+
# Wipe filesystem/LVM signatures from newly created partitions
757+
# to prevent "signature detected" errors
758+
for part_mod in filtered_part:
759+
if part_mod.dev_path:
760+
debug(f'Wiping signatures from: {part_mod.dev_path}')
761+
SysCommand(f'wipefs --all {part_mod.dev_path}')
762+
763+
# Sync with udev after wiping signatures
764+
if filtered_part:
765+
self.udev_sync()
766+
732767
@staticmethod
733768
def swapon(path: Path) -> None:
734769
try:

0 commit comments

Comments
 (0)