Merge branch 'master' of github.com:Torxed/archinstall into torxed-fix-350

This commit is contained in:
Anton Hvornum 2021-05-12 14:45:04 +02:00
commit 12dc55650d
48 changed files with 378 additions and 221 deletions

View File

@ -3,7 +3,7 @@ from .lib.general import *
from .lib.disk import * from .lib.disk import *
from .lib.user_interaction import * from .lib.user_interaction import *
from .lib.exceptions import * from .lib.exceptions import *
from .lib.installer import __packages__, __base_packages__, Installer from .lib.installer import __packages__, Installer
from .lib.profiles import * from .lib.profiles import *
from .lib.luks import * from .lib.luks import *
from .lib.mirrors import * from .lib.mirrors import *

View File

@ -1,3 +1,4 @@
from typing import Optional
import glob, re, os, json, time, hashlib import glob, re, os, json, time, hashlib
import pathlib, traceback, logging import pathlib, traceback, logging
from collections import OrderedDict from collections import OrderedDict
@ -205,7 +206,7 @@ class Partition():
return f'Partition(path={self.path}, size={self.size}, fs={self.filesystem}{mount_repr})' return f'Partition(path={self.path}, size={self.size}, fs={self.filesystem}{mount_repr})'
@property @property
def uuid(self) -> str: def uuid(self) -> Optional[str]:
""" """
Returns the PARTUUID as returned by lsblk. Returns the PARTUUID as returned by lsblk.
This is more reliable than relying on /dev/disk/by-partuuid as This is more reliable than relying on /dev/disk/by-partuuid as
@ -214,7 +215,7 @@ class Partition():
lsblk = b''.join(sys_command(f'lsblk -J -o+PARTUUID {self.path}')) lsblk = b''.join(sys_command(f'lsblk -J -o+PARTUUID {self.path}'))
for partition in json.loads(lsblk.decode('UTF-8'))['blockdevices']: for partition in json.loads(lsblk.decode('UTF-8'))['blockdevices']:
return partition.get('partuuid', None) return partition.get('partuuid', None)
return None
@property @property
def encrypted(self): def encrypted(self):
return self._encrypted return self._encrypted
@ -471,7 +472,6 @@ class Filesystem():
def raw_parted(self, string:str): def raw_parted(self, string:str):
x = sys_command(f'/usr/bin/parted -s {string}') x = sys_command(f'/usr/bin/parted -s {string}')
log(f"'parted -s {string}' returned: {b''.join(x)}", level=logging.DEBUG)
return x return x
def parted(self, string:str): def parted(self, string:str):
@ -625,3 +625,11 @@ def get_filesystem_type(path):
return b''.join(handle).strip().decode('UTF-8') return b''.join(handle).strip().decode('UTF-8')
except SysCallError: except SysCallError:
return None return None
def disk_layouts():
try:
handle = sys_command(f"lsblk -f -o+TYPE,SIZE -J")
return json.loads(b''.join(handle).decode('UTF-8'))
except SysCallError as err:
log(f"Could not return disk layouts: {err}")
return None

View File

@ -5,6 +5,7 @@ from subprocess import Popen, STDOUT, PIPE, check_output
from select import epoll, EPOLLIN, EPOLLHUP from select import epoll, EPOLLIN, EPOLLHUP
from .exceptions import * from .exceptions import *
from .output import log from .output import log
from typing import Optional, Union
def gen_uid(entropy_length=256): def gen_uid(entropy_length=256):
return hashlib.sha512(os.urandom(entropy_length)).hexdigest() return hashlib.sha512(os.urandom(entropy_length)).hexdigest()
@ -160,16 +161,15 @@ class sys_command():#Thread):
'exit_code': self.exit_code 'exit_code': self.exit_code
} }
def peak(self, output :str): def peak(self, output : Union[str, bytes]) -> bool:
if type(output) == bytes: if type(output) == bytes:
try: try:
output = output.decode('UTF-8') output = output.decode('UTF-8')
except UnicodeDecodeError: except UnicodeDecodeError:
return None return False
output = output.strip('\r\n ') output = output.strip('\r\n ')
if len(output) <= 0: if len(output) <= 0:
return None return False
if self.peak_output: if self.peak_output:
from .user_interaction import get_terminal_width from .user_interaction import get_terminal_width
@ -191,6 +191,7 @@ class sys_command():#Thread):
# And print the new output we're peaking on: # And print the new output we're peaking on:
sys.stdout.write(output) sys.stdout.write(output)
sys.stdout.flush() sys.stdout.flush()
return True
def run(self): def run(self):
self.status = 'running' self.status = 'running'

View File

@ -3,22 +3,53 @@ from .general import sys_command
from .networking import list_interfaces, enrichIfaceTypes from .networking import list_interfaces, enrichIfaceTypes
from typing import Optional from typing import Optional
__packages__ = [
"mesa",
"xf86-video-amdgpu",
"xf86-video-ati",
"xf86-video-nouveau",
"xf86-video-vmware",
"libva-mesa-driver",
"libva-intel-driver",
"intel-media-driver",
"vulkan-radeon",
"vulkan-intel",
"nvidia",
]
AVAILABLE_GFX_DRIVERS = { AVAILABLE_GFX_DRIVERS = {
# Sub-dicts are layer-2 options to be selected # Sub-dicts are layer-2 options to be selected
# and lists are a list of packages to be installed # and lists are a list of packages to be installed
'AMD / ATI' : { "All open-source (default)": [
'amd' : ['xf86-video-amdgpu'], "mesa",
'ati' : ['xf86-video-ati'] "xf86-video-amdgpu",
"xf86-video-ati",
"xf86-video-nouveau",
"xf86-video-vmware",
"libva-mesa-driver",
"libva-intel-driver",
"intel-media-driver",
"vulkan-radeon",
"vulkan-intel",
],
"AMD / ATI (open-source)": [
"mesa",
"xf86-video-amdgpu",
"xf86-video-ati",
"libva-mesa-driver",
"vulkan-radeon",
],
"Intel (open-source)": [
"mesa",
"libva-intel-driver",
"intel-media-driver",
"vulkan-intel",
],
"Nvidia": {
"open-source": ["mesa", "xf86-video-nouveau", "libva-mesa-driver"],
"proprietary": ["nvidia"],
}, },
'intel' : ['xf86-video-intel'], "VMware / VirtualBox (open-source)": ["mesa", "xf86-video-vmware"],
'nvidia' : {
'open-source' : ['xf86-video-nouveau'],
'proprietary' : ['nvidia']
},
'mesa' : ['mesa'],
'fbdev' : ['xf86-video-fbdev'],
'vesa' : ['xf86-video-vesa'],
'vmware' : ['xf86-video-vmware']
} }
def hasWifi()->bool: def hasWifi()->bool:
@ -60,10 +91,11 @@ def cpuVendor()-> Optional[str]:
if info.get('field',None): if info.get('field',None):
if info.get('field',None) == "Vendor ID:": if info.get('field',None) == "Vendor ID:":
return info.get('data',None) return info.get('data',None)
return None
def isVM() -> bool: def isVM() -> bool:
try: try:
subprocess.check_call(["systemd-detect-virt"]) # systemd-detect-virt issues a none 0 exit code if it is not on a virtual machine subprocess.check_call(["systemd-detect-virt"]) # systemd-detect-virt issues a non-zero exit code if it is not on a virtual machine
return True return True
except: except:
return False return False

