Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

substituteAll is a terrible function and we need to stop using it / fix it respectively #237216

Open
roberth opened this issue Jun 11, 2023 · 12 comments

Comments

@roberth
Copy link
Member

roberth commented Jun 11, 2023

Issue description

substituteAll is a bash function and a Nix function in pkgs.

The bash function is bad because

  • it takes variables from the environment, which is too easily affected, and often the definition of those values is through mkDerivation arguments, in which case it is not clear that any relation exists between the definition and the usage.
  • the variables tend to be hard to grep. For one, the @ signs are omitted anywhere but in the file
    • this is a --subst-var complaint as well

The Nix function is even worse. Nix has semi-structured data, so the expectations are a bit higher. Variables passed to the pkgs.substituteAll functions all get substituted into the file, right? ... WRONG! Those variables are poured carelessly into mkDerivation, which assigns special meaning to them, and even ignores and replaces some, like system.

Steps to reproduce

Technical details

substituteAll would be useful if it wasn't such a hack. Maybe something could be achieved with structuredAttrs, so that the variables aren't taken from the environment, but from a separate, safe, json file.

There's also this. Not sure if I want to know more about the horrors inside.

@roberth roberth added the 0.kind: bug Something is broken label Jun 11, 2023
@roberth roberth changed the title substituteAll is a terrible function and we need to stop using it substituteAll is a terrible function and we need to stop using it / fix it respectively Jun 11, 2023
roberth added a commit that referenced this issue Jun 12, 2023
While the intent of the code was correct, the system string can not be used
in the substituteAll function.

See #237216
github-actions bot pushed a commit that referenced this issue Jun 12, 2023
While the intent of the code was correct, the system string can not be used
in the substituteAll function.

See #237216

(cherry picked from commit 1350e52)
@figsoda figsoda mentioned this issue Jun 12, 2023
12 tasks
@sternenseemann
Copy link
Member

Maybe something could be achieved with structuredAttrs, so that the variables aren't taken from the environment, but from a separate, safe, json file.

Much simpler than structured attrs is probably just prefixing the env vars or dumping the argument to substituteAll into a file via builtins.toJSON and passAsFile.

raphaelr added a commit to raphaelr/nixpkgs that referenced this issue Jun 19, 2023
The `substituteAll` bash function that was previously used substitutes
all environment variables. This can cause surprising behaviour, because
some environment variables, like `system`, and not under control of the
expression author that calls `substituteAll`.

This commit changes the nixpkgs `substituteAll` function to use
`substitute` with an explicit variable list instead. This ensures that
the only variables that will be substituted are those that the caller
specified, and not any that stdenv.mkDerivation or Nix itself
added/modified.

Partially addresses NixOS#237216.
@AndersonTorres
Copy link
Member

This should be still a Bash program?

@roberth
Copy link
Member Author

roberth commented Aug 12, 2023

This should be still a Bash program?

I think it's pure bash because that makes it easy to use as part of bootstrapping.
As for the pkgs.substituteAll derivation function, I don't think it has the same requirement, but we might as well reuse what we have.

Maybe something could be achieved with structuredAttrs

prefixing the env vars or dumping the argument to substituteAll into a file via builtins.toJSON and passAsFile.

Or we could use command line arguments. I don't think the limitations of the process env+args memory space apply here because it's pure bash without any execv. Testing needed I guess.

@illode
Copy link

illode commented Nov 8, 2023

Also not immediately obvious is the fact that the pkgs.substituteAll nix function silently skips any variables with hyphens in them, e.g. @serial-number@.

When the bash substituteAll function is called manually, bash stops the user from creating an environment variable with a hyphen since they aren't allowed. I assume that that error gets lost in the build log when using pkgs.substituteAll.

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/string-interpolation-in-files-without-substituteall/38779/1

@roberth
Copy link
Member Author

roberth commented Feb 28, 2024

pkgs.substitute seems like a decent replacement for pkgs.substituteAll, especially after #287364

For generating build scripts, we could have a function that produces a substitute command invocation instead of a derivation.

At that point I think we have all the conveniences and all that remains is remove usages of the bad flags, and perhaps deprecate them.

@roberth
Copy link
Member Author

roberth commented Apr 9, 2024

Elephant in the room:
This isn't Nix substitution, and that's really bad for what's supposed to be a coherent ecosystem.
I think we should change Nixpkgs substitute to replace whenever we have the opportunity.

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/shellcheck-inline-scripts-during-test-phase/16274/7

@infinisil

This comment was marked as duplicate.

@Guanran928
Copy link
Contributor

Guanran928 commented Jun 16, 2024

Any updates? Hit this today while trying to replace something containing a slash. (example: @foo/bar@)

Maybe we can replace substituteAll with something like this?

substituteV2 = {src, ...} @ args: let
  args' = builtins.removeAttrs args ["src"];
in
  pkgs.substitute {
    inherit src;
    substitutions = lib.flatten (lib.mapAttrsToList (n: v: ["--subst-var-by" n v]) args');
  };

@philiptaron
Copy link
Contributor

Now that the PR above has landed, my plan is to send out a bunch of PRs for simple uses of substituteAll throughout the tree.

@philiptaron
Copy link
Contributor

@wolfgangwalther has taken up the torch and is driving the refactor tractor around Nixpkgs, delivering a set of wonderful gifts.

wolfgangwalther added a commit to wolfgangwalther/nixpkgs that referenced this issue Dec 15, 2024
wolfgangwalther added a commit to wolfgangwalther/nixpkgs that referenced this issue Dec 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants