Skip to content
This repository has been archived by the owner on Oct 15, 2022. It is now read-only.

Avoid having to run lorri watch yourself #2

Closed
manveru opened this issue Mar 28, 2019 · 14 comments
Closed

Avoid having to run lorri watch yourself #2

manveru opened this issue Mar 28, 2019 · 14 comments
Labels
feature request Request for new functionality P2 major: an upcoming release user-facing Improvement that increases user experience

Comments

@manveru
Copy link

manveru commented Mar 28, 2019

So, lorri is amazing, but immediately after trying it, i found it lacking in one regard: I jump between projects a lot, and would have to remember to run lorri watch.

So instead i wrote this little .direnvrc:

use_nix() {
  eval "$(lorri direnv)"

  found=false
  for lpid in $(pgrep -xf "lorri watch"); do
    lpwd="$(readlink -e "/proc/$lpid/cwd")"
    self="$(readlink -e "/proc/$$/cwd")"
    if [[ "$lpwd" == "$self" ]]; then
      found=true
    fi
  done

  if [[ "$found" == "false" ]]; then
    lorri watch 2>/dev/null 1>/dev/null &
    pid=$!
    echo "started lorri watch with pid $pid"
    disown $pid
  fi
}

I'm sure this could be written in a better way, but that's the quickest pure-shell version i could come up with right now.

I'd just like to have this functionality in lorri :)

@Profpatsch
Copy link
Collaborator

We had planned a feature like this, but hadn’t found a good design yet. Your snippet above might come in handy, thanks!

@Profpatsch Profpatsch added P2 major: an upcoming release feature request Request for new functionality user-facing Improvement that increases user experience labels Mar 29, 2019
@Mic92
Copy link

Mic92 commented Mar 29, 2019

What was the rationale behind having a watch command instead of using direnv's auto-reload functionality?

@mwillsey
Copy link

Running a single daemon for the whole system could also be a good alternative. Then client instances kicked off by direnv could just communicate with the daemon, which would have control over potentially long running builds.

@manveru
Copy link
Author

manveru commented Mar 29, 2019

For me the biggest drawback of my approach is that I have no clue what builds might be happening unless I check something like nix-top, htop and my network traffic.
I'm not sure how to solve this though.

@Profpatsch
Copy link
Collaborator

What was the rationale behind having a watch command instead of using direnv's auto-reload functionality?

lorri uses the native filesystem watchers (via the notify crate) to watch all nix dependencies of your shell.nix, push-based. The direnv integration only watches the evaluation output, poll-based.

We want to re-build on changes in the background, so we need a push-based mechanism. Plus, I don’t know how well direnv watch scales for thousands of files (which are watched by lorri watch when you depend on e.g. a local nixpkgs checkout).

@Profpatsch
Copy link
Collaborator

@BrianHicks wrote:

I'd really like to run lorri watch once and then not have to worry about it when I switch between projects. Is that safe, as designed? I think it'd work looking at where files end up, but I'm not 100% sure if that's incidental or on purpose.
I guess if it's on purpose, I'd also like a way to query status in my shell or editor—e.g. lorri status figures out if the relevant process is idle, building, or in error. That way I can integrate it into my prompt. (edit: although I'd like that regardless of if it's a per-project or system daemon, so LMK if it would be more helpful for me to open another issue with more details to track separately?)

@peterhoeg
Copy link

An alternative on nixos so you don't have to do manual pid tracking is simply to use a systemd user unit and start it like this:

systemctl --user start lorri@$(systemd-escape $(pwd))

And the corresponding unit:

# /nix/store/nym436n8j68pj32ryn0sn5vcj8jnam6d-unit-lorri-.service/[email protected]
[Unit]
ConditionPathExists=%I
ConditionUser=!@system
Description=Lorri Watch

