It all started when I made a small refactor of one our files which I've simplified this to smaller reproduction:
# ./release.nix
{ werror ? true }@args: import ./default.nix args
# ./default.nix
{ werror ? false}: …
… where werror is a flag that controls whether or not we build with -Werror.
The refactor I made was to change this:
# ./release.nix
{ werror ? true }@args: import ./default.nix args
… to something like this:
{ werror ? true }: import ./default.nix { inherit werror; }
… which I would soon learn was NOT the same thing.
To understand why, consider this sample nix repl session:
nix-repl> defaultFile = { werror ? false }: werror
nix-repl> releaseFile = { werror ? true }@args: defaultFile args
nix-repl> releaseFile { }
false
Most people (myself included) would expect the final result to be true, but Nix disagrees! This section of the manual explains how this cursed behavior was apparently working as designed:
Warning
The
args@expression is bound to the argument passed to the function which means that attributes with defaults that aren't explicitly specified in the function call won't cause an evaluation error, but won't exist inargs.For instance
let function = args@{ a ? 23, ... }: args; in function {}will evaluate to an empty attribute set.
Yikes!
So it turned out that our production build was actually building release.nix with -Werror OFF (because it would not explicitly specify werror as an argument, which led it to trigger the above cursed behavior).