Files
nixos/modules/features/unraid-usb-creator/default.nix
T

120 lines
5.1 KiB
Nix
Raw Normal View History

{...}: {
# Unraid USB Creator - writes Unraid OS straight to a USB stick, streaming the
# image from unraid.net instead of making you download it first.
#
# Not in nixpkgs, and upstream ships an AppImage only, so this is a repack.
# The AppImage is a Qt6/QML app (a fork of rpi-imager) built with linuxdeploy,
# meaning it already vendors the entire Qt6 stack, xcb, icu, gnutls and krb5 in
# usr/lib. We keep that bundle verbatim and only autoPatchelf it against the
# handful of libraries linuxdeploy deliberately left out (libc, libstdc++, GL,
# X11, fontconfig, ...). No FHS wrapper needed, so no 400MB buildFHSEnv closure.
flake.nixosModules.unraid-usb-creator = {
pkgs,
lib,
...
}: let
pname = "unraid-usb-creator";
version = "1.1.0";
# unraid.net/download's "stable" link is a redirect to this GitHub asset.
# Note the v1.1.0 tag is not returned by the releases API (the API only lists
# the v2.0.x line), but the download URL is live and matches the checksum.
appimage = pkgs.fetchurl {
url = "https://github.com/unraid/usb-creator-next/releases/download/v${version}/${pname}-${version}.appimage";
hash = "sha256-NI/SMgOuzifvjhtK4Py96lbuI/uvJO0NiMr2zNtNSUk=";
};
unraid-usb-creator = pkgs.stdenvNoCC.mkDerivation {
inherit pname version;
src = pkgs.appimageTools.extract {
inherit pname version;
src = appimage;
};
nativeBuildInputs = with pkgs; [
autoPatchelfHook
makeWrapper
];
# Only the libraries the AppImage does *not* vendor. Everything else
# resolves inside $out/share/unraid-usb-creator/lib.
buildInputs = with pkgs; [
e2fsprogs # libcom_err.so.2, pulled in by the vendored krb5
fontconfig
freetype
gmp # pulled in by the vendored nettle/gnutls
libgpg-error
libglvnd # libGL / libGLX / libEGL / libOpenGL
libx11 # also provides libX11-xcb
libxcb
stdenv.cc.cc # libstdc++ / libgcc_s
zlib
];
# Qt's openssl TLS backend resolves libssl/libcrypto with dlopen, so
# autoPatchelf never sees the dependency. Without this the app cannot fetch
# the release JSON or stream the image over HTTPS.
#
# This has to be appendRunpaths and NOT runtimeDependencies: in
# auto-patchelf, `rpath += runtime_deps` lives inside the set-interpreter
# branch, so it only ever reaches executables, while `rpath.extend(
# append_rpaths)` runs for every patched file. The dlopen happens from
# plugins/tls/libqopensslbackend.so, and glibc resolves dlopen against the
# calling object's DT_RUNPATH - the wrapper binary's RUNPATH is not
# consulted - so the path has to be on the plugin itself.
appendRunpaths = ["${pkgs.openssl.out}/lib"];
installPhase = ''
runHook preInstall
# qt.conf next to the binary declares Prefix=../ with Plugins=plugins and
# Qml2Imports=qml, so the usr/ layout has to be preserved as a unit.
install -Dm755 usr/bin/unraid-usb-creator -t $out/share/${pname}/bin
install -Dm444 usr/bin/qt.conf -t $out/share/${pname}/bin
cp -r usr/lib usr/plugins usr/qml $out/share/${pname}/
cp -r usr/share/icons $out/share/
install -Dm444 usr/share/metainfo/*.xml -t $out/share/metainfo
install -Dm444 usr/share/applications/*.desktop -t $out/share/applications
# mkfs.fat is shelled out to when formatting the stick; syslinux is NOT
# needed as a runtime dep because upstream statically vendors it.
#
# linuxdeploy only bundled the xcb platform plugin - there is no
# libqwayland*.so anywhere in the AppImage - so this always runs through
# XWayland under Hyprland. Pinning the platform avoids Qt first trying
# wayland and falling back; --set-default rather than --set so it can
# still be overridden if upstream ever ships the Wayland plugin.
makeWrapper $out/share/${pname}/bin/unraid-usb-creator $out/bin/${pname} \
--prefix PATH : ${lib.makeBinPath (with pkgs; [dosfstools util-linux])} \
--set-default QT_QPA_PLATFORM xcb
runHook postInstall
'';
postFixup = ''
substituteInPlace $out/share/applications/*.desktop \
--replace-fail "Exec=unraid-usb-creator" "Exec=$out/bin/${pname}"
'';
meta = {
description = "Tool for writing Unraid OS to USB drives";
homepage = "https://github.com/unraid/usb-creator-next";
license = lib.licenses.asl20;
sourceProvenance = [lib.sourceTypes.binaryNativeCode];
platforms = ["x86_64-linux"];
mainProgram = pname;
};
};
in {
environment.systemPackages = [unraid-usb-creator];
# The app enumerates and unmounts removable drives over the UDisks2 D-Bus API
# rather than poking /dev directly, which is also how it avoids needing root.
# hyprland/default.nix already enables this for udiskie; repeated here so the
# module is self-contained on a host that does not use Hyprland.
services.udisks2.enable = true;
};
}