Working from $HOME

(updated: )

Various ways to install things: First, globally with dnf; second, homedir-locally with mise; third, homedir-locally with language-specific installers; fourth, project-locally with mise.

This is a quick reference for the third way, using language-specific installers.

In the past, I’ve configured all third-party tools to install to ~/.local/bin but that made it hard to track executables back to their owners, so I’ve changed my approach (and this post) to allow third-party tools to install to their specific dirs.

export BUN_INSTALL=~/.bun

paths=(
  # Personal overrides first
  ~/bin

  # General third-party install area
  ~/.local/bin

  # Language-specific installs
  "$BUN_INSTALL/bin"
  ~/.cargo/bin
  ~/.cask/bin
  ~/.gem/bin
  ~/.go/bin
  ~/.npm-local/bin
  ~/.yarn/bin

  # System-provided path
  "$PATH"

  # Flatpak launchers for easy access
  /var/lib/flatpak/exports/bin
)

export PATH=$(IFS=:; echo "${path[*]}")

Beware that, because this includes $PATH, it will accumulate if you run this sequence separate from profile initialization.

Node with npm

Install node with dnf install node (Fedora) or brew install node (macOS).

npm has two installation modes: local and global. Local mode installs in node_modules under the current working directory, especially for projects with dependencies managed by package.json. Global mode normally installs to /usr or /usr/local which requires sudo, but you can configure it to install to your home directory instead:

✸ npm config set prefix '${HOME}/.npm-local'

Installing prettier, the opinionated code formatter:

✸ npm install -g prettier

This installs the package in ~/.npm-local/lib/node_modules/prettier and creates a symlink at ~/.npm-local/bin/prettier for running it:

✸ which prettier
~/.npm-local/bin/prettier

✸ prettier --version
1.19.1

Upgrade with npm update -g prettier, and uninstall with npm uninstall -g prettier

Node with yarn

I’m not sure there’s a good reason to use yarn for global installs instead of npm, but for reference…

After preparing npm above, install yarn with npm i -g yarn

yarn has the same local/global modes as npm, and similarly, you can override the global installation dir:

✸ yarn config set prefix '${HOME}/.yarn'

Installing cssunminifier:

✸ yarn global add cssunminifier

This installs the package in ~/.config/yarn/global/node_modules/cssunminifier but it creates a symlink in ~/.yarn/bin according to the configured prefix:

✸ which cssunminifier
~/.local/bin/cssunminifier

✸ cssunminifier --version
cssunminifier 0.0.1 (CSS Unminifier) [JavaScript]

Upgrade is yarn global upgrade cssunminifier, and uninstall is yarn global remove cssunminifier

Python with pip

Pip accepts --user to install to your home directory. On Linux, this defaults to ~/.local/bin, and on Mac to ~/Library/Python/3.7/bin.

It is possible to override this with PYTHONUSERBASE and then set PYTHONPATH accordingly, but it’s annoying. I’ve mostly switched to using mise with python, instead.

The following instructions assume you’re not using mise, though.

Installing black, the uncompromising code formatter:

✸ pip install --user black

✸ which black
~/.local/bin/black

✸ black --version
black, version 19.10b0

Upgrade is pip install --user --upgrade and uninstall is pip uninstall (without --user)

Ruby with gem

gem accepts --user-install to install to your home directory. Unfortunately on Fedora the executable lands in ~/bin which is my personal scripts area—I’d rather install packages to ~/.gem/bin. On Mac the executable lands in ~/.gem/ruby/2.6.0/bin which isn’t on my PATH.

The easy fix is the following line in ~/.gemrc:

gem: --user-install --bindir ~/.gem/bin

Installing rubocop, the Ruby linter:

✸ gem install rubocop

✸ which rubocop
~/.gem/bin/rubocop

✸ rubocop --version
0.80.0

Upgrade is gem update rubocop and uninstall is gem uninstall rubocop

Rust with cargo

Install rust to your home directory with rustup. You can also use dnf or brew, but rustup provides a more recent compiler, even nightly if you want it.

Regardless how Rust is installed, cargo installs to ~/.cargo by default, with executables in ~/.cargo/bin.

Installing ripgrep, the really fast recursive grep:

✸ cargo install ripgrep
Installed package `ripgrep v11.0.2` (executable `rg`)

✸ which rg
~/.cargo/bin/rg

✸ rg --version
ripgrep 11.0.2

Go with go install

This isn’t working at the moment.

Go installs in ~/go by default, but you can keep it out of your visible home directory by directing it to a different location.

✸ go env -w GOPATH=$HOME/.go

That writes to ~/.config/go/env which the go command reads. Alternatively you can set these in your shell profile—it’s up to you.

Installing elvish, the friendly interactive shell:

✸ go install -u github.com/elves/elvish@latest

✸ which elvish
~/.go/bin/elvish

✸ elvish
~> █

Lua with luarocks

Install luarocks on Fedora with dnf install luarocks. The package manager includes configurations for both 5.4 (default) and 5.1 (what we need for NeoVim/LuaJIT compatibility), for example, see /etc/luarocks/config-5.1.lua.

rocks_trees = {
   { name = "user", root = home .. "/.luarocks" };
   { name = "system", root = "/usr" };
}
lua_interpreter = "lua-5.1";
variables = {
   LUA_DIR = "/usr";
   LUA_BINDIR = "/usr/bin";
}

Unfortunately, we need a couple of fixes:

sudo ln -s luajit /usr/bin/lua-5.1
luarocks --lua-version=5.4 --local install luarocks-build-rust-mlua

And now we can install a required rock for CopilotChat.nvim:

✸ luarocks --lua-version=5.1 --local install tiktoken_core