diff options
author | Elizabeth Alexander Hunt <me@liz.coffee> | 2025-05-02 16:41:53 -0700 |
---|---|---|
committer | Elizabeth Alexander Hunt <me@liz.coffee> | 2025-05-02 17:44:10 -0700 |
commit | 68e9b1dc775ac2d013c50c256539364f869f04f1 (patch) | |
tree | 72929f2d59467a7cffa0b5f37b2ce46bf0c26303 | |
download | archinstall-68e9b1dc775ac2d013c50c256539364f869f04f1.tar.gz archinstall-68e9b1dc775ac2d013c50c256539364f869f04f1.zip |
initial commit
-rw-r--r-- | archinstall-aur.py | 229 | ||||
-rw-r--r-- | user_configuration.json | 183 |
2 files changed, 412 insertions, 0 deletions
diff --git a/archinstall-aur.py b/archinstall-aur.py new file mode 100644 index 0000000..b032e85 --- /dev/null +++ b/archinstall-aur.py @@ -0,0 +1,229 @@ +import archinstall +import re +import glob +import shutil +import pathlib +import logging +import urllib.request + +__version__ = 0.2 + +sudo_user = archinstall.arguments.get("aur-user", "aoffline_usr") +try: + found_aur_user = archinstall.SysCommand(f"id {sudo_user}").exit_code == 0 +except: + found_aur_user = False + +AUR_USER_CREATED = False +AUR_HELPER = None +AUR_HELPERS = { + "yay-bin": "LANG=C yay --noprovides --answerdiff None --answerclean None --mflags '--noconfirm'" +} + + +def untar_file(file): + archinstall.log( + f"(runas {sudo_user}) /usr/bin/tar --directory /home/{sudo_user}/ -xvzf {file}", + level=logging.DEBUG, + fg="gray", + ) + archinstall.storage["installation_session"].arch_chroot( + f"/usr/bin/tar --directory /home/{sudo_user}/ -xvzf {file}", run_as=sudo_user + ) + + +def download_file(url, destination, filename=""): + if not (dst := pathlib.Path(destination)).exists(): + dst.mkdir(parents=True) + + if dst.is_file(): + return False + + tmp_filename, headers = urllib.request.urlretrieve(url) + shutil.move(tmp_filename, f"{destination}/{filename}") + + return True + + +class Plugin: + def on_pacstrap(self, packages: list) -> list: + global AUR_USER_CREATED + global AUR_HELPER + global AUR_HELPERS + + if type(packages) == str: + packages = packages.split(" ") + + archinstall.log( + f"Identifying AUR packages in package list: {packages}", + level=logging.INFO, + fg="gray", + ) + aur_packages = [] + std_packages = [] + + # We'd like to use upstream or a local JSON database to lookup packages. + # But for now this is the lowest latency option that doesn't hog resources upstream. + for package in packages: + try: + if archinstall.SysCommand(f"pacman -Ss {package}").exit_code == 0: + std_packages.append(package) + else: + aur_packages.append(package) + + except archinstall.lib.exceptions.SysCallError: + aur_packages.append(package) + + mount_location = archinstall.storage["installation_session"].target + + aur_packages = sorted( + aur_packages, key=lambda package: 0 if package in AUR_HELPERS else 1 + ) + for package in aur_packages: + if AUR_USER_CREATED is False: + archinstall.log( + f"Setting up temporary AUR build user {sudo_user} and installing build tools for {aur_packages}", + level=logging.INFO, + fg="gray", + ) + # We have to install fakeroot to the live medium as it's missing + # (wasn't ever really intended to build stuff..) + archinstall.storage["installation_session"].add_additional_packages( + ["fakeroot", "base-devel"] + ) + + with open(f"{mount_location}/etc/sudoers.d/{sudo_user}", "w") as fh: + # TODO: This could be tweaked to only contain the binaries needed, such as `makepkg` and `pacman -U`. + # But it's done in the live environment, not the final installation.. + # So risks are low unless the user pre-enabled sshd with a login for said user. + fh.write(f"{sudo_user} ALL=(ALL:ALL) NOPASSWD: ALL\n") + + archinstall.log(f"Creating temporary build user {sudo_user}") + archinstall.storage["installation_session"].user_create( + sudo_user, password="somethingrandom" + ) + # archinstall.SysCommand(f"/usr/bin/useradd -m -N -s /bin/bash {sudo_user}") + + AUR_USER_CREATED = True + + archinstall.log( + f"Building AUR package {package}", level=logging.INFO, fg="yellow" + ) + + if AUR_HELPER is not None: + build_command = f"{AUR_HELPER} -S {package}" + try: + archinstall.storage["installation_session"].arch_chroot( + f'/bin/sh -c "{build_command}"', run_as=sudo_user + ) + continue + except archinstall.lib.exceptions.SysCallError: + pass + + if not download_file( + f"https://aur.archlinux.org/cgit/aur.git/snapshot/{package}.tar.gz", + destination=f"{mount_location}/home/{sudo_user}/", + filename=f"{package}.tar.gz", + ): + archinstall.log( + f"Could not retrieve {package} from: https://aur.archlinux.org/cgit/aur.git/snapshot/{package}.tar.gz", + fg="red", + level=logging.ERROR, + ) + exit(1) + + archinstall.storage["installation_session"].chown( + sudo_user, f"/home/{sudo_user}/{package}.tar.gz" + ) + untar_file(f"/home/{sudo_user}/{package}.tar.gz") + with open( + f"{mount_location}/home/{sudo_user}/{package}/PKGBUILD", "r" + ) as fh: + PKGBUILD = fh.read() + + # This regexp needs to accomodate multiple keys, as well as the logic below + gpgkeys = re.findall("validpgpkeys=\(.*\)", PKGBUILD) + if gpgkeys: + for key in gpgkeys: + key = key[13:].strip("(')\"") + archinstall.log(f"Adding GPG-key {key} to session for {sudo_user}") + archinstall.storage["installation_session"].arch_chroot( + f"/usr/bin/gpg --recv-keys {key}", run_as=sudo_user + ) + + build_command = f'cd /home/{sudo_user}/{package}; makepkg --clean --force --cleanbuild --noconfirm --needed -s' + if ( + build_handle := archinstall.storage["installation_session"].arch_chroot( + f'/bin/bash -c "{build_command}"', run_as=sudo_user + ) + ).exit_code != 0: + archinstall.log(build_handle, level=logging.ERROR) + archinstall.log( + f"Could not build {package}, see traceback above. Continuing to avoid re-build needs for the rest of the run and re-runs.", + fg="red", + level=logging.ERROR, + ) + else: + print( + f"Looking for: {mount_location}/home/{sudo_user}/{package}/*.tar.zst" + ) + if built_package := glob.glob( + f"{mount_location}/home/{sudo_user}/{package}/*.tar.zst" + ): + built_package = pathlib.Path(built_package[0]).name + print(f"Found package: {built_package}") + + archinstall.storage["installation_session"].arch_chroot( + f"/usr/bin/pacman --noconfirm -U /home/{sudo_user}/{package}/{built_package}" + ) + shutil.rmtree(f"{mount_location}/home/{sudo_user}/{package}") + pathlib.Path( + f"{mount_location}/home/{sudo_user}/{package}.tar.gz" + ).unlink() + AUR_HELPER = AUR_HELPERS.get(package, AUR_HELPER) + else: + archinstall.log( + f"Could not locate {package}.tar.zst after build.", + fg="red", + level=logging.ERROR, + ) + exit(1) + + if AUR_USER_CREATED: + archinstall.log(f"Removing temporary build user {sudo_user}") + + pathlib.Path(f"{mount_location}/etc/sudoers.d/{sudo_user}").unlink() + + # TODO: These are only needed if we run Installation.Boot(): + # Stop dirmngr and gpg-agent before removing home directory and running userdel + # archinstall.storage['installation_session'].arch_chroot(f"/usr/bin/systemctl --machine={sudo_user}@.host --user stop dirmngr.socket", run_as=sudo_user) + archinstall.storage["installation_session"].arch_chroot( + f"/usr/bin/gpgconf --kill gpg-agent", run_as=sudo_user + ) + try: + archinstall.storage["installation_session"].arch_chroot( + f"/usr/bin/killall -u {sudo_user}", run_as=sudo_user + ) + except archinstall.lib.exceptions.SysCallError: + # We'll terminate our own running process and that's fine + pass + archinstall.storage["installation_session"].arch_chroot( + f"/usr/bin/userdel {sudo_user}" + ) + + shutil.rmtree(f"{mount_location}/home/{sudo_user}") + + AUR_USER_CREATED = False + + # Returns a curated list of packages that exludes any AUR packages. + # This allows installataion.pacstrap() to contain AUR packages, + # but won't handle them or try to install them since we remove those here. + return std_packages + + +def dummy_example(*args, **kwargs): + pass + + +# Example function injection +archinstall.plugin_function = dummy_example diff --git a/user_configuration.json b/user_configuration.json new file mode 100644 index 0000000..f6af4f4 --- /dev/null +++ b/user_configuration.json @@ -0,0 +1,183 @@ +{ + "archinstall-language": "English", + "audio_config": { + "audio": "pipewire" + }, + "bootloader": "Grub", + "custom_commands": [], + "disk_config": null, + "disk_encryption": null, + "hostname": "okabe", + "kernels": ["linux"], + "locale_config": { + "kb_layout": "us", + "sys_enc": "UTF-8", + "sys_lang": "en_US" + }, + "mirror_config": { + "custom_repositories": [], + "custom_servers": [], + "mirror_regions": { + "United States": [ + "https://mirrors.rit.edu/archlinux/$repo/os/$arch", + "https://mirror.umd.edu/archlinux/$repo/os/$arch", + "https://ftp.osuosl.org/pub/archlinux/$repo/os/$arch", + "https://mirrors.lug.mtu.edu/archlinux/$repo/os/$arch", + "https://mirrors.kernel.org/archlinux/$repo/os/$arch", + "https://mirror.sfo12.us.leaseweb.net/archlinux/$repo/os/$arch", + "https://mirror.wdc1.us.leaseweb.net/archlinux/$repo/os/$arch", + "https://dfw.mirror.rackspace.com/archlinux/$repo/os/$arch", + "https://iad.mirror.rackspace.com/archlinux/$repo/os/$arch", + "https://ord.mirror.rackspace.com/archlinux/$repo/os/$arch", + "https://arch.mirror.constant.com/$repo/os/$arch", + "https://mirrors.ocf.berkeley.edu/archlinux/$repo/os/$arch", + "https://arlm.tyzoid.com/$repo/os/$arch", + "https://us.mirrors.cicku.me/archlinux/$repo/os/$arch", + "https://mirrors.sonic.net/archlinux/$repo/os/$arch", + "https://mirrors.xtom.com/archlinux/$repo/os/$arch", + "https://repo.ialab.dsu.edu/archlinux/$repo/os/$arch", + "https://iad.mirrors.misaka.one/archlinux/$repo/os/$arch", + "https://mirror.pit.teraswitch.com/archlinux/$repo/os/$arch", + "https://mirror.arizona.edu/archlinux/$repo/os/$arch", + "https://archmirror1.octyl.net/$repo/os/$arch", + "https://plug-mirror.rcac.purdue.edu/archlinux/$repo/os/$arch", + "https://mirrors.mit.edu/archlinux/$repo/os/$arch", + "https://arch.hu.fo/archlinux/$repo/os/$arch", + "https://zxcvfdsa.com/arch/$repo/os/$arch", + "https://mirror.theash.xyz/arch/$repo/os/$arch", + "https://mirror.clarkson.edu/archlinux/$repo/os/$arch", + "https://mirrors.bloomu.edu/archlinux/$repo/os/$arch", + "https://codingflyboy.mm.fcix.net/archlinux/$repo/os/$arch", + "https://coresite.mm.fcix.net/archlinux/$repo/os/$arch", + "https://forksystems.mm.fcix.net/archlinux/$repo/os/$arch", + "https://irltoolkit.mm.fcix.net/archlinux/$repo/os/$arch", + "https://mirror.fcix.net/archlinux/$repo/os/$arch", + "https://mnvoip.mm.fcix.net/archlinux/$repo/os/$arch", + "https://nnenix.mm.fcix.net/archlinux/$repo/os/$arch", + "https://nocix.mm.fcix.net/archlinux/$repo/os/$arch", + "https://ohioix.mm.fcix.net/archlinux/$repo/os/$arch", + "https://opencolo.mm.fcix.net/archlinux/$repo/os/$arch", + "https://ridgewireless.mm.fcix.net/archlinux/$repo/os/$arch", + "https://southfront.mm.fcix.net/archlinux/$repo/os/$arch", + "https://volico.mm.fcix.net/archlinux/$repo/os/$arch", + "https://ziply.mm.fcix.net/archlinux/$repo/os/$arch", + "https://america.mirror.pkgbuild.com/$repo/os/$arch", + "https://losangeles.mirror.pkgbuild.com/$repo/os/$arch", + "https://mirrors.vectair.net/archlinux/$repo/os/$arch", + "https://arch.mirror.k0.ae/$repo/os/$arch", + "https://mirror.zackmyers.io/archlinux/$repo/os/$arch", + "https://m.lqy.me/arch/$repo/os/$arch", + "https://mirror.adectra.com/archlinux/$repo/os/$arch", + "https://arch.goober.cloud/$repo/os/$arch", + "https://mirrors.bjg.at/arch/$repo/os/$arch", + "https://mirror.pilotfiber.com/archlinux/$repo/os/$arch", + "https://mirrors.iu13.net/archlinux/$repo/os/$arch", + "https://mirror.colonelhosting.com/archlinux/$repo/os/$arch", + "https://us.arch.niranjan.co/$repo/os/$arch", + "https://mirror.hasphetica.win/archlinux/$repo/os/$arch", + "https://arch-mirror.marcusspencer.xyz:4443/archlinux/$repo/os/$arch", + "https://us-mnz.soulharsh007.dev/archlinux/$repo/os/$arch", + "https://mirror.akane.network/archmirror/$repo/os/$arch", + "https://arch.miningtcup.me/$repo/os/$arch", + "https://mirrors.smeal.xyz/arch-linux/$repo/os/$arch", + "https://arch-mirror.brightlight.today/$repo/os/$arch", + "https://yonderly.org/mirrors/archlinux/$repo/os/$arch", + "https://mirrors.lahansons.com/archlinux/$repo/os/$arch", + "https://mirror.givebytes.net/archlinux/$repo/os/$arch" + ] + }, + "optional_repositories": [] + }, + "network_config": { + "type": "nm" + }, + "ntp": true, + "packages": [ + "ansible", + "ansible-core", + "ansible-language-server", + "ansible-lint", + "base-devel", + "betterdiscordctl", + "bibata-cursor-theme-bin", + "bitwarden-cli", + "bitwarden", + "bluez", + "bluez-utils", + "cinny-desktop-bin", + "dconf-editor", + "breeze-plymouth", + "discord", + "docker", + "docker-compose", + "emacs-wayland", + "flatpak", + "git", + "gnome-keyring", + "graphite-grub-theme-default-1080p", + "libmpeg2", + "librewolf-bin", + "mpv", + "neovim", + "niri", + "noto-fonts", + "noto-fonts-emoji", + "nvidia-open", + "nvidia-utils", + "obs-studio", + "openssh", + "pandoc-cli", + "pavucontrol", + "polkit", + "polkit-kde-agent", + "reflector", + "sshfs", + "starship", + "swaybg", + "swayidle", + "swaylock", + "texlive-basic", + "texlive-latex", + "texlive-mathscience", + "thunderbird", + "tidal-hifi-bin", + "ttf-firacode-nerd", + "ttf-font-awesome", + "ttf-hack-nerd", + "ttf-iawriter-nerd", + "ttf-ibmplex-mono-nerd", + "ttf-lilex-nerd", + "ttf-mononoki-nerd", + "ttf-nerd-fonts-symbols", + "ttf-space-mono-nerd", + "waybar", + "wezterm", + "x264", + "x265", + "xdg-desktop-portal-gtk", + "xdg-desktop-portal-gnome", + "xdg-utils", + "xorg-xrdb", + "xorg-xwayland", + "xwayland-satellite", + "xvidcore", + "yay-bin", + "zed", + "zathura", + "zoxide", + "zsh", + "zsh-autocomplete", + "zsh-autosuggestions", + "zsh-completions", + "zsh-syntax-highlighting", + "ly", + "mise", + "mako" + ], + "parallel downloads": 0, + "profile_config": null, + "services": ["bluetooth", "ly", "NetworkManager", "reflector", "polkit"], + "swap": true, + "timezone": "US/Pacific", + "version": "2.8.3" +} |