fnm icon indicating copy to clipboard operation
fnm copied to clipboard

Nushell support

Open Tarnadas opened this issue 3 years ago โ€ข 37 comments

Now that nushell has an eval-like mechanism for setting environment variables, Iโ€™d like to open this feature request to also add support for it.

Via the load-env command, one can set multiple environment variables, see here: https://github.com/nushell/nushell/issues/3481

Is that enough to make it possible or is something missing?

Tarnadas avatar Jun 01 '21 19:06 Tarnadas

It's enough for basic usage. But if we want --use-on-cd we might want to add functions or whatever it is called in nushell. I really want to try nushell again so it might be a nice experiment ๐Ÿ˜ƒ

Schniz avatar Jun 01 '21 19:06 Schniz

In the meanwhile - you can get the path edit via:

/home/orta> fnm env | lines | split column '"' | first | get Column2
/tmp/fnm_multishells/10174_1623514914978/bin

Then the rest of the env vars via:

fnm env | str  find-replace -a "export " '' | str find-replace -a '"' '' |  lines | split column = | rename name value | last 5
โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 # โ”‚         name         โ”‚                   value                    
โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 0 โ”‚ FNM_MULTISHELL_PATH  โ”‚ "/tmp/fnm_multishells/10210_1623514981704" 
 1 โ”‚ FNM_DIR              โ”‚ "/home/orta/.fnm"                          
 2 โ”‚ FNM_LOGLEVEL         โ”‚ "info"                                     
 3 โ”‚ FNM_NODE_DIST_MIRROR โ”‚ "https://nodejs.org/dist"                  
 4 โ”‚ FNM_ARCH             โ”‚ "x64"                                      
โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

So to mix it all together:

#Update paths
echo $nu.path | append (fnm env | lines | split column '"' | first | get Column2) | config set_into  path

# Add env vars
fnm env | str  find-replace -a "export " '' | str find-replace -a '"' '' |  lines | split column = | rename name value | last 5 | load-env

And at least from some current messing around it seems to work

orta avatar Jun 12 '21 16:06 orta

Nushell added pathvar command for dynamic path updates. To make work fnm in nushell, add these two commands in the startup:

#load env variables
fnm env --shell bash | lines | last 5 | str find-replace 'export ' '' | str find-replace -a '"' '' | split column = | rename name value | load-env

#add dynamic fnm path
pathvar add $nu.env.FNM_MULTISHELL_PATH

Info: Requires nushell v0.34+ (pathvar command is supported only with v0.34 and above)

ghost avatar Aug 09 '21 20:08 ghost

I had to change the path like this: pathvar add ($nu.env.FNM_MULTISHELL_PATH + /bin)

fellnerse avatar Dec 29 '21 15:12 fellnerse

Nushell has from json so a quick easy way to support this and other experimental shells would be to expose environment variables as a simple JSON object.

I made a super quick Go program for this: https://github.com/Southclaws/fnm-nushell

Install with go install github.com/Southclaws/fnm-nushell@latest then run a short pipeline: fnm env --shell powershell | fnm-nushell | from json | load-env in your nushell config.

Southclaws avatar Apr 13 '22 15:04 Southclaws

This is the code I added to my env.nu using nushell 0.61.0 that works:

#load env variables
load-env (fnm env --shell bash | lines | str replace 'export ' '' | str replace -a '"' '' | split column = | rename name value | where name != "FNM_ARCH" && name != "PATH" | reduce -f {} {|it, acc| $acc | upsert $it.name $it.value })

#add dynamic fnm path
let-env PATH = $"($env.FNM_MULTISHELL_PATH)/bin:($env.PATH)"

greym0uth avatar Apr 22 '22 21:04 greym0uth

This is my first experience with nu, so it needs to be tested, but it seems to work:

# FNM
# load env variables
load-env (fnm env --shell bash | lines | str replace 'export ' '' | str replace -a '"' '' | split column = | rename name value | where name != "FNM_ARCH" && name != "PATH" | reduce -f {} {|it, acc| $acc | upsert $it.name $it.value })

# add dynamic fnm path
let-env PATH = ($env.PATH | prepend $"($env.FNM_MULTISHELL_PATH)/bin")

# add fnm with cd
def-env fnmcd [path: string] {
  let-env PWD = ($path | path expand)
  if (['.node-version' '.nvmrc'] | any? ($env.PWD | path join $it | path exists)) {
     fnm use --silent-if-unchanged
  }
}

alias cd = fnmcd

dex157 avatar May 02 '22 10:05 dex157

