Merged in master changes

This commit is contained in:
Anton Hvornum 2021-11-05 17:29:53 +01:00
commit bf52588926
No known key found for this signature in database
GPG Key ID: F1234C5BA67C59DF
24 changed files with 146 additions and 118 deletions

View File

@ -1,2 +1,9 @@
[flake8] [flake8]
ignore = E501, W191, E741, E266 count = True
# Several of the following could be autofixed or improved by running the code through psf/black
ignore = E126,E128,E203,E231,E261,E302,E402,E722,F541,W191,W292,W293
max-complexity = 40
max-line-length = 236
show-source = True
statistics = True
per-file-ignores = __init__.py:F401,F403,F405

View File

@ -20,11 +20,7 @@ jobs:
- run: python -m pip install --upgrade pip - run: python -m pip install --upgrade pip
- run: pip install flake8 - run: pip install flake8
- name: Lint with flake8 - name: Lint with flake8
run: | run: flake8 # See the .flake8 file for runtime parameters
# stop the build if there are Python syntax errors
flake8 . --count --select=E9,F63,F7 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
pytest: pytest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:

View File

@ -1,12 +1,17 @@
# flake8: noqa
# The above ignore, see issue: https://github.com/archlinux/archinstall/pull/650#issuecomment-961995949
import os import os
import json import json
import logging import logging
from .exceptions import DiskError
from .helpers import all_disks
from ..output import log from ..output import log
from ..general import SysCommand from ..general import SysCommand
class BlockDevice: class BlockDevice:
def __init__(self, path, info=None): def __init__(self, path, info=None):
if not info: if not info:
from .helpers import all_disks
# If we don't give any information, we need to auto-fill it. # If we don't give any information, we need to auto-fill it.
# Otherwise any subsequent usage will break. # Otherwise any subsequent usage will break.
info = all_disks()[path].info info = all_disks()[path].info

View File

