#!/usr/bin/env ruby require 'tmpdir' require 'json' require 'fileutils' require 'tty-command' require 'etc' require 'optparse' class NixOSUpdater def initialize @options = parse_options @system_flake = ARGV[0] @target_host = ARGV[1] validate_args! ENV['SOPS_AGE_KEY_FILE'] ||= File.expand_path('~/.config/sops/age/keys.txt') end def run if @options[:install] perform_install else perform_update end end private def parse_options options = {} OptionParser.new do |opts| opts.banner = 'Usage: nixos-update.rb [--install] --system-def --target ' opts.on('--install', 'Perform initial installation with nixos-anywhere') do options[:install] = true end opts.on('-h', '--help', 'Show this help message') do puts opts exit end opts.on('--system-def', 'what system configuration to install') do |defn| @system_def = defn end opts.on('--target', 'what user@host to install to') do |target| @target = target end opts.on('--build-on', 'what user@host to build on') do |build_on| @build_on = build_on end end.parse! options end def validate_args! if @system_def.nil? warn 'Error: --system-def argument is required' exit 1 end @target = "#{Etc.getlogin}@#{@system_def}" if @target.nil? @build_on = @target if @build_on.nil? && RUBY_PLATFORM !~ /linux/ end def decrypt_secrets cmd = TTY::Command.new(printer: :null) result = cmd.run(sops_exe, 'decrypt', '--output-type', 'json', "#{@system_flake}/secrets.yaml") JSON.parse(result.out) end def perform_install puts "### Performing initial installation of #{@system_flake} to #{@target_host} ###" secrets = decrypt_secrets Dir.mktmpdir('secrets') do |dir| FileUtils.mkdir_p("#{dir}/copy_dir/etc/ssh") File.write("#{dir}/copy_dir/etc/ssh/ssh_host_ed25519_key", secrets['ssh_host_key_ed25519']) File.write("#{dir}/luks_passphrase", secrets['luks_passphrase']) cmd_args = [ 'nixos-anywhere', '--disk-encryption-keys', "#{dir}/luks_passphrase", "#{dir}/luks_passphrase", '--extra-files', "#{dir}/copy_dir", '--flake', ".##{@system_flake}" ] cmd_args << '--build-on-remote' if RUBY_PLATFORM !~ /linux/ cmd_args << @target_host # Execute nixos-anywhere cmd = TTY::Command.new cmd.run(*cmd_args) end end def perform_update puts "### Updating #{@system_flake} configuration on #{@target_host} ###" cmd_args = [ 'nixos-rebuild-ng', 'switch', '--flake', ".##{@system_flake}", '--sudo', '--ask-sudo-passowrd' ] if @target_host cmd_args << '--target-host' << @target_host cmd_args << '--use-remote-sudo' end cmd_args << '--build-on' << @build_on if @build_on cmd = TTY::Command.new cmd.run(*cmd_args) end end # Run the updater NixOSUpdater.new.run