From ea70650c455c046e322c9b94c80330011ffd2eca Mon Sep 17 00:00:00 2001 From: Strix Date: Tue, 4 Mar 2025 22:08:08 +0100 Subject: [PATCH] feat: working app --- crates/common/crate.toml | 45 ++++--- crates/ssh/config | 22 ++++ crates/ssh/crate.toml | 11 ++ sync-runner/Cargo.lock | 187 ++++++++++++++++++++++++++++- sync-runner/Cargo.toml | 3 + sync-runner/package_manager.list | 11 -- sync-runner/src/cfg/mod.rs | 8 +- sync-runner/src/crates/action.rs | 89 ++++++++++++++ sync-runner/src/crates/manifest.rs | 28 +++++ sync-runner/src/crates/mod.rs | 86 ++++++++----- sync-runner/src/crates/package.rs | 78 ++++++++++++ sync-runner/src/crates/pm.rs | 90 -------------- sync-runner/src/main.rs | 30 +++-- sync-runner/src/prelude.rs | 6 +- sync-runner/src/source/git.rs | 18 +-- sync-runner/src/source/mod.rs | 46 ++++--- sync-runner/src/tags.rs | 71 +++++++++++ sync.sh | 7 +- syncr.toml | 14 +-- 19 files changed, 653 insertions(+), 197 deletions(-) create mode 100644 crates/ssh/config create mode 100644 crates/ssh/crate.toml delete mode 100644 sync-runner/package_manager.list create mode 100644 sync-runner/src/crates/action.rs create mode 100644 sync-runner/src/crates/manifest.rs create mode 100644 sync-runner/src/crates/package.rs delete mode 100644 sync-runner/src/crates/pm.rs create mode 100644 sync-runner/src/tags.rs diff --git a/crates/common/crate.toml b/crates/common/crate.toml index 45fc314..be4a4a8 100644 --- a/crates/common/crate.toml +++ b/crates/common/crate.toml @@ -1,23 +1,28 @@ -name = "common" -description = "Common set of utilities" +# Crate Manifest for Dotfile Processor -[pkgs] -openssh-server = {} -openssh-client = {} -git = {} -netselect = { distros = ["debian", "ubuntu"] } -reflector = { distros = ["arch"] } +[crate] +name = "dotfile-processor" +description = "A versatile crate for managing dotfiles and system configurations." +author = "Strix" -[actions] -setup_ssh = ["sh", "./scripts/%"] -reflector = { args = ["sh", "./scripts/setup_pm/%"], distro = ["arch"] } -netselect = { args = [ - "sh", - "./scripts/setup_pm/%", -], distro = [ - "debian", - "ubuntu", -] } +# List of packages to be installed +[[packages]] +name = "git" +description = "Version control system" -[super_actions] -pam_wheel = ["sh", "./scripts/%"] +[[packages]] +name = "nvim" +description = "Text editor for creating and editing files" +distro_name_mapping = { pacman = "neovim" } + +[[packages]] +name = "zsh" +description = "Shell designed for interactive use" + +[[actions.command]] +user = "root" +command = "sh ./scripts/pam_wheel.sh" +description = "Pam wheel setup" + +[metadata] +repository = "https://git.saluco.nl/strix/dotfiles" diff --git a/crates/ssh/config b/crates/ssh/config new file mode 100644 index 0000000..8c9646d --- /dev/null +++ b/crates/ssh/config @@ -0,0 +1,22 @@ +## neb servers + +# hydrogen red helix +Host H + Hostname hydrogen.red.helix.saluco.nl + +# argon red helix +Host A + Hostname argon.red.helix.saluco.nl + +# iron red sphere +Host I + Hostname iron.red.sphere.saluco.nl + +## utility servers +Host git + Hostname git.saluco.nl + User git + +Host github + Hostname github.com + User git diff --git a/crates/ssh/crate.toml b/crates/ssh/crate.toml new file mode 100644 index 0000000..d26d45e --- /dev/null +++ b/crates/ssh/crate.toml @@ -0,0 +1,11 @@ +[crate] +name = "ssh" +author = "Strix" +description = "fixes the ssh files" + +[[actions.command]] +command = "ls -lah" + +[[actions.link]] +src = "./config" +dest = "~/.ssh/config" \ No newline at end of file diff --git a/sync-runner/Cargo.lock b/sync-runner/Cargo.lock index 3493d48..0eb3d2a 100644 --- a/sync-runner/Cargo.lock +++ b/sync-runner/Cargo.lock @@ -76,6 +76,12 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + [[package]] name = "cc" version = "1.2.15" @@ -167,6 +173,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + [[package]] name = "digest" version = "0.10.7" @@ -177,13 +194,33 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys 0.3.7", +] + [[package]] name = "dirs" version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", ] [[package]] @@ -503,6 +540,16 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -515,6 +562,15 @@ version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + [[package]] name = "libgit2-sys" version = "0.18.0+1.9.0" @@ -643,6 +699,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.6" @@ -683,6 +748,15 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "resolve-path" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321e5e41b3b192dab6f1e75b9deacb6688b4b8c5e68906a78e8f43e7c2887bb5" +dependencies = [ + "dirs 4.0.0", +] + [[package]] name = "serde" version = "1.0.218" @@ -729,7 +803,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" dependencies = [ - "dirs", + "dirs 5.0.1", ] [[package]] @@ -773,6 +847,7 @@ version = "0.1.0" dependencies = [ "clap", "colored", + "dbus", "execute", "fern", "git2", @@ -781,10 +856,12 @@ dependencies = [ "lazy_static", "log", "regex", + "resolve-path", "serde", "sha2", "shellexpand", "toml", + "whoami", ] [[package]] @@ -921,6 +998,112 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/sync-runner/Cargo.toml b/sync-runner/Cargo.toml index 4eaa739..6ef4146 100644 --- a/sync-runner/Cargo.toml +++ b/sync-runner/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] clap = { version = "4.5.29", features = ["derive"] } colored = "3.0.0" +dbus = "0.9.7" execute = "0.2.13" fern = "0.7.1" git2 = "0.20.0" @@ -14,7 +15,9 @@ hex = "0.4.3" lazy_static = "1.5.0" log = "0.4.26" regex = "1.11.1" +resolve-path = "0.1.0" serde = { version = "1.0.218", features = ["serde_derive"] } sha2 = "0.10.8" shellexpand = "3.1.0" toml = "0.8.20" +whoami = "1.5.2" diff --git a/sync-runner/package_manager.list b/sync-runner/package_manager.list deleted file mode 100644 index e175111..0000000 --- a/sync-runner/package_manager.list +++ /dev/null @@ -1,11 +0,0 @@ -install(pacman): pacman -Sy %args -uninstall(pacman): pacman -Rn %args - -install(apt): apt install %args -uninstall(apt): apt remove %args - -install(dnf): dnf install %args -uninstall(dnf): dnf remove %args - -install(yum): yum install %args -uninstall(yum): yum remove %args diff --git a/sync-runner/src/cfg/mod.rs b/sync-runner/src/cfg/mod.rs index 1f56292..44f4c48 100644 --- a/sync-runner/src/cfg/mod.rs +++ b/sync-runner/src/cfg/mod.rs @@ -1,7 +1,7 @@ #![allow(unused)] -use std::collections::HashMap; +use std::{collections::HashMap, fs::read_to_string}; use serde::{Deserialize, Serialize}; @@ -13,3 +13,9 @@ pub struct Config { pub daemon: daemon::Daemon, pub source: HashMap } + +impl Config { + pub fn parse(path: &str) -> Result> { + Ok(toml::from_str::(&read_to_string(path)?)?) + } +} diff --git a/sync-runner/src/crates/action.rs b/sync-runner/src/crates/action.rs new file mode 100644 index 0000000..5a43b7c --- /dev/null +++ b/sync-runner/src/crates/action.rs @@ -0,0 +1,89 @@ +use std::{ + collections::HashMap, fs, process::{Command, Stdio}, time::Duration +}; + +use dbus::{ + arg::Variant, + blocking::{BlockingSender, Connection}, + Message, +}; +use log::{debug, error, info, trace}; +use resolve_path::PathResolveExt; +use serde::{Deserialize, Serialize}; + +use crate::{tags::Tag}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Actions { + /// command actions + #[serde(rename = "command")] + pub commands: Option>, + /// link actions + #[serde(rename = "link")] + pub links: Option>, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct CommandAction { + #[serde(default = "whoami::username")] + user: String, + command: String, + pub description: Option, + pub require: Option>, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct LinkAction { + pub src: String, + pub dest: String, +} + +impl CommandAction { + pub fn new>(command: S, user: S) -> Self { + Self { + command: command.into(), + user: user.into(), + description: None, + require: None + } + } + + pub fn run(&self) -> Result> { + trace!("running \"{}\" as {}...", &self.command, &self.user); + if self.user != whoami::username() { + Ok(Command::new("sudo") + .arg("-u") + .arg(&self.user) + .arg("--") + .arg("sh") + .arg("-c") + .arg(&self.command) + .status()? + .code() + .unwrap_or(1)) + } else { + Ok(Command::new("sh") + .arg("-c") + .arg(&self.command) + .status()? + .code() + .unwrap_or(1)) + } + } +} + +impl LinkAction { + pub fn link(&self) -> Result<(), Box> { + trace!("linking from {:?} to {:?}...", &self.src.resolve(), &self.dest.resolve()); + if let Ok(existing) = fs::read_link(&self.dest.resolve()) { + if existing == self.src.resolve() { + debug!("link OK"); + return Ok(()); + } else { + return Err("Destination is linked to a different path".into()); + } + } + std::os::unix::fs::symlink(&self.src.resolve(), &self.dest.resolve())?; + Ok(()) + } +} \ No newline at end of file diff --git a/sync-runner/src/crates/manifest.rs b/sync-runner/src/crates/manifest.rs new file mode 100644 index 0000000..70c85fe --- /dev/null +++ b/sync-runner/src/crates/manifest.rs @@ -0,0 +1,28 @@ +use std::fmt::Display; + +use serde::{Deserialize, Serialize}; + +use super::{action::Actions, package::Package}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct CrateManifest { + #[serde(rename = "crate")] + pub crate_info: CrateInfo, + pub packages: Option>, + pub actions: Option, + pub metadata: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct CrateInfo { + pub name: String, + pub description: String, + pub author: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Metadata { + pub homepage: Option, + pub repository: Option, + pub issues: Option, +} \ No newline at end of file diff --git a/sync-runner/src/crates/mod.rs b/sync-runner/src/crates/mod.rs index ec3d5dd..af10023 100644 --- a/sync-runner/src/crates/mod.rs +++ b/sync-runner/src/crates/mod.rs @@ -1,34 +1,62 @@ -use std::collections::HashMap; - +use log::{error, info, warn}; +use manifest::CrateManifest; +use package::PackageManager; use serde::{Deserialize, Serialize}; -mod pm; +pub mod action; +pub mod manifest; +pub mod package; -#[derive(Debug, Serialize, Deserialize)] -pub struct Package { - name: String, -} - -impl Package { - pub fn install(&self) { - todo!() - } - - pub fn uninstall(&self) { - todo!() - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct CrateAction { - pub name: String, - pub command: String, - pub args: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] pub struct Crate { - pub pkgs: HashMap, - pub actions: HashMap, - pub super_actions: HashMap, + pub manifest: CrateManifest, +} + +impl Crate { + pub fn from_toml_str(string: &str) -> Result { + Ok(Crate { + manifest: toml::from_str::(string)?, + }) + } + + pub fn install_packages(&self) -> bool { + if let Some(packages) = &self.manifest.packages { + info!("Installing packages..."); + let pkgs: Vec = packages + .iter() + .map(|p| p.get_correct_package_name()) + .collect(); + info!(target: "item", "pkgs: {}", pkgs.join(", ")); + if let Some(pm) = PackageManager::get_available() { + pm.install(pkgs).is_ok() + } else { + false + } + } else { + false + } + } + + pub fn run_actions(&self) -> Result<(), Box> { + if let Some(actions) = &self.manifest.actions { + if let Some(commands) = &actions.commands { + for command in commands { + info!( + "Running {}...", + &command.description.clone().unwrap_or("action".to_string()) + ); + command.run()?; + } + } + if let Some(links) = &actions.links { + for link in links { + info!("Link {} -> {}...", link.src, link.dest); + if let Err(e) = link.link() { + error!("could not link: {e}"); + continue; + } + } + } + } + Ok(()) + } } diff --git a/sync-runner/src/crates/package.rs b/sync-runner/src/crates/package.rs new file mode 100644 index 0000000..2e11f8d --- /dev/null +++ b/sync-runner/src/crates/package.rs @@ -0,0 +1,78 @@ +use std::{collections::HashMap, io}; + +use log::{error, info}; +use serde::{Deserialize, Serialize}; + +use super::action::CommandAction; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] +pub enum PackageManager { + #[serde(rename = "pacman")] + Pacman, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Package { + pub name: String, + pub distro_name_mapping: Option>, +} + +impl PackageManager { + pub fn install(&self, packages: Vec) -> Result> { + match &self { + PackageManager::Pacman => { + CommandAction::new( + format!("pacman -S --noconfirm {}", packages.join(" ")), + "root".to_string(), + ) + .run()?; + } + } + Ok(true) + } + + pub fn get_available() -> Option { + Self::from_str(match whoami::distro().as_str() { + "Manjaro Linux" => "pacman", + _ => "unknown" + }) + } + + pub fn from_str(distro: &str) -> Option { + match distro { + "pacman" => Some(PackageManager::Pacman), + _ => None, + } + } +} + +impl Package { + pub fn get_correct_package_name(&self) -> String { + if let Some(pm) = PackageManager::get_available() { + if let Some(mappings) = &self.distro_name_mapping { + if let Some(name) = mappings.get(&pm) { + name.to_string() + } else { + self.name.clone() + } + } else { + self.name.clone() + } + } else { + self.name.clone() + } + } + + pub fn install(&self) -> Result> { + if let Some(pm) = PackageManager::get_available() { + pm.install(vec![self.get_correct_package_name()])?; + } else { + error!("no package manager found..."); + return Err(Box::new(io::Error::new( + io::ErrorKind::NotFound, + "package manager not found", + ))); + } + Ok(false) + } +} diff --git a/sync-runner/src/crates/pm.rs b/sync-runner/src/crates/pm.rs deleted file mode 100644 index 692ec15..0000000 --- a/sync-runner/src/crates/pm.rs +++ /dev/null @@ -1,90 +0,0 @@ -use lazy_static::lazy_static; -use regex::Regex; -use std::{process::Command, str::FromStr}; - -const pm_cfg: &str = include_str!("../../package_manager.list"); - -/// regex: `(?[a-z]+)>(?(?:un)?install+): (?.*)` -/// example: pacman>install: pacman -Sy %args - -lazy_static! { - static ref PM_REGEX: Regex = - Regex::new(r"(?P[a-z]+)>(?P(?:un)?install+): (?P.*)").unwrap(); -} - -#[derive(Debug)] -struct PackageManager { - name: String, - command: String, - args: Vec, -} - -impl PackageManager { - fn new(name: &str, command: &str, args: Vec) -> Self { - Self { - name: name.to_string(), - command: command.to_string(), - args, - } - } - - pub fn install(&self, packages: Vec) -> Result<(), Vec> { - Command::new(&self.command) - .args(&self.args) - .args(packages) - .spawn() - .expect("err"); - Ok(()) - } - pub fn uninstall(&self, packages: Vec) -> Result<(), Vec> { - todo!(); - } -} -impl FromStr for PackageManager { - type Err = String; - fn from_str(s: &str) -> Result { - let caps = PM_REGEX.captures(s).ok_or("invalid package manager")?; - let name = caps.name("pm").ok_or("invalid package manager")?.as_str(); - let command = caps - .name("command") - .ok_or("invalid package manager")? - .as_str(); - let args = caps - .name("args") - .ok_or("invalid package manager")? - .as_str() - .split_whitespace() - .map(|s| s.to_string()) - .collect(); - Ok(Self::new(name, command, args)) - } -} -#[derive(Debug)] -struct Package { - name: String, -} -impl FromStr for Package { - type Err = String; - fn from_str(s: &str) -> Result { - let caps = PM_REGEX.captures(s).ok_or("invalid package")?; - let name = caps.name("name").ok_or("invalid package")?.as_str(); - Ok(Self::new(name)) - } -} - -impl Package { - fn new(name: &str) -> Self { - Self { - name: name.to_string(), - } - } -} - -pub fn package_managers() -> Vec { - pm_cfg - .lines() - .map(|s| s.to_string()) - .map(|s| s.parse::()) - .collect::, String>>() - .expect("invalid package manager") -} diff --git a/sync-runner/src/main.rs b/sync-runner/src/main.rs index 539e733..5735a1e 100644 --- a/sync-runner/src/main.rs +++ b/sync-runner/src/main.rs @@ -19,16 +19,13 @@ mod crates; mod logging; mod prelude; mod source; +mod tags; fn main() -> Result<(), Box> { logging::setup_logger()?; - let git_sha1 = String::from_utf8( - command_args!("git", "rev-parse", "HEAD") - .stdout(Stdio::piped()) - .execute_output()? - .stdout, - )?; + info!(target: "item", "user: {}", whoami::username()); + info!(target: "item", "distro: {}", whoami::distro()); let action = Action::parse(); match action { @@ -38,8 +35,7 @@ fn main() -> Result<(), Box> { trace!("setting config dir as cwd... {config_path}"); set_current_dir(config_path)?; } - let config = - toml::from_str::(&read_to_string(abspath("./syncr.toml").unwrap())?)?; + let config = Config::parse(&abspath("./syncr.toml").unwrap())?; info!("syncing \"{}\"...", config.title.bold()); info!("updating sources..."); @@ -63,10 +59,24 @@ fn main() -> Result<(), Box> { for source in available_sources { // cd to source dir source.go_to_dir()?; - for c in source.get_crates()? { - info!("{} pkgs", c.pkgs.len()) + for (mut path, c) in source.get_crates()? { + path.pop(); + set_current_dir(absolute(path)?)?; + info!("Syncing crate: {}...", c.manifest.crate_info.name); + + c.install_packages(); + + if let Err(e) = c.run_actions() { + error!("action failed: {e}"); + } + + set_current_dir(&oldpwd)?; // i hate this but im lazy okay + source.go_to_dir()?; } } + set_current_dir(oldpwd)?; + + info!("Completed sync."); } _ => { println!("{action:#?}"); diff --git a/sync-runner/src/prelude.rs b/sync-runner/src/prelude.rs index 0b1994b..f318ed2 100644 --- a/sync-runner/src/prelude.rs +++ b/sync-runner/src/prelude.rs @@ -1,5 +1,9 @@ +use std::path::{Path, PathBuf}; + +use std::env; + pub fn abspath(p: &str) -> Option { let exp_path = shellexpand::full(p).ok()?; let can_path = std::fs::canonicalize(exp_path.as_ref()).ok()?; can_path.into_os_string().into_string().ok() -} \ No newline at end of file +} diff --git a/sync-runner/src/source/git.rs b/sync-runner/src/source/git.rs index 8ad2389..408011c 100644 --- a/sync-runner/src/source/git.rs +++ b/sync-runner/src/source/git.rs @@ -1,8 +1,11 @@ use std::{ - env::current_dir, fs::{self, create_dir_all, exists}, io::Write, path::{Path, PathBuf} + env::current_dir, + fs::{self, create_dir_all, exists}, + io::Write, + path::{Path, PathBuf}, }; -use git2::Repository; +use git2::{build::RepoBuilder, Repository}; use log::{debug, info, trace, warn}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256, Sha512}; @@ -30,13 +33,13 @@ impl Git { fn branch_hash(&self) -> String { let mut hasher = Sha256::new(); - hasher.update(&self.url); + hasher.update(&self.branch); let hash = hasher.finalize(); hex::encode(hash) } pub fn repository_path_str(&self) -> String { - format!(".data/git/{}{}", self.url_hash(), self.branch_hash()) + format!(".data/git/{}.{}", self.url_hash(), self.branch_hash()) } pub fn repository_path(&self) -> Result { @@ -48,7 +51,9 @@ impl Git { } pub fn clone_repository(&self) -> Result { - Repository::clone_recurse(&self.url, Path::new(&self.repository_path_str())) + RepoBuilder::new() + .branch(&self.branch) + .clone(&self.url, Path::new(&self.repository_path_str())) } pub fn repository(&self) -> Result { @@ -61,7 +66,7 @@ impl Git { } } - fn up_to_date(&self) -> Result>{ + fn up_to_date(&self) -> Result> { debug!("checking repo up to date..."); let repo = self.repository()?; let mut remote = repo.find_remote("origin")?; @@ -72,7 +77,6 @@ impl Git { let fetch_head = repo.refname_to_id(&format!("refs/remotes/origin/{}", self.branch))?; let local_head = repo.refname_to_id(&format!("refs/heads/{}", self.branch))?; - Ok(fetch_head == local_head) } diff --git a/sync-runner/src/source/mod.rs b/sync-runner/src/source/mod.rs index 7ab1dcd..9b43853 100644 --- a/sync-runner/src/source/mod.rs +++ b/sync-runner/src/source/mod.rs @@ -1,4 +1,8 @@ -use std::{env::{current_dir, set_current_dir}, fs::{create_dir_all, read_to_string}, path::PathBuf}; +use std::{ + env::{current_dir, set_current_dir}, + fs::{create_dir_all, exists, read_to_string}, + path::PathBuf, +}; use log::{debug, info, trace}; use serde::{Deserialize, Serialize}; @@ -12,6 +16,7 @@ pub mod git; pub struct Source { interval: u64, git: Option, + dir: Option, } impl Default for Source { @@ -19,6 +24,7 @@ impl Default for Source { Source { interval: 60, git: None, + dir: None, } } } @@ -29,40 +35,44 @@ impl Source { trace!("checking git..."); return git.ensure().is_ok(); } + if let Some(dir) = &self.dir { + return exists(dir).is_ok(); + } false } pub fn go_to_dir(&self) -> Result<(), Box> { if let Some(git) = &self.git { if PathBuf::from(git.repository_path_str()) == current_dir()? { - return Ok(()) + return Ok(()); } let dir = git.ensure()?.repository_path()?; - trace!("setting git dir as cwd... ({}@{}, {})", git.url, git.branch, dir.display()); + trace!( + "setting git dir as cwd... ({}@{}, {})", + git.url, + git.branch, + dir.display() + ); set_current_dir(dir)?; } + if let Some(path) = &self.dir { + set_current_dir(path)?; + } Ok(()) } - pub fn get_crates(&self) -> Result, Box> { + pub fn get_crates(&self) -> Result, Box> { let mut crates = vec![]; - if let Some(git) = &self.git { - trace!("getting crates from git..."); - debug!("{}", current_dir()?.display()); - - // get crates (read dir, crates/*/crate.toml) - for crate_file in glob::glob("crates/*/crate.toml").expect("err") { - debug!("{crate_file:#?}"); - match crate_file { - Ok(cd) =>{ - debug!("{}", cd.display()); - crates.push(toml::from_str(&read_to_string(cd)?)?) - }, - _ => continue + // get crates (read dir, crates/*/crate.toml) + for crate_file in glob::glob("crates/*/crate.toml").expect("err") { + match crate_file { + Ok(cd) => { + debug!("found {}", cd.display()); + crates.push((cd.clone(), Crate::from_toml_str(&read_to_string(cd)?)?)) } + _ => continue, } } - debug!("{:#?}", crates); Ok(crates) } } diff --git a/sync-runner/src/tags.rs b/sync-runner/src/tags.rs new file mode 100644 index 0000000..4be8d32 --- /dev/null +++ b/sync-runner/src/tags.rs @@ -0,0 +1,71 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug)] +pub struct Tag { + pub category: String, + pub value: String +} + +impl<'de> Deserialize<'de> for Tag { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + // First, deserialize the string + let s: String = Deserialize::deserialize(deserializer)?; + + // Split the string into category and value by ":" + let parts: Vec<&str> = s.splitn(2, ':').collect(); + if parts.len() != 2 { + return Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"a string in the format 'category:value'", + )); + } + + // Return a Tag with the split parts + Ok(Tag { + category: parts[0].to_string(), + value: parts[1].to_string(), + }) + } +} + +impl Serialize for Tag { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{}:{}", self.category, self.value)) + } +} + +impl Into for Tag { + fn into(self) -> String { + self.category + " : " + &self.value + } +} + +impl TryFrom for Tag { + type Error = std::io::Error; + fn try_from(value: String) -> Result { + let parts: Vec<&str> = value.split(':').collect(); + if parts.len() != 2 { + Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "could not match string to tag")) + } else { + Ok(Self { + category: parts[0].into(), + value: parts[1].into() + }) + } + } +} + +impl Tag { + pub fn distro() -> Tag { + Tag { + category: "distro".into(), + value: whoami::distro() + } + } +} \ No newline at end of file diff --git a/sync.sh b/sync.sh index fd8e62d..e0d44c3 100644 --- a/sync.sh +++ b/sync.sh @@ -1 +1,6 @@ -node sync-runner/index.js +if ! [ -f /tmp/sync-runner ]; then + curl -O /tmp/sync-runner https://git.saluco.nl/repos/strix/releases/download/latest/sync-runner + chmod +x /tmp/sync-runner +fi + +/tmp/sync-runner \ No newline at end of file diff --git a/syncr.toml b/syncr.toml index 13e4343..d012160 100644 --- a/syncr.toml +++ b/syncr.toml @@ -7,11 +7,11 @@ title = "strix's syncr config" # unit = minutes interval = 60 -[source.personal.git] # default is the uid -url = "https://git.saluco.nl/strix/dotfiles.git" -crate_dir = "./crates" # default -cfg_toml = "./syncr.toml" # default +[source.personal] +dir = "." -[source.work.git] -url = "https://git.saluco.nl/dotfiles.git" -branch = "work" \ No newline at end of file +# [source.personal.git] # default is the uid +# url = "https://git.saluco.nl/strix/dotfiles.git" +# branch = "syncr" +# crate_dir = "./crates" # default +# cfg_toml = "./syncr.toml" # default