Skip to content

Instantly share code, notes, and snippets.

@0atman
Last active April 20, 2024 21:45
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save 0atman/1a5133b842f929ba4c1e195ee67599d5 to your computer and use it in GitHub Desktop.
Save 0atman/1a5133b842f929ba4c1e195ee67599d5 to your computer and use it in GitHub Desktop.
A rebuild script that commits on a successful build
{
config,
pkgs,
options,
...
}: let
hostname = "oatman-pc"; # to alllow per-machine config
in {
networking.hostName = hostname;
imports = [
/etc/nixos/hardware-configuration.nix
(/home/oatman/dotfiles/nixos + "/${hostname}.nix")
];
}
#!/usr/bin/env bash
#
# I believe there are a few ways to do this:
#
# 1. My current way, using a minimal /etc/nixos/configuration.nix that just imports my config from my home directory (see it in the gist)
# 2. Symlinking to your own configuration.nix in your home directory (I think I tried and abandoned this and links made relative paths weird)
# 3. My new favourite way: as @clot27 says, you can provide nixos-rebuild with a path to the config, allowing it to be entirely inside your dotfies, with zero bootstrapping of files required.
# `nixos-rebuild switch -I nixos-config=path/to/configuration.nix`
# 4. If you uses a flake as your primary config, you can specify a path to `configuration.nix` in it and then `nixos-rebuild switch —flake` path/to/directory
# As I hope was clear from the video, I am new to nixos, and there may be other, better, options, in which case I'd love to know them! (I'll update the gist if so)
# A rebuild script that commits on a successful build
set -e
# Edit your config
$EDITOR configuration.nix
# cd to your config dir
pushd ~/dotfiles/nixos/
# Early return if no changes were detected (thanks @singiamtel!)
if git diff --quiet '*.nix'; then
echo "No changes detected, exiting."
popd
exit 0
fi
# Autoformat your nix files
alejandra . &>/dev/null \
|| ( alejandra . ; echo "formatting failed!" && exit 1)
# Shows your changes
git diff -U0 '*.nix'
echo "NixOS Rebuilding..."
# Rebuild, output simplified errors, log trackebacks
sudo nixos-rebuild switch &>nixos-switch.log || (cat nixos-switch.log | grep --color error && exit 1)
# Get current generation metadata
current=$(nixos-rebuild list-generations | grep current)
# Commit all changes witih the generation metadata
git commit -am "$current"
# Back to where you were
popd
# Notify all OK!
notify-send -e "NixOS Rebuilt OK!" --icon=software-update-available
@0atman
Copy link
Author

0atman commented Feb 24, 2024

This is either genius or stupid, or perhaps more likely BOTH

When you use nixos, you do two things very often

  1. Edit your config
  2. Run nixos-rebuild switch, to switch to it

This is the primary loop of NixOS configuration, imagine how often you edit a config file or add a package or run a configuration command on other Linux distros.
All those actions and disparate configs are now unified, on NixOS, so nearly everyone builds their own script or alias for streamlining this process.

Here is mine, and I quite like it.

@Karitham
Copy link

