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 "dusk@devel.mobi": {
64 type: "home",
65 user: "dusk",
66 addr: "devel.mobi",
67 },
68}
69
70def deploy [hostname: string] {
71 log info $"start deploy host ($hostname)"
72 let hooktitle = $"/($hostname)/deploy"
73 let hostcfg = $hosts | get $hostname
74
75 webhook $hooktitle $"=== deploy for ($hostname): started ===\n\n(sys disks | to text)\n\n(sys mem | to text)"
76
77 def run_step [action: string, block]: nothing -> bool {
78 webhook $"($hooktitle)/($action)" $"=== ($action) ($hostname) started ==="
79 let result = time-block { do $block | tee -e {print -r} | tee {print -r} | complete }
80 let failed = $result.result.exit_code != 0
81 webhook $"($hooktitle)/($action)" $"=== ($action) ($hostname) is done ===\n\ntook ($result.elapsed)\n\nlog: ($result.result | upload-paste)" $result.result.exit_code $failed
82 return $failed
83 }
84
85 let result_dir = mktemp -d | path join "result"
86 let build_cmd = {
87 match $hostcfg.type {
88 "nixos" => {nh os build --no-nom -H $hostname -o $result_dir -- -L --show-trace}
89 "home" => {nh home build --no-nom -c $hostname -o $result_dir -- -L --show-trace}
90 }
91 }
92 if (run_step "build" $build_cmd) {
93 return
94 }
95 let result_link = readlink $result_dir
96
97 let target = $"($hostcfg.user)@($hostcfg.addr)"
98 let copy_cmd = {nix copy -s --to $"ssh://($target)" $result_link}
99 if (run_step "copy to" $copy_cmd) {
100 return
101 }
102
103 let activate_cmd = {
104 let cmd = match $hostcfg.type {
105 "nixos" => $"sudo '($result_link)/bin/switch-to-configuration' 'switch'",
106 "home" => $"($result_link)/activate",
107 }
108 ssh $target $cmd
109 }
110 if (run_step "activate" $activate_cmd) {
111 return
112 }
113
114 webhook $hooktitle $"=== deploy for ($hostname): finished ===" 0 true
115}
116
117def update-inputs []: list<string> -> bool {
118 let inputsText = $in | str join ", "
119 let stashed = try {
120 let stash_result = git stash | complete
121 $stash_result.stdout | str contains "Saved working directory"
122 } catch {
123 false
124 }
125 log info $"trying to update inputs ($inputsText)"
126 let result = nix run .#nvfetcher -- -f $"\(($in | str join '|')\)" | complete
127 let is_ok = ($result.stdout | str contains "Changes:")
128 if $is_ok {
129 let changes_content = $result.stdout | lines | skip until {|line| $line | str contains "Changes:"} | skip 1 | str join "\n"
130 webhook $"/inputs" $"=== updated inputs ===\n\n($changes_content)" $result.exit_code
131 }
132 if $is_ok {
133 # try committing flake updates
134 try {
135 git add _sources
136 git commit -m "chore(nix): update inputs [skip ci]"
137 }
138 } else {
139 try {
140 git restore .
141 }
142 }
143 if $stashed {
144 try {
145 git stash pop
146 }
147 }
148 $is_ok
149}
150
151def main [hostname: string = "wolumonde", --only-deploy (-d)] {
152 webhook "deploy" "=== started deploying ==="
153
154 mut inputs_updated = false
155 if $only_deploy == false {
156 $inputs_updated = ["blog" "limbusart" "nsid-tracker" "tangled"] | update-inputs
157 try {
158 log info "trying to update dns records"
159 nix run ".#dns" -- push
160 } catch { |err|
161 webhook "dns" $"=== error pushing dns ===\n\n($err.msg | to text)" 1
162 }
163 }
164
165 if $hostname == "all" {
166 $hosts | columns | each {|host| deploy $host}
167 } else {
168 deploy $hostname
169 }
170
171 if $inputs_updated {
172 try { git push }
173 }
174}