Rewrite bin/hey in ruby
Because bash is a pain at this (and planned) levels of complexity.
Also, better auto-documentation on 'hey {h,help,-h,--help}'
			
			
This commit is contained in:
		
							parent
							
								
									66868d48ac
								
							
						
					
					
						commit
						b155537219
					
				
							
								
								
									
										574
									
								
								bin/hey
									
									
									
									
									
								
							
							
						
						
									
										574
									
								
								bin/hey
									
									
									
									
									
								
							|  | @ -1,230 +1,368 @@ | |||
| #!/usr/bin/env zsh | ||||
| #!/usr/bin/env cached-nix-shell | ||||
| #! nix-shell -p ruby -i "ruby -S" --quiet | ||||
| 
 | ||||
| # I don't like the user story for Nix's binaries, unlike Guix's.  So I wrote | ||||
| # this to abstract away the unneeded complexity for everyday maintenance of my | ||||
| # NixOS config. | ||||
| # | ||||
| #   hey cd|check | ||||
| #     nix flake check $DOTFILES | ||||
| #   hey sh|show | ||||
| #     nix flake show $DOTFILES | ||||
| #   hey u|update [INPUT...] | ||||
| #     nix flake update --recreate-lock-file $DOTFILES | ||||
| #   hey re|rebuild [ARGS...] | ||||
| #     nixos-rebuild --flake $DOTFILES#$HOST ${@:-build} | ||||
| #   hey t|test        -> nixos-rebuild --flake $DOTFILES#$HOST --fast test | ||||
| #   hey theme $THEME  -> THEME=$THEME hey test (quick theme swapping) | ||||
| #   hey rollback      -> hey rebuild --rollback switch | ||||
| #   hey s|search      -> nix search nixpkgs $@ | ||||
| #   hey swap $PATH    -> swaps all specified nix-store symlinks (or recursively | ||||
| #                        in a specified directory) with copies so they can be | ||||
| #                        modified in place (while tweaking). Run again to | ||||
| #                        restore the old link (careful, you'll lose work if you | ||||
| #                        don't save your changes elsewhere!) | ||||
| #   hey vm            -> hey rebuild vm | ||||
| #   hey up|upgrade    -> hey update && hey rebuild switch | ||||
| #   hey gc            -> GC user profile, optimise nix store | ||||
| #   sudo hey gc       -> GC, optimise nix store, & delete old boot entries | ||||
| #   hey push REMOTE   -> 1. Build machine locally, 2. Copy result to remote, 3. Activate config on remote | ||||
| #   hey gen|generations -> explore, manage, diff across generations (WIP) | ||||
| # | ||||
| # hey (with no sub-command) can also be used as an alternative to nix-env: | ||||
| # | ||||
| #   hey -q | ||||
| #   hey -iA nixos.htop | ||||
| #   hey -e htop | ||||
| DESC = <<~DESC | ||||
|   Nix's CLI has a poor user story, in my opinion. It could be better; Guix is a | ||||
|   good role model here. I wrote this script to abstract the more common | ||||
|   workflows, like rebuilding (nixos-rebuild), cleaning up (nix-collect-garbage), | ||||
|   package/env management (nix-env), updating, rolling back, etc. As well as some | ||||
|   utility operations (like managing generations, building remotely, or swapping | ||||
|   themes on the fly). | ||||
| 
 | ||||
| set -e | ||||
| if [[ -z $FLAKE ]]; then | ||||
|   for dir in /etc/nixos /etc/dotfiles; do | ||||
|     if [[ -f "$dir/flake.nix" ]]; then | ||||
|       FLAKE=$dir | ||||
|       break | ||||
|     fi | ||||
|   done | ||||
| fi | ||||
|   Because I can go months without shaving yaks in NixOS, exact flags or details | ||||
|   are often and easily forgotten. This spares me that fumbling phase and also | ||||
|   serves as a reference. | ||||
| 
 | ||||