View File

@ -12,8 +12,7 @@ from .storage import storage
from .hardware import * from .hardware import *
# 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", "linux-firmware", "efibootmgr", "nano", "ntp", "iwd"] __packages__ = ["base", "base-devel", "linux-firmware", "linux", "linux-lts", "linux-zen", "linux-hardened"]
__base_packages__ = __packages__[:6]
class Installer(): class Installer():
""" """
@ -39,8 +38,7 @@ class Installer():
:type hostname: str, optional :type hostname: str, optional
""" """
def __init__(self, target, *, base_packages='base base-devel linux-firmware', kernels='linux'): def __init__(self, target, *, base_packages=__packages__[:3], kernels=['linux']):
kernels = kernels.split(",")
self.target = target self.target = target
self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S') self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S')
self.milliseconds = int(str(time.time()).split('.')[1]) self.milliseconds = int(str(time.time()).split('.')[1])
@ -53,10 +51,7 @@ class Installer():
self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages
for kernel in kernels: for kernel in kernels:
self.base_packages.append(kernel) self.base_packages.append(kernel)
if hasUEFI():
self.base_packages.append("efibootmgr")
else:
self.base_packages.append("grub")
self.post_base_install = [] self.post_base_install = []
storage['session'] = self storage['session'] = self
@ -201,6 +196,9 @@ class Installer():
return sys_command(f'/usr/bin/arch-chroot {self.target} {cmd}') return sys_command(f'/usr/bin/arch-chroot {self.target} {cmd}')
def arch_chroot(self, cmd, *args, **kwargs): def arch_chroot(self, cmd, *args, **kwargs):
if 'runas' in kwargs:
cmd = f"su - {kwargs['runas']} -c \"{cmd}\""
return self.run_command(cmd) return self.run_command(cmd)
def drop_to_shell(self): def drop_to_shell(self):
@ -364,12 +362,12 @@ class Installer():
boot_partition = partition boot_partition = partition
elif partition.mountpoint == self.target: elif partition.mountpoint == self.target:
root_partition = partition root_partition = partition
if hasUEFI():
self.log(f'Adding bootloader {bootloader} to {boot_partition}', level=logging.INFO) self.log(f'Adding bootloader {bootloader} to {boot_partition if boot_partition else root_partition}', level=logging.INFO)
else:
self.log(f'Adding bootloader {bootloader} to {root_partition}', level=logging.INFO)
if bootloader == 'systemd-bootctl': if bootloader == 'systemd-bootctl':
self.pacstrap('efibootmgr')
if not hasUEFI(): if not hasUEFI():
raise HardwareIncompatibilityError raise HardwareIncompatibilityError
# TODO: Ideally we would want to check if another config # TODO: Ideally we would want to check if another config
@ -432,7 +430,10 @@ class Installer():
raise RequirementError(f"Could not identify the UUID of {self.partition}, there for {self.target}/boot/loader/entries/arch.conf will be broken until fixed.") raise RequirementError(f"Could not identify the UUID of {self.partition}, there for {self.target}/boot/loader/entries/arch.conf will be broken until fixed.")
elif bootloader == "grub-install": elif bootloader == "grub-install":
self.pacstrap('grub')
if hasUEFI(): if hasUEFI():
self.pacstrap('efibootmgr')
o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.target} grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB')) o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.target} grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB'))
sys_command('/usr/bin/arch-chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg') sys_command('/usr/bin/arch-chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg')
return True return True

View File

@ -19,7 +19,7 @@ class journald(dict):
@abc.abstractmethod @abc.abstractmethod
def log(message, level=logging.DEBUG): def log(message, level=logging.DEBUG):
try: try:
import systemd.journal import systemd.journal # type: ignore
except ModuleNotFoundError: except ModuleNotFoundError:
return False return False

View File

