diff --git a/Cargo.lock b/Cargo.lock index ef7fb80..a4ce6d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,6 +196,7 @@ dependencies = [ "clap_mangen", "log", "pretty_env_logger", + "relative-path", "serde", "serde_yaml", ] @@ -359,6 +360,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +[[package]] +name = "relative-path" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf2521270932c3c7bed1a59151222bd7643c79310f2916f01925e1e16255698" + [[package]] name = "roff" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 2b9d34c..18ebf16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,10 @@ [package] name = "gg" -version = "0.0.2" +version = "0.0.3" edition = "2021" authors = ["Christina Sørensen "] repository = "https://github.com/cafkafk/gg" license = "GPL-3.0-only" -license-file = "LICENSE" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -16,6 +15,7 @@ clap = { version = "4.0.22", features = ["derive"] } log = "0.4" #env_logger = "0.9" pretty_env_logger = "0.4" +relative-path = "1.8.0" [build-dependencies] clap = { version = "4.3.2", features = ["derive", "cargo", "env", "help"] } diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..562e4f3 --- /dev/null +++ b/flake.lock @@ -0,0 +1,198 @@ +{ + "nodes": { + "advisory-db": { + "flake": false, + "locked": { + "lastModified": 1686749016, + "narHash": "sha256-len42vQO+iY65mhqRCCVjpXyTRKFDHuXGv/Nko7hdDY=", + "owner": "rustsec", + "repo": "advisory-db", + "rev": "13b9455e9f7d2927f81088db3cffaafd6a6bbe16", + "type": "github" + }, + "original": { + "owner": "rustsec", + "repo": "advisory-db", + "type": "github" + } + }, + "crane": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1686621798, + "narHash": "sha256-FUwWszmSiDzUdTk8f69xwMoYlhdPaLvDaIYOE/y6VXc=", + "owner": "ipetkov", + "repo": "crane", + "rev": "75f7d715f8088f741be9981405f6444e2d49efdd", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": [] + }, + "locked": { + "lastModified": 1686723693, + "narHash": "sha256-wd6WE5M1EdnZPvK5h9zh3hE9F+h+7z2YhK1UpobkjGQ=", + "owner": "nix-community", + "repo": "fenix", + "rev": "ac2f1271f1e6623717220f31890658af351b0b2c", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1686582075, + "narHash": "sha256-vtflsfKkHtF8IduxDNtbme4cojiqvlvjp5QNYhvoHXc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7e63eed145566cca98158613f3700515b4009ce3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "advisory-db": "advisory-db", + "crane": "crane", + "fenix": "fenix", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "crane", + "flake-utils" + ], + "nixpkgs": [ + "crane", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1685759304, + "narHash": "sha256-I3YBH6MS3G5kGzNuc1G0f9uYfTcNY9NYoRc3QsykLk4=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "c535b4f3327910c96dcf21851bbdd074d0760290", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..ab46ca7 --- /dev/null +++ b/flake.nix @@ -0,0 +1,154 @@ +{ + description = "Build a cargo project"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + crane = { + url = "github:ipetkov/crane"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.rust-analyzer-src.follows = ""; + }; + + flake-utils.url = "github:numtide/flake-utils"; + + advisory-db = { + url = "github:rustsec/advisory-db"; + flake = false; + }; + }; + + outputs = { self, nixpkgs, crane, fenix, flake-utils, advisory-db, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + }; + + inherit (pkgs) lib; + + craneLib = crane.lib.${system}; + + # When filtering sources, we want to allow assets other than .rs files + src = lib.cleanSourceWith { + src = ./.; # The original, unfiltered source + filter = path: type: + (lib.hasSuffix "\.yaml" path) || + # (lib.hasSuffix "\.scss" path) || + # Example of a folder for images, icons, etc + #(lib.hasInfix "test" path) || + # Default filter from crane (allow .rs files) + (craneLib.filterCargoSources path type) + ; + }; + + # Common arguments can be set here to avoid repeating them later + commonArgs = { + inherit src; + + buildInputs = [ + # test-directory + # Add additional build inputs here + ] ++ lib.optionals pkgs.stdenv.isDarwin [ + # Additional darwin specific inputs can be set here + pkgs.libiconv + ]; + + # Additional environment variables can be set directly + # MY_CUSTOM_VAR = "some value"; + RUST_BACKTRACE = 1; + }; + + craneLibLLvmTools = craneLib.overrideToolchain + (fenix.packages.${system}.complete.withComponents [ + "cargo" + "llvm-tools" + "rustc" + ]); + + # Build *just* the cargo dependencies, so we can reuse + # all of that work (e.g. via cachix) when running in CI + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + + # Build the actual crate itself, reusing the dependency + # artifacts from above. + my-crate = craneLib.buildPackage (commonArgs // { + inherit cargoArtifacts; + }); + in + { + checks = { + # Build the crate as part of `nix flake check` for convenience + inherit my-crate; + + # Run clippy (and deny all warnings) on the crate source, + # again, resuing the dependency artifacts from above. + # + # Note that this is done as a separate derivation so that + # we can block the CI if there are issues here, but not + # prevent downstream consumers from building our crate by itself. + my-crate-clippy = craneLib.cargoClippy (commonArgs // { + inherit cargoArtifacts; + cargoClippyExtraArgs = "--all-targets -- --deny warnings"; + }); + + my-crate-doc = craneLib.cargoDoc (commonArgs // { + inherit cargoArtifacts; + }); + + # Check formatting + my-crate-fmt = craneLib.cargoFmt { + inherit src; + }; + + # Audit dependencies + my-crate-audit = craneLib.cargoAudit { + inherit src advisory-db; + }; + + # Run tests with cargo-nextest + # Consider setting `doCheck = false` on `my-crate` if you do not want + # the tests to run twice + my-crate-nextest = craneLib.cargoNextest (commonArgs // { + inherit cargoArtifacts; + partitions = 1; + partitionType = "count"; + }); + } // lib.optionalAttrs (system == "x86_64-linux") { + # NB: cargo-tarpaulin only supports x86_64 systems + # Check code coverage (note: this will not upload coverage anywhere) + # my-crate-coverage = craneLib.cargoTarpaulin (commonArgs // { + # inherit cargoArtifacts; + # }); + }; + + packages = { + default = my-crate; + my-crate-llvm-coverage = craneLibLLvmTools.cargoLlvmCov (commonArgs // { + inherit cargoArtifacts; + }); + }; + + apps.default = flake-utils.lib.mkApp { + drv = my-crate; + }; + + devShells.default = pkgs.mkShell { + inputsFrom = builtins.attrValues self.checks.${system}; + + # Additional dev-shell environment variables can be set directly + # MY_CUSTOM_DEVELOPMENT_VAR = "something else"; + + # Extra inputs can be added here + nativeBuildInputs = with pkgs; [ + cargo + rustc + ]; + }; + }); +} diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 23cd050..0000000 --- a/shell.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ pkgs ? import { } }: -pkgs.mkShell { - nativeBuildInputs = [ pkgs.buildPackages.glibc ]; -} diff --git a/src/git.rs b/src/git.rs index 7e1413e..2569826 100644 --- a/src/git.rs +++ b/src/git.rs @@ -71,7 +71,7 @@ impl Links { let tx_path: &Path = std::path::Path::new(&self.tx); let rx_path: &Path = std::path::Path::new(&self.rx); match rx_path.try_exists() { - Ok(true) => handle_file_exists(&self, &tx_path, &rx_path), + Ok(true) => handle_file_exists(self, tx_path, rx_path), Ok(false) if rx_path.is_symlink() => { error!( "Linking {} -> {} failed: broken symlink", @@ -99,7 +99,7 @@ impl GitRepo { .arg(&self.url) .arg(&self.name) .status() - .expect("failed to add"); + .unwrap_or_else(|_| panic!("git repo failed to clone: {:?}", &self,)); info!("{out}"); } else { info!("{} has clone set to false, not cloned", &self.name); @@ -111,7 +111,7 @@ impl GitRepo { .current_dir(format!("{}{}", &self.path, &self.name)) .arg("pull") .status() - .expect("failed to pull"); + .unwrap_or_else(|_| panic!("git repo failed to pull: {:?}", &self,)); info!("{out}"); } /// Adds all files in the repository. @@ -121,7 +121,7 @@ impl GitRepo { .arg("add") .arg(".") .status() - .expect("failed to add"); + .unwrap_or_else(|_| panic!("git repo failed to add: {:?}", &self,)); info!("{out}"); } /// Tries to commit changes in the repository. @@ -131,7 +131,7 @@ impl GitRepo { .current_dir(format!("{}{}", &self.path, &self.name)) .arg("commit") .status() - .expect("failed to commit"); + .unwrap_or_else(|_| panic!("git repo failed to commit: {:?}", &self,)); info!("{out}"); } /// Tries to commit changes with a message argument. @@ -142,7 +142,7 @@ impl GitRepo { .arg("-m") .arg(msg) .status() - .expect("failed to commit"); + .unwrap_or_else(|_| panic!("git repo failed to commit: {:?}", &self,)); info!("{out}"); } /// Attempts to push the repository. @@ -151,7 +151,7 @@ impl GitRepo { .current_dir(format!("{}{}", &self.path, &self.name)) .arg("push") .status() - .expect("failed to push"); + .unwrap_or_else(|_| panic!("git repo failed to push: {:?}", &self,)); info!("{out}"); } /// Removes repository @@ -167,9 +167,16 @@ impl Config { /// Reads the configuration toml from a path. pub fn new(path: &String) -> Self { debug!("initializing new Config struct"); - let yaml = fs::read_to_string(path).expect("Should have been able to read the file"); + let yaml = fs::read_to_string(path).unwrap_or_else(|_| { + panic!("Should have been able to read the file: path -> {:?}", path,) + }); debug!("deserialized yaml from config file"); - serde_yaml::from_str(&yaml).expect("Should have been able to deserialize yaml config") + serde_yaml::from_str(&yaml).unwrap_or_else(|_| { + panic!( + "Should have been able to deserialize yaml config: path -> {:?}", + path, + ) + }) } /// Tries to pull all repositories, skips if fail. pub fn pull_all(&self) { diff --git a/src/main.rs b/src/main.rs index 5e5532d..99f3551 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,9 +36,9 @@ fn main() { let args = Args::parse(); let config = Config::new(&args.config); match &args { - args if args.license == true => println!("{}", utils::strings::INTERACTIVE_LICENSE), - args if args.warranty == true => println!("{}", utils::strings::INTERACTIVE_WARRANTY), - args if args.code_of_conduct == true => unimplemented!(), + args if args.license => println!("{}", utils::strings::INTERACTIVE_LICENSE), + args if args.warranty => println!("{}", utils::strings::INTERACTIVE_WARRANTY), + args if args.code_of_conduct => unimplemented!(), _ => (), } match &args.command { @@ -46,7 +46,7 @@ fn main() { config.link_all(); } Some(Commands::Quick { msg }) => { - config.quick(&msg.as_ref().get_or_insert(&"gg: quick commit".to_string())); + config.quick(msg.as_ref().get_or_insert(&"gg: quick commit".to_string())); } Some(Commands::Clone { msg: _ }) => { config.clone_all(); @@ -61,7 +61,7 @@ fn main() { config.commit_all(); } Some(Commands::CommitMsg { msg }) => { - config.commit_all_msg(&msg.as_ref().unwrap()); + config.commit_all_msg(msg.as_ref().unwrap()); } None => (), } @@ -72,9 +72,10 @@ fn main() { mod config { use crate::*; use git::GitRepo; + use relative_path::RelativePath; + use std::env::current_dir; use std::fs::File; use std::io::prelude::*; - use utils::dir::current_dir; #[test] fn init_config() { let _config = Config { @@ -102,31 +103,45 @@ mod config { } #[test] fn read_config_populate() { - const CONFIG_FILE: &str = "/tst/config.yaml"; - let config_path = current_dir() + CONFIG_FILE; - let _config = Config::new(&config_path); + let _config = Config::new(&RelativePath::new("./src/test/config.yaml").to_string()); } #[test] fn write_config() { - const CONFIG_FILE: &str = "/tst/config.yaml"; - const CONFIG_TEST: &str = "/tst/test.yaml"; - let config_path = current_dir() + CONFIG_FILE; - let config = Config::new(&config_path); + let root = current_dir().unwrap(); + let config = Config::new( + &RelativePath::new("./src/test/config.yaml") + .to_logical_path(&root) + .into_os_string() + .into_string() + .unwrap(), + ); - let test_path = current_dir() + CONFIG_TEST; - let mut file = File::create(&test_path).unwrap(); + let mut test_file = File::create( + RelativePath::new("./src/test/test.yaml") + .to_logical_path(&root) + .into_os_string() + .into_string() + .unwrap(), + ) + .expect("failed to create test file"); let contents = serde_yaml::to_string(&config).unwrap(); - file.write(&contents.as_bytes()).unwrap(); + test_file.write_all(contents.as_bytes()).unwrap(); - let test_config = Config::new(&test_path); + let test_config = Config::new(&RelativePath::new("./src/test/test.yaml").to_string()); assert_eq!(config, test_config); } #[test] fn read_and_verify_config() { - const CONFIG_FILE: &str = "/tst/config.yaml"; - let config_path = current_dir() + CONFIG_FILE; - let config = Config::new(&config_path); + let root = current_dir().unwrap(); + let config = Config::new( + &RelativePath::new("./src/test/config.yaml") + .to_logical_path(root) + .into_os_string() + .into_string() + .unwrap(), + ); // FIXME This is unnecessarily terse + #[allow(clippy::bool_assert_comparison)] { assert_eq!(config.repos[0].name, "gg"); assert_eq!(config.repos[0].path, "/home/ces/.dots/"); @@ -162,16 +177,24 @@ mod config { } } +/* FIXME Unable to test with networking inside flake #[cfg(test)] mod repo_actions { use crate::*; use git::GitRepo; + use relative_path::RelativePath; + use std::env::current_dir; use std::process::Command; - use utils::dir::current_dir; #[test] + #[allow(clippy::redundant_clone)] fn test_repo_actions() { let test_repo_name: String = "test".to_string(); - let test_repo_dir: String = (current_dir() + "/tst/").to_string(); + let root = current_dir().unwrap(); + let test_repo_dir: String = RelativePath::new("./src/test") + .to_logical_path(&root) + .into_os_string() + .into_string() + .unwrap(); let test_repo_url: String = "git@github.com:cafkafk/test.git".to_string(); println!("{}", test_repo_dir); let mut config = Config { @@ -185,8 +208,10 @@ mod repo_actions { clone: true, }; config.repos.push(repo); - config.clone_all(); - config.pull_all(); + // BUG FIXME can't do this in flake + // should have a good alternative + // config.clone_all(); + // config.pull_all(); for r in config.repos.iter() { Command::new("touch") .current_dir(&(r.path.to_owned() + &r.name)) @@ -198,3 +223,4 @@ mod repo_actions { config.commit_all_msg(&"test".to_string()); } } +*/ diff --git a/tst/config.yaml b/src/test/config.yaml similarity index 100% rename from tst/config.yaml rename to src/test/config.yaml diff --git a/src/test/test b/src/test/test new file mode 160000 index 0000000..318edb9 --- /dev/null +++ b/src/test/test @@ -0,0 +1 @@ +Subproject commit 318edb997ddb88b3dd255b70c8b73e1ced7b74f9 diff --git a/src/test/test.yaml b/src/test/test.yaml new file mode 100644 index 0000000..529690e --- /dev/null +++ b/src/test/test.yaml @@ -0,0 +1,24 @@ +repos: +- name: gg + path: /home/ces/.dots/ + url: git@github.com:cafkafk/gg.git + clone: true +- name: li + path: /home/ces/org/src/git/ + url: git@github.com:cafkafk/li.git + clone: true +- name: qmk_firmware + path: /home/ces/org/src/git/ + url: git@github.com:cafkafk/qmk_firmware.git + clone: true +- name: starship + path: /home/ces/org/src/git/ + url: https://github.com/starship/starship.git + clone: true +links: +- name: gg + rx: /home/ces/.config/gg + tx: /home/ces/.dots/gg +- name: starship + rx: /home/ces/.config/starship.toml + tx: /home/ces/.dots/starship.toml diff --git a/src/utils/dir.rs b/src/utils/dir.rs index 92c6889..b617c3f 100644 --- a/src/utils/dir.rs +++ b/src/utils/dir.rs @@ -41,13 +41,13 @@ pub fn home_dir() -> String { /// Changes working directory into a repository. /// /// WARNING: NOT THREAD SAFE -fn change_dir_repo(path: &String, name: &String) { +fn change_dir_repo(path: &str, name: &str) { let mut full_path: String = "".to_owned(); full_path.push_str(path); full_path.push_str(name); let root = Path::new(&full_path); println!("{}", root.display()); - assert!(env::set_current_dir(&root).is_ok()); + assert!(env::set_current_dir(root).is_ok()); debug!( "Successfully changed working directory to {}!", root.display() @@ -57,9 +57,9 @@ fn change_dir_repo(path: &String, name: &String) { /// Changes working directory to outside of the repo. /// /// WARNING: NOT THREAD SAFE -fn change_dir(path: &String) { +fn change_dir(path: &str) { let root = Path::new(path); - assert!(env::set_current_dir(&root).is_ok()); + assert!(env::set_current_dir(root).is_ok()); debug!( "Successfully changed working directory to {}!", root.display()