Merged latest master

This commit is contained in:
Anton Hvornum 2021-11-05 21:37:40 +01:00
commit 0067e34970
No known key found for this signature in database
GPG Key ID: F1234C5BA67C59DF
24 changed files with 149 additions and 121 deletions

View File

@ -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

View File

@ -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:

View File

@ -4,4 +4,4 @@ from .blockdevice import BlockDevice
from .filesystem import Filesystem, MBR, GPT
from .partition import *
from .user_guides import *
from .validators import *
from .validators import *

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 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
@ -57,7 +62,7 @@ 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']
@ -164,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
@ -220,4 +225,4 @@ class BlockDevice:
def get_partition(self, uuid):
for partition in self:
if partition.uuid == uuid:
return partition
return partition

View File

@ -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}")
raise DiskError(f"Could not create a subvolume at {target}: {cmd}")

View File

@ -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,7 +114,7 @@ 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
@ -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
return self.raw_parted(f'{device} mklabel {disk_label}').exit_code == 0

View File

@ -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
return partition

View File

@ -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
@ -346,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
return True

View File

@ -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):
@ -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
layout[block_device.path]['partitions'][1]['btrfs'] = {
"subvolumes" : {
'@home' : '/home',
'@log' : '/var/log',
'@pkgs' : '/var/cache/pacman/pkg',
'@.snapshots' : '/.snapshots'
"@home" : "/home",
"@log" : "/var/log",
"@pkgs" : "/var/cache/pacman/pkg",
"@.snapshots" : "/.snapshots"
}
}
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
return layout

View File

@ -39,4 +39,4 @@ def valid_fs_type(fstype :str) -> bool:
"reiserfs",
"udf", # "ufs", not included in `man parted`
"xfs", # `man parted` allows this
]
]

View File

@ -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:

View File

@ -99,9 +99,13 @@ def has_wifi() -> bool:
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_uefi() -> bool:
return os.path.isdir('/sys/firmware/efi')

View File

@ -1,16 +1,25 @@
import time
import logging
import os
import shutil
from .disk import *
from .hardware import *
import shlex
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 .disk.btrfs import create_subvolume, mount_subvolume
from .exceptions import DiskError, ServiceException
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 .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"]
@ -141,7 +150,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}")
@ -200,7 +209,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}')
@ -249,10 +258,20 @@ class Installer:
)
def activate_ntp(self):
self.log('Installing and activating NTP.', level=logging.INFO)
if self.pacstrap('ntp'):
if self.enable_service('ntpd'):
return True
log(f"activate_ntp() is deprecated, use activate_time_syncronization()", fg="yellow", level=logging.INFO)
self.activate_time_syncronization()
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):
for service in services:
@ -267,9 +286,9 @@ class Installer:
def run_command(self, cmd, *args, **kwargs):
return SysCommand(f'/usr/bin/arch-chroot {self.target} {cmd}')
def arch_chroot(self, cmd, *args, **kwargs):
if 'runas' in kwargs:
cmd = f"su - {kwargs['runas']} -c \"{cmd}\""
def arch_chroot(self, cmd, run_as=None):
if run_as:
cmd = f"su - {run_as} -c {shlex.quote(cmd)}"
return self.run_command(cmd)
@ -568,7 +587,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
@ -609,14 +628,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
@ -628,14 +647,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()):

View File

@ -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):

View File

@ -1,8 +1,9 @@
import logging
import urllib.error
import urllib.request
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
@ -115,7 +115,7 @@ def insert_mirrors(mirrors, *args, **kwargs):
def use_mirrors(
regions: Mapping[str, Iterable[str]],
destination: str ='/etc/pacman.d/mirrorlist'
destination: str = '/etc/pacman.d/mirrorlist'
) -> None:
log(f'A new package mirror-list has been created: {destination}', level=logging.INFO)
with open(destination, 'w') as mirrorlist:

View File

@ -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")

View File

@ -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}/'

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):
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

View File

@ -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):

View File

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

View File

@ -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 = {

View File

@ -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):
@ -570,10 +569,10 @@ 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)
@ -603,7 +602,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()]
}
@ -637,10 +635,10 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> dict:
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()
@ -677,7 +675,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.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:
return block_device_struct
else:

View File

@ -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! : )
Hop in, I hope to see you there! : )

View File

@ -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:
@ -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:
@ -139,25 +133,21 @@ def ask_user_questions():
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:
@ -165,19 +155,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.")
@ -248,7 +235,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']:
@ -281,7 +268,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"])
if archinstall.archinstall['swap']:
@ -328,7 +315,7 @@ def perform_installation(mountpoint):
installation.set_timezone(timezone)
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):
installation.user_set_pw('root', root_pw)
@ -350,7 +337,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'):
@ -365,7 +352,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)