Hey, this is a nice little script (I'm stealing it). One thing I'd like to point out though is that the shebang should probably be #!/usr/bin/env bash instead. This makes sure to use the bash in path, instead of a path that doesn't exist on, namely, NixOS (at least without nix-ld enabled)

@0atman
Copy link
Author

0atman commented Feb 25, 2024

@Karitham so it should! I don't know why I hard-coded it, I remember having a problem, but your exact shebang works great, so I'll update!
Thank you! 🎉

@s-aditya-k
Copy link

s-aditya-k commented Feb 25, 2024

@0atman cool script! i actually selfrolled my own scripts but this is better, i like the error thing! out of curiosity,

  1. why did you choose to format with alejandra
  2. and why use alejandra from cli instead of using nix fmt?
    also, you might want to look into [nix shell shebangs](https://nixos.wiki/wiki/Nix-shell_shebang] - personally, i make sure my deploy scripts use it, but it's not super useful most of the time here.

@0atman
Copy link
Author

0atman commented Feb 27, 2024

@s-aditya-k Thanks! I just threw it together over the last few days. Answers:

  1. I used the first tool that I found!
  2. nix fmt seems to require flake, but alejandra is standalone. Also, I think alejandra can be used as a nix fmt backend?

@0atman
Copy link
Author

0atman commented Feb 27, 2024

Whoever wanted a comprehensive guide, I do explain in the video https://www.youtube.com/watch?v=CwfKlX3rA6E
But I've also now commented the file :-)

@TechT10n
Copy link

TechT10n commented Mar 1, 2024

I appreciate your video that brought me here. As a noobie to nixos, I found it very helpful in getting me started.

However, I feel I am missing some context or knowledge relating to this script specifically. In the video you show rebuilding from /etc/nixos/ but this script seems to be building from ~/dotfiles/nixos/. Are you using a simlink or something like that? and if that is what you're doing, which path is the link and which path is the true files?

For the nix noobs out there, I managed to get this script working with a few minor changes.

  • modified the pushd line to point to /etc/nixos/ instead. # I don't think this is a good solution, but it is a solution.
  • install alejandra & libnotify
  • Removed the $EDITOR line. It still worked with it, but for me this seemed like a redundant step.

@NECooley
Copy link

NECooley commented Mar 1, 2024

@TechT10n /etc/nixos is only the default location for the configuration files. Your .nix files can be stored anywhere. nixos-rebuild switch will check the current working directory first, before checking the default.

Tris didnt mention it specifically, but the vimjoyer video he linked talks about it in more detail around the 13 minute mark. I'm also just getting my start in nixos through this video, haha. I'd be happy to swap notes with you, maybe we can help each other out.

@clot27
Copy link

clot27 commented Mar 2, 2024

nixos-rebuild switch will check the current working directory first

I didn't see this behavior documented anywhere and it didn't work either. After searching a bit, I found that one needs to specify the configuration.nix file if its not the default one by -I flag, like: nixos-rebuild switch -I nixos-config=path/to/configuration.nix (I think one need to change the nixos-rebuild in script to this, doing this worked for me). More info: https://nixos.wiki/wiki/Nixos-rebuild
I am also just a starter and maybe wrong :)

@TechT10n
Copy link

TechT10n commented Mar 2, 2024

@clot27 that makes sense. I was having the same difficulty my config was only updating from the default location regardless of where I was running ti from. And I was very confused and frustrated. My solution was to delete the default files and make them symbolic links to the location where I have them managed in my git repo.

rm /etc/nixos/configuration.nix /etc/nixos/hardware-configuration.nix
ln -s ~/mynixos/configuration.nix /etc/nixos/configuration.nix
ln -s ~/mynixos/hardware-configuration.nix /etc/nixos/hardware-configuration.nix

This is probably bad practice for portability's sake, but it lets me progress with my learning journey right now.

I think your suggestion is better for any real system setups. I will keep it in mind. Thanks

@0atman
Copy link
Author

0atman commented Mar 2, 2024

Apologies for the lack of detail folks, I have added the options in as comments in the file, but I'll repeat it here so you get a notification:

I believe there are a few ways to do this:

  1. My current way, using a minimal /etc/nixos/configuration.nix that just imports my config from my home directory (added to this gist)
  2. Symlinking to your own configuration.nix in your home directory (I think I tried and abandoned this and links made relative paths weird)
  3. My new favourite way: as @clot27 says, you can provide nixos-rebuild with a path to the config, allowing it to be entirely inside your dotfies, with zero bootstrapping of files required.
    nixos-rebuild switch -I nixos-config=path/to/configuration.nix

As I hope was clear from the video, I am new to nixos, and there may be other, better, options, in which case I'd love to know them! (I'll update the gist if so)

