Rewrite in rust
This commit is contained in:
parent
6199802e26
commit
a7d4ac9c6d
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
|||||||
.idea/
|
/target
|
||||||
|
205
Cargo.lock
generated
Normal file
205
Cargo.lock
generated
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gitm-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[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-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "gitm-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Arsen Musayelyan <moussaelianarsen@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
toml = "0.5"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
serde = "1.0"
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.8"
|
@ -1,11 +1,11 @@
|
|||||||
# Gitm
|
# Gitm
|
||||||
Automatic git mirroring script.
|
Automatic git mirroring program.
|
||||||
|
|
||||||
### How it works
|
### How it works
|
||||||
This is a simple script that intercepts commands like `git init` and `git push` and automatically configures and pushes to many different remotes.
|
This is a simple program that intercepts commands like `git init` and `git push` and automatically configures and pushes to many different remotes.
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
To use this script, create a file called `.gitm.toml` and populate it with repositories like so:
|
To use this program, create a file called `.gitm.toml` and populate it with repositories like so:
|
||||||
```toml
|
```toml
|
||||||
[repos]
|
[repos]
|
||||||
origin = "https://gitea.arsenm.dev/Arsen6331/gitm.git"
|
origin = "https://gitea.arsenm.dev/Arsen6331/gitm.git"
|
||||||
|
58
gitm.rb
58
gitm.rb
@ -1,58 +0,0 @@
|
|||||||
#!/usr/bin/ruby
|
|
||||||
|
|
||||||
# Gitm. Automatic git mirroring script.
|
|
||||||
#
|
|
||||||
# Copyright (C) 2021 Arsen Musayelyan <arsen@arsenm.dev>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU General Public License as published by the Free Software
|
|
||||||
# Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License along with
|
|
||||||
# this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
require 'toml'
|
|
||||||
require 'colorize'
|
|
||||||
|
|
||||||
args = ARGV
|
|
||||||
cfgName = ".gitm.toml"
|
|
||||||
|
|
||||||
def log(str)
|
|
||||||
txt = "[gitm]".cyan
|
|
||||||
puts "#{txt} #{str}"
|
|
||||||
end
|
|
||||||
|
|
||||||
File.open(cfgName, "a") {} unless File.exists? cfgName
|
|
||||||
|
|
||||||
cfgData = TOML::Parser.new(File.read(cfgName)).parsed
|
|
||||||
repos = cfgData["repos"] || {}
|
|
||||||
branch = cfgData["defaultBranch"] || "master"
|
|
||||||
if repos["origin"].nil?
|
|
||||||
log "Error: origin repo required"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if repos.length < 1
|
|
||||||
puts "Please add repos to the #{cfgName} file"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
case args[0]
|
|
||||||
when "push"
|
|
||||||
log "Intercepted push command"
|
|
||||||
repos.each { |name, _| system "git push #{name} #{branch}", *args[1...] }
|
|
||||||
when "init"
|
|
||||||
log "Intercepted init command"
|
|
||||||
system "git init", *args[1...]
|
|
||||||
repos.each { |name, repo| system "git remote add #{name} #{repo}" }
|
|
||||||
system "git fetch origin"
|
|
||||||
system "git checkout master"
|
|
||||||
else
|
|
||||||
system "git", *args
|
|
||||||
end
|
|
149
src/main.rs
Normal file
149
src/main.rs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
use std::{collections::HashMap, env, process::{Command, Child, exit}};
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
use std::fs;
|
||||||
|
use toml;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
// TOML config filename
|
||||||
|
const CFG_NAME: &str = ".gitm.toml";
|
||||||
|
|
||||||
|
// Config struct stores decoded TOML from config
|
||||||
|
#[derive(Deserialize, Default)]
|
||||||
|
struct Config {
|
||||||
|
repos: HashMap<String, String>,
|
||||||
|
options: Option<Options>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options struct stores the options table in the TOML config
|
||||||
|
#[derive(Deserialize, Default)]
|
||||||
|
struct Options {
|
||||||
|
branch: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Create new logger with level Info and no timestamp
|
||||||
|
env_logger::builder()
|
||||||
|
.filter_level(log::LevelFilter::Info)
|
||||||
|
.format_timestamp(None)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
// Get config contents
|
||||||
|
let cfg_contents = fs::read_to_string(CFG_NAME).log_err("Error reading file");
|
||||||
|
|
||||||
|
// Decode config contents
|
||||||
|
let config: Config = toml::from_str(&cfg_contents).unwrap_or_default();
|
||||||
|
|
||||||
|
// If no repos provided, error and exit
|
||||||
|
if config.repos.len() < 1 {
|
||||||
|
error!("Please add repos to the {} file", CFG_NAME);
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If origin repo is not defined, error and exit
|
||||||
|
if config.repos.get("origin").is_none() {
|
||||||
|
error!("Origin repo required in {} file", CFG_NAME);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect arguments into vector
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
|
// If no arguments provided
|
||||||
|
if args.len() < 2 {
|
||||||
|
// Run git --help
|
||||||
|
let mut proc = Command::new("git").arg("--help").spawn()
|
||||||
|
.log_err("Error running git command");
|
||||||
|
exit_if_code_nonzero(&mut proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure options exists in config, otherwise set to default
|
||||||
|
let options = config.options.unwrap_or_default();
|
||||||
|
// Ensure branch exists in options, otherwise set to "master"
|
||||||
|
let branch = options.branch.unwrap_or("master".to_string());
|
||||||
|
match args[1].as_str() {
|
||||||
|
"push" => {
|
||||||
|
info!("Intercepted push command");
|
||||||
|
// For every repo in config
|
||||||
|
for (name, _) in config.repos {
|
||||||
|
// Run git push with applicable arguments
|
||||||
|
let mut proc = Command::new("git")
|
||||||
|
.args(&["push", &name, &branch])
|
||||||
|
.args(&args[2..])
|
||||||
|
.spawn()
|
||||||
|
.log_err("Error running git command");
|
||||||
|
exit_if_code_nonzero(&mut proc);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"init" => {
|
||||||
|
info!("Intercepted init command");
|
||||||
|
// Run git init with any preceding arguments
|
||||||
|
let mut proc = Command::new("git").arg("init").args(&args[2..]).spawn()
|
||||||
|
.log_err("Error running git command");
|
||||||
|
exit_if_code_nonzero(&mut proc);
|
||||||
|
// For every repo in config
|
||||||
|
for (name, repo) in config.repos {
|
||||||
|
// Run git remote add with name and repository URL
|
||||||
|
let mut proc = Command::new("git")
|
||||||
|
.args(&["remote", "add", &name, &repo])
|
||||||
|
.spawn()
|
||||||
|
.log_err("Error running git command");
|
||||||
|
exit_if_code_nonzero(&mut proc);
|
||||||
|
}
|
||||||
|
// Run git fetch origin
|
||||||
|
proc = Command::new("git").args(&["fetch", "origin"]).spawn()
|
||||||
|
.log_err("Error running git command");
|
||||||
|
exit_if_code_nonzero(&mut proc);
|
||||||
|
// Run git checkout master
|
||||||
|
proc = Command::new("git").args(&["checkout", "master"]).spawn()
|
||||||
|
.log_err("Error running git command");
|
||||||
|
exit_if_code_nonzero(&mut proc);
|
||||||
|
},
|
||||||
|
// Default
|
||||||
|
_ => {
|
||||||
|
// Run git, passing through all arguments provided
|
||||||
|
let mut proc = Command::new("git").args(&args[1..]).spawn()
|
||||||
|
.log_err("Error running git command");
|
||||||
|
exit_if_code_nonzero(&mut proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_if_code_nonzero(proc: &mut Child) {
|
||||||
|
// Wait for process and get exit status
|
||||||
|
let status = proc.wait().log_err("Command was not running");
|
||||||
|
// Get exit code, default 0
|
||||||
|
let exit_code = status.code().unwrap_or(0);
|
||||||
|
// If nonzero exit code
|
||||||
|
if exit_code != 0 {
|
||||||
|
// Exit with the same exit code as process
|
||||||
|
exit(exit_code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trait LogErr allows user-friendly error messages
|
||||||
|
trait LogErr<T> {
|
||||||
|
fn log_err(self, msg: &str) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement LogErr on Result of any type where E implements Debug
|
||||||
|
impl<T, E> LogErr<T> for Result<T, E> where E: ::std::fmt::Debug {
|
||||||
|
// log_err unwraps Result, logging error and exiting if needed
|
||||||
|
fn log_err(self, msg: &str) -> T {
|
||||||
|
match self {
|
||||||
|
// If no error
|
||||||
|
Ok(res) => {
|
||||||
|
// Return value within Result
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
// If error
|
||||||
|
Err(err) => {
|
||||||
|
// Log error in format "message: error" and exit
|
||||||
|
error!("{}: {:?}", msg, err);
|
||||||
|
exit(1)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user