index : archinstall32 | |
Archlinux32 installer | gitolite user |
summaryrefslogtreecommitdiff |
author | Anton Hvornum <anton@hvornum.se> | 2021-11-05 16:52:19 +0100 |
---|---|---|
committer | Anton Hvornum <anton@hvornum.se> | 2021-11-05 16:52:19 +0100 |
commit | f234b631691e7dcd587243e09c787139bc56fe13 (patch) | |
tree | 435e7bd6f6796a4404e67d9f7b1a210bdeb22b4d | |
parent | 5db146a0eb017c240f68579fc58ff825300352cf (diff) | |
parent | 0071a069080732047e11309134869f3ab40c642c (diff) |
@@ -1,2 +1,9 @@ [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 diff --git a/.github/workflows/lint-python.yaml b/.github/workflows/lint-python.yaml index 6e6ab071..d4b92f6e 100644 --- a/.github/workflows/lint-python.yaml +++ b/.github/workflows/lint-python.yaml @@ -20,11 +20,7 @@ jobs: - run: python -m pip install --upgrade pip - run: pip install flake8 - name: Lint with flake8 - run: | - # 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 + run: flake8 # See the .flake8 file for runtime parameters pytest: runs-on: ubuntu-latest container: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a92cff91..ca4aa9e3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,7 @@ # Contributing to archinstall Any contributions through pull requests are welcome as this project aims to be a community based project to ease some Arch Linux installation steps. -Bear in mind that in the future this repo might be transferred to the official [GitLab repo under Arch Linux](http://gitlab.archlinux.org/archlinux/) - *(if GitLab becomes open to the general public)*. +Bear in mind that in the future this repo might be transferred to the official [GitLab repo under Arch Linux](http://gitlab.archlinux.org/archlinux/) *(if GitLab becomes open to the general public)*. Therefore, guidelines and style changes to the code might come into effect as well as guidelines surrounding bug reporting and discussions. @@ -106,7 +106,6 @@ with archinstall.Installer('/mnt') as installation: installation.user_create('devel', 'devel') installation.user_set_pw('root', 'airoot') - ``` This installer will perform the following: diff --git a/archinstall/lib/disk/__init__.py b/archinstall/lib/disk/__init__.py index 352d04b9..bb6eb815 100644 --- a/archinstall/lib/disk/__init__.py +++ b/archinstall/lib/disk/__init__.py @@ -4,4 +4,4 @@ from .blockdevice import BlockDevice from .filesystem import Filesystem, MBR, GPT from .partition import * from .user_guides import * -from .validators import *
\ No newline at end of file +from .validators import * diff --git a/archinstall/lib/disk/blockdevice.py b/archinstall/lib/disk/blockdevice.py index 57cbcfa6..fad04c7b 100644 --- a/archinstall/lib/disk/blockdevice.py +++ b/archinstall/lib/disk/blockdevice.py @@ -1,12 +1,17 @@ +# flake8: noqa +# The above ignore, see issue: https://github.com/archlinux/archinstall/pull/650#issuecomment-961995949 import os import json import logging +from .exceptions import DiskError +from .helpers import all_disks from ..output import log from ..general import SysCommand class BlockDevice: def __init__(self, path, info=None): if not info: + from .helpers import all_disks # If we don't give any information, we need to auto-fill it. # Otherwise any subsequent usage will break. info = all_disks()[path].info @@ -21,7 +26,7 @@ class BlockDevice: # I'm placing the encryption password on a BlockDevice level. def __repr__(self, *args, **kwargs): - return f"BlockDevice({self.device}, size={self.size}GB, free_space={'+'.join(part[2] for part in self.free_space)}, bus_type={self.bus_type})" + return f"BlockDevice({self.device_or_backfile}, size={self.size}GB, free_space={'+'.join(part[2] for part in self.free_space)}, bus_type={self.bus_type})" def __iter__(self): for partition in self.partitions: @@ -57,28 +62,38 @@ class BlockDevice: @property def partition_type(self): output = json.loads(SysCommand(f"lsblk --json -o+PTTYPE {self.path}").decode('UTF-8')) - + for device in output['blockdevices']: return device['pttype'] @property - def device(self): + def device_or_backfile(self): """ Returns the actual device-endpoint of the BlockDevice. If it's a loop-back-device it returns the back-file, - If it's a ATA-drive it returns the /dev/X device - And if it's a crypto-device it returns the parent device + For other types it return self.device """ - if "type" not in self.info: - raise DiskError(f'Could not locate backplane info for "{self.path}"') - if self.info['type'] == 'loop': for drive in json.loads(SysCommand(['losetup', '--json']).decode('UTF_8'))['loopdevices']: if not drive['name'] == self.path: continue return drive['back-file'] - elif self.info['type'] == 'disk': + else: + return self.device + + @property + def device(self): + """ + Returns the device file of the BlockDevice. + If it's a loop-back-device it returns the /dev/X device, + If it's a ATA-drive it returns the /dev/X device + And if it's a crypto-device it returns the parent device + """ + if "type" not in self.info: + raise DiskError(f'Could not locate backplane info for "{self.path}"') + + if self.info['type'] in ['disk','loop']: return self.path elif self.info['type'][:4] == 'raid': # This should catch /dev/md## raid devices @@ -154,21 +169,21 @@ class BlockDevice: @property def size(self): output = json.loads(SysCommand(f"lsblk --json -o+SIZE {self.path}").decode('UTF-8')) - + for device in output['blockdevices']: return self.convert_size_to_gb(device['size']) @property def bus_type(self): output = json.loads(SysCommand(f"lsblk --json -o+ROTA,TRAN {self.path}").decode('UTF-8')) - + for device in output['blockdevices']: return device['tran'] - + @property def spinning(self): output = json.loads(SysCommand(f"lsblk --json -o+ROTA,TRAN {self.path}").decode('UTF-8')) - + for device in output['blockdevices']: return device['rota'] is True @@ -210,4 +225,4 @@ class BlockDevice: def get_partition(self, uuid): for partition in self: if partition.uuid == uuid: - return partition
\ No newline at end of file + return partition diff --git a/archinstall/lib/disk/btrfs.py b/archinstall/lib/disk/btrfs.py index 6fafab34..7ae4f6a6 100644 --- a/archinstall/lib/disk/btrfs.py +++ b/archinstall/lib/disk/btrfs.py @@ -1,4 +1,5 @@ -import pathlib, glob +import pathlib +import glob import logging from typing import Union from .helpers import get_mount_info @@ -27,9 +28,9 @@ def mount_subvolume(installation, subvolume_location :Union[pathlib.Path, str], if not target.exists(): 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)") - + log(f"Mounting {target} as a subvolume", level=logging.INFO) # Mount the logical volume to the physical structure mount_information, mountpoint_device_real_path = get_mount_info(target, traverse=True, return_real_path=True) @@ -46,7 +47,7 @@ def create_subvolume(installation, subvolume_location :Union[pathlib.Path, str]) @installation: archinstall.Installer instance @subvolume_location: a localized string or path inside the installation / or /boot for instance without specifying /mnt/boot """ - + installation_mountpoint = installation.target if type(installation_mountpoint) == str: installation_mountpoint = pathlib.Path(installation_mountpoint) @@ -61,7 +62,7 @@ def create_subvolume(installation, subvolume_location :Union[pathlib.Path, str]) if not target.parent.exists(): 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)") # Remove the target if it exists @@ -70,4 +71,4 @@ def create_subvolume(installation, subvolume_location :Union[pathlib.Path, str]) log(f"Creating a subvolume on {target}", level=logging.INFO) if (cmd := SysCommand(f"btrfs subvolume create {target}")).exit_code != 0: - raise DiskError(f"Could not create a subvolume at {target}: {cmd}")
\ No newline at end of file + raise DiskError(f"Could not create a subvolume at {target}: {cmd}") diff --git a/archinstall/lib/disk/filesystem.py b/archinstall/lib/disk/filesystem.py index 0328cd83..69593d08 100644 --- a/archinstall/lib/disk/filesystem.py +++ b/archinstall/lib/disk/filesystem.py @@ -1,8 +1,9 @@ import time import logging import json +from .exceptions import DiskError from .partition import Partition -from .blockdevice import BlockDevice +from .validators import valid_fs_type from ..general import SysCommand from ..output import log from ..storage import storage @@ -55,7 +56,7 @@ class Filesystem: def partuuid_to_index(self, uuid): output = json.loads(SysCommand(f"lsblk --json -o+PARTUUID {self.blockdevice.device}").decode('UTF-8')) - + for device in output['blockdevices']: for index, partition in enumerate(device['children']): if partition['partuuid'].lower() == uuid: @@ -101,7 +102,7 @@ class Filesystem: partition['password'] = get_password(f"Enter a encryption password for {partition['device_instance']}") 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 storage['arguments'] == 'silent': raise ValueError(f"Missing fs-type to format on newly created encrypted partition {partition['device_instance']}") @@ -113,13 +114,13 @@ class Filesystem: while True: 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: - 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 break - unlocked_device.format(partition['filesystem']['format']) + unlocked_device.format(partition['filesystem']['format'], options=partition.get('options', [])) elif partition.get('format', False): - partition['device_instance'].format(partition['filesystem']['format']) + partition['device_instance'].format(partition['filesystem']['format'], options=partition.get('options', [])) if partition.get('boot', False): self.set(self.partuuid_to_index(partition['device_instance'].uuid), 'boot on') @@ -170,7 +171,6 @@ class Filesystem: raise DiskError(f"New partition never showed up after adding new partition on {self} (timeout 10 seconds).") time.sleep(0.025) - # Todo: Find a better way to detect if the new UUID of the partition has showed up. # 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) @@ -190,4 +190,4 @@ class Filesystem: SysCommand(f'bash -c "umount {device}?"') except: pass - return self.raw_parted(f'{device} mklabel {disk_label}').exit_code == 0
\ No newline at end of file + return self.raw_parted(f'{device} mklabel {disk_label}').exit_code == 0 diff --git a/archinstall/lib/disk/helpers.py b/archinstall/lib/disk/helpers.py index 341b732f..8e044ce3 100644 --- a/archinstall/lib/disk/helpers.py +++ b/archinstall/lib/disk/helpers.py @@ -1,10 +1,11 @@ import re +import os import json import logging import pathlib from typing import Union from .blockdevice import BlockDevice -from ..exceptions import SysCallError +from ..exceptions import SysCallError, DiskError from ..general import SysCommand from ..output import log @@ -199,4 +200,4 @@ def find_partition_by_mountpoint(block_devices, relative_mountpoint :str): for device in block_devices: for partition in block_devices[device]['partitions']: if partition.get('mountpoint', None) == relative_mountpoint: - return partition
\ No newline at end of file + return partition diff --git a/archinstall/lib/disk/partition.py b/archinstall/lib/disk/partition.py index 3bb2982b..afe46db3 100644 --- a/archinstall/lib/disk/partition.py +++ b/archinstall/lib/disk/partition.py @@ -4,9 +4,12 @@ import time import logging import json import os +import hashlib from typing import Optional from .blockdevice import BlockDevice +from .exceptions import DiskError, SysCallError, UnknownFilesystemFormat from .helpers import get_mount_info, get_filesystem_type +from .storage import storage from ..output import log from ..general import SysCommand @@ -82,14 +85,14 @@ class Partition: @property def sector_size(self): output = json.loads(SysCommand(f"lsblk --json -o+LOG-SEC {self.path}").decode('UTF-8')) - + for device in output['blockdevices']: return device.get('log-sec', None) @property def start(self): output = json.loads(SysCommand(f"sfdisk --json {self.block_device.path}").decode('UTF-8')) - + for partition in output.get('partitiontable', {}).get('partitions', []): if partition['node'] == self.path: return partition['start']# * self.sector_size @@ -173,7 +176,7 @@ class Partition: from .luks import luks2 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 except SysCallError: return None @@ -208,7 +211,7 @@ class Partition: handle = luks2(self, None, None) return handle.encrypt(self, *args, **kwargs) - def format(self, filesystem=None, path=None, log_formatting=True): + def format(self, filesystem=None, path=None, log_formatting=True, options=[]): """ Format can be given an overriding path, for instance /dev/null to test the formatting functionality and in essence the support for the given filesystem. @@ -228,33 +231,45 @@ class Partition: log(f'Formatting {path} -> {filesystem}', level=logging.INFO) if filesystem == 'btrfs': - if 'UUID:' not in (mkfs := SysCommand(f'/usr/bin/mkfs.btrfs -f {path}').decode('UTF-8')): + options = ['-f'] + options + + if 'UUID:' not in (mkfs := SysCommand(f"/usr/bin/mkfs.btrfs {' '.join(options)} {path}").decode('UTF-8')): raise DiskError(f'Could not format {path} with {filesystem} because: {mkfs}') self.filesystem = filesystem elif filesystem == 'fat32': - mkfs = SysCommand(f'/usr/bin/mkfs.vfat -F32 {path}').decode('UTF-8') + options = ['-F32'] + options + + mkfs = SysCommand(f"/usr/bin/mkfs.vfat {' '.join(options)} {path}").decode('UTF-8') if ('mkfs.fat' not in mkfs and 'mkfs.vfat' not in mkfs) or 'command not found' in mkfs: raise DiskError(f"Could not format {path} with {filesystem} because: {mkfs}") self.filesystem = filesystem elif filesystem == 'ext4': - if (handle := SysCommand(f'/usr/bin/mkfs.ext4 -F {path}')).exit_code != 0: + options = ['-F'] + options + + if (handle := SysCommand(f"/usr/bin/mkfs.ext4 {' '.join(options)} {path}")).exit_code != 0: raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}") self.filesystem = filesystem elif filesystem == 'ext2': - if (handle := SysCommand(f'/usr/bin/mkfs.ext2 -F {path}')).exit_code != 0: + options = ['-F'] + options + + if (handle := SysCommand(f"/usr/bin/mkfs.ext2 {' '.join(options)} {path}")).exit_code != 0: raise DiskError(f'Could not format {path} with {filesystem} because: {b"".join(handle)}') self.filesystem = 'ext2' elif filesystem == 'xfs': - if (handle := SysCommand(f'/usr/bin/mkfs.xfs -f {path}')).exit_code != 0: + options = ['-f'] + options + + if (handle := SysCommand(f"/usr/bin/mkfs.xfs {' '.join(options)} {path}")).exit_code != 0: raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}") self.filesystem = filesystem elif filesystem == 'f2fs': - if (handle := SysCommand(f'/usr/bin/mkfs.f2fs -f {path}')).exit_code != 0: + options = ['-f'] + options + + if (handle := SysCommand(f"/usr/bin/mkfs.f2fs {' '.join(options)} {path}")).exit_code != 0: raise DiskError(f"Could not format {path} with {filesystem} because: {handle.decode('UTF-8')}") self.filesystem = filesystem @@ -334,4 +349,4 @@ class Partition: pass # We supported it, but /dev/null is not formatable as expected so the mkfs call exited with an error code except UnknownFilesystemFormat as err: raise err - return True
\ No newline at end of file + return True diff --git a/archinstall/lib/disk/user_guides.py b/archinstall/lib/disk/user_guides.py index 93013fbc..5354fe2a 100644 --- a/archinstall/lib/disk/user_guides.py +++ b/archinstall/lib/disk/user_guides.py @@ -1,4 +1,5 @@ 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 def suggest_single_disk_layout(block_device, default_filesystem=None): @@ -55,7 +56,7 @@ def suggest_single_disk_layout(block_device, default_filesystem=None): } } else: - pass #... implement a guided setup + pass # ... implement a guided setup elif block_device.size >= MIN_SIZE_TO_ALLOW_HOME_PART: # If we don't want to use subvolumes, @@ -146,4 +147,4 @@ def suggest_multi_disk_layout(block_devices, default_filesystem=None): } }) - return layout
\ No newline at end of file + return layout diff --git a/archinstall/lib/disk/validators.py b/archinstall/lib/disk/validators.py index 0c9f5a74..e0ab6a86 100644 --- a/archinstall/lib/disk/validators.py +++ b/archinstall/lib/disk/validators.py @@ -39,4 +39,4 @@ def valid_fs_type(fstype :str) -> bool: "reiserfs", "udf", # "ufs", not included in `man parted` "xfs", # `man parted` allows this - ]
\ No newline at end of file + ] diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 1e8fc837..887a60d3 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -14,6 +14,7 @@ except: import select EPOLLIN = 0 EPOLLHUP = 0 + class epoll(): """ #!if windows Create a epoll() implementation that simulates the epoll() behavior. @@ -38,7 +39,7 @@ except: except OSError: return [] -from .exceptions import * +from .exceptions import RequirementError, SysCallError from .output import log from .storage import storage @@ -241,7 +242,7 @@ class SysCommandWorker: got_output = True self.peak(output) self._trace_log += output - except OSError as err: + except OSError: self.ended = time.time() break @@ -379,8 +380,7 @@ def prerequisite_check(): def reboot(): - o = b''.join(SysCommand("/usr/bin/reboot")) - + SysCommand("/usr/bin/reboot") def pid_exists(pid: int): try: diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index a8f87b80..cf99a530 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -1,4 +1,5 @@ import os +from functools import partial from pathlib import Path from typing import Iterator, Optional, Union @@ -75,12 +76,10 @@ def cpuinfo() -> Iterator[dict[str, str]]: cpu[key.strip()] = value.strip() -def meminfo(key: Optional[str] = None) -> Union[dict[str, int], int]: +def meminfo(key: Optional[str] = None) -> Union[dict[str, int], Optional[int]]: """Returns a dict with memory info if called with no args or the value of the given key of said dict. """ - mem_info = {} - with MEMINFO.open() as file: mem_info = { (columns := line.strip().split())[0].rstrip(':'): int(columns[1]) @@ -97,11 +96,15 @@ def has_wifi() -> bool: return 'WIRELESS' in enrich_iface_types(list_interfaces().values()).values() -def has_amd_cpu() -> bool: - return any(cpu.get("vendor_id") == "AuthenticAMD" for cpu in cpuinfo()) +def has_cpu_vendor(vendor_id: str) -> bool: + return any(cpu.get("vendor_id") == vendor_id for cpu in cpuinfo()) + + +has_amd_cpu = partial(has_cpu_vendor, "AuthenticAMD") + + +has_intel_cpu = partial(has_cpu_vendor, "GenuineIntel") -def has_intel_cpu() -> bool: - return any(cpu.get("vendor_id") == "GenuineIntel" for cpu in cpuinfo()) def has_uefi() -> bool: return os.path.isdir('/sys/firmware/efi') @@ -152,15 +155,15 @@ def product_name() -> Optional[str]: return product.read().strip() -def mem_available() -> Optional[str]: +def mem_available() -> Optional[int]: return meminfo('MemAvailable') -def mem_free() -> Optional[str]: +def mem_free() -> Optional[int]: return meminfo('MemFree') -def mem_total() -> Optional[str]: +def mem_total() -> Optional[int]: return meminfo('MemTotal') diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 0bdddb2e..47a26d6d 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1,14 +1,23 @@ import time -from .disk import * -from .hardware import * +import logging +import os +import shutil +import pathlib +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 .disk.helpers import get_mount_info -from .mirrors import * +from .mirrors import use_mirrors from .plugins import plugins from .storage import storage -from .user_interaction import * +# from .user_interaction import * +from .output import log +from .profiles import Profile 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) __packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "linux-zen", "linux-hardened"] @@ -139,7 +148,7 @@ class Installer: for mountpoint in sorted(mountpoints.keys()): if mountpoints[mountpoint]['encrypted']: - loopdev = storage.get('ENC_IDENTIFIER', 'ai')+'loop' + loopdev = storage.get('ENC_IDENTIFIER', 'ai') + 'loop' password = mountpoints[mountpoint]['password'] with luks2(mountpoints[mountpoint]['device_instance'], loopdev, password, auto_unmount=False) as unlocked_device: unlocked_device.mount(f"{self.target}{mountpoint}") @@ -198,7 +207,7 @@ class Installer: self.log(f"Updating {self.target}/etc/fstab", level=logging.INFO) 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'): raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n{fstab}') @@ -554,7 +563,7 @@ class Installer: self.helper_flags['bootloader'] = True return True 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-mkconfig -o /boot/grub/grub.cfg') self.helper_flags['bootloader'] = True @@ -595,14 +604,14 @@ class Installer: if not handled_by_plugin: 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: self.user_set_pw(user, password) if 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): self.helper_flags['user'] = True @@ -614,14 +623,12 @@ class Installer: # This means the root account isn't locked/disabled with * in /etc/passwd self.helper_flags['user'] = True - o = b''.join(SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{user}:{password}' | chpasswd\"")) - pass + SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{user}:{password}' | chpasswd\"") def user_set_shell(self, user, shell): 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}\"")) - pass + SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"chsh -s {shell} {user}\"") def set_keyboard_language(self, language: str) -> bool: if len(language.strip()): diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index 781bed43..d10058ef 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -1,9 +1,14 @@ +import json +import logging +import os import pathlib +import shlex +import time from .disk import Partition -from .general import * +from .general import SysCommand from .output import log - +from .exceptions import SysCallError, DiskError class luks2: def __init__(self, partition, mountpoint, password, key_file=None, auto_unmount=False, *args, **kwargs): diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 1b62a61b..5fad6cb6 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -1,8 +1,9 @@ +import logging import urllib.error import urllib.request -from typing import Union +from typing import Union, Mapping, Iterable -from .general import * +from .general import SysCommand from .output import log 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"" - categories = {key: [] for key in sort_order+["Unknown"]} + categories = {key: [] for key in sort_order + ["Unknown"]} for line in raw_data.split(b"\n"): if line[0:2] in (b'##', b''): 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() if (category := url.split(b'://',1)[0].decode('UTF-8')) in categories: categories[category].append(comments_and_whitespaces) - categories[category].append(opening+b' = '+url+b'\n') + categories[category].append(opening + b' = ' + url + b'\n') else: 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"" - new_raw_data = b'' - for category in sort_order+["Unknown"]: + for category in sort_order + ["Unknown"]: for line in categories[category]: new_raw_data += line @@ -113,20 +113,29 @@ def insert_mirrors(mirrors, *args, **kwargs): return True -def use_mirrors(regions: dict, destination='/etc/pacman.d/mirrorlist'): +def use_mirrors( + regions: Mapping[str, Iterable[str]], + destination: str = '/etc/pacman.d/mirrorlist' +) -> None: log(f'A new package mirror-list has been created: {destination}', level=logging.INFO) - for region, mirrors in regions.items(): - with open(destination, 'w') as mirrorlist: + with open(destination, 'w') as mirrorlist: + for region, mirrors in regions.items(): for mirror in mirrors: mirrorlist.write(f'## {region}\n') mirrorlist.write(f'Server = {mirror}\n') - return True -def re_rank_mirrors(top=10, *positionals, **kwargs): - if SysCommand(f'/usr/bin/rankmirrors -n {top} /etc/pacman.d/mirrorlist > /etc/pacman.d/mirrorlist').exit_code == 0: - return True - return False +def re_rank_mirrors( + top: int = 10, + src: str = '/etc/pacman.d/mirrorlist', + dst: str = '/etc/pacman.d/mirrorlist', +) -> bool: + cmd = SysCommand(f"/usr/bin/rankmirrors -n {top} {src}") + if cmd.exit_code != 0: + return False + with open(dst, 'w') as f: + f.write(str(cmd)) + return True def list_mirrors(sort_order=["https", "http"]): diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py index ded5ebe6..0d94572a 100644 --- a/archinstall/lib/networking.py +++ b/archinstall/lib/networking.py @@ -4,7 +4,7 @@ import socket import struct from collections import OrderedDict -from .exceptions import * +from .exceptions import HardwareIncompatibilityError from .general import SysCommand from .output import log from .storage import storage @@ -30,7 +30,7 @@ def list_interfaces(skip_loopback=True): def check_mirror_reachable(): 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 elif os.geteuid() != 0: log("check_mirror_reachable() uses 'pacman -Sy' which requires root.", level=logging.ERROR, fg="red") diff --git a/archinstall/lib/packages.py b/archinstall/lib/packages.py index 0178d257..ffc44cbe 100644 --- a/archinstall/lib/packages.py +++ b/archinstall/lib/packages.py @@ -4,7 +4,7 @@ import urllib.error import urllib.parse import urllib.request -from .exceptions import * +from .exceptions import RequirementError BASE_URL = 'https://archlinux.org/packages/search/json/?name={package}' BASE_GROUP_URL = 'https://archlinux.org/groups/x86_64/{group}/' diff --git a/archinstall/lib/plugins.py b/archinstall/lib/plugins.py index 24fbd8ee..027b58d5 100644 --- a/archinstall/lib/plugins.py +++ b/archinstall/lib/plugins.py @@ -65,7 +65,7 @@ def import_via_path(path :str, namespace=None): # -> module (not sure how to wri def find_nth(haystack, needle, n): start = haystack.find(needle) while start >= 0 and n > 1: - start = haystack.find(needle, start+len(needle)) + start = haystack.find(needle, start + len(needle)) n -= 1 return start @@ -98,4 +98,4 @@ def load_plugin(path :str): # -> module (not sure how to write that in type defi log(err, level=logging.ERROR) log(f"The above error was detected when initiating the plugin: {path}", fg="red", level=logging.ERROR) else: - log(f"Plugin '{path}' is missing a valid entry-point or is corrupt.", fg="yellow", level=logging.WARNING)
\ No newline at end of file + log(f"Plugin '{path}' is missing a valid entry-point or is corrupt.", fg="yellow", level=logging.WARNING) diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py index ebb08990..7d5373c5 100644 --- a/archinstall/lib/profiles.py +++ b/archinstall/lib/profiles.py @@ -1,6 +1,7 @@ import hashlib import importlib.util import json +import os import re import ssl import sys @@ -10,8 +11,9 @@ import urllib.request from typing import Optional from .general import multisplit -from .networking import * +from .networking import list_interfaces from .storage import storage +from .exceptions import ProfileNotFound def grab_url_data(path): diff --git a/archinstall/lib/services.py b/archinstall/lib/services.py index 6f8f2a87..d295bdbb 100644 --- a/archinstall/lib/services.py +++ b/archinstall/lib/services.py @@ -1,4 +1,5 @@ -from .general import * +import os +from .general import SysCommand def service_state(service_name: str): diff --git a/archinstall/lib/storage.py b/archinstall/lib/storage.py index 67f8e716..ae330382 100644 --- a/archinstall/lib/storage.py +++ b/archinstall/lib/storage.py @@ -4,7 +4,7 @@ import os # 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 # 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. storage = { diff --git a/archinstall/lib/systemd.py b/archinstall/lib/systemd.py index 383f1f17..d297c507 100644 --- a/archinstall/lib/systemd.py +++ b/archinstall/lib/systemd.py @@ -64,6 +64,7 @@ class Boot: self.session = SysCommandWorker([ '/usr/bin/systemd-nspawn', '-D', self.instance.target, + '--timezone=off', '-b', '--machine', self.container_name ]) diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 6854ccfd..6b142288 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -10,14 +10,13 @@ import sys 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 .exceptions import * -from .general import SysCommand +from .exceptions import RequirementError, UserError, DiskError 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 .networking import list_interfaces from .output import log from .profiles import Profile, list_profiles -from .storage import * +from .storage import storage # TODO: Some inconsistencies between the selection processes. # 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 # TODO: Next version perhaps we can support multiple encrypted partitions - #options = [] - #for partition in block_devices.values(): - # options.append({key: val for key, val in partition.items() if val}) + # options = [] + # for partition in block_devices.values(): + # 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: def __init__(self, width, height): @@ -567,22 +566,22 @@ def get_default_partition_layout(block_devices): # TODO: Implement sane generic layout for 2+ drives def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict: - if has_uefi(): - partition_type = 'gpt' - else: - partition_type = 'msdos' + # if has_uefi(): + # partition_type = 'gpt' + # else: + # partition_type = 'msdos' # 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_to_wipe = generic_multi_select(partitions, "Which partitions do you wish to wipe (multiple can be selected): ", sort=True) - + # mountpoints = {} # struct = { # "partitions" : [] # } # for partition in partitions: # mountpoint = input(f"Select a mountpoint (or skip) for {partition}: ").strip() - + # part_struct = {} # if mountpoint: # part_struct['mountpoint'] = mountpoint @@ -590,7 +589,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict: # part_struct['boot'] = True # if has_uefi(): # part_struct['ESP'] = True - # elif mountpoint == '/' and + # elif mountpoint == '/' and # if partition.uuid: # part_struct['PARTUUID'] = partition.uuid # if partition in partitions_to_wipe: @@ -600,7 +599,6 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict: # return struct - mountpoints = {} block_device_struct = { "partitions" : [partition.__dump__() for partition in block_device.partitions.values()] } @@ -632,15 +630,15 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict: if not task: break - + if task == 'Create a new partition': - if partition_type == 'gpt': - # https://www.gnu.org/software/parted/manual/html_node/mkpart.html - # https://www.gnu.org/software/parted/manual/html_node/mklabel.html - name = input("Enter a desired name for the partition: ").strip() - + # if partition_type == 'gpt': + # # https://www.gnu.org/software/parted/manual/html_node/mkpart.html + # # https://www.gnu.org/software/parted/manual/html_node/mklabel.html + # name = input("Enter a desired name for the partition: ").strip() + fstype = input("Enter a desired filesystem type for the partition: ").strip() - + start = input(f"Enter the start sector (percentage or block number, default: {block_device.largest_free_space[0]}): ").strip() if not start.strip(): start = block_device.largest_free_space[0] @@ -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'): continue - block_device_struct["partitions"] = suggest_single_disk_layout(block_device)[block_device] + block_device_struct.update(suggest_single_disk_layout(block_device)[block_device.path]) elif task is None: return block_device_struct else: @@ -730,7 +728,10 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict: block_device_struct["partitions"][block_device_struct["partitions"].index(partition)]['boot'] = not block_device_struct["partitions"][block_device_struct["partitions"].index(partition)].get('boot', False) elif task == "Set desired filesystem for a partition": - if (partition := generic_select(block_device_struct["partitions"], 'Select which partition to set a filesystem on: ', options_output=False)): + if not block_device_struct["partitions"]: + log("No partitions found. Create some partitions first", level=logging.WARNING, fg='yellow') + continue + elif (partition := generic_select(block_device_struct["partitions"], 'Select which partition to set a filesystem on: ', options_output=False)): if not block_device_struct["partitions"][block_device_struct["partitions"].index(partition)].get('filesystem', None): block_device_struct["partitions"][block_device_struct["partitions"].index(partition)]['filesystem'] = {} @@ -750,7 +751,7 @@ def select_individual_blockdevice_usage(block_devices :list): for device in block_devices: layout = manage_new_and_existing_partitions(device) - + result[device.path] = layout return result diff --git a/docs/help/discord.rst b/docs/help/discord.rst index ce15766a..73727c35 100644 --- a/docs/help/discord.rst +++ b/docs/help/discord.rst @@ -3,12 +3,12 @@ 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. -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. Another thing is the `@Contributors` role which you can get by writing `!verify` and verify that you're a contributor. -Hop in, I hope to see you there! : )
\ No newline at end of file +Hop in, I hope to see you there! : ) diff --git a/examples/guided.py b/examples/guided.py index f8791c9b..45395add 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -5,10 +5,6 @@ import pathlib import time 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'): print("See `man archinstall` for help.") @@ -51,7 +47,7 @@ def load_config(): if archinstall.arguments.get('sys-encoding', None) is not None: archinstall.arguments['sys-encoding'] = archinstall.arguments.get('sys-encoding', 'utf-8') 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: archinstall.storage['_selected_servers'] = archinstall.arguments.get('servers', None) if archinstall.arguments.get('disk_layouts', None) is not None: @@ -66,7 +62,7 @@ def load_config(): archinstall.storage['disk_layouts'] = json.loads(archinstall.arguments['disk_layouts']) except: raise ValueError("--disk_layouts=<json> needs either a JSON file or a JSON string given with a valid disk layout.") - + def ask_user_questions(): """ First, we'll ask the user for a bunch of user input. @@ -81,13 +77,11 @@ def ask_user_questions(): except archinstall.RequirementError as err: archinstall.log(err, fg="red") - # Before continuing, set the preferred keyboard layout/language in the current terminal. # This will just help the user with the next following questions. if len(archinstall.arguments['keyboard-layout']): archinstall.set_keyboard_language(archinstall.arguments['keyboard-layout']) - # Set which region to download packages from during the installation if not archinstall.arguments.get('mirror-region', None): while True: @@ -132,30 +126,25 @@ def ask_user_questions(): if not archinstall.arguments.get("bootloader", None): archinstall.arguments["bootloader"] = archinstall.ask_for_bootloader() - # Get the hostname for the machine if not archinstall.arguments.get('hostname', None): archinstall.arguments['hostname'] = input('Desired hostname for the installation: ').strip(' ') - # Ask for a root password (optional, but triggers requirement for super-user if skipped) 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): ') - # 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): 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['superusers'] = {**archinstall.arguments['superusers'], **superusers} - # Ask for archinstall-specific profiles (such as desktop environments etc) if not archinstall.arguments.get('profile', None): archinstall.arguments['profile'] = archinstall.select_profile() - # 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(): 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') exit(1) - # Ask about audio server selection if one is not already set if not archinstall.arguments.get('audio', None): # 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: if not archinstall.arguments.get("kernels", None): kernels = ["linux", "linux-lts", "linux-zen", "linux-hardened"] archinstall.arguments['kernels'] = archinstall.select_kernel(kernels) - # 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("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() """ mode = archinstall.GPT - if has_uefi() is False: + if archinstall.has_uefi() is False: mode = archinstall.MBR for drive in archinstall.arguments['harddrives']: @@ -279,7 +265,7 @@ def perform_installation(mountpoint): installation.set_hostname(archinstall.arguments['hostname']) 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 - 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_bootloader(archinstall.arguments["bootloader"]) @@ -346,7 +332,7 @@ def perform_installation(mountpoint): # If the user provided custom commands to be run post-installation, execute them now. 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") 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) -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)) 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) @@ -371,4 +357,4 @@ if not archinstall.arguments.get('silent'): ask_user_questions() perform_filesystem_operations() -perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt'))
\ No newline at end of file +perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt')) diff --git a/profiles/cutefish.py b/profiles/cutefish.py index 1df2467a..486fa389 100644 --- a/profiles/cutefish.py +++ b/profiles/cutefish.py @@ -7,7 +7,6 @@ is_top_level_profile = False __packages__ = [ "cutefish", "noto-fonts", - "konsole", "sddm" ] diff --git a/profiles/xfce4.py b/profiles/xfce4.py index ef0ddf2f..89c04f7c 100644 --- a/profiles/xfce4.py +++ b/profiles/xfce4.py @@ -11,6 +11,7 @@ __packages__ = [ "lightdm", "lightdm-gtk-greeter", "gvfs", + "network-manager-applet", ] diff --git a/profiles/xorg.py b/profiles/xorg.py index b8f33068..b76227ed 100644 --- a/profiles/xorg.py +++ b/profiles/xorg.py @@ -11,7 +11,6 @@ __packages__ = [ 'xorg-server', 'xorg-xinit', 'nvidia-dkms', - 'xorg-server', *archinstall.lib.hardware.__packages__, ] @@ -29,7 +29,7 @@ include = archinstall.* [options.package_data] -archinstall = +archinstall = examples/*.py profiles/*.py profiles/applications/*.py |