@ -1,4 +1,5 @@
import pathlib, glob import pathlib
import glob
import logging import logging
from typing import Union from typing import Union
from .helpers import get_mount_info from .helpers import get_mount_info
@ -27,7 +28,7 @@ def mount_subvolume(installation, subvolume_location :Union[pathlib.Path, str],
if not target.exists(): if not target.exists():
target.mkdir(parents=True) target.mkdir(parents=True)
if glob.glob(str(target/'*')) and force is False: if glob.glob(str(target / '*')) and force is False:
raise DiskError(f"Cannot mount subvolume to {target} because it contains data (non-empty folder target)") raise DiskError(f"Cannot mount subvolume to {target} because it contains data (non-empty folder target)")
log(f"Mounting {target} as a subvolume", level=logging.INFO) log(f"Mounting {target} as a subvolume", level=logging.INFO)
@ -61,7 +62,7 @@ def create_subvolume(installation, subvolume_location :Union[pathlib.Path, str])
if not target.parent.exists(): if not target.parent.exists():
target.parent.mkdir(parents=True) target.parent.mkdir(parents=True)
if glob.glob(str(target/'*')) and force is False: if glob.glob(str(target / '*')):
raise DiskError(f"Cannot create subvolume at {target} because it contains data (non-empty folder target)") raise DiskError(f"Cannot create subvolume at {target} because it contains data (non-empty folder target)")
# Remove the target if it exists # Remove the target if it exists

View File

@ -1,8 +1,9 @@
import time import time
import logging import logging
import json import json
from .exceptions import DiskError
from .partition import Partition from .partition import Partition
from .blockdevice import BlockDevice from .validators import valid_fs_type
from ..general import SysCommand from ..general import SysCommand
from ..output import log from ..output import log
from ..storage import storage from ..storage import storage
@ -101,7 +102,7 @@ class Filesystem:
partition['password'] = get_password(f"Enter a encryption password for {partition['device_instance']}") partition['password'] = get_password(f"Enter a encryption password for {partition['device_instance']}")
partition['device_instance'].encrypt(password=partition['password']) partition['device_instance'].encrypt(password=partition['password'])
with luks2(partition['device_instance'], storage.get('ENC_IDENTIFIER', 'ai')+'loop', partition['password']) as unlocked_device: with luks2(partition['device_instance'], storage.get('ENC_IDENTIFIER', 'ai') + 'loop', partition['password']) as unlocked_device:
if not partition.get('format'): if not partition.get('format'):
if storage['arguments'] == 'silent': if storage['arguments'] == 'silent':
raise ValueError(f"Missing fs-type to format on newly created encrypted partition {partition['device_instance']}") raise ValueError(f"Missing fs-type to format on newly created encrypted partition {partition['device_instance']}")
@ -113,7 +114,7 @@ class Filesystem:
while True: while True:
partition['filesystem']['format'] = input(f"Enter a valid fs-type for newly encrypted partition {partition['filesystem']['format']}: ").strip() partition['filesystem']['format'] = input(f"Enter a valid fs-type for newly encrypted partition {partition['filesystem']['format']}: ").strip()
if not partition['filesystem']['format'] or valid_fs_type(partition['filesystem']['format']) is False: if not partition['filesystem']['format'] or valid_fs_type(partition['filesystem']['format']) is False:
pint("You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's.") print("You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's.")
continue continue
break break
@ -170,7 +171,6 @@ class Filesystem:
raise DiskError(f"New partition never showed up after adding new partition on {self} (timeout 10 seconds).") raise DiskError(f"New partition never showed up after adding new partition on {self} (timeout 10 seconds).")
time.sleep(0.025) time.sleep(0.025)
# Todo: Find a better way to detect if the new UUID of the partition has showed up. # Todo: Find a better way to detect if the new UUID of the partition has showed up.
# But this will address (among other issues) # But this will address (among other issues)
time.sleep(float(storage['arguments'].get('disk-sleep', 2.0))) # Let the kernel catch up with quick block devices (nvme for instance) time.sleep(float(storage['arguments'].get('disk-sleep', 2.0))) # Let the kernel catch up with quick block devices (nvme for instance)

View File

@ -1,10 +1,11 @@
import re import re
import os
import json import json
import logging import logging
import pathlib import pathlib
from typing import Union from typing import Union
from .blockdevice import BlockDevice from .blockdevice import BlockDevice
from ..exceptions import SysCallError from ..exceptions import SysCallError, DiskError
from ..general import SysCommand from ..general import SysCommand
from ..output import log from ..output import log

View File

@ -4,9 +4,12 @@ import time
import logging import logging
import json import json
import os import os
import hashlib
from typing import Optional from typing import Optional
from .blockdevice import BlockDevice from .blockdevice import BlockDevice
from .exceptions import DiskError, SysCallError, UnknownFilesystemFormat
from .helpers import get_mount_info, get_filesystem_type from .helpers import get_mount_info, get_filesystem_type
from .storage import storage
from ..output import log from ..output import log
from ..general import SysCommand from ..general import SysCommand
@ -173,7 +176,7 @@ class Partition:
from .luks import luks2 from .luks import luks2
try: try:
with luks2(self, storage.get('ENC_IDENTIFIER', 'ai')+'loop', password, auto_unmount=True) as unlocked_device: with luks2(self, storage.get('ENC_IDENTIFIER', 'ai') + 'loop', password, auto_unmount=True) as unlocked_device:
return unlocked_device.filesystem return unlocked_device.filesystem
except SysCallError: except SysCallError:
return None return None

View File

@ -1,4 +1,5 @@
import logging import logging
from .helpers import sort_block_devices_based_on_performance, select_largest_device, select_disk_larger_than_or_close_to
from ..output import log from ..output import log
def suggest_single_disk_layout(block_device, default_filesystem=None): def suggest_single_disk_layout(block_device, default_filesystem=None):
@ -48,14 +49,14 @@ def suggest_single_disk_layout(block_device, default_filesystem=None):
# https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh
layout[block_device.path]['partitions'][1]['btrfs'] = { layout[block_device.path]['partitions'][1]['btrfs'] = {
"subvolumes" : { "subvolumes" : {
'@home' : '/home', "@home" : "/home",
'@log' : '/var/log', "@log" : "/var/log",
'@pkgs' : '/var/cache/pacman/pkg', "@pkgs" : "/var/cache/pacman/pkg",
'@.snapshots' : '/.snapshots' "@.snapshots" : "/.snapshots"
} }
} }
else: else:
pass #... implement a guided setup pass # ... implement a guided setup
elif block_device.size >= MIN_SIZE_TO_ALLOW_HOME_PART: elif block_device.size >= MIN_SIZE_TO_ALLOW_HOME_PART:
# If we don't want to use subvolumes, # If we don't want to use subvolumes,

View File

@ -14,6 +14,7 @@ except:
import select import select
EPOLLIN = 0 EPOLLIN = 0
EPOLLHUP = 0 EPOLLHUP = 0
class epoll(): class epoll():
""" #!if windows """ #!if windows
Create a epoll() implementation that simulates the epoll() behavior. Create a epoll() implementation that simulates the epoll() behavior.
@ -38,7 +39,7 @@ except:
except OSError: except OSError:
return [] return []
from .exceptions import * from .exceptions import RequirementError, SysCallError
from .output import log from .output import log
from .storage import storage from .storage import storage
@ -241,7 +242,7 @@ class SysCommandWorker:
got_output = True got_output = True
self.peak(output) self.peak(output)
self._trace_log += output self._trace_log += output
except OSError as err: except OSError:
self.ended = time.time() self.ended = time.time()
break break
@ -379,8 +380,7 @@ def prerequisite_check():
def reboot(): def reboot():
o = b''.join(SysCommand("/usr/bin/reboot")) SysCommand("/usr/bin/reboot")
def pid_exists(pid: int): def pid_exists(pid: int):
try: try:

View File

@ -99,9 +99,13 @@ def has_wifi() -> bool:
def has_cpu_vendor(vendor_id: str) -> bool: def has_cpu_vendor(vendor_id: str) -> bool:
return any(cpu.get("vendor_id") == vendor_id for cpu in cpuinfo()) return any(cpu.get("vendor_id") == vendor_id for cpu in cpuinfo())
has_amd_cpu = partial(has_cpu_vendor, "AuthenticAMD") has_amd_cpu = partial(has_cpu_vendor, "AuthenticAMD")
has_intel_cpu = partial(has_cpu_vendor, "GenuineIntel") has_intel_cpu = partial(has_cpu_vendor, "GenuineIntel")
def has_uefi() -> bool: def has_uefi() -> bool:
return os.path.isdir('/sys/firmware/efi') return os.path.isdir('/sys/firmware/efi')

View File

@ -1,15 +1,25 @@
import time import time
import logging
import os
import shutil
import shlex import shlex
from .disk import * import pathlib
from .hardware import * import subprocess
import glob
from .disk import get_partitions_in_use, Partition, find_partition
from .general import SysCommand
from .hardware import has_uefi, is_vm, cpu_vendor
from .locale_helpers import verify_keyboard_layout, verify_x11_keyboard_layout from .locale_helpers import verify_keyboard_layout, verify_x11_keyboard_layout
from .disk.helpers import get_mount_info from .disk.helpers import get_mount_info
from .mirrors import * from .mirrors import use_mirrors
from .plugins import plugins from .plugins import plugins
from .storage import storage from .storage import storage
from .user_interaction import * from .systemd import Boot
# from .user_interaction import *
from .output import log
from .profiles import Profile
from .disk.btrfs import create_subvolume, mount_subvolume from .disk.btrfs import create_subvolume, mount_subvolume
from .exceptions import DiskError, ServiceException from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError
# Any package that the Installer() is responsible for (optional and the default ones) # Any package that the Installer() is responsible for (optional and the default ones)
__packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "linux-zen", "linux-hardened"] __packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "linux-zen", "linux-hardened"]
@ -140,7 +150,7 @@ class Installer:
for mountpoint in sorted(mountpoints.keys()): for mountpoint in sorted(mountpoints.keys()):
if mountpoints[mountpoint]['encrypted']: if mountpoints[mountpoint]['encrypted']:
loopdev = storage.get('ENC_IDENTIFIER', 'ai')+'loop' loopdev = storage.get('ENC_IDENTIFIER', 'ai') + 'loop'
password = mountpoints[mountpoint]['password'] password = mountpoints[mountpoint]['password']
with luks2(mountpoints[mountpoint]['device_instance'], loopdev, password, auto_unmount=False) as unlocked_device: with luks2(mountpoints[mountpoint]['device_instance'], loopdev, password, auto_unmount=False) as unlocked_device:
unlocked_device.mount(f"{self.target}{mountpoint}") unlocked_device.mount(f"{self.target}{mountpoint}")
@ -199,7 +209,7 @@ class Installer:
self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO) self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO)
with open(f"{self.target}/etc/fstab", 'a') as fstab_fh: with open(f"{self.target}/etc/fstab", 'a') as fstab_fh:
fstab_fh.write(SysCommand(f'/usr/bin/genfstab {flags} {self.target}').decode()) fstab_fh.write((fstab := SysCommand(f'/usr/bin/genfstab {flags} {self.target}')).decode())
if not os.path.isfile(f'{self.target}/etc/fstab'): if not os.path.isfile(f'{self.target}/etc/fstab'):
raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n{fstab}') raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n{fstab}')
@ -248,10 +258,20 @@ class Installer:
) )
def activate_ntp(self): def activate_ntp(self):
self.log('Installing and activating NTP.', level=logging.INFO) log(f"activate_ntp() is deprecated, use activate_time_syncronization()", fg="yellow", level=logging.INFO)
if self.pacstrap('ntp'): self.activate_time_syncronization()
if self.enable_service('ntpd'):
return True def activate_time_syncronization(self):
self.log('Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers.', level=logging.INFO)
self.enable_service('systemd-timesyncd')
with open(f"{self.target}/etc/systemd/timesyncd.conf", "w") as fh:
fh.write("[Time]\n")
fh.write("NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org\n")
fh.write("FallbackNTP=0.pool.ntp.org 1.pool.ntp.org 0.fr.pool.ntp.org\n")
with Boot(self) as session:
session.SysCommand(["timedatectl", "set-ntp", 'true'])
def enable_service(self, *services): def enable_service(self, *services):
for service in services: for service in services:
@ -555,7 +575,7 @@ class Installer:
self.helper_flags['bootloader'] = True self.helper_flags['bootloader'] = True
return True return True
else: else:
boot_partition = filesystem.find_partition(mountpoint=f"{self.target}/boot") boot_partition = find_partition(mountpoint=f"{self.target}/boot")
SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --target=i386-pc --recheck {boot_partition.path}') SysCommand(f'/usr/bin/arch-chroot {self.target} grub-install --target=i386-pc --recheck {boot_partition.path}')
SysCommand(f'/usr/bin/arch-chroot {self.target} grub-mkconfig -o /boot/grub/grub.cfg') SysCommand(f'/usr/bin/arch-chroot {self.target} grub-mkconfig -o /boot/grub/grub.cfg')
self.helper_flags['bootloader'] = True self.helper_flags['bootloader'] = True
@ -596,14 +616,14 @@ class Installer:
if not handled_by_plugin: if not handled_by_plugin:
self.log(f'Creating user {user}', level=logging.INFO) self.log(f'Creating user {user}', level=logging.INFO)
o = b''.join(SysCommand(f'/usr/bin/arch-chroot {self.target} useradd -m -G wheel {user}')) SysCommand(f'/usr/bin/arch-chroot {self.target} useradd -m -G wheel {user}')
if password: if password:
self.user_set_pw(user, password) self.user_set_pw(user, password)
if groups: if groups:
for group in groups: for group in groups:
o = b''.join(SysCommand(f'/usr/bin/arch-chroot {self.target} gpasswd -a {user} {group}')) SysCommand(f'/usr/bin/arch-chroot {self.target} gpasswd -a {user} {group}')
if sudo and self.enable_sudo(user): if sudo and self.enable_sudo(user):
self.helper_flags['user'] = True self.helper_flags['user'] = True
@ -615,14 +635,12 @@ class Installer:
# This means the root account isn't locked/disabled with * in /etc/passwd # This means the root account isn't locked/disabled with * in /etc/passwd
self.helper_flags['user'] = True self.helper_flags['user'] = True
o = b''.join(SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{user}:{password}' | chpasswd\"")) SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{user}:{password}' | chpasswd\"")
pass
def user_set_shell(self, user, shell): def user_set_shell(self, user, shell):
self.log(f'Setting shell for {user} to {shell}', level=logging.INFO) self.log(f'Setting shell for {user} to {shell}', level=logging.INFO)
o = b''.join(SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"chsh -s {shell} {user}\"")) SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"chsh -s {shell} {user}\"")
pass
def set_keyboard_language(self, language: str) -> bool: def set_keyboard_language(self, language: str) -> bool:
if len(language.strip()): if len(language.strip()):

