Skip to content

Instantly share code, notes, and snippets.

@orzklv
Last active August 4, 2026 10:33
Show Gist options
  • Select an option

  • Save orzklv/37cbced405a437e1934a2b07254824e3 to your computer and use it in GitHub Desktop.

Select an option

Save orzklv/37cbced405a437e1934a2b07254824e3 to your computer and use it in GitHub Desktop.
nix-shell packaged rust script for automatic deployment of every server configuration
#!/usr/bin/env nix-shell
//! ```cargo
//! [dependencies]
//! toml = "1"
//! thiserror = "2"
//! serde = {features = ["derive"], version = "1"}
//! ```
/*
#!nix-shell -i rust-script -p rustc -p rust-script -p cargo
*/
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::process::{Command, Output, Stdio};
use thiserror::Error;
#[derive(Error, Debug)]
enum Error {
#[error("command pissing went wrong: {0}")]
IO(#[from] std::io::Error),
#[error("seems like a required file is missing: {0}")]
MissingFile(String),
#[error("failed while parsing server list: {0}")]
Parse(#[from] toml::de::Error),
}
type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Serialize, Deserialize)]
struct Server {
location: String,
hostname: String,
repition: u8,
}
impl From<&Server> for Vec<Result<Output>> {
fn from(value: &Server) -> Self {
(1..value.repition)
.map(|iter| {
Command::new("nixos-rebuild")
.arg("switch")
.arg("--flake")
.arg(format!(
".#{}",
value.hostname.replace("$", &iter.to_string())
))
.arg("--target-host")
.arg(value.location.replace("$", &iter.to_string()))
.arg("--sudo")
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.output()
.map_err(Error::IO)
})
.collect::<Vec<Result<Output>>>()
}
}
#[derive(Serialize, Deserialize)]
struct Servers {
instance: Vec<Server>,
}
fn exists(path: &str) -> Result<()> {
Path::new(path)
.exists()
.then_some(())
.ok_or(Error::MissingFile(path.to_string()))
}
fn execute(servers: Vec<Server>) -> Result<()> {
servers
.iter()
.flat_map(|server| -> Vec<Result<Output>> { server.into() })
.collect::<Result<Vec<Output>>>()
.map(|_ov| ())
}
fn script() -> Result<()> {
exists("./flake.nix")?;
exists("./servers.toml")?;
std::fs::read_to_string("./servers.toml")
.map_err(Error::IO)
.and_then(|string| toml::from_str::<Servers>(&string).map_err(Error::Parse))
.map(|hosts| hosts.instance)
.map(execute)
.and_then(|result| result)
}
fn main() -> Result<()> {
script().or_else(|e| {
eprintln!("error: {}", e);
std::process::exit(1)
})
}
[[instance]]
location = "ns$.example.uz"
hostname = "example-ns$"
repition = 2
[[instance]]
location = "ns$.another.uz"
hostname = "another-ns$"
repition = 2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment