summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElizabeth Alexander Hunt <me@liz.coffee>2025-05-02 16:41:53 -0700
committerElizabeth Alexander Hunt <me@liz.coffee>2025-05-02 17:44:10 -0700
commit68e9b1dc775ac2d013c50c256539364f869f04f1 (patch)
tree72929f2d59467a7cffa0b5f37b2ce46bf0c26303
downloadarchinstall-68e9b1dc775ac2d013c50c256539364f869f04f1.tar.gz
archinstall-68e9b1dc775ac2d013c50c256539364f869f04f1.zip
initial commit
-rw-r--r--archinstall-aur.py229
-rw-r--r--user_configuration.json183
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"
+}