Unverified Commit 9bfd0d68 authored by r-vdp's avatar r-vdp
Browse files

nixos/activation: avoid build failure with large activation script

Two improvements:
* use `writeShellApplication` (which uses `passAsFile` instead of
  passing the activation script as an env var into the derivation.
  We disable shellcheck and the bash options that this builder
  usually adds to avoid issues with out-of-tree activation scripts.
* use `sed` instead of `substituteInPlace`, since the substitute
  functions load the file content into a shell variable

This avoids issues when the activation is too long to fit in a shell variable.
Before this commit, a very large activation script, would cause build
failures because of different limits on the file content size, e.g.
```
➜ nix build -f . nixosTests.restartByActivationScript.nodes.machine.system.build.toplevel -vL
this derivation will be built:
  /nix/store/xw6anpfjyamnycjg58cmj01sz3ididyl-nixos-system-machine-test.drv
building '/nix/store/xw6anpfjyamnycjg58cmj01sz3ididyl-nixos-system-machine-test.drv'...
nixos-system-machine-test> error: executing '/nix/store/rlq03x4cwf8zn73hxaxnx0zn5q9kifls-bash-5.3p3/bin/bash': Argument list too long
error: builder for '/nix/store/xw6anpfjyamnycjg58cmj01sz3ididyl-nixos-system-machine-test.drv' failed with exit code 1;
       last 1 log lines:
       > error: executing '/nix/store/rlq03x4cwf8zn73hxaxnx0zn5q9kifls-bash-5.3p3/bin/bash': Argument list too long
       For full logs, run:
        nix log /nix/store/xw6anpfjyamnycjg58cmj01sz3ididyl-nixos-system-machine-test.drv
```
parent d8ed3999
Loading
Loading
Loading
Loading
+41 −32
Original line number Diff line number Diff line
@@ -11,12 +11,6 @@ let
    mkOption
    types
    ;

  systemBuilderArgs = {
    activationScript = config.system.activationScripts.script;
    dryActivationScript = config.system.dryActivationScript;
  };

in
{
  options = {
@@ -52,33 +46,48 @@ in
      '';
    };
  };
  config = {
    system.activatableSystemBuilderCommands = ''
      echo "$activationScript" > $out/activate
      echo "$dryActivationScript" > $out/dry-activate
      substituteInPlace $out/activate --subst-var-by out ''${!toplevelVar}
      substituteInPlace $out/dry-activate --subst-var-by out ''${!toplevelVar}
      chmod u+x $out/activate $out/dry-activate
      unset activationScript dryActivationScript
  config =
    let
      activationScript = lib.getExe (
        pkgs.writeShellApplication {
          name = "activate";
          text = config.system.activationScripts.script;
          checkPhase = "";
          bashOptions = [ ];
        }
      );
      dryActivationScript = lib.getExe (
        pkgs.writeShellApplication {
          name = "dry-activate";
          text = config.system.dryActivationScript;
          checkPhase = "";
          bashOptions = [ ];
        }
      );
    in
    {
      system.activatableSystemBuilderCommands =
        # We use sed here instead of substitute(InPlace), because the substitute
        # functions load the content of the file into a bash variable, which fails
        # for very large activation scripts.
        # bash
        ''
          cp ${activationScript} $out/activate
          cp ${dryActivationScript} $out/dry-activate
          ${lib.getExe pkgs.gnused} --in-place --expression "s|@out@|''${!toplevelVar}|g" $out/activate $out/dry-activate
        '';

      system.systemBuilderCommands = lib.mkIf config.system.activatable config.system.activatableSystemBuilderCommands;
    system.systemBuilderArgs = lib.mkIf config.system.activatable (
      systemBuilderArgs
      // {
      system.systemBuilderArgs = lib.mkIf config.system.activatable {
        toplevelVar = "out";
      }
    );
      };

      system.build.separateActivationScript =
        pkgs.runCommand "separate-activation-script"
        (
          systemBuilderArgs
          // {
          {
            toplevelVar = "toplevel";
            toplevel = config.system.build.toplevel;
          }
        )
          ''
            mkdir $out
            ${config.system.activatableSystemBuilderCommands}
+0 −2
Original line number Diff line number Diff line
@@ -50,8 +50,6 @@ let
      ) withHeadlines;
    in
    ''
      #!${pkgs.runtimeShell}

      source ${./lib/lib.sh}

      systemConfig='@out@'
+9 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
  };

  nodes.machine =
    { pkgs, ... }:
    { pkgs, lib, ... }:
    {
      imports = [ ../modules/profiles/minimal.nix ];

@@ -46,6 +46,14 @@
          fi
        '';
      };

      # Make sure we don't crash on long activation scripts
      specialisation.longscript.configuration = {
        system.activationScripts.long = {
          supportsDryActivation = true;
          text = lib.concatStringsSep "\n" (lib.genList (i: ''# line number ${toString i}'') 1000000);
        };
      };
    };

  testScript = # python