devshell icon indicating copy to clipboard operation
devshell copied to clipboard

devshell does not include `outputs` attrs

Open maxheld83 opened this issue 10 months ago • 1 comments

Describe the bug

A vanilla flake (or even with flake-parts) with devShells entry will yield, for example, a outputs.devShells.aarch64-darwin.default.outputs attribute set. A flake using on devshell does not have this attribute set.

To Reproduce

Steps to reproduce the behavior:

Vanilla/flake-parts flake:

{
  description = "Description for the project";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-parts = {
      inputs.nixpkgs-lib.follows = "nixpkgs";
      url = "github:hercules-ci/flake-parts";
    };
  };

  outputs =
    inputs@{ flake-parts, ... }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      systems = [
        "aarch64-darwin"
      ];
      perSystem =
        { 
          pkgs,
          ...
        }:
        {
          devShells.default = pkgs.mkShell {
            packages = [
              pkgs.git
            ];
          };
        };
    };
}

in nix repl, this yields:

nix-repl> outputs.devShells.aarch64-darwin.default.outputs
[ "out" ]

Using a minimal devshell flake:

{
  description = "Description for the project";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-parts = {
      inputs.nixpkgs-lib.follows = "nixpkgs";
      url = "github:hercules-ci/flake-parts";
    };
    devshell.url = "github:numtide/devshell";
  };

  outputs =
    inputs@{ flake-parts, ... }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      imports = [ inputs.devshell.flakeModule ];
      systems = [
        "aarch64-darwin"
      ];
      perSystem =
        { 
          pkgs,
          ...
        }:
        {
          devshells.default = {
            packages = [
              pkgs.git
            ];
          };
        };
    };
}

nix repl yields:

nix-repl> outputs.devShells.aarch64-darwin.default.outputs
warning: Nix search path entry '/Users/max/.nix-defexpr/channels' does not exist, ignoring
error: attribute 'outputs' missing
       at «string»:1:1:
            1| outputs.devShells.aarch64-darwin.default.outputs
             | ^

Expected behavior

I am interpreting derivation {}'s docs to imply that while it's outputs argument is optional, because it has a default "out", all flake outputs can be expected to have a *.default.outputs attribute set.

System information

This is macOS 15.3.1, nix (Nix) 2.26.2.

Additional context

Other software such as flake-iter also seems to assume the *.default.outputs attribute; that's where I ran into this problem, see https://github.com/DeterminateSystems/flake-iter/issues/22

maxheld83 avatar Mar 14 '25 08:03 maxheld83

hey You've correctly identified a difference in the structure of the devShell attribute set when using pkgs.mkShell directly (via vanilla flakes or flake-parts without devshell) versus using the numtide/devshell flake module.

Explanation of the Difference

  1. Vanilla pkgs.mkShell / flake-parts:

    • When you define devShells.default = pkgs.mkShell { ... };, you are directly assigning the result of pkgs.mkShell to the default attribute within your flake's outputs (outputs.devShells.<system>.default).
    • pkgs.mkShell fundamentally creates a Nix derivation.
    • According to Nix documentation and standard behavior, derivations have certain standard attributes. One of these is outputs, which is a list of strings representing the names of the derivation's outputs.
    • By default, most derivations (including those from mkShell) have a single output named "out".
    • Therefore, the derivation created by pkgs.mkShell has an attribute outputs with the value [ "out" ]. This is what you observe in your first example.
  2. numtide/devshell Flake Module:

    • When you use imports = [ inputs.devshell.flakeModule ]; and define perSystem.devshells.default = { ... }; (note the plural devshells used by the module), you are not directly calling pkgs.mkShell yourself in the final output structure.
    • Instead, the devshell.flakeModule takes your configuration (under perSystem.devshells) and uses its own internal logic (likely involving devshell.lib.mkShell or similar functions which eventually call pkgs.mkShell or pkgs.stdenv.mkDerivation) to construct the final attributes under outputs.devShells.<system>.
    • The numtide/devshell library adds its own features and abstractions (like commands, environment variables management, hooks, etc.). The final attribute set it places at outputs.devShells.<system>.default is not necessarily the raw derivation itself. It's a structure defined by numtide/devshell which contains the information needed for nix develop or nix shell to work correctly with the devshell features.
    • Because the structure at outputs.devShells.<system>.default isn't the raw derivation, it doesn't necessarily expose the derivation's standard outputs attribute directly at that level. The actual derivation might be nested within this structure, or devshell might construct it in a way that omits or places the .outputs attribute elsewhere (or nowhere accessible at that top level).

Addressing Your Expectation

Your interpretation of the derivation {} documentation is correct for standard Nix derivations. However, flake outputs can contain any valid Nix attribute set, not just raw derivations.

The numtide/devshell library provides an abstraction over pkgs.mkShell. The structure it outputs under outputs.devShells.<system>.default is specific to how devshell works and is designed to be consumed by nix develop in conjunction with devshell's tooling. It doesn't guarantee to expose all the raw attributes of the underlying derivation (like .outputs) at that specific path.

Conclusion

This isn't strictly a "bug" in numtide/devshell in the sense of it malfunctioning, but rather a consequence of the abstraction it provides. It constructs the devShell output differently than a direct pkgs.mkShell call.

  • Vanilla/flake-parts: outputs.devShells.<system>.default is the derivation -> has .outputs attribute.
  • numtide/devshell: outputs.devShells.<system>.default is a structure generated by the devshell module (which internally uses a derivation) -> does not necessarily expose the underlying derivation's .outputs attribute at this top level.

The incompatibility you observed with flake-iter arises because flake-iter assumes the common pattern where devShells.<system>.default is a derivation and thus expects the .outputs attribute, which isn't true when using the numtide/devshell abstraction.

Pavan3861 avatar Apr 29 '25 17:04 Pavan3861