dotfiles

My PC Dotfiles & Website

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  )