Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall
diff options
context:
space:
mode:
Diffstat (limited to 'archinstall')
-rw-r--r--archinstall/__init__.py33
-rw-r--r--archinstall/__main__.py30
-rw-r--r--archinstall/lib/disk.py13
-rw-r--r--archinstall/lib/installer.py20
-rw-r--r--archinstall/lib/luks.py2
-rw-r--r--archinstall/lib/networking.py4
-rw-r--r--archinstall/lib/output.py6
-rw-r--r--archinstall/lib/profiles.py14
-rw-r--r--archinstall/lib/storage.py2
-rw-r--r--archinstall/lib/user_interaction.py33
10 files changed, 91 insertions, 66 deletions
diff --git a/archinstall/__init__.py b/archinstall/__init__.py
index 91cf17be..8a49bef6 100644
--- a/archinstall/__init__.py
+++ b/archinstall/__init__.py
@@ -14,6 +14,8 @@ from .lib.output import *
from .lib.storage import *
from .lib.hardware import *
+__version__ = "2.1.3"
+
## Basic version of arg.parse() supporting:
## --key=value
## --boolean
@@ -27,4 +29,33 @@ for arg in sys.argv[1:]:
key, val = arg[2:], True
arguments[key] = val
else:
- positionals.append(arg) \ No newline at end of file
+ positionals.append(arg)
+
+
+# TODO: Learn the dark arts of argparse...
+# (I summon thee dark spawn of cPython)
+
+def run_as_a_module():
+ """
+ Since we're running this as a 'python -m archinstall' module OR
+ a nuitka3 compiled version of the project.
+ This function and the file __main__ acts as a entry point.
+ """
+
+ # Add another path for finding profiles, so that list_profiles() in Script() can find guided.py, unattended.py etc.
+ storage['PROFILE_PATH'].append(os.path.abspath(f'{os.path.dirname(__file__)}/examples'))
+
+ if len(sys.argv) == 1:
+ sys.argv.append('guided')
+
+ try:
+ script = Script(sys.argv[1])
+ except ProfileNotFound as err:
+ print(f"Couldn't find file: {err}")
+ sys.exit(1)
+
+ os.chdir(os.path.abspath(os.path.dirname(__file__)))
+
+ # Remove the example directory from the PROFILE_PATH, to avoid guided.py etc shows up in user input questions.
+ storage['PROFILE_PATH'].pop()
+ script.execute()
diff --git a/archinstall/__main__.py b/archinstall/__main__.py
index 63c2f715..86ed0108 100644
--- a/archinstall/__main__.py
+++ b/archinstall/__main__.py
@@ -2,33 +2,5 @@ import archinstall
import sys
import os
-# TODO: Learn the dark arts of argparse...
-# (I summon thee dark spawn of cPython)
-
-def run_as_a_module():
- """
- Since we're running this as a 'python -m archinstall' module OR
- a nuitka3 compiled version of the project.
- This function and the file __main__ acts as a entry point.
- """
-
- # Add another path for finding profiles, so that list_profiles() in Script() can find guided.py, unattended.py etc.
- archinstall.storage['PROFILE_PATH'].append(os.path.abspath(f'{os.path.dirname(__file__)}/examples'))
-
- if len(sys.argv) == 1:
- sys.argv.append('guided')
-
- try:
- script = archinstall.Script(sys.argv[1])
- except archinstall.ProfileNotFound as err:
- print(f"Couldn't find file: {err}")
- sys.exit(1)
-
- os.chdir(os.path.abspath(os.path.dirname(__file__)))
-
- # Remove the example directory from the PROFILE_PATH, to avoid guided.py etc shows up in user input questions.
- archinstall.storage['PROFILE_PATH'].pop()
- script.execute()
-
if __name__ == '__main__':
- run_as_a_module()
+ archinstall.run_as_a_module()
diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py
index 4ea9f214..e2f4d76e 100644
--- a/archinstall/lib/disk.py
+++ b/archinstall/lib/disk.py
@@ -25,11 +25,12 @@ class BlockDevice():
self.path = path
self.info = info
+ self.keep_partitions = True
self.part_cache = OrderedDict()
- # TODO: Currently disk encryption is a BIT missleading.
+ # TODO: Currently disk encryption is a BIT misleading.
# It's actually partition-encryption, but for future-proofing this
# I'm placing the encryption password on a BlockDevice level.
- self.encryption_passwoed = None
+ self.encryption_password = None
def __repr__(self, *args, **kwargs):
return f"BlockDevice({self.device})"
@@ -285,10 +286,10 @@ class Partition():
handle = luks2(self, None, None)
return handle.encrypt(self, *args, **kwargs)
- def format(self, filesystem=None, path=None, allow_formatting=None, log_formating=True):
+ def format(self, filesystem=None, path=None, allow_formatting=None, log_formatting=True):
"""
Format can be given an overriding path, for instance /dev/null to test
- the formating functionality and in essence the support for the given filesystem.
+ the formatting functionality and in essence the support for the given filesystem.
"""
if filesystem is None:
filesystem = self.filesystem
@@ -306,7 +307,7 @@ class Partition():
if not allow_formatting:
raise PermissionError(f"{self} is not formatable either because instance is locked ({self.allow_formatting}) or a blocking flag was given ({allow_formatting})")
- if log_formating:
+ if log_formatting:
log(f'Formatting {path} -> {filesystem}', level=LOG_LEVELS.Info)
if filesystem == 'btrfs':
@@ -401,7 +402,7 @@ class Partition():
2. UnknownFilesystemFormat that indicates that we don't support the given filesystem type
"""
try:
- self.format(self.filesystem, '/dev/null', log_formating=False, allow_formatting=True)
+ self.format(self.filesystem, '/dev/null', log_formatting=False, allow_formatting=True)
except SysCallError:
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:
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
index 8938b371..e9343cd1 100644
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -79,7 +79,7 @@ class Installer():
return self
def __exit__(self, *args, **kwargs):
- # b''.join(sys_command(f'sync')) # No need to, since the underlaying fs() object will call sync.
+ # b''.join(sys_command(f'sync')) # No need to, since the underlying fs() object will call sync.
# TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
if len(args) >= 2 and args[1]:
@@ -91,7 +91,7 @@ class Installer():
# We avoid printing /mnt/<log path> because that might confuse people if they note it down
# and then reboot, and a identical log file will be found in the ISO medium anyway.
print(f"[!] A log file has been created here: {os.path.join(storage['LOG_PATH'], storage['LOG_FILE'])}")
- print(f" Please submit this issue (and file) to https://github.com/Torxed/archinstall/issues")
+ print(f" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues")
raise args[1]
self.genfstab()
@@ -104,8 +104,10 @@ class Installer():
self.log('Some required steps were not successfully installed/configured before leaving the installer:', bg='black', fg='red', level=LOG_LEVELS.Warning)
for step in missing_steps:
self.log(f' - {step}', bg='black', fg='red', level=LOG_LEVELS.Warning)
- self.log(f"Detailed error logs can be found at: {log_path}", level=LOG_LEVELS.Warning)
- self.log(f"Submit this zip file as an issue to https://github.com/Torxed/archinstall/issues", level=LOG_LEVELS.Warning)
+
+ self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=LOG_LEVELS.Warning)
+ self.log(f"Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=LOG_LEVELS.Warning)
+
self.sync_log_to_install_medium()
return False
@@ -155,7 +157,7 @@ class Installer():
fstab_fh.write(fstab)
if not os.path.isfile(f'{self.mountpoint}/etc/fstab'):
- raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n{o}')
+ raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n{fstab}')
return True
@@ -274,7 +276,7 @@ class Installer():
return True
def minimal_installation(self):
- ## Add nessecary packages if encrypting the drive
+ ## Add necessary packages if encrypting the drive
## (encrypted partitions default to btrfs for now, so we need btrfs-progs)
## TODO: Perhaps this should be living in the function which dictates
## the partitioning. Leaving here for now.
@@ -319,14 +321,14 @@ class Installer():
mkinit.write('MODULES=(btrfs)\n')
mkinit.write('BINARIES=(/usr/bin/btrfs)\n')
mkinit.write('FILES=()\n')
- mkinit.write('HOOKS=(base udev autodetect modconf block encrypt filesystems keymap keyboard fsck)\n')
+ mkinit.write('HOOKS=(base udev autodetect keyboard keymap modconf block encrypt filesystems fsck)\n')
sys_command(f'/usr/bin/arch-chroot {self.mountpoint} mkinitcpio -p linux')
elif self.partition.encrypted:
with open(f'{self.mountpoint}/etc/mkinitcpio.conf', 'w') as mkinit:
mkinit.write('MODULES=()\n')
mkinit.write('BINARIES=()\n')
mkinit.write('FILES=()\n')
- mkinit.write('HOOKS=(base udev autodetect modconf block encrypt filesystems keymap keyboard fsck)\n')
+ mkinit.write('HOOKS=(base udev autodetect keyboard keymap modconf block encrypt filesystems fsck)\n')
sys_command(f'/usr/bin/arch-chroot {self.mountpoint} mkinitcpio -p linux')
self.helper_flags['base'] = True
@@ -433,7 +435,7 @@ class Installer():
# The tricky thing with doing the import archinstall.session instead is that
# profiles might be run from a different chroot, and there's no way we can
# guarantee file-path safety when accessing the installer object that way.
- # Doing the __builtins__ replacement, ensures that the global vriable "installation"
+ # Doing the __builtins__ replacement, ensures that the global variable "installation"
# is always kept up to date. It's considered a nasty hack - but it's a safe way
# of ensuring 100% accuracy of archinstall session variables.
__builtins__['installation'] = self
diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py
index 62067ec1..a1d42196 100644
--- a/archinstall/lib/luks.py
+++ b/archinstall/lib/luks.py
@@ -43,8 +43,6 @@ class luks2():
return True
def encrypt(self, partition, password=None, key_size=512, hash_type='sha512', iter_time=10000, key_file=None):
- # TODO: We should be able to integrate this into the main log some how.
- # Perhaps post-mortem?
if not self.partition.allow_formatting:
raise DiskError(f'Could not encrypt volume {self.partition} due to it having a formatting lock.')
diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py
index 882bcff3..2dc8be9b 100644
--- a/archinstall/lib/networking.py
+++ b/archinstall/lib/networking.py
@@ -56,7 +56,7 @@ def wirelessScan(interface):
storage['_WIFI'][interface]['scanning'] = True
-# TOOD: Full WiFi experience might get evolved in the future, pausing for now 2021-01-25
+# TODO: Full WiFi experience might get evolved in the future, pausing for now 2021-01-25
def getWirelessNetworks(interface):
# TODO: Make this oneliner pritter to check if the interface is scanning or not.
if not '_WIFI' in storage or interface not in storage['_WIFI'] or storage['_WIFI'][interface].get('scanning', False) is False:
@@ -65,4 +65,4 @@ def getWirelessNetworks(interface):
time.sleep(5)
for line in sys_command(f"iwctl station {interface} get-networks"):
- print(line) \ No newline at end of file
+ print(line)
diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py
index 537fb695..6b184b4b 100644
--- a/archinstall/lib/output.py
+++ b/archinstall/lib/output.py
@@ -6,7 +6,7 @@ from pathlib import Path
from .storage import storage
# TODO: use logging's built in levels instead.
-# Altough logging is threaded and I wish to avoid that.
+# Although logging is threaded and I wish to avoid that.
# It's more Pythonistic or w/e you want to call it.
class LOG_LEVELS:
Critical = 0b001
@@ -88,7 +88,7 @@ def log(*args, **kwargs):
# Attempt to colorize the output if supported
# Insert default colors and override with **kwargs
if supports_color():
- kwargs = {'bg' : 'black', 'fg': 'white', **kwargs}
+ kwargs = {'fg': 'white', **kwargs}
string = stylize_output(string, **kwargs)
# If a logfile is defined in storage,
@@ -130,4 +130,4 @@ def log(*args, **kwargs):
# We use sys.stdout.write()+flush() instead of print() to try and
# fix issue #94
sys.stdout.write(f"{string}\n")
- sys.stdout.flush() \ No newline at end of file
+ sys.stdout.flush()
diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py
index 70c21a67..39411553 100644
--- a/archinstall/lib/profiles.py
+++ b/archinstall/lib/profiles.py
@@ -112,11 +112,11 @@ class Script():
if f"{self.profile}" in self.examples:
return self.localize_path(self.examples[self.profile]['path'])
- # TODO: Redundant, the below block shouldnt be needed as profiles are stripped of their .py, but just in case for now:
+ # TODO: Redundant, the below block shouldn't be needed as profiles are stripped of their .py, but just in case for now:
elif f"{self.profile}.py" in self.examples:
return self.localize_path(self.examples[f"{self.profile}.py"]['path'])
- # Path was not found in any known examples, check if it's an abolute path
+ # Path was not found in any known examples, check if it's an absolute path
if os.path.isfile(self.profile):
return self.profile
@@ -156,7 +156,7 @@ class Profile(Script):
def install(self):
# Before installing, revert any temporary changes to the namespace.
- # This ensures that the namespace during installation is the original initation namespace.
+ # This ensures that the namespace during installation is the original initiation namespace.
# (For instance awesome instead of aweosme.py or app-awesome.py)
self.namespace = self.original_namespace
return self.execute()
@@ -231,11 +231,11 @@ class Application(Profile):
if f"{self.profile}" in self.examples:
return self.localize_path(self.examples[self.profile]['path'])
- # TODO: Redundant, the below block shouldnt be needed as profiles are stripped of their .py, but just in case for now:
+ # TODO: Redundant, the below block shouldn't be needed as profiles are stripped of their .py, but just in case for now:
elif f"{self.profile}.py" in self.examples:
return self.localize_path(self.examples[f"{self.profile}.py"]['path'])
- # Path was not found in any known examples, check if it's an abolute path
+ # Path was not found in any known examples, check if it's an absolute path
if os.path.isfile(self.profile):
return os.path.basename(self.profile)
@@ -247,7 +247,7 @@ class Application(Profile):
def install(self):
# Before installing, revert any temporary changes to the namespace.
- # This ensures that the namespace during installation is the original initation namespace.
+ # This ensures that the namespace during installation is the original initiation namespace.
# (For instance awesome instead of aweosme.py or app-awesome.py)
self.namespace = self.original_namespace
- return self.execute() \ No newline at end of file
+ return self.execute()
diff --git a/archinstall/lib/storage.py b/archinstall/lib/storage.py
index 9bda017d..43d088bb 100644
--- a/archinstall/lib/storage.py
+++ b/archinstall/lib/storage.py
@@ -14,7 +14,7 @@ storage = {
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'profiles'),
#os.path.abspath(f'{os.path.dirname(__file__)}/../examples')
],
- 'UPSTREAM_URL' : 'https://raw.githubusercontent.com/Torxed/archinstall/master/profiles',
+ 'UPSTREAM_URL' : 'https://raw.githubusercontent.com/archlinux/archinstall/master/profiles',
'PROFILE_DB' : None, # Used in cases when listing profiles is desired, not mandatory for direct profile grabing.
'LOG_PATH' : '/var/log/archinstall',
'LOG_FILE' : 'install.log',
diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py
index 58f88bd2..c5ff17ca 100644
--- a/archinstall/lib/user_interaction.py
+++ b/archinstall/lib/user_interaction.py
@@ -1,4 +1,4 @@
-import getpass, pathlib, os, shutil
+import getpass, pathlib, os, shutil, re
from .exceptions import *
from .profiles import Profile
from .locale_helpers import search_keyboard_layout
@@ -18,11 +18,21 @@ def get_terminal_width():
def get_longest_option(options):
return max([len(x) for x in options])
+def check_for_correct_username(username):
+ if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32:
+ return True
+ log(
+ "The username you entered is invalid. Try again",
+ level=LOG_LEVELS.Warning,
+ fg='red'
+ )
+ return False
+
def get_password(prompt="Enter a password: "):
while (passwd := getpass.getpass(prompt)):
passwd_verification = getpass.getpass(prompt='And one more time for verification: ')
if passwd != passwd_verification:
- log(' * Passwords did not match * ', bg='black', fg='red')
+ log(' * Passwords did not match * ', fg='red')
continue
if len(passwd.strip()) <= 0:
@@ -50,14 +60,16 @@ def print_large_list(options, padding=5, margin_bottom=0, separator=': '):
def ask_for_superuser_account(prompt='Create a required super-user with sudo privileges: ', forced=False):
while 1:
new_user = input(prompt).strip(' ')
-
+
if not new_user and forced:
# TODO: make this text more generic?
# 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!', bg='black', fg='red')
+ log(' * Since root is disabled, you need to create a least one (super) user!', fg='red')
continue
elif not new_user and not forced:
raise UserError("No superuser was created.")
+ elif not check_for_correct_username(new_user):
+ continue
password = get_password(prompt=f'Password for user {new_user}: ')
return {new_user: {"!password" : password}}
@@ -70,6 +82,8 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan
new_user = input(prompt).strip(' ')
if not new_user:
break
+ if not check_for_correct_username(new_user):
+ continue
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'):
@@ -89,6 +103,14 @@ def ask_for_a_timezone():
level=LOG_LEVELS.Warning,
fg='red'
)
+
+def ask_for_audio_selection():
+ audio = "pulseaudio" # Default for most desktop environments
+ pipewire_choice = input("Would you like to install pipewire instead of pulseaudio as the default audio server? [Y/n] ").lower()
+ if pipewire_choice == "y":
+ audio = "pipewire"
+
+ return audio
def ask_to_configure_network():
# Optionally configure one network interface.
@@ -110,7 +132,6 @@ def ask_to_configure_network():
log(
"You need to enter a valid IP in IP-config mode.",
level=LOG_LEVELS.Warning,
- bg='black',
fg='red'
)
@@ -153,7 +174,7 @@ def ask_for_main_filesystem_format():
def generic_select(options, input_text="Select one of the above by index or absolute value: ", sort=True):
"""
A generic select function that does not output anything
- other than the options and their indexs. As an example:
+ other than the options and their indexes. As an example:
generic_select(["first", "second", "third option"])
1: first