From 38eab934879e0b49af3594d50470bdba721d9371 Mon Sep 17 00:00:00 2001 From: Sander Date: Tue, 24 Dec 2024 12:34:46 +0400 Subject: [PATCH 1/9] direnv: fix double reload on dependency changes --- direnvrc | 91 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 16 deletions(-) diff --git a/direnvrc b/direnvrc index cb47ef057..64025d8ba 100644 --- a/direnvrc +++ b/direnvrc @@ -78,7 +78,10 @@ _nix_import_env() { _nix_argsum_suffix() { local out checksum content - content=$(cat "$@" 2>/dev/null) + args=("$@") + sorted_args=$(printf "%s\n" "${args[@]}" | sort -u) + + content=$(cat "${sorted_args[@]}" 2>/dev/null) if has sha1sum; then out=$(sha1sum <<< "$content") @@ -94,8 +97,52 @@ _nix_argsum_suffix() { } nix_direnv_watch_file() { + log_error "nix_direnv_watch_file is deprecated. Use watch_file instead." watch_file "$@" - nix_watches+=("$@") +} + +_nix_direnv_watches() { + local -n _watches=$1 + if [[ -z ${DIRENV_WATCHES-} ]]; then + return + fi + while IFS= read -r line; do + local regex='"[Pp]ath": "(.+)"$' + if [[ $line =~ $regex ]]; then + local path="${BASH_REMATCH[1]}" + if [[ $path == "${XDG_DATA_HOME:-${HOME:-/var/empty}/.local/share}/direnv/allow/"* ]]; then + continue + fi + # expand new lines and other json escapes + # shellcheck disable=2059 + path=$(printf "$path") + _watches+=("$path") + fi + done < <($direnv show_dump "${DIRENV_WATCHES}") +} + +_devenv_watches() { + local -n path=$1 + local -n watches=$2 + if [[ -f $path ]]; then + for file in $(cat "$path"); do + watches+=("$file") + done + fi +} + +_devenv_profile_rc() { + local -n _watches=$1 + local layout_dir profile + layout_dir=$(direnv_layout_dir) + profile="${layout_dir}/devenv-profile$(_nix_argsum_suffix "${_watches[@]}")" + echo "${profile}.rc" +} + +_devenv_test() { + local arg=$1 + + echo "$@" } use_devenv() { @@ -103,35 +150,38 @@ use_devenv() { flake_expr="${1:-.}" flake_dir="${flake_expr%#*}" + env_deps_path="$flake_dir/.devenv/input-paths.txt" - local files_to_watch - files_to_watch=(".envrc" "$HOME/.direnvrc" "$HOME/.config/direnv/direnvrc") + local default_watches + default_watches=(".envrc" "$HOME/.direnvrc" "$HOME/.config/direnv/direnvrc") if [[ -d "$flake_dir" ]]; then - files_to_watch+=("$flake_dir/devenv.nix" "$flake_dir/devenv.lock" "$flake_dir/devenv.yaml" "$flake_dir/devenv.local.nix") + default_watches+=("$flake_dir/devenv.nix" "$flake_dir/devenv.lock" "$flake_dir/devenv.yaml" "$flake_dir/devenv.local.nix") + if [[ -f "$flake_dir/devenv.yaml" ]]; then if ! devenv assemble; then log_error "$(devenv version) failed to parse devenv.yaml, make sure to use version 0.6 or newer and fix the errors above." exit 0 fi - - if [[ -f "$flake_dir/.devenv/input-paths.txt" ]]; then - for file in $(cat "$flake_dir/.devenv/input-paths.txt"); do - files_to_watch+=("$file") - done - fi fi fi - nix_direnv_watch_file "${files_to_watch[@]}" + # Watch the default files + watch_file "${default_watches[@]}" - local layout_dir profile_rc - layout_dir=$(direnv_layout_dir) - profile_rc="${layout_dir}/devenv-profile$(_nix_argsum_suffix "${files_to_watch[@]}").rc" + # Fetch and watch files that affect the env + local env_watches + _devenv_watches env_deps_path env_watches + watch_file "${env_watches[@]}" + + local watches + _nix_direnv_watches watches + + profile_rc=$(_devenv_profile_rc watches) local need_update=0 local file= - for file in "${nix_watches[@]}"; do + for file in "${watches[@]}"; do if [[ "$file" -nt "$profile_rc" ]]; then need_update=1 log_status "$file changed, reloading" @@ -148,6 +198,15 @@ use_devenv() { exit 0 fi + local env_watches + _devenv_watches env_deps_path env_watches + watch_file "${env_watches[@]}" + + # Fetch the final watches and compute the new profile_rc + local watches + _nix_direnv_watches watches + profile_rc=$(_devenv_profile_rc watches) + echo "$env" > "$profile_rc" log_status "updated devenv shell cache" _nix_import_env "$profile_rc" From 6d9683d5b7d41b8894c67104c31911d5571677a0 Mon Sep 17 00:00:00 2001 From: Sander Date: Tue, 24 Dec 2024 18:59:08 +0400 Subject: [PATCH 2/9] devenv: add a command to print out direnvrc --- .envrc | 2 +- devenv/init/.envrc | 2 +- devenv/src/cli.rs | 6 +++++- devenv/src/main.rs | 9 +++++++-- package.nix | 1 + 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.envrc b/.envrc index cf905f7c4..4c8eecbd1 100755 --- a/.envrc +++ b/.envrc @@ -7,7 +7,7 @@ set -euo pipefail nix build --print-out-paths --accept-flake-config || echo "nix build failed, using previous build" PATH_add "result/bin" -# External users should use `source_url` to load this file +# External users should eval `devenv direnvrc` or use `source_url` to load this file source_env ./direnvrc use devenv diff --git a/devenv/init/.envrc b/devenv/init/.envrc index 49a985a61..30da14fdc 100644 --- a/devenv/init/.envrc +++ b/devenv/init/.envrc @@ -1,5 +1,5 @@ export DIRENV_WARN_TIMEOUT=20s -source_url "https://raw.githubusercontent.com/cachix/devenv/82c0147677e510b247d8b9165c54f73d32dfd899/direnvrc" "sha256-7u4iDd1nZpxL4tCzmPG0dQgC5V+/44Ba+tHkPob1v2k=" +eval "$(devenv direnvrc)" use devenv diff --git a/devenv/src/cli.rs b/devenv/src/cli.rs index a9e1b5ffa..5773485b8 100644 --- a/devenv/src/cli.rs +++ b/devenv/src/cli.rs @@ -274,8 +274,12 @@ pub enum Commands { attributes: Vec, }, + #[command(about = "Generate and print out a direnvrc.")] + #[clap(hide = true)] + Direnvrc, + #[command(about = "Print the version of devenv.")] - Version {}, + Version, #[clap(hide = true)] Assemble, diff --git a/devenv/src/main.rs b/devenv/src/main.rs index 5746e7a69..2cc1e6841 100644 --- a/devenv/src/main.rs +++ b/devenv/src/main.rs @@ -21,7 +21,11 @@ async fn main() -> Result<()> { let command = match cli.command { None => return print_version(), - Some(Commands::Version { .. }) => return print_version(), + Some(Commands::Version) => return print_version(), + Some(Commands::Direnvrc) => { + print!("{}", include_str!("../../direnvrc")); + return Ok(()); + } Some(cmd) => cmd, }; @@ -156,6 +160,7 @@ async fn main() -> Result<()> { config::write_json_schema(); Ok(()) } - Commands::Version {} => unreachable!(), + Commands::Direnvrc => unreachable!(), + Commands::Version => unreachable!(), } } diff --git a/package.nix b/package.nix index 1ab3e6edf..3c22f0144 100644 --- a/package.nix +++ b/package.nix @@ -27,6 +27,7 @@ rustPlatform.buildRustPackage { ".*devenv-eval-cache(/.*)?" ".*devenv-run-tests(/.*)?" ".*devenv-tasks(/.*)?" + "direnvrc" ".*nix-conf-parser(/.*)?" ".*xtask(/.*)?" ]; From 2c534d2da1c0c3214a274be73592e427fed6ac4a Mon Sep 17 00:00:00 2001 From: Sander Date: Tue, 24 Dec 2024 19:23:41 +0400 Subject: [PATCH 3/9] docs: improve direnv docs --- devenv/src/cli.rs | 7 ++++--- docs/automatic-shell-activation.md | 30 ++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/devenv/src/cli.rs b/devenv/src/cli.rs index 5773485b8..981081455 100644 --- a/devenv/src/cli.rs +++ b/devenv/src/cli.rs @@ -264,7 +264,7 @@ pub enum Commands { Repl {}, #[command( - about = "Deletes previous shell generations. See http://devenv.sh/garbage-collection" + about = "Deletes previous shell generations. See https://devenv.sh/garbage-collection" )] Gc {}, @@ -274,8 +274,9 @@ pub enum Commands { attributes: Vec, }, - #[command(about = "Generate and print out a direnvrc.")] - #[clap(hide = true)] + #[command( + about = "Print a direnvrc that adds devenv support to direnv. See https://devenv.sh/automatic-shell-activation." + )] Direnvrc, #[command(about = "Print the version of devenv.")] diff --git a/docs/automatic-shell-activation.md b/docs/automatic-shell-activation.md index b79302163..46eda7918 100644 --- a/docs/automatic-shell-activation.md +++ b/docs/automatic-shell-activation.md @@ -7,15 +7,31 @@ This feature relies on a separate tool called [direnv](https://direnv.net) (not 1. [Install direnv](https://direnv.net/docs/installation.html#from-system-packages) 2. [Add the direnv hook to your shell](https://direnv.net/docs/hook.html) -## Using ``direnv`` +## Configure shell activation -Once installed, you'll see a warning in your shell the next time you enter the project directory: +To enable automatic shell activation, create a `.envrc` file in your project directory with the following content: + +```bash +eval "$(devenv direnvrc)" + +use devenv +``` + +This file configures direnv to use devenv for shell activation. + +`devenv init` will create this file by default when you initialize a new project. + +## Approving and loading the shell + +Once the `.envrc` file is in place, you'll see a warning in your shell: ``` direnv: error ~/myproject/.envrc is blocked. Run `direnv allow` to approve its content ``` -Run ``direnv allow`` to enable the environment. It will now be automatically loaded and unloaded whenever you enter and exit the project directory. +Run `direnv allow` to approve the `.envrc` file. This step is a security measure to ensure you've reviewed the content before allowing it to modify your shell environment. + +After approval, direnv will automatically load and unload the devenv environment whenever you enter and exit the project directory: ```shell-session $ cd /home/user/myproject/ @@ -31,6 +47,12 @@ Entering shell ... If you'd like to use direnv and have your prompt be aware of it, we recommend [installing Starship](https://starship.rs/guide/). -## Managing the `.direnv` directory +## Ignoring the `.direnv` directory The `.direnv` directory will be added to your `.gitignore` file by default when you run `devenv init`. + +If you need to add it manually, run: + +``` +echo ".direnv" >> .gitignore +``` From b3db36646d05ac8db3520ff4f0c009a80d351951 Mon Sep 17 00:00:00 2001 From: Sander Date: Fri, 27 Dec 2024 06:24:00 +0400 Subject: [PATCH 4/9] direnv: clean up direnvrc --- direnvrc | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/direnvrc b/direnvrc index 64025d8ba..b39ecf0d8 100644 --- a/direnvrc +++ b/direnvrc @@ -50,7 +50,7 @@ _nix_import_env() { local old_xdg_data_dirs=${XDG_DATA_DIRS:-} eval "$(< "$profile_rc")" # `nix print-dev-env` will create a temporary directory and use it as TMPDIR - # We cannot rely on this directory being availble at all times, + # We cannot rely on this directory being available at all times, # as it may be garbage collected. # Instead - just remove it immediately. # Use recursive & force as it may not be empty. @@ -122,12 +122,13 @@ _nix_direnv_watches() { } _devenv_watches() { - local -n path=$1 - local -n watches=$2 - if [[ -f $path ]]; then - for file in $(cat "$path"); do - watches+=("$file") - done + local path=$1 + local -n _watches=$2 + if [[ -f "$path" ]]; then + while IFS= read -r file; do + file=$(printf "$file") + _watches+=("$file") + done < "$path" fi } @@ -139,12 +140,6 @@ _devenv_profile_rc() { echo "${profile}.rc" } -_devenv_test() { - local arg=$1 - - echo "$@" -} - use_devenv() { _nix_direnv_preflight @@ -171,9 +166,10 @@ use_devenv() { # Fetch and watch files that affect the env local env_watches - _devenv_watches env_deps_path env_watches + _devenv_watches "$env_deps_path" env_watches watch_file "${env_watches[@]}" + # Fetch all files that direnv is currently watching local watches _nix_direnv_watches watches @@ -198,10 +194,11 @@ use_devenv() { exit 0 fi + # Re-watch files that affect the env local env_watches - _devenv_watches env_deps_path env_watches + _devenv_watches "$env_deps_path" env_watches watch_file "${env_watches[@]}" - + # Fetch the final watches and compute the new profile_rc local watches _nix_direnv_watches watches From fc5a2d62ea9bb00bd0c01a685467586b38d1f9a7 Mon Sep 17 00:00:00 2001 From: Sander Date: Fri, 27 Dec 2024 10:40:41 +0400 Subject: [PATCH 5/9] direnv: add check for outdated direnvrc --- Cargo.lock | 5 ++- devenv/Cargo.toml | 1 + devenv/src/devenv.rs | 21 +++++++++- devenv/src/flake.tmpl.nix | 5 +++ devenv/src/lib.rs | 2 +- devenv/src/main.rs | 5 +-- direnvrc | 3 ++ src/modules/top-level.nix | 9 ++-- src/modules/update-check.nix | 79 ++++++++++++++++++++++++++++++------ 9 files changed, 105 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7fd46bc6f..cd31f9aa6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -547,6 +547,7 @@ dependencies = [ "miette", "nix", "nix-conf-parser", + "once_cell", "petgraph", "regex", "reqwest", @@ -1473,9 +1474,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" diff --git a/devenv/Cargo.toml b/devenv/Cargo.toml index 80c70c27a..366e082f9 100644 --- a/devenv/Cargo.toml +++ b/devenv/Cargo.toml @@ -43,3 +43,4 @@ tracing-subscriber.workspace = true which.workspace = true whoami.workspace = true xdg.workspace = true +once_cell = "1.20.2" diff --git a/devenv/src/devenv.rs b/devenv/src/devenv.rs index 79fda0e4a..d35baa331 100644 --- a/devenv/src/devenv.rs +++ b/devenv/src/devenv.rs @@ -6,6 +6,7 @@ use include_dir::{include_dir, Dir}; use miette::{bail, Result}; use nix::sys::signal; use nix::unistd::Pid; +use once_cell::sync::Lazy; use serde::Deserialize; use sha2::Digest; use std::collections::HashMap; @@ -22,6 +23,20 @@ const FLAKE_TMPL: &str = include_str!("flake.tmpl.nix"); const REQUIRED_FILES: [&str; 4] = ["devenv.nix", "devenv.yaml", ".envrc", ".gitignore"]; const EXISTING_REQUIRED_FILES: [&str; 1] = [".gitignore"]; const PROJECT_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/init"); +static DIRENVRC: Lazy = Lazy::new(|| { + include_str!("../../direnvrc").replace( + "DEVENV_DIRENVRC_ROLLING_UPGRADE=0", + "DEVENV_DIRENVRC_ROLLING_UPGRADE=1", + ) +}); +static DIRENVRC_VERSION: Lazy = Lazy::new(|| { + DIRENVRC + .lines() + .find(|line| line.contains("export DEVENV_DIRENVRC_VERSION")) + .map(|line| line.split('=').last().unwrap().trim()) + .and_then(|version| version.parse().ok()) + .unwrap_or(0) +}); // project vars const DEVENV_FLAKE: &str = ".devenv.flake.nix"; @@ -817,6 +832,7 @@ impl Devenv { devenv_tmpdir = \"{}\"; devenv_runtime = \"{}\"; devenv_istesting = {}; + devenv_direnvrc_latest_version = {}; ", crate_version!(), self.global_options.system, @@ -829,7 +845,8 @@ impl Devenv { .unwrap_or_else(|| "null".to_string()), self.devenv_tmp, self.devenv_runtime.display(), - is_testing + is_testing, + DIRENVRC_VERSION ); let flake = FLAKE_TMPL.replace("__DEVENV_VARS__", &vars); let flake_path = self.devenv_root.join(DEVENV_FLAKE); @@ -852,7 +869,7 @@ impl Devenv { let span = tracing::info_span!("building_shell", devenv.user_message = "Building shell",); let env = self.nix.dev_env(json, &gc_root).instrument(span).await?; - std::fs::write( + fs::write( self.devenv_dotfile.join("input-paths.txt"), env.paths .iter() diff --git a/devenv/src/flake.tmpl.nix b/devenv/src/flake.tmpl.nix index 0449a54f9..2f3fd0460 100644 --- a/devenv/src/flake.tmpl.nix +++ b/devenv/src/flake.tmpl.nix @@ -79,6 +79,11 @@ container.isBuilding = pkgs.lib.mkForce true; containers.${container_name}.isBuilding = true; }) + ({ options, ... }: { + config.devenv = pkgs.lib.optionalAttrs (builtins.hasAttr "direnvrcLatestVersion" options.devenv) { + direnvrcLatestVersion = devenv_direnvrc_latest_version; + }; + }) ] ++ (map importModule (devenv.imports or [ ])) ++ [ ./devenv.nix (devenv.devenv or { }) diff --git a/devenv/src/lib.rs b/devenv/src/lib.rs index 5a894696d..2e0e3aa2a 100644 --- a/devenv/src/lib.rs +++ b/devenv/src/lib.rs @@ -5,5 +5,5 @@ mod devenv; pub mod log; pub use cli::{default_system, GlobalOptions}; -pub use devenv::{Devenv, DevenvOptions}; +pub use devenv::{Devenv, DevenvOptions, DIRENVRC, DIRENVRC_VERSION}; pub use devenv_tasks as tasks; diff --git a/devenv/src/main.rs b/devenv/src/main.rs index 2cc1e6841..8379a864c 100644 --- a/devenv/src/main.rs +++ b/devenv/src/main.rs @@ -20,10 +20,9 @@ async fn main() -> Result<()> { }; let command = match cli.command { - None => return print_version(), - Some(Commands::Version) => return print_version(), + None | Some(Commands::Version) => return print_version(), Some(Commands::Direnvrc) => { - print!("{}", include_str!("../../direnvrc")); + print!("{}", devenv::DIRENVRC); return Ok(()); } Some(cmd) => cmd, diff --git a/direnvrc b/direnvrc index b39ecf0d8..f2d31ac2b 100644 --- a/direnvrc +++ b/direnvrc @@ -28,6 +28,9 @@ _nix_direnv_preflight () { if [[ ! -d "$layout_dir" ]]; then mkdir -p "$layout_dir" fi + + export DEVENV_DIRENVRC_VERSION=1 + export DEVENV_DIRENVRC_ROLLING_UPGRADE=0 } _nix_export_or_unset() { diff --git a/src/modules/top-level.nix b/src/modules/top-level.nix index e17d74ad3..715ca357a 100644 --- a/src/modules/top-level.nix +++ b/src/modules/top-level.nix @@ -213,7 +213,6 @@ in type = types.package; internal = true; }; - }; }; @@ -265,7 +264,7 @@ in pkgs.pkg-config ]; - enterShell = '' + enterShell = lib.mkBefore '' export PS1="\[\e[0;34m\](devenv)\[\e[0m\] ''${PS1-}" # set path to locales on non-NixOS Linux hosts @@ -280,10 +279,10 @@ in export DIRENV_ACTIVE="$PWD:''${DIRENV_ACTIVE-}" fi - # devenv helper + # direnv 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" + echo "An .envrc file was detected, but the direnv command is not installed." + echo "To use this configuration, please install direnv: https://direnv.net/docs/installation.html" fi mkdir -p "$DEVENV_STATE" diff --git a/src/modules/update-check.nix b/src/modules/update-check.nix index 2a2454e1a..652cf1a79 100644 --- a/src/modules/update-check.nix +++ b/src/modules/update-check.nix @@ -1,16 +1,7 @@ -{ pkgs, lib, config, ... }: +{ lib, config, ... }: let cfg = config.devenv; - action = { - "0" = ""; - "1" = '' - echo "✨ devenv ${cfg.cliVersion} is newer than devenv input (${cfg.latestVersion}) in devenv.lock. Run 'devenv update' to sync." - ''; - "-1" = '' - echo "✨ devenv ${cfg.cliVersion} is out of date. Please update to ${cfg.latestVersion}: https://devenv.sh/getting-started/#installation" >&2 - ''; - }; in { options.devenv = { @@ -26,7 +17,7 @@ in type = lib.types.bool; default = true; description = '' - Whether to warn when a new version of devenv is available. + Whether to warn when a new version of either devenv or the direnv integration is available. ''; }; cliVersion = lib.mkOption { @@ -40,9 +31,73 @@ in The latest version of devenv. ''; }; + direnvrcLatestVersion = lib.mkOption { + type = lib.types.int; + description = '' + The latest version of the direnv integration. + ''; + internal = true; + default = 1; + }; }; config = lib.mkIf cfg.warnOnNewVersion { - enterShell = action."${ toString (builtins.compareVersions cfg.cliVersion cfg.latestVersion) }"; + enterShell = + let + action = { + "0" = ""; + "1" = '' + echo "✨ devenv ${cfg.cliVersion} is newer than devenv input (${cfg.latestVersion}) in devenv.lock. Run 'devenv update' to sync." >&2 + ''; + "-1" = '' + echo "✨ devenv ${cfg.cliVersion} is out of date. Please update to ${cfg.latestVersion}: https://devenv.sh/getting-started/#installation" >&2 + ''; + }; + in + '' + # Check whether a newer version of the devenv CLI is available. + ${action."${toString (builtins.compareVersions cfg.cliVersion cfg.latestVersion)}"} + + # Check whether the direnv integration is out of date. + { + if [[ ":''${DIRENV_ACTIVE-}:" == *":${cfg.root}:"* ]]; then + if [[ ! "''${DEVENV_NO_DIRENVRC_OUTDATED_WARNING-}" == 1 && ! "''${DEVENV_DIRENVRC_ROLLING_UPGRADE-}" == 1 ]]; then + if [[ ''${DEVENV_DIRENVRC_VERSION:-0} -lt ${toString cfg.direnvrcLatestVersion} ]]; then + direnv_line=$(grep --color=never -E "source_url.*cachix/devenv" .envrc || echo "") + + echo "✨ The direnv integration in your .envrc is out of date." + echo "" + echo -n "RECOMMENDED: devenv can now auto-upgrade the direnv integration. " + if [[ -n "$direnv_line" ]]; then + echo "To enable this feature, replace the following line in your .envrc:" + echo "" + echo " $direnv_line" + echo "" + echo "with:" + echo "" + echo " eval \"\$(devenv direnvrc)\"" + else + echo "To enable this feature, replace the \`source_url\` line that fetches the direnvrc integration in your .envrc with:" + echo "" + echo " eval \"$(devenv direnvrc)\"" + fi + echo "" + echo "If you prefer to continue managing the integration manually, follow the upgrade instructions at https://devenv.sh/automatic-shell-activation/." + echo "" + echo "To disable this message:" + echo "" + echo " Add the following environment to your .envrc before \`use devenv\`:" + echo "" + echo " export DEVENV_NO_DIRENVRC_OUTDATED_WARNING=1" + echo "" + echo " Or set the following option in your devenv configuration:" + echo "" + echo " devenv.warnOnNewVersion = false;" + echo "" + fi + fi + fi + } >&2 + ''; }; } From c48fa1a72eb4c04420b59d468da9a530797cc47c Mon Sep 17 00:00:00 2001 From: Sander Date: Fri, 27 Dec 2024 11:30:39 +0400 Subject: [PATCH 6/9] direnv: move DIRENV_ACTIVE var to direnvrc --- direnvrc | 5 +++++ src/modules/top-level.nix | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/direnvrc b/direnvrc index f2d31ac2b..316ea991c 100644 --- a/direnvrc +++ b/direnvrc @@ -45,6 +45,11 @@ _nix_export_or_unset() { _nix_import_env() { local profile_rc=$1 + # Note which environments are active, but make sure we don't repeat them + if [[ ! "''${DIRENV_ACTIVE-}" =~ (^|:)"$PWD"(:|$) ]]; then + export DIRENV_ACTIVE="$PWD:''${DIRENV_ACTIVE-}" + fi + local old_nix_build_top=${NIX_BUILD_TOP:-__UNSET__} local old_tmp=${TMP:-__UNSET__} local old_tmpdir=${TMPDIR:-__UNSET__} diff --git a/src/modules/top-level.nix b/src/modules/top-level.nix index 715ca357a..0cca10c3d 100644 --- a/src/modules/top-level.nix +++ b/src/modules/top-level.nix @@ -274,11 +274,6 @@ in fi ''} - # 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 - # direnv helper if [ ! type -p direnv &>/dev/null && -f .envrc ]; then echo "An .envrc file was detected, but the direnv command is not installed." From 721689973d739e04aaa6ec3ee81b5ec49cdb460a Mon Sep 17 00:00:00 2001 From: Sander Date: Fri, 27 Dec 2024 11:41:10 +0400 Subject: [PATCH 7/9] devenv: fix build --- devenv/src/devenv.rs | 6 +++--- devenv/src/main.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/devenv/src/devenv.rs b/devenv/src/devenv.rs index d35baa331..4e2bffdc6 100644 --- a/devenv/src/devenv.rs +++ b/devenv/src/devenv.rs @@ -23,13 +23,13 @@ const FLAKE_TMPL: &str = include_str!("flake.tmpl.nix"); const REQUIRED_FILES: [&str; 4] = ["devenv.nix", "devenv.yaml", ".envrc", ".gitignore"]; const EXISTING_REQUIRED_FILES: [&str; 1] = [".gitignore"]; const PROJECT_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/init"); -static DIRENVRC: Lazy = Lazy::new(|| { +pub static DIRENVRC: Lazy = Lazy::new(|| { include_str!("../../direnvrc").replace( "DEVENV_DIRENVRC_ROLLING_UPGRADE=0", "DEVENV_DIRENVRC_ROLLING_UPGRADE=1", ) }); -static DIRENVRC_VERSION: Lazy = Lazy::new(|| { +pub static DIRENVRC_VERSION: Lazy = Lazy::new(|| { DIRENVRC .lines() .find(|line| line.contains("export DEVENV_DIRENVRC_VERSION")) @@ -846,7 +846,7 @@ impl Devenv { self.devenv_tmp, self.devenv_runtime.display(), is_testing, - DIRENVRC_VERSION + DIRENVRC_VERSION.to_string() ); let flake = FLAKE_TMPL.replace("__DEVENV_VARS__", &vars); let flake_path = self.devenv_root.join(DEVENV_FLAKE); diff --git a/devenv/src/main.rs b/devenv/src/main.rs index 8379a864c..235a65f76 100644 --- a/devenv/src/main.rs +++ b/devenv/src/main.rs @@ -22,7 +22,7 @@ async fn main() -> Result<()> { let command = match cli.command { None | Some(Commands::Version) => return print_version(), Some(Commands::Direnvrc) => { - print!("{}", devenv::DIRENVRC); + print!("{}", devenv::DIRENVRC.to_string()); return Ok(()); } Some(cmd) => cmd, From 53899b823797ef5a83155b88f623b46342f1252b Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2024 07:57:19 +0000 Subject: [PATCH 8/9] Auto generate docs/reference/options.md --- docs/reference/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/options.md b/docs/reference/options.md index c9240e691..0542f4619 100644 --- a/docs/reference/options.md +++ b/docs/reference/options.md @@ -1825,7 +1825,7 @@ string -Whether to warn when a new version of devenv is available. +Whether to warn when a new version of either devenv or the direnv integration is available. From 042a95f483bacf751c927e73761d6b6eac536091 Mon Sep 17 00:00:00 2001 From: Sander Date: Mon, 30 Dec 2024 13:56:42 +0400 Subject: [PATCH 9/9] docs: document how to pin and manually update direvnrc --- docs/automatic-shell-activation.md | 42 +++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/docs/automatic-shell-activation.md b/docs/automatic-shell-activation.md index 46eda7918..2d8a74da1 100644 --- a/docs/automatic-shell-activation.md +++ b/docs/automatic-shell-activation.md @@ -9,7 +9,7 @@ This feature relies on a separate tool called [direnv](https://direnv.net) (not ## Configure shell activation -To enable automatic shell activation, create a `.envrc` file in your project directory with the following content: +To enable automatic shell activation, create an `.envrc` file in your project directory with the following content: ```bash eval "$(devenv direnvrc)" @@ -51,8 +51,44 @@ we recommend [installing Starship](https://starship.rs/guide/). The `.direnv` directory will be added to your `.gitignore` file by default when you run `devenv init`. -If you need to add it manually, run: +To add it manually, run: -``` +```shell-session echo ".direnv" >> .gitignore ``` + +## Manually managing updates to direnvrc + +We occasionally make updates to our direnv integration script, also known as the `direnvrc`. +Devenv will use the latest compatible version if set up using the method described above in [Configure Shell Activation](#configure-shell-activation). + +Alternatively, you can pin the `direnvrc` to a specific version from the source repository. +This approach allows you audit the `direnvrc` script and have full control over when it is updated. +The downside is that you will have to manually update the URL and content hash of the script for every single project individually. + +We strongly recommend using the approach that supports automated upgrades described in [Configure Shell Activation](#configure-shell-activation). + +The `direnvrc` can be found at: + +```text +https://raw.githubusercontent.com/cachix/devenv/VERSION/direnvrc +``` + +Replace `VERSION` with a valid git tag or branch name. + +To use it in your `.envrc`, first compute its sha256 hash: + +```shell-session +direnv fetchurl "https://raw.githubusercontent.com/cachix/devenv/VERSION/direnvrc" +``` +```shell-session +Found hash: +``` + +Then modify your `.envrc`, updating the URL and inserting the computed hash from the previous step: + +```bash +source_url "https://raw.githubusercontent.com/cachix/devenv/VERSION/direnvrc" "" + +use devenv +```