dotfiles
My PC Dotfiles & Websitegit clone https://github.com/maydayv7/dotfiles
scripts/os.nix (15887B)
1{
2 lib,
3 inputs,
4 pkgs,
5 files,
6 ...
7}:
8with files; let
9 inherit (inputs) self;
10 inherit (lib) licenses recursiveUpdate;
11
12 list = attrs: builtins.foldl' (x: y: x + y + " ") "" (builtins.attrNames attrs);
13 devShells = list self.devShells."${pkgs.stdenv.system}";
14 nixosConfigurations = list self.nixosConfigurations;
15
16 # Usage Description
17 usage = {
18 script = ''
19 # Legend #
20 xxx - Command
21 [ ] - Optional - Description
22 ' ' - Variable
23
24 # Usage #
25 apply [ --'option' ] - Applies device and user config
26 cache 'command' - Pushes binary output to Cachix
27 check [ --trace ] - Checks system configuration [ Displays error trace ]
28 clean [ --all ] - Cleans and optimises Nix Store
29 explore - Opens interactive shell to explore syntax and config
30 iso 'variant' [ --burn ] - Builds image for specified device [ Burns '.iso' to USB ]
31 list [ 'pattern' ] - Lists all installed packages [ Returns matches ]
32 locate 'package' - Locates installed package
33 run [ 'path' ] 'command' - Runs specified command [ from 'path' ] (Wraps 'nix run')
34 search 'term' [ 'source' ] - Searches for packages [ providing 'term' ] or config options
35 secret 'choice' [ 'path' ] - Manages 'sops' encrypted secrets
36 setup - Sets up system (on first boot)
37 shell [ 'name' ] - Opens desired Nix Developer Shell
38 update [ 'repo' / --'option' ] - Manages system package updates
39 '';
40
41 apply = ''
42 # Usage #
43 'specialisation' - Activates specified system specialisation
44 --activate [ home ] - Activates current [ home ] config
45 --boot - Applies config on boot
46 --rollback [ 'generation' ] - Reverts to last [ or specified ] build
47 --test - Tests config build
48 '';
49
50 search = ''
51 # Usage #
52 cmd.'command' - Searches for package providing 'command'
53 pkgs.'package' [ 'repo' ] - Searches for 'package' [ In Repository ]
54 'term' - Searches for packages and config option (matching 'term')
55 '';
56
57 secret = ''
58 # Usage #
59 create 'path' - Creates desired secret
60 edit 'name' - Edits desired secret
61 list - Lists all 'sops' encrypted secrets
62 show 'name' - Shows desired secret
63 update - Updates secrets to defined keys
64 '';
65
66 update = ''
67 # Usage #
68 --pkgs - Automatically updates custom packages
69 --commit - Updates 'inputs' and commits changes
70 'repo' [ 'source' ] - Updates 'repo' input [ To specified 'source' ]
71 '';
72 };
73in
74 recursiveUpdate
75 {
76 meta = {
77 mainProgram = "os";
78 description = "System Management Script";
79 homepage = path.repo;
80 license = licenses.gpl3Only;
81 maintainers = ["maydayv7"];
82 };
83 }
84 (
85 pkgs.writeShellApplication {
86 name = "os";
87 runtimeInputs = with pkgs; [
88 coreutils
89 gnugrep
90 gnused
91 git
92 jq
93 tree
94 gnupg
95 sops
96
97 nixFlakes
98 cachix
99 manix
100 nh
101 nix-output-monitor
102 ];
103
104 text = ''
105 set +eu
106 ${scripts.commands}
107
108 installed() { nix-store -q -R /run/current-system | sed -n -e 's/\/nix\/store\/[0-9a-z]\{32\}-//p' | sort | uniq; }
109
110 missing() {
111 if nix search nixpkgs#"$1" &> /dev/null
112 then error "Package '$1' is not installed"
113 else error "$2"
114 fi
115 }
116
117 secret_exists() { find ${path.system} -name "$1".secret | grep "secret" &> /dev/null; }
118
119 if [[ -n $IN_NIX_SHELL ]]
120 then
121 warn "You are in a Nix Developer Shell" "This script may not work here properly\n"
122 fi
123
124 case $1 in
125 "") error "Expected an option" "${usage.script}";;
126 help|--help|-h) echo -e "## Tool for NixOS System Management ##\n${usage.script}";;
127 "apply")
128 case $2 in
129 help|--help|-h) echo "${usage.apply}";;
130 "")
131 echo "Applying Configuration..."
132 nh os switch ${path.system}
133 ;;
134 "--activate")
135 case $3 in
136 "")
137 SPEC=$(cat /etc/specialisation 2> /dev/null || true)
138 if [ -z "$SPEC" ]
139 then
140 echo "Activating Configuration..."
141 sudo /nix/var/nix/profiles/system/bin/switch-to-configuration switch
142 else
143 echo "Activating Configuration ($SPEC)..."
144 sudo /nix/var/nix/profiles/system/specialisation/"$SPEC"/bin/switch-to-configuration switch
145 fi
146 ;;
147 "home")
148 echo "Applying Home Configuration..."
149 sudo systemctl restart home-manager-"$USER"
150 ;;
151 *) error "Unknown option '$3'";;
152 esac
153 ;;
154 "--boot")
155 echo "Applying Configuration..."
156 if nh os boot ${path.system}
157 then
158 restart
159 else
160 error "Couldn't build generation successfully"
161 fi
162 ;;
163 "--test")
164 echo "Testing Configuration..."
165 nh os test ${path.system}
166 ;;
167 "--rollback")
168 case $3 in
169 "")
170 echo "Applying Rollback..."
171 sudo nixos-rebuild switch --rollback
172 ;;
173 "list")
174 echo "# System Generations #"
175 nixos-rebuild list-generations
176 ;;
177 *)
178 echo "Rolling Back to Generation '$3'..."
179 sudo nix-env --switch-generation "$3" -p "/nix/var/nix/profiles/system" && os apply --activate
180 ;;
181 esac
182 ;;
183 *)
184 SPECIALISATIONS=$(ls -1 /nix/var/nix/profiles/system/specialisation)
185 if grep -wq "$2" <<<"$SPECIALISATIONS" &> /dev/null
186 then
187 echo "Applying Configuration ($2)..."
188 nh os switch ${path.system} -s "$2"
189 else
190 error "Unknown option '$2'\n${usage.apply}" "# Available Specialisations #\n$SPECIALISATIONS"
191 fi
192 ;;
193 esac
194 ;;
195 "cache")
196 if [ -z "$2" ]
197 then
198 error "Expected a build command"
199 else
200 echo "Executing command '" "''${@:2}" "'..."
201 cachix authtoken "$(find ${path.system} -name cachix-token.secret -exec sops --config ${path.sops} -d {} \+)"
202 cachix watch-exec ${path.cache} "''${@:2}"
203 fi
204 ;;
205 "check")
206 echo "Formatting Code..."
207 pushd ${path.system} &> /dev/null; nix fmt; popd &> /dev/null
208 case $2 in
209 "") nix flake check ${path.system} --keep-going;;
210 "--trace") nix flake check ${path.system} --keep-going --show-trace;;
211 *) nix flake check "$2" --keep-going;;
212 esac
213 ;;
214 "clean")
215 if [ "$EUID" -ne 0 ] && [ "$2" != "--all" ]
216 then
217 echo "Running Garbage Collection..."
218 nh clean user --optimise
219 warn "Run as 'root' or use '--all' to clean system generations"
220 else
221 echo "Running Garbage Collection..."
222 nh clean all --optimise
223 sudo rm -rf /run/secrets/*
224 os apply --activate
225 fi
226 ;;
227 "explore")
228 case $2 in
229 "") nix repl --arg host true --arg path ${path.system} --file ${repl};;
230 *) nix repl --arg path "$(readlink -f "$2" | sed 's|/flake.nix||')" --file ${repl};;
231 esac
232 ;;
233 "iso")
234 case $2 in
235 "") error "Expected a device name";;
236 *)
237 if grep -wq "$2" <<<"${nixosConfigurations}" &> /dev/null
238 then
239 echo "Building '$2' Image..."
240 nom build ${path.system}#nixosConfigurations."$2".config.system.build.images.iso
241 else
242 error "Unknown device '$2'" "# Available devices #\n ${nixosConfigurations}"
243 fi
244 ;;
245 esac
246 case $3 in
247 "") echo "The '--burn' option can be used to flash the image to a USB";;
248 "--burn")
249 case $4 in
250 "") error "Expected 'path' to USB";;
251 *)
252 IMAGE=$(find ./result/iso -type f -name "*.iso")
253 sudo dd if="$IMAGE" of="$4" status=progress bs=1M
254 ;;
255 esac
256 ;;
257 *) error "Unknown option '$3'";;
258 esac
259 ;;
260 "list")
261 case $2 in
262 "") installed;;
263 *) find=$(installed | grep "$2")
264 if [ -z "$find" ]
265 then
266 missing "$2" "Package '$2' not found"
267 else
268 echo "$find"
269 fi
270 ;;
271 esac
272 ;;
273 "locate")
274 case $2 in
275 "") error "Expected package name";;
276 *)
277 package=$(installed | grep "$2")
278 if [ -z "$package" ]
279 then
280 location=$(find /nix/store -maxdepth 1 -type d -name "*$2*")
281 if [ -n "$location" ]
282 then
283 if (( $(grep -c . <<<"$location") > 1 ))
284 then
285 echo -e "Locations:\n$location"
286 else
287 echo -e "Location: $location"
288 fi
289 else
290 missing "$2" "Package '$2' is invalid"
291 fi
292 else
293 if (( $(grep -c . <<<"$package") > 1 ))
294 then
295 locations=$(find /nix/store -maxdepth 1 -type d -name "*$2*")
296 echo -e "Locations:\n$locations"
297 else
298 echo "Package $package found"
299 nix search nixpkgs#"$2" &> /dev/null && location=$(nix eval nixpkgs#"$2".outPath 2> /dev/null | sed 's/"//g') || location=$(find /nix/store -maxdepth 1 -type d -name "*$package")
300 echo "Location: $location"
301 fi
302 fi
303 ;;
304 esac
305 ;;
306 "run")
307 export NIXPKGS_ALLOW_UNFREE=1
308 if [[ "$2" == *[:/]* ]] || grep -wq "$2" <<<"${list inputs}"
309 then
310 nix run "$2"#"$3" --impure -- "''${@:4}"
311 else
312 nix run ${path.system}#"$2" --impure -- "''${@:3}"
313 fi
314 ;;
315 "search")
316 case $2 in
317 "") error "Expected an option" "${usage.search}";;
318 help|--help|-h) echo "${usage.search}";;
319 cmd.*)
320 command="''${2//cmd\./}"
321 echo "Searching for Package providing command '$command'..."
322 output=$(nix-locate --whole-name --type x --type s --no-group --top-level --at-root "/bin/$command")
323 if [ -z "$output" ]
324 then
325 error "Command '$command' not found"
326 else
327 echo "$output"
328 fi
329 ;;
330 pkgs.*)
331 package="''${2//pkgs\./}"
332 echo "Searching for package '$package'..."
333 if [ -z "$3" ]
334 then
335 nix search nixpkgs#"$package"
336 else
337 nix search "$3"#"$package"
338 fi
339 ;;
340 *)
341 echo "Searching for term '$2'..."
342 manix "$2"
343 ;;
344 esac
345 ;;
346 "secret")
347 case $2 in
348 "") error "Expected an option" "${usage.secret}";;
349 help|--help|-h) echo "${usage.secret}";;
350 "create")
351 case $3 in
352 "") error "Expected 'name' of secret";;
353 *)
354 echo "Creating secret '$3'..."
355 sops --config ${path.sops} -i ${path.system}/"$3".secret
356 ;;
357 esac
358 ;;
359 "edit")
360 case $3 in
361 "") error "Expected 'name' of secret";;
362 *)
363 if secret_exists "$3"
364 then
365 echo "Editing secret '$3'..."
366 find ${path.system} -name "$3".secret -exec sops --config ${path.sops} -i {} \+
367 else
368 error "Unknown secret '$3'"
369 fi
370 ;;
371 esac
372 ;;
373 "list")
374 echo "## Secrets in ${path.system} ##"
375 grep / ${path.sops} | sed -e 's|- path_regex:||' -e 's/\/\.\*\$//' -e 's| |${path.system}/|' | xargs tree -C --noreport -P '*.secret' -I '_*' | sed 's/\.secret//'
376 ;;
377 "show")
378 if secret_exists "$3"
379 then
380 echo "Showing secret '$3'..."
381 find ${path.system} -name "$3".secret -exec sops --config ${path.sops} -d {} \+
382 else
383 error "Unknown secret '$3'"
384 fi
385 ;;
386 "update")
387 echo "Updating secrets..."
388 find ${path.system} -name '*.secret' ! -name '_*' -exec sops --config ${path.sops} updatekeys {} \;
389 ;;
390 *) error "Unknown option '$2'" "${usage.secret}";;
391 esac
392 ;;
393 "setup")
394 if [ -d ${path.persist} ]
395 then
396 DIR=${path.persist}${path.system}
397 else
398 DIR=${path.system}
399 fi
400
401 echo "Cloning Repository..."
402 sudo git clone --recurse-submodules ${path.repo} "$DIR"
403 pushd "$DIR" &> /dev/null; sudo git config core.fileMode false; popd &> /dev/null
404 sudo chgrp -R keys "$DIR"
405 sudo chmod -R u=rwX,g=rwX,o= "$DIR"
406 sudo find -P "$DIR" -type d -exec chmod g+s {} +
407 newline
408
409 read -rp "Enter path to GPG Keys (path/.git): " KEY
410 LINK='(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]'
411 if [ -z "$KEY" ]
412 then
413 error "Path cannot be empty"
414 elif [[ $KEY =~ $LINK ]]
415 then
416 echo "Cloning Keys..."
417 git clone "$KEY" keys --progress
418 else
419 cp -r "$KEY"/. ./keys
420 fi
421 echo "Importing Keys..."
422 find ./keys -name '*.gpg' -exec sudo gpg --homedir ${path.gpg} --import {} \+
423 rm -rf ./keys
424 newline
425
426 os apply --activate
427 ;;
428 "shell")
429 case $2 in
430 "") nix develop ${path.system} --command "$SHELL";;
431 *)
432 if grep -wq "$2" <<<"${devShells}" &> /dev/null
433 then
434 nix develop ${path.system}#"$2" --command "$SHELL"
435 else
436 error "Unknown Shell '$2'" "# Available Shells #\n ${devShells}"
437 fi
438 ;;
439 esac
440 ;;
441 "update")
442 case $2 in
443 help|--help|-h) echo "${usage.update}";;
444 "--pkgs")
445 echo "Updating Packages..."
446 pushd ${path.system}/packages &> /dev/null
447 bash ${./packages.sh}
448 popd &> /dev/null
449 ;;
450 "--commit")
451 echo "Updating Flake inputs..."
452 nix flake update --flake ${path.system} --commit-lock-file
453 ;;
454 "")
455 echo "Updating Flake inputs..."
456 nix flake update --flake ${path.system}
457 ;;
458 *)
459 echo "Updating Flake Input '$2'..."
460 if [ -z "$3" ]
461 then
462 nix flake update --flake ${path.system} "$2"
463 else
464
465 nix flake update --flake ${path.system} "$2" --override-input "$2" "$3"
466 fi
467 ;;
468 esac
469 ;;
470 *) error "Unknown option '$1'" "${usage.script}";;
471 esac
472 '';
473 }
474 )