-
Notifications
You must be signed in to change notification settings - Fork 84
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
Enable remote nix builds #239
Conversation
Co-authored-by: Guillaume Maudoux <[email protected]>
72c177a
to
8fd2975
Compare
I would love feedback on this one. And maybe a test that uses nixbuild.net to ensure that it is at least usable. As for the things that do not work, well
I guess the question is: is is okay to have this as an experimental feature ? How could we mark it as such ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to suggest an shift in approach, which would take into consideration the hermeticity and configurability of the solution - I strongly feel, that rules_nixpkgs should be a ‘poster child’ for such approach and should shy away from hacks relying on host configuration / global environments etc.
Perhaps nixpkgs_package should contain two additional attributes:
Use_remote_nix_builder - boolean, which indicates if remote nix builder is to be used
Remote_nix_builder_config - a dictionary, containing all the configuration needed for the builder to properly run
In that way, we retain the composability of the solution, we do not break hermeticy and are not relying on global system state by default.
# are there issues with this? | ||
# another idea is to have config_settings, and have attributes for this in nixpkgs_package | ||
# but that seems clunkier (have to modify rules, introduce some non-building related logic to rules) | ||
ENV_NIX_REMOTE = 'RULES_NIXPKGS_NIX_REMOTE' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is de-facto a hidden, implict flow control.
Explicit is nearly always better than implicit, especially when dealing with Bazel rules which should clearly define it’s input and output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you use an env-var, then make sure to add it to the environ
parameter of repository_rule
so that Bazel knows when to invalidate and re-run the rule.
This is particularly relevant for this use-case, where failing to re-run the rule after enabling Nix-remote might cause missing store paths on the remote end.
Whether to use an env-var or parameters - we can consider the desired use-cases to see which is better suited.
Parameters provide explicit, checked-in configuration, that will be consistent for all contributors. But, they are difficult to change temporarily, e.g. through a command-line flag such as --config
.
Parameters provide fine-grained, per repository rule control. But, they are difficult to set or override globally. E.g. if I need to work offline on transit and want to disable remote builds temporarily.
My understanding is that remote build configuration is something that should be consistent across all instances of nixpkgs_package
, to avoid missing remote Nix store paths on individual packages where the parameter was forgotten (e.g. a transitive dependency). And that it is something that we may want to enable or disable via command-line flags or similar configuration, e.g. for temporary offline builds, or different developer and CI configuration.
Viewed that way, env-vars seem closer to what we want.
output_path = exec_result.stdout.splitlines()[-1] | ||
|
||
# TODO[GL]: use nix provided ssh? | ||
ssh_path = repository_ctx.which("ssh") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps path to ssh should come from configuration attribute and by default be taken from nix?
Or maybe we can drop ssh'ing all together? Let remote builder worry about caching (or lack of there of)
output_path = exec_result.stdout.splitlines()[-1] | ||
|
||
# TODO[GL]: use nix provided ssh? | ||
ssh_path = repository_ctx.which("ssh") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be capability to control certificates / root certificate / ssh config which is used for this operations (otherwise we are 'escaping to host', breaking hermetcity.
repository_ctx.report_progress("Remote-building Nix derivation") | ||
exec_result = execute_or_fail( | ||
repository_ctx, | ||
[nix_build_path, "--store", nix_store] + expr_args, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imho it makes sense to add --eval-store auto
.
(...) which instructs Nix to use a specific Nix store during the evaluation phase (before starting the build). It should be possible to set the evaluation phase to a remote store too, but in practice it makes most sense to always set it to auto.
nixbuild.net documentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely, figured out this one out afterwards :-).
repository_ctx.attr.attribute_path, | ||
), | ||
quiet = repository_ctx.attr.quiet, | ||
timeout = 10000, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're using the same timeout value in three places across this PR. I'd suggest either declaring a new variable or re-using the one which is already specified within this file.
@AleksanderGondek What about a configurable nix toolchain ? Or something simmilar adapted to external repositories ? Ultimately this nix build sequence of actions should move to a proper wrapper script instead of this starlark spaghetti. The current design tries to mimic the remote cache and execution setup, where the remote endpoints are defined in .bazelrc. With env vars, you can also set the remote nix endpoint in .bazelrc, which is pretty nice. Having to specify it in each and every nixpkgs_package seems pretty bad to me. I know you can wrap the repo rule, but the wrapper will not be used everywhere. I think that global state is excatly what we want. Every call, nested inside nix_go setup, nix_python setup or deep inside dependencies of dependencies should inherit the workspace defaults. |
@layus I think that nix toolchain is a solution long-due :) I really like the idea! |
But we cannot use toolchains in repository_rules, right ? Oh dear, mind is stuck on Friday. |
# are there issues with this? | ||
# another idea is to have config_settings, and have attributes for this in nixpkgs_package | ||
# but that seems clunkier (have to modify rules, introduce some non-building related logic to rules) | ||
ENV_NIX_REMOTE = 'RULES_NIXPKGS_NIX_REMOTE' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you use an env-var, then make sure to add it to the environ
parameter of repository_rule
so that Bazel knows when to invalidate and re-run the rule.
This is particularly relevant for this use-case, where failing to re-run the rule after enabling Nix-remote might cause missing store paths on the remote end.
Whether to use an env-var or parameters - we can consider the desired use-cases to see which is better suited.
Parameters provide explicit, checked-in configuration, that will be consistent for all contributors. But, they are difficult to change temporarily, e.g. through a command-line flag such as --config
.
Parameters provide fine-grained, per repository rule control. But, they are difficult to set or override globally. E.g. if I need to work offline on transit and want to disable remote builds temporarily.
My understanding is that remote build configuration is something that should be consistent across all instances of nixpkgs_package
, to avoid missing remote Nix store paths on individual packages where the parameter was forgotten (e.g. a transitive dependency). And that it is something that we may want to enable or disable via command-line flags or similar configuration, e.g. for temporary offline builds, or different developer and CI configuration.
Viewed that way, env-vars seem closer to what we want.
timeout = 10000, | ||
) | ||
|
||
nix_path = repository_ctx.which("nix") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which
could return None
, which should be handled to provide a reasonable error message. I think the lookup for nix-build
already has some relevant logic around it.
Indeed, this is a case where a parameter would serve better than an env-var.
Indeed, there are no toolchains in repository rules. That said, one can emulate something like it, at least to a certain extent. |
First of all - thank you for taking time to creating the change and taking time to listen to feedback. My comments are coming from the place of deep admiration and care for Secondly - I am afraid I quite cannot grasp the reason as to why the environment variables are considered adequate in the context of this change. Allow me to expand upon my concerns / point of view (and if I am missing out on some information / plainly wrong - please, just say so):
Bazel - in principle and design - wants to describe everything a project needs to build itself. This have multiple advantages (and disadvantages ;) ), one of which is - onboarding new developers / CI executors / general ‘consumers’ of build process is made significantly easier and less error prone, as you have explicitly stated requirements in the WORKSPACE. Now to the part, where I start to complain about the usage of environment variables for configuration of remote nix-build commands (or lack of them): it will introduce a significant surface for misconfiguration and lack of synchronization between users of the Bazel project. At the moment, as long as you have nix installed, you can have your I also think, that the very same argument that I have seen raised in proposition of using environment variables, could be made if we were implementing what is currently a Then there is also the matter of further configurability, extendability and sensible feedback to user: usage of environment variables make those unwiedly in very short span of features to implement or use-cases to cover. Third - to give a constructive suggestion: Perhaps a better alternative to using raw environment variables, would be to:
Therefore we would have:
Please let me know your thoughts - I would be trilled to prepare a PoC / do some pair-coding if that would help! |
Chiming in as a newbie, I agree with the feelings and assessments that @AleksanderGondek has. I think it's also important to be able to switch remote builds on/off for everything at once for basically the reasons @aherrmann outlined. The solution proposed by Aleksander seems like it's almost there - I can try to implement it. One thing that would be nice but I'm not sure how to do with that approach is to be able to configure through the CLI (or more commonly in |
I consider the question of env-var or not an implementation detail. What I consider important is the following:
That said, at least for the temporary switch, an env-var paired with
Unfortunately, Bazel's configuration features, like Env-vars are a way to achieve command-line flag configuration with repository rules. Notably, these env-vars don't have to be set manually by the user in their environment. Instead, one can e.g. set them in
To be used as
in the Daml example.
I don't agree with that view - I view the two as different kinds of configurations. The nixpkgs revision has meaning w.r.t. what to build and import, i.e. which version of which tool or library. If another contributor builds their project with a different nixpkgs revision, then I can't expect them to get the same results as I do. But, the remote configuration has meaning w.r.t. how to build it, i.e. it's operational. In that regard it's similar to a remote cache configuration. In a properly configured, hermetic project I expect to get the same results no matter if I use remote cache or not, it'll just take longer without. Remote execution has a similar quality to it. Of course, it's a bit more complicated, as it can also provide access to more platforms. But, at least in the case of same platform builds, it should behave the same. |
Closing as there have been no changes for over a year now. See Support remote execution with rules_nixpkgs for the latest on this topic. |
Hey @benradf, thanks! Is that link intended to be public, or is it intended entirely for tweagers? Currently it leads to a private Tweag repo, so it's a bit confusing for people who don't have access to it. |
Hey @googleson78, good to hear from you again! Thanks for letting me know the link is private - my mistake. I've changed it to link to the public issue in this repo. |
@layus said there are some new issues with this.
Also need to resolve all the TODOs and add tests.