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