From d7ef2defdadd066beaa81c82206dc23217402ef6 Mon Sep 17 00:00:00 2001 From: WilliButz Date: Tue, 19 Mar 2024 17:04:47 +0100 Subject: [PATCH] nixos/repart-image: refactor to use mkDerivation As a follow-up to https://github.com/NixOS/nixpkgs/pull/294096 this should further improve the flexibility around building OS images with systemd-repart: * Previously the attribute set `compression` needed to be fully populated, including `algorithm` and `level` because `compression.enable` was evaluated by bash, after being interpolated as strings into the `buildCommand`. Now it's sufficient to pass `compression.enable = false` to the builder, e.g. in `overrideAttrs`, to disable the compression. * Using mkDerivation allows for much more customization than the previously used `runCommand`, making use of phases and pre/post hooks. This is especially helpful for building multiple images from the same system configuration, e.g. to build an image `Y` based on a partially built raw image `X`, by injecting a UKI that depends on `X` into a defered ESP. * Before this change it was non-trivial to conduct further manipulations on the amended repart definitions. Now, the definitions that systemd-repart uses to build the image can be easily manipulated in `postPatch` or `preBuild`. Aside from this, the build is now executed in the build directory, rather than `$out`. This allows references to relative paths in the build environment to be used, especially for `--definitions`, which previously required an absolute path. --- nixos/modules/image/repart-image.nix | 85 +++++++++++++++++++--------- nixos/modules/image/repart.nix | 6 +- 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/nixos/modules/image/repart-image.nix b/nixos/modules/image/repart-image.nix index 5ae523c43f58..83e766268cf0 100644 --- a/nixos/modules/image/repart-image.nix +++ b/nixos/modules/image/repart-image.nix @@ -2,8 +2,8 @@ # NixOS module that can be imported. { lib +, stdenvNoCC , runCommand -, runCommandLocal , python3 , black , ruff @@ -26,15 +26,18 @@ , xz # arguments +, name +, version , imageFileBasename , compression , fileSystems -, partitions +, partitionsJSON , split , seed , definitionsDirectory , sectorSize , mkfsEnv ? {} +, createEmpty ? true }: let @@ -52,11 +55,6 @@ let mypy --strict $out ''; - amendedRepartDefinitions = runCommandLocal "amended-repart.d" {} '' - definitions=$(${amendRepartDefinitions} ${partitions} ${definitionsDirectory}) - cp -r $definitions $out - ''; - fileSystemToolMapping = { "vfat" = [ dosfstools mtools ]; "ext4" = [ e2fsprogs.bin ]; @@ -78,53 +76,88 @@ let "xz" = "xz --keep --verbose --threads=0 -${toString compression.level}"; }."${compression.algorithm}"; in - -runCommand imageFileBasename -{ + stdenvNoCC.mkDerivation (finalAttrs: + (if (version != null) + then { pname = name; inherit version; } + else { inherit name; } + ) // { __structuredAttrs = true; nativeBuildInputs = [ systemd fakeroot util-linux + ] ++ lib.optionals (compression.enable) [ compressionPkg ] ++ fileSystemTools; env = mkfsEnv; + inherit partitionsJSON definitionsDirectory; + + # relative path to the repart definitions that are read by systemd-repart + finalRepartDefinitions = "repart.d"; + systemdRepartFlags = [ "--dry-run=no" - "--empty=create" "--size=auto" "--seed=${seed}" - "--definitions=${amendedRepartDefinitions}" + "--definitions=${finalAttrs.finalRepartDefinitions}" "--split=${lib.boolToString split}" "--json=pretty" + ] ++ lib.optionals createEmpty [ + "--empty=create" ] ++ lib.optionals (sectorSize != null) [ "--sector-size=${toString sectorSize}" ]; - passthru = { - inherit amendRepartDefinitions amendedRepartDefinitions; - }; -} '' - mkdir -p $out - cd $out + dontUnpack = true; + dontConfigure = true; + doCheck = false; - echo "Building image with systemd-repart..." - unshare --map-root-user fakeroot systemd-repart \ - ''${systemdRepartFlags[@]} \ - ${imageFileBasename}.raw \ - | tee repart-output.json + patchPhase = '' + runHook prePatch + amendedRepartDefinitionsDir=$(${amendRepartDefinitions} $partitionsJSON $definitionsDirectory) + ln -vs $amendedRepartDefinitionsDir $finalRepartDefinitions + + runHook postPatch + ''; + + buildPhase = '' + runHook preBuild + + echo "Building image with systemd-repart..." + unshare --map-root-user fakeroot systemd-repart \ + ''${systemdRepartFlags[@]} \ + ${imageFileBasename}.raw \ + | tee repart-output.json + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out + '' # Compression is implemented in the same derivation as opposed to in a # separate derivation to allow users to save disk space. Disk images are # already very space intensive so we want to allow users to mitigate this. - if ${lib.boolToString compression.enable}; then + + lib.optionalString compression.enable + '' for f in ${imageFileBasename}*; do echo "Compressing $f with ${compression.algorithm}..." # Keep the original file when compressing and only delete it afterwards ${compressionCommand} $f && rm $f done - fi -'' + '' + '' + mv -v repart-output.json ${imageFileBasename}* $out + + runHook postInstall + ''; + + passthru = { + inherit amendRepartDefinitions; + }; +}) diff --git a/nixos/modules/image/repart.nix b/nixos/modules/image/repart.nix index 90c9c7e51dfa..aa5426cef076 100644 --- a/nixos/modules/image/repart.nix +++ b/nixos/modules/image/repart.nix @@ -266,14 +266,14 @@ in format (lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) finalPartitions); - partitions = pkgs.writeText "partitions.json" (builtins.toJSON finalPartitions); + partitionsJSON = pkgs.writeText "partitions.json" (builtins.toJSON finalPartitions); mkfsEnv = mkfsOptionsToEnv cfg.mkfsOptions; in pkgs.callPackage ./repart-image.nix { systemd = cfg.package; - inherit (cfg) imageFileBasename compression split seed sectorSize; - inherit fileSystems definitionsDirectory partitions mkfsEnv; + inherit (cfg) name version imageFileBasename compression split seed sectorSize; + inherit fileSystems definitionsDirectory partitionsJSON mkfsEnv; }; meta.maintainers = with lib.maintainers; [ nikstur ];