Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig13
-rw-r--r--.github/CODEOWNERS7
-rw-r--r--README.md4
-rw-r--r--archinstall/__init__.py2
-rw-r--r--archinstall/lib/disk.py36
-rw-r--r--archinstall/lib/general.py54
-rw-r--r--archinstall/lib/hardware.py18
-rw-r--r--archinstall/lib/installer.py24
-rw-r--r--archinstall/lib/luks.py25
-rw-r--r--archinstall/lib/profiles.py22
-rw-r--r--archinstall/lib/systemd.py16
-rw-r--r--archinstall/lib/user_interaction.py13
-rw-r--r--docs/archinstall/general.rst7
-rw-r--r--docs/examples/python.rst4
-rw-r--r--docs/index.rst4
-rw-r--r--docs/installing/guided.rst6
-rw-r--r--docs/installing/python.rst4
-rw-r--r--examples/guided.py3
-rw-r--r--profiles/applications/awesome.py6
-rw-r--r--profiles/applications/cinnamon.py4
-rw-r--r--profiles/applications/xfce4.py4
-rw-r--r--profiles/awesome.py10
-rw-r--r--profiles/cinnamon.py33
-rw-r--r--profiles/desktop.py2
-rw-r--r--profiles/xfce4.py34
25 files changed, 272 insertions, 83 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..59406405
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+# http://editorconfig.org
+# See coding conventions in CONTRIBUTING.md
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+
+[*.py]
+indent_style = tab
+indent_size = 4
+trim_trailing_whitespace = true
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..4c93403a
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,7 @@
+# As per https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#example-of-a-codeowners-file
+
+* @Torxed
+
+# Any PKGBUILD changes should tag grazzolini
+/PKGBUILDs/ @grazzolini
+/PKGBUILD @grazzolini
diff --git a/README.md b/README.md
index 9e611dd6..5451b55f 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ with archinstall.Filesystem(harddrive, archinstall.GPT) as fs:
installation.add_bootloader(harddrive.partition[0])
installation.add_additional_packages(['nano', 'wget', 'git'])
- installation.install_profile('workstation')
+ installation.install_profile('awesome')
installation.user_create('anton', 'test')
installation.user_set_pw('root', 'toor')
@@ -62,7 +62,7 @@ This installer will perform the following:
* Installs a basic instance of Arch Linux *(base base-devel linux linux-firmware btrfs-progs efibootmgr)*
* Installs and configures a bootloader to partition 0 on uefi. on bios it sets the root to partition 0.
* Install additional packages *(nano, wget, git)*
- * Installs a network-profile called [workstation](https://github.com/Torxed/archinstall/blob/master/profiles/workstation.json) *(more on network profiles in the docs)*
+ * Installs a network-profile called [awesome](https://github.com/Torxed/archinstall/blob/master/profiles/awesome.py) *(more on network profiles in the documentation)*
> **Creating your own ISO with this script on it:** Follow [ArchISO](https://wiki.archlinux.org/index.php/archiso)'s guide on how to create your own ISO or use a pre-built [guided ISO](https://hvornum.se/archiso/) to skip the python installation step, or to create auto-installing ISO templates. Further down are examples and cheat sheets on how to create different live ISO's.
diff --git a/archinstall/__init__.py b/archinstall/__init__.py
index d4452d38..91cf17be 100644
--- a/archinstall/__init__.py
+++ b/archinstall/__init__.py
@@ -2,7 +2,7 @@ from .lib.general import *
from .lib.disk import *
from .lib.user_interaction import *
from .lib.exceptions import *
-from .lib.installer import *
+from .lib.installer import __packages__, __base_packages__, Installer
from .lib.profiles import *
from .lib.luks import *
from .lib.mirrors import *
diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py
index fdc2fbc5..4ea9f214 100644
--- a/archinstall/lib/disk.py
+++ b/archinstall/lib/disk.py
@@ -73,26 +73,31 @@ class BlockDevice():
raise DiskError(f'Could not locate backplane info for "{self.path}"')
if self.info['type'] == 'loop':
- for drive in json.loads(b''.join(sys_command(f'losetup --json', hide_from_log=True)).decode('UTF_8'))['loopdevices']:
+ for drive in json.loads(b''.join(sys_command(['losetup', '--json'], hide_from_log=True)).decode('UTF_8'))['loopdevices']:
if not drive['name'] == self.path: continue
return drive['back-file']
elif self.info['type'] == 'disk':
return self.path
+ elif self.info['type'][:4] == 'raid':
+ # This should catch /dev/md## raid devices
+ return self.path
elif self.info['type'] == 'crypt':
if 'pkname' not in self.info:
raise DiskError(f'A crypt device ({self.path}) without a parent kernel device name.')
return f"/dev/{self.info['pkname']}"
+ else:
+ log(f"Unknown blockdevice type for {self.path}: {self.info['type']}", level=LOG_LEVELS.Debug)
# if not stat.S_ISBLK(os.stat(full_path).st_mode):
# raise DiskError(f'Selected disk "{full_path}" is not a block device.')
@property
def partitions(self):
- o = b''.join(sys_command(f'partprobe {self.path}'))
+ o = b''.join(sys_command(['partprobe', self.path]))
#o = b''.join(sys_command('/usr/bin/lsblk -o name -J -b {dev}'.format(dev=dev)))
- o = b''.join(sys_command(f'/usr/bin/lsblk -J {self.path}'))
+ o = b''.join(sys_command(['/usr/bin/lsblk', '-J', self.path]))
if b'not a block device' in o:
raise DiskError(f'Can not read partitions off something that isn\'t a block device: {self.path}')
@@ -187,6 +192,17 @@ class Partition():
return f'Partition(path={self.path}, fs={self.filesystem}{mount_repr})'
@property
+ def uuid(self) -> str:
+ """
+ Returns the PARTUUID as returned by lsblk.
+ This is more reliable than relying on /dev/disk/by-partuuid as
+ it doesn't seam to be able to detect md raid partitions.
+ """
+ lsblk = b''.join(sys_command(f'lsblk -J -o+PARTUUID {self.path}'))
+ for partition in json.loads(lsblk.decode('UTF-8'))['blockdevices']:
+ return partition.get('partuuid', None)
+
+ @property
def encrypted(self):
return self._encrypted
@@ -203,7 +219,7 @@ class Partition():
if not self._encrypted:
return self.path
else:
- for blockdevice in json.loads(b''.join(sys_command('lsblk -J')).decode('UTF-8'))['blockdevices']:
+ for blockdevice in json.loads(b''.join(sys_command(['lsblk', '-J'])).decode('UTF-8'))['blockdevices']:
if (parent := self.find_parent_of(blockdevice, os.path.basename(self.path))):
return f"/dev/{parent}"
# raise DiskError(f'Could not find appropriate parent for encrypted partition {self}')
@@ -241,9 +257,15 @@ class Partition():
if self.allow_formatting is False:
log(f"Partition {self} is not marked for formatting.", level=LOG_LEVELS.Debug)
return False
- elif self.target_mountpoint == '/boot' and self.has_content():
- log(f"Partition {self} is a boot partition and has content inside.", level=LOG_LEVELS.Debug)
- return False
+ elif self.target_mountpoint == '/boot':
+ try:
+ if self.has_content():
+ log(f"Partition {self} is a boot partition and has content inside.", level=LOG_LEVELS.Debug)
+ return False
+ except SysCallError as err:
+ log(err.message, LOG_LEVELS.Debug)
+ log(f"Partition {self} was identified as /boot but we could not mount to check for content, continuing!", level=LOG_LEVELS.Debug)
+ pass
return True
diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py
index f2a714e7..5b1b3c2a 100644
--- a/archinstall/lib/general.py
+++ b/archinstall/lib/general.py
@@ -76,7 +76,7 @@ class sys_command():#Thread):
"""
Stolen from archinstall_gui
"""
- def __init__(self, cmd, callback=None, start_callback=None, *args, **kwargs):
+ def __init__(self, cmd, callback=None, start_callback=None, peak_output=False, *args, **kwargs):
kwargs.setdefault("worker_id", gen_uid())
kwargs.setdefault("emulate", False)
kwargs.setdefault("suppress_errors", False)
@@ -86,13 +86,22 @@ class sys_command():#Thread):
if kwargs['emulate']:
self.log(f"Starting command '{cmd}' in emulation mode.", level=LOG_LEVELS.Debug)
- self.raw_cmd = cmd
- try:
- self.cmd = shlex.split(cmd)
- except Exception as e:
- raise ValueError(f'Incorrect string to split: {cmd}\n{e}')
+ if type(cmd) is list:
+ # if we get a list of arguments
+ self.raw_cmd = shlex.join(cmd)
+ self.cmd = cmd
+ else:
+ # else consider it a single shell string
+ # this should only be used if really necessary
+ self.raw_cmd = cmd
+ try:
+ self.cmd = shlex.split(cmd)
+ except Exception as e:
+ raise ValueError(f'Incorrect string to split: {cmd}\n{e}')
+
self.args = args
self.kwargs = kwargs
+ self.peak_output = peak_output
self.kwargs.setdefault("worker", None)
self.callback = callback
@@ -150,6 +159,38 @@ class sys_command():#Thread):
'exit_code': self.exit_code
}
+ def peak(self, output :str):
+ if type(output) == bytes:
+ try:
+ output = output.decode('UTF-8')
+ except UnicodeDecodeError:
+ return None
+
+ output = output.strip('\r\n ')
+ if len(output) <= 0:
+ return None
+
+ if self.peak_output:
+ from .user_interaction import get_terminal_width
+
+ # Move back to the beginning of the terminal
+ sys.stdout.flush()
+ sys.stdout.write("\033[%dG" % 0)
+ sys.stdout.flush()
+
+ # Clear the line
+ sys.stdout.write(" " * get_terminal_width())
+ sys.stdout.flush()
+
+ # Move back to the beginning again
+ sys.stdout.flush()
+ sys.stdout.write("\033[%dG" % 0)
+ sys.stdout.flush()
+
+ # And print the new output we're peaking on:
+ sys.stdout.write(output)
+ sys.stdout.flush()
+
def run(self):
self.status = 'running'
old_dir = os.getcwd()
@@ -181,6 +222,7 @@ class sys_command():#Thread):
for fileno, event in poller.poll(0.1):
try:
output = os.read(child_fd, 8192)
+ self.peak(output)
self.trace_log += output
except OSError:
alive = False
diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py
index 3586ad09..10f3970f 100644
--- a/archinstall/lib/hardware.py
+++ b/archinstall/lib/hardware.py
@@ -3,9 +3,7 @@ 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
+ return 'WIRELESS' in enrichIfaceTypes(list_interfaces().values()).values()
def hasAMDCPU():
if subprocess.check_output("lscpu | grep AMD", shell=True).strip().decode():
@@ -24,18 +22,12 @@ def graphicsDevices():
return cards
def hasNvidiaGraphics():
- if [x for x in graphicsDevices() if 'nvidia' in x]:
- return True
- return False
+ return any('nvidia' in x for x in graphicsDevices())
def hasAmdGraphics():
- if [x for x in graphicsDevices() if 'amd' in x]:
- return True
- return False
+ return any('amd' in x for x in graphicsDevices())
def hasIntelGraphics():
- if [x for x in graphicsDevices() if 'intel' in x]:
- return True
- return False
+ return any('intel' in x for x in graphicsDevices())
-# TODO: Add more identifiers \ No newline at end of file
+# TODO: Add more identifiers
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
index e38860d3..a99bc944 100644
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -10,6 +10,10 @@ from .systemd import Networkd
from .output import log, LOG_LEVELS
from .storage import storage
+# Any package that the Installer() is responsible for (optional and the default ones)
+__packages__ = ["base", "base-devel", "linux", "linux-firmware", "efibootmgr", "nano", "ntp", "iwd"]
+__base_packages__ = __packages__[:6]
+
class Installer():
"""
`Installer()` is the wrapper for most basic installation steps.
@@ -34,7 +38,7 @@ 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', logdir=None, logfile=None):
+ def __init__(self, partition, boot_partition, *, base_packages=__base_packages__, profile=None, mountpoint='/mnt', hostname='ArchInstalled', logdir=None, logfile=None):
self.profile = profile
self.hostname = hostname
self.mountpoint = mountpoint
@@ -52,7 +56,7 @@ class Installer():
'user' : False # Root counts as a user, if additional users are skipped.
}
- self.base_packages = base_packages.split(' ')
+ self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages
self.post_base_install = []
storage['session'] = self
@@ -133,7 +137,7 @@ class Installer():
self.log(f'Installing packages: {packages}', level=LOG_LEVELS.Info)
if (sync_mirrors := sys_command('/usr/bin/pacman -Syy')).exit_code == 0:
- if (pacstrap := sys_command(f'/usr/bin/pacstrap {self.mountpoint} {" ".join(packages)}', **kwargs)).exit_code == 0:
+ if (pacstrap := sys_command(f'/usr/bin/pacstrap {self.mountpoint} {" ".join(packages)}', peak_output=True, **kwargs)).exit_code == 0:
return True
else:
self.log(f'Could not strap in packages: {pacstrap.exit_code}', level=LOG_LEVELS.Info)
@@ -388,18 +392,10 @@ class Installer():
break
else:
log(f"Identifying root partition by PART-UUID on {self.partition}, looking for '{os.path.basename(self.partition.path)}'.", level=LOG_LEVELS.Debug)
- for root, folders, uids in os.walk('/dev/disk/by-partuuid'):
- for uid in uids:
- real_path = os.path.realpath(os.path.join(root, uid))
-
- log(f"Checking root partition match {os.path.basename(real_path)} against {os.path.basename(self.partition.path)}: {os.path.basename(real_path) == os.path.basename(self.partition.path)}", level=LOG_LEVELS.Debug)
- if not os.path.basename(real_path) == os.path.basename(self.partition.path): continue
+ entry.write(f'options root=PARTUUID={self.partition.uuid} rw intel_pstate=no_hwp\n')
- entry.write(f'options root=PARTUUID={uid} rw intel_pstate=no_hwp\n')
-
- self.helper_flags['bootloader'] = bootloader
- return True
- break
+ self.helper_flags['bootloader'] = bootloader
+ return True
raise RequirementError(f"Could not identify the UUID of {self.partition}, there for {self.mountpoint}/boot/loader/entries/arch.conf will be broken until fixed.")
elif bootloader == "grub-install":
diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py
index 19c21795..62067ec1 100644
--- a/archinstall/lib/luks.py
+++ b/archinstall/lib/luks.py
@@ -1,4 +1,5 @@
import os
+import shlex
from .exceptions import *
from .general import *
from .disk import Partition
@@ -64,9 +65,23 @@ class luks2():
with open(key_file, 'wb') as fh:
fh.write(password)
+ cryptsetup_args = shlex.join([
+ '/usr/bin/cryptsetup',
+ '--batch-mode',
+ '--verbose',
+ '--type', 'luks2',
+ '--pbkdf', 'argon2i',
+ '--hash', hash_type,
+ '--key-size', str(key_size),
+ '--iter-time', str(iter_time),
+ '--key-file', os.path.abspath(key_file),
+ '--use-urandom',
+ 'luksFormat', partition.path,
+ ])
+
try:
# Try to setup the crypt-device
- cmd_handle = sys_command(f'/usr/bin/cryptsetup -q -v --type luks2 --pbkdf argon2i --hash {hash_type} --key-size {key_size} --iter-time {iter_time} --key-file {os.path.abspath(key_file)} --use-urandom luksFormat {partition.path}')
+ cmd_handle = sys_command(cryptsetup_args)
except SysCallError as err:
if err.exit_code == 256:
log(f'{partition} is being used, trying to unmount and crypt-close the device and running one more attempt at encrypting the device.', level=LOG_LEVELS.Debug)
@@ -90,12 +105,12 @@ class luks2():
sys_command(f"cryptsetup close {child['name']}")
# Then try again to set up the crypt-device
- cmd_handle = sys_command(f'/usr/bin/cryptsetup -q -v --type luks2 --pbkdf argon2i --hash {hash_type} --key-size {key_size} --iter-time {iter_time} --key-file {os.path.abspath(key_file)} --use-urandom luksFormat {partition.path}')
+ cmd_handle = sys_command(cryptsetup_args)
else:
raise err
- if b'Command successful.' not in b''.join(cmd_handle):
- raise DiskError(f'Could not encrypt volume "{partition.path}": {o}')
+ if cmd_handle.exit_code != 0:
+ raise DiskError(f'Could not encrypt volume "{partition.path}": {cmd_output}')
return key_file
@@ -126,4 +141,4 @@ class luks2():
def format(self, path):
if (handle := sys_command(f"/usr/bin/cryptsetup -q -v luksErase {path}")).exit_code != 0:
- raise DiskError(f'Could not format {path} with {self.filesystem} because: {b"".join(handle)}') \ No newline at end of file
+ raise DiskError(f'Could not format {path} with {self.filesystem} because: {b"".join(handle)}')
diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py
index 77c9c6b2..70c21a67 100644
--- a/archinstall/lib/profiles.py
+++ b/archinstall/lib/profiles.py
@@ -193,6 +193,28 @@ class Profile(Script):
if hasattr(imported, '_post_install'):
return True
+ @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):
def __repr__(self, *args, **kwargs):
return f'Application({os.path.basename(self.profile)})'
diff --git a/archinstall/lib/systemd.py b/archinstall/lib/systemd.py
index edd75098..f2b7c9b3 100644
--- a/archinstall/lib/systemd.py
+++ b/archinstall/lib/systemd.py
@@ -26,15 +26,11 @@ class Ini():
return result
class Systemd(Ini):
- def __init__(self, *args, **kwargs):
- """
- Placeholder class to do systemd specific setups.
- """
- super(Systemd, self).__init__(*args, **kwargs)
+ """
+ Placeholder class to do systemd specific setups.
+ """
class Networkd(Systemd):
- def __init__(self, *args, **kwargs):
- """
- Placeholder class to do systemd-network specific setups.
- """
- super(Networkd, self).__init__(*args, **kwargs) \ No newline at end of file
+ """
+ Placeholder class to do systemd-network specific setups.
+ """
diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py
index f8b4d9c5..58f88bd2 100644
--- a/archinstall/lib/user_interaction.py
+++ b/archinstall/lib/user_interaction.py
@@ -274,9 +274,16 @@ def select_language(options, show_only_country_codes=True):
print(' -- You can enter ? or help to search for more languages --')
selected_language = input('Select one of the above keyboard languages (by number or full name): ')
if selected_language.lower() in ('?', 'help'):
- filter_string = input('Search for layout containing (example: "sv-"): ')
- new_options = search_keyboard_layout(filter_string)
- return select_language(new_options, show_only_country_codes=False)
+ while True:
+ filter_string = input('Search for layout containing (example: "sv-"): ')
+ new_options = list(search_keyboard_layout(filter_string))
+
+ if len(new_options) <= 0:
+ log(f"Search string '{filter_string}' yielded no results, please try another search or Ctrl+D to abort.", fg='yellow')
+ continue
+
+ return select_language(new_options, show_only_country_codes=False)
+
elif selected_language.isdigit() and (pos := int(selected_language)) <= len(languages)-1:
selected_language = languages[pos]
# I'm leaving "options" on purpose here.
diff --git a/docs/archinstall/general.rst b/docs/archinstall/general.rst
index 349393de..e5459749 100644
--- a/docs/archinstall/general.rst
+++ b/docs/archinstall/general.rst
@@ -22,7 +22,10 @@ Locale related
.. autofunction:: archinstall.search_keyboard_layout
-.. autofunction:: archinstall.set_keyboard_layout
+.. autofunction:: archinstall.set_keyboard_language
+
+..
+ autofunction:: archinstall.Installer.set_keyboard_layout
Services
========
@@ -34,7 +37,7 @@ Mirrors
.. autofunction:: archinstall.filter_mirrors_by_region
-.. autofunction:: archinstall.add_custom_mirror
+.. autofunction:: archinstall.add_custom_mirrors
.. autofunction:: archinstall.insert_mirrors
diff --git a/docs/examples/python.rst b/docs/examples/python.rst
index 04f2f43a..f6388738 100644
--- a/docs/examples/python.rst
+++ b/docs/examples/python.rst
@@ -9,9 +9,9 @@ The way the library is invoked in module mode is limited to executing scripts un
It's there for important to place any script or profile you wish to invoke in the examples folder prior to building and installing.
Pre-requisites
--------------
+--------------
-We'll assume you've followed the `installing.python.manual`_ method.
+We'll assume you've followed the :ref:`installing.python.manual` method.
Before actually installing the library, you will need to place your custom installer-scripts under `./archinstall/examples/` as a python file.
More on how you create these in the next section.
diff --git a/docs/index.rst b/docs/index.rst
index c03cc451..fea8f788 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -2,7 +2,7 @@ python-archinstall Documentation
================================
| **python-archinstall** *(or, archinstall for short)* is a helper library to install Arch Linux and manage services, packages and other things.
-| It comes packaged with different pre-configured installers, such as the `installing.guided`_ installer.
+| It comes packaged with different pre-configured installers, such as the :ref:`guided <installing.guided>` installer.
|
| A demo can be viewed here: `https://www.youtube.com/watch?v=9Xt7X_Iqg6E <https://www.youtube.com/watch?v=9Xt7X_Iqg6E>`_ which uses the default guided installer.
@@ -43,12 +43,14 @@ Some of the features of Archinstall are:
examples/python
examples/binary
+..
examples/scripting
.. toctree::
:maxdepth: 3
:caption: Programming Guide
+..
programming_guide/requirements
programming_guide/basic_concept
diff --git a/docs/installing/guided.rst b/docs/installing/guided.rst
index 92324589..40d86055 100644
--- a/docs/installing/guided.rst
+++ b/docs/installing/guided.rst
@@ -1,5 +1,3 @@
-.. _installing.guided:
-
Guided installation
===================
@@ -100,6 +98,8 @@ Default is :code:`Archinstall`
The hostname in which the machine will identify itself on the local network.
This step is optional, but a default hostname of `Archinstall` will be set if none is selected.
+.. _root_password:
+
Root password
-------------
@@ -115,7 +115,7 @@ Super User (sudo)
-----------------
.. warning::
- This step only applies if you correctly skipped the previous step :ref:`root_password`_ which also makes this step mandatory.
+ This step only applies if you correctly skipped :ref:`the previous step <root_password>` which also makes this step mandatory.
If the previous step was skipped, and only if it is skipped.
This step enables you to create a :code:`sudo` enabled user with a password.
diff --git a/docs/installing/python.rst b/docs/installing/python.rst
index 44606166..94cfb243 100644
--- a/docs/installing/python.rst
+++ b/docs/installing/python.rst
@@ -10,7 +10,7 @@ But the library can be installed manually as well.
This is not required if you're running archinstall on a pre-built ISO. The installation is only required if you're creating your own scripted installations.
Using pacman
-----------
+------------
Archinstall is on the `official repositories <https://wiki.archlinux.org/index.php/Official_repositories>`_.
@@ -44,7 +44,7 @@ Or as a user module:
Which will allow you to start using the library.
-.. _installing.python.manual
+.. _installing.python.manual:
Manual installation
-------------------
diff --git a/examples/guided.py b/examples/guided.py
index 85492a81..81cc2991 100644
--- a/examples/guided.py
+++ b/examples/guided.py
@@ -20,6 +20,9 @@ def sig_handler(sig, frame):
original_sigint_handler = signal.getsignal(signal.SIGINT)
signal.signal(signal.SIGINT, sig_handler)
+if archinstall.arguments.get('help'):
+ print("See `man archinstall` for help.")
+ exit(0)
def ask_user_questions():
"""
diff --git a/profiles/applications/awesome.py b/profiles/applications/awesome.py
index 578f246e..b8d779c0 100644
--- a/profiles/applications/awesome.py
+++ b/profiles/applications/awesome.py
@@ -1,10 +1,10 @@
import archinstall
+__packages__ = ["awesome", "xorg-xrandr", "xterm", "feh", "slock", "terminus-font", "gnu-free-fonts", "ttf-liberation", "xsel"]
+
installation.install_profile('xorg')
-installation.add_additional_packages(
- "awesome xorg-xrandr xterm feh slock terminus-font gnu-free-fonts ttf-liberation xsel"
-)
+installation.add_additional_packages(__packages__)
with open(f'{installation.mountpoint}/etc/X11/xinit/xinitrc', 'r') as xinitrc:
xinitrc_data = xinitrc.read()
diff --git a/profiles/applications/cinnamon.py b/profiles/applications/cinnamon.py
new file mode 100644
index 00000000..af1cbee2
--- /dev/null
+++ b/profiles/applications/cinnamon.py
@@ -0,0 +1,4 @@
+import archinstall
+
+installation.add_additional_packages("cinnamon system-config-printer gnome-keyring gnome-terminal blueberry metacity lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings")
+# We'll create a cinnamon-minimal later, but for now, we'll avoid issues by giving more than we need.
diff --git a/profiles/applications/xfce4.py b/profiles/applications/xfce4.py
new file mode 100644
index 00000000..6d6f8f7c
--- /dev/null
+++ b/profiles/applications/xfce4.py
@@ -0,0 +1,4 @@
+import archinstall
+
+installation.add_additional_packages("xfce4 xfce4-goodies lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings")
+# We'll create a xfce4-minimal later, but for now, we'll avoid issues by giving more than we need.
diff --git a/profiles/awesome.py b/profiles/awesome.py
index b914b175..8004fc62 100644
--- a/profiles/awesome.py
+++ b/profiles/awesome.py
@@ -2,6 +2,7 @@
import archinstall
+__packages__ = ['nano', 'nemo', 'gpicview-gtk3', 'chromium', 'openssh', 'sshfs', 'htop', 'scrot', 'wget']
def _prep_function(*args, **kwargs):
"""
@@ -28,14 +29,7 @@ if __name__ == 'awesome':
awesome = archinstall.Application(installation, 'awesome')
awesome.install()
- # Then setup and configure the desktop environment: awesome
- editor = "nano"
- filebrowser = "nemo gpicview-gtk3"
- webbrowser = "chromium" # TODO: Ask the user to select one instead
- utils = "openssh sshfs htop scrot wget"
-
-
- installation.add_additional_packages(f"{webbrowser} {utils} {filebrowser} {editor}")
+ installation.add_additional_packages(__packages__)
alacritty = archinstall.Application(installation, 'alacritty')
alacritty.install()
diff --git a/profiles/cinnamon.py b/profiles/cinnamon.py
new file mode 100644
index 00000000..e9c5d085
--- /dev/null
+++ b/profiles/cinnamon.py
@@ -0,0 +1,33 @@
+# A desktop environment using "Cinnamon"
+
+import archinstall
+
+def _prep_function(*args, **kwargs):
+ """
+ Magic function called by the importing installer
+ before continuing any further. It also avoids executing any
+ other code in this stage. So it's a safe way to ask the user
+ for more input before any other installer steps start.
+ """
+
+ # Cinnamon optionally supports xorg, we'll install it since it also
+ # includes graphic driver setups (this might change in the future)
+ profile = archinstall.Profile(None, 'xorg')
+ with profile.load_instructions(namespace='xorg.py') as imported:
+ if hasattr(imported, '_prep_function'):
+ return imported._prep_function()
+ else:
+ print('Deprecated (??): xorg profile has no _prep_function() anymore')
+
+# Ensures that this code only gets executed if executed
+# through importlib.util.spec_from_file_location("cinnamon", "/somewhere/cinnamon.py")
+# or through conventional import cinnamon
+if __name__ == 'cinnamon':
+ # Install dependency profiles
+ installation.install_profile('xorg')
+
+ # Install the application cinnamon from the template under /applications/
+ cinnamon = archinstall.Application(installation, 'cinnamon')
+ cinnamon.install()
+
+ installation.enable_service('lightdm') # Light Display Manager
diff --git a/profiles/desktop.py b/profiles/desktop.py
index 869cf0a0..0c89e543 100644
--- a/profiles/desktop.py
+++ b/profiles/desktop.py
@@ -10,7 +10,7 @@ def _prep_function(*args, **kwargs):
for more input before any other installer steps start.
"""
- supported_desktops = ['gnome', 'kde', 'awesome']
+ supported_desktops = ['gnome', 'kde', 'awesome', 'xfce4', 'cinnamon']
desktop = archinstall.generic_select(supported_desktops, 'Select your desired desktop environment: ')
# Temporarly store the selected desktop profile
diff --git a/profiles/xfce4.py b/profiles/xfce4.py
new file mode 100644
index 00000000..1cc4a62d
--- /dev/null
+++ b/profiles/xfce4.py
@@ -0,0 +1,34 @@
+
+# A desktop environment using "Xfce4"
+
+import archinstall
+
+def _prep_function(*args, **kwargs):
+ """
+ Magic function called by the importing installer
+ before continuing any further. It also avoids executing any
+ other code in this stage. So it's a safe way to ask the user
+ for more input before any other installer steps start.
+ """
+
+ # Gnome optionally supports xorg, we'll install it since it also
+ # includes graphic driver setups (this might change in the future)
+ profile = archinstall.Profile(None, 'xorg')
+ with profile.load_instructions(namespace='xorg.py') as imported:
+ if hasattr(imported, '_prep_function'):
+ return imported._prep_function()
+ else:
+ print('Deprecated (??): xorg profile has no _prep_function() anymore')
+
+# Ensures that this code only gets executed if executed
+# through importlib.util.spec_from_file_location("xfce4", "/somewhere/xfce4.py")
+# or through conventional import xfce4
+if __name__ == 'xfce4':
+ # Install dependency profiles
+ installation.install_profile('xorg')
+
+ # Install the application xfce4 from the template under /applications/
+ xfce = archinstall.Application(installation, 'xfce4')
+ xfce.install()
+
+ installation.enable_service('lightdm') # Light Display Manager