@ -1,3 +1,4 @@
from typing import Optional
import os, urllib.request, urllib.parse, ssl, json, re import os, urllib.request, urllib.parse, ssl, json, re
import importlib.util, sys, glob, hashlib, logging import importlib.util, sys, glob, hashlib, logging
from collections import OrderedDict from collections import OrderedDict
@ -49,7 +50,7 @@ def list_profiles(filter_irrelevant_macs=True, subpath='', filter_top_level_prof
except urllib.error.HTTPError as err: except urllib.error.HTTPError as err:
print(f'Error: Listing profiles on URL "{profiles_url}" resulted in:', err) print(f'Error: Listing profiles on URL "{profiles_url}" resulted in:', err)
return cache return cache
except: except json.decoder.JSONDecodeError as err:
print(f'Error: Could not decode "{profiles_url}" result as JSON:', err) print(f'Error: Could not decode "{profiles_url}" result as JSON:', err)
return cache return cache
@ -92,6 +93,9 @@ class Script():
if len(args) >= 2 and args[1]: if len(args) >= 2 and args[1]:
raise args[1] raise args[1]
if self.original_namespace:
self.namespace = self.original_namespace
def localize_path(self, profile_path): def localize_path(self, profile_path):
if (url := urllib.parse.urlparse(profile_path)).scheme and url.scheme in ('https', 'http'): if (url := urllib.parse.urlparse(profile_path)).scheme and url.scheme in ('https', 'http'):
if not self.converted_path: if not self.converted_path:
@ -202,11 +206,17 @@ class Profile(Script):
with open(self.path, 'r') as source: with open(self.path, 'r') as source:
source_data = source.read() source_data = source.read()
# TODO: I imagine that there is probably a better way to write this. if '__name__' in source_data and 'is_top_level_profile' in source_data:
return 'top_level_profile = True' in source_data with self.load_instructions(namespace=f"{self.namespace}.py") as imported:
if hasattr(imported, 'is_top_level_profile'):
return imported.is_top_level_profile
# Default to True if nothing is specified,
# since developers like less code - omitting it should assume they want to present it.
return True
@property @property
def packages(self) -> list: def packages(self) -> Optional[list]:
""" """
Returns a list of packages baked into the profile definition. Returns a list of packages baked into the profile definition.
If no package definition has been done, .packages() will return None. If no package definition has been done, .packages() will return None.
@ -226,50 +236,6 @@ class Profile(Script):
return imported.__packages__ return imported.__packages__
return None return None
def has_post_install(self):
with open(self.path, 'r') as source:
source_data = source.read()
# Some crude safety checks, make sure the imported profile has
# a __name__ check and if so, check if it's got a _prep_function()
# we can call to ask for more user input.
#
# If the requirements are met, import with .py in the namespace to not
# trigger a traditional:
# if __name__ == 'moduleName'
if '__name__' in source_data and '_post_install' in source_data:
with self.load_instructions(namespace=f"{self.namespace}.py") as imported:
if hasattr(imported, '_post_install'):
return True
def is_top_level_profile(self):
with open(self.path, 'r') as source:
source_data = source.read()
return 'top_level_profile = True' in source_data
@property
def packages(self) -> list:
"""
Returns a list of packages baked into the profile definition.
If no package definition has been done, .packages() will return None.
"""
with open(self.path, 'r') as source:
source_data = source.read()
# Some crude safety checks, make sure the imported profile has
# a __name__ check before importing.
#
# If the requirements are met, import with .py in the namespace to not
# trigger a traditional:
# if __name__ == 'moduleName'
if '__name__' in source_data and '__packages__' in source_data:
with self.load_instructions(namespace=f"{self.namespace}.py") as imported:
if hasattr(imported, '__packages__'):
return imported.__packages__
return None
class Application(Profile): class Application(Profile):
def __repr__(self, *args, **kwargs): def __repr__(self, *args, **kwargs):
return f'Application({os.path.basename(self.profile)})' return f'Application({os.path.basename(self.profile)})'

View File

@ -83,16 +83,18 @@ def get_password(prompt="Enter a password: "):
def print_large_list(options, padding=5, margin_bottom=0, separator=': '): def print_large_list(options, padding=5, margin_bottom=0, separator=': '):
highest_index_number_length = len(str(len(options))) highest_index_number_length = len(str(len(options)))
longest_line = highest_index_number_length + len(separator) + get_longest_option(options) + padding longest_line = highest_index_number_length + len(separator) + get_longest_option(options) + padding
spaces_without_option = longest_line - (len(separator) + highest_index_number_length)
max_num_of_columns = get_terminal_width() // longest_line max_num_of_columns = get_terminal_width() // longest_line
max_options_in_cells = max_num_of_columns * (get_terminal_height()-margin_bottom) max_options_in_cells = max_num_of_columns * (get_terminal_height()-margin_bottom)
if (len(options) > max_options_in_cells): if (len(options) > max_options_in_cells):
for index, option in enumerate(options): for index, option in enumerate(options):
print(f"{index}: {option}") print(f"{index}: {option}")
return 1, index
else: else:
for row in range(0, (get_terminal_height()-margin_bottom)): for row in range(0, (get_terminal_height()-margin_bottom)):
for column in range(row, len(options), (get_terminal_height()-margin_bottom)): for column in range(row, len(options), (get_terminal_height()-margin_bottom)):
spaces = " "*(longest_line - len(options[column])) spaces = " "*(spaces_without_option - len(options[column]))
print(f"{str(column): >{highest_index_number_length}}{separator}{options[column]}", end = spaces) print(f"{str(column): >{highest_index_number_length}}{separator}{options[column]}", end = spaces)
print() print()
@ -100,6 +102,18 @@ def print_large_list(options, padding=5, margin_bottom=0, separator=': '):
def generic_multi_select(options, text="Select one or more of the options above (leave blank to continue): ", sort=True, default=None, allow_empty=False): def generic_multi_select(options, text="Select one or more of the options above (leave blank to continue): ", sort=True, default=None, allow_empty=False):
# Checking if the options are different from `list` or `dict` or if they are empty
if type(options) not in [list, dict]:
log(f" * Generic multi-select doesn't support ({type(options)}) as type of options * ", fg='red')
log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow')
raise RequirementError("generic_multi_select() requires list or dictionary as options.")
if not options:
log(f" * Generic multi-select didn't find any options to choose from * ", fg='red')
log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow')
raise RequirementError('generic_multi_select() requires at least one option to proceed.')
# After passing the checks, function continues to work
if type(options) == dict:
options = list(options.values())
if sort: if sort:
options = sorted(options) options = sorted(options)
@ -108,7 +122,7 @@ def generic_multi_select(options, text="Select one or more of the options above
selected_options = [] selected_options = []
while True: while True:
if len(selected_options) <= 0 and default and default in options: if not selected_options and default in options:
selected_options.append(default) selected_options.append(default)
printed_options = [] printed_options = []
@ -119,32 +133,42 @@ def generic_multi_select(options, text="Select one or more of the options above
printed_options.append(f'{option}') printed_options.append(f'{option}')
section.clear(0, get_terminal_height()-section._cursor_y-1) section.clear(0, get_terminal_height()-section._cursor_y-1)
x, y = print_large_list(printed_options, margin_bottom=2) print_large_list(printed_options, margin_bottom=2)
section._cursor_y = len(printed_options) section._cursor_y = len(printed_options)
section._cursor_x = 0 section._cursor_x = 0
section.write_line(text) section.write_line(text)
section.input_pos = section._cursor_x section.input_pos = section._cursor_x
selected_option = section.get_keyboard_input(end=None) selected_option = section.get_keyboard_input(end=None)
# This string check is necessary to correct work with it
if selected_option is None: # Without this, Python will raise AttributeError because of stripping `None`
if len(selected_options) <= 0 and default: # It also allows to remove empty spaces if the user accidentally entered them.
selected_options = [default] if isinstance(selected_option, str):
selected_option = selected_option.strip()
if len(selected_options) or allow_empty is True: try:
break if not selected_option:
if not selected_options and default:
selected_options = [default]
elif selected_options or allow_empty:
break
else:
raise RequirementError('Please select at least one option to continue')
elif selected_option.isnumeric():
if (selected_option := int(selected_option)) >= len(options):
raise RequirementError(f'Selected option "{selected_option}" is out of range')
selected_option = options[selected_option]
if selected_option in selected_options:
selected_options.remove(selected_option)
else:
selected_options.append(selected_option)
elif selected_option in options:
if selected_option in selected_options:
selected_options.remove(selected_option)
else:
selected_options.append(selected_option)
else: else:
log('* Need to select at least one option!', fg='red') raise RequirementError(f'Selected option "{selected_option}" does not exist in available options')
continue except RequirementError as e:
log(f" * {e} * ", fg='red')
elif selected_option.isdigit():
if (selected_option := int(selected_option)) >= len(options):
log('* Option is out of range, please select another one!', fg='red')
continue
selected_option = options[selected_option]
if selected_option in selected_options:
selected_options.remove(selected_option)
else:
selected_options.append(selected_option)
return selected_options return selected_options
@ -263,14 +287,14 @@ class MiniCurses():
if response: if response:
return response return response
def ask_for_superuser_account(prompt='Username for required super-user with sudo privileges: ', forced=False): def ask_for_superuser_account(prompt='Username for required superuser with sudo privileges: ', forced=False):
while 1: while 1:
new_user = input(prompt).strip(' ') new_user = input(prompt).strip(' ')
if not new_user and forced: if not new_user and forced:
# TODO: make this text more generic? # TODO: make this text more generic?
# It's only used to create the first sudo user when root is disabled in guided.py # It's only used to create the first sudo user when root is disabled in guided.py
log(' * Since root is disabled, you need to create a least one (super) user!', fg='red') log(' * Since root is disabled, you need to create a least one superuser!', fg='red')
continue continue
elif not new_user and not forced: elif not new_user and not forced:
raise UserError("No superuser was created.") raise UserError("No superuser was created.")
@ -282,7 +306,7 @@ def ask_for_superuser_account(prompt='Username for required super-user with sudo
def ask_for_additional_users(prompt='Any additional users to install (leave blank for no users): '): def ask_for_additional_users(prompt='Any additional users to install (leave blank for no users): '):
users = {} users = {}
super_users = {} superusers = {}
while 1: while 1:
new_user = input(prompt).strip(' ') new_user = input(prompt).strip(' ')
@ -292,12 +316,12 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan
continue continue
password = get_password(prompt=f'Password for user {new_user}: ') password = get_password(prompt=f'Password for user {new_user}: ')
if input("Should this user be a sudo (super) user (y/N): ").strip(' ').lower() in ('y', 'yes'): if input("Should this user be a superuser (sudoer) [y/N]: ").strip(' ').lower() in ('y', 'yes'):
super_users[new_user] = {"!password" : password} superusers[new_user] = {"!password" : password}
else: else:
users[new_user] = {"!password" : password} users[new_user] = {"!password" : password}
return users, super_users return users, superusers
def ask_for_a_timezone(): def ask_for_a_timezone():
while True: while True:
@ -435,19 +459,23 @@ def generic_select(options, input_text="Select one of the above by index or abso
this function returns an item from list, a string, or None this function returns an item from list, a string, or None
""" """
# Checking if options are different from `list` or `dict` # Checking if the options are different from `list` or `dict` or if they are empty
if type(options) not in [list, dict]: if type(options) not in [list, dict]:
log(f" * Generic select doesn't support ({type(options)}) as type of options * ", fg='red') log(f" * Generic select doesn't support ({type(options)}) as type of options * ", fg='red')
log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow') log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow')
raise RequirementError("generic_select() requires list or dictionary as options.") raise RequirementError("generic_select() requires list or dictionary as options.")
# To allow only `list` and `dict`, converting values of options here. if not options:
# Therefore, now we can only provide the dictionary itself
if type(options) == dict: options = list(options.values())
if sort: options = sorted(options) # As we pass only list and dict (converted to list), we can skip converting to list
if len(options) == 0:
log(f" * Generic select didn't find any options to choose from * ", fg='red') log(f" * Generic select didn't find any options to choose from * ", fg='red')
log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow') log(" * If problem persists, please create an issue on https://github.com/archlinux/archinstall/issues * ", fg='yellow')
raise RequirementError('generic_select() requires at least one option to proceed.') raise RequirementError('generic_select() requires at least one option to proceed.')
# After passing the checks, function continues to work
if type(options) == dict:
# To allow only `list` and `dict`, converting values of options here.
# Therefore, now we can only provide the dictionary itself
options = list(options.values())
if sort:
# As we pass only list and dict (converted to list), we can skip converting to list
options = sorted(options)
# Added ability to disable the output of options items, # Added ability to disable the output of options items,
@ -460,8 +488,8 @@ def generic_select(options, input_text="Select one of the above by index or abso
# Now the try...except block handles validation for invalid input from the user # Now the try...except block handles validation for invalid input from the user
while True: while True:
try: try:
selected_option = input(input_text) selected_option = input(input_text).strip()
if len(selected_option.strip()) == 0: if not selected_option:
# `allow_empty_input` parameter handles return of None on empty input, if necessary # `allow_empty_input` parameter handles return of None on empty input, if necessary
# Otherwise raise `RequirementError` # Otherwise raise `RequirementError`
if allow_empty_input: if allow_empty_input:
@ -469,8 +497,7 @@ def generic_select(options, input_text="Select one of the above by index or abso
raise RequirementError('Please select an option to continue') raise RequirementError('Please select an option to continue')
# Replaced `isdigit` with` isnumeric` to discard all negative numbers # Replaced `isdigit` with` isnumeric` to discard all negative numbers
elif selected_option.isnumeric(): elif selected_option.isnumeric():
selected_option = int(selected_option) if (selected_option := int(selected_option)) >= len(options):
if selected_option >= len(options):
raise RequirementError(f'Selected option "{selected_option}" is out of range') raise RequirementError(f'Selected option "{selected_option}" is out of range')
selected_option = options[selected_option] selected_option = options[selected_option]
break break
@ -480,7 +507,6 @@ def generic_select(options, input_text="Select one of the above by index or abso
raise RequirementError(f'Selected option "{selected_option}" does not exist in available options') raise RequirementError(f'Selected option "{selected_option}" does not exist in available options')
except RequirementError as err: except RequirementError as err:
log(f" * {err} * ", fg='red') log(f" * {err} * ", fg='red')
continue
return selected_option return selected_option
@ -649,6 +675,7 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS):
""" """
drivers = sorted(list(options)) drivers = sorted(list(options))
default_option = options["All open-source (default)"]
if drivers: if drivers:
lspci = sys_command(f'/usr/bin/lspci') lspci = sys_command(f'/usr/bin/lspci')
@ -660,6 +687,10 @@ def select_driver(options=AVAILABLE_GFX_DRIVERS):
print(' ** AMD card detected, suggested driver: AMD / ATI **') print(' ** AMD card detected, suggested driver: AMD / ATI **')
initial_option = generic_select(drivers, input_text="Select your graphics card driver: ") initial_option = generic_select(drivers, input_text="Select your graphics card driver: ")
if not initial_option:
return default_option
selected_driver = options[initial_option] selected_driver = options[initial_option]
if type(selected_driver) == dict: if type(selected_driver) == dict:
@ -691,6 +722,6 @@ def select_kernel(options):
kernels = sorted(list(options)) kernels = sorted(list(options))
if kernels: if kernels:
return generic_multi_select(kernels, f"Choose which kernel to use (leave blank for default: {DEFAULT_KERNEL}): ", default=DEFAULT_KERNEL) return generic_multi_select(kernels, f"Choose which kernels to use (leave blank for default: {DEFAULT_KERNEL}): ", default=DEFAULT_KERNEL, sort=False)
raise RequirementError("Selecting kernels require a least one kernel to be given as an option.") raise RequirementError("Selecting kernels require a least one kernel to be given as an option.")

0
examples/__init__.py Normal file
View File

View File

@ -7,6 +7,9 @@ if archinstall.arguments.get('help'):
print("See `man archinstall` for help.") print("See `man archinstall` for help.")
exit(0) exit(0)
# For support reasons, we'll log the disk layout pre installation to match against post-installation layout
archinstall.log(f"Disk states before installing: {archinstall.disk_layouts()}", level=archinstall.LOG_LEVELS.Debug)
def ask_user_questions(): def ask_user_questions():
""" """
First, we'll ask the user for a bunch of user input. First, we'll ask the user for a bunch of user input.
@ -136,12 +139,14 @@ def ask_user_questions():
archinstall.log('Using existing partition table reported above.') archinstall.log('Using existing partition table reported above.')
elif option == 'format-all': elif option == 'format-all':
archinstall.arguments['filesystem'] = archinstall.ask_for_main_filesystem_format() if not archinstall.arguments.get('filesystem', None):
archinstall.arguments['filesystem'] = archinstall.ask_for_main_filesystem_format()
archinstall.arguments['harddrive'].keep_partitions = False archinstall.arguments['harddrive'].keep_partitions = False
elif archinstall.arguments['harddrive']: elif archinstall.arguments['harddrive']:
# If the drive doesn't have any partitions, safely mark the disk with keep_partitions = False # If the drive doesn't have any partitions, safely mark the disk with keep_partitions = False
# and ask the user for a root filesystem. # and ask the user for a root filesystem.
archinstall.arguments['filesystem'] = archinstall.ask_for_main_filesystem_format() if not archinstall.arguments.get('filesystem', None):
archinstall.arguments['filesystem'] = archinstall.ask_for_main_filesystem_format()
archinstall.arguments['harddrive'].keep_partitions = False archinstall.arguments['harddrive'].keep_partitions = False
# Get disk encryption password (or skip if blank) # Get disk encryption password (or skip if blank)
@ -383,5 +388,8 @@ def perform_installation(mountpoint):
except: except:
pass pass
# For support reasons, we'll log the disk layout post installation (crash or no crash)
archinstall.log(f"Disk states after installing: {archinstall.disk_layouts()}", level=archinstall.LOG_LEVELS.Debug)
ask_user_questions() ask_user_questions()
perform_installation_steps() perform_installation_steps()

View File

@ -1,8 +1,14 @@
import archinstall import archinstall
import json import json
import urllib.request import urllib.request
import git
__packages__ = ['nano', 'wget', 'git']
if __name__ == '52-54-00-12-34-56':
awesome = archinstall.Application(installation, 'postgresql')
awesome.install()
"""
# Unmount and close previous runs (Mainly only used for re-runs, but won't hurt.) # Unmount and close previous runs (Mainly only used for re-runs, but won't hurt.)
archinstall.sys_command(f'umount -R /mnt', suppress_errors=True) archinstall.sys_command(f'umount -R /mnt', suppress_errors=True)
archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_errors=True) archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_errors=True)
@ -30,22 +36,19 @@ with archinstall.Filesystem(harddrive) as fs:
if installation.minimal_installation(): if installation.minimal_installation():
installation.add_bootloader() installation.add_bootloader()
installation.add_additional_packages(['nano', 'wget', 'git']) installation.add_additional_packages(__packages__)
installation.install_profile('awesome') installation.install_profile('awesome')
installation.user_create('anton', 'test') installation.user_create('devel', 'devel')
installation.user_set_pw('root', 'toor') installation.user_set_pw('root', 'toor')
repo = git.Repo('./') print(f'Submitting {archinstall.__version__}: success')
commit = repo.head.commit.hexsha[:7]
print(f'Submitting {commit}: success')
conditions = { conditions = {
"project": "archinstall", "project": "archinstall",
"profile": "52-54-00-12-34-56", "profile": "52-54-00-12-34-56",
"status": "success", "status": "success",
"commit": commit "version": archinstall.__version__
} }
req = urllib.request.Request("https://api.archlinux.life/build/success", req = urllib.request.Request("https://api.archlinux.life/build/success",
data=json.dumps(conditions).encode('utf8'), data=json.dumps(conditions).encode('utf8'),
@ -54,3 +57,4 @@ with archinstall.Filesystem(harddrive) as fs:
urllib.request.urlopen(req, timeout=5) urllib.request.urlopen(req, timeout=5)
except: except:
pass pass
"""

0
profiles/__init__.py Normal file
View File

View File

View File

@ -1,3 +0,0 @@
import archinstall
installation.add_additional_packages("alacritty")

View File

@ -1,4 +0,0 @@
import archinstall
# "It is recommended also to install the gnome group, which contains applications required for the standard GNOME experience." - Arch Wiki
installation.add_additional_packages("budgie-desktop lightdm lightdm-gtk-greeter gnome")

View File

@ -1,3 +0,0 @@
import archinstall
installation.add_additional_packages("cinnamon system-config-printer gnome-keyring gnome-terminal blueberry metacity lightdm lightdm-gtk-greeter")

View File

@ -0,0 +1,9 @@
import archinstall
# Define the package list in order for lib to source
# which packages will be installed by this profile
__packages__ = ["cockpit", "udisks2", "packagekit"]
installation.add_additional_packages(__packages__)
installation.enable_service('cockpit.socket')

View File

@ -1,5 +0,0 @@
import archinstall
packages = "deepin deepin-terminal deepin-editor"
installation.add_additional_packages(packages)

View File

@ -0,0 +1,9 @@
import archinstall
# Define the package list in order for lib to source
# which packages will be installed by this profile
__packages__ = ["docker"]
installation.add_additional_packages(__packages__)
installation.enable_service('docker')

View File

@ -1,4 +0,0 @@
import archinstall
installation.add_additional_packages("gnome gnome-tweaks gdm")
# Note: gdm should be part of the gnome group, but adding it here for clarity

View File

@ -0,0 +1,9 @@
import archinstall
# Define the package list in order for lib to source
# which packages will be installed by this profile
__packages__ = ["apache"]
installation.add_additional_packages(__packages__)
installation.enable_service('httpd')

View File

@ -1,2 +0,0 @@
import archinstall
installation.add_additional_packages("i3-gaps")

View File

@ -1,2 +0,0 @@
import archinstall
installation.add_additional_packages("i3-wm")

View File

@ -1,5 +0,0 @@
import archinstall
packages = "plasma-meta konsole kate dolphin sddm plasma-wayland-session"
if "nvidia" in _gfx_driver_packages:
packages = packages + " egl-wayland"
installation.add_additional_packages(packages)

View File

@ -0,0 +1,9 @@
import archinstall
# Define the package list in order for lib to source
# which packages will be installed by this profile
__packages__ = ["lighttpd"]
installation.add_additional_packages(__packages__)
installation.enable_service('lighttpd')

View File

@ -1,3 +0,0 @@
import archinstall
installation.add_additional_packages("lxqt breeze-icons oxygen-icons xdg-utils ttf-freefont leafpad slock sddm")

View File

@ -0,0 +1,11 @@
import archinstall
# Define the package list in order for lib to source
# which packages will be installed by this profile
__packages__ = ["mariadb"]
installation.add_additional_packages(__packages__)
installation.arch_chroot("mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql")
installation.enable_service('mariadb')

View File

@ -1,3 +0,0 @@
import archinstall
installation.add_additional_packages("mate mate-extra lightdm lightdm-gtk-greeter")

View File

@ -0,0 +1,9 @@
import archinstall
# Define the package list in order for lib to source
# which packages will be installed by this profile
__packages__ = ["nginx"]
installation.add_additional_packages(__packages__)
installation.enable_service('nginx')

View File

@ -0,0 +1,11 @@
import archinstall
# Define the package list in order for lib to source
# which packages will be installed by this profile
__packages__ = ["postgresql"]
installation.add_additional_packages(__packages__)
installation.arch_chroot("initdb -D /var/lib/postgres/data", runas='postgres')
installation.enable_service('postgresql')

View File

@ -0,0 +1,9 @@
import archinstall
# Define the package list in order for lib to source
# which packages will be installed by this profile
__packages__ = ["openssh"]
installation.add_additional_packages(__packages__)
installation.enable_service('sshd')

View File

@ -1,3 +0,0 @@
import archinstall
__packages__ = "sway swaylock swayidle waybar dmenu light grim slurp pavucontrol alacritty"
installation.add_additional_packages(__packages__)

View File

@ -0,0 +1,12 @@
import archinstall
# This is using Tomcat 10 as that is the latest release at the time of implementation.
# This should probably be updated to use newer releases as they come out.
# Define the package list in order for lib to source
# which packages will be installed by this profile
__packages__ = ["tomcat10"]
installation.add_additional_packages(__packages__)
installation.enable_service('tomcat10')

View File

@ -1,3 +0,0 @@
import archinstall
__packages__ = "xfce4 xfce4-goodies lightdm lightdm-gtk-greeter"
installation.add_additional_packages(__packages__)

View File

@ -6,7 +6,7 @@ is_top_level_profile = False
# New way of defining packages for a profile, which is iterable and can be used out side # New way of defining packages for a profile, which is iterable and can be used out side
# of the profile to get a list of "what packages will be installed". # of the profile to get a list of "what packages will be installed".
__packages__ = ['nemo', 'gpicview-gtk3', 'scrot'] __packages__ = ['nemo', 'gpicview-gtk3', 'main', 'alacritty']
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
@ -35,9 +35,6 @@ if __name__ == 'awesome':
installation.add_additional_packages(__packages__) installation.add_additional_packages(__packages__)
alacritty = archinstall.Application(installation, 'alacritty')
alacritty.install()
# TODO: Copy a full configuration to ~/.config/awesome/rc.lua instead. # TODO: Copy a full configuration to ~/.config/awesome/rc.lua instead.
with open(f'{installation.target}/etc/xdg/awesome/rc.lua', 'r') as fh: with open(f'{installation.target}/etc/xdg/awesome/rc.lua', 'r') as fh:
awesome_lua = fh.read() awesome_lua = fh.read()

View File

@ -4,6 +4,9 @@ import archinstall
is_top_level_profile = False is_top_level_profile = False
# "It is recommended also to install the gnome group, which contains applications required for the standard GNOME experience." - Arch Wiki
__packages__ = ["budgie-desktop", "lightdm", "lightdm-gtk-greeter", "gnome"]
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
Magic function called by the importing installer Magic function called by the importing installer
@ -27,8 +30,7 @@ if __name__ == 'budgie':
# Install dependency profiles # Install dependency profiles
installation.install_profile('xorg') installation.install_profile('xorg')
# Install the application budgie from the template under /applications/ # Install the Budgie packages
budgie = archinstall.Application(installation, 'budgie') installation.add_additional_packages(__packages__)
budgie.install()
installation.enable_service('lightdm') # Light Display Manager installation.enable_service('lightdm') # Light Display Manager

View File

@ -4,6 +4,8 @@ import archinstall
is_top_level_profile = False is_top_level_profile = False
__packages__ = ["cinnamon", "system-config-printer", "gnome-keyring", "gnome-terminal", "blueberry", "metacity", "lightdm", "lightdm-gtk-greeter"]
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
Magic function called by the importing installer Magic function called by the importing installer
@ -27,8 +29,7 @@ if __name__ == 'cinnamon':
# Install dependency profiles # Install dependency profiles
installation.install_profile('xorg') installation.install_profile('xorg')
# Install the application cinnamon from the template under /applications/ # Install the Cinnamon packages
cinnamon = archinstall.Application(installation, 'cinnamon') installation.add_additional_packages(__packages__)
cinnamon.install()
installation.enable_service('lightdm') # Light Display Manager installation.enable_service('lightdm') # Light Display Manager

View File

@ -4,6 +4,7 @@ import archinstall, os
is_top_level_profile = False is_top_level_profile = False
__packages__ = ["deepin", "deepin-terminal", "deepin-editor"]
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
@ -29,9 +30,8 @@ if __name__ == 'deepin':
# Install dependency profiles # Install dependency profiles
installation.install_profile('xorg') installation.install_profile('xorg')
# Install the application deepin from the template under /applications/ # Install the Deepin packages
deepin = archinstall.Application(installation, 'deepin') installation.add_additional_packages(__packages__)
deepin.install()
# Enable autostart of Deepin for all users # Enable autostart of Deepin for all users
installation.enable_service('lightdm') installation.enable_service('lightdm')

View File

@ -4,6 +4,9 @@ import archinstall
is_top_level_profile = False is_top_level_profile = False
# Note: GDM should be part of the gnome group, but adding it here for clarity
__packages__ = ["gnome", "gnome-tweaks", "gdm"]
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
Magic function called by the importing installer Magic function called by the importing installer
@ -28,9 +31,8 @@ if __name__ == 'gnome':
# Install dependency profiles # Install dependency profiles
installation.install_profile('xorg') installation.install_profile('xorg')
# Install the application gnome from the template under /applications/ # Install the GNOME packages
gnome = archinstall.Application(installation, 'gnome') installation.add_additional_packages(__packages__)
gnome.install()
installation.enable_service('gdm') # Gnome Display Manager installation.enable_service('gdm') # Gnome Display Manager
# We could also start it via xinitrc since we do have Xorg, # We could also start it via xinitrc since we do have Xorg,

View File

@ -6,7 +6,7 @@ is_top_level_profile = False
# New way of defining packages for a profile, which is iterable and can be used out side # New way of defining packages for a profile, which is iterable and can be used out side
# of the profile to get a list of "what packages will be installed". # of the profile to get a list of "what packages will be installed".
__packages__ = ['i3lock', 'i3status', 'i3blocks', 'xterm'] __packages__ = ['i3lock', 'i3status', 'i3blocks', 'xterm', 'lightdm-gtk-greeter', 'lightdm', 'dmenu']
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
@ -48,17 +48,16 @@ if __name__ == 'i3':
""" """
# Install common packages for all i3 configurations # Install common packages for all i3 configurations
installation.add_additional_packages(__packages__) installation.add_additional_packages(__packages__[:4])
# Install dependency profiles # Install dependency profiles
installation.install_profile('xorg') installation.install_profile('xorg')
# gaps is installed by deafult so we are overriding it here # gaps is installed by deafult so we are overriding it here with lightdm
installation.add_additional_packages("lightdm-gtk-greeter lightdm") installation.add_additional_packages(__packages__[4:])
# Auto start lightdm for all users # Auto start lightdm for all users
installation.enable_service('lightdm') installation.enable_service('lightdm')
# install the i3 group now # install the i3 group now
i3 = archinstall.Application(installation, archinstall.storage['_i3_configuration']) installation.add_additional_packages(archinstall.storage['_i3_configuration'])
i3.install()

View File

@ -4,6 +4,8 @@ import archinstall, os
is_top_level_profile = False is_top_level_profile = False
__packages__ = ["plasma-meta", "konsole", "kate", "dolphin", "sddm", "plasma-wayland-session", "egl-wayland"]
# TODO: Remove hard dependency of bash (due to .bash_profile) # TODO: Remove hard dependency of bash (due to .bash_profile)
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
@ -37,9 +39,8 @@ if __name__ == 'kde':
# Install dependency profiles # Install dependency profiles
installation.install_profile('xorg') installation.install_profile('xorg')
# Install the application kde from the template under /applications/ # Install the KDE packages
kde = archinstall.Application(installation, 'kde') installation.add_additional_packages(__packages__)
kde.install()
# Enable autostart of KDE for all users # Enable autostart of KDE for all users
installation.enable_service('sddm') installation.enable_service('sddm')

View File

@ -5,6 +5,8 @@ import archinstall
is_top_level_profile = False is_top_level_profile = False
__packages__ = ["lxqt", "breeze-icons", "oxygen-icons", "xdg-utils", "ttf-freefont", "leafpad", "slock", "sddm"]
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
Magic function called by the importing installer Magic function called by the importing installer
@ -28,8 +30,7 @@ if __name__ == 'lxqt':
# Install dependency profiles # Install dependency profiles
installation.install_profile('xorg') installation.install_profile('xorg')
# Install the application xfce4 from the template under /applications/ # Install the LXQt packages
xfce = archinstall.Application(installation, 'lxqt') installation.add_additional_packages(__packages__)
xfce.install()
installation.enable_service('sddm') # SDDM Display Manager installation.enable_service('sddm') # SDDM Display Manager

View File

@ -4,6 +4,8 @@ import archinstall
is_top_level_profile = False is_top_level_profile = False
__packages__ = ["mate", "mate-extra", "lightdm", "lightdm-gtk-greeter"]
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
Magic function called by the importing installer Magic function called by the importing installer
@ -27,8 +29,7 @@ if __name__ == 'mate':
# Install dependency profiles # Install dependency profiles
installation.install_profile('xorg') installation.install_profile('xorg')
# Install the application mate from the template under /applications/ # Install the MATE packages
mate = archinstall.Application(installation, 'mate') installation.add_additional_packages(__packages__)
mate.install()
installation.enable_service('lightdm') # Light Display Manager installation.enable_service('lightdm') # Light Display Manager

30
profiles/server.py Normal file
View File

@ -0,0 +1,30 @@
# Used to select various server application profiles on top of a minimal installation.
import archinstall, os, logging
is_top_level_profile = True
available_servers = ["cockpit", "docker", "httpd", "lighttpd", "mariadb", "nginx", "postgresql", "sshd", "tomcat"]
def _prep_function(*args, **kwargs):
"""
Magic function called by the importing installer
before continuing any further.
"""
selected_servers = archinstall.generic_multi_select(available_servers, f"Choose which servers to install and enable (leave blank for a minimal installation): ")
archinstall.storage['_selected_servers'] = selected_servers
return True
if __name__ == 'server':
"""
This "profile" is a meta-profile.
"""
archinstall.log(f'Now installing the selected servers.', level=logging.INFO)
archinstall.log(archinstall.storage['_selected_servers'], level=logging.DEBUG)
for server in archinstall.storage['_selected_servers']:
archinstall.log(f'Installing {server} ...', level=logging.INFO)
app = archinstall.Application(installation, server)
app.install()
archinstall.log('If your selections included multiple servers with the same port, you may have to reconfigure them.', fg="yellow", level=logging.INFO)

View File

@ -4,6 +4,20 @@ import archinstall
is_top_level_profile = False is_top_level_profile = False
__packages__ = [
"sway",
"swaylock",
"swayidle",
"waybar",
"dmenu",
"light",
"grim",
"slurp",
"pavucontrol",
"alacritty",
]
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
Magic function called by the importing installer Magic function called by the importing installer
@ -11,19 +25,26 @@ def _prep_function(*args, **kwargs):
other code in this stage. So it's a safe way to ask the user other code in this stage. So it's a safe way to ask the user
for more input before any other installer steps start. for more input before any other installer steps start.
""" """
if "nvidia" in _gfx_driver_packages: __builtins__["_gfx_driver_packages"] = archinstall.select_driver()
choice = input("The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues. Continue anyways? [y/N] ")
if choice.lower() in ("n", ""):
raise archinstall.lib.exceptions.HardwareIncompatibilityError("Sway does not support the proprietary nvidia drivers.")
__builtins__['_gfx_driver_packages'] = archinstall.select_driver()
return True return True
# Ensures that this code only gets executed if executed # Ensures that this code only gets executed if executed
# through importlib.util.spec_from_file_location("sway", "/somewhere/sway.py") # through importlib.util.spec_from_file_location("sway", "/somewhere/sway.py")
# or through conventional import sway # or through conventional import sway
if __name__ == 'sway': if __name__ == "sway":
# Install the application sway from the template under /applications/ if "nvidia" in _gfx_driver_packages:
sway = archinstall.Application(installation, 'sway') choice = input(
sway.install() "The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues. Continue anyways? [y/N] "
)
if choice.lower() in ("n", ""):
raise archinstall.lib.exceptions.HardwareIncompatibilityError(
"Sway does not support the proprietary nvidia drivers."
)
# Install the Sway packages
installation.add_additional_packages(__packages__)
# Install the graphics driver packages
installation.add_additional_packages(_gfx_driver_packages)

View File

@ -5,6 +5,8 @@ import archinstall
is_top_level_profile = False is_top_level_profile = False
__packages__ = ["xfce4", "xfce4-goodies", "lightdm", "lightdm-gtk-greeter"]
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
Magic function called by the importing installer Magic function called by the importing installer
@ -28,8 +30,7 @@ if __name__ == 'xfce4':
# Install dependency profiles # Install dependency profiles
installation.install_profile('xorg') installation.install_profile('xorg')
# Install the application xfce4 from the template under /applications/ # Install the XFCE4 packages
xfce = archinstall.Application(installation, 'xfce4') installation.add_additional_packages(__packages__)
xfce.install()
installation.enable_service('lightdm') # Light Display Manager installation.enable_service('lightdm') # Light Display Manager

View File

@ -1,10 +1,12 @@
# A system with "xorg" installed # A system with "xorg" installed
import os import os
from archinstall import generic_select, sys_command, RequirementError
import archinstall import archinstall
is_top_level_profile = True is_top_level_profile = True
__packages__ = ['dkms', 'xorg-server', 'xorg-xinit', 'nvidia-dkms', 'xorg-server', *archinstall.lib.hardware.__packages__]
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """
Magic function called by the importing installer Magic function called by the importing installer

View File

@ -1,2 +1,2 @@
import setuptools import setuptools # type: ignore
setuptools.setup() setuptools.setup()