main.rs 15.73 KiB
// SPDX-FileCopyrightText: 2023 Nicolas Fontrodona
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use crate::compact::Compact;
use crate::delete::Delete;
use crate::prune::Prune;
use clap::{Args, Parser, Subcommand};
use colored::Colorize;
use configt::PartialPrune;
mod checkout;
mod clean;
mod clone;
mod commit;
mod compact;
mod config_structure;
mod configt;
mod create_hooks;
mod delete;
mod diff;
mod init;
mod list;
mod mount;
mod prune;
mod pull;
mod push;
mod remote;
mod restore;
#[derive(Debug, Parser)]
#[clap(name = "gblk")]
/// A tool used to link borg and git together
///
/// This tool was created to link borg and git together and ease the management of developpment artifact versionning using git
struct Cli {
#[clap(subcommand)]
commands: Commands,
}
#[derive(Debug, Subcommand)]
enum Commands {
/// Initialize a borg repository inside a git project
Init(Init),
/// Save the results folder of a git repository in an archive
///
/// The archive will be named as the current commit in git
Commit(Commit),
/// List the content of the .borg archive
List(List),
/// Check if a checkout can be performed without losing data
#[clap(name = "pre-co")]
PreCo,
/// Checkout results to the current git commit
#[clap(alias = "co")]
Checkout(Checkout),
/// Create github hooks to use gbl automaticaly after commit, before and after checkout
#[clap(
name = "create-hooks",
alias = "ch",
long_about = "Create github hooks to use gbl automaticaly after commit, \
before and after checkout \n \n\
This command should be used after git init and glb init. \n \n\
This will create 2 hooks file: \n\
1. post-commit: `glb commit` will be launched after git commit \n\
2. post-checkout: `gbl checkout` will be launched after git checkout
"
)]
CreateHooks(CreateHooks),
/// Remove the post-checkout and the post-commit hooks
#[clap(name = "delete-hooks", alias = "dh")]
DeleteHooks,
/// Show differences between two commits of the `results` folder
Diff(Diff),
/// Mount an old file/directory from one or multiple archive named after
/// git commits into the .mount folder inside the project directory.
Mount(Mount),
/// Unmount everything in the folder .mount
Umount,
/// This command deletes an archive from the repository or the complete repository
///
/// This is basically a wrapper of the borg delete command
///
/// You can visit:
/// <https://borgbackup.readthedocs.io/en/stable/usage/prune.html> for more details
///
/// This function don't free disk space until gblk compact is used
Delete(Delete),
/// This command prunes the .borg repository. This can be used to keep only
/// archive created during a given time interval
///
/// This is basically a wrapper of the borg prune command
///
/// You can visit:
/// <https://borgbackup.readthedocs.io/en/stable/usage/prune.html> for more details
///
/// This function don't free disk space until gblk compact is used
Prune(Prune),
/// This command frees repository space by compacting segments.
///
/// It is especially useful after deleting archives because compaction will
/// free repository space
Compact(Compact),
/// This command can bed used to add gblk configuration
///
/// It's useful when you want to defined to always keep archive in given
/// time interval without typing always the same prune command
#[clap(subcommand)]
Config(Config),
/// This command can be used to add a new remote for push and pull commands
#[clap(subcommand)]
Remote(Remote),
/// This command can be used to push a repository using a remote
Push(Push),
/// This command can be used to pull a repository using a remote
///
/// This commands pull a remote repository inside the folder .borg. Before
/// erasing the content of the .borg repostory, it's content is saved
/// inside .tmp/<PROJECT_DIR>_bkp where PROJECT_DIR is the folder name at
/// the root of your project. If something goes wrong, you can restore your
/// .borg folder with the command gblk restore. If everything is ok, remove
/// the content of the .tmp folder using gblk clean
Pull(Pull),
/// This command cleans the .tmp repository of the project folder
Clean,
/// This command moves the borg folder .tmp/<PROJECT_DIR>_bkp folder into
/// .borg repository
///
/// The purpose of this command is to be used if a pull command fails after
/// removing content inside the .borg folder
Restore,
/// Clones a repository given a destination
Clone(Clone),
}
#[derive(Debug, Args)]
struct Init {
/// If specified, hooks are created inside `.git/hooks repository`
#[clap(takes_value = false, short = 'H', long)]
hooks: bool,
/// The compression to use automatically at each commit if hooks are created
#[clap(short, long, default_value = "lz4", value_name = "COMPRESSION")]
compression: String,
/// The checkout mode used by gblk automatically after a git checkout: soft or hard.
/// This option is only used if hooks are created.
/// The hard mode will delete every file in your results folder and extract
/// those corresponding to the commit targeted by the checkout.
///
/// The soft mode will only update files that existed in the targeted checkout
#[clap(short, long, default_value = "hard", value_name = "MODE")]
mode: String,
}
#[derive(Debug, Args)]
struct Commit {
/// The compression used to save the results folder (no, lz4, zstd, zlib or lzma)
#[clap(short, long, default_value = "lz4", value_name = "COMPRESSION")]
compression: String,
/// Use this flag to update the content of the current commit archive if it has changed
#[clap(takes_value = false, short, long)]
update: bool,
/// Use this flag to revert your results folder like it was before for this commit
#[clap(takes_value = false, short, long)]
revert: bool,
}
#[derive(Debug, Args)]
struct List {
/// consider first N archives
#[clap(short, long, default_value_t = 0, value_name = "N")]
first: i32,
/// consider last N archives
#[clap(short, long, default_value_t = 0, value_name = "N")]
last: i32,
/// If set list the files in this archive
#[clap(short, long, default_value = "", value_name = "ARCHIVE")]
archive: String,
}
#[derive(Debug, Args)]
struct Checkout {
/// The checkout mode: hard or soft
///
/// The hard mode will delete every file in your results folder and extract
/// those corresponding to the commit targeted by the checkout.
///
/// The soft mode will only update files that existed in the targeted checkout
#[clap(short, long, default_value = "soft", value_name = "MODE")]
mode: String,
}
#[derive(Debug, Args)]
struct CreateHooks {
/// The compression that will automatically be used after each commit
#[clap(short, long, default_value = "lz4", value_name = "COMPRESSION")]
compression: String,
/// The checkout mode used by gblk automatically after a git checkout: soft or hard.
/// The hard mode will delete every file in your results folder and extract
/// those corresponding to the commit targeted by the checkout.
///
/// The soft mode will only update files that existed in the targeted checkout
#[clap(short, long, default_value = "hard", value_name = "MODE")]
mode: String,
}
#[derive(Debug, Args)]
struct Diff {
/// The SHA1 of a commit
commit1: String,
/// The SHA1 of another commit.
/// If you leave this blank,
/// it will check the different between the commit1 and
/// your current result folder
commit2: Option<String>,
}
#[derive(Debug, Args)]
struct Mount {
/// Commit name, sh: Glob is supported. This is an optional parameter: if
/// not set then all commit archives will be mounted into the .mount directory
#[clap(short, long, value_name = "COMMIT")]
commit: Option<String>,
/// The file/directory to extract. This is an optional parameter. If not set
/// then all files in the archive will be displayed
#[clap(short, long, value_name = "PATH")]
path: Option<String>,
/// If set, displays the .mount directory in 'version view'.
///
/// - Normal view: The `.mount` directory contains a subfolder with the name
/// of archives.
///
/// - Version view: The `.mount` directory contains the results folder and
/// every file within it becomes a directory storing every version of
/// that file
///
/// This option is deactivated when used with --diff
#[clap(short, long, takes_value = false)]
versions: bool,
/// Displays the differences between two files mounted corresponding to the
/// given path.
///
/// Note that if only one file is recovered then, the other is taken from
/// the current result folder
#[clap(short, long, takes_value = false)]
diff: bool,
/// Consider last N archive after other filter were applied
#[clap(short, long, value_name = "N")]
last: Option<u8>,
}
#[derive(Debug, Subcommand)]
enum Config {
/// Add a new parameter in the gblk config file to be able to automatically
/// prune some commits
Add(Add),
/// Display the current gblk configuration
Show(Show),
/// Remove the configuration of a given key in prune
Rm(Rm),
/// Prune using the project configuration
Prune(PartialPrune),
}
#[derive(Debug, Args)]
struct Add {
/// The name of the option to add, see optional arguments of the prune subcommands
key: String,
/// The value of the option to add in the configuration file
value: String,
/// Use this flag if you wan to add an option in the global gblk
/// configuration file
#[clap(short, long, takes_value = false)]
global: bool,
}
#[derive(Debug, Args)]
struct Rm {
/// The name of the option to remove, see optional arguments of the prune subcommands
key: String,
/// Use this flag if you want to remove an argument in the global gblk
/// configuration file
#[clap(short, long, takes_value = false)]
global: bool,
}
#[derive(Debug, Args)]
struct Show {
/// Use this flag if you want to show an argument in the global gblk
/// configuration file
#[clap(short, long, takes_value = false)]
global: bool,
}
#[derive(Debug, Subcommand)]
enum Remote {
/// Add or update a remote in the configuration file
Add(RemoteAdd),
/// Display globally and locally defined remotes
Show,
/// Remove the configuration of a given key in prune
Rm(RemoteRm),
}
#[derive(Debug, Args)]
struct RemoteAdd {
/// The name of the remote to add
key: String,
/// The path where the remote is pointing
value: String,
/// Use this flag if you wan to add a remote in the global gblk
/// configuration file
#[clap(short, long, takes_value = false)]
global: bool,
}
#[derive(Debug, Args)]
struct RemoteRm {
/// The name of the remote to remove
key: String,
/// Use this flag if you want to remove the remote in the global gblk
/// configuration file
#[clap(short, long, takes_value = false)]
global: bool,
}
#[derive(Debug, Args)]
struct Push {
/// The name of the remote to use
key: String,
/// The name of the archive to push
archive: String,
/// The compression to use when pushin a commit
#[clap(short, long, default_value = "lz4", value_name = "COMPRESSION")]
compression: String,
}
#[derive(Debug, Args)]
struct Pull {
/// The name of the remote to use
key: String,
}
#[derive(Debug, Args)]
struct Clone {
/// The path pointing to a borg folder
path: String,
/// If specified, hooks are created inside `.git/hooks repository`
#[clap(takes_value = false, short = 'H', long)]
hooks: bool,
/// The compression to use automatically at each commit if hooks are created
#[clap(short, long, default_value = "lz4", value_name = "COMPRESSION")]
compression: String,
/// The checkout mode used by gblk automatically after a git checkout: soft or hard.
/// This option is only used if hooks are created.
/// The hard mode will delete every file in your results folder and extract
/// those corresponding to the commit targeted by the checkout.
///
/// The soft mode will only update files that existed in the targeted checkout
#[clap(short, long, default_value = "hard", value_name = "MODE")]
mode: String,
}
fn main() {
let args = Cli::parse();
match args.commands {
Commands::Init(init) => {
init::init_and_hook(init.hooks, &init.compression, &init.mode);
}
Commands::Commit(commit) => {
mount::umount_archive(true);
if commit.revert {
if commit.revert == commit.update {
eprintln!(
"{} You moust choose between `{}` and `{}` option",
"Error:".red(),
"--revert".blue(),
"--update".blue()
)
}
}
if !commit.revert {
commit::commit(commit.compression, String::from(""), commit.update);
} else {
commit::revert_commit();
}
}
Commands::List(list) => {
list::borg_list(list.first, list.last, &list.archive);
}
Commands::PreCo => {
mount::umount_archive(true);
checkout::prepare_checkout();
}
Commands::Checkout(co) => {
mount::umount_archive(true);
checkout::checkout(&co.mode);
}
Commands::CreateHooks(ch) => {
create_hooks::create_hooks(&ch.compression, &ch.mode);
}
Commands::Diff(diff) => {
diff::compute_diff(&diff.commit1, &diff.commit2);
}
Commands::DeleteHooks => {
create_hooks::delete_hooks();
}
Commands::Mount(mount) => {
mount::umount_archive(true);
mount::mount_archive(
&mount.commit,
&mount.path,
mount.versions,
mount.diff,
mount.last,
);
}
Commands::Umount => {
mount::umount_archive(false);
}
Commands::Delete(my_delete) => {
delete::deletion_launcher(my_delete);
}
Commands::Prune(my_prune) => {
prune::prune_launcher(my_prune);
}
Commands::Compact(my_compact) => {
compact::launch_compact(my_compact);
}
Commands::Config(conf) => match conf {
Config::Add(e) => configt::update_config(&e.key, &e.value, e.global),
Config::Show(e) => configt::show(e.global),
Config::Rm(e) => configt::remove_config(&e.key, e.global),
Config::Prune(pp) => configt::launch_config_prune(pp),
},
Commands::Remote(remote) => match remote {
Remote::Add(e) => remote::update_config(&e.key, &e.value, e.global),
Remote::Show => {
remote::show();
}
Remote::Rm(e) => remote::remove_config(&e.key, e.global),
},
Commands::Push(p) => {
mount::umount_archive(true);
push::push(&p.key, &p.archive, &p.compression);
}
Commands::Pull(_) => {
// pull::pull(&p.key);
eprintln!("function disabled");
}
Commands::Clean => {
clean::clean();
}
Commands::Restore => {
// restore::restore();
eprintln!("function disabled");
}
Commands::Clone(_) => {
// clone::clone(&c.path, c.hooks, &c.compression, &c.mode);
eprintln!("function disabled");
}
}
}