Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--PKGBUILD44
-rw-r--r--PKGBUILDs/archinstall-bin/PKGBUILD (renamed from PKGBUILD/archinstall-bin/PKGBUILD)0
-rw-r--r--PKGBUILDs/archinstall/PKGBUILD (renamed from PKGBUILD/archinstall/PKGBUILD)0
-rw-r--r--PKGBUILDs/python-archinstall/PKGBUILD (renamed from PKGBUILD/python-archinstall/PKGBUILD)0
-rw-r--r--archinstall/__init__.py1
-rw-r--r--archinstall/lib/disk.py6
-rw-r--r--archinstall/lib/exceptions.py2
-rw-r--r--archinstall/lib/general.py6
-rw-r--r--archinstall/lib/hardware.py36
-rw-r--r--archinstall/lib/installer.py102
-rw-r--r--archinstall/lib/luks.py2
-rw-r--r--archinstall/lib/mirrors.py6
-rw-r--r--archinstall/lib/networking.py49
-rw-r--r--archinstall/lib/output.py38
-rw-r--r--archinstall/lib/packages.py4
-rw-r--r--archinstall/lib/storage.py4
-rw-r--r--docs/installing/binary.rst2
-rw-r--r--docs/installing/guided.rst2
-rw-r--r--examples/guided.py41
-rw-r--r--profiles/applications/awesome.py2
-rw-r--r--profiles/awesome.py17
-rw-r--r--test.py3
23 files changed, 290 insertions, 78 deletions
diff --git a/.gitignore b/.gitignore
index aebb999d..dc75bed8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@ SAFETY_LOCK
**/**.network
**/**.target
**/**.qcow2
+**/test.py
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 00000000..7e073666
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,44 @@
+# Maintainer: Anton Hvornum <anton@hvornum.se>
+# Contributor: Giancarlo Razzolini <grazzolini@archlinux.org>
+# Contributor: demostanis worlds <demostanis@protonmail.com>
+
+pkgbase=archinstall-git
+pkgname=('archinstall-git' 'python-archinstall-git')
+pkgver=$(git describe --long | sed 's/\([^-]*-g\)/r\1/;s/-/./g')
+pkgrel=1
+pkgdesc="Just another guided/automated Arch Linux installer with a twist"
+arch=('any')
+url="https://github.com/Torxed/archinstall"
+license=('GPL')
+depends=('python')
+makedepends=('python-setuptools')
+
+build() {
+ cd "$startdir"
+
+ python setup.py build
+}
+
+
+package_archinstall-git() {
+ depends=('python-archinstall-git')
+ conflicts=('archinstall')
+ cd "$startdir"
+
+ mkdir -p "${pkgdir}/usr/bin"
+
+ # Install a guided profile
+ cat - > "${pkgdir}/usr/bin/archinstall" <<EOF
+#!/bin/sh
+python -m archinstall $@
+EOF
+
+ chmod +x "${pkgdir}/usr/bin/archinstall"
+}
+
+package_python-archinstall-git() {
+ conflicts=('python-archinstall')
+ cd "$startdir"
+
+ python setup.py install --prefix=/usr --root="${pkgdir}" --optimize=1 --skip-build
+}
diff --git a/PKGBUILD/archinstall-bin/PKGBUILD b/PKGBUILDs/archinstall-bin/PKGBUILD
index cf6c1a9c..cf6c1a9c 100644
--- a/PKGBUILD/archinstall-bin/PKGBUILD
+++ b/PKGBUILDs/archinstall-bin/PKGBUILD
diff --git a/PKGBUILD/archinstall/PKGBUILD b/PKGBUILDs/archinstall/PKGBUILD
index d76fe7cf..d76fe7cf 100644
--- a/PKGBUILD/archinstall/PKGBUILD
+++ b/PKGBUILDs/archinstall/PKGBUILD
diff --git a/PKGBUILD/python-archinstall/PKGBUILD b/PKGBUILDs/python-archinstall/PKGBUILD
index 83b174fc..83b174fc 100644
--- a/PKGBUILD/python-archinstall/PKGBUILD
+++ b/PKGBUILDs/python-archinstall/PKGBUILD
diff --git a/archinstall/__init__.py b/archinstall/__init__.py
index 174c6885..ee2d0361 100644
--- a/archinstall/__init__.py
+++ b/archinstall/__init__.py
@@ -12,3 +12,4 @@ from .lib.services import *
from .lib.packages import *
from .lib.output import *
from .lib.storage import *
+from .lib.hardware import * \ No newline at end of file
diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py
index 499bb5bc..caf5c4e1 100644
--- a/archinstall/lib/disk.py
+++ b/archinstall/lib/disk.py
@@ -119,7 +119,7 @@ class Partition():
return f'Partition(path={self.path}, fs={self.filesystem}, mounted={self.mountpoint})'
def format(self, filesystem):
- log(f'Formatting {self} -> {filesystem}', level=LOG_LEVELS.Info, file=storage.get('logfile', None))
+ log(f'Formatting {self} -> {filesystem}', level=LOG_LEVELS.Info)
if filesystem == 'btrfs':
o = b''.join(sys_command(f'/usr/bin/mkfs.btrfs -f {self.path}'))
if b'UUID' not in o:
@@ -166,7 +166,7 @@ class Partition():
def mount(self, target, fs=None, options=''):
if not self.mountpoint:
- log(f'Mounting {self} to {target}', level=LOG_LEVELS.Info, file=storage.get('logfile', None))
+ log(f'Mounting {self} to {target}', level=LOG_LEVELS.Info)
if not fs:
if not self.filesystem: raise DiskError(f'Need to format (or define) the filesystem on {self} before mounting.')
fs = self.filesystem
@@ -231,7 +231,7 @@ class Filesystem():
self.add_partition('primary', start='513MiB', end='100%', format='ext4')
def add_partition(self, type, start, end, format=None):
- log(f'Adding partition to {self.blockdevice}', level=LOG_LEVELS.Info, file=storage.get('logfile', None))
+ log(f'Adding partition to {self.blockdevice}', level=LOG_LEVELS.Info)
previous_partitions = self.blockdevice.partitions
if format:
diff --git a/archinstall/lib/exceptions.py b/archinstall/lib/exceptions.py
index 2a1cae14..84e6a766 100644
--- a/archinstall/lib/exceptions.py
+++ b/archinstall/lib/exceptions.py
@@ -7,4 +7,6 @@ class ProfileError(BaseException):
class SysCallError(BaseException):
pass
class ProfileNotFound(BaseException):
+ pass
+class HardwareIncompatibilityError(BaseException):
pass \ No newline at end of file
diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py
index 203f5fa9..dc94b063 100644
--- a/archinstall/lib/general.py
+++ b/archinstall/lib/general.py
@@ -245,9 +245,9 @@ class sys_command():#Thread):
self.exit_code = 0
if self.exit_code != 0 and not self.kwargs['suppress_errors']:
- self.log(f"'{self.raw_cmd}' did not exit gracefully, exit code {self.exit_code}.", level=LOG_LEVELS.Error)
- self.log(self.trace_log.decode('UTF-8'), level=LOG_LEVELS.Debug)
- raise SysCallError(f"'{self.raw_cmd}' did not exit gracefully, exit code {self.exit_code}.\n{self.trace_log.decode('UTF-8')}")
+ #self.log(self.trace_log.decode('UTF-8'), level=LOG_LEVELS.Debug)
+ #self.log(f"'{self.raw_cmd}' did not exit gracefully, exit code {self.exit_code}.", level=LOG_LEVELS.Error)
+ raise SysCallError(f"{self.trace_log.decode('UTF-8')}\n'{self.raw_cmd}' did not exit gracefully (trace log above), exit code: {self.exit_code}")
self.ended = time.time()
with open(f'{self.cwd}/trace.log', 'wb') as fh:
diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py
new file mode 100644
index 00000000..93eb560f
--- /dev/null
+++ b/archinstall/lib/hardware.py
@@ -0,0 +1,36 @@
+import os
+from .general import sys_command
+from .networking import list_interfaces, enrichIfaceTypes
+
+def hasWifi():
+ if 'WIRELESS' in enrichIfaceTypes(list_interfaces().values()).values():
+ return True
+ return False
+
+def hasUEFI():
+ return os.path.isdir('/sys/firmware/efi')
+
+def graphicsDevices():
+ cards = {}
+ for line in sys_command(f"lspci"):
+ if b' VGA ' in line:
+ _, identifier = line.split(b': ',1)
+ cards[identifier.strip().lower().decode('UTF-8')] = line
+ return cards
+
+def hasNvidiaGraphics():
+ if [x for x in graphicsDevices() if 'nvidia' in x]:
+ return True
+ return False
+
+def hasAmdGraphics():
+ if [x for x in graphicsDevices() if 'amd' in x]:
+ return True
+ return False
+
+def hasIntelGraphics():
+ if [x for x in graphicsDevices() if 'intel' in x]:
+ return True
+ return False
+
+# TODO: Add more identifiers \ No newline at end of file
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
index b3837bb8..21c1b5d9 100644
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -1,4 +1,4 @@
-import os, stat, time
+import os, stat, time, shutil
from .exceptions import *
from .disk import *
@@ -34,13 +34,18 @@ class Installer():
:type hostname: str, optional
"""
- def __init__(self, partition, boot_partition, *, base_packages='base base-devel linux linux-firmware efibootmgr nano', profile=None, mountpoint='/mnt', hostname='ArchInstalled'):
+ def __init__(self, partition, boot_partition, *, base_packages='base base-devel linux linux-firmware efibootmgr nano', profile=None, mountpoint='/mnt', hostname='ArchInstalled', logdir=None, logfile=None):
self.profile = profile
self.hostname = hostname
self.mountpoint = mountpoint
self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S')
self.milliseconds = int(str(time.time()).split('.')[1])
+ if logdir:
+ storage['LOG_PATH'] = logdir
+ if logfile:
+ storage['LOG_FILE'] = logfile
+
self.helper_flags = {
'bootloader' : False,
'base' : False,
@@ -48,23 +53,18 @@ class Installer():
}
self.base_packages = base_packages.split(' ')
+ self.post_base_install = []
storage['session'] = self
self.partition = partition
self.boot_partition = boot_partition
- def log(self, *args, level=LOG_LEVELS.Debug, file=None, **kwargs):
- if not file:
- if 'logfile' not in storage:
- log_root = os.path.join(os.path.expanduser('~/'), '.cache/archinstall')
- if not os.path.isdir(log_root):
- os.makedirs(log_root)
-
- storage['logfile'] = f"{log_root}/install-session_{self.init_time}.{self.milliseconds}.log"
-
- file = storage['logfile']
-
- log(*args, level=level, file=file, **kwargs)
+ def log(self, *args, level=LOG_LEVELS.Debug, **kwargs):
+ """
+ installer.log() wraps output.log() mainly to set a default log-level for this install session.
+ Any manual override can be done per log() call.
+ """
+ log(*args, level=level, **kwargs)
def __enter__(self, *args, **kwargs):
self.partition.mount(self.mountpoint)
@@ -75,13 +75,24 @@ class Installer():
def __exit__(self, *args, **kwargs):
# b''.join(sys_command(f'sync')) # No need to, since the underlaying 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]:
+ #self.log(self.trace_log.decode('UTF-8'), level=LOG_LEVELS.Debug)
+ self.log(args[1], level=LOG_LEVELS.Error)
+
+ self.sync_log_to_install_medium()
+
+ # 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")
raise args[1]
self.genfstab()
if not (missing_steps := self.post_install_check()):
self.log('Installation completed without any errors. You may now reboot.', bg='black', fg='green', level=LOG_LEVELS.Info)
+ self.sync_log_to_install_medium()
return True
else:
self.log('Some required steps were not successfully installed/configured before leaving the installer:', bg='black', fg='red', level=LOG_LEVELS.Warning)
@@ -89,8 +100,23 @@ class Installer():
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.sync_log_to_install_medium()
return False
+ def sync_log_to_install_medium(self):
+ # Copy over the install log (if there is one) to the install medium if
+ # at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to.
+ if self.helper_flags.get('base-strapped', False) is True:
+ if (filename := storage.get('LOG_FILE', None)):
+ absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename)
+
+ if not os.path.isdir(f"{self.mountpoint}/{os.path.dirname(absolute_logfile)}"):
+ os.makedirs(f"{self.mountpoint}/{os.path.dirname(absolute_logfile)}")
+
+ shutil.copy2(absolute_logfile, f"{self.mountpoint}/{absolute_logfile}")
+
+ return True
+
def mount(self, partition, mountpoint, create_mountpoint=True):
if create_mountpoint and not os.path.isdir(f'{self.mountpoint}{mountpoint}'):
os.makedirs(f'{self.mountpoint}{mountpoint}')
@@ -182,6 +208,47 @@ class Installer():
with open(f"{self.mountpoint}/etc/systemd/network/10-{nic}.network", "a") as netconf:
netconf.write(str(conf))
+ def copy_ISO_network_config(self, enable_services=False):
+ # Copy (if any) iwd password and config files
+ if os.path.isdir('/var/lib/iwd/'):
+ if (psk_files := glob.glob('/var/lib/iwd/*.psk')):
+ if not os.path.isdir(f"{self.mountpoint}/var/lib/iwd"):
+ os.makedirs(f"{self.mountpoint}/var/lib/iwd")
+
+ if enable_services:
+ # If we haven't installed the base yet (function called pre-maturely)
+ if self.helper_flags.get('base', False) is False:
+ self.base_packages.append('iwd')
+ # This function will be called after minimal_installation()
+ # as a hook for post-installs. This hook is only needed if
+ # base is not installed yet.
+ def post_install_enable_iwd_service(*args, **kwargs):
+ self.enable_service('iwd')
+ self.enable_service('systemd-networkd')
+ self.enable_service('systemd-resolved')
+
+ self.post_base_install.append(post_install_enable_iwd_service)
+ # Otherwise, we can go ahead and add the required package
+ # and enable it's service:
+ else:
+ self.pacstrap('iwd')
+ self.enable_service('iwd')
+ self.enable_service('systemd-networkd')
+ self.enable_service('systemd-resolved')
+
+ for psk in psk_files:
+ shutil.copy2(psk, f"{self.mountpoint}/var/lib/iwd/{os.path.basename(psk)}")
+
+ # Copy (if any) systemd-networkd config files
+ if (netconfigurations := glob.glob('/etc/systemd/network/*')):
+ if not os.path.isdir(f"{self.mountpoint}/etc/systemd/network/"):
+ os.makedirs(f"{self.mountpoint}/etc/systemd/network/")
+
+ for netconf_file in netconfigurations:
+ shutil.copy2(netconf_file, f"{self.mountpoint}/etc/systemd/network/{os.path.basename(netconf_file)}")
+
+ return True
+
def minimal_installation(self):
## Add nessecary packages if encrypting the drive
## (encrypted partitions default to btrfs for now, so we need btrfs-progs)
@@ -195,6 +262,7 @@ class Installer():
if self.partition.filesystem == 'f2fs':
self.base_packages.append('f2fs-tools')
self.pacstrap(self.base_packages)
+ self.helper_flags['base-strapped'] = True
#self.genfstab()
with open(f"{self.mountpoint}/etc/fstab", "a") as fstab:
@@ -222,6 +290,12 @@ class Installer():
sys_command(f'/usr/bin/arch-chroot {self.mountpoint} mkinitcpio -p linux')
self.helper_flags['base'] = True
+
+ # Run registered post-install hooks
+ for function in self.post_base_install:
+ self.log(f"Running post-installation hook: {function}", level=LOG_LEVELS.Info)
+ function(self)
+
return True
def add_bootloader(self, bootloader='systemd-bootctl'):
diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py
index 7dfa9edc..e1f14bab 100644
--- a/archinstall/lib/luks.py
+++ b/archinstall/lib/luks.py
@@ -26,7 +26,7 @@ class luks2():
def encrypt(self, partition, password, 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?
- log(f'Encrypting {partition}', level=LOG_LEVELS.Info, file=storage.get('logfile', None))
+ log(f'Encrypting {partition} (This might take a while)', level=LOG_LEVELS.Info)
if not key_file:
key_file = f"/tmp/{os.path.basename(self.partition.path)}.disk_pw" # TODO: Make disk-pw-file randomly unique?
diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py
index e74732ec..d7d35782 100644
--- a/archinstall/lib/mirrors.py
+++ b/archinstall/lib/mirrors.py
@@ -16,7 +16,7 @@ def filter_mirrors_by_region(regions, destination='/etc/pacman.d/mirrorlist', tm
region_list = []
for region in regions.split(','):
region_list.append(f'country={region}')
- o = b''.join(sys_command((f"/usr/bin/wget 'https://www.archlinux.org/mirrorlist/?{'&'.join(region_list)}&protocol=https&ip_version=4&ip_version=6&use_mirror_status=on' -O {tmp_dir}/mirrorlist")))
+ o = b''.join(sys_command((f"/usr/bin/wget 'https://archlinux.org/mirrorlist/?{'&'.join(region_list)}&protocol=https&ip_version=4&ip_version=6&use_mirror_status=on' -O {tmp_dir}/mirrorlist")))
o = b''.join(sys_command((f"/usr/bin/sed -i 's/#Server/Server/' {tmp_dir}/mirrorlist")))
o = b''.join(sys_command((f"/usr/bin/mv {tmp_dir}/mirrorlist {destination}")))
@@ -59,7 +59,7 @@ def insert_mirrors(mirrors, *args, **kwargs):
return True
def use_mirrors(regions :dict, destination='/etc/pacman.d/mirrorlist'):
- log(f'A new package mirror-list has been created: {destination}', level=LOG_LEVELS.Info, file=storage.get('logfile', None))
+ log(f'A new package mirror-list has been created: {destination}', level=LOG_LEVELS.Info)
for region, mirrors in regions.items():
with open(destination, 'w') as mirrorlist:
for mirror in mirrors:
@@ -73,7 +73,7 @@ def re_rank_mirrors(top=10, *positionals, **kwargs):
return False
def list_mirrors():
- url = f"https://www.archlinux.org/mirrorlist/?protocol=https&ip_version=4&ip_version=6&use_mirror_status=on"
+ url = f"https://archlinux.org/mirrorlist/?protocol=https&ip_version=4&ip_version=6&use_mirror_status=on"
response = urllib.request.urlopen(url)
regions = {}
diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py
index 4829a58b..882bcff3 100644
--- a/archinstall/lib/networking.py
+++ b/archinstall/lib/networking.py
@@ -1,8 +1,11 @@
+import os
import fcntl
import socket
import struct
from collections import OrderedDict
-
+from .exceptions import *
+from .general import sys_command
+from .storage import storage
def getHwAddr(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -19,5 +22,47 @@ def list_interfaces(skip_loopback=True):
interfaces[mac] = iface
return interfaces
+def enrichIfaceTypes(interfaces :dict):
+ result = {}
+ for iface in interfaces:
+ if os.path.isdir(f"/sys/class/net/{iface}/bridge/"):
+ result[iface] = 'BRIDGE'
+ elif os.path.isfile(f"/sys/class/net/{iface}/tun_flags"):
+ # ethtool -i {iface}
+ result[iface] = 'TUN/TAP'
+ elif os.path.isdir(f"/sys/class/net/{iface}/device"):
+ if os.path.isdir(f"/sys/class/net/{iface}/wireless/"):
+ result[iface] = 'WIRELESS'
+ else:
+ result[iface] = 'PHYSICAL'
+ else:
+ result[iface] = 'UNKNOWN'
+ return result
+
def get_interface_from_mac(mac):
- return list_interfaces().get(mac.lower(), None) \ No newline at end of file
+ return list_interfaces().get(mac.lower(), None)
+
+def wirelessScan(interface):
+ interfaces = enrichIfaceTypes(list_interfaces().values())
+ if interfaces[interface] != 'WIRELESS':
+ raise HardwareIncompatibilityError(f"Interface {interface} is not a wireless interface: {interfaces}")
+
+ sys_command(f"iwctl station {interface} scan")
+
+ if not '_WIFI' in storage:
+ storage['_WIFI'] = {}
+ if not interface in storage['_WIFI']:
+ storage['_WIFI'][interface] = {}
+
+ storage['_WIFI'][interface]['scanning'] = True
+
+# TOOD: 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:
+ import time
+ wirelessScan(interface)
+ time.sleep(5)
+
+ for line in sys_command(f"iwctl station {interface} get-networks"):
+ print(line) \ No newline at end of file
diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py
index 3c1b12e2..956ad0c4 100644
--- a/archinstall/lib/output.py
+++ b/archinstall/lib/output.py
@@ -2,6 +2,7 @@ import abc
import os
import sys
import logging
+from pathlib import Path
from .storage import storage
class LOG_LEVELS:
@@ -36,6 +37,11 @@ class journald(dict):
# Fallback logger
log_adapter.debug(message)
+# TODO: Replace log() for session based logging.
+class SessionLogging():
+ def __init__(self):
+ pass
+
# Found first reference here: https://stackoverflow.com/questions/7445658/how-to-detect-if-the-console-does-support-ansi-escape-codes-in-python
# And re-used this: https://github.com/django/django/blob/master/django/core/management/color.py#L12
def supports_color():
@@ -76,30 +82,28 @@ def stylize_output(text :str, *opts, **kwargs):
def log(*args, **kwargs):
string = orig_string = ' '.join([str(x) for x in args])
+ # Attempt to colorize the output if supported
+ # Insert default colors and override with **kwargs
if supports_color():
kwargs = {'bg' : 'black', 'fg': 'white', **kwargs}
string = stylize_output(string, **kwargs)
- if (logfile := storage.get('logfile', None)) and 'file' not in kwargs:
- kwargs['file'] = logfile
+ # If a logfile is defined in storage,
+ # we use that one to output everything
+ if (filename := storage.get('LOG_FILE', None)):
+ absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename)
+ if not os.path.isfile(absolute_logfile):
+ os.makedirs(os.path.dirname(absolute_logfile))
+ Path(absolute_logfile).touch() # Overkill?
- # Log to a file output unless specifically told to suppress this feature.
- # (level has no effect on the log file, everything will be written there)
- if 'file' in kwargs and ('suppress' not in kwargs or kwargs['suppress'] == False):
- if type(kwargs['file']) is str:
- with open(kwargs['file'], 'a') as log_file:
- log_file.write(f"{orig_string}\n")
- elif kwargs['file']:
- kwargs['file'].write(f"{orig_string}\n")
+ with open(absolute_logfile, 'a') as log_file:
+ log_file.write(f"{orig_string}\n")
# If we assigned a level, try to log it to systemd's journald.
# Unless the level is higher than we've decided to output interactively.
# (Remember, log files still get *ALL* the output despite level restrictions)
if 'level' in kwargs:
- if 'LOG_LEVEL' not in storage:
- storage['LOG_LEVEL'] = LOG_LEVELS.Info
-
- if kwargs['level'] > storage['LOG_LEVEL']:
+ if kwargs['level'] > storage.get('LOG_LEVEL', LOG_LEVELS.Info):
# Level on log message was Debug, but output level is set to Info.
# In that case, we'll drop it.
return None
@@ -110,5 +114,7 @@ def log(*args, **kwargs):
pass # Ignore writing to journald
# Finally, print the log unless we skipped it based on level.
- # And we print the string which may or may not contain color formatting.
- print(string) \ No newline at end of file
+ # 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
diff --git a/archinstall/lib/packages.py b/archinstall/lib/packages.py
index 03bb2154..4f6b6c61 100644
--- a/archinstall/lib/packages.py
+++ b/archinstall/lib/packages.py
@@ -2,8 +2,8 @@ import urllib.request, urllib.parse
import ssl, json
from .exceptions import *
-BASE_URL = 'https://www.archlinux.org/packages/search/json/?name={package}'
-BASE_GROUP_URL = 'https://www.archlinux.org/groups/x86_64/{group}/'
+BASE_URL = 'https://archlinux.org/packages/search/json/?name={package}'
+BASE_GROUP_URL = 'https://archlinux.org/groups/x86_64/{group}/'
def find_group(name):
ssl_context = ssl.create_default_context()
diff --git a/archinstall/lib/storage.py b/archinstall/lib/storage.py
index 3af15153..e881700f 100644
--- a/archinstall/lib/storage.py
+++ b/archinstall/lib/storage.py
@@ -15,5 +15,7 @@ storage = {
#os.path.abspath(f'{os.path.dirname(__file__)}/../examples')
],
'UPSTREAM_URL' : 'https://raw.githubusercontent.com/Torxed/archinstall/master/profiles',
- 'PROFILE_DB' : None # Used in cases when listing profiles is desired, not mandatory for direct profile grabing.
+ '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/docs/installing/binary.rst b/docs/installing/binary.rst
index fa331e48..c51fb2c0 100644
--- a/docs/installing/binary.rst
+++ b/docs/installing/binary.rst
@@ -49,4 +49,4 @@ Simply clone or download the source, and while standing in the cloned folder `./
nuitka3 --standalone --show-progress archinstall
-This requires the `nuitka <https://www.archlinux.org/packages/community/any/nuitka/>`_ package as well as `python3` to be installed locally. \ No newline at end of file
+This requires the `nuitka <https://archlinux.org/packages/community/any/nuitka/>`_ package as well as `python3` to be installed locally. \ No newline at end of file
diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst
index 19aee62c..d09a9622 100644
--- a/docs/installing/guided.rst
+++ b/docs/installing/guided.rst
@@ -100,7 +100,7 @@ There is a list of profiles to choose from. If you are unsure of what any of the
Additional packages
^^^^^^^^^^^^^^^^^^^
-Some additional packages can be installed if need be. This step allows you to list *(space separated)* officially supported packages from the `package database <https://www.archlinux.org/packages/>`_.
+Some additional packages can be installed if need be. This step allows you to list *(space separated)* officially supported packages from the `package database <https://archlinux.org/packages/>`_.
.. warning::
When selecting *(or skipping)* this step. The installation will begin and your selected hard drive will be wiped after a 5 second countdown.
diff --git a/examples/guided.py b/examples/guided.py
index 7726d5b9..f0620b05 100644
--- a/examples/guided.py
+++ b/examples/guided.py
@@ -1,15 +1,10 @@
import getpass, time, json, sys, signal, os
import archinstall
-# Setup a global log file.
-# Archinstall will honor storage['logfile'] in most of it's functions log handle.
-log_root = os.path.join(os.path.expanduser('~/'), '.cache/archinstall')
-if not os.path.isdir(log_root):
- os.makedirs(log_root)
-
-init_time = time.strftime('%Y-%m-%d_%H-%M-%S')
-milliseconds = int(str(time.time()).split('.')[1])
-archinstall.storage['logfile'] = f"{log_root}/install-session_{init_time}.{milliseconds}.log"
+# Create a storage structure for all our information.
+# We'll print this right before the user gets informed about the formatting timer.
+archinstall.storage['_guided'] = {}
+archinstall.storage['_guided_hidden'] = {} # This will simply be hidden from printouts and things.
"""
This signal-handler chain (and global variable)
@@ -49,11 +44,18 @@ def perform_installation(device, boot_partition, language, mirrors):
installation.set_keyboard_language(language)
installation.add_bootloader()
- if archinstall.storage['_guided']['network']:
+ # If user selected to copy the current ISO network configuration
+ # Perform a copy of the config
+ if archinstall.storage['_guided']['network'] == 'Copy ISO network configuration to installation':
+ installation.copy_ISO_network_config(enable_services=True) # Sources the ISO network configuration to the install medium.
+
+ # Otherwise, if a interface was selected, configure that interface
+ elif archinstall.storage['_guided']['network']:
installation.configure_nic(**archinstall.storage['_guided']['network'])
installation.enable_service('systemd-networkd')
installation.enable_service('systemd-resolved')
+
if archinstall.storage['_guided']['packages'] and archinstall.storage['_guided']['packages'][0] != '':
installation.add_additional_packages(archinstall.storage['_guided']['packages'])
@@ -77,6 +79,7 @@ def perform_installation(device, boot_partition, language, mirrors):
archinstall.sys_command(f'umount -R /mnt', suppress_errors=True)
archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_errors=True)
+
"""
First, we'll ask the user for a bunch of user input.
Not until we're satisfied with what we want to install
@@ -85,11 +88,7 @@ archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_error
if len(keyboard_language := archinstall.select_language(archinstall.list_keyboard_languages()).strip()):
archinstall.set_keyboard_language(keyboard_language)
-
-# Create a storage structure for all our information.
-# We'll print this right before the user gets informed about the formatting timer.
-archinstall.storage['_guided'] = {}
-archinstall.storage['_guided_hidden'] = {} # This will simply be hidden from printouts and things.
+ archinstall.storage['_guided']['keyboard_layout'] = keyboard_language
# Set which region to download packages from during the installation
mirror_regions = archinstall.select_mirror_regions(archinstall.list_mirrors())
@@ -102,6 +101,7 @@ while (disk_password := getpass.getpass(prompt='Enter disk encryption password (
if disk_password != disk_password_verification:
archinstall.log(' * Passwords did not match * ', bg='black', fg='red')
continue
+ archinstall.storage['_guided']['disk_encryption'] = True
break
archinstall.storage['_guided']['harddrive'] = harddrive
@@ -118,7 +118,10 @@ while (root_pw := getpass.getpass(prompt='Enter root password (leave blank to le
archinstall.log(' * Passwords did not match * ', bg='black', fg='red')
continue
+ # Storing things in _guided_hidden helps us avoid printing it
+ # when echoing user configuration: archinstall.storage['_guided']
archinstall.storage['_guided_hidden']['root_pw'] = root_pw
+ archinstall.storage['_guided']['root_unlocked'] = True
break
# Ask for additional users (super-user if root pw was not set)
@@ -188,11 +191,12 @@ while 1:
# Optionally configure one network interface.
#while 1:
-interfaces = archinstall.list_interfaces() # {MAC: Ifname}
+# {MAC: Ifname}
+interfaces = {'ISO-CONFIG' : 'Copy ISO network configuration to installation', **archinstall.list_interfaces()}
archinstall.storage['_guided']['network'] = None
nic = archinstall.generic_select(interfaces.values(), "Select one network interface to configure (leave blank to skip): ")
-if nic:
+if nic and nic != 'Copy ISO network configuration to installation':
mode = archinstall.generic_select(['DHCP (auto detect)', 'IP (static)'], f"Select which mode to configure for {nic}: ")
if mode == 'IP (static)':
while 1:
@@ -217,7 +221,8 @@ if nic:
archinstall.storage['_guided']['network'] = {'nic': nic, 'dhcp': False, 'ip': ip, 'gateway' : gateway, 'dns' : dns}
else:
archinstall.storage['_guided']['network'] = {'nic': nic}
-
+elif nic:
+ archinstall.storage['_guided']['network'] = nic
print()
print('This is your chosen configuration:')
diff --git a/profiles/applications/awesome.py b/profiles/applications/awesome.py
index db7e8f4f..578f246e 100644
--- a/profiles/applications/awesome.py
+++ b/profiles/applications/awesome.py
@@ -3,7 +3,7 @@ import archinstall
installation.install_profile('xorg')
installation.add_additional_packages(
- "awesome xorg-xrandr xterm feh slock terminus-font-otb gnu-free-fonts ttf-liberation xsel"
+ "awesome xorg-xrandr xterm feh slock terminus-font gnu-free-fonts ttf-liberation xsel"
)
with open(f'{installation.mountpoint}/etc/X11/xinit/xinitrc', 'r') as xinitrc:
diff --git a/profiles/awesome.py b/profiles/awesome.py
index 27e7fc05..0e027714 100644
--- a/profiles/awesome.py
+++ b/profiles/awesome.py
@@ -31,25 +31,24 @@ if __name__ == 'awesome':
# Then setup and configure the desktop environment: awesome
editor = "nano"
filebrowser = "nemo gpicview-gtk3"
- webbrowser = "chromium"
- virtulization = "qemu ovmf"
- utils = "openssh sshfs git htop pkgfile scrot dhclient wget smbclient cifs-utils libu2f-host"
+ webbrowser = "chromium" # TODO: Ask the user to select one instead
+ utils = "openssh sshfs git htop pkgfile scrot dhclient wget libu2f-host"
- installation.add_additional_packages(f"{webbrowser} {utils} {virtulization} {filebrowser} {editor}")
+ installation.add_additional_packages(f"{webbrowser} {utils} {filebrowser} {editor}")
alacritty = archinstall.Application(installation, 'alacritty')
alacritty.install()
# TODO: Copy a full configuration to ~/.config/awesome/rc.lua instead.
- with open(f'{installation.mountpoint}/etc/xdg/awesome/rc.lua', 'r') as awesome_rc_lua:
- awesome_lua = awesome_rc_lua.read()
+ with open(f'{installation.mountpoint}/etc/xdg/awesome/rc.lua', 'r') as fh:
+ awesome_lua = fh.read()
## Replace xterm with alacritty for a smoother experience.
- awesome_lua = awesome_rc_lua.replace('"xterm"', '"alacritty"')
+ awesome_lua = awesome_lua.replace('"xterm"', '"alacritty"')
- with open(f'{installation.mountpoint}/etc/xdg/awesome/rc.lua', 'w') as awesome_rc_lua:
- awesome_rc_lua.write(awesome_lua)
+ with open(f'{installation.mountpoint}/etc/xdg/awesome/rc.lua', 'w') as fh:
+ fh.write(awesome_lua)
## TODO: Configure the right-click-menu to contain the above packages that were installed. (as a user config)
diff --git a/test.py b/test.py
deleted file mode 100644
index 61ba1925..00000000
--- a/test.py
+++ /dev/null
@@ -1,3 +0,0 @@
-import archinstall
-
-print(archinstall.harddrive(size=111.8, model='KINGSTON_SKC100S3120G'))