Skip to content

Unclear semantics of "apply" #75

@zenoli

Description

@zenoli

TL;DR:

  • When having conflicting configs in .apply chains, should the newest win or do we have to make priorities explicit using mkForce?
  • Multiple occurences of the same flag (i.e. "--greeting") get concatenated. Is this intended?
  • grepping the wrapper scripts seems not to be robust enough.

Some things I noted when studying the checks in apply.nix:

The semantics are not clear from reading the tests to .apply:

  # Apply with initial settings
  initialConfig = helloModule.apply {
    inherit pkgs;
    flags."--verbose" = { };
  };

  # Extend the configuration
  extendedConfig = initialConfig.apply {
    flags."--greeting" = "extended";
    flags."--extra" = "flag";
  };

  # Test mkForce to override a value
  forcedConfig = initialConfig.apply (
    { lib, ... }:
    {
      flags."--greeting" = lib.mkForce "forced";   # <-- here it is implied that we need to `mkForce` to avoid conflicts
      flags."--forced-flag" = { };
    }
  );

  # Test extending via wrapper.passthru.configuration.apply
  passthruExtendedConfig = initialConfig.wrapper.passthru.configuration.apply {
    flags."--passthru" = "test";
  };

  # Test chaining apply multiple levels deep
  doubleApply = extendedConfig.apply {
    flags."--greeting" = "double";   # <-- here we do not use mkForce
    flags."--double" = "level2";
  };

  tripleApply = doubleApply.apply {
    flags."--greeting" = "triple"; # <-- here we do not use mkForce as well
    flags."--triple" = "level3";
  };

When trying to look at it myself I found the following:

Consider this demo-wrapper.nix

{
  config,
  lib,
  wlib,
  ...
}:
{
  options.foo = lib.mkOption {
    type = lib.types.str;
    default = "foo (default)";
  };
  options.bar = lib.mkOption {
    type = lib.types.str;
    default = "bar (default)";
  };
  config.package = config.pkgs.hello;
  config.foo = lib.mkDefault "foo (initial)";
  config.flags = {
    "--greeting" = "hi (initial)";
  };
}

and then use it as follows (flake.nix):

{
  description = "A very basic flake";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    wrappers.url = "github:lassulus/wrappers";
  };

  outputs = { self, nixpkgs, wrappers }: 
  let 
    system = "x86_64-linux";
    pkgs = nixpkgs.legacyPackages.${system};
    lib = pkgs.lib;
    demoWrapperModule = import ./demo-wrapper.nix;
    fooEvaledWrappers = wrappers.lib.wrapModule demoWrapperModule;
    fooApplied = fooEvaledWrappers.apply {
      foo = "foo (applied)"; 
      flags = { "--greeting" = "hi (apply)"; };
    };
    fooApplied2 = fooApplied.apply { 
      foo = lib.mkForce "foo (applied2)"; # <-- this will yield an error if I don't use mkForce
      flags = { "--greeting" = "hi (apply2)"; }; # <-- this works without mkForce
    };

  in
  {
    fooWrappers = fooApplied2;
  };
}

Evaluating the flags yields:

/> ~/repos/nix-pg/ > nix eval .#fooWrappers.flags                                                                                                         $(vi_mode_prompt_info)
{ "--greeting" = "hi (apply)hi (apply2)hi (initial)"; }

As you can see, the flags get concatenated. Is this intended?

Another thing I found is that grepping the wrapper scripts seems not to be robust enough:

For example, in this test:

  tripleApply = doubleApply.apply {
    flags."--greeting" = "triple";
    flags."--triple" = "level3";
  };
  # Check triple apply - greeting should be "triple" (newest wins)
  if ! grep -q "triple" "$tripleScript"; then
    echo "FAIL: triple apply should have 'triple' greeting"
    cat "$tripleScript"
    exit 1
  fi

grepping for 'triple' would succeed independent of whether the greeting flag is set to "triple" because the world "triple" also occurs in the flag "--triple".

Also the comment "newest wins" leads me to believe that the concatenation of flags is not intended. But it leaves the question to whether new values should win, or whether we need to control that via mkForce.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions