summaryrefslogtreecommitdiff
path: root/nixos-update.rb
blob: 55c61e68b2d2ced884225620d45428e227b620c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/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 <def> --target <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', "#{@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