| # | ||||
| function main { | ||||
|   function _do { [[ $DRYRUN ]] && echo "$@" || "$@" } | ||||
|   Note: `hey` can also be used as a shortcut for nix-env: | ||||
| 
 | ||||
|   ### Helpers | ||||
|   function _usage { | ||||
|     echo "Usage: hey COMMAND [FLAGS...] [ARGS...]" | ||||
|     echo | ||||
|     echo "Available commands:" | ||||
|     echo "  check (ch)         Same as 'nix flake check \$DOTFILES'" | ||||
|     echo "  gc                 Garbage collect and optimize store" | ||||
|     echo "  generations (gen)  TODO" | ||||
|     echo "  push               Push thie config to a remote NixOS system and rebuild it" | ||||
|     echo "  rebuild (re)       Rebuild current system flake" | ||||
|     echo "  rollback           Roll back to last generation" | ||||
|     echo "  search (s)         Searches nixpkgs for a package" | ||||
|     echo "  show (sh)          Same as 'nix flake show \$DOTFILES'" | ||||
|     echo "  swap               Swap nix-store link (or directory thereof) for tweaking" | ||||
|     echo "  test (t)           Quickly rebuild, for quick iteration" | ||||
|     echo "  theme              Quickly swap to another theme" | ||||
|     echo "  upgrade (up)       Update flake lockfile and rebuild" | ||||
|     echo "  vm                 Build flake into a VM image" | ||||
|   } | ||||
|     hey -q | ||||
|     hey -iA nixos.htop | ||||
|     hey -e htop | ||||
| DESC | ||||
| 
 | ||||
|   function _rebuild { | ||||
|     # Move to $DOTFILES to prevent polluting the system with result links | ||||
|     pushd $FLAKE >/dev/null | ||||
|     # REVIEW Use --impure instead when it's in a stable release of nix | ||||
|     _do ${SUDO:+sudo} nixos-rebuild \ | ||||
|       --flake "${FLAKE}#${HOSTNAME:-$HOST}" \ | ||||
|       --option pure-eval no \ | ||||
|       ${@:-build} | ||||
|     popd >/dev/null | ||||
|   } | ||||
| require 'shellwords' | ||||
| require 'optparse' | ||||
| 
 | ||||
|   function _update { | ||||
|     echo "Updating NixOS flake" | ||||
|     if [[ "$@" ]]; then | ||||
|       for flake in $@; do | ||||
|         _do nix flake update --update-input "$flake" "$FLAKE" | ||||
|       done | ||||
| module Hey | ||||
|   extend self | ||||
|   Command = Struct.new(:name, :desc, :args, :opts, :fn) | ||||
| 
 | ||||
|   @@opts = { flake: File.expand_path('../', File.dirname(__FILE__)) } | ||||
|   @@alias = {} | ||||
|   @@commands = {} | ||||
|   @@cli = nil | ||||
|   def self.defcmd names, &block | ||||
|     names = (names.kind_of?(Array) ? names : [names]).map(&:to_s) | ||||
|     names.drop(1).each { |n| @@alias[n] = names.first } | ||||
|     cli = Command.new names.first, [], [] | ||||
|     cli.opts = OptionParser.new do |opt| | ||||
|       opt.banner = "Usage: #{opt.program_name} #{cli.name} [options]" | ||||
|       opt.separator "\nOptions:" | ||||
|       opt.on_tail('-h', '--help', 'Prints this help') { puts opt; exit } | ||||
|     end | ||||
|     @@cli = cli | ||||
|     fn = block.call cli | ||||
|     cli.fn = lambda do |args| | ||||
|       begin | ||||
|         cli.opts.banner += | ||||
|           (cli.args ? " #{cli.args.join ' '}" : "") + | ||||
|           (cli.desc ? "\n\n#{cli.desc[0]}\n\n#{cli.desc[1..].join "\n"}" : "") | ||||
|         cli.opts.order!(args, into: @@opts) | ||||
|         fn.call args | ||||
|       ensure @@cli = nil end | ||||
|     end | ||||
|     @@commands[cli.name] = @@cli | ||||
|   end | ||||
|   def self.opts(*args) @@cli.opts.on(*args) end | ||||
|   def self.args(*args) @@cli.args = args end | ||||
|   def self.desc(*args) @@cli.desc = args end | ||||
|   def self.sh(args, sudo: false, noerror: false) | ||||
|     raise ArgumentError unless args.kind_of?(Array) | ||||
|     args.unshift('sudo') if sudo | ||||
|     if @@opts[:dryrun] | ||||
|       print args.compact | ||||
|       return true | ||||
|     else | ||||
|       _do nix flake update --recreate-lock-file "$FLAKE" | ||||
|     fi | ||||
|   } | ||||
|       result = system "#{args.compact.shelljoin} 2>&1" | ||||
|       noerror ? result : exit($?.exitstatus) | ||||
|     end | ||||
|   end | ||||
|   def self.rebuild(*args, sudo: false) | ||||
|     sh ["nixos-rebuild", | ||||
|         "--flake", @@opts[:flake], | ||||
|         "--option", "pure-eval", "no", | ||||
|         *args], sudo: sudo | ||||
|   end | ||||
| 
 | ||||
|   function _gc { | ||||
|     if [[ $EUID == 0 ]]; then | ||||
|       echo "Cleaning up your system..." | ||||
|       _do nix-collect-garbage ${@:--d} | ||||
|       _do nix-store --optimise | ||||
|       # Clear out left-over secrets. The switch-to-configuration call below will | ||||
|       # regenerate them. | ||||
|       [[ -d /run/secrets ]] && _do rm -f /run/secrets/* | ||||
|       # nix-collect-garbage is a Nix tool, not a NixOS tool. It won't delete old | ||||
|       # boot entries until you do a nixos-rebuild (which means we'll always have | ||||
|       # 2 boot entries at any time). Instead, we properly delete them by | ||||
|       # reloading the current environment. | ||||
|       _do nix-env --delete-generations old --profile /nix/var/nix/profiles/system | ||||
|       _do /nix/var/nix/profiles/system/bin/switch-to-configuration switch | ||||
|     else | ||||
|       echo "Cleaning up your user profile..." | ||||
|       _do nix-collect-garbage ${@:--d} | ||||
|     fi | ||||
|   } | ||||
|   # | ||||
|   defcmd [:shell, :sh] do | ||||
|     desc "Open a nix-shell with the dotfiles loaded" | ||||
|     lambda do |args| | ||||
|       sh ["nix", "develop", args.first || @@opts[:flake]] | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   function _swap { | ||||
|     for target in $@; do | ||||
|       if [ -d "$target" ]; then | ||||
|         files=( "$target"/**/*.nix-store-backup ) | ||||
|         links=( $(find "$target" -type l) ) | ||||
|         if [[ "$files" ]]; then | ||||
|           echo "There are swapped files here. Unswapping!" | ||||
|           for file in $files[@]; do | ||||
|             _swap "$link" | ||||
|           done | ||||
|   defcmd :repl do | ||||
|     desc "Open a nix-repl with nixpkgs and dotfiles preloaded" | ||||
|     lambda do |args| | ||||
|       sh ["nix", "repl", @@opts[:flake], "<nixpkgs>", *args] | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd [:check, :ch] do | ||||
|     desc "Run 'nix flake check' on your dotfiles" | ||||
|     lambda do |args| | ||||
|       sh ["nix", "flake", "check", @@opts[:flake]] | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd :show do | ||||
|     desc "[ARGS...]", "Run 'nix flake show' on your dotfiles" | ||||
|     lambda do |args| | ||||
|       sh ["nix", "flake", "show", @@opts[:flake]] | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd :theme do | ||||
|     desc 'Quickly swap to another theme module' | ||||
|     args "THEME_NAME" | ||||
|     lambda do |args| | ||||
|       ENV["THEME"] = args.shift | ||||
|       rebuild "--fast", "test" | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd [:test, :t] do | ||||
|     desc "Quickly rebuild, for quick iteration" | ||||
|     lambda do |args| | ||||
|       rebuild "--fast", "test", *args | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd [:rebuild, :re] do | ||||
|     desc "Rebuild the current system's flake" | ||||
|     opts       '--build-host HOST',  "TODO" | ||||
|     opts       '--target-host HOST', "TODO" | ||||
|     opts       '--host HOST',        "TODO" | ||||
|     opts       '--user',             "TODO" | ||||
|     opts       '--fast',             "TODO" | ||||
|     lambda do |args| | ||||
|       Dir.chdir(@@opts[:flake]) do | ||||
|         @@opts[:host] ||= ENV["HOST"] || `hostname`.rstrip | ||||
|         args = ["switch"] unless args.length > 0 | ||||
|         rebuild(*(@@opts[:"build-host"]  ? ["--build-host",  @@opts[:"build-host"]]  : []), | ||||
|                 *(@@opts[:"target-host"] ? ["--target-host", @@opts[:"target-host"]] : []), | ||||
|                 @@opts[:fast]  ? "--fast"       : nil, | ||||
|                 @@opts[:debug] ? "--show-trace" : nil, | ||||
|                 *args, | ||||
|                 sudo: !@@opts[:user]) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd :rollback do | ||||
|     desc "Roll back to last generation" | ||||
|     lambda do |_args| | ||||
|       rebuild "--rollback", "switch" | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd [:update, :u] do | ||||
|     desc "Update specific flakes or all of them" | ||||
|     args "[INPUT...]" | ||||
|     lambda do |args| | ||||
|       args.each do |flake| | ||||
|         puts "Trying to update #{flake}..." | ||||
|         sh ["nix", "flake", "update", "--update-input", flake, @@opts[:flake]] | ||||
|       end.empty? and begin | ||||
|         puts "Updating all flakes.." | ||||
|         sh ["nix", "flake", "update", "--recreate-lock-file", @@opts[:flake]] | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd [:upgrade, :up] do | ||||
|     desc "Update all flakes and rebuild system" | ||||
|     lambda do |_args| | ||||
|       update | ||||
|       rebuild :switch | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd [:search, :s] do | ||||
|     desc "Search nixpkgs for a package" | ||||
|     lambda do |args| | ||||
|       sh ["nix", "search", "nixpkgs", *args] | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd :swap do | ||||
|     desc "Recursively swap nix-store links (or folders thereof) with copies.", | ||||
|          "The purpose is so you can have copies of your dotfiles that can be modified in", | ||||
|          "place (so you can iterate on them quickly instead of 'hey re'ing between", | ||||
|          "changes). Run again to restore the old link(s).\n", | ||||
|          "Warning: backup your copies, or they will be destroyed when restored!" | ||||
|     args "PATH", "[PATHS...]" | ||||
|     lambda do |args| | ||||
|       for target in args | ||||
|         if File.directory? target | ||||
|           files = Dir.glob("#{target}/**/*.nix-store-backup") | ||||
|           if files.length > 0 | ||||
|             puts "There are swapped files here. Unswapping!" | ||||
|             call :swap, *files | ||||
|           else | ||||
|             puts "Swapping files in #{target}" | ||||
|             call :swap, *(Dir.glob("#{target}/**/*") | ||||
|                             .select { |f| File.symlink? f } | ||||
|                             .select { |l| File.readlink(l).match '^/nix/store/' }) | ||||
|           end | ||||
|         elsif File.file? "#{target}.nix-store-backup" | ||||
|           puts "Unswapping #{target}" | ||||
|           sh [:mv, '-i', "#{target}.nix-store-backup", target] | ||||
|         elsif File.file? target | ||||
|           puts "Swapping #{target}" | ||||
|           sh [:mv, target, "#{target}.nix-store-backup"] | ||||
|           sh [:cp, "#{target}.nix-store-backup", target] | ||||
|         else | ||||
|           echo "Swapping files in $target" | ||||
|           for link in $links[@]; do | ||||
|             if [[ "$(readlink $link)" == /nix/store/* ]]; then | ||||
|               _swap "$link" | ||||
|             fi | ||||
|           done | ||||
|         fi | ||||
|       elif [ -f "$target.nix-store-backup" ]; then | ||||
|         echo "Unswapping $target" | ||||
|         _do mv -i "$target.nix-store-backup" "$target" | ||||
|       elif [ -f "$target" ]; then | ||||
|         echo "Swapping $target" | ||||
|         _do mv "$target" "$target.nix-store-backup" | ||||
|         _do cp "$target.nix-store-backup" "$target" | ||||
|       else | ||||
|         >&2 echo "Not a valid target: $target" | ||||
|         exit 1 | ||||
|       fi | ||||
|     done | ||||
|   } | ||||
| 
 | ||||
|   ### | ||||
|   eval set -- $(getopt -n hey -o df:h:ieAqp -l dryrun,flake:,host: -- "$@") | ||||
|   while : | ||||
|   do | ||||
|     case "$1" in | ||||
|       -d|--dryrun) DRYRUN=1;    shift   ;; | ||||
|       -f|--flake)  FLAKE=$2;    shift 2 ;; | ||||
|       -h|--host)   HOSTNAME=$2; shift 2 ;; | ||||
|       -i|-A|-q|-e|-p) _do nix-env "$@"; exit 0 ;; | ||||
|       --) shift; break ;; | ||||
|     esac | ||||
|   done | ||||
|   if [[ ! -d $FLAKE ]]; then | ||||
|     >&2 echo "Couldn't find flake.nix in $FLAKE" | ||||
|     exit 1 | ||||
|   fi | ||||
| 
 | ||||
|   cmd="$1" | ||||
|   [[ $# > 0 ]] && shift | ||||
|   case "$cmd" in | ||||
|     sh|shell)    _do nix develop "$FLAKE" "$@" ;; | ||||
|     repl)        _do nix repl "$FLAKE/flake.nix" "<nixpkgs>" "$@" ;; | ||||
| 
 | ||||
|     ch|check)    _do nix flake check "$@" "$FLAKE"      ;; | ||||
|     sh|show)     _do nix flake show "$@" "$FLAKE"       ;; | ||||
| 
 | ||||
|     theme)       THEME="$1" SUDO=1 _rebuild --fast test ;; | ||||
|     t|test)      SUDO=1 _rebuild --fast test "$@"       ;; | ||||
|     re|rebuild)  SUDO=1 _rebuild "${@:-switch}"         ;; | ||||
|     rollback)    SUDO=1 _rebuild --rollback switch      ;; | ||||
|     vm)          SUDO=1 _rebuild "$@" vm                ;; | ||||
| 
 | ||||
|     u|update)    _update "$@"                           ;; | ||||
|     up|upgrade)  _update && SUDO=1 _rebuild switch      ;; | ||||
|     s|search)    nix search nixpkgs "$@"                ;; | ||||
| 
 | ||||
|     swap)        _swap "$@" ;; | ||||
|     gc)          _gc "$@" ;; | ||||
|     push) | ||||
|       target=$1 | ||||
|       cmd=${2:-test} | ||||
|       echo "Enabling root ssh access on $target" | ||||
|       if _do ssh -t "$target" "sudo mkdir -p /root/.ssh && sudo cp -f ~/.ssh/authorized_keys /root/.ssh/authorized_keys"; then | ||||
|         trap '_do ssh -t "root@$target" "rm -f /root/.ssh/authorized_keys"' EXIT | ||||
|         echo "Building $target ($cmd)" | ||||
|         HOSTNAME="${HOSTNAME:-$target}" _rebuild \ | ||||
|           --build-host localhost \ | ||||
|           --target-host "root@$target" \ | ||||
|           $cmd | ||||
|         echo "Undoing root ssh access on $target" | ||||
|       fi | ||||
|       ;; | ||||
| 
 | ||||
|     gen|generations) | ||||
|       profile=/nix/var/nix/profiles/system | ||||
|       case "$1" in | ||||
|         switch) >&2 echo "Targeted rollback not supported yet" ;;  # TODO Is this true? | ||||
|         diff) | ||||
|           to=/nix/var/nix/profiles/system-${2}-link | ||||
|           diff \ | ||||
|             <(nix-store -q --references $profile) \ | ||||
|             <(nix-store -q --references $to) | ||||
|           ;; | ||||
|         remove) | ||||
|           shift | ||||
|           _do sudo nix-env --delete-generations --profile $profile $@ | ||||
|           ;; | ||||
|         "") | ||||
|           _do sudo nix-env --list-generations --profile $profile | ||||
|           ;; | ||||
|       esac | ||||
|       ;; | ||||
| 
 | ||||
|     h|help) _usage ;; | ||||
|     "") _usage ;; | ||||
|     *)  if command -v hey-$cmd >/dev/null; then | ||||
|           hey-$cmd $@ | ||||
|         else | ||||
|           echo "Couldn't find command: $cmd" 1>&2 | ||||
|           puts "Not a valid target: #{target}" | ||||
|           exit 1 | ||||
|         fi | ||||
|         ;; | ||||
|   esac | ||||
| } | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
| main "$@" | ||||
|   defcmd :gc do | ||||
|     desc "Garbage collect & optimize nix store" | ||||
|     opts "-a", "--all",         "GC both the user and system profiles" | ||||
|     opts "-s", "--system",      "Only GC the system profile" | ||||
|     lambda do |_args| | ||||
|       if @@opts[:all] or not @@opts[:root] | ||||
|         puts "Cleaning up your user profile..." | ||||
|         sh %w{nix-collect-garbage -d} | ||||
|       end | ||||
|       if @@opts[:all] or @@opts[:system] | ||||
|         puts "Cleaning up your system..." | ||||
|         sh %w{nix-collect-garbage -d}, sudo: true | ||||
|         sh %w{nix-store --optimise}, sudo: true | ||||
|         # Clear out left-over secrets. The switch-to-configuration call below will | ||||
|         # regenerate them. | ||||
|         if File.exists?("/run/secrets/*") | ||||
|           sh %w{rm -rf /run/secrets/*}, sudo: true | ||||
|         end | ||||
|         # nix-collect-garbage is a Nix tool, not a NixOS tool. It won't delete old | ||||
|         # boot entries until you do a nixos-rebuild (which means we'll always have | ||||
|         # 2 boot entries at any time). Instead, we properly delete them by | ||||
|         # reloading the current environment. | ||||
|         sh %w{nix-env --delete-generations old --profile /nix/var/nix/profiles/system}, sudo: true | ||||
|         sh %w{/nix/var/nix/profiles/system/bin/switch-to-configuration switch}, sudo: true | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   defcmd :ssh do | ||||
|     desc "Run a Hey command on a remote NixOS system", | ||||
|          "This command assumes that the remote system is built on these dotfiles, it is", | ||||
|          "dangerous to do this if that isn't the case." | ||||
|     args "HOST", "[COMMAND]" | ||||
|     opts '--host HOST', "The nixos config to build and deploy to HOST" | ||||
|     lambda do |args| | ||||
|       target, command = args.take(2) | ||||
|       if command.match /switch|test|boot/ | ||||
|         puts "Enabling root ssh access on #{target}. Enter root password for remote system:" | ||||
|         if sh ["ssh", "-t", target, | ||||
|                "sudo mkdir -p /root/.ssh && sudo cp -f ~/.ssh/authorized_keys /root/.ssh/"], | ||||
|               noerror: true | ||||
|           begin | ||||
|             puts "Building #{target}" | ||||
|             call "rebuild", ["--user", | ||||
|                              "--host", @@opts[:host] || target, | ||||
|                              "--build-host",  "localhost", | ||||
|                              "--target-host", "root@#{target}", | ||||
|                              command || "test"] | ||||
|           ensure | ||||
|             puts "Undoing root ssh access on #{target}" | ||||
|             sh ["ssh", "-t", "root@#{target}", "rm -f /root/.ssh/authorized_keys"] | ||||
|           end | ||||
|         end | ||||
|       else | ||||
|         command = "nix run github:hlissner/dotfiles -- #{command}" | ||||
|         puts "Running '#{command}' on #{target}" | ||||
|         sh ["ssh", "-t", target, command] | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   # TODO Generalize this to nix profiles | ||||
|   defcmd [:generations, :gen] do | ||||
|     desc "Explore, manage, diff across generations" | ||||
|     opts       '--diff FROM TO',   "TODO" | ||||
|     opts '-r', '--remove PROFILE', "TODO" | ||||
|     lambda do |args| | ||||
|       profile = "/nix/var/nix/profiles/system" | ||||
|       case args.shift | ||||
|       when "diff" | ||||
|         from, to = *args | ||||
|         if from == to | ||||
|           puts "Comparing the same profile! Aborting..." | ||||
|           exit 1 | ||||
|         elsif [from, to].compact.select do |gen| | ||||
|                 File.exists? "#{profile}-#{gen}-link" or | ||||
|                   puts "Generation #{gen} does not exist!" | ||||
|               end.length != [from, to].compact.length | ||||
|           exit 2 | ||||
|         end | ||||
|         from = from ? "#{profile}-#{from}-link" : profile | ||||
|         to   = to   ? "#{profile}-#{to}-link"   : profile | ||||
|         fromDevs = `nix-store -q --references #{from}`.split | ||||
|         toDevs   = `nix-store -q --references #{to}`.split | ||||
|         puts (fromDevs - toDevs).map { |d| "\e[31m-#{d}\e[0m" } | ||||
|         puts (toDevs - fromDevs).map { |d| "\e[32m+#{d}\e[0m" } | ||||
|       when /ls|list/ | ||||
|         sh ["nix-env", "--list-generations", "--profile", profile], sudo: true | ||||
|       when "remove" | ||||
|         sh ["nix-env", "--delete-generations", "--profile", profile, *args], sudo: true | ||||
|       when "switch" | ||||
|         puts "Targeted rollback not supported yet"  # TODO Is this true? | ||||
|         exit 1 | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   class UsageError < StandardError; end | ||||
|   defcmd [:help, :h] do | ||||
|     desc "Show usage information for this script or a subcommand" | ||||
|     args "[SUBCOMMAND]" | ||||
|     lambda do |args| | ||||
|       raise UsageError unless args.first | ||||
|       call args.first, ["-h", *args.drop(1)] | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   # | ||||
|   def self.call command, args | ||||
|     cmd = command.to_s | ||||
|     cmd = @@alias[cmd] while cmd and not @@commands.key?(cmd) | ||||
|     if cmd | ||||
|       @@commands[cmd].fn.call args | ||||
|     elsif command | ||||
|       puts "Couldn't recognize command '#{command}'" | ||||
|       exit 1 | ||||
|     else | ||||
|       raise UsageError | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def self.run args=[] | ||||
|     opts = OptionParser.new do |opt| | ||||
|       opt.banner = "Usage: hey [global-options] [command] [sub-options]\n\n" + DESC | ||||
|       opt.separator "\n\nAvailable Commands:" | ||||
|       opt.separator (@@commands.values | ||||
|                        .reject { |c| c.desc.empty? } | ||||
|                        .sort { |a,b| a.name <=> b.name } | ||||
|                        .map { |c| "  %-18s %s\n" % ["#{c.name} #{@@alias.key(c.name)}", | ||||
|                                                    c.desc[0]]}) | ||||
|       opt.separator "\nOptions:" | ||||
|       opt.on('-d', '--dryrun',    "Don't change anything; preform dry run") | ||||
|       opt.on('-D', '--debug',     "Show trace on nix errors") | ||||
|       opt.on('-f', '--flake URI', "Change target flake to URI") | ||||
|       opt.on(*%w{-h --help},      "Display this help, or help for a specific command") | ||||
|       opt.on(*%w{-i -A -q -e -p}, "Forward to nix-env") { sh ["nix-env", *ARGV]; exit } | ||||
|     end | ||||
|     opts.order!(args, into: @@opts) | ||||
| 
 | ||||
|     begin | ||||
|       args.unshift("help") if @@opts[:help] | ||||
|       if args.length == 0 | ||||
|         puts "Error: No command specified!" | ||||
|         puts | ||||
|         raise UsageError | ||||
|       else | ||||
|         call args.first, args.drop(1) | ||||
|       end | ||||
|     rescue UsageError | ||||
|       puts opts | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Hey.run ARGV.dup | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue