devenv icon indicating copy to clipboard operation
devenv copied to clipboard

[ENHANCEMENT] Multiple devenv(-up) profiles/variants

Open clotodex opened this issue 1 year ago • 4 comments

Concrete use case: I have a devenv.nix in the root of our repo to run everything together. Now depending on different development focus, we would love to either pass variables for devenv to evaluate (think --no-frontend) or to have multiple devenv profiles where we could e.g. say devenv up --backend-only. Similar to a flake's `nix develop '.#profile'.

I know that this should be possible with flakes, but since the rest of the team is not "nix-native" and we really enjoy the abstraction devenv brings, this would be a killer feature.

What would be necessary to build this up?

clotodex avatar Sep 06 '24 22:09 clotodex

We'd need to use something like https://github.com/shakacode/steward to run our processes and then expose interface to start only specific ones

domenkozar avatar Sep 10 '24 16:09 domenkozar

Alternatively, add devenv.<name>.{packages,services,tasks,...} and allow composing setups through the module system and imports.

For example:

devenv.default = {
  imports = [
    config.outputs.frontendModule
    config.outputs.backendModule
 ];
};

devenv.frontend = {
  imports = [ config.outputs.frontendModule ];
};

devenv.backend = {
  imports = [ config.outputs.backendModule ];
};

outputs.frontendModule = {
  services.nginx.enable = true;
  # ...
};

outputs.backendModule = {
  services.postgres.enable = true;
  # ...
};

szlend avatar Oct 15 '24 18:10 szlend

While I think it would be nice to have profiles, as they would be an official solution to this problem and enter the documentation, there's a workaround via environment variables.

In devenv.nix use

  scripts = {
    only.exec = ''
      echo "checking if ENV is in: $*"
      for arg in "$@"; do
        if [ "$arg" == "$ENV" ]; then
          echo "ENV ($ENV) matched"
          exit 1
        fi
      done
      echo "ENV not matched, exiting"
      exit 0
    '';
  };

  processes = {
    start-frontend.exec = ''only frontend && exit ; echo "running frontend" '';
    start-backend.exec = ''only backend && exit ; echo "running backend" '';
    start-common.exec = ''only backend frontend && exit ; echo "running common"'';
  };

then start it with

ENV=backend devenv up

the script will try to match ENV with each parameter string and, if none matches, the && exit will run.

I hope this helps someone; maybe this could be added somewhere in the docs.

EDIT: I guess this could be made nicer by wrapping it in some nix expression that makes it nicer to look at, like

processes.start-frontend.exec = only [ "frontend" ] ''echo starting frontend''; 

and I think it could be even refactored to move it up higher

processes = only {
  "frontend" = {
    start-frontend = "echo starting frontend";
   };
};

or something like this.

akiross avatar Jan 30 '25 22:01 akiross

Well, I ended up making a specific tool for this: https://codeberg.org/akiross/run-only

It's used like this:

{ pkgs, inputs, ... }:
{
  processes = inputs.run-only.on-env pkgs {
    # Plain definition, always executed
    always.exec = "...";

    # Common for backend and frontend
    common = { exec = "..."; profiles = [ "backend" "frontend" ]; };

    # Only backend
    backend = { exec = "..."; profiles = [ "backend" ]; };

    # Both stats and backend profiles
    stats = { exec = "..."; profiles = [ "stats" "backend" ]; };

    # Only backend
    frontend = { exec = "..."; profiles = [ "frontend" ]; };
  };
}

and then

ENV=backend devenv up

I hope it helps!

akiross avatar Feb 02 '25 11:02 akiross

I've written up a guide that allows this (finally!): https://github.com/cachix/devenv/pull/1841

Does that fit into what you need?

domenkozar avatar Apr 22 '25 15:04 domenkozar

Exactly what I want - this is perfect! Thank you.

A follow up question though - how does this work with other devenv.nix files? I think this should also be possible since everything gets evaluated together. That would mean all options get imported as well - what if i have options with the same name imported from different files? Does nix throw an error?

Scenario: Dev tools in path monorepo/frontend

  • I want the dev tools in the frontend environment
  • but when frontend is imported as part of the main app, I dont want them
  • have an option "devtools" in the specific devenv.nix in frontend (maybe also in backend, etc) I guess one could:
  • set this option as default with higher prio to false in the main devenv.nix
  • and then create the cascading options, like --release or --frontend-only which affect different parts

Overall - excited to start playing around with 1.6 🎉

clotodex avatar Apr 23 '25 07:04 clotodex

A follow up question though - how does this work with other devenv.nix files?

It works, you can now set profile option in any devenv file you're importing!

That would mean all options get imported as well - what if i have options with the same name imported from different files? Does nix throw an error?

Yes, that's a conflict option error.

Scenario: Dev tools in path monorepo/frontend

You can add options like myproject.frontend.enable and myproject.backend.enable, etc... If you want more granuality.

domenkozar avatar Apr 23 '25 09:04 domenkozar