[Service]
Environment="LOCALE_ARCHIVE=/nix/store/k1mlzmf7czx6xpizqwnj4fyd62c65qlw-glibc-locales-2.27/lib/locale/locale-archive"
Environment="PATH=/run/wrappers/bin:%h/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/nix/store/6w3670wdhf83b9xj0wnyq4s9yz7gfvr6-libnotify-0.7.7/bin:/nix/store/d9s1kq1bnwqgxwcvv4zrc36ysnxg8gv7-coreutils-8.30/bin:/nix/store/krhqmaqal0gklh15rs2bwrqzz8mg9lrn-findutils-4.6.0/bin:/nix/store/wnjv27b3j6jfdl0968xpcymlc7chpqil-gnugrep-3.3/bin:/nix/store/x1khw8x0465xhkv6w31af75syyyxc65j-gnused-4.7/bin:/nix/store/rl4ky8x58ixnfjssliygq7iwyd30l3gn-systemd-239.20190219/bin:/run/wrappers/sbin:%h/.nix-profile/sbin:/nix/var/nix/profiles/default/sbin:/run/current-system/sw/sbin:/nix/store/6w3670wdhf83b9xj0wnyq4s9yz7gfvr6-libnotify-0.7.7/sbin:/nix/store/d9s1kq1bnwqgxwcvv4zrc36ysnxg8gv7-coreutils-8.30/sbin:/nix/store/krhqmaqal0gklh15rs2bwrqzz8mg9lrn-findutils-4.6.0/sbin:/nix/store/wnjv27b3j6jfdl0968xpcymlc7chpqil-gnugrep-3.3/sbin:/nix/store/x1khw8x0465xhkv6w31af75syyyxc65j-gnused-4.7/sbin:/nix/store/rl4ky8x58ixnfjssliygq7iwyd30l3gn-systemd-239.20190219/sbin"
Environment="TZ=Asia/Singapore"
Environment="TZDIR=/nix/store/izrzziiaa27bf9rbdb8hy867vxfjfzbi-tzdata-2018g/share/zoneinfo"



ExecStart=%h/.nix-profile/bin/lorri watch
PrivateTmp=true
ProtectSystem=full
Restart=on-failure
WorkingDirectory=%I

@gotcha
Copy link
Contributor

gotcha commented Apr 3, 2019

@manveru

I am a direnv and Nix newbie. Where do you store that .direnvrc file ?

If I check https://direnv.net/, I guess it is actually ~/.direnvrc or ~/.config/direnv/direnvrc, is it correct ?

@manveru
Copy link
Author

manveru commented Apr 3, 2019

@gotcha exactly. For reference i'm using home-manager for it with this:

  home.file.".direnvrc".text = ''
    use_nix() {
      eval "$(lorri direnv)"

      found=false
      for lpid in $(pgrep -xf "lorri watch"); do
        lpwd="$(readlink -e "/proc/$lpid/cwd")"
        self="$(readlink -e "/proc/$$/cwd")"
        if [[ "$lpwd" == "$self" ]]; then
          found=true
        fi
      done

      if [[ "$found" == "false" ]]; then
        lorri watch 2>/dev/null 1>/dev/null &
        pid=$!
        echo "started lorri watch with pid $pid"
        disown $pid
      fi
    }
  '';

@peterhoeg
Copy link

Just in case that wasn't clear, .direnvrc would look like this:

  home.file.".direnvrc".text = ''
    use_nix() {
      eval "$(lorri direnv)"
      systemctl --user start lorri@$(systemd-escape $(pwd))
    }
  '';

And if you then wanted to kill all the lorri watchers at one go:

systemctl --user stop lorri.slice

You then don't have to hunt for them.

@BrianHicks
Copy link

For those of us living without the blessings of /proc (in my case, macOS), I've got it to work like so:

use_nix() {
  eval "$(lorri direnv)"

  target="$(lsof -p "$$" | grep cwd | awk '{ print $9 }')"
  for pid in $(ps -o pid,command | grep 'lorri watch' | grep -v 'grep' | awk '{ print $1 }'); do
    if lsof -p "$pid" | grep cwd | grep -q "$target"; then
      echo "using existing lorri watch with pid $pid"
      return 0;
    fi
  done

  # existing lorri watch wasn't found
  lorri watch 2>/dev/null 1>/dev/null &
  lorri_pid=$!
  disown $lorri_pid
  echo "started lorri watch with pid $lorri_pid"
}

(n.b. terrible hacks with the lsof output. I only had so much time to do this and it works well for my configuration. If your lsof has different columns it will break.)

@Shados
Copy link

Shados commented Jun 8, 2019

@BrianHicks assuming lsof -F works the same on mac: target=$(lsof -F np -p "$$" | grep '^fcwd' -A1 | sed -rne 's|^n(.*)$|\1|p') should be reliable

@Profpatsch
Copy link
Collaborator

Tracking issue for this is #96

@Profpatsch
Copy link
Collaborator

Profpatsch commented Oct 3, 2019

I’ll close this, since we have lorri daemon now and the relevant discussion towards setting it up as a system/user service is in #96.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature request Request for new functionality P2 major: an upcoming release user-facing Improvement that increases user experience
Projects
None yet
Development

No branches or pull requests

8 participants