From 530a99ba8f072f888678b70bd9cebc8d776cfaa1 Mon Sep 17 00:00:00 2001 From: Krisztian Szabo Date: Sat, 19 Nov 2022 13:53:46 +0100 Subject: [PATCH 1/5] use mkNakedShell from devshell for less noisy shell Fixes #79 --- src/modules/mkNakedShell.nix | 94 ++++++++++++++++++++++++++++++++++++ src/modules/top-level.nix | 76 ++++++++++++++++------------- 2 files changed, 136 insertions(+), 34 deletions(-) create mode 100644 src/modules/mkNakedShell.nix diff --git a/src/modules/mkNakedShell.nix b/src/modules/mkNakedShell.nix new file mode 100644 index 000000000..ed0e14df7 --- /dev/null +++ b/src/modules/mkNakedShell.nix @@ -0,0 +1,94 @@ +# copied from https://github.com/amarshall/devshell/blob/917a891e98ca26c006a6be1fdb3335a74720c237/nix/mkNakedShell.nix#L1 +# to support inputDerivation for easy CI caching as well: https://github.com/numtide/devshell/pull/226 +{ bashInteractive +, coreutils +, system +, writeTextFile +}: +let + bashPath = "${bashInteractive}/bin/bash"; + stdenv = writeTextFile { + name = "naked-stdenv"; + destination = "/setup"; + text = '' + # Fix for `nix develop` + : ''${outputs:=out} + + runHook() { + eval "$shellHook" + unset runHook + } + ''; + }; +in +{ name +, # A path to a buildEnv that will be loaded by the shell. + # We assume that the buildEnv contains an ./env.bash script. + profile +, shellHook ? "" +, meta ? { } +, passthru ? { } +}: +let + derivationArg = { + inherit name system; + + # `nix develop` actually checks and uses builder. And it must be bash. + builder = bashPath; + + # Bring in the dependencies on `nix-build` + args = [ "-ec" "${coreutils}/bin/ln -s ${profile} $out; exit 0" ]; + + # $stdenv/setup is loaded by nix-shell during startup. + # https://github.com/nixos/nix/blob/377345e26f1ac4bbc87bb21debcc52a1d03230aa/src/nix-build/nix-build.cc#L429-L432 + stdenv = stdenv; + + # The shellHook is loaded directly by `nix develop`. But nix-shell + # requires that other trampoline. + shellHook = '' + # Remove all the unnecessary noise that is set by the build env + unset NIX_BUILD_TOP NIX_BUILD_CORES NIX_STORE + unset TEMP TEMPDIR TMP TMPDIR + # $name variable is preserved to keep it compatible with pure shell https://github.com/sindresorhus/pure/blob/47c0c881f0e7cfdb5eaccd335f52ad17b897c060/pure.zsh#L235 + unset builder out shellHook stdenv system + # Flakes stuff + unset dontAddDisableDepTrack outputs + + # For `nix develop`. We get /noshell on Linux and /sbin/nologin on macOS. + if [[ "$SHELL" == "/noshell" || "$SHELL" == "/sbin/nologin" ]]; then + export SHELL=${bashPath} + fi + + # https://github.com/numtide/devshell/issues/158 + PATH=''${PATH#/path-not-set:} + + export PATH="${profile}/bin:$PATH" + + ${shellHook} + ''; + }; +in +(derivation derivationArg) // { + inherit meta passthru; + + # https://github.com/NixOS/nixpkgs/blob/41f7e338216fd7f5e57817c4f8e148d42fb88b24/pkgs/stdenv/generic/make-derivation.nix#L486-L504 + inputDerivation = derivation (derivationArg // { + # Add a name in case the original drv didn't have one + name = derivationArg.name or "inputDerivation"; + # This always only has one output + outputs = [ "out" ]; + + # Propagate the original builder and arguments, since we override + # them and they might contain references to build inputs + _derivation_original_builder = derivationArg.builder; + _derivation_original_args = derivationArg.args; + + builder = bashPath; + # The bash builtin `export` dumps all current environment variables, + # which is where all build input references end up (e.g. $PATH for + # binaries). By writing this to $out, Nix can find and register + # them as runtime dependencies (since Nix greps for store paths + # through $out to find them) + args = [ "-c" "export > $out" ]; + }); +} // passthru diff --git a/src/modules/top-level.nix b/src/modules/top-level.nix index 52beac8b3..ef25929bf 100644 --- a/src/modules/top-level.nix +++ b/src/modules/top-level.nix @@ -71,39 +71,47 @@ in ./update-check.nix ] ++ map (name: ./. + "/languages/${name}") (builtins.attrNames (builtins.readDir ./languages)); - config = { - # TODO: figure out how to get relative path without impure mode - env.DEVENV_ROOT = builtins.getEnv "PWD"; - env.DEVENV_DOTFILE = config.env.DEVENV_ROOT + "/.devenv"; - env.DEVENV_STATE = config.env.DEVENV_DOTFILE + "/state"; - - procfile = pkgs.writeText "procfile" - (lib.concatStringsSep "\n" (lib.mapAttrsToList (name: process: "${name}: ${process.exec}") config.processes)); - - procfileEnv = pkgs.writeText "procfile-env" - (lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name}=${toString value}") config.env)); - - enterShell = '' - export PS1="(devenv) $PS1" + config = + let + mkNakedShell = pkgs.callPackage ./mkNakedShell.nix { }; + in + { + # TODO: figure out how to get relative path without impure mode + env.DEVENV_ROOT = builtins.getEnv "PWD"; + env.DEVENV_DOTFILE = config.env.DEVENV_ROOT + "/.devenv"; + env.DEVENV_STATE = config.env.DEVENV_DOTFILE + "/state"; + + procfile = pkgs.writeText "procfile" + (lib.concatStringsSep "\n" (lib.mapAttrsToList (name: process: "${name}: ${process.exec}") config.processes)); + + procfileEnv = pkgs.writeText "procfile-env" + (lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name}=${toString value}") config.env)); + + enterShell = '' + export PS1="(devenv) $PS1" - # note what environments are active, but make sure we don't repeat them - if [[ ! "$DIRENV_ACTIVE" =~ (^|:)"$PWD"(:|$) ]]; then - export DIRENV_ACTIVE="$PWD:$DIRENV_ACTIVE" - fi - - # devenv helper - if [ ! type -p direnv &>/dev/null && -f .envrc ]; then - echo "You have .envrc but direnv command is not installed." - echo "Please install direnv: https://direnv.net/docs/installation.html" - fi - ''; - - shell = pkgs.mkShell ({ - name = "devenv"; - packages = config.packages; - shellHook = config.enterShell; - } // config.env); - - ci = [ config.shell config.procfile ]; - }; + # note what environments are active, but make sure we don't repeat them + if [[ ! "$DIRENV_ACTIVE" =~ (^|:)"$PWD"(:|$) ]]; then + export DIRENV_ACTIVE="$PWD:$DIRENV_ACTIVE" + fi + + # devenv helper + if [ ! type -p direnv &>/dev/null && -f .envrc ]; then + echo "You have .envrc but direnv command is not installed." + echo "Please install direnv: https://direnv.net/docs/installation.html" + fi + ''; + + shell = mkNakedShell { + name = "devenv"; + profile = pkgs.buildEnv { + name = "devenv-profile"; + paths = config.packages; + }; + shellHook = config.enterShell; + passthru = config.env; + }; + + ci = [ config.shell config.procfile ]; + }; } From 357e54a05706e45dfb6e293656a7de7fac6d5d64 Mon Sep 17 00:00:00 2001 From: Krisztian Szabo Date: Mon, 21 Nov 2022 23:29:33 +0100 Subject: [PATCH 2/5] Fix missing env vars on shell activation ```bash $ direnv reload direnv: loading ~/projects/devenv/.envrc Building shell ... pre-commit-hooks.nix: hooks up to date To Install: virtualenv . bin/pip install -r requirements.txt direnv: export +DEVENV_DOTFILE +DEVENV_ROOT +DEVENV_STATE +IN_NIX_SHELL +name ~PATH ~XDG_DATA_DIRS ``` --- src/modules/mkNakedShell.nix | 7 +++++++ src/modules/top-level.nix | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modules/mkNakedShell.nix b/src/modules/mkNakedShell.nix index ed0e14df7..898f281ad 100644 --- a/src/modules/mkNakedShell.nix +++ b/src/modules/mkNakedShell.nix @@ -4,6 +4,7 @@ , coreutils , system , writeTextFile +, lib }: let bashPath = "${bashInteractive}/bin/bash"; @@ -25,11 +26,15 @@ in , # A path to a buildEnv that will be loaded by the shell. # We assume that the buildEnv contains an ./env.bash script. profile +, env ? { } , shellHook ? "" , meta ? { } , passthru ? { } }: let + # simpler version of https://github.com/numtide/devshell/blob/20d50fc6adf77fd8a652fc824c6e282d7737b85d/modules/env.nix#L41 + envToBash = name: value: "export ${name}=${lib.escapeShellArg (toString value)}"; + startupEnv = lib.concatStringsSep "\n" (lib.mapAttrsToList envToBash env); derivationArg = { inherit name system; @@ -64,6 +69,8 @@ let export PATH="${profile}/bin:$PATH" + ${startupEnv} + ${shellHook} ''; }; diff --git a/src/modules/top-level.nix b/src/modules/top-level.nix index ef25929bf..168a6cabe 100644 --- a/src/modules/top-level.nix +++ b/src/modules/top-level.nix @@ -103,13 +103,13 @@ in ''; shell = mkNakedShell { - name = "devenv"; + name = "devenv-shell"; + env = config.env; profile = pkgs.buildEnv { name = "devenv-profile"; paths = config.packages; }; shellHook = config.enterShell; - passthru = config.env; }; ci = [ config.shell config.procfile ]; From f564c66bbb21aabcd97363f04a98d2dfb4fa9e69 Mon Sep 17 00:00:00 2001 From: Krisztian Szabo Date: Tue, 22 Nov 2022 13:13:03 +0100 Subject: [PATCH 3/5] Use shell.inputDerivation in ci attribute --- src/modules/top-level.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/top-level.nix b/src/modules/top-level.nix index 168a6cabe..45a668029 100644 --- a/src/modules/top-level.nix +++ b/src/modules/top-level.nix @@ -112,6 +112,6 @@ in shellHook = config.enterShell; }; - ci = [ config.shell config.procfile ]; + ci = [ config.shell.inputDerivation config.procfile ]; }; } From 4e1145a13420de584d6ed073727c9e609dd1d8ab Mon Sep 17 00:00:00 2001 From: Krisztian Szabo Date: Sat, 26 Nov 2022 10:45:22 +0100 Subject: [PATCH 4/5] pre-commit run -a --- src/modules/top-level.nix | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/modules/top-level.nix b/src/modules/top-level.nix index bd5502be8..e9ffb0d27 100644 --- a/src/modules/top-level.nix +++ b/src/modules/top-level.nix @@ -66,18 +66,18 @@ in echo "You have .envrc but direnv command is not installed." echo "Please install direnv: https://direnv.net/docs/installation.html" fi - ''; + ''; - shell = mkNakedShell { - name = "devenv-shell"; - env = config.env; - profile = pkgs.buildEnv { - name = "devenv-profile"; - paths = config.packages; - }; - shellHook = config.enterShell; + shell = mkNakedShell { + name = "devenv-shell"; + env = config.env; + profile = pkgs.buildEnv { + name = "devenv-profile"; + paths = config.packages; }; - - ci = [ config.shell.inputDerivation config.procfile ]; + shellHook = config.enterShell; }; + + ci = [ config.shell.inputDerivation config.procfile ]; + }; } From 70403897df850b05a62b9f2e90c1cdd7b79246d0 Mon Sep 17 00:00:00 2001 From: Krisztian Szabo Date: Sat, 26 Nov 2022 10:51:21 +0100 Subject: [PATCH 5/5] Attribute numtide/devshell, fix buildEnv --- src/modules/mkNakedShell.nix | 26 ++++++++++++++++++++++++-- src/modules/top-level.nix | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/modules/mkNakedShell.nix b/src/modules/mkNakedShell.nix index 898f281ad..9af87250d 100644 --- a/src/modules/mkNakedShell.nix +++ b/src/modules/mkNakedShell.nix @@ -1,5 +1,27 @@ -# copied from https://github.com/amarshall/devshell/blob/917a891e98ca26c006a6be1fdb3335a74720c237/nix/mkNakedShell.nix#L1 -# to support inputDerivation for easy CI caching as well: https://github.com/numtide/devshell/pull/226 +# using copied code from https://github.com/numtide/devshell +# +# MIT License + +# Copyright (c) 2021 Numtide and contributors + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + { bashInteractive , coreutils , system diff --git a/src/modules/top-level.nix b/src/modules/top-level.nix index e9ffb0d27..d2f8df0d5 100644 --- a/src/modules/top-level.nix +++ b/src/modules/top-level.nix @@ -74,6 +74,7 @@ in profile = pkgs.buildEnv { name = "devenv-profile"; paths = config.packages; + ignoreCollisions = true; }; shellHook = config.enterShell; };