1#!/usr/bin/env nu
2
3use std "path add"
4use std/log
5
6path add /nix/var/nix/profiles/default/bin
7
8cd $env.NH_FLAKE
9
10# load webhook secrets
11rage -d -i ./ssh_key ./secrets/deployWebhook.age | from toml | load-env
12
13def webhook [title: string, content: string, exit_code?: number, ping?: bool = false] {
14 let type = if $exit_code == null { "⌛" } else if $exit_code == 0 { "✔️" } else { "❌" }
15 let msg = {
16 content: (if $ping { "hey <@853064602904166430>!" } else { "" }),
17 embeds: [{
18 description: $content,
19 title: $"($type) /($title)/",
20 footer: {
21 text: $"(date now) \((sys host | get hostname)\)",
22 }
23 }]
24 }
25
26 if $exit_code == 0 or $exit_code == null {
27 log info $content
28 } else {
29 log error $content
30 }
31 # http post --content-type application/json $"https://discord.com/api/webhooks/($env.WEBHOOK_ID)/($env.WEBHOOK_TOKEN)" $msg
32}
33
34def upload-paste []: any -> string {
35 # let paste_url = http post -H ["user-agent" "gaze.systems terra deploy"] --content-type multipart/form-data "https://0x0.st" {file: ($in | to text | into binary), secret: true}
36 # return $paste_url
37 return ""
38}
39
40def time-block [block]: nothing -> record {
41 let start = date now
42 let result = do $block
43 let end = date now
44 return {result: $result, elapsed: ($end - $start)}
45}
46
47let hosts = {
48 wolumonde: {
49 type: "nixos",
50 user: "root",
51 addr: "23.88.101.188",
52 },
53 dzwonek: {
54 type: "nixos",
55 user: "root",
56 addr: "94.237.26.47",
57 },
58 volsinii: {
59 type: "nixos",
60 user: "root",
61 addr: "199.71.188.53",
62 },
63 trimounts: {
64 type: "nixos",
65 user: "root",
66 addr: "159.195.58.28",
67 },
68 "dusk@devel.mobi": {
69 type: "home",
70 user: "dusk",
71 addr: "devel.mobi",
72 },
73}
74
75def deploy [hostname: string] {
76 log info $"start deploy host ($hostname)"
77 let hooktitle = $"/($hostname)/deploy"
78 let hostcfg = $hosts | get $hostname
79
80 webhook $hooktitle $"=== deploy for ($hostname): started ===\n\n(sys disks | to text)\n\n(sys mem | to text)"
81
82 def run_step [action: string, block]: nothing -> bool {
83 webhook $"($hooktitle)/($action)" $"=== ($action) ($hostname) started ==="
84 let result = time-block { do $block | tee -e {print -r} | tee {print -r} | complete }
85 let failed = $result.result.exit_code != 0
86 webhook $"($hooktitle)/($action)" $"=== ($action) ($hostname) is done ===\n\ntook ($result.elapsed)\n\nlog: ($result.result | upload-paste)" $result.result.exit_code $failed
87 return $failed
88 }
89
90 let result_dir = mktemp -d | path join "result"
91 let build_cmd = {
92 match $hostcfg.type {
93 "nixos" => {nh os build --no-nom -H $hostname -o $result_dir -- -L --show-trace}
94 "home" => {nh home build --no-nom -c $hostname -o $result_dir -- -L --show-trace}
95 }
96 }
97 if (run_step "build" $build_cmd) {
98 return
99 }
100 let result_link = readlink $result_dir
101
102 let target = $"($hostcfg.user)@($hostcfg.addr)"
103 let copy_cmd = {nix copy -s --to $"ssh://($target)" $result_link}
104 if (run_step "copy to" $copy_cmd) {
105 return
106 }
107
108 let activate_cmd = {
109 let cmd = match $hostcfg.type {
110 "nixos" => $"sudo '($result_link)/bin/switch-to-configuration' 'switch'",
111 "home" => $"($result_link)/activate",
112 }
113 ssh $target $cmd
114 }
115 if (run_step "activate" $activate_cmd) {
116 return
117 }
118
119 webhook $hooktitle $"=== deploy for ($hostname): finished ===" 0 true
120}
121
122def update-inputs []: list<string> -> bool {
123 let inputsText = $in | str join ", "
124 let stashed = try {
125 let stash_result = git stash | complete
126 $stash_result.stdout | str contains "Saved working directory"
127 } catch {
128 false
129 }
130 log info $"trying to update inputs ($inputsText)"
131 let result = nix run .#nvfetcher -- -f $"\(($in | str join '|')\)" | complete
132 let is_ok = ($result.stdout | str contains "Changes:")
133 if $is_ok {
134 let changes_content = $result.stdout | lines | skip until {|line| $line | str contains "Changes:"} | skip 1 | str join "\n"
135 webhook $"/inputs" $"=== updated inputs ===\n\n($changes_content)" $result.exit_code
136 }
137 if $is_ok {
138 # try committing flake updates
139 try {
140 git add _sources
141 git commit -m "chore(nix): update inputs [skip ci]"
142 }
143 } else {
144 try {
145 git restore .
146 }
147 }
148 if $stashed {
149 try {
150 git stash pop
151 }
152 }
153 $is_ok
154}
155
156def main [hostname: string = "wolumonde", --only-deploy (-d)] {
157 webhook "deploy" "=== started deploying ==="
158
159 mut inputs_updated = false
160 if $only_deploy == false {
161 $inputs_updated = ["blog" "limbusart" "nsid-tracker" "tangled"] | update-inputs
162 try {
163 log info "trying to update dns records"
164 nix run ".#dns" -- push
165 } catch { |err|
166 webhook "dns" $"=== error pushing dns ===\n\n($err.msg | to text)" 1
167 }
168 }
169
170 if $hostname == "all" {
171 $hosts | columns | each {|host| deploy $host}
172 } else {
173 deploy $hostname
174 }
175
176 if $inputs_updated {
177 try { git push }
178 }
179}