#!/usr/bin/env ruby require 'tmpdir' require 'json' require 'fileutils' require 'tty-command' require 'etc' require 'optparse' class NixOSUpdater def initialize parse_options! validate_args! ENV['SOPS_AGE_KEY_FILE'] ||= File.expand_path('~/.config/sops/age/keys.txt') end def run if @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 @install = true end opts.on('-h', '--help', 'Show this help message') do puts opts exit end opts.on('--system-def=DEFN', 'what system configuration to install') do |defn| @system_def = defn end opts.on('--target=TARGET', 'what user@host to install to') do |target| @target = target end opts.on('--build-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/ puts "TARGET: #{@target} BUILD_ON #{@build_on} DEF #{@system_def}" end def decrypt_secrets cmd = TTY::Command.new(printer: :null) result = cmd.run('sops', 'decrypt', '--output-type', 'json', "machines/#{@system_def}/secrets.yaml") JSON.parse(result.out) end def perform_install puts "### Performing initial installation of #{@system_flake} to #{@target} ###" 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', "/run/secrets/luks_passphrase", "#{dir}/luks_passphrase", '--extra-files', "#{dir}/copy_dir", '--flake', ".##{@system_def}" ] cmd_args << '--build-on-remote' if RUBY_PLATFORM !~ /linux/ cmd_args << @target # Execute nixos-anywhere cmd = TTY::Command.new cmd.run(*cmd_args) end end def perform_update puts "### Updating #{@system_def} configuration on #{@target} ###" cmd_args = [ 'nixos-rebuild-ng', 'switch', '--flake', ".##{@system_def}", '--sudo', '--ask-sudo-password' ] if @target cmd_args << '--target-host' << @target end cmd_args << '--build-host' << @build_on if @build_on cmd = TTY::Command.new(printer: :quiet, pty: true) cmd.run(*cmd_args) end end # Run the updater NixOSUpdater.new.run