Merging changes from master into feature branch to avoid future conflics.

This commit is contained in:
Anton Hvornum 2021-04-07 11:15:42 +02:00
commit 42470dcc9a
36 changed files with 194 additions and 278 deletions

View File

@ -6,7 +6,7 @@ Therefore guidelines and style changes to the code might come into affect as wel
## Discussions ## Discussions
Currently, questions, bugs and suggestions should be reported through [GitHub issue tracker](https://github.com/Torxed/archinstall/issues).<br> Currently, questions, bugs and suggestions should be reported through [GitHub issue tracker](https://github.com/archlinux/archinstall/issues).<br>
For less formal discussions there are also a [archinstall Discord server](https://discord.gg/cqXU88y). For less formal discussions there are also a [archinstall Discord server](https://discord.gg/cqXU88y).
## Coding convention ## Coding convention

View File

@ -2,43 +2,24 @@
# Contributor: Giancarlo Razzolini <grazzolini@archlinux.org> # Contributor: Giancarlo Razzolini <grazzolini@archlinux.org>
# Contributor: demostanis worlds <demostanis@protonmail.com> # Contributor: demostanis worlds <demostanis@protonmail.com>
pkgbase=archinstall-git pkgname=archinstall-git
pkgname=('archinstall-git' 'python-archinstall-git')
pkgver=$(git describe --long | sed 's/\([^-]*-g\)/r\1/;s/-/./g') pkgver=$(git describe --long | sed 's/\([^-]*-g\)/r\1/;s/-/./g')
pkgrel=1 pkgrel=1
pkgdesc="Just another guided/automated Arch Linux installer with a twist" pkgdesc="Just another guided/automated Arch Linux installer with a twist"
arch=('any') arch=('any')
url="https://github.com/Torxed/archinstall" url="https://github.com/archlinux/archinstall"
license=('GPL') license=('GPL')
depends=('python') depends=('python')
makedepends=('python-setuptools') makedepends=('python-setuptools')
provides=('python-archinstall')
conflicts=('archinstall' 'python-archinstall' 'python-archinstall-git')
build() { build() {
cd "$startdir" cd "$startdir"
python setup.py build python setup.py build
} }
package() {
package_archinstall-git() { cd "$startdir"
depends=('python-archinstall-git') python setup.py install --root="${pkgdir}" --optimize=1 --skip-build
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
} }

View File

@ -1,39 +0,0 @@
# Maintainer: Anton Hvornum anton@hvornum.se
# Contributor: Anton Hvornum anton@hvornum.se
pkgname="archinstall-bin"
pkgver="2.1.3"
pkgdesc="Installs a pre-built binary of ${pkgname}"
pkgrel=1
url="https://github.com/Torxed/archinstall"
license=('GPLv3')
provides=("${pkgname}")
arch=('x86_64')
source=("${pkgname}-v${pkgver}-x86_64.tar.gz::https://github.com/Torxed/archinstall/archive/v$pkgver.tar.gz")
#depends=('python>=3.8')
makedepends=('python>=3.8' 'nuitka')
optdepends=('pyttsx3: Adds text-to-speach support for log/screen output.')
sha256sums=('53c00f7e7ad245cd2cbbf041b5a735df2fc29454c24b1d369f678cc0610b7cea')
build() {
cd "${pkgname}-${pkgver}"
nuitka3 --standalone --show-progress archinstall
cp -r examples/ archinstall.dist/
}
package() {
echo "${srcdir}"
cd "${pkgname}-${pkgver}"
mkdir -p "${pkgdir}/var/lib/archinstall/"
mkdir -p "${pkgdir}/usr/bin"
mv archinstall.dist/* "${pkgdir}/var/lib/archinstall/"
echo '#!/bin/bash' > "${pkgdir}/usr/bin/archinstall-bin"
echo '(cd /var/lib/archinstall && exec ./archinstall)' >> "${pkgdir}/usr/bin/archinstall-bin"
chmod +x "${pkgdir}/var/lib/archinstall/archinstall"
chmod +x "${pkgdir}/usr/bin/archinstall-bin"
}

View File

@ -1,30 +0,0 @@
# Maintainer: Anton Hvornum <anton@hvornum.se>
# Contributor: demostanis worlds <demostanis@protonmail.com>
pkgname="archinstall"
pkgver="2.1.3"
pkgdesc="Installs launcher scripts for archinstall"
pkgrel=1
url="https://github.com/Torxed/archinstall"
license=('GPLv3')
provides=("${pkgname}")
arch=('x86_64')
source=("${pkgname}-v${pkgver}-x86_64.tar.gz::https://github.com/Torxed/archinstall/archive/v$pkgver.tar.gz")
depends=('python-archinstall')
sha256sums=('53c00f7e7ad245cd2cbbf041b5a735df2fc29454c24b1d369f678cc0610b7cea')
package() {
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"
}
# vim:ft=sh

View File

@ -1,40 +0,0 @@
# Maintainer: Anton Hvornum <anton@hvornum.se>
# Contributor: demostanis worlds <demostanis@protonmail.com>
pkgname="python-archinstall"
pkgver="2.1.3"
pkgdesc="Installs ${pkgname} as a python library."
pkgrel=1
url="https://github.com/Torxed/archinstall"
source=("${pkgname}-v${pkgver}-x86_64.tar.gz::https://github.com/Torxed/archinstall/archive/v$pkgver.tar.gz")
license=('GPLv3')
provides=("${pkgname}")
arch=('x86_64')
depends=('python>=3.8')
makedepends=('python-setuptools')
optdepends=('pyttsx3: Adds text-to-speech support for log/screen output.')
sha256sums=('53c00f7e7ad245cd2cbbf041b5a735df2fc29454c24b1d369f678cc0610b7cea')
build() {
cd "archinstall-${pkgver}"
python setup.py build
# Build man pages
cd docs
make man
}
package() {
cd "archinstall-${pkgver}"
python setup.py install \
--prefix=/usr \
--root="${pkgdir}" \
--optimize=1
install -Dm644 docs/_build/man/archinstall.1 "${pkgdir}"/usr/share/man/man1/archinstall.1
}
# vim:ft=sh

View File

@ -1,4 +1,4 @@
# <img src="https://github.com/Torxed/archinstall/raw/master/docs/logo.png" alt="drawing" width="200"/> # <img src="https://github.com/archlinux/archinstall/raw/master/docs/logo.png" alt="drawing" width="200"/>
Just another guided/automated [Arch Linux](https://wiki.archlinux.org/index.php/Arch_Linux) installer with a twist. Just another guided/automated [Arch Linux](https://wiki.archlinux.org/index.php/Arch_Linux) installer with a twist.
The installer also doubles as a python library to install Arch Linux and manage services, packages and other things inside the installed system *(Usually from a live medium)*. The installer also doubles as a python library to install Arch Linux and manage services, packages and other things inside the installed system *(Usually from a live medium)*.
@ -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 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. * 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)* * Install additional packages *(nano, wget, git)*
* Installs a network-profile called [awesome](https://github.com/Torxed/archinstall/blob/master/profiles/awesome.py) *(more on network profiles in the documentation)* * 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))*.
> **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. > **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.
@ -73,6 +73,23 @@ When doing so, attach any `install-session_*.log` to the issue ticket which can
# Testing # Testing
## Using a Live ISO Image
If you want to test a commit, branch or bleeding edge release from the repository using the vanilla Arch Live ISO image, you can replace the version of archinstall with a new version and run that with the steps described below.
1. You need a working network connection
2. Install the build requirements with `pacman -Sy; pacman -S git python-pip`
*(note that this may or may not work depending on your RAM and current state of the squashfs maximum filesystem free space)*
3. Uninstall the previous version of archinstall with `pip uninstall archinstall`
4. Now clone the latest repository with `git clone https://github.com/archlinux/archinstall`
5. Enter the repository with `cd archinstall`
*At this stage, you can choose to check out a feature branch for instance with `git checkout torxed-v2.2.0`*
6. Build the project and install it using `python setup.py install`
After this, running archinstall with `python -m archinstall` will run against whatever branch you chose in step 5.
## Without a Live ISO Image
To test this without a live ISO, the simplest approach is to use a local image and create a loop device.<br> To test this without a live ISO, the simplest approach is to use a local image and create a loop device.<br>
This can be done by installing `pacman -S arch-install-scripts util-linux` locally and doing the following: This can be done by installing `pacman -S arch-install-scripts util-linux` locally and doing the following:
@ -87,5 +104,5 @@ This will create a *5GB* `testimage.img` and create a loop device which we can u
`archinstall` is installed and executed in [guided mode](#docs-todo). Once the installation is complete,<br> `archinstall` is installed and executed in [guided mode](#docs-todo). Once the installation is complete,<br>
~~you can use qemu/kvm to boot the test media.~~ *(You'd actually need to do some EFI magic in order to point the EFI vars to the partition 0 in the test medium so this won't work entirely out of the box, but gives you a general idea of what we're going for here)* ~~you can use qemu/kvm to boot the test media.~~ *(You'd actually need to do some EFI magic in order to point the EFI vars to the partition 0 in the test medium so this won't work entirely out of the box, but gives you a general idea of what we're going for here)*
There's also a [Building and Testing](https://github.com/Torxed/archinstall/wiki/Building-and-Testing) guide.<br> There's also a [Building and Testing](https://github.com/archlinux/archinstall/wiki/Building-and-Testing) guide.<br>
It will go through everything from packaging, building and running *(with qemu)* the installer against a dev branch. It will go through everything from packaging, building and running *(with qemu)* the installer against a dev branch.

View File

@ -1 +0,0 @@
2.1.3

View File

@ -1,3 +0,0 @@
# This __init__ file is just here to support the
# use of archinstall as a git submodule.
from .archinstall import *

View File

@ -14,6 +14,8 @@ from .lib.output import *
from .lib.storage import * from .lib.storage import *
from .lib.hardware import * from .lib.hardware import *
__version__ = "2.1.3"
## Basic version of arg.parse() supporting: ## Basic version of arg.parse() supporting:
## --key=value ## --key=value
## --boolean ## --boolean
@ -28,3 +30,32 @@ for arg in sys.argv[1:]:
arguments[key] = val arguments[key] = val
else: else:
positionals.append(arg) positionals.append(arg)
# TODO: Learn the dark arts of argparse...
# (I summon thee dark spawn of cPython)
def run_as_a_module():
"""
Since we're running this as a 'python -m archinstall' module OR
a nuitka3 compiled version of the project.
This function and the file __main__ acts as a entry point.
"""
# Add another path for finding profiles, so that list_profiles() in Script() can find guided.py, unattended.py etc.
storage['PROFILE_PATH'].append(os.path.abspath(f'{os.path.dirname(__file__)}/examples'))
if len(sys.argv) == 1:
sys.argv.append('guided')
try:
script = Script(sys.argv[1])
except ProfileNotFound as err:
print(f"Couldn't find file: {err}")
sys.exit(1)
os.chdir(os.path.abspath(os.path.dirname(__file__)))
# Remove the example directory from the PROFILE_PATH, to avoid guided.py etc shows up in user input questions.
storage['PROFILE_PATH'].pop()
script.execute()

View File

@ -2,33 +2,5 @@ import archinstall
import sys import sys
import os import os
# TODO: Learn the dark arts of argparse...
# (I summon thee dark spawn of cPython)
def run_as_a_module():
"""
Since we're running this as a 'python -m archinstall' module OR
a nuitka3 compiled version of the project.
This function and the file __main__ acts as a entry point.
"""
# Add another path for finding profiles, so that list_profiles() in Script() can find guided.py, unattended.py etc.
archinstall.storage['PROFILE_PATH'].append(os.path.abspath(f'{os.path.dirname(__file__)}/examples'))
if len(sys.argv) == 1:
sys.argv.append('guided')
try:
script = archinstall.Script(sys.argv[1])
except archinstall.ProfileNotFound as err:
print(f"Couldn't find file: {err}")
sys.exit(1)
os.chdir(os.path.abspath(os.path.dirname(__file__)))
# Remove the example directory from the PROFILE_PATH, to avoid guided.py etc shows up in user input questions.
archinstall.storage['PROFILE_PATH'].pop()
script.execute()
if __name__ == '__main__': if __name__ == '__main__':
run_as_a_module() archinstall.run_as_a_module()

View File

@ -25,11 +25,12 @@ class BlockDevice():
self.path = path self.path = path
self.info = info self.info = info
self.keep_partitions = True
self.part_cache = OrderedDict() self.part_cache = OrderedDict()
# TODO: Currently disk encryption is a BIT missleading. # TODO: Currently disk encryption is a BIT misleading.
# It's actually partition-encryption, but for future-proofing this # It's actually partition-encryption, but for future-proofing this
# I'm placing the encryption password on a BlockDevice level. # I'm placing the encryption password on a BlockDevice level.
self.encryption_passwoed = None self.encryption_password = None
def __repr__(self, *args, **kwargs): def __repr__(self, *args, **kwargs):
return f"BlockDevice({self.device})" return f"BlockDevice({self.device})"
@ -285,10 +286,10 @@ class Partition():
handle = luks2(self, None, None) handle = luks2(self, None, None)
return handle.encrypt(self, *args, **kwargs) return handle.encrypt(self, *args, **kwargs)
def format(self, filesystem=None, path=None, allow_formatting=None, log_formating=True): def format(self, filesystem=None, path=None, allow_formatting=None, log_formatting=True):
""" """
Format can be given an overriding path, for instance /dev/null to test Format can be given an overriding path, for instance /dev/null to test
the formating functionality and in essence the support for the given filesystem. the formatting functionality and in essence the support for the given filesystem.
""" """
if filesystem is None: if filesystem is None:
filesystem = self.filesystem filesystem = self.filesystem
@ -306,7 +307,7 @@ class Partition():
if not allow_formatting: if not allow_formatting:
raise PermissionError(f"{self} is not formatable either because instance is locked ({self.allow_formatting}) or a blocking flag was given ({allow_formatting})") raise PermissionError(f"{self} is not formatable either because instance is locked ({self.allow_formatting}) or a blocking flag was given ({allow_formatting})")
if log_formating: if log_formatting:
log(f'Formatting {path} -> {filesystem}', level=LOG_LEVELS.Info) log(f'Formatting {path} -> {filesystem}', level=LOG_LEVELS.Info)
if filesystem == 'btrfs': if filesystem == 'btrfs':
@ -401,7 +402,7 @@ class Partition():
2. UnknownFilesystemFormat that indicates that we don't support the given filesystem type 2. UnknownFilesystemFormat that indicates that we don't support the given filesystem type
""" """
try: try:
self.format(self.filesystem, '/dev/null', log_formating=False, allow_formatting=True) self.format(self.filesystem, '/dev/null', log_formatting=False, allow_formatting=True)
except SysCallError: except SysCallError:
pass # We supported it, but /dev/null is not formatable as expected so the mkfs call exited with an error code pass # We supported it, but /dev/null is not formatable as expected so the mkfs call exited with an error code
except UnknownFilesystemFormat as err: except UnknownFilesystemFormat as err:

View File

@ -79,7 +79,7 @@ class Installer():
return self return self
def __exit__(self, *args, **kwargs): def __exit__(self, *args, **kwargs):
# b''.join(sys_command(f'sync')) # No need to, since the underlaying fs() object will call sync. # b''.join(sys_command(f'sync')) # No need to, since the underlying fs() object will call sync.
# TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager # TODO: https://stackoverflow.com/questions/28157929/how-to-safely-handle-an-exception-inside-a-context-manager
if len(args) >= 2 and args[1]: if len(args) >= 2 and args[1]:
@ -91,7 +91,7 @@ class Installer():
# We avoid printing /mnt/<log path> because that might confuse people if they note it down # 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. # 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"[!] A log file has been created here: {os.path.join(storage['LOG_PATH'], storage['LOG_FILE'])}")
print(f" Please submit this issue (and file) to https://github.com/Torxed/archinstall/issues") print(f" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues")
raise args[1] raise args[1]
self.genfstab() self.genfstab()
@ -104,8 +104,10 @@ class Installer():
self.log('Some required steps were not successfully installed/configured before leaving the installer:', bg='black', fg='red', level=LOG_LEVELS.Warning) self.log('Some required steps were not successfully installed/configured before leaving the installer:', bg='black', fg='red', level=LOG_LEVELS.Warning)
for step in missing_steps: for step in missing_steps:
self.log(f' - {step}', bg='black', fg='red', level=LOG_LEVELS.Warning) self.log(f' - {step}', bg='black', fg='red', level=LOG_LEVELS.Warning)
self.log(f"Detailed error logs can be found at: {log_path}", level=LOG_LEVELS.Warning)
self.log(f"Submit this zip file as an issue to https://github.com/Torxed/archinstall/issues", level=LOG_LEVELS.Warning) self.log(f"Detailed error logs can be found at: {storage['LOG_PATH']}", level=LOG_LEVELS.Warning)
self.log(f"Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues", level=LOG_LEVELS.Warning)
self.sync_log_to_install_medium() self.sync_log_to_install_medium()
return False return False
@ -151,11 +153,11 @@ class Installer():
self.log(f"Updating {self.mountpoint}/etc/fstab", level=LOG_LEVELS.Info) self.log(f"Updating {self.mountpoint}/etc/fstab", level=LOG_LEVELS.Info)
fstab = sys_command(f'/usr/bin/genfstab {flags} {self.mountpoint}').trace_log fstab = sys_command(f'/usr/bin/genfstab {flags} {self.mountpoint}').trace_log
with open(f"{self.mountpoint}/etc/fstab", 'ab') as fstab_fh: with open(f"{self.mountpoint}/etc/fstab", 'ab',newline='\n') as fstab_fh:
fstab_fh.write(fstab) fstab_fh.write(fstab)
if not os.path.isfile(f'{self.mountpoint}/etc/fstab'): if not os.path.isfile(f'{self.mountpoint}/etc/fstab'):
raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n{o}') raise RequirementError(f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n{b"".join(fstab)}')
return True return True
@ -274,7 +276,7 @@ class Installer():
return True return True
def minimal_installation(self): def minimal_installation(self):
## Add nessecary packages if encrypting the drive ## Add necessary packages if encrypting the drive
## (encrypted partitions default to btrfs for now, so we need btrfs-progs) ## (encrypted partitions default to btrfs for now, so we need btrfs-progs)
## TODO: Perhaps this should be living in the function which dictates ## TODO: Perhaps this should be living in the function which dictates
## the partitioning. Leaving here for now. ## the partitioning. Leaving here for now.
@ -312,14 +314,14 @@ class Installer():
mkinit.write('MODULES=(btrfs)\n') mkinit.write('MODULES=(btrfs)\n')
mkinit.write('BINARIES=(/usr/bin/btrfs)\n') mkinit.write('BINARIES=(/usr/bin/btrfs)\n')
mkinit.write('FILES=()\n') mkinit.write('FILES=()\n')
mkinit.write('HOOKS=(base udev autodetect modconf block encrypt filesystems keymap keyboard fsck)\n') mkinit.write('HOOKS=(base udev autodetect keyboard keymap modconf block encrypt filesystems fsck)\n')
sys_command(f'/usr/bin/arch-chroot {self.mountpoint} mkinitcpio -p linux') sys_command(f'/usr/bin/arch-chroot {self.mountpoint} mkinitcpio -p linux')
elif self.partition.encrypted: elif self.partition.encrypted:
with open(f'{self.mountpoint}/etc/mkinitcpio.conf', 'w') as mkinit: with open(f'{self.mountpoint}/etc/mkinitcpio.conf', 'w') as mkinit:
mkinit.write('MODULES=()\n') mkinit.write('MODULES=()\n')
mkinit.write('BINARIES=()\n') mkinit.write('BINARIES=()\n')
mkinit.write('FILES=()\n') mkinit.write('FILES=()\n')
mkinit.write('HOOKS=(base udev autodetect modconf block encrypt filesystems keymap keyboard fsck)\n') mkinit.write('HOOKS=(base udev autodetect keyboard keymap modconf block encrypt filesystems fsck)\n')
sys_command(f'/usr/bin/arch-chroot {self.mountpoint} mkinitcpio -p linux') sys_command(f'/usr/bin/arch-chroot {self.mountpoint} mkinitcpio -p linux')
self.helper_flags['base'] = True self.helper_flags['base'] = True
@ -419,7 +421,7 @@ class Installer():
# The tricky thing with doing the import archinstall.session instead is that # The tricky thing with doing the import archinstall.session instead is that
# profiles might be run from a different chroot, and there's no way we can # profiles might be run from a different chroot, and there's no way we can
# guarantee file-path safety when accessing the installer object that way. # guarantee file-path safety when accessing the installer object that way.
# Doing the __builtins__ replacement, ensures that the global vriable "installation" # Doing the __builtins__ replacement, ensures that the global variable "installation"
# is always kept up to date. It's considered a nasty hack - but it's a safe way # is always kept up to date. It's considered a nasty hack - but it's a safe way
# of ensuring 100% accuracy of archinstall session variables. # of ensuring 100% accuracy of archinstall session variables.
__builtins__['installation'] = self __builtins__['installation'] = self

View File

@ -43,8 +43,6 @@ class luks2():
return True return True
def encrypt(self, partition, password=None, key_size=512, hash_type='sha512', iter_time=10000, key_file=None): def encrypt(self, partition, password=None, key_size=512, hash_type='sha512', iter_time=10000, key_file=None):
# TODO: We should be able to integrate this into the main log some how.
# Perhaps post-mortem?
if not self.partition.allow_formatting: if not self.partition.allow_formatting:
raise DiskError(f'Could not encrypt volume {self.partition} due to it having a formatting lock.') raise DiskError(f'Could not encrypt volume {self.partition} due to it having a formatting lock.')

View File

@ -56,7 +56,7 @@ def wirelessScan(interface):
storage['_WIFI'][interface]['scanning'] = True storage['_WIFI'][interface]['scanning'] = True
# TOOD: Full WiFi experience might get evolved in the future, pausing for now 2021-01-25 # TODO: Full WiFi experience might get evolved in the future, pausing for now 2021-01-25
def getWirelessNetworks(interface): def getWirelessNetworks(interface):
# TODO: Make this oneliner pritter to check if the interface is scanning or not. # 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: if not '_WIFI' in storage or interface not in storage['_WIFI'] or storage['_WIFI'][interface].get('scanning', False) is False:

View File

@ -6,7 +6,7 @@ from pathlib import Path
from .storage import storage from .storage import storage
# TODO: use logging's built in levels instead. # TODO: use logging's built in levels instead.
# Altough logging is threaded and I wish to avoid that. # Although logging is threaded and I wish to avoid that.
# It's more Pythonistic or w/e you want to call it. # It's more Pythonistic or w/e you want to call it.
class LOG_LEVELS: class LOG_LEVELS:
Critical = 0b001 Critical = 0b001
@ -88,7 +88,7 @@ def log(*args, **kwargs):
# Attempt to colorize the output if supported # Attempt to colorize the output if supported
# Insert default colors and override with **kwargs # Insert default colors and override with **kwargs
if supports_color(): if supports_color():
kwargs = {'bg' : 'black', 'fg': 'white', **kwargs} kwargs = {'fg': 'white', **kwargs}
string = stylize_output(string, **kwargs) string = stylize_output(string, **kwargs)
# If a logfile is defined in storage, # If a logfile is defined in storage,

View File

@ -112,11 +112,11 @@ class Script():
if f"{self.profile}" in self.examples: if f"{self.profile}" in self.examples:
return self.localize_path(self.examples[self.profile]['path']) return self.localize_path(self.examples[self.profile]['path'])
# TODO: Redundant, the below block shouldnt be needed as profiles are stripped of their .py, but just in case for now: # TODO: Redundant, the below block shouldn't be needed as profiles are stripped of their .py, but just in case for now:
elif f"{self.profile}.py" in self.examples: elif f"{self.profile}.py" in self.examples:
return self.localize_path(self.examples[f"{self.profile}.py"]['path']) return self.localize_path(self.examples[f"{self.profile}.py"]['path'])
# Path was not found in any known examples, check if it's an abolute path # Path was not found in any known examples, check if it's an absolute path
if os.path.isfile(self.profile): if os.path.isfile(self.profile):
return self.profile return self.profile
@ -156,7 +156,7 @@ class Profile(Script):
def install(self): def install(self):
# Before installing, revert any temporary changes to the namespace. # Before installing, revert any temporary changes to the namespace.
# This ensures that the namespace during installation is the original initation namespace. # This ensures that the namespace during installation is the original initiation namespace.
# (For instance awesome instead of aweosme.py or app-awesome.py) # (For instance awesome instead of aweosme.py or app-awesome.py)
self.namespace = self.original_namespace self.namespace = self.original_namespace
return self.execute() return self.execute()
@ -231,11 +231,11 @@ class Application(Profile):
if f"{self.profile}" in self.examples: if f"{self.profile}" in self.examples:
return self.localize_path(self.examples[self.profile]['path']) return self.localize_path(self.examples[self.profile]['path'])
# TODO: Redundant, the below block shouldnt be needed as profiles are stripped of their .py, but just in case for now: # TODO: Redundant, the below block shouldn't be needed as profiles are stripped of their .py, but just in case for now:
elif f"{self.profile}.py" in self.examples: elif f"{self.profile}.py" in self.examples:
return self.localize_path(self.examples[f"{self.profile}.py"]['path']) return self.localize_path(self.examples[f"{self.profile}.py"]['path'])
# Path was not found in any known examples, check if it's an abolute path # Path was not found in any known examples, check if it's an absolute path
if os.path.isfile(self.profile): if os.path.isfile(self.profile):
return os.path.basename(self.profile) return os.path.basename(self.profile)
@ -247,7 +247,7 @@ class Application(Profile):
def install(self): def install(self):
# Before installing, revert any temporary changes to the namespace. # Before installing, revert any temporary changes to the namespace.
# This ensures that the namespace during installation is the original initation namespace. # This ensures that the namespace during installation is the original initiation namespace.
# (For instance awesome instead of aweosme.py or app-awesome.py) # (For instance awesome instead of aweosme.py or app-awesome.py)
self.namespace = self.original_namespace self.namespace = self.original_namespace
return self.execute() return self.execute()

View File

@ -14,7 +14,7 @@ storage = {
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'profiles'), os.path.join(os.path.dirname(os.path.abspath(__file__)), 'profiles'),
#os.path.abspath(f'{os.path.dirname(__file__)}/../examples') #os.path.abspath(f'{os.path.dirname(__file__)}/../examples')
], ],
'UPSTREAM_URL' : 'https://raw.githubusercontent.com/Torxed/archinstall/master/profiles', 'UPSTREAM_URL' : 'https://raw.githubusercontent.com/archlinux/archinstall/master/profiles',
'PROFILE_DB' : None, # Used in cases when listing profiles is desired, not mandatory for direct profile grabing. 'PROFILE_DB' : None, # Used in cases when listing profiles is desired, not mandatory for direct profile grabing.
'LOG_PATH' : '/var/log/archinstall', 'LOG_PATH' : '/var/log/archinstall',
'LOG_FILE' : 'install.log', 'LOG_FILE' : 'install.log',

View File

@ -1,4 +1,4 @@
import getpass, pathlib, os, shutil import getpass, pathlib, os, shutil, re
from .exceptions import * from .exceptions import *
from .profiles import Profile from .profiles import Profile
from .locale_helpers import search_keyboard_layout from .locale_helpers import search_keyboard_layout
@ -18,11 +18,21 @@ def get_terminal_width():
def get_longest_option(options): def get_longest_option(options):
return max([len(x) for x in options]) return max([len(x) for x in options])
def check_for_correct_username(username):
if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32:
return True
log(
"The username you entered is invalid. Try again",
level=LOG_LEVELS.Warning,
fg='red'
)
return False
def get_password(prompt="Enter a password: "): def get_password(prompt="Enter a password: "):
while (passwd := getpass.getpass(prompt)): while (passwd := getpass.getpass(prompt)):
passwd_verification = getpass.getpass(prompt='And one more time for verification: ') passwd_verification = getpass.getpass(prompt='And one more time for verification: ')
if passwd != passwd_verification: if passwd != passwd_verification:
log(' * Passwords did not match * ', bg='black', fg='red') log(' * Passwords did not match * ', fg='red')
continue continue
if len(passwd.strip()) <= 0: if len(passwd.strip()) <= 0:
@ -54,10 +64,12 @@ def ask_for_superuser_account(prompt='Create a required super-user with sudo pri
if not new_user and forced: if not new_user and forced:
# TODO: make this text more generic? # TODO: make this text more generic?
# It's only used to create the first sudo user when root is disabled in guided.py # It's only used to create the first sudo user when root is disabled in guided.py
log(' * Since root is disabled, you need to create a least one (super) user!', bg='black', fg='red') log(' * Since root is disabled, you need to create a least one (super) user!', fg='red')
continue continue
elif not new_user and not forced: elif not new_user and not forced:
raise UserError("No superuser was created.") raise UserError("No superuser was created.")
elif not check_for_correct_username(new_user):
continue
password = get_password(prompt=f'Password for user {new_user}: ') password = get_password(prompt=f'Password for user {new_user}: ')
return {new_user: {"!password" : password}} return {new_user: {"!password" : password}}
@ -70,6 +82,8 @@ def ask_for_additional_users(prompt='Any additional users to install (leave blan
new_user = input(prompt).strip(' ') new_user = input(prompt).strip(' ')
if not new_user: if not new_user:
break break
if not check_for_correct_username(new_user):
continue
password = get_password(prompt=f'Password for user {new_user}: ') password = get_password(prompt=f'Password for user {new_user}: ')
if input("Should this user be a sudo (super) user (y/N): ").strip(' ').lower() in ('y', 'yes'): if input("Should this user be a sudo (super) user (y/N): ").strip(' ').lower() in ('y', 'yes'):
@ -110,7 +124,6 @@ def ask_to_configure_network():
log( log(
"You need to enter a valid IP in IP-config mode.", "You need to enter a valid IP in IP-config mode.",
level=LOG_LEVELS.Warning, level=LOG_LEVELS.Warning,
bg='black',
fg='red' fg='red'
) )
@ -153,7 +166,7 @@ def ask_for_main_filesystem_format():
def generic_select(options, input_text="Select one of the above by index or absolute value: ", sort=True): def generic_select(options, input_text="Select one of the above by index or absolute value: ", sort=True):
""" """
A generic select function that does not output anything A generic select function that does not output anything
other than the options and their indexs. As an example: other than the options and their indexes. As an example:
generic_select(["first", "second", "third option"]) generic_select(["first", "second", "third option"])
1: first 1: first

View File

@ -12,7 +12,7 @@ Packages
======== ========
.. autofunction:: archinstall.find_package .. autofunction:: archinstall.find_package
Be
.. autofunction:: archinstall.find_packages .. autofunction:: archinstall.find_packages
Locale related Locale related
@ -24,9 +24,6 @@ Locale related
.. autofunction:: archinstall.set_keyboard_language .. autofunction:: archinstall.set_keyboard_language
..
autofunction:: archinstall.Installer.set_keyboard_layout
Services Services
======== ========

View File

@ -1,4 +1,4 @@
.. _examples.python: .. _examples.binary:
Binary executable Binary executable
================= =================
@ -11,7 +11,7 @@ It's compiled using `nuitka <https://nuitka.net/>`_ with the flag `--standalone`
Executing the binary Executing the binary
-------------------- --------------------
As an example we'll use the `guided <https://github.com/Torxed/archinstall/blob/master/examples/guided.py>`_ installer. As an example we'll use the `guided <https://github.com/archlinux/archinstall/blob/master/examples/guided.py>`_ installer.
To run the `guided` installed, all you have to do *(after installing or compiling the binary)*, is run: To run the `guided` installed, all you have to do *(after installing or compiling the binary)*, is run:

View File

@ -3,7 +3,7 @@
Discord Discord
======= =======
There's a discord channel which is frequent by some `contributors <https://github.com/Torxed/archinstall/graphs/contributors>`_. There's a discord channel which is frequent by some `contributors <https://github.com/archlinux/archinstall/graphs/contributors>`_.
To join the server, head over to `https://discord.gg/cqXU88y <https://discord.gg/cqXU88y>`_'s server and join in. To join the server, head over to `https://discord.gg/cqXU88y <https://discord.gg/cqXU88y>`_'s server and join in.
There's not many rules other than common sense and treat others with respect. There's not many rules other than common sense and treat others with respect.

View File

@ -3,7 +3,7 @@
Issue tracker & bugs Issue tracker & bugs
==================== ====================
Issues and bugs should be reported over at `https://github.com/Torxed/archinstall/issues <https://github.com/Torxed/archinstall/issues>`_. Issues and bugs should be reported over at `https://github.com/archlinux/archinstall/issues <https://github.com/Torxed/archinstall/issues>`_.
General questions, enhancements and security issues can be reported over there too. General questions, enhancements and security issues can be reported over there too.
For quick issues or if you need help, head over the to the Discord server which has a help channel. For quick issues or if you need help, head over the to the Discord server which has a help channel.

View File

@ -46,7 +46,8 @@ Some of the features of Archinstall are:
.. ..
examples/scripting examples/scripting
.. toctree:: ..
.. toctree::
:maxdepth: 3 :maxdepth: 3
:caption: Programming Guide :caption: Programming Guide

View File

@ -1,10 +1,10 @@
.. _installing.binary .. _installing.binary:
Binary executable Binary executable
================= =================
Archinstall can be compiled into a standalone executable. Archinstall can be compiled into a standalone executable.
For Arch Linux based systems, there's a package for this called `archinstall <https://archlinux.life/>`_. For Arch Linux based systems, there's a package for this called `archinstall <https://archlinux.org/packages/extra/any/archinstall/>`_.
.. warning:: .. warning::
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. 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.
@ -21,7 +21,7 @@ Archinstall is on the `official repositories <https://wiki.archlinux.org/index.p
Using PKGBUILD Using PKGBUILD
-------------- --------------
The `source <https://github.com/Torxed/archinstall>`_ contains a binary `PKGBUILD <https://github.com/Torxed/archinstall/tree/master/PKGBUILD/archinstall>`_ which can be either copied straight off the website. Or cloned using `git clone https://github.com/Torxed/archinstall`. The `source <https://github.com/archlinux/archinstall>`_ contains a binary `PKGBUILD <https://github.com/Torxed/archinstall/tree/master/PKGBUILD/archinstall>`_ which can be either copied straight off the website. Or cloned using `git clone https://github.com/Torxed/archinstall`.
Once you've obtained the `PKGBUILD`, building it is pretty straight forward. Once you've obtained the `PKGBUILD`, building it is pretty straight forward.
@ -37,7 +37,7 @@ Which should produce a `archinstall-X.x.z-1.pkg.tar.zst` that can be installed u
.. note:: .. note::
For a complete guide on the build process, please consult the wiki on `PKGBUILD <https://wiki.archlinux.org/index.php/PKGBUILD>`_. For a complete guide on the build process, please consult the `PKGBUILD on ArchWiki <https://wiki.archlinux.org/index.php/PKGBUILD>`_.
Manual compilation Manual compilation
------------------ ------------------

View File

@ -64,7 +64,7 @@ Default is :code:`auto detect best mirror`
As an example: As an example:
* :code:`Sweden` *(wich a capital :code:`S`)* will only use mirrors from Sweden. * :code:`Sweden` *(with a capital :code:`S`)* will only use mirrors from Sweden.
Selection of drive Selection of drive
------------------ ------------------

View File

@ -54,7 +54,7 @@ Or you can clone it, we'll clone it here but both methods work the same.
.. code-block:: console .. code-block:: console
git clone https://github.com/Torxed/archinstall git clone https://github.com/archlinux/archinstall
Either you can move the folder into your project and simply do Either you can move the folder into your project and simply do

View File

@ -1,6 +1,6 @@
# Pull Request Template # Pull Request Template
Make sure you've checked out the [contribution guideline](https://github.com/Torxed/archinstall/blob/master/CONTRIBUTING.md).<br> Make sure you've checked out the [contribution guideline](https://github.com/archlinux/archinstall/blob/master/CONTRIBUTING.md).<br>
Most of the guidelines are not enforced, but is heavily encouraged. Most of the guidelines are not enforced, but is heavily encouraged.
## Description ## Description

View File

@ -59,7 +59,7 @@ def ask_user_questions():
if archinstall.arguments['harddrive'].has_partitions(): if archinstall.arguments['harddrive'].has_partitions():
archinstall.log(f"{archinstall.arguments['harddrive']} contains the following partitions:", fg='yellow') archinstall.log(f"{archinstall.arguments['harddrive']} contains the following partitions:", fg='yellow')
# We curate a list pf supported paritions # We curate a list pf supported partitions
# and print those that we don't support. # and print those that we don't support.
partition_mountpoints = {} partition_mountpoints = {}
for partition in archinstall.arguments['harddrive']: for partition in archinstall.arguments['harddrive']:
@ -70,7 +70,7 @@ def ask_user_questions():
except archinstall.UnknownFilesystemFormat as err: except archinstall.UnknownFilesystemFormat as err:
archinstall.log(f" {partition} (Filesystem not supported)", fg='red') archinstall.log(f" {partition} (Filesystem not supported)", fg='red')
# We then ask what to do with the paritions. # We then ask what to do with the partitions.
if (option := archinstall.ask_for_disk_layout()) == 'abort': if (option := archinstall.ask_for_disk_layout()) == 'abort':
archinstall.log(f"Safely aborting the installation. No changes to the disk or system has been made.") archinstall.log(f"Safely aborting the installation. No changes to the disk or system has been made.")
exit(1) exit(1)
@ -90,7 +90,7 @@ def ask_user_questions():
mountpoint = input(f"Enter a mount-point for {partition}: ").strip(' ') mountpoint = input(f"Enter a mount-point for {partition}: ").strip(' ')
if len(mountpoint): if len(mountpoint):
# Get a valid & supported filesystem for the parition: # Get a valid & supported filesystem for the partition:
while 1: while 1:
new_filesystem = input(f"Enter a valid filesystem for {partition} (leave blank for {partition.filesystem}): ").strip(' ') new_filesystem = input(f"Enter a valid filesystem for {partition} (leave blank for {partition.filesystem}): ").strip(' ')
if len(new_filesystem) <= 0: if len(new_filesystem) <= 0:
@ -113,7 +113,7 @@ def ask_user_questions():
try: try:
partition.format(new_filesystem, path='/dev/null', log_formating=False, allow_formatting=True) partition.format(new_filesystem, path='/dev/null', log_formating=False, allow_formatting=True)
except archinstall.UnknownFilesystemFormat: except archinstall.UnknownFilesystemFormat:
archinstall.log(f"Selected filesystem is not supported yet. If you want archinstall to support '{new_filesystem}', please create a issue-ticket suggesting it on github at https://github.com/Torxed/archinstall/issues.") archinstall.log(f"Selected filesystem is not supported yet. If you want archinstall to support '{new_filesystem}', please create a issue-ticket suggesting it on github at https://github.com/archlinux/archinstall/issues.")
archinstall.log(f"Until then, please enter another supported filesystem.") archinstall.log(f"Until then, please enter another supported filesystem.")
continue continue
except archinstall.SysCallError: except archinstall.SysCallError:
@ -121,7 +121,7 @@ def ask_user_questions():
# But that means our .format() function supported it. # But that means our .format() function supported it.
break break
# When we've selected all three criterias, # When we've selected all three criteria,
# We can safely mark the partition for formatting and where to mount it. # We can safely mark the partition for formatting and where to mount it.
# TODO: allow_formatting might be redundant since target_mountpoint should only be # TODO: allow_formatting might be redundant since target_mountpoint should only be
# set if we actually want to format it anyway. # set if we actually want to format it anyway.
@ -171,19 +171,19 @@ def ask_user_questions():
else: else:
archinstall.arguments['profile'] = archinstall.list_profiles()[archinstall.arguments['profile']] archinstall.arguments['profile'] = archinstall.list_profiles()[archinstall.arguments['profile']]
# Check the potentially selected profiles preperations to get early checks if some additional questions are needed. # Check the potentially selected profiles preparations to get early checks if some additional questions are needed.
if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_prep_function(): if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_prep_function():
with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported: with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported:
if not imported._prep_function(): if not imported._prep_function():
archinstall.log( archinstall.log(
' * Profile\'s preparation requirements was not fulfilled.', ' * Profile\'s preparation requirements was not fulfilled.',
bg='black',
fg='red' fg='red'
) )
exit(1) exit(1)
# Additional packages (with some light weight error handling for invalid package names) # Additional packages (with some light weight error handling for invalid package names)
if not archinstall.arguments.get('packages', None): if not archinstall.arguments.get('packages', None):
print("Packages not part of the desktop environment are not installed by default. If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.")
archinstall.arguments['packages'] = [package for package in input('Write additional packages to install (space separated, leave blank to skip): ').split(' ') if len(package)] archinstall.arguments['packages'] = [package for package in input('Write additional packages to install (space separated, leave blank to skip): ').split(' ') if len(package)]
# Verify packages that were given # Verify packages that were given
@ -358,5 +358,3 @@ def perform_installation(device, boot_partition, language, mirrors):
ask_user_questions() ask_user_questions()
perform_installation_steps() perform_installation_steps()

View File

@ -1,4 +1,4 @@
import archinstall import archinstall
installation.add_additional_packages("gnome gnome-extra gdm") # We'll create a gnome-minimal later, but for now, we'll avoid issues by giving more than we need. installation.add_additional_packages("gnome gnome-tweaks gnome-todo gnome-sound-recorder gdm")
# Note: gdm should be part of the gnome group, but adding it here for clarity # Note: gdm should be part of the gnome group, but adding it here for clarity

View File

@ -2,7 +2,9 @@
import archinstall import archinstall
__packages__ = ['nano', 'nemo', 'gpicview-gtk3', 'chromium', 'openssh', 'sshfs', 'htop', 'scrot', 'wget'] # New way of defining packages for a profile, which is iterable and can be used out side
# of the profile to get a list of "what packages will be installed".
__packages__ = ['nano', 'nemo', 'gpicview-gtk3', 'openssh', 'sshfs', 'htop', 'scrot', 'wget']
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):
""" """

View File

@ -1,5 +1,4 @@
# A desktop environment using "Cinnamon" # A desktop environment using "Cinnamon"
import archinstall import archinstall
def _prep_function(*args, **kwargs): def _prep_function(*args, **kwargs):

View File

@ -13,8 +13,8 @@ def _prep_function(*args, **kwargs):
supported_desktops = ['gnome', 'kde', 'awesome', 'xfce4', 'cinnamon'] supported_desktops = ['gnome', 'kde', 'awesome', 'xfce4', 'cinnamon']
desktop = archinstall.generic_select(supported_desktops, 'Select your desired desktop environment: ') desktop = archinstall.generic_select(supported_desktops, 'Select your desired desktop environment: ')
# Temporarly store the selected desktop profile # Temporarily store the selected desktop profile
# in a session-safe location, since this module will get re-loaded # in a session-safe location, since this module will get reloaded
# the next time it gets executed. # the next time it gets executed.
archinstall.storage['_desktop_profile'] = desktop archinstall.storage['_desktop_profile'] = desktop
@ -29,7 +29,7 @@ def _prep_function(*args, **kwargs):
if __name__ == 'desktop': if __name__ == 'desktop':
""" """
This "profile" is a meta-profile. This "profile" is a meta-profile.
There are no specific desktop-steps, it simply routes There are no desktop-specific steps, it simply routes
the installer to whichever desktop environment/window manager was chosen. the installer to whichever desktop environment/window manager was chosen.
Maybe in the future, a network manager or similar things *could* be added here. Maybe in the future, a network manager or similar things *could* be added here.
@ -37,7 +37,7 @@ if __name__ == 'desktop':
it trying to be a turn-key desktop distribution. it trying to be a turn-key desktop distribution.
There are plenty of desktop-turn-key-solutions based on Arch Linux, There are plenty of desktop-turn-key-solutions based on Arch Linux,
this is therefor just a helper to get started this is therefore just a helper to get started
""" """
# TODO: Remove magic variable 'installation' and place it # TODO: Remove magic variable 'installation' and place it

View File

@ -1,4 +1,4 @@
# A desktop environement using "KDE". # A desktop environment using "KDE".
import archinstall, os import archinstall, os

3
pyproject.toml Normal file
View File

@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

39
setup.cfg Normal file
View File

@ -0,0 +1,39 @@
[metadata]
name = archinstall
version = attr: archinstall.__version__
description = Arch Linux installer - guided, templates etc.
author = Anton Hvornum
author_email = anton@hvornum.se
long_description = file: README.md
long_description_content_type = text/markdown
license = GPL
license_files =
LICENSE
project_urls =
Source = https://github.com/archlinux/archinstall
Documentation = https://archinstall.readthedocs.io/
classifers =
Programming Language :: Python :: 3
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Operating System :: POSIX :: Linux
[options]
packages = find:
python_requires = >= 3.8
[options.packages.find]
include =
archinstall
archinstall.*
[options.package_data]
archinstall =
examples/*.py
profiles/*.py
profiles/applications/*.py
[options.entry_points]
console_scripts =
archinstall = archinstall:run_as_a_module

View File

@ -1,27 +1,2 @@
import setuptools, glob, shutil import setuptools
setuptools.setup()
with open("README.md", "r") as fh:
long_description = fh.read()
with open('VERSION', 'r') as fh:
VERSION = fh.read()
setuptools.setup(
name="archinstall",
version=VERSION,
author="Anton Hvornum",
author_email="anton@hvornum.se",
description="Arch Linux installer - guided, templates etc.",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/Torxed/archinstall",
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3.8",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: POSIX :: Linux",
],
python_requires='>=3.8',
setup_requires=['wheel'],
package_data={'archinstall': glob.glob('examples/*.py') + glob.glob('profiles/*.py') + glob.glob('profiles/applications/*.py')},
)