View File

@ -1,9 +1,14 @@
import json
import logging
import os
import pathlib import pathlib
import shlex
import time
from .disk import Partition from .disk import Partition
from .general import * from .general import SysCommand
from .output import log from .output import log
from .exceptions import SysCallError, DiskError
class luks2: class luks2:
def __init__(self, partition, mountpoint, password, key_file=None, auto_unmount=False, *args, **kwargs): def __init__(self, partition, mountpoint, password, key_file=None, auto_unmount=False, *args, **kwargs):

View File

@ -1,8 +1,9 @@
import logging
import urllib.error import urllib.error
import urllib.request import urllib.request
from typing import Union, Mapping, Iterable from typing import Union, Mapping, Iterable
from .general import * from .general import SysCommand
from .output import log from .output import log
def sort_mirrorlist(raw_data :bytes, sort_order=["https", "http"]) -> bytes: def sort_mirrorlist(raw_data :bytes, sort_order=["https", "http"]) -> bytes:
@ -26,7 +27,7 @@ def sort_mirrorlist(raw_data :bytes, sort_order=["https", "http"]) -> bytes:
""" """
comments_and_whitespaces = b"" comments_and_whitespaces = b""
categories = {key: [] for key in sort_order+["Unknown"]} categories = {key: [] for key in sort_order + ["Unknown"]}
for line in raw_data.split(b"\n"): for line in raw_data.split(b"\n"):
if line[0:2] in (b'##', b''): if line[0:2] in (b'##', b''):
comments_and_whitespaces += line + b'\n' comments_and_whitespaces += line + b'\n'
@ -35,16 +36,15 @@ def sort_mirrorlist(raw_data :bytes, sort_order=["https", "http"]) -> bytes:
opening, url = opening.strip(), url.strip() opening, url = opening.strip(), url.strip()
if (category := url.split(b'://',1)[0].decode('UTF-8')) in categories: if (category := url.split(b'://',1)[0].decode('UTF-8')) in categories:
categories[category].append(comments_and_whitespaces) categories[category].append(comments_and_whitespaces)
categories[category].append(opening+b' = '+url+b'\n') categories[category].append(opening + b' = ' + url + b'\n')
else: else:
categories["Unknown"].append(comments_and_whitespaces) categories["Unknown"].append(comments_and_whitespaces)
categories["Unknown"].append(opening+b' = '+url+b'\n') categories["Unknown"].append(opening + b' = ' + url + b'\n')
comments_and_whitespaces = b"" comments_and_whitespaces = b""
new_raw_data = b'' new_raw_data = b''
for category in sort_order+["Unknown"]: for category in sort_order + ["Unknown"]:
for line in categories[category]: for line in categories[category]:
new_raw_data += line new_raw_data += line
@ -115,7 +115,7 @@ def insert_mirrors(mirrors, *args, **kwargs):
def use_mirrors( def use_mirrors(
regions: Mapping[str, Iterable[str]], regions: Mapping[str, Iterable[str]],
destination: str ='/etc/pacman.d/mirrorlist' destination: str = '/etc/pacman.d/mirrorlist'
) -> None: ) -> None:
log(f'A new package mirror-list has been created: {destination}', level=logging.INFO) log(f'A new package mirror-list has been created: {destination}', level=logging.INFO)
with open(destination, 'w') as mirrorlist: with open(destination, 'w') as mirrorlist:

