add a netops attrset with winbox and an unraid usb creator repack
two network/storage admin tools that only shadow needs, grouped behind one attrset so the host list stays a list of intents rather than of packages. winbox is a one-liner: pkgs.winbox resolves to winbox4, which nixpkgs already builds through autoPatchelfHook against the qt/xcb stack it needs. it does not go anywhere near nix-ld, so nothing has to be added to the nix-ld library list for it - a comment says so, since the opposite is an easy assumption to make about a vendored mikrotik binary. the installed binary is WinBox, capital w, which is worth knowing at a shell prompt. unraid-usb-creator is not in nixpkgs and upstream ships an appimage only, so it is a repack. the appimage is a qt6/qml app - a fork of rpi-imager - built with linuxdeploy, which means it already vendors the whole qt6 stack plus xcb, icu, gnutls and krb5 under usr/lib. that bundle is kept verbatim and autopatchelfed against only the handful of libraries linuxdeploy deliberately leaves to the host: libc, libstdc++, GL, x11, fontconfig and friends. no FHS wrapper, so no ~400mb buildFHSEnv closure. the usr/ layout has to survive as a unit because qt.conf sitting next to the binary declares Prefix=../ with Plugins=plugins and Qml2Imports=qml. the non-obvious part is appendRunpaths rather than runtimeDependencies for openssl. qt's tls backend dlopens libssl/libcrypto, so autopatchelf never sees the dependency, and without it the app cannot fetch the release json or stream the image. it has to be appendRunpaths because in auto-patchelf `rpath += runtime_deps` sits inside the set-interpreter branch and so 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's runpath is not consulted - so the path must land on the plugin itself. QT_QPA_PLATFORM is pinned to xcb because linuxdeploy bundled only the xcb platform plugin; there is no libqwayland*.so anywhere in the appimage, so this always runs through xwayland under hyprland regardless. --set-default rather than --set, so it can still be overridden if upstream ever ships the wayland plugin. udisks2 is enabled from this module even though hyprland/default.nix already enables it for udiskie. the app enumerates and unmounts removable drives over the udisks2 d-bus api rather than poking /dev, which is how it avoids needing root, and repeating the option keeps the module standalone on a host with no hyprland. bool options merge on equal values, so this costs nothing. v1.1.0 is pinned by hash. note the tag is not returned by the github releases api - which only lists the v2.0.x line - but the asset url that unraid.net/download redirects to is live and matches. verified: shadow evaluates clean, both packages build, the .desktop Exec is rewritten from the bare name to the absolute store path, ldd on the main binary reports nothing unresolved, and the openssl lib dir is present on libqopensslbackend.so's runpath. not yet exercised at runtime against an actual usb stick.
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
{...}: {
|
||||
# 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;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user