Could you at least add something like fnm env --json with outputting only fnm related variables there (without modified PATH that is)? In that case we could maintain fnm integration ourselves since nushell is constantly changing. I myself don't really need on-cd functionality and am using fnm as simple nodejs sdk manager.

tuscen avatar May 20 '22 06:05 tuscen

This is the code I added to my env.nu using nushell 0.64.0 that works on WINDOWS:

# FNM for windows
# load env variables
load-env (fnm env --shell cmd | lines | str replace 'SET ' '' | split column = | rename name value | where name != "FNM_ARCH" && name != "PATH" | reduce -f {} {|it, acc| $acc | upsert $it.name $it.value })

# add dynamic fnm path
let-env Path = ($env.Path | prepend $env.FNM_MULTISHELL_PATH)

jcoppis avatar Jul 03 '22 00:07 jcoppis

Nu has changed a lot. It is better now. Any plan for fnm to have built-in support for Nushell?

Current workaround inspired by https://github.com/Schniz/fnm/issues/463#issuecomment-1106868402: Nu 0.67.0, GNU/Linux.

# FNM config

# Parse FNM env from other supported shell. It result should looks like this:
# โ”‚ FNM_VERSION_FILE_STRATEGY โ”‚ local                          โ”‚
# โ”‚ FNM_DIR                   โ”‚ /home/user/.fnm                |
# Then load these value key pair to Nushell env
load-env (fnm env --shell bash | lines | str replace 'export ' '' | str replace -a '"' '' | split column = | rename name value | where name != "FNM_ARCH" && name != "PATH" | reduce -f {} {|it, acc| $acc | upsert $it.name $it.value })

# Add dynamic FNM path
let-env PATH = ($env.PATH | append [
  $"($env.FNM_MULTISHELL_PATH)/bin"
])

azzamsa avatar Aug 21 '22 21:08 azzamsa

It seems nushell support is on the way: #801

For the meantime the solution from @azzamsa works for me, but with prepend in the PATH setting.

Nushell has very handy hook feature that can be used to pick node version on directory change. I added this to my config.nu:

let-env config = {
  โ€ฆ
  hooks: {
    env_change: {
      PWD: [{|before, after|
        if ([.nvmrc .node-version] | path exists | any? ($it == true)) {
          fnm use
        }
      }]
    }
  }
  โ€ฆ
}

vajdagabor avatar Aug 29 '22 14:08 vajdagabor

Now that #800 landed in 1.32.0, the setup for nu already got much nicer using the fnm env --json command ๐ŸŽ‰

The PR mentions this script:

fnm env --json | from json | load-env
let-env PATH = ($env.PATH | prepend ($env.FNM_MULTISHELL_PATH + "/bin"))

Personally I use this:

^fnm env --json | from json | load-env
let-env PATH = ([([$env.FNM_MULTISHELL_PATH "bin"] | path join) $env.PATH] | str join (char esep))

I concatenate the paths as strings, because this is part of my env.nu where the $env.PATH is not yet parsed as a nu list unless one manually did so. (And I like to rely on path join for adding to a path)

remmycat avatar Nov 20 '22 13:11 remmycat

Ok so, this is currently in my env.nu, and seems to at least not have nushell 0.71 errors

# FNM
# load env variables
fnm env --json | from json | load-env

# add dynamic fnm path
let-env PATH = ($env.PATH | split row (char esep) | prepend ([$env.FNM_MULTISHELL_PATH "bin"] | path join))

# add fnm with cd
def-env fnmcd [path: string] {
  let-env PWD = ($path | path expand)
  if (['.node-version' '.nvmrc'] | any ($env.PWD | path join $it | path exists)) {
     fnm use --silent-if-unchanged
  }
}

alias cd = fnmcd

Fishrock123 avatar Nov 26 '22 01:11 Fishrock123

Ok so, this is currently in my env.nu, and seems to at least not have nushell 0.71 errors

# FNM
# load env variables
fnm env --json | from json | load-env

# add dynamic fnm path
let-env PATH = ($env.PATH | split row (char esep) | prepend ([$env.FNM_MULTISHELL_PATH "bin"] | path join))

# add fnm with cd
def-env fnmcd [path: string] {
  let-env PWD = ($path | path expand)
  if (['.node-version' '.nvmrc'] | any ($env.PWD | path join $it | path exists)) {
     fnm use --silent-if-unchanged
  }
}

alias cd = fnmcd

Updated the snippet to work under 0.72:

# FNM
# load env variables
fnm env --json | from json | load-env

