syncr tool #2
					 19 changed files with 653 additions and 197 deletions
				
			
		|  | @ -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" | ||||
|  |  | |||
							
								
								
									
										22
									
								
								crates/ssh/config
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								crates/ssh/config
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										11
									
								
								crates/ssh/crate.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								crates/ssh/crate.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -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" | ||||
							
								
								
									
										187
									
								
								sync-runner/Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										187
									
								
								sync-runner/Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -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" | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -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<String, crate::source::Source> | ||||
| } | ||||
| 
 | ||||
| impl Config { | ||||
|     pub fn parse(path: &str) -> Result<Config, Box<dyn std::error::Error>> { | ||||
|         Ok(toml::from_str::<Config>(&read_to_string(path)?)?) | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										89
									
								
								sync-runner/src/crates/action.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								sync-runner/src/crates/action.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<Vec<CommandAction>>, | ||||
|     /// link actions
 | ||||
|     #[serde(rename = "link")] | ||||
|     pub links: Option<Vec<LinkAction>>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug)] | ||||
| pub struct CommandAction { | ||||
|     #[serde(default = "whoami::username")] | ||||
|     user: String, | ||||
|     command: String, | ||||
|     pub description: Option<String>, | ||||
|     pub require: Option<Vec<Tag>>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug)] | ||||
| pub struct LinkAction { | ||||
|     pub src: String, | ||||
|     pub dest: String, | ||||
| } | ||||
| 
 | ||||
