Merge branch 'master' into misc-cleanup
This commit is contained in:
commit
0c6ebc7e80
|
|
@ -3,6 +3,10 @@
|
|||
name: Build Arch ISO with ArchInstall Commit
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main # In case we adopt this convention in the future
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
|
|
|
|||
|
|
@ -21,11 +21,10 @@ jobs:
|
|||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools wheel twine
|
||||
pip install setuptools wheel flit
|
||||
- name: Build and publish
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||
FLIT_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
||||
FLIT_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||
run: |
|
||||
python setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
||||
flit publish
|
||||
|
|
|
|||
|
|
@ -21,3 +21,5 @@ SAFETY_LOCK
|
|||
**/**.target
|
||||
**/**.qcow2
|
||||
**/test.py
|
||||
**/archiso
|
||||
/guided.py
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
[distutils]
|
||||
index-servers =
|
||||
pypi
|
||||
|
||||
[pypi]
|
||||
repository = https://upload.pypi.org/legacy/
|
||||
|
|
@ -95,10 +95,10 @@ with archinstall.Installer('/mnt') as installation:
|
|||
This installer will perform the following:
|
||||
|
||||
* Prompt the user to select a disk and disk-password
|
||||
* Proceed to wipe the selected disk with a `GPT` partition table.
|
||||
* Proceed to wipe the selected disk with a `GPT` partition table on a UEFI system and MBR on a bios system.
|
||||
* Sets up a default 100% used disk with encryption.
|
||||
* Installs a basic instance of Arch Linux *(base base-devel linux linux-firmware btrfs-progs efibootmgr)*
|
||||
* Installs and configures a bootloader to partition 0.
|
||||
* 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 profile with a window manager called [awesome](https://github.com/archlinux/archinstall/blob/master/profiles/awesome.py) *(more on profile installations in the [documentation](https://python-archinstall.readthedocs.io/en/latest/archinstall/Profile.html))*.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
"""Arch Linux installer - guided, templates etc."""
|
||||
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 *
|
||||
|
|
@ -14,7 +15,7 @@ from .lib.output import *
|
|||
from .lib.storage import *
|
||||
from .lib.hardware import *
|
||||
|
||||
__version__ = "2.1.4"
|
||||
__version__ = "2.2.0"
|
||||
|
||||
## Basic version of arg.parse() supporting:
|
||||
## --key=value
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from .exceptions import DiskError
|
|||
from .general import *
|
||||
from .output import log, LOG_LEVELS
|
||||
from .storage import storage
|
||||
from .hardware import hasUEFI
|
||||
|
||||
ROOT_DIR_PATTERN = re.compile('^.*?/devices')
|
||||
GPT = 0b00000001
|
||||
|
|
@ -73,7 +74,7 @@ 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']
|
||||
|
|
@ -94,10 +95,10 @@ class BlockDevice():
|
|||
|
||||
@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}')
|
||||
|
|
@ -427,7 +428,7 @@ class Filesystem():
|
|||
# TODO:
|
||||
# When instance of a HDD is selected, check all usages and gracefully unmount them
|
||||
# as well as close any crypto handles.
|
||||
def __init__(self, blockdevice, mode=GPT):
|
||||
def __init__(self, blockdevice,mode):
|
||||
self.blockdevice = blockdevice
|
||||
self.mode = mode
|
||||
|
||||
|
|
@ -440,6 +441,11 @@ class Filesystem():
|
|||
return self
|
||||
else:
|
||||
raise DiskError(f'Problem setting the partition format to GPT:', f'/usr/bin/parted -s {self.blockdevice.device} mklabel gpt')
|
||||
elif self.mode == MBR:
|
||||
if sys_command(f'/usr/bin/parted -s {self.blockdevice.device} mklabel msdos').exit_code == 0:
|
||||
return self
|
||||
else:
|
||||
raise DiskError(f'Problem setting the partition format to GPT:', f'/usr/bin/parted -s {self.blockdevice.device} mklabel msdos')
|
||||
else:
|
||||
raise DiskError(f'Unknown mode selected to format in: {self.mode}')
|
||||
|
||||
|
|
@ -482,28 +488,39 @@ class Filesystem():
|
|||
|
||||
def use_entire_disk(self, root_filesystem_type='ext4'):
|
||||
log(f"Using and formatting the entire {self.blockdevice}.", level=LOG_LEVELS.Debug)
|
||||
self.add_partition('primary', start='1MiB', end='513MiB', format='fat32')
|
||||
self.set_name(0, 'EFI')
|
||||
self.set(0, 'boot on')
|
||||
# TODO: Probably redundant because in GPT mode 'esp on' is an alias for "boot on"?
|
||||
# https://www.gnu.org/software/parted/manual/html_node/set.html
|
||||
self.set(0, 'esp on')
|
||||
self.add_partition('primary', start='513MiB', end='100%')
|
||||
if hasUEFI():
|
||||
self.add_partition('primary', start='1MiB', end='513MiB', format='fat32')
|
||||
self.set_name(0, 'EFI')
|
||||
self.set(0, 'boot on')
|
||||
# TODO: Probably redundant because in GPT mode 'esp on' is an alias for "boot on"?
|
||||
# https://www.gnu.org/software/parted/manual/html_node/set.html
|
||||
self.set(0, 'esp on')
|
||||
self.add_partition('primary', start='513MiB', end='100%')
|
||||
|
||||
self.blockdevice.partition[0].filesystem = 'vfat'
|
||||
self.blockdevice.partition[1].filesystem = root_filesystem_type
|
||||
log(f"Set the root partition {self.blockdevice.partition[1]} to use filesystem {root_filesystem_type}.", level=LOG_LEVELS.Debug)
|
||||
self.blockdevice.partition[0].filesystem = 'vfat'
|
||||
self.blockdevice.partition[1].filesystem = root_filesystem_type
|
||||
log(f"Set the root partition {self.blockdevice.partition[1]} to use filesystem {root_filesystem_type}.", level=LOG_LEVELS.Debug)
|
||||
|
||||
self.blockdevice.partition[0].target_mountpoint = '/boot'
|
||||
self.blockdevice.partition[1].target_mountpoint = '/'
|
||||
self.blockdevice.partition[0].target_mountpoint = '/boot'
|
||||
self.blockdevice.partition[1].target_mountpoint = '/'
|
||||
|
||||
self.blockdevice.partition[0].allow_formatting = True
|
||||
self.blockdevice.partition[1].allow_formatting = True
|
||||
self.blockdevice.partition[0].allow_formatting = True
|
||||
self.blockdevice.partition[1].allow_formatting = True
|
||||
else:
|
||||
#we don't need a seprate boot partition it would be a waste of space
|
||||
self.add_partition('primary', start='1MB', end='100%')
|
||||
self.blockdevice.partition[0].filesystem=root_filesystem_type
|
||||
log(f"Set the root partition {self.blockdevice.partition[0]} to use filesystem {root_filesystem_type}.", level=LOG_LEVELS.Debug)
|
||||
self.blockdevice.partition[0].target_mountpoint = '/'
|
||||
self.blockdevice.partition[0].allow_formatting = True
|
||||
|
||||
def add_partition(self, type, start, end, format=None):
|
||||
log(f'Adding partition to {self.blockdevice}', level=LOG_LEVELS.Info)
|
||||
|
||||
previous_partitions = self.blockdevice.partitions
|
||||
if self.mode == MBR:
|
||||
if len(self.blockdevice.partitions)>3:
|
||||
DiskError("Too many partitions on disk, MBR disks can only have 3 parimary partitions")
|
||||
if format:
|
||||
partitioning = self.parted(f'{self.blockdevice.device} mkpart {type} {format} {start} {end}') == 0
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class sys_command():#Thread):
|
|||
"""
|
||||
Stolen from archinstall_gui
|
||||
"""
|
||||
def __init__(self, cmd, callback=None, start_callback=None, environment_vars={}, *args, **kwargs):
|
||||
def __init__(self, cmd, callback=None, start_callback=None, peak_output=False, environment_vars={}, *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.environment_vars = environment_vars
|
||||
|
||||
self.kwargs.setdefault("worker", None)
|
||||
|
|
@ -151,6 +160,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()
|
||||
|
|
@ -182,6 +223,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
|
||||
|
|
|
|||
|
|
@ -1,14 +1,42 @@
|
|||
import os
|
||||
import os, subprocess, json
|
||||
from .general import sys_command
|
||||
from .networking import list_interfaces, enrichIfaceTypes
|
||||
from typing import Optional
|
||||
|
||||
def hasWifi():
|
||||
AVAILABLE_GFX_DRIVERS = {
|
||||
# Sub-dicts are layer-2 options to be selected
|
||||
# and lists are a list of packages to be installed
|
||||
'AMD / ATI' : {
|
||||
'amd' : ['xf86-video-amdgpu'],
|
||||
'ati' : ['xf86-video-ati']
|
||||
},
|
||||
'intel' : ['xf86-video-intel'],
|
||||
'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:
|
||||
return 'WIRELESS' in enrichIfaceTypes(list_interfaces().values()).values()
|
||||
|
||||
def hasUEFI():
|
||||
def hasAMDCPU()->bool:
|
||||
if subprocess.check_output("lscpu | grep AMD", shell=True).strip().decode():
|
||||
return True
|
||||
return False
|
||||
def hasIntelCPU()->bool:
|
||||
if subprocess.check_output("lscpu | grep Intel", shell=True).strip().decode():
|
||||
return True
|
||||
return False
|
||||
|
||||
def hasUEFI()->bool:
|
||||
return os.path.isdir('/sys/firmware/efi')
|
||||
|
||||
def graphicsDevices():
|
||||
def graphicsDevices()->dict:
|
||||
cards = {}
|
||||
for line in sys_command(f"lspci"):
|
||||
if b' VGA ' in line:
|
||||
|
|
@ -16,13 +44,28 @@ def graphicsDevices():
|
|||
cards[identifier.strip().lower().decode('UTF-8')] = line
|
||||
return cards
|
||||
|
||||
def hasNvidiaGraphics():
|
||||
def hasNvidiaGraphics()->bool:
|
||||
return any('nvidia' in x for x in graphicsDevices())
|
||||
|
||||
def hasAmdGraphics():
|
||||
def hasAmdGraphics()->bool:
|
||||
return any('amd' in x for x in graphicsDevices())
|
||||
|
||||
def hasIntelGraphics():
|
||||
def hasIntelGraphics()->bool:
|
||||
return any('intel' in x for x in graphicsDevices())
|
||||
|
||||
|
||||
def cpuVendor()-> Optional[str]:
|
||||
cpu_info = json.loads(subprocess.check_output("lscpu -J", shell=True).decode('utf-8'))['lscpu']
|
||||
for info in cpu_info:
|
||||
if info.get('field',None):
|
||||
if info.get('field',None) == "Vendor ID:":
|
||||
return info.get('data',None)
|
||||
|
||||
def isVM() -> bool:
|
||||
try:
|
||||
subprocess.check_call(["systemd-detect-virt"]) # systemd-detect-virt issues a none 0 exit code if it is not on a virtual machine
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
# TODO: Add more identifiers
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ from .mirrors import *
|
|||
from .systemd import Networkd
|
||||
from .output import log, LOG_LEVELS
|
||||
from .storage import storage
|
||||
from .hardware import *
|
||||
|
||||
# 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():
|
||||
"""
|
||||
|
|
@ -18,7 +23,7 @@ class Installer():
|
|||
:param partition: Requires a partition as the first argument, this is
|
||||
so that the installer can mount to `mountpoint` and strap packages there.
|
||||
:type partition: class:`archinstall.Partition`
|
||||
|
||||
|
||||
:param boot_partition: There's two reasons for needing a boot partition argument,
|
||||
The first being so that `mkinitcpio` can place the `vmlinuz` kernel at the right place
|
||||
during the `pacstrap` or `linux` and the base packages for a minimal installation.
|
||||
|
|
@ -29,12 +34,13 @@ class Installer():
|
|||
:param profile: A profile to install, this is optional and can be called later manually.
|
||||
This just simplifies the process by not having to call :py:func:`~archinstall.Installer.install_profile` later on.
|
||||
:type profile: str, optional
|
||||
|
||||
|
||||
:param hostname: The given /etc/hostname for the machine.
|
||||
:type hostname: str, optional
|
||||
|
||||
"""
|
||||
def __init__(self, target, *, base_packages='base base-devel linux linux-firmware efibootmgr'):
|
||||
def __init__(self, target, *, base_packages='base base-devel linux linux-firmware', kernels='linux'):
|
||||
base_packages = base_packages + kernels.replace(',', ' ')
|
||||
self.target = target
|
||||
self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S')
|
||||
self.milliseconds = int(str(time.time()).split('.')[1])
|
||||
|
|
@ -43,8 +49,12 @@ class Installer():
|
|||
'base' : False,
|
||||
'bootloader' : False
|
||||
}
|
||||
|
||||
|
||||
self.base_packages = base_packages.split(' ') if type(base_packages) is str else base_packages
|
||||
if hasUEFI():
|
||||
self.base_packages.append("efibootmgr")
|
||||
else:
|
||||
self.base_packages.append("grub")
|
||||
self.post_base_install = []
|
||||
|
||||
storage['session'] = self
|
||||
|
|
@ -141,7 +151,7 @@ class Installer():
|
|||
|
||||
if not os.path.isfile(f'{self.target}/etc/fstab'):
|
||||
raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n{fstab}')
|
||||
|
||||
|
||||
return True
|
||||
|
||||
def set_hostname(self, hostname :str, *args, **kwargs):
|
||||
|
|
@ -223,7 +233,7 @@ class Installer():
|
|||
# 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()
|
||||
# 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):
|
||||
|
|
@ -273,6 +283,7 @@ class Installer():
|
|||
## (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.
|
||||
|
||||
MODULES = []
|
||||
BINARIES = []
|
||||
FILES = []
|
||||
|
|
@ -298,10 +309,20 @@ class Installer():
|
|||
if 'encrypt' not in HOOKS:
|
||||
HOOKS.insert(HOOKS.index('filesystems'), 'encrypt')
|
||||
|
||||
if not(hasUEFI()): # TODO: Allow for grub even on EFI
|
||||
self.base_packages.append('grub')
|
||||
|
||||
self.pacstrap(self.base_packages)
|
||||
self.helper_flags['base-strapped'] = True
|
||||
#self.genfstab()
|
||||
|
||||
if not isVM():
|
||||
vendor = cpuVendor()
|
||||
if vendor == "AuthenticAMD":
|
||||
self.base_packages.append("amd-ucode")
|
||||
elif vendor == "GenuineIntel":
|
||||
self.base_packages.append("intel-ucode")
|
||||
else:
|
||||
self.log("Unknown cpu vendor not installing ucode")
|
||||
with open(f"{self.target}/etc/fstab", "a") as fstab:
|
||||
fstab.write(
|
||||
"\ntmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0\n"
|
||||
|
|
@ -322,7 +343,7 @@ class Installer():
|
|||
mkinit.write(f"BINARIES=({' '.join(BINARIES)})\n")
|
||||
mkinit.write(f"FILES=({' '.join(FILES)})\n")
|
||||
mkinit.write(f"HOOKS=({' '.join(HOOKS)})\n")
|
||||
sys_command(f'/usr/bin/arch-chroot {self.target} mkinitcpio -p linux')
|
||||
sys_command(f'/usr/bin/arch-chroot {self.target} mkinitcpio -P')
|
||||
|
||||
self.helper_flags['base'] = True
|
||||
|
||||
|
|
@ -345,6 +366,8 @@ class Installer():
|
|||
self.log(f'Adding bootloader {bootloader} to {boot_partition}', level=LOG_LEVELS.Info)
|
||||
|
||||
if bootloader == 'systemd-bootctl':
|
||||
if not hasUEFI():
|
||||
raise HardwareIncompatibilityError
|
||||
# TODO: Ideally we would want to check if another config
|
||||
# points towards the same disk and/or partition.
|
||||
# And in which case we should do some clean up.
|
||||
|
|
@ -372,13 +395,20 @@ class Installer():
|
|||
## For some reason, blkid and /dev/disk/by-uuid are not getting along well.
|
||||
## And blkid is wrong in terms of LUKS.
|
||||
#UUID = sys_command('blkid -s PARTUUID -o value {drive}{partition_2}'.format(**args)).decode('UTF-8').strip()
|
||||
|
||||
# Setup the loader entry
|
||||
with open(f'{self.target}/boot/loader/entries/{self.init_time}.conf', 'w') as entry:
|
||||
entry.write(f'# Created by: archinstall\n')
|
||||
entry.write(f'# Created on: {self.init_time}\n')
|
||||
entry.write(f'title Arch Linux\n')
|
||||
entry.write(f'linux /vmlinuz-linux\n')
|
||||
if not isVM():
|
||||
vendor = cpuVendor()
|
||||
if vendor == "AuthenticAMD":
|
||||
entry.write("initrd /amd-ucode.img\n")
|
||||
elif vendor == "GenuineIntel":
|
||||
entry.write("initrd /intel-ucode.img\n")
|
||||
else:
|
||||
self.log("unknow cpu vendor, not adding ucode to systemd-boot config")
|
||||
entry.write(f'initrd /initramfs-linux.img\n')
|
||||
## blkid doesn't trigger on loopback devices really well,
|
||||
## so we'll use the old manual method until we get that sorted out.
|
||||
|
|
@ -396,9 +426,21 @@ class Installer():
|
|||
self.helper_flags['bootloader'] = bootloader
|
||||
return True
|
||||
|
||||
raise RequirementError(f"Could not identify the UUID of {root_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":
|
||||
if hasUEFI():
|
||||
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')
|
||||
return True
|
||||
else:
|
||||
root_device = subprocess.check_output(f'basename "$(readlink -f /sys/class/block/{root_partition.path.replace("/dev/","")}/..)"', shell=True).decode().strip()
|
||||
if root_device == "block":
|
||||
root_device = f"{root_partition.path}"
|
||||
o = b''.join(sys_command(f'/usr/bin/arch-chroot {self.target} grub-install --target=i386-pc /dev/{root_device}'))
|
||||
sys_command('/usr/bin/arch-chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg')
|
||||
return True
|
||||
else:
|
||||
raise RequirementError(f"Unknown (or not yet implemented) bootloader added to add_bootloader(): {bootloader}")
|
||||
raise RequirementError(f"Unknown (or not yet implemented) bootloader requested: {bootloader}")
|
||||
|
||||
def add_additional_packages(self, *packages):
|
||||
return self.pacstrap(*packages)
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ class Profile(Script):
|
|||
if hasattr(imported, '_prep_function'):
|
||||
return True
|
||||
return False
|
||||
"""
|
||||
|
||||
def has_post_install(self):
|
||||
with open(self.path, 'r') as source:
|
||||
source_data = source.read()
|
||||
|
|
@ -198,7 +198,6 @@ class Profile(Script):
|
|||
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:
|
||||
|
|
@ -229,6 +228,49 @@ class Profile(Script):
|
|||
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):
|
||||
def __repr__(self, *args, **kwargs):
|
||||
return f'Application({os.path.basename(self.profile)})'
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from .locale_helpers import list_keyboard_languages, verify_keyboard_layout, sea
|
|||
from .output import log, LOG_LEVELS
|
||||
from .storage import storage
|
||||
from .networking import list_interfaces
|
||||
from .general import sys_command
|
||||
from .hardware import AVAILABLE_GFX_DRIVERS, hasUEFI
|
||||
|
||||
## TODO: Some inconsistencies between the selection processes.
|
||||
## Some return the keys from the options, some the values?
|
||||
|
|
@ -131,7 +133,7 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan
|
|||
|
||||
def ask_for_a_timezone():
|
||||
while True:
|
||||
timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip()
|
||||
timezone = input('Enter a valid timezone (examples: Europe/Stockholm, US/Eastern) or press enter to use UTC: ').strip().strip('*.')
|
||||
if timezone == '':
|
||||
timezone = 'UTC'
|
||||
if (pathlib.Path("/usr")/"share"/"zoneinfo"/timezone).exists():
|
||||
|
|
@ -142,7 +144,17 @@ def ask_for_a_timezone():
|
|||
level=LOG_LEVELS.Warning,
|
||||
fg='red'
|
||||
)
|
||||
|
||||
|
||||
def ask_for_bootloader() -> str:
|
||||
bootloader = "systemd-bootctl"
|
||||
if hasUEFI()==False:
|
||||
bootloader="grub-install"
|
||||
else:
|
||||
bootloader_choice = input("Would you like to use GRUB as a bootloader instead of systemd-boot? [y/N] ").lower()
|
||||
if bootloader_choice == "y":
|
||||
bootloader="grub-install"
|
||||
return bootloader
|
||||
|
||||
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()
|
||||
|
|
@ -319,10 +331,13 @@ def select_disk(dict_o_disks):
|
|||
if len(drives) >= 1:
|
||||
for index, drive in enumerate(drives):
|
||||
print(f"{index}: {drive} ({dict_o_disks[drive]['size'], dict_o_disks[drive].device, dict_o_disks[drive]['label']})")
|
||||
drive = generic_select(drives, 'Select one of the above disks (by number or full path) or leave blank to skip partitioning: ',
|
||||
|
||||
log(f"You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)", fg="yellow")
|
||||
drive = generic_select(drives, 'Select one of the above disks (by name or number) or leave blank to use /mnt: ',
|
||||
options_output=False)
|
||||
if not drive:
|
||||
return drive
|
||||
|
||||
drive = dict_o_disks[drive]
|
||||
return drive
|
||||
|
||||
|
|
@ -453,3 +468,70 @@ def select_mirror_regions(mirrors, show_top_mirrors=True):
|
|||
|
||||
selected_mirrors[selected_mirror] = mirrors[selected_mirror]
|
||||
return selected_mirrors
|
||||
|
||||
raise RequirementError("Selecting mirror region require a least one region to be given as an option.")
|
||||
|
||||
def select_driver(options=AVAILABLE_GFX_DRIVERS):
|
||||
"""
|
||||
Some what convoluted function, which's job is simple.
|
||||
Select a graphics driver from a pre-defined set of popular options.
|
||||
|
||||
(The template xorg is for beginner users, not advanced, and should
|
||||
there for appeal to the general public first and edge cases later)
|
||||
"""
|
||||
drivers = sorted(list(options))
|
||||
|
||||
if len(drivers) >= 1:
|
||||
for index, driver in enumerate(drivers):
|
||||
print(f"{index}: {driver}")
|
||||
|
||||
print(' -- The above list are supported graphic card drivers. --')
|
||||
print(' -- You need to select (and read about) which one you need. --')
|
||||
|
||||
lspci = sys_command(f'/usr/bin/lspci')
|
||||
for line in lspci.trace_log.split(b'\r\n'):
|
||||
if b' vga ' in line.lower():
|
||||
if b'nvidia' in line.lower():
|
||||
print(' ** nvidia card detected, suggested driver: nvidia **')
|
||||
elif b'amd' in line.lower():
|
||||
print(' ** AMD card detected, suggested driver: AMD / ATI **')
|
||||
|
||||
selected_driver = input('Select your graphics card driver: ')
|
||||
initial_option = selected_driver
|
||||
|
||||
# Disabled search for now, only a few profiles exist anyway
|
||||
#
|
||||
#print(' -- You can enter ? or help to search for more drivers --')
|
||||
#if selected_driver.lower() in ('?', 'help'):
|
||||
# filter_string = input('Search for layout containing (example: "sv-"): ')
|
||||
# new_options = search_keyboard_layout(filter_string)
|
||||
# return select_language(new_options)
|
||||
if selected_driver.isdigit() and (pos := int(selected_driver)) <= len(drivers)-1:
|
||||
selected_driver = options[drivers[pos]]
|
||||
elif selected_driver in options:
|
||||
selected_driver = options[options.index(selected_driver)]
|
||||
elif len(selected_driver) == 0:
|
||||
raise RequirementError("At least one graphics driver is needed to support a graphical environment. Please restart the installer and try again.")
|
||||
else:
|
||||
raise RequirementError("Selected driver does not exist.")
|
||||
|
||||
if type(selected_driver) == dict:
|
||||
driver_options = sorted(list(selected_driver))
|
||||
for index, driver_package_group in enumerate(driver_options):
|
||||
print(f"{index}: {driver_package_group}")
|
||||
|
||||
selected_driver_package_group = input(f'Which driver-type do you want for {initial_option}: ')
|
||||
if selected_driver_package_group.isdigit() and (pos := int(selected_driver_package_group)) <= len(driver_options)-1:
|
||||
selected_driver_package_group = selected_driver[driver_options[pos]]
|
||||
elif selected_driver_package_group in selected_driver:
|
||||
selected_driver_package_group = selected_driver[selected_driver.index(selected_driver_package_group)]
|
||||
elif len(selected_driver_package_group) == 0:
|
||||
raise RequirementError(f"At least one driver package is required for a graphical environment using {selected_driver}. Please restart the installer and try again.")
|
||||
else:
|
||||
raise RequirementError(f"Selected driver-type does not exist for {initial_option}.")
|
||||
|
||||
return selected_driver_package_group
|
||||
|
||||
return selected_driver
|
||||
|
||||
raise RequirementError("Selecting drivers require a least one profile to be given as an option.")
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Packages
|
|||
========
|
||||
|
||||
.. autofunction:: archinstall.find_package
|
||||
|
||||
Be
|
||||
.. autofunction:: archinstall.find_packages
|
||||
|
||||
Locale related
|
||||
|
|
|
|||
|
|
@ -2,18 +2,8 @@
|
|||
|
||||
# New features *(v2.2.0)*
|
||||
|
||||
Merge new features in to `torxed-v2.2.0`.<br>
|
||||
This branch is designated for potential breaking changes, added complexity and new functionality.
|
||||
|
||||
# Bug fixes *(v2.1.4)*
|
||||
|
||||
Merge against `master` for bug fixes and anything that improves stability and quality of life.<br>
|
||||
This excludes:
|
||||
* New functionality
|
||||
* Added complexity
|
||||
* Breaking changes
|
||||
|
||||
Any changes to `master` automatically gets pulled in to `torxed-v2.2.0` to avoid merge hell.
|
||||
All future work towards *`v2.2.0`* is done against `master` now.<br>
|
||||
Any patch work to existing verions will have to create a new branch against the tagged versions.
|
||||
|
||||
# Describe your PR
|
||||
|
||||
|
|
@ -23,6 +13,4 @@ If the PR is larger than ~20 lines, please describe it here unless described in
|
|||
# Testing
|
||||
|
||||
Any new feature or stability improvement should be tested if possible.
|
||||
Please follow the test instructions at the bottom of the README.
|
||||
|
||||
*These PR guidelines will change after 2021-05-01, which is when `v2.1.4` gets onto the new ISO*
|
||||
Please follow the test instructions at the bottom of the README or use the ISO built on each PR.
|
||||
|
|
@ -2,10 +2,11 @@ import getpass, time, json, os
|
|||
import archinstall
|
||||
from archinstall.lib.hardware import hasUEFI
|
||||
from archinstall.lib.profiles import Profile
|
||||
from archinstall.lib.user_interaction import generic_select
|
||||
|
||||
if hasUEFI() is False:
|
||||
archinstall.log("ArchInstall currently only supports machines booted with UEFI.\nMBR & GRUB support is coming in version 2.2.0!", fg="red", level=archinstall.LOG_LEVELS.Error)
|
||||
exit(1)
|
||||
if archinstall.arguments.get('help'):
|
||||
print("See `man archinstall` for help.")
|
||||
exit(0)
|
||||
|
||||
def ask_user_questions():
|
||||
"""
|
||||
|
|
@ -64,6 +65,7 @@ def ask_user_questions():
|
|||
partition_mountpoints[partition] = None
|
||||
except archinstall.UnknownFilesystemFormat as err:
|
||||
archinstall.log(f" {partition} (Filesystem not supported)", fg='red')
|
||||
|
||||
|
||||
# We then ask what to do with the partitions.
|
||||
if (option := archinstall.ask_for_disk_layout()) == 'abort':
|
||||
|
|
@ -148,7 +150,7 @@ def ask_user_questions():
|
|||
if (passwd := archinstall.get_password(prompt='Enter disk encryption password (leave blank for no encryption): ')):
|
||||
archinstall.arguments['!encryption-password'] = passwd
|
||||
archinstall.arguments['harddrive'].encryption_password = archinstall.arguments['!encryption-password']
|
||||
|
||||
archinstall.arguments["bootloader"] = archinstall.ask_for_bootloader()
|
||||
# Get the hostname for the machine
|
||||
if not archinstall.arguments.get('hostname', None):
|
||||
archinstall.arguments['hostname'] = input('Desired hostname for the installation: ').strip(' ')
|
||||
|
|
@ -185,7 +187,6 @@ def ask_user_questions():
|
|||
|
||||
# Ask about audio server selection if one is not already set
|
||||
if not archinstall.arguments.get('audio', None):
|
||||
|
||||
# only ask for audio server selection on a desktop profile
|
||||
if str(archinstall.arguments['profile']) == 'Profile(desktop)':
|
||||
archinstall.arguments['audio'] = archinstall.ask_for_audio_selection()
|
||||
|
|
@ -194,6 +195,15 @@ def ask_user_questions():
|
|||
# we will not try to remove packages post-installation to not have audio, as that may cause multiple issues
|
||||
archinstall.arguments['audio'] = None
|
||||
|
||||
# Ask what kernel user wants:
|
||||
if not archinstall.arguments.get("kernels", None):
|
||||
archinstall.log(f"Here you can choose which kernel to use, leave blank for default which is 'linux'.")
|
||||
|
||||
if (kernel := generic_select(["linux", "linux-lts", "linux-zen", "continue"], "choose a kernel:")):
|
||||
archinstall.arguments['kernels'] = kernel
|
||||
else:
|
||||
archinstall.arguments['kernels'] = 'linux'
|
||||
|
||||
# Additional packages (with some light weight error handling for invalid package names)
|
||||
print("Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.")
|
||||
print("If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.")
|
||||
|
|
@ -246,7 +256,11 @@ def perform_installation_steps():
|
|||
Setup the blockdevice, filesystem (and optionally encryption).
|
||||
Once that's done, we'll hand over to perform_installation()
|
||||
"""
|
||||
with archinstall.Filesystem(archinstall.arguments['harddrive'], archinstall.GPT) as fs:
|
||||
mode = archinstall.GPT
|
||||
if hasUEFI() is False:
|
||||
mode = archinstall.MBR
|
||||
|
||||
with archinstall.Filesystem(archinstall.arguments['harddrive'], mode) as fs:
|
||||
# Wipe the entire drive if the disk flag `keep_partitions`is False.
|
||||
if archinstall.arguments['harddrive'].keep_partitions is False:
|
||||
fs.use_entire_disk(root_filesystem_type=archinstall.arguments.get('filesystem', 'btrfs'))
|
||||
|
|
@ -269,8 +283,8 @@ def perform_installation_steps():
|
|||
partition.format()
|
||||
else:
|
||||
archinstall.log(f"Did not format {partition} because .safe_to_format() returned False or .allow_formatting was False.", level=archinstall.LOG_LEVELS.Debug)
|
||||
|
||||
fs.find_partition('/boot').format('vfat')
|
||||
if hasUEFI():
|
||||
fs.find_partition('/boot').format('vfat')# we don't have a boot partition in bios mode
|
||||
|
||||
if archinstall.arguments.get('!encryption-password', None):
|
||||
# First encrypt and unlock, then format the desired partition inside the encrypted part.
|
||||
|
|
@ -282,8 +296,8 @@ def perform_installation_steps():
|
|||
else:
|
||||
fs.find_partition('/').format(fs.find_partition('/').filesystem)
|
||||
fs.find_partition('/').mount('/mnt')
|
||||
|
||||
fs.find_partition('/boot').mount('/mnt/boot')
|
||||
if hasUEFI():
|
||||
fs.find_partition('/boot').mount('/mnt/boot')
|
||||
|
||||
perform_installation('/mnt')
|
||||
|
||||
|
|
@ -294,7 +308,7 @@ def perform_installation(mountpoint):
|
|||
Only requirement is that the block devices are
|
||||
formatted and setup prior to entering this function.
|
||||
"""
|
||||
with archinstall.Installer(mountpoint) as installation:
|
||||
with archinstall.Installer(mountpoint, kernels=archinstall.arguments.get('kernels', 'linux')) as installation:
|
||||
## if len(mirrors):
|
||||
# Certain services might be running that affects the system during installation.
|
||||
# Currently, only one such service is "reflector.service" which updates /etc/pacman.d/mirrorlist
|
||||
|
|
@ -302,17 +316,17 @@ def perform_installation(mountpoint):
|
|||
installation.log(f'Waiting for automatic mirror selection (reflector) to complete.', level=archinstall.LOG_LEVELS.Info)
|
||||
while archinstall.service_state('reflector') not in ('dead', 'failed'):
|
||||
time.sleep(1)
|
||||
|
||||
# Set mirrors used by pacstrap (outside of installation)
|
||||
if archinstall.arguments.get('mirror-region', None):
|
||||
archinstall.use_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors for the live medium
|
||||
|
||||
if installation.minimal_installation():
|
||||
installation.set_hostname(archinstall.arguments['hostname'])
|
||||
if archinstall.arguments['mirror-region'].get("mirrors",{})!= None:
|
||||
installation.set_mirrors(archinstall.arguments['mirror-region']) # Set the mirrors in the installation medium
|
||||
if archinstall.arguments["bootloader"]=="grub-install" and hasUEFI()==True:
|
||||
installation.add_additional_packages("grub")
|
||||
installation.set_keyboard_language(archinstall.arguments['keyboard-language'])
|
||||
installation.add_bootloader()
|
||||
installation.add_bootloader(archinstall.arguments["bootloader"])
|
||||
|
||||
# If user selected to copy the current ISO network configuration
|
||||
# Perform a copy of the config
|
||||
|
|
@ -331,6 +345,7 @@ def perform_installation(mountpoint):
|
|||
installation.log(f"This audio server will be used: {archinstall.arguments.get('audio', None)}", level=archinstall.LOG_LEVELS.Info)
|
||||
if archinstall.arguments.get('audio', None) == 'pipewire':
|
||||
print('Installing pipewire ...')
|
||||
|
||||
installation.add_additional_packages(["pipewire", "pipewire-alsa", "pipewire-jack", "pipewire-media-session", "pipewire-pulse", "gst-plugin-pipewire", "libpulse"])
|
||||
elif archinstall.arguments.get('audio', None) == 'pulseaudio':
|
||||
print('Installing pulseaudio ...')
|
||||
|
|
@ -346,7 +361,7 @@ def perform_installation(mountpoint):
|
|||
|
||||
for user, user_info in archinstall.arguments.get('users', {}).items():
|
||||
installation.user_create(user, user_info["!password"], sudo=False)
|
||||
|
||||
|
||||
for superuser, user_info in archinstall.arguments.get('superusers', {}).items():
|
||||
installation.user_create(superuser, user_info["!password"], sudo=True)
|
||||
|
||||
|
|
@ -356,6 +371,15 @@ def perform_installation(mountpoint):
|
|||
if (root_pw := archinstall.arguments.get('!root-password', None)) and len(root_pw):
|
||||
installation.user_set_pw('root', root_pw)
|
||||
|
||||
if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_post_install():
|
||||
with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported:
|
||||
if not imported._post_install():
|
||||
archinstall.log(
|
||||
' * Profile\'s post configuration requirements was not fulfilled.',
|
||||
fg='red'
|
||||
)
|
||||
exit(1)
|
||||
|
||||
installation.log("For post-installation tips, see https://wiki.archlinux.org/index.php/Installation_guide#Post-installation", fg="yellow")
|
||||
choice = input("Would you like to chroot into the newly created installation and perform post-installation configuration? [Y/n] ")
|
||||
if choice.lower() in ("y", ""):
|
||||
|
|
@ -366,4 +390,3 @@ def perform_installation(mountpoint):
|
|||
|
||||
ask_user_questions()
|
||||
perform_installation_steps()
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', suppress_error
|
|||
harddrive = archinstall.all_disks()['/dev/sda']
|
||||
disk_password = '1234'
|
||||
|
||||
with archinstall.Filesystem(harddrive, archinstall.GPT) as fs:
|
||||
with archinstall.Filesystem(harddrive) as fs:
|
||||
# Use the entire disk instead of setting up partitions on your own
|
||||
fs.use_entire_disk('luks2')
|
||||
|
||||
|
|
|
|||
|
|
@ -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.target}/etc/X11/xinit/xinitrc', 'r') as xinitrc:
|
||||
xinitrc_data = xinitrc.read()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import archinstall
|
||||
|
||||
installation.add_additional_packages("cinnamon system-config-printer gnome-keyring gnome-terminal blueberry metacity lightdm lightdm-gtk-greeter")
|
||||
installation.add_additional_packages("cinnamon system-config-printer gnome-keyring gnome-terminal blueberry metacity lightdm lightdm-gtk-greeter")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import archinstall
|
||||
|
||||
packages = "deepin deepin-terminal deepin-editor"
|
||||
|
||||
installation.add_additional_packages(packages)
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import archinstall
|
||||
|
||||
installation.add_additional_packages("lxqt breeze-icons oxygen-icons xdg-utils ttf-freefont leafpad slock archlinux-wallpaper sddm")
|
||||
installation.add_additional_packages("lxqt breeze-icons oxygen-icons xdg-utils ttf-freefont leafpad slock sddm")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import archinstall
|
||||
packages = "sway swaylock swayidle waybar dmenu light grim slurp pavucontrol alacritty"
|
||||
installation.add_additional_packages(packages)
|
||||
__packages__ = "sway swaylock swayidle waybar dmenu light grim slurp pavucontrol alacritty"
|
||||
installation.add_additional_packages(__packages__)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
import archinstall
|
||||
|
||||
installation.add_additional_packages("xfce4 xfce4-goodies lightdm lightdm-gtk-greeter")
|
||||
__packages__ = "xfce4 xfce4-goodies lightdm lightdm-gtk-greeter"
|
||||
installation.add_additional_packages(__packages__)
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# A desktop environment using "Deepin".
|
||||
|
||||
import archinstall, os
|
||||
|
||||
is_top_level_profile = False
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
# Deepin requires a functioning Xorg installation.
|
||||
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("deepin", "/somewhere/deepin.py")
|
||||
# or through conventional import deepin
|
||||
if __name__ == 'deepin':
|
||||
# Install dependency profiles
|
||||
installation.install_profile('xorg')
|
||||
|
||||
# Install the application deepin from the template under /applications/
|
||||
deepin = archinstall.Application(installation, 'deepin')
|
||||
deepin.install()
|
||||
|
||||
# Enable autostart of Deepin for all users
|
||||
installation.enable_service('lightdm')
|
||||
|
|
@ -11,6 +11,9 @@ def _prep_function(*args, **kwargs):
|
|||
other code in this stage. So it's a safe way to ask the user
|
||||
for more input before any other installer steps start.
|
||||
"""
|
||||
|
||||
__builtins__['_gfx_driver_packages'] = archinstall.select_driver()
|
||||
|
||||
return True
|
||||
|
||||
# Ensures that this code only gets executed if executed
|
||||
|
|
|
|||
|
|
@ -2,79 +2,9 @@
|
|||
|
||||
import os
|
||||
from archinstall import generic_select, sys_command, RequirementError
|
||||
|
||||
import archinstall
|
||||
is_top_level_profile = True
|
||||
|
||||
AVAILABLE_DRIVERS = {
|
||||
# Sub-dicts are layer-2 options to be selected
|
||||
# and lists are a list of packages to be installed
|
||||
'AMD / ATI' : {
|
||||
'amd' : ['xf86-video-amdgpu'],
|
||||
'ati' : ['xf86-video-ati']
|
||||
},
|
||||
'intel' : ['xf86-video-intel'],
|
||||
'nvidia' : {
|
||||
'open source' : ['xf86-video-nouveau'],
|
||||
'proprietary' : ['nvidia']
|
||||
},
|
||||
'mesa' : ['mesa'],
|
||||
'fbdev' : ['xf86-video-fbdev'],
|
||||
'vesa' : ['xf86-video-vesa'],
|
||||
'vmware' : ['xf86-video-vmware']
|
||||
}
|
||||
|
||||
def select_driver(options):
|
||||
"""
|
||||
Some what convoluted function, which's job is simple.
|
||||
Select a graphics driver from a pre-defined set of popular options.
|
||||
|
||||
(The template xorg is for beginner users, not advanced, and should
|
||||
there for appeal to the general public first and edge cases later)
|
||||
"""
|
||||
drivers = sorted(list(options))
|
||||
|
||||
if len(drivers) >= 1:
|
||||
for index, driver in enumerate(drivers):
|
||||
print(f"{index}: {driver}")
|
||||
|
||||
print(' -- The above list are supported graphic card drivers. --')
|
||||
print(' -- You need to select (and read about) which one you need. --')
|
||||
|
||||
lspci = sys_command(f'/usr/bin/lspci')
|
||||
for line in lspci.trace_log.split(b'\r\n'):
|
||||
if b' vga ' in line.lower():
|
||||
if b'nvidia' in line.lower():
|
||||
print(' ** nvidia card detected, suggested driver: nvidia **')
|
||||
elif b'amd' in line.lower():
|
||||
print(' ** AMD card detected, suggested driver: AMD / ATI **')
|
||||
|
||||
selected_driver = generic_select(drivers, 'Select your graphics card driver: ',
|
||||
allow_empty_input=False, options_output=False)
|
||||
initial_option = selected_driver
|
||||
|
||||
# Disabled search for now, only a few profiles exist anyway
|
||||
#
|
||||
#print(' -- You can enter ? or help to search for more drivers --')
|
||||
#if selected_driver.lower() in ('?', 'help'):
|
||||
# filter_string = input('Search for layout containing (example: "sv-"): ')
|
||||
# new_options = search_keyboard_layout(filter_string)
|
||||
# return select_language(new_options)
|
||||
|
||||
selected_driver = options[selected_driver]
|
||||
|
||||
if type(selected_driver) == dict:
|
||||
driver_options = sorted(list(selected_driver))
|
||||
|
||||
driver_package_group = generic_select(driver_options, f'Which driver-type do you want for {initial_option}: ',
|
||||
allow_empty_input=False)
|
||||
driver_package_group = selected_driver[driver_package_group]
|
||||
|
||||
return driver_package_group
|
||||
|
||||
return selected_driver
|
||||
|
||||
raise RequirementError("Selecting drivers require a least one profile to be given as an option.")
|
||||
|
||||
def _prep_function(*args, **kwargs):
|
||||
"""
|
||||
Magic function called by the importing installer
|
||||
|
|
@ -82,10 +12,8 @@ def _prep_function(*args, **kwargs):
|
|||
other code in this stage. So it's a safe way to ask the user
|
||||
for more input before any other installer steps start.
|
||||
"""
|
||||
print('You need to select which graphics card you\'re using.')
|
||||
print('This in order to setup the required graphics drivers.')
|
||||
|
||||
__builtins__['_gfx_driver_packages'] = select_driver(AVAILABLE_DRIVERS)
|
||||
__builtins__['_gfx_driver_packages'] = archinstall.select_driver()
|
||||
|
||||
# TODO: Add language section and/or merge it with the locale selected
|
||||
# earlier in for instance guided.py installer.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,30 @@
|
|||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
requires = ["flit_core >=2,<4"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
|
||||
[tool.flit.metadata]
|
||||
module = "archinstall"
|
||||
author = "Anton Hvornum"
|
||||
author-email = "anton@hvornum.se"
|
||||
home-page = "https://archlinux.org"
|
||||
classifiers = [ "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
]
|
||||
description-file = "README.md"
|
||||
requires-python=">=3.8"
|
||||
[tool.flit.metadata.urls]
|
||||
Source = "https://github.com/archlinux/archinstall"
|
||||
Documentation = "https://archinstall.readthedocs.io/"
|
||||
|
||||
[tool.flit.scripts]
|
||||
archinstall = "archinstall:run_as_a_module"
|
||||
|
||||
[tool.flit.sdist]
|
||||
include = ["docs/","profiles"]
|
||||
exclude = ["docs/*.html", "docs/_static","docs/*.png","docs/*.psd"]
|
||||
|
||||
[tool.flit.metadata.requires-extra]
|
||||
doc = ["sphinx"]
|
||||
Loading…
Reference in New Issue