@TheyCallMeHacked
Copy link

TheyCallMeHacked commented Mar 2, 2024

Just a little suggestion: check that the editor exited successfully. This allows to cancel your edits using :cq in (neo)vim and kill-emacs 1 in emacs, like you would with git. Something simple like

if [ "$?" -ne 0 ]; then
    echo Cancelling...
    popd
    exit 1
fi

EDIT: forgot to popd

@NECooley
Copy link

NECooley commented Mar 2, 2024

Just to add a fourth option here, if you implement flakes in your nixos deployment that makes it a bit easier since the path to configuration.nix is specified inside your flake.nix

then your rebuild can be run with nixos-rebuild switch —flake path/to/directory where the directory contains your flake.nix so for this script it would just be . since it cds to it.

I think that’s what I got wrong earlier, the normal nixos-rebuild doesn’t search your cwd but my install command does. I just added —flake . to line 29 before the stdout redirect.

@freddycansic
Copy link

@0atman Hey Tris, thanks for providing this script and introducing me to NixOS, I'm loving it so far.

After using this script for a bit, I noticed that if there is a nix config syntax error, the script exits without printing anything.
Since set -e is active, errors produced from alejandra exit the script, and since alejandra's output is being &>/dev/null'ed, we can't see this output.

My suggestion is to change the alejandra command.
Before

alejandra . &>/dev/null

freddy@nixos:~/nixos/ > ./nixos-rebuild.sh  
~/nixos ~/nixos

After

alejandra . >/dev/null

freddy@nixos:~/nixos/ > ./nixos-rebuild.sh  
~/nixos ~/nixos
Checking style in 4 files using 4 threads.


Failed! 1 error found at:
- ./hosts/default/configuration.nix: unexpected TOKEN_ASSIGN at 5215..5224, wanted any of [TOKEN_SEMICOLON]

Although the error message isn't particularly useful, it is useful to know something has gone wrong.

Thanks

@0atman
Copy link
Author

0atman commented Mar 3, 2024

@freddycansic perfect, I've updated the gist!

@0atman
Copy link
Author

0atman commented Mar 3, 2024

@NECooley nice, added that as a 4th option in the file!

@TechT10n
Copy link

TechT10n commented Mar 4, 2024

Yeah, I agree with @NECooley , The flakes option is by far the cleanest way to go.

For me, I had a hell of a time figuring out the basics of flakes and almost gave up on trying to use them. That is, until I watched the linked video below that explained how flakes will ignore any files in a git repo that havent been staged, as if the file didnt exist. That missing bit of knowledge was resulting in file not found errors that frustrated me to no end. That one little tip made all the difference for me.

Creating this as a flake automatically solves the default pathing issue. In addition to all the other benefits they bring.

Recommend watching this video for a great demo on how to use them.
https://youtu.be/H_Qct7TVB6o?si=WHov4EddXUYqFcKO

@0atman
Copy link
Author

0atman commented Mar 5, 2024

Thanks, I'll check out the video! Luckily I was warned about the 'not staged files' problem with flakes early on. An absolutely insane design decision, and honestly put me off the whole thing.
I don't use things I can't recommend, and I can't - yet - recommend flakes, the wtf/min is too high! I imagine I'll revisit them when I get more into nixos, but I am yet to be impressed.

@singiamtel
Copy link

Just a little suggestion: check that the editor exited successfully. This allows to cancel your edits using :cq in (neo)vim and kill-emacs 1 in emacs, like you would with git. Something simple like

if [ "$?" -ne 0 ]; then
    echo Cancelling...
    popd
    exit 1
fi

EDIT: forgot to popd

I'm doing this too but checking for file changes instead of return code, works better with my workflow

# Early return if no changes were detected
if git diff --quiet *.nix; then
    echo "No changes detected, exiting."
    popd
    exit 0
fi

Very nice gist, thanks for providing it!