View File

@ -4,7 +4,7 @@ import socket
import struct import struct
from collections import OrderedDict from collections import OrderedDict
from .exceptions import * from .exceptions import HardwareIncompatibilityError
from .general import SysCommand from .general import SysCommand
from .output import log from .output import log
from .storage import storage from .storage import storage
@ -30,7 +30,7 @@ def list_interfaces(skip_loopback=True):
def check_mirror_reachable(): def check_mirror_reachable():
log("Testing connectivity to the Arch Linux mirrors ...", level=logging.INFO) log("Testing connectivity to the Arch Linux mirrors ...", level=logging.INFO)
if (exit_code := SysCommand("pacman -Sy").exit_code) == 0: if SysCommand("pacman -Sy").exit_code == 0:
return True return True
elif os.geteuid() != 0: elif os.geteuid() != 0:
log("check_mirror_reachable() uses 'pacman -Sy' which requires root.", level=logging.ERROR, fg="red") log("check_mirror_reachable() uses 'pacman -Sy' which requires root.", level=logging.ERROR, fg="red")

View File

@ -4,7 +4,7 @@ import urllib.error
import urllib.parse import urllib.parse
import urllib.request import urllib.request
from .exceptions import * from .exceptions import RequirementError
BASE_URL = 'https://archlinux.org/packages/search/json/?name={package}' BASE_URL = 'https://archlinux.org/packages/search/json/?name={package}'
BASE_GROUP_URL = 'https://archlinux.org/groups/x86_64/{group}/' BASE_GROUP_URL = 'https://archlinux.org/groups/x86_64/{group}/'

