Unverified Commit d7ef2def authored by WilliButz's avatar WilliButz
Browse files

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.
parent 1357b820
Loading
Loading
Loading
Loading
+59 −26
Original line number Diff line number Diff line
@@ -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,38 +76,56 @@ 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;

  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 \
@@ -117,14 +133,31 @@ runCommand imageFileBasename
      ${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;
  };
})
+3 −3
Original line number Diff line number Diff line
@@ -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 ];