@0atman
Copy link
Author

0atman commented Mar 15, 2024

@singiamtel omg that's genius, I've added it to the gist, works great!

@Lemm1
Copy link

Lemm1 commented Mar 18, 2024

Thanks for sharing this script. Now, I won't ruin my system because I messed up my configuration.nix. One improvement that I made when I started using it was to handle the script itself the nix way by storing is a nixos-rebuild.nix file:

{
  config,
  pkgs,
  ...
}: {
  environment.systemPackages = with pkgs; [
    (writeShellScriptBin "nixos-rebuild.sh" ''
    // Script without shebang
        '')
  ];
}

And just importing it in my managed version of configuration.nix. This way, I don't need to think about shebang and my script is also versioned via NixOS using generations too.

@aikomastboom
Copy link

my changes so far:

alejandra . &>/dev/null \
  || ( alejandra . ; echo "formatting failed!" && exit 1)

and:

SUDO="sudo"
if [ "$(id -u)" == "0" ]; then
  SUDO="" # already root
fi

# $SUDO nixos-rebuild switch --flake .#${HOST} &> nixos-switch.log \
$SUDO nixos-rebuild switch &> nixos-switch.log \
  || (cat nixos-switch.log | grep --color error && false)

not sure why the git diff check is above editing the configuration.nix, I would put it below 🙃.

@0atman
Copy link
Author

0atman commented Mar 19, 2024

One improvement that I made when I started using it was to handle the script itself the nix way by storing is a nixos-rebuild.nix file:
@Lemm1

Very interesting idea! I have a clean division between my system (managed by nixos) and my home directory and dotfiles (managed by ME). So I don't think I'll do this just yet, but everyone else, take note!

@0atman
Copy link
Author

0atman commented Mar 19, 2024

@aikomastboom love the alejandra tweak - added.

Interesting sudo tweak - but on my machine root using sudo is a no-op and can safely run both normal and sudoed commands?

@aikomastboom
Copy link

Yeah, I know.. I came across it when fiddling with a VM that only had root and no sudo installed

I like the idea to nixify the script too ( to ensure those dependency commands are available)

@MvRens
Copy link

MvRens commented Mar 19, 2024

Fellow NixOS noob here thanks to your video! thanks for sharing this script as well.

FYI, I had to surround the two references to '*.nix' in single quotes to prevent bash from expanding the glob which resulted in 'No changes detected' and no diff output. Probably because my configuration is mostly in subfolders.

@0atman
Copy link
Author

0atman commented Mar 20, 2024

@MvRens updated, well spotted!

@mipdableep
Copy link

Hi! First of all, great video and an amazing script!
A weird problem i got stuck with is the fact that the nixos-rebuild hashing would not rebuild if the git repo was dirty - this blog post helped but i realized that staging the files fixed it - git add ./*.nix before the rebuild stage.
Aside from that, i found that using sudo nix-env -p /nix/var/nix/profiles/system --list-generations | grep "current" gave a cleaner output then nixos-rebuild list-generations

@JustCoderdev
Copy link

I added a bit where if a rebuild fails it asks if you want to open the logs or not

# In my case I use flakes but here it checks whether it fails or not
if sudo nixos-rebuild switch --flake ".#$1" &>.nixos-switch.log; then
	echo -e "Done\n"
else
	echo ""
	cat .nixos-switch.log | grep --color error

	# this is needed otherwise the script would not start next time telling you "no changes detected"
	# (The weird patter is to include all subdirectories)
	sudo git restore --staged ./**/*.nix

	if read -p "Open log? (y/N): " confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]]; then
		cat .nixos-switch.log | vim - 	
	fi

	# Clean stuff and exit
	shopt -u globstar
	popd > /dev/null
	exit 1
fi

thx @0atman for the *hem inspiration ;)

@0atman
Copy link
Author

0atman commented Mar 26, 2024

Very cool!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment