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
|