Posts tagged shell


Safer Nix installation

:: linux, nix, packaging, sandbox, shell, system, test, testing, tutorial

By: Maciej Barć

Nix is useful for quickly testing out software and providing a strict environment that can be shared between people.

Today I’m trying out Nix again, this time I want to do it my way.

Installation process

Nix store

I know Nix needs “Nix store” installation on / (the system root).

Create it manually to prevent the installation script from calling sudo. 1st I switch to the root account, and then I run:

1
2
mkdir -p -m 0755 /nix
chown -R xy:xy /nix

Running the install script

Download the Nix install script and examine the contents.

1
curl -L https://nixos.org/nix/install > nix_install.sh

Then, run it with --no-daemon to prevent it running as system service.

1
sh ./nix_install.sh --no-daemon
performing a single-user installation of Nix...
copying Nix to /nix/store...
installing 'nix-2.20.1'
building '/nix/store/1ahlg3bviy174d6ig1gn393c23sqlki6-user-environment.drv'...
unpacking channels...
modifying /home/xy/.bash_profile...
modifying /home/xy/.zshenv...
placing /home/xy/.config/fish/conf.d/nix.fish...

Installation finished!  To ensure that the necessary environment
variables are set, either log in again, or type

. /home/xy/.nix-profile/etc/profile.d/nix.fish

in your shell.

Wait!

modifying /home/xy/.bash_profile...
modifying /home/xy/.zshenv...
placing /home/xy/.config/fish/conf.d/nix.fish...

That’s very rude!

Stopping Nix from making a mess

I need to prevent Nix from mess up with my environment when I do not want it to. Nix puts some code into the Bash, ZSH and Fish initialization files during installation to ease it’s use. I do not want that since I do not want Nix to meddle with my environment without me knowing it.

I keep my .bash_profile and .zshenv in a stow-managed git repo so I can just cd into my repo and do git reset --hard, but for you will have to revert those files to their old forms manually.

Playing with Nix

We do not have nix in PATH but we still can launch it. Nix executables are located inside ~/.nix-profile/bin/.

By invoking nix-shell one can create a ephemeral environment containing only packages specified after the -p flag. I always add -p nix to have the Nix tools available also inside the spawned environment.

I will test out chibi (small Scheme interpreter) + rlwrap (REPL support for software lacking it) inside a Nix ephemeral environment:

1
~/.nix-profile/bin/nix-shell -p nix chibi rlwrap

Inside the spawned shell:

1
rlwrap chibi-scheme

In the chibi REPL, let’s see the contents of the PATH environment variable:

1
(get-environment-variable "PATH")

And exit the Scheme REPL:

1
(exit)

After the playtime, run garbage collection:

1
~/.nix-profile/bin/nix-collect-garbage

Shell script setup

:: programming, python, shell

By: Maciej Barć

Good practices

Use sh

If you do not need bash features, then use sh, it is installed on every UNIX-like system.

1
#!/bin/sh

Exit on failure

Shell scripts continue even if a commend returns error. To fail right away use:

1
set -e

Trap C-c

Catch Control-c and exit.

1
trap 'exit 128' INT

Use script directory

Assume we are executing a script from directory /Admin, where / is the root of a given project directory.

1
2
script_path="${0}"
script_root="$(dirname "${script_path}")"

We can use ${script_root} to call other scripts form the Admin directory, but we can also use it to relate to the /.

1
2
3
project_root="$(realpath "${script_root}/../")"
echo "[INFO] Entering directory: ${project_root}"
cd "${project_root}"

So with above we can run commands form / (repository root). Like for example make and other:

1
2
make
python3 ./Admin/serve_1.py

Even better

Use Python for repository maintenance scripts.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from os import chdir
from os import path

from subprocess import run
from sys import argv

script_path = path.realpath(__file__)
script_root = path.dirname(script_path)
project_root = path.realpath(path.join(script_root, ".."))

print(f" * Entering directory: {project_root}")
chdir(project_root)

leftover_args = argv[1::]
command_arguments = ["make"] + leftover_args

cmd_string = " ".join(command_arguments)
print(f" * Executing command: {cmd_string}")
run(command_arguments, check=True)