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