View File

@ -65,7 +65,7 @@ def import_via_path(path :str, namespace=None): # -> module (not sure how to wri
def find_nth(haystack, needle, n): def find_nth(haystack, needle, n):
start = haystack.find(needle) start = haystack.find(needle)
while start >= 0 and n > 1: while start >= 0 and n > 1:
start = haystack.find(needle, start+len(needle)) start = haystack.find(needle, start + len(needle))
n -= 1 n -= 1
return start return start

View File

@ -1,6 +1,7 @@
import hashlib import hashlib
import importlib.util import importlib.util
import json import json
import os
import re import re
import ssl import ssl
import sys import sys
@ -10,8 +11,9 @@ import urllib.request
from typing import Optional from typing import Optional
from .general import multisplit from .general import multisplit
from .networking import * from .networking import list_interfaces
from .storage import storage from .storage import storage
from .exceptions import ProfileNotFound
def grab_url_data(path): def grab_url_data(path):

View File

@ -1,4 +1,5 @@
from .general import * import os
from .general import SysCommand
def service_state(service_name: str): def service_state(service_name: str):

View File

@ -4,7 +4,7 @@ import os
# 1. In the git repository, where ./profiles/ exist # 1. In the git repository, where ./profiles/ exist
# 2. When executing from a remote directory, but targeted a script that starts from the git repository # 2. When executing from a remote directory, but targeted a script that starts from the git repository
# 3. When executing as a python -m archinstall module where profiles exist one step back for library reasons. # 3. When executing as a python -m archinstall module where profiles exist one step back for library reasons.
# (4. Added the ~/.config directory as a additional option for future reasons) # (4. Added the ~/.config directory as an additional option for future reasons)
# #
# And Keeping this in dict ensures that variables are shared across imports. # And Keeping this in dict ensures that variables are shared across imports.
storage = { storage = {

View File

@ -10,14 +10,13 @@ import sys
import time import time
from .disk import BlockDevice, valid_fs_type, find_partition_by_mountpoint, suggest_single_disk_layout, suggest_multi_disk_layout, valid_parted_position from .disk import BlockDevice, valid_fs_type, find_partition_by_mountpoint, suggest_single_disk_layout, suggest_multi_disk_layout, valid_parted_position
from .exceptions import * from .exceptions import RequirementError, UserError, DiskError
from .general import SysCommand
from .hardware import AVAILABLE_GFX_DRIVERS, has_uefi, has_amd_graphics, has_intel_graphics, has_nvidia_graphics from .hardware import AVAILABLE_GFX_DRIVERS, has_uefi, has_amd_graphics, has_intel_graphics, has_nvidia_graphics
from .locale_helpers import list_keyboard_languages, verify_keyboard_layout, search_keyboard_layout from .locale_helpers import list_keyboard_languages, verify_keyboard_layout, search_keyboard_layout
from .networking import list_interfaces from .networking import list_interfaces
from .output import log from .output import log
from .profiles import Profile, list_profiles from .profiles import Profile, list_profiles
from .storage import * from .storage import storage
# TODO: Some inconsistencies between the selection processes. # TODO: Some inconsistencies between the selection processes.
# Some return the keys from the options, some the values? # Some return the keys from the options, some the values?
@ -201,11 +200,11 @@ def select_encrypted_partitions(block_devices :dict, password :str) -> dict:
return block_devices return block_devices
# TODO: Next version perhaps we can support multiple encrypted partitions # TODO: Next version perhaps we can support multiple encrypted partitions
#options = [] # options = []
#for partition in block_devices.values(): # for partition in block_devices.values():
# options.append({key: val for key, val in partition.items() if val}) # options.append({key: val for key, val in partition.items() if val})
#print(generic_multi_select(options, f"Choose which partitions to encrypt (leave blank when done): ")) # print(generic_multi_select(options, f"Choose which partitions to encrypt (leave blank when done): "))
class MiniCurses: class MiniCurses:
def __init__(self, width, height): def __init__(self, width, height):
@ -567,10 +566,10 @@ def get_default_partition_layout(block_devices):
# TODO: Implement sane generic layout for 2+ drives # TODO: Implement sane generic layout for 2+ drives
def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict: def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict:
if has_uefi(): # if has_uefi():
partition_type = 'gpt' # partition_type = 'gpt'
else: # else:
partition_type = 'msdos' # partition_type = 'msdos'
# log(f"Selecting which partitions to re-use on {block_device}...", fg="yellow", level=logging.INFO) # log(f"Selecting which partitions to re-use on {block_device}...", fg="yellow", level=logging.INFO)
# partitions = generic_multi_select(block_device.partitions.values(), "Select which partitions to re-use (the rest will be left alone): ", sort=True) # partitions = generic_multi_select(block_device.partitions.values(), "Select which partitions to re-use (the rest will be left alone): ", sort=True)
@ -600,7 +599,6 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict:
# return struct # return struct
mountpoints = {}
block_device_struct = { block_device_struct = {
"partitions" : [partition.__dump__() for partition in block_device.partitions.values()] "partitions" : [partition.__dump__() for partition in block_device.partitions.values()]
} }
@ -634,10 +632,10 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict:
break break
if task == 'Create a new partition': if task == 'Create a new partition':
if partition_type == 'gpt': # if partition_type == 'gpt':
# https://www.gnu.org/software/parted/manual/html_node/mkpart.html # # https://www.gnu.org/software/parted/manual/html_node/mkpart.html
# https://www.gnu.org/software/parted/manual/html_node/mklabel.html # # https://www.gnu.org/software/parted/manual/html_node/mklabel.html
name = input("Enter a desired name for the partition: ").strip() # name = input("Enter a desired name for the partition: ").strip()
fstype = input("Enter a desired filesystem type for the partition: ").strip() fstype = input("Enter a desired filesystem type for the partition: ").strip()
@ -674,7 +672,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict:
if input(f"{block_device} contains queued partitions, this will remove those, are you sure? y/N: ").strip().lower() in ('', 'n'): if input(f"{block_device} contains queued partitions, this will remove those, are you sure? y/N: ").strip().lower() in ('', 'n'):
continue continue
block_device_struct.update( suggest_single_disk_layout(block_device)[block_device.path] ) block_device_struct.update(suggest_single_disk_layout(block_device)[block_device.path])
elif task is None: elif task is None:
return block_device_struct return block_device_struct
else: else:

View File

@ -3,10 +3,10 @@
Discord Discord
======= =======
There's a discord channel which is frequent by some `contributors <https://github.com/archlinux/archinstall/graphs/contributors>`_. There's a discord channel which is frequented by some contributors <https://github.com/archlinux/archinstall/graphs/contributors>`_.
To join the server, head over to `https://discord.gg/cqXU88y <https://discord.gg/cqXU88y>`_'s server and join in. To join the server, head over to `https://discord.gg/cqXU88y <https://discord.gg/cqXU88y>`_'s server and join in.
There's not many rules other than common sense and treat others with respect. There's not many rules other than common sense and to treat others with respect.
There's the `@Party Animals` role if you want notifications of new releases which is posted in the `#Release Party` channel. There's the `@Party Animals` role if you want notifications of new releases which is posted in the `#Release Party` channel.
Another thing is the `@Contributors` role which you can get by writing `!verify` and verify that you're a contributor. Another thing is the `@Contributors` role which you can get by writing `!verify` and verify that you're a contributor.

View File

@ -5,10 +5,6 @@ import pathlib
import time import time
import archinstall import archinstall
from archinstall.lib.general import run_custom_user_commands
from archinstall.lib.hardware import *
from archinstall.lib.networking import check_mirror_reachable
from archinstall.lib.profiles import Profile, is_desktop_profile
if archinstall.arguments.get('help'): if archinstall.arguments.get('help'):
print("See `man archinstall` for help.") print("See `man archinstall` for help.")
@ -51,7 +47,7 @@ def load_config():
if archinstall.arguments.get('sys-encoding', None) is not None: if archinstall.arguments.get('sys-encoding', None) is not None:
archinstall.arguments['sys-encoding'] = archinstall.arguments.get('sys-encoding', 'utf-8') archinstall.arguments['sys-encoding'] = archinstall.arguments.get('sys-encoding', 'utf-8')
if archinstall.arguments.get('gfx_driver', None) is not None: if archinstall.arguments.get('gfx_driver', None) is not None:
archinstall.storage['gfx_driver_packages'] = AVAILABLE_GFX_DRIVERS.get(archinstall.arguments.get('gfx_driver', None), None) archinstall.storage['gfx_driver_packages'] = archinstall.hardware.AVAILABLE_GFX_DRIVERS.get(archinstall.arguments.get('gfx_driver', None), None)
if archinstall.arguments.get('servers', None) is not None: if archinstall.arguments.get('servers', None) is not None:
archinstall.storage['_selected_servers'] = archinstall.arguments.get('servers', None) archinstall.storage['_selected_servers'] = archinstall.arguments.get('servers', None)
if archinstall.arguments.get('disk_layouts', None) is not None: if archinstall.arguments.get('disk_layouts', None) is not None:
@ -81,13 +77,11 @@ def ask_user_questions():
except archinstall.RequirementError as err: except archinstall.RequirementError as err:
archinstall.log(err, fg="red") archinstall.log(err, fg="red")
# Before continuing, set the preferred keyboard layout/language in the current terminal. # Before continuing, set the preferred keyboard layout/language in the current terminal.
# This will just help the user with the next following questions. # This will just help the user with the next following questions.
if len(archinstall.arguments['keyboard-layout']): if len(archinstall.arguments['keyboard-layout']):
archinstall.set_keyboard_language(archinstall.arguments['keyboard-layout']) archinstall.set_keyboard_language(archinstall.arguments['keyboard-layout'])
# Set which region to download packages from during the installation # Set which region to download packages from during the installation
if not archinstall.arguments.get('mirror-region', None): if not archinstall.arguments.get('mirror-region', None):
while True: while True:
@ -132,30 +126,25 @@ def ask_user_questions():
if not archinstall.arguments.get("bootloader", None): if not archinstall.arguments.get("bootloader", None):
archinstall.arguments["bootloader"] = archinstall.ask_for_bootloader() archinstall.arguments["bootloader"] = archinstall.ask_for_bootloader()
# Get the hostname for the machine # Get the hostname for the machine
if not archinstall.arguments.get('hostname', None): if not archinstall.arguments.get('hostname', None):
archinstall.arguments['hostname'] = input('Desired hostname for the installation: ').strip(' ') archinstall.arguments['hostname'] = input('Desired hostname for the installation: ').strip(' ')
# Ask for a root password (optional, but triggers requirement for super-user if skipped) # Ask for a root password (optional, but triggers requirement for super-user if skipped)
if not archinstall.arguments.get('!root-password', None): if not archinstall.arguments.get('!root-password', None):
archinstall.arguments['!root-password'] = archinstall.get_password(prompt='Enter root password (leave blank to disable disabled & create superuser): ') archinstall.arguments['!root-password'] = archinstall.get_password(prompt='Enter root password (leave blank to disable disabled & create superuser): ')
# Ask for additional users (super-user if root pw was not set) # Ask for additional users (super-user if root pw was not set)
if not archinstall.arguments.get('!root-password', None) and not archinstall.arguments.get('superusers', None): if not archinstall.arguments.get('!root-password', None) and not archinstall.arguments.get('superusers', None):
archinstall.arguments['superusers'] = archinstall.ask_for_superuser_account('Create a required super-user with sudo privileges: ', forced=True) archinstall.arguments['superusers'] = archinstall.ask_for_superuser_account('Create a required super-user with sudo privileges: ', forced=True)
users, superusers = archinstall.ask_for_additional_users('Enter a username to create a additional user (leave blank to skip & continue): ') users, superusers = archinstall.ask_for_additional_users('Enter a username to create an additional user (leave blank to skip & continue): ')
archinstall.arguments['users'] = users archinstall.arguments['users'] = users
archinstall.arguments['superusers'] = {**archinstall.arguments['superusers'], **superusers} archinstall.arguments['superusers'] = {**archinstall.arguments['superusers'], **superusers}
# Ask for archinstall-specific profiles (such as desktop environments etc) # Ask for archinstall-specific profiles (such as desktop environments etc)
if not archinstall.arguments.get('profile', None): if not archinstall.arguments.get('profile', None):
archinstall.arguments['profile'] = archinstall.select_profile() archinstall.arguments['profile'] = archinstall.select_profile()
# Check the potentially selected profiles preparations to get early checks if some additional questions are needed. # Check the potentially selected profiles preparations to get early checks if some additional questions are needed.
if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_prep_function(): if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_prep_function():
with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported: with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported:
@ -163,19 +152,16 @@ def ask_user_questions():
archinstall.log(' * Profile\'s preparation requirements was not fulfilled.', fg='red') archinstall.log(' * Profile\'s preparation requirements was not fulfilled.', fg='red')
exit(1) exit(1)
# Ask about audio server selection if one is not already set # Ask about audio server selection if one is not already set
if not archinstall.arguments.get('audio', None): if not archinstall.arguments.get('audio', None):
# The argument to ask_for_audio_selection lets the library know if it's a desktop profile # The argument to ask_for_audio_selection lets the library know if it's a desktop profile
archinstall.arguments['audio'] = archinstall.ask_for_audio_selection(is_desktop_profile(archinstall.arguments['profile'])) archinstall.arguments['audio'] = archinstall.ask_for_audio_selection(archinstall.profiles.is_desktop_profile(archinstall.arguments['profile']))
# Ask for preferred kernel: # Ask for preferred kernel:
if not archinstall.arguments.get("kernels", None): if not archinstall.arguments.get("kernels", None):
kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"] kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"]
archinstall.arguments['kernels'] = archinstall.select_kernel(kernels) archinstall.arguments['kernels'] = archinstall.select_kernel(kernels)
# Additional packages (with some light weight error handling for invalid package names) # Additional packages (with some light weight error handling for invalid package names)
print("Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.") print("Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.")
print("If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.") print("If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.")
@ -246,7 +232,7 @@ def perform_filesystem_operations():
Once that's done, we'll hand over to perform_installation() Once that's done, we'll hand over to perform_installation()
""" """
mode = archinstall.GPT mode = archinstall.GPT
if has_uefi() is False: if archinstall.has_uefi() is False:
mode = archinstall.MBR mode = archinstall.MBR
for drive in archinstall.arguments['harddrives']: for drive in archinstall.arguments['harddrives']:
@ -279,7 +265,7 @@ def perform_installation(mountpoint):
installation.set_hostname(archinstall.arguments['hostname']) installation.set_hostname(archinstall.arguments['hostname'])
if archinstall.arguments['mirror-region'].get("mirrors", None) is not None: if archinstall.arguments['mirror-region'].get("mirrors", None) is not None:
installation.set_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium installation.set_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium
if archinstall.arguments["bootloader"] == "grub-install" and has_uefi(): if archinstall.arguments["bootloader"] == "grub-install" and archinstall.has_uefi():
installation.add_additional_packages("grub") installation.add_additional_packages("grub")
installation.add_bootloader(archinstall.arguments["bootloader"]) installation.add_bootloader(archinstall.arguments["bootloader"])
@ -324,7 +310,7 @@ def perform_installation(mountpoint):
installation.set_timezone(timezone) installation.set_timezone(timezone)
if archinstall.arguments.get('ntp', False): if archinstall.arguments.get('ntp', False):
installation.activate_ntp() installation.activate_time_syncronization()
if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw): if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw):
installation.user_set_pw('root', root_pw) installation.user_set_pw('root', root_pw)
@ -346,7 +332,7 @@ def perform_installation(mountpoint):
# If the user provided custom commands to be run post-installation, execute them now. # If the user provided custom commands to be run post-installation, execute them now.
if archinstall.arguments.get('custom-commands', None): if archinstall.arguments.get('custom-commands', None):
run_custom_user_commands(archinstall.arguments['custom-commands'], installation) archinstall.run_custom_user_commands(archinstall.arguments['custom-commands'], installation)
installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow") installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow")
if not archinstall.arguments.get('silent'): if not archinstall.arguments.get('silent'):
@ -361,7 +347,7 @@ def perform_installation(mountpoint):
archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=logging.DEBUG) archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=logging.DEBUG)
if not check_mirror_reachable(): if not archinstall.check_mirror_reachable():
log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None)) log_file = os.path.join(archinstall.storage.get('LOG_PATH', None), archinstall.storage.get('LOG_FILE', None))
archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red") archinstall.log(f"Arch Linux mirrors are not reachable. Please check your internet connection and the log file '{log_file}'.", level=logging.INFO, fg="red")
exit(1) exit(1)