# add dynamic fnm path
let-env PATH = ($env.PATH | split row (char esep) | prepend ([$env.FNM_MULTISHELL_PATH "bin"] | path join))

# add fnm with cd
def-env fnmcd [path?: string] {
  if ($path == null) {
    cd
  } else {
    cd ($path | path expand)
  }
  if (['.node-version' '.nvmrc'] | any ($env.PWD | path join $it | path exists)) {
     fnm use --silent-if-unchanged
  }
}

alias cd = fnmcd

lirc571 avatar Dec 05 '22 03:12 lirc571

Ok so, this is currently in my env.nu, and seems to at least not have nushell 0.71 errors

# FNM
# load env variables
fnm env --json | from json | load-env

# add dynamic fnm path
let-env PATH = ($env.PATH | split row (char esep) | prepend ([$env.FNM_MULTISHELL_PATH "bin"] | path join))

# add fnm with cd
def-env fnmcd [path: string] {
  let-env PWD = ($path | path expand)
  if (['.node-version' '.nvmrc'] | any ($env.PWD | path join $it | path exists)) {
     fnm use --silent-if-unchanged
  }
}

alias cd = fnmcd

Updated the snippet to work under 0.72:

# FNM
# load env variables
fnm env --json | from json | load-env

# add dynamic fnm path
let-env PATH = ($env.PATH | split row (char esep) | prepend ([$env.FNM_MULTISHELL_PATH "bin"] | path join))

# add fnm with cd
def-env fnmcd [path?: string] {
  if ($path == null) {
    cd
  } else {
    cd ($path | path expand)
  }
  if (['.node-version' '.nvmrc'] | any ($env.PWD | path join $it | path exists)) {
     fnm use --silent-if-unchanged
  }
}

alias cd = fnmcd

Thank you for this, saved me a ton of time. Had to make these small changes for it to work for me on windows, but I can't say if it's correct or not confidently.

# FNM
# load env variables
fnm env --json | from json | load-env

# add dynamic fnm path
let-env Path = ($env.Path | split row (char esep) | prepend ([$env.FNM_MULTISHELL_PATH] | path join))

# add fnm with cd
def-env fnmcd [path?: string] {
  if ($path == null) {
    cd
  } else {
    cd ($path | path expand)
  }
  if (['.node-version' '.nvmrc'] | any ($env.PWD | path join $it | path exists)) {
     fnm use --silent-if-unchanged
  }
}

alias cd = fnmcd

ManneWas avatar Dec 14 '22 09:12 ManneWas

Update for nushell v0.73:

It seems that $it has been removed for any, so any ($env.PWD | path join $it | path exists) needs to be changed to any {|it| $env.PWD | path join $it | path exists}.

We might be using too many unstable features of nushell in the script :joy:

lirc571 avatar Dec 27 '22 09:12 lirc571

That's why I simply wrote it in Go instead ๐Ÿ˜‚

Southclaws avatar Dec 27 '22 12:12 Southclaws

Full fnm script for nu 0.73

# FNM
# load env variables
fnm env --json | from json | load-env

# add dynamic fnm path
let-env PATH = ($env.PATH | split row (char esep) | prepend ([$env.FNM_MULTISHELL_PATH "bin"] | path join))

# add fnm with cd
def-env fnmcd [path?: string] {
  if ($path == null) {
    cd
  } else {
    cd ($path | path expand)
  }
  if (['.node-version' '.nvmrc'] | any {|it| $env.PWD | path join $it | path exists}) {
     fnm use --silent-if-unchanged
  }
}

alias cd = fnmcd

Fishrock123 avatar Jan 16 '23 20:01 Fishrock123

On mac I'm getting

 65 โ”‚ # load env variables
 66 โ”‚ fnm env --json | from json | load-env
    ยท โ”€โ”ฌโ”€
    ยท  โ•ฐโ”€โ”€ did you mean 'fnmcd'?
 67 โ”‚
    โ•ฐโ”€โ”€โ”€โ”€
  help: No such file or directory (os error 2)

How can I solve this? :D

sr-mothership avatar Mar 01 '23 08:03 sr-mothership

@sr-mothership You need to have fnm in your PATH first. Add something like let-env PATH = ($env.PATH | split row (char esep) | prepend '/home/ubuntu/.cargo/bin') (change the path to the directory of your fnm binary) before the snippet given above.

lirc572 avatar Mar 11 '23 14:03 lirc572

@sr-mothership You need to have fnm in your PATH first. Add something like let-env PATH = ($env.PATH | split row (char esep) | prepend '/home/ubuntu/.cargo/bin') (change the path to the directory of your fnm binary) before the snippet given above.

That is so weird, since I already have let-env PATH = ($env.PATH |split row ":"| prepend $"($env.HOME)/.cargo/bin") in my config.nu ๐Ÿค”

sr-mothership avatar Mar 13 '23 07:03 sr-mothership

@sr-mothership It should be in env.nu instead. Both the let-env command and the snippet above.

lirc572 avatar Mar 13 '23 08:03 lirc572

@sr-mothership It should be in env.nu instead. Both the let-env command and the snippet above.

Damn that solved it, haha. Thanks ๐Ÿ™‚

sr-mothership avatar Mar 13 '23 08:03 sr-mothership

In Nushell 0.77, there is a breaking change related to the use of alias. If you try to use the snippet above, it will break on aliasing cd to the snippet. You can simply rename alias to old-alias, for the time being.

JadoJodo avatar Apr 04 '23 13:04 JadoJodo

It seems nushell support is on the way: #801

For the meantime the solution from @azzamsa works for me, but with prepend in the PATH setting.

Nushell has very handy hook feature that can be used to pick node version on directory change. I added this to my config.nu:

let-env config = {
  โ€ฆ
  hooks: {
    env_change: {
      PWD: [{|before, after|
        if ([.nvmrc .node-version] | path exists | any? ($it == true)) {
          fnm use
        }
      }]
    }
  }
  โ€ฆ
}

For nu 0.79.1 I had to change the hook a little bit:

    env_change: {
      PWD: [{|before, after|
        if ([.nvmrc .node-version] | path exists | any {|it| ($it == true)}) {
          fnm use
        }
      }]
    }

VuiMuich avatar May 11 '23 10:05 VuiMuich

So to summarize:

  1. add to env.nu:

    if not (which fnm | is-empty) {
      ^fnm env --json | from json | load-env
      let-env PATH = ($env.PATH | prepend [
        $"($env.FNM_MULTISHELL_PATH)/bin"
      ])
    }
    
  2. add to config.nu:

    let-env config = {
      [โ€ฆ]
      hooks: {
        [โ€ฆ]
        env_change: {
          PWD: [{ |before, after|
            if ('FNM_DIR' in $env) and ([.nvmrc .node-version] | path exists | any { |it| $it }) {
              fnm use
            }
          }]
        }
        [โ€ฆ]
      }
      [โ€ฆ]
    }
    

If you have fnm on every machine, you can leave out the not (which fnm | is-empty) and 'FNM_DIR' in $env checks.

flying-sheep avatar Jun 22 '23 08:06 flying-sheep

For fnm 1.3.5 and nu 0.83:

# env.nu
if not (which fnm | is-empty) {
  ^fnm env --json | from json | load-env
  # Checking `Path` for Windows
  let path = if 'Path' in $env { $env.Path } else { $env.PATH }
  let node_path = if (sys).host.name == 'Windows' {
    $"($env.FNM_MULTISHELL_PATH)"
  } else {
    $"($env.FNM_MULTISHELL_PATH)/bin"
  }
  $env.PATH = ($path | prepend [ $node_path ])
}

# config.nu
$env.config = {
   hooks: {
    env_change: {
      PWD: [{ |before, after|
        if ('FNM_DIR' in $env) and ([.nvmrc .node-version] | path exists | any { |it| $it }) {
          fnm use
        }
      }]
    }
  }
}

hustcer avatar Aug 09 '23 15:08 hustcer

For fnm 1.3.5 and nu 0.83:

@hustcer your solution works for me, but I always get this warning:

warning: The current Node.js path is not on your PATH environment variable.
You should setup your shell profile to evaluate `fnm env`, see https://github.com/Schniz/fnm#shell-setup on how to do this
Check out our documentation for more information: https://fnm.vercel.app```

marcelarie avatar Sep 07 '23 12:09 marcelarie

@marcelarie It works for me, However, You can give this PR a try: https://github.com/nushell/nu_scripts/pull/593/files

hustcer avatar Sep 08 '23 07:09 hustcer

use this: above v0.85 nushell
anywhere: fnm.nu

load-env (fnm env --shell bash | lines | str replace 'export ' '' | str replace -a '"' '' | split column = | rename name value | where name != "FNM_ARCH" and name != "PATH" | reduce -f {} {|it, acc| $acc | upsert $it.name $it.value })

# Add dynamic FNM path
$env.Path = ($env.Path | append [
  $env.FNM_MULTISHELL_PATH
])

then source it in $nu.config-path

source  /path/to/fnm.nu

Borber avatar Sep 26 '23 03:09 Borber