| impl CommandAction { | ||||
|     pub fn new<S: Into<String>>(command: S, user: S) -> Self { | ||||
|         Self { | ||||
|             command: command.into(), | ||||
|             user: user.into(), | ||||
|             description: None, | ||||
|             require: None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn run(&self) -> Result<i32, Box<dyn std::error::Error>> { | ||||
|         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<dyn std::error::Error>> { | ||||
|         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(()) | ||||
|     } 
 | ||||
| } | ||||
							
								
								
									
										28
									
								
								sync-runner/src/crates/manifest.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								sync-runner/src/crates/manifest.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<Vec<Package>>, | ||||
|     pub actions: Option<Actions>, | ||||
|     pub metadata: Option<Metadata>, | ||||
| } | ||||
| 
 | ||||
| #[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<String>, | ||||
|     pub repository: Option<String>, | ||||
|     pub issues: Option<String>, | ||||
| } | ||||
|  | @ -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<String>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct Crate { | ||||
|     pub pkgs: HashMap<String, Package>, | ||||
|     pub actions: HashMap<String, CrateAction>, | ||||
|     pub super_actions: HashMap<String, CrateAction>, | ||||
|     pub manifest: CrateManifest, | ||||
| } | ||||
| 
 | ||||
| impl Crate { | ||||
|     pub fn from_toml_str(string: &str) -> Result<Self, toml::de::Error> { | ||||
|         Ok(Crate { | ||||
|             manifest: toml::from_str::<manifest::CrateManifest>(string)?, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn install_packages(&self) -> bool { | ||||
|         if let Some(packages) = &self.manifest.packages { | ||||
|             info!("Installing packages..."); | ||||
|             let pkgs: Vec<String> = 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<dyn std::error::Error>> { | ||||
|         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(()) | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										78
									
								
								sync-runner/src/crates/package.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								sync-runner/src/crates/package.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<HashMap<PackageManager, String>>, | ||||
| } | ||||
| 
 | ||||
| impl PackageManager { | ||||
|     pub fn install(&self, packages: Vec<String>) -> Result<bool, Box<dyn std::error::Error>> { | ||||
|         match &self { | ||||
|             PackageManager::Pacman => { | ||||
|                 CommandAction::new( | ||||
|                     format!("pacman -S --noconfirm {}", packages.join(" ")), | ||||
|                     "root".to_string(), | ||||
|                 ) | ||||
|                 .run()?; | ||||
|             } | ||||
|         } | ||||
|         Ok(true) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_available() -> Option<Self> { | ||||
|         Self::from_str(match whoami::distro().as_str() { | ||||
|             "Manjaro Linux" => "pacman", | ||||
|             _ => "unknown" | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn from_str(distro: &str) -> Option<Self> { | ||||
|         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<bool, Box<dyn std::error::Error>> { | ||||
|         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) | ||||
|     } | ||||
| } | ||||
|  | @ -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: `(?<pm>[a-z]+)>(?<action>(?:un)?install+): (?<command>.*)`
 | ||||
| /// example: pacman>install: pacman -Sy %args
 | ||||
| 
 | ||||
| lazy_static! { | ||||
|     static ref PM_REGEX: Regex = | ||||
|         Regex::new(r"(?P<pm>[a-z]+)>(?P<action>(?:un)?install+): (?P<command>.*)").unwrap(); | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| struct PackageManager { | ||||
|     name: String, | ||||
|     command: String, | ||||
|     args: Vec<String>, | ||||
| } | ||||
| 
 | ||||
| impl PackageManager { | ||||
|     fn new(name: &str, command: &str, args: Vec<String>) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             command: command.to_string(), | ||||
|             args, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn install(&self, packages: Vec<String>) -> Result<(), Vec<String>> { | ||||
|         Command::new(&self.command) | ||||
|             .args(&self.args) | ||||
|             .args(packages) | ||||
|             .spawn() | ||||
|             .expect("err"); | ||||
|         Ok(()) | ||||
|     } | ||||
|     pub fn uninstall(&self, packages: Vec<String>) -> Result<(), Vec<String>> { | ||||
|         todo!(); | ||||
|     } | ||||
| } | ||||
| impl FromStr for PackageManager { | ||||
|     type Err = String; | ||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|         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<Self, Self::Err> { | ||||
|         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<PackageManager> { | ||||
|     pm_cfg | ||||
|         .lines() | ||||
|         .map(|s| s.to_string()) | ||||
|         .map(|s| s.parse::<PackageManager>()) | ||||
|         .collect::<Result<Vec<PackageManager>, String>>() | ||||
|         .expect("invalid package manager") | ||||
| } | ||||
|  | @ -19,16 +19,13 @@ mod crates; | |||
| mod logging; | ||||
| mod prelude; | ||||
| mod source; | ||||
| mod tags; | ||||
| 
 | ||||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     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<dyn std::error::Error>> { | |||
|                 trace!("setting config dir as cwd... {config_path}"); | ||||
|                 set_current_dir(config_path)?; | ||||
|             } | ||||
|             let config = | ||||
|                 toml::from_str::<Config>(&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<dyn std::error::Error>> { | |||
|             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:#?}"); | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| use std::path::{Path, PathBuf}; | ||||
| 
 | ||||
| use std::env; | ||||
| 
 | ||||
| pub fn abspath(p: &str) -> Option<String> { | ||||
|     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() | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -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<PathBuf, std::io::Error> { | ||||
|  | @ -48,7 +51,9 @@ impl Git { | |||
|     } | ||||
| 
 | ||||
|     pub fn clone_repository(&self) -> Result<Repository, git2::Error> { | ||||
|         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<Repository, git2::Error> { | ||||
|  | @ -61,7 +66,7 @@ impl Git { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn up_to_date(&self) -> Result<bool, Box<dyn std::error::Error>>{ | ||||
|     fn up_to_date(&self) -> Result<bool, Box<dyn std::error::Error>> { | ||||
|         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) | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<git::Git>, | ||||
|     dir: Option<String>, | ||||
| } | ||||
| 
 | ||||
| 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<dyn std::error::Error>> { | ||||
|         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<Vec<Crate>, Box<dyn std::error::Error>> { | ||||
|     pub fn get_crates(&self) -> Result<Vec<(PathBuf, Crate)>, Box<dyn std::error::Error>> { | ||||
|         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) | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										71
									
								
								sync-runner/src/tags.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								sync-runner/src/tags.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     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<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: Serializer, | ||||
|     { | ||||
|         serializer.serialize_str(&format!("{}:{}", self.category, self.value)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<String> for Tag { | ||||
|     fn into(self) -> String { | ||||
|         self.category + " : " + &self.value | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<String> for Tag { | ||||
|     type Error = std::io::Error; | ||||
|     fn try_from(value: String) -> Result<Self, Self::Error> { | ||||
|         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() | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										7
									
								
								sync.sh
									
										
									
									
									
								
							
							
						
						
									
										7
									
								
								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 | ||||
							
								
								
									
										14
									
								
								syncr.toml
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								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" | ||||
| # [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 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue