From 6cdc18742ed841435b2b24a1b861e81bf8b967f9 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 8 Jul 2023 14:50:18 +0200 Subject: [PATCH 01/13] feat: don't read non-contributing archives for export --- CHANGELOG.md | 5 +++++ src/backup/delta.rs | 11 +++++++++-- src/backup/manager/mod.rs | 11 +++++++++-- src/backup/state.rs | 8 ++++++++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 918b0ea..62b6692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/Chewing_Bever/alex/src/branch/dev) +### Changed + +* Export command no longer reads backups that do not contribute to the final + state + ## [0.3.1](https://git.rustybever.be/Chewing_Bever/alex/src/tag/0.3.1) ### Added diff --git a/src/backup/delta.rs b/src/backup/delta.rs index 1bc5477..21f626c 100644 --- a/src/backup/delta.rs +++ b/src/backup/delta.rs @@ -18,11 +18,18 @@ pub struct Delta { impl Delta { pub fn new() -> Self { Self { - added: Default::default(), - removed: Default::default(), + added: State::new(), + removed: State::new(), } } + /// Returns whether the delta is empty by checking whether both its added and removed state + /// return true for their `is_empty`. + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.added.is_empty() && self.removed.is_empty() + } + /// Calculate the union of this delta with another delta. /// /// The union of two deltas is a delta that produces the same state as if you were to apply diff --git a/src/backup/manager/mod.rs b/src/backup/manager/mod.rs index 6a43f9f..7cbd43c 100644 --- a/src/backup/manager/mod.rs +++ b/src/backup/manager/mod.rs @@ -226,9 +226,16 @@ where let enc = GzEncoder::new(tar_gz, Compression::default()); let mut ar = tar::Builder::new(enc); - for (contribution, backup) in - contributions.iter().rev().zip(chain.iter().take(index + 1)) + // We only need to consider backups that have a non-empty contribution. + // This allows us to skip reading backups that have been completely + // overwritten by their successors anyways. + for (contribution, backup) in contributions + .iter() + .rev() + .zip(chain.iter().take(index + 1)) + .filter(|(contribution, _)| !contribution.is_empty()) { + println!("{}", &backup); backup.append(&self.backup_dir, contribution, &mut ar)?; } diff --git a/src/backup/state.rs b/src/backup/state.rs index 4b09acf..de4b2c0 100644 --- a/src/backup/state.rs +++ b/src/backup/state.rs @@ -41,6 +41,14 @@ impl State { path.starts_with(dir) && files.contains(path.strip_prefix(dir).unwrap()) }) } + + /// Returns whether the state is empty. + /// + /// Note that this does not necessarily mean that the state does not contain any sets, but + /// rather that any sets that it does contain are also empty. + pub fn is_empty(&self) -> bool { + self.0.values().all(|s| s.is_empty()) + } } impl From for State From 241bb4d68eb9fb432965de8fa5c3996419950a50 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 8 Jul 2023 15:31:01 +0200 Subject: [PATCH 02/13] feat: add extract command --- CHANGELOG.md | 4 +++ src/backup/mod.rs | 66 +++++++++++++++++++++++++++++------------------ src/cli/backup.rs | 63 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62b6692..bef4cf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/Chewing_Bever/alex/src/branch/dev) +### Added + +* Extract command for working with the output of export + ### Changed * Export command no longer reads backups that do not contribute to the final diff --git a/src/backup/mod.rs b/src/backup/mod.rs index 8eecc0f..6bd798f 100644 --- a/src/backup/mod.rs +++ b/src/backup/mod.rs @@ -55,6 +55,45 @@ impl Backup<()> { let filename = format!("{}", start_time.format(Self::FILENAME_FORMAT)); backup_dir.join(filename) } + + /// Extract an archive. + /// + /// # Arguments + /// + /// * `backup_path` - Path to the archive to extract + /// * `dirs` - list of tuples `(path_in_tar, dst_dir)` with `dst_dir` the directory on-disk + /// where the files stored under `path_in_tar` inside the tarball should be extracted to. + pub fn extract_archive>( + archive_path: P, + dirs: &Vec<(PathBuf, PathBuf)>, + ) -> io::Result<()> { + let tar_gz = File::open(archive_path)?; + let enc = GzDecoder::new(tar_gz); + let mut ar = tar::Archive::new(enc); + + // Unpack each file by matching it with one of the destination directories and extracting + // it to the right path + for entry in ar.entries()? { + let mut entry = entry?; + let entry_path_in_tar = entry.path()?.to_path_buf(); + + for (path_in_tar, dst_dir) in dirs { + if entry_path_in_tar.starts_with(path_in_tar) { + let dst_path = + dst_dir.join(entry_path_in_tar.strip_prefix(path_in_tar).unwrap()); + + // Ensure all parent directories are present + std::fs::create_dir_all(dst_path.parent().unwrap())?; + + entry.unpack(dst_path)?; + + break; + } + } + } + + Ok(()) + } } impl Backup { @@ -199,31 +238,8 @@ impl Backup { backup_dir: P, dirs: &Vec<(PathBuf, PathBuf)>, ) -> io::Result<()> { - let path = Backup::path(backup_dir, self.start_time); - let tar_gz = File::open(path)?; - let enc = GzDecoder::new(tar_gz); - let mut ar = tar::Archive::new(enc); - - // Unpack each file by matching it with one of the destination directories and extracting - // it to the right path - for entry in ar.entries()? { - let mut entry = entry?; - let entry_path_in_tar = entry.path()?.to_path_buf(); - - for (path_in_tar, dst_dir) in dirs { - if entry_path_in_tar.starts_with(path_in_tar) { - let dst_path = - dst_dir.join(entry_path_in_tar.strip_prefix(path_in_tar).unwrap()); - - // Ensure all parent directories are present - std::fs::create_dir_all(dst_path.parent().unwrap())?; - - entry.unpack(dst_path)?; - - break; - } - } - } + let backup_path = Backup::path(backup_dir, self.start_time); + Backup::extract_archive(backup_path, dirs)?; // Remove any files for (path_in_tar, dst_dir) in dirs { diff --git a/src/cli/backup.rs b/src/cli/backup.rs index 3a993fc..2ec1e2c 100644 --- a/src/cli/backup.rs +++ b/src/cli/backup.rs @@ -12,10 +12,13 @@ pub enum BackupCommands { List(BackupListArgs), /// Manually create a new backup Create(BackupCreateArgs), - /// Restore a backup + /// Restore a backup including all of its required predecessors Restore(BackupRestoreArgs), /// Export a backup into a full archive Export(BackupExportArgs), + /// Extract a single backup; meant as a convenience method for working with the output of + /// `export` + Extract(BackupExtractArgs), } #[derive(Args)] @@ -63,6 +66,22 @@ pub struct BackupExportArgs { make: bool, } +#[derive(Args)] +pub struct BackupExtractArgs { + /// Path to the backup to extract + path: PathBuf, + /// Directory to store config in + output_config: PathBuf, + /// Directory to store worlds in + output_worlds: PathBuf, + /// Whether to overwrite the contents of the output directories + #[arg(short, long, default_value_t = false)] + force: bool, + /// Create output directories if they don't exist + #[arg(short, long, default_value_t = false)] + make: bool, +} + impl BackupArgs { pub fn run(&self, cli: &Cli) -> io::Result<()> { match &self.command { @@ -70,6 +89,7 @@ impl BackupArgs { BackupCommands::List(args) => args.run(cli), BackupCommands::Restore(args) => args.run(cli), BackupCommands::Export(args) => args.run(cli), + BackupCommands::Extract(args) => args.run(cli), } } } @@ -219,3 +239,44 @@ impl BackupExportArgs { } } } + +impl BackupExtractArgs { + pub fn run(&self, _cli: &Cli) -> io::Result<()> { + // Create directories if needed + if self.make { + std::fs::create_dir_all(&self.output_config)?; + std::fs::create_dir_all(&self.output_worlds)?; + } + + let output_config = self.output_config.canonicalize()?; + let output_worlds = self.output_worlds.canonicalize()?; + let backup_path = self.path.canonicalize()?; + + // Clear previous contents of directories + let mut entries = output_config + .read_dir()? + .chain(output_worlds.read_dir()?) + .peekable(); + + if entries.peek().is_some() && !self.force { + return Err(other("Output directories are not empty. If you wish to overwrite these contents, use the force flag.")); + } + + for entry in entries { + let path = entry?.path(); + + if path.is_dir() { + std::fs::remove_dir_all(path)?; + } else { + std::fs::remove_file(path)?; + } + } + + let dirs = vec![ + (PathBuf::from("config"), output_config), + (PathBuf::from("worlds"), output_worlds), + ]; + + Backup::extract_archive(backup_path, &dirs) + } +} From bfb264e8232c9414bc049d8f1841d049e9a09b2f Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 8 Jul 2023 15:34:24 +0200 Subject: [PATCH 03/13] docs: add some more help strings --- src/cli/backup.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/cli/backup.rs b/src/cli/backup.rs index 2ec1e2c..6810e42 100644 --- a/src/cli/backup.rs +++ b/src/cli/backup.rs @@ -9,15 +9,29 @@ use std::path::{Path, PathBuf}; #[derive(Subcommand)] pub enum BackupCommands { /// List all tracked backups + /// + /// Note that this will only list backups for the layers currently configured, and will ignore + /// any other layers also present in the backup directory. List(BackupListArgs), /// Manually create a new backup + /// + /// Note that backups created using this command will count towards the length of a chain, and + /// can therefore shorten how far back in time your backups will be stored. Create(BackupCreateArgs), - /// Restore a backup including all of its required predecessors + /// Restore a backup + /// + /// This command will restore the selected backup by extracting its entire chain up to and + /// including the requested backup in-order. Restore(BackupRestoreArgs), /// Export a backup into a full archive + /// + /// Just like the restore command, this will extract each backup from the chain up to and + /// including the requested backup, but instead of writing the files to disk, they will be + /// recompressed into a new tarball, resulting in a new tarball containing a full backup. Export(BackupExportArgs), - /// Extract a single backup; meant as a convenience method for working with the output of - /// `export` + /// Extract an archive file, which is assumed to be a full backup. + /// + /// This command mostly exists as a convenience method for working with the output of `export`. Extract(BackupExtractArgs), } @@ -48,6 +62,9 @@ pub struct BackupRestoreArgs { /// Directory to store worlds in output_worlds: PathBuf, /// Whether to overwrite the contents of the output directories + /// + /// If set, the output directories will be completely cleared before trying to restore the + /// backup. #[arg(short, long, default_value_t = false)] force: bool, /// Create output directories if they don't exist @@ -75,6 +92,9 @@ pub struct BackupExtractArgs { /// Directory to store worlds in output_worlds: PathBuf, /// Whether to overwrite the contents of the output directories + /// + /// If set, the output directories will be completely cleared before trying to restore the + /// backup. #[arg(short, long, default_value_t = false)] force: bool, /// Create output directories if they don't exist From bf83357464ed010e4dd51513d06804734cebae2a Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 8 Jul 2023 16:11:36 +0200 Subject: [PATCH 04/13] feat: publish arch packages --- .woodpecker/arch-release.yml | 37 +++++++++++++++++++++++++++++++ CHANGELOG.md | 1 + PKGBUILD | 42 ++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 .woodpecker/arch-release.yml create mode 100644 PKGBUILD diff --git a/.woodpecker/arch-release.yml b/.woodpecker/arch-release.yml new file mode 100644 index 0000000..ece2c52 --- /dev/null +++ b/.woodpecker/arch-release.yml @@ -0,0 +1,37 @@ +matrix: + PLATFORM: + - 'linux/amd64' + # - linux/arm64 + +platform: ${PLATFORM} +branches: [ main ] +skip_clone: true + +pipeline: + build: + image: 'menci/archlinuxarm:base-devel' + pull: true + commands: + - echo -e '[bur]\nServer = https://arch.r8r.be/$repo/$arch\nSigLevel = Optional' >> /etc/pacman.conf + # Update packages + - pacman -Syu --noconfirm + # Create non-root user to perform build & switch to their home + - groupadd -g 1000 builder + - useradd -mg builder builder + - chown -R builder:builder "$PWD" + - "echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers" + - su builder + - curl -OL "https://git.rustybever.be/Chewing_Bever/alex/raw/tag/$CI_COMMIT_TAG/PKGBUILD" + - makepkg -s --noconfirm --needed + when: + event: tag + + publish: + image: 'curlimages/curl' + commands: + # Publish the package + - 'for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $VIETER_API_KEY" https://arch.r8r.be/bur/publish; done' + secrets: + - vieter_api_key + when: + event: tag diff --git a/CHANGELOG.md b/CHANGELOG.md index bef4cf9..ebf8a14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Extract command for working with the output of export +* Arch packages are now published to my bur repo ### Changed diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 0000000..6bb6b6c --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,42 @@ +# Maintainer: Jef Roosens + +pkgname='alex' +pkgver=0.3.1 +pkgdesc='Wrapper around Minecraft server processes, designed to complement Docker image installations.' +pkgrel=1 +arch=('x86_64' 'aarch64') + +url='https://git.rustybever.be/Chewing_Bever/alex' +license=('MIT') + +makedepends=('cargo') +depends=('glibc' 'gcc-libs' ) + +source=("${pkgname}-${pkgver}.tar.gz::https://git.rustybever.be/Chewing_Bever/alex/archive/${pkgver}.tar.gz") +sha512sums=('f88903bd99397f3e9a1c4a40febc65eace0d594cde8de20d54ed1cd0597512152111e7a20acaaa971309d5afe1ea267c7ef3b08f0d803237e8004808a83752c5') + +prepare() { + cd "${pkgname}" + + export RUSTUP_TOOLCHAIN=stable + cargo fetch --locked --target "$CARCH-unknown-linux-gnu" +} + +build() { + cd "${pkgname}" + + export RUSTUP_TOOLCHAIN=stable + export CARGO_TARGET_DIR=target + cargo build --frozen --release --all-features +} + +check() { + cd "${pkgname}" + + export RUSTUP_TOOLCHAIN=stable + cargo test --frozen --all-features +} + +package() { + install -Dm0755 -t "${pkgdir}/usr/bin/" "${pkgname}/target/release/${pkgname}" +} From 34d016fd3fd48eb9507183d17e07bd921f96addf Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Fri, 11 Aug 2023 21:26:17 +0200 Subject: [PATCH 05/13] feat: allow passing global configuration as TOML file --- Cargo.lock | 164 +++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + alex-example.toml | 16 ++++ src/backup/manager/config.rs | 4 +- src/cli/backup.rs | 13 ++- src/cli/config.rs | 49 +++++++++++ src/cli/mod.rs | 115 ++++++++++++------------ src/cli/run.rs | 16 ++-- src/error.rs | 32 +++++++ src/main.rs | 11 ++- 10 files changed, 347 insertions(+), 74 deletions(-) create mode 100644 alex-example.toml create mode 100644 src/cli/config.rs create mode 100644 src/error.rs diff --git a/Cargo.lock b/Cargo.lock index 16ec2ca..1854dec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,7 @@ version = "0.3.1" dependencies = [ "chrono", "clap", + "figment", "flate2", "serde", "serde_json", @@ -85,6 +86,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + [[package]] name = "autocfg" version = "1.1.0" @@ -194,6 +201,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.1" @@ -215,6 +228,20 @@ dependencies = [ "libc", ] +[[package]] +name = "figment" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4547e226f4c9ab860571e070a9034192b3175580ecea38da34fcdb53a018c9a5" +dependencies = [ + "atomic", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + [[package]] name = "filetime" version = "0.2.21" @@ -237,6 +264,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.4.1" @@ -272,6 +305,22 @@ dependencies = [ "cc", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -328,6 +377,12 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -352,6 +407,29 @@ version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" +[[package]] +name = "pear" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a386cd715229d399604b50d1361683fe687066f42d56f54be995bc6868f71c" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f0f13dac8069c139e8300a6510e3f4143ecf5259c60b116a9b271b4ca0d54" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.59" @@ -361,6 +439,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + [[package]] name = "quote" version = "1.0.28" @@ -430,6 +521,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "signal-hook" version = "0.3.15" @@ -488,6 +588,49 @@ dependencies = [ "winapi", ] +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "uncased" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-ident" version = "1.0.9" @@ -500,6 +643,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -657,6 +806,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f495880723d0999eb3500a9064d8dbcf836460b24c17df80ea7b5794053aac" +dependencies = [ + "memchr", +] + [[package]] name = "xattr" version = "0.2.3" @@ -665,3 +823,9 @@ checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ "libc", ] + +[[package]] +name = "yansi" +version = "1.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee746ad3851dd3bc40e4a028ab3b00b99278d929e48957bcb2d111874a7e43e" diff --git a/Cargo.toml b/Cargo.toml index 01d0593..777407a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ clap = { version = "4.3.1", features = ["derive", "env"] } signal-hook = "0.3.15" serde = { version = "1.0.164", features = ["derive", "rc"] } serde_json = "1.0.96" +figment = { version = "0.10.10", features = ["env", "toml"] } [profile.release] lto = "fat" diff --git a/alex-example.toml b/alex-example.toml new file mode 100644 index 0000000..d4b4122 --- /dev/null +++ b/alex-example.toml @@ -0,0 +1,16 @@ +config = "data/config" +world = "data/worlds" +backup = "data/backups" +server = "Paper" + +[[layers]] +name = "2min" +frequency = 2 +chains = 4 +chain_len = 4 + +[[layers]] +name = "3min" +frequency = 3 +chains = 2 +chain_len = 2 diff --git a/src/backup/manager/config.rs b/src/backup/manager/config.rs index 8adbd5a..75af34e 100644 --- a/src/backup/manager/config.rs +++ b/src/backup/manager/config.rs @@ -2,7 +2,9 @@ use std::error::Error; use std::fmt; use std::str::FromStr; -#[derive(Clone, Debug)] +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct ManagerConfig { pub name: String, pub frequency: u32, diff --git a/src/cli/backup.rs b/src/cli/backup.rs index 6810e42..85c0cee 100644 --- a/src/cli/backup.rs +++ b/src/cli/backup.rs @@ -1,5 +1,4 @@ use crate::backup::Backup; -use crate::cli::Cli; use crate::other; use chrono::{TimeZone, Utc}; use clap::{Args, Subcommand}; @@ -103,7 +102,7 @@ pub struct BackupExtractArgs { } impl BackupArgs { - pub fn run(&self, cli: &Cli) -> io::Result<()> { + pub fn run(&self, cli: &super::Config) -> io::Result<()> { match &self.command { BackupCommands::Create(args) => args.run(cli), BackupCommands::List(args) => args.run(cli), @@ -115,7 +114,7 @@ impl BackupArgs { } impl BackupCreateArgs { - pub fn run(&self, cli: &Cli) -> io::Result<()> { + pub fn run(&self, cli: &super::Config) -> io::Result<()> { let mut meta = cli.meta()?; if let Some(res) = meta.create_backup(&self.layer) { @@ -127,7 +126,7 @@ impl BackupCreateArgs { } impl BackupListArgs { - pub fn run(&self, cli: &Cli) -> io::Result<()> { + pub fn run(&self, cli: &super::Config) -> io::Result<()> { let meta = cli.meta()?; // A bit scuffed? Sure @@ -184,7 +183,7 @@ fn parse_backup_path( } impl BackupRestoreArgs { - pub fn run(&self, cli: &Cli) -> io::Result<()> { + pub fn run(&self, cli: &super::Config) -> io::Result<()> { let backup_dir = cli.backup.canonicalize()?; // Create directories if needed @@ -237,7 +236,7 @@ impl BackupRestoreArgs { } impl BackupExportArgs { - pub fn run(&self, cli: &Cli) -> io::Result<()> { + pub fn run(&self, cli: &super::Config) -> io::Result<()> { let backup_dir = cli.backup.canonicalize()?; if self.make { @@ -261,7 +260,7 @@ impl BackupExportArgs { } impl BackupExtractArgs { - pub fn run(&self, _cli: &Cli) -> io::Result<()> { + pub fn run(&self, _cli: &super::Config) -> io::Result<()> { // Create directories if needed if self.make { std::fs::create_dir_all(&self.output_config)?; diff --git a/src/cli/config.rs b/src/cli/config.rs new file mode 100644 index 0000000..da80269 --- /dev/null +++ b/src/cli/config.rs @@ -0,0 +1,49 @@ +use std::{io, path::PathBuf}; + +use serde::{Deserialize, Serialize}; + +use crate::{ + backup::{ManagerConfig, MetaManager}, + server::{Metadata, ServerType}, +}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Config { + pub config: PathBuf, + pub world: PathBuf, + pub backup: PathBuf, + pub layers: Vec, + pub server: ServerType, + pub server_version: String, +} + +impl Default for Config { + fn default() -> Self { + Self { + config: PathBuf::from("."), + world: PathBuf::from("../worlds"), + backup: PathBuf::from("../backups"), + layers: Vec::new(), + server: ServerType::Unknown, + server_version: String::from(""), + } + } +} + +impl Config { + /// Convenience method to initialize backup manager from the cli arguments + pub fn meta(&self) -> io::Result> { + let metadata = Metadata { + server_type: self.server, + server_version: self.server_version.clone(), + }; + let dirs = vec![ + (PathBuf::from("config"), self.config.canonicalize()?), + (PathBuf::from("worlds"), self.world.canonicalize()?), + ]; + let mut meta = MetaManager::new(self.backup.canonicalize()?, dirs, metadata); + meta.add_all(&self.layers)?; + + Ok(meta) + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 0594e8e..6ba3f94 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,4 +1,5 @@ mod backup; +mod config; mod run; pub use crate::backup::MetaManager; @@ -8,7 +9,13 @@ pub use run::RunArgs; use crate::backup::ManagerConfig; use crate::server::ServerType; -use clap::{Parser, Subcommand}; +use clap::{Args, Parser, Subcommand}; +pub use config::Config; +use figment::{ + providers::{Env, Format, Serialized, Toml}, + Figment, +}; +use serde::{Deserialize, Serialize}; use std::io; use std::path::PathBuf; @@ -20,73 +27,71 @@ pub enum Commands { Backup(BackupArgs), } -#[derive(Parser)] -#[command(author, version, about, long_about = None)] -pub struct Cli { - #[command(subcommand)] - pub command: Commands, +#[derive(Args, Serialize, Deserialize, Clone)] +pub struct CliArgs { /// Directory where configs are stored, and where the server will run - #[arg( - long, - value_name = "CONFIG_DIR", - default_value = ".", - env = "ALEX_CONFIG_DIR", - global = true - )] - pub config: PathBuf, + #[arg(long, value_name = "CONFIG_DIR", global = true)] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub config: Option, + /// Directory where world files will be saved - #[arg( - long, - value_name = "WORLD_DIR", - default_value = "../worlds", - env = "ALEX_WORLD_DIR", - global = true - )] - pub world: PathBuf, + #[arg(long, value_name = "WORLD_DIR", global = true)] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub world: Option, + /// Directory where backups will be stored - #[arg( - long, - value_name = "BACKUP_DIR", - default_value = "../backups", - env = "ALEX_BACKUP_DIR", - global = true - )] - pub backup: PathBuf, + #[arg(long, value_name = "BACKUP_DIR", global = true)] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub backup: Option, /// What backup layers to employ, provided as a list of tuples name,frequency,chains,chain_len /// delimited by semicolons (;). - #[arg(long, env = "ALEX_LAYERS", global = true, value_delimiter = ';')] - pub layers: Vec, + #[arg(long, global = true, value_delimiter = ';')] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub layers: Option>, /// Type of server - #[arg(long, default_value = "unknown", env = "ALEX_SERVER", global = true)] - pub server: ServerType, + #[arg(long, global = true)] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub server: Option, + /// Version string for the server, e.g. 1.19.4-545 - #[arg(long, default_value = "", env = "ALEX_SERVER_VERSION", global = true)] - pub server_version: String, + #[arg(long, global = true)] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub server_version: Option, +} + +#[derive(Parser, Serialize)] +#[command(author, version, about, long_about = None)] +pub struct Cli { + #[command(subcommand)] + #[serde(skip)] + pub command: Commands, + + /// Path to a TOML configuration file + #[arg(long = "config-file", global = true)] + pub config_file: Option, + + #[command(flatten)] + pub args: CliArgs, } impl Cli { - pub fn run(&self) -> io::Result<()> { + pub fn run(&self) -> crate::Result<()> { + let toml_file = self + .config_file + .clone() + .unwrap_or(PathBuf::from(Env::var_or("ALEX_CONFIG_FILE", ""))); + let config: Config = Figment::new() + .merge(Serialized::defaults(Config::default())) + .merge(Toml::file(toml_file)) + .merge(Env::prefixed("ALEX_")) + .merge(Serialized::defaults(&self.args)) + .extract()?; + match &self.command { - Commands::Run(args) => args.run(self), - Commands::Backup(args) => args.run(self), + Commands::Run(args) => args.run(&config), + Commands::Backup(args) => Ok(args.run(&config)?), } } - - /// Convenience method to initialize backup manager from the cli arguments - pub fn meta(&self) -> io::Result> { - let metadata = Metadata { - server_type: self.server, - server_version: self.server_version.clone(), - }; - let dirs = vec![ - (PathBuf::from("config"), self.config.canonicalize()?), - (PathBuf::from("worlds"), self.world.canonicalize()?), - ]; - let mut meta = MetaManager::new(self.backup.canonicalize()?, dirs, metadata); - meta.add_all(&self.layers)?; - - Ok(meta) - } } diff --git a/src/cli/run.rs b/src/cli/run.rs index 986fe8a..4e577b8 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -57,16 +57,16 @@ fn backups_thread(counter: Arc>) { } impl RunArgs { - pub fn run(&self, cli: &Cli) -> io::Result<()> { + pub fn run(&self, config: &super::Config) -> crate::Result<()> { let (_, mut signals) = signals::install_signal_handlers()?; - let mut cmd = server::ServerCommand::new(cli.server, &cli.server_version) + let mut cmd = server::ServerCommand::new(config.server, &config.server_version) .java(&self.java) .jar(self.jar.clone()) - .config(cli.config.clone()) - .world(cli.world.clone()) - .backup(cli.backup.clone()) - .managers(cli.layers.clone()) + .config(config.config.clone()) + .world(config.world.clone()) + .backup(config.backup.clone()) + .managers(config.layers.clone()) .xms(self.xms) .xmx(self.xmx); cmd.canonicalize()?; @@ -79,7 +79,7 @@ impl RunArgs { let counter = Arc::new(Mutex::new(cmd.spawn()?)); - if !cli.layers.is_empty() { + if !config.layers.is_empty() { let clone = Arc::clone(&counter); std::thread::spawn(move || backups_thread(clone)); } @@ -89,6 +89,6 @@ impl RunArgs { std::thread::spawn(move || stdin::handle_stdin(clone)); // Signal handler loop exits the process when necessary - signals::handle_signals(&mut signals, counter) + Ok(signals::handle_signals(&mut signals, counter)?) } } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..0ea7532 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,32 @@ +use std::{fmt, io}; + +pub type Result = std::result::Result; + +#[derive(Debug)] +pub enum Error { + IO(io::Error), + Figment(figment::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::IO(err) => write!(fmt, "{}", err), + Error::Figment(err) => write!(fmt, "{}", err), + } + } +} + +impl std::error::Error for Error {} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::IO(err) + } +} + +impl From for Error { + fn from(err: figment::Error) -> Self { + Error::Figment(err) + } +} diff --git a/src/main.rs b/src/main.rs index f357d1b..245a796 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,17 @@ mod backup; mod cli; +mod error; mod server; mod signals; mod stdin; -use crate::cli::Cli; -use clap::Parser; use std::io; +use clap::Parser; + +use crate::cli::Cli; +pub use error::{Error, Result}; + pub fn other(msg: &str) -> io::Error { io::Error::new(io::ErrorKind::Other, msg) } @@ -32,7 +36,8 @@ pub fn other(msg: &str) -> io::Error { // // manager.remove_old_backups() // } -fn main() -> io::Result<()> { +fn main() -> crate::Result<()> { let cli = Cli::parse(); + cli.run() } From db3bba5a422b41f3abc8be90b0f39a1aed68291d Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Fri, 11 Aug 2023 23:13:17 +0200 Subject: [PATCH 06/13] feat: also allow run args to be passed from toml file --- CHANGELOG.md | 1 + Cargo.toml | 2 +- alex-example.toml | 20 ++++----- src/cli/mod.rs | 31 ++++++++----- src/cli/run/config.rs | 22 ++++++++++ src/cli/{run.rs => run/mod.rs} | 80 +++++++++++++++++++--------------- 6 files changed, 99 insertions(+), 57 deletions(-) create mode 100644 src/cli/run/config.rs rename src/cli/{run.rs => run/mod.rs} (58%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebf8a14..553fd69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Extract command for working with the output of export * Arch packages are now published to my bur repo +* Allow passing configuration variables from TOML file ### Changed diff --git a/Cargo.toml b/Cargo.toml index 777407a..09a8b1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ flate2 = "1.0.26" chrono = { version = "0.4.26", features = ["serde"] } clap = { version = "4.3.1", features = ["derive", "env"] } signal-hook = "0.3.15" -serde = { version = "1.0.164", features = ["derive", "rc"] } +serde = { version = "1.0.164", features = ["derive"] } serde_json = "1.0.96" figment = { version = "0.10.10", features = ["env", "toml"] } diff --git a/alex-example.toml b/alex-example.toml index d4b4122..234a7bf 100644 --- a/alex-example.toml +++ b/alex-example.toml @@ -3,14 +3,14 @@ world = "data/worlds" backup = "data/backups" server = "Paper" -[[layers]] -name = "2min" -frequency = 2 -chains = 4 -chain_len = 4 +# [[layers]] +# name = "2min" +# frequency = 2 +# chains = 4 +# chain_len = 4 -[[layers]] -name = "3min" -frequency = 3 -chains = 2 -chain_len = 2 +# [[layers]] +# name = "3min" +# frequency = 3 +# chains = 2 +# chain_len = 2 diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 6ba3f94..7a76ea1 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -5,7 +5,7 @@ mod run; pub use crate::backup::MetaManager; pub use crate::server::Metadata; pub use backup::{BackupArgs, BackupCommands}; -pub use run::RunArgs; +pub use run::RunCli; use crate::backup::ManagerConfig; use crate::server::ServerType; @@ -16,13 +16,12 @@ use figment::{ Figment, }; use serde::{Deserialize, Serialize}; -use std::io; use std::path::PathBuf; #[derive(Subcommand)] pub enum Commands { /// Run the server - Run(RunArgs), + Run(RunCli), /// Interact with the backup system without starting a server Backup(BackupArgs), } @@ -78,20 +77,28 @@ pub struct Cli { impl Cli { pub fn run(&self) -> crate::Result<()> { + let config = self.config(&self.args)?; + + match &self.command { + Commands::Run(args) => args.run(self, &config), + Commands::Backup(args) => Ok(args.run(&config)?), + } + } + + pub fn config(&self, args: &U) -> Result + where + T: Default + Serialize + for<'de> Deserialize<'de>, + U: Serialize, + { let toml_file = self .config_file .clone() .unwrap_or(PathBuf::from(Env::var_or("ALEX_CONFIG_FILE", ""))); - let config: Config = Figment::new() - .merge(Serialized::defaults(Config::default())) + Figment::new() + .merge(Serialized::defaults(T::default())) .merge(Toml::file(toml_file)) .merge(Env::prefixed("ALEX_")) - .merge(Serialized::defaults(&self.args)) - .extract()?; - - match &self.command { - Commands::Run(args) => args.run(&config), - Commands::Backup(args) => Ok(args.run(&config)?), - } + .merge(Serialized::defaults(args)) + .extract() } } diff --git a/src/cli/run/config.rs b/src/cli/run/config.rs new file mode 100644 index 0000000..5783451 --- /dev/null +++ b/src/cli/run/config.rs @@ -0,0 +1,22 @@ +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Config { + pub jar: PathBuf, + pub java: String, + pub xms: u64, + pub xmx: u64, +} + +impl Default for Config { + fn default() -> Self { + Self { + jar: PathBuf::from("server.jar"), + java: String::from("java"), + xms: 1024, + xmx: 2048, + } + } +} diff --git a/src/cli/run.rs b/src/cli/run/mod.rs similarity index 58% rename from src/cli/run.rs rename to src/cli/run/mod.rs index 4e577b8..fb48491 100644 --- a/src/cli/run.rs +++ b/src/cli/run/mod.rs @@ -1,33 +1,19 @@ -use crate::cli::Cli; +mod config; + use crate::server; use crate::signals; use crate::stdin; use clap::Args; -use std::io; +use config::Config; use std::path::PathBuf; use std::sync::{Arc, Mutex}; +use serde::{Deserialize, Serialize}; + #[derive(Args)] -pub struct RunArgs { - /// Server jar to execute - #[arg( - long, - value_name = "JAR_PATH", - default_value = "server.jar", - env = "ALEX_JAR" - )] - pub jar: PathBuf, - - /// Java command to run the server jar with - #[arg(long, value_name = "JAVA_CMD", default_value_t = String::from("java"), env = "ALEX_JAVA")] - pub java: String, - - /// XMS value in megabytes for the server instance - #[arg(long, default_value_t = 1024, env = "ALEX_XMS")] - pub xms: u64, - /// XMX value in megabytes for the server instance - #[arg(long, default_value_t = 2048, env = "ALEX_XMX")] - pub xmx: u64, +pub struct RunCli { + #[command(flatten)] + pub args: RunArgs, /// Don't actually run the server, but simply output the server configuration that would have /// been ran @@ -35,6 +21,29 @@ pub struct RunArgs { pub dry: bool, } +#[derive(Args, Serialize, Deserialize, Clone)] +pub struct RunArgs { + /// Server jar to execute + #[arg(long, value_name = "JAR_PATH")] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub jar: Option, + + /// Java command to run the server jar with + #[arg(long, value_name = "JAVA_CMD")] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub java: Option, + + /// XMS value in megabytes for the server instance + #[arg(long)] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub xms: Option, + + /// XMX value in megabytes for the server instance + #[arg(long)] + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub xmx: Option, +} + fn backups_thread(counter: Arc>) { loop { let next_scheduled_time = { @@ -56,19 +65,22 @@ fn backups_thread(counter: Arc>) { } } -impl RunArgs { - pub fn run(&self, config: &super::Config) -> crate::Result<()> { +impl RunCli { + pub fn run(&self, cli: &super::Cli, global: &super::Config) -> crate::Result<()> { + let config: Config = cli.config(&self.args)?; + println!("{:?}", config); + let (_, mut signals) = signals::install_signal_handlers()?; - let mut cmd = server::ServerCommand::new(config.server, &config.server_version) - .java(&self.java) - .jar(self.jar.clone()) - .config(config.config.clone()) - .world(config.world.clone()) - .backup(config.backup.clone()) - .managers(config.layers.clone()) - .xms(self.xms) - .xmx(self.xmx); + let mut cmd = server::ServerCommand::new(global.server, &global.server_version) + .java(&config.java) + .jar(config.jar.clone()) + .config(global.config.clone()) + .world(global.world.clone()) + .backup(global.backup.clone()) + .managers(global.layers.clone()) + .xms(config.xms) + .xmx(config.xmx); cmd.canonicalize()?; if self.dry { @@ -79,7 +91,7 @@ impl RunArgs { let counter = Arc::new(Mutex::new(cmd.spawn()?)); - if !config.layers.is_empty() { + if !global.layers.is_empty() { let clone = Arc::clone(&counter); std::thread::spawn(move || backups_thread(clone)); } From a51ff3937d48f78ee48748f795bfc5539c0fddf3 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Fri, 11 Aug 2023 23:24:14 +0200 Subject: [PATCH 07/13] refactor: reorder imports --- src/backup/delta.rs | 7 ++--- src/backup/manager/config.rs | 4 +-- src/backup/manager/meta.rs | 15 ++++++----- src/backup/manager/mod.rs | 22 +++++++-------- src/backup/mod.rs | 25 ++++++++--------- src/backup/path.rs | 13 +++++---- src/backup/state.rs | 14 ++++++---- src/cli/backup.rs | 9 ++++--- src/cli/mod.rs | 52 +++++++++++++++++------------------- src/cli/run/mod.rs | 15 ++++++----- src/signals.rs | 15 ++++++----- src/stdin.rs | 6 +++-- 12 files changed, 106 insertions(+), 91 deletions(-) diff --git a/src/backup/delta.rs b/src/backup/delta.rs index 21f626c..1260b43 100644 --- a/src/backup/delta.rs +++ b/src/backup/delta.rs @@ -1,7 +1,8 @@ -use super::State; +use std::{borrow::Borrow, fmt}; + use serde::{Deserialize, Serialize}; -use std::borrow::Borrow; -use std::fmt; + +use super::State; /// Represents the changes relative to the previous backup #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/src/backup/manager/config.rs b/src/backup/manager/config.rs index 75af34e..a07be56 100644 --- a/src/backup/manager/config.rs +++ b/src/backup/manager/config.rs @@ -1,6 +1,4 @@ -use std::error::Error; -use std::fmt; -use std::str::FromStr; +use std::{error::Error, fmt, str::FromStr}; use serde::{Deserialize, Serialize}; diff --git a/src/backup/manager/meta.rs b/src/backup/manager/meta.rs index 4831f3f..a649764 100644 --- a/src/backup/manager/meta.rs +++ b/src/backup/manager/meta.rs @@ -1,10 +1,13 @@ -use super::{Manager, ManagerConfig}; +use std::{ + collections::HashMap, + io, + path::{Path, PathBuf}, +}; + use chrono::Utc; -use serde::Deserialize; -use serde::Serialize; -use std::collections::HashMap; -use std::io; -use std::path::{Path, PathBuf}; +use serde::{Deserialize, Serialize}; + +use super::{Manager, ManagerConfig}; /// Manages a collection of backup layers, allowing them to be utilized as a single object. pub struct MetaManager diff --git a/src/backup/manager/mod.rs b/src/backup/manager/mod.rs index 7cbd43c..ec91a3d 100644 --- a/src/backup/manager/mod.rs +++ b/src/backup/manager/mod.rs @@ -1,20 +1,20 @@ mod config; mod meta; -pub use config::ManagerConfig; -pub use meta::MetaManager; +use std::{ + fs::{File, OpenOptions}, + io, + path::{Path, PathBuf}, +}; + +use chrono::{SubsecRound, Utc}; +use flate2::{write::GzEncoder, Compression}; +use serde::{Deserialize, Serialize}; use super::{Backup, BackupType, Delta, State}; use crate::other; -use chrono::SubsecRound; -use chrono::Utc; -use flate2::write::GzEncoder; -use flate2::Compression; -use serde::Deserialize; -use serde::Serialize; -use std::fs::{File, OpenOptions}; -use std::io; -use std::path::{Path, PathBuf}; +pub use config::ManagerConfig; +pub use meta::MetaManager; /// Manages a single backup layer consisting of one or more chains of backups. pub struct Manager diff --git a/src/backup/mod.rs b/src/backup/mod.rs index 6bd798f..c833b55 100644 --- a/src/backup/mod.rs +++ b/src/backup/mod.rs @@ -4,23 +4,24 @@ pub mod manager; mod path; mod state; +use std::{ + collections::HashSet, + fmt, + fs::File, + io, + path::{Path, PathBuf}, +}; + +use chrono::Utc; +use flate2::{read::GzDecoder, write::GzEncoder, Compression}; +use serde::{Deserialize, Serialize}; + use delta::Delta; pub use manager::Manager; pub use manager::ManagerConfig; pub use manager::MetaManager; -pub use state::State; - -use chrono::Utc; -use flate2::read::GzDecoder; -use flate2::write::GzEncoder; -use flate2::Compression; use path::PathExt; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; -use std::fmt; -use std::fs::File; -use std::io; -use std::path::{Path, PathBuf}; +pub use state::State; const BYTE_SUFFIXES: [&str; 5] = ["B", "KiB", "MiB", "GiB", "TiB"]; diff --git a/src/backup/path.rs b/src/backup/path.rs index 77c6883..d8c2cec 100644 --- a/src/backup/path.rs +++ b/src/backup/path.rs @@ -1,9 +1,12 @@ +use std::{ + collections::HashSet, + ffi::OsString, + fs::{self, DirEntry}, + io, + path::{Path, PathBuf}, +}; + use chrono::{Local, Utc}; -use std::collections::HashSet; -use std::ffi::OsString; -use std::fs::DirEntry; -use std::path::{Path, PathBuf}; -use std::{fs, io}; pub struct ReadDirRecursive { ignored: HashSet, diff --git a/src/backup/state.rs b/src/backup/state.rs index de4b2c0..1f81abb 100644 --- a/src/backup/state.rs +++ b/src/backup/state.rs @@ -1,9 +1,13 @@ -use crate::backup::Delta; +use std::{ + borrow::Borrow, + collections::{HashMap, HashSet}, + ops::{Deref, DerefMut}, + path::{Path, PathBuf}, +}; + use serde::{Deserialize, Serialize}; -use std::borrow::Borrow; -use std::collections::{HashMap, HashSet}; -use std::ops::{Deref, DerefMut}; -use std::path::{Path, PathBuf}; + +use crate::backup::Delta; /// Struct that represents a current state for a backup. This struct acts as a smart pointer around /// a HashMap. diff --git a/src/cli/backup.rs b/src/cli/backup.rs index 85c0cee..548facc 100644 --- a/src/cli/backup.rs +++ b/src/cli/backup.rs @@ -1,10 +1,11 @@ -use crate::backup::Backup; -use crate::other; -use chrono::{TimeZone, Utc}; -use clap::{Args, Subcommand}; use std::io; use std::path::{Path, PathBuf}; +use chrono::{TimeZone, Utc}; +use clap::{Args, Subcommand}; + +use crate::{backup::Backup, other}; + #[derive(Subcommand)] pub enum BackupCommands { /// List all tracked backups diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 7a76ea1..4eaf6f8 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -2,28 +2,33 @@ mod backup; mod config; mod run; -pub use crate::backup::MetaManager; -pub use crate::server::Metadata; -pub use backup::{BackupArgs, BackupCommands}; -pub use run::RunCli; +use std::path::PathBuf; -use crate::backup::ManagerConfig; -use crate::server::ServerType; use clap::{Args, Parser, Subcommand}; -pub use config::Config; use figment::{ providers::{Env, Format, Serialized, Toml}, Figment, }; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; -#[derive(Subcommand)] -pub enum Commands { - /// Run the server - Run(RunCli), - /// Interact with the backup system without starting a server - Backup(BackupArgs), +use crate::{backup::ManagerConfig, server::ServerType}; +use backup::BackupArgs; +use config::Config; +use run::RunCli; + +#[derive(Parser, Serialize)] +#[command(author, version, about, long_about = None)] +pub struct Cli { + #[command(subcommand)] + #[serde(skip)] + pub command: Commands, + + /// Path to a TOML configuration file + #[arg(long = "config-file", global = true)] + pub config_file: Option, + + #[command(flatten)] + pub args: CliArgs, } #[derive(Args, Serialize, Deserialize, Clone)] @@ -60,19 +65,12 @@ pub struct CliArgs { pub server_version: Option, } -#[derive(Parser, Serialize)] -#[command(author, version, about, long_about = None)] -pub struct Cli { - #[command(subcommand)] - #[serde(skip)] - pub command: Commands, - - /// Path to a TOML configuration file - #[arg(long = "config-file", global = true)] - pub config_file: Option, - - #[command(flatten)] - pub args: CliArgs, +#[derive(Subcommand)] +pub enum Commands { + /// Run the server + Run(RunCli), + /// Interact with the backup system without starting a server + Backup(BackupArgs), } impl Cli { diff --git a/src/cli/run/mod.rs b/src/cli/run/mod.rs index fb48491..21c6a0a 100644 --- a/src/cli/run/mod.rs +++ b/src/cli/run/mod.rs @@ -1,15 +1,16 @@ mod config; -use crate::server; -use crate::signals; -use crate::stdin; -use clap::Args; -use config::Config; -use std::path::PathBuf; -use std::sync::{Arc, Mutex}; +use std::{ + path::PathBuf, + sync::{Arc, Mutex}, +}; +use clap::Args; use serde::{Deserialize, Serialize}; +use crate::{server, signals, stdin}; +use config::Config; + #[derive(Args)] pub struct RunCli { #[command(flatten)] diff --git a/src/signals.rs b/src/signals.rs index 61c38f9..5462a75 100644 --- a/src/signals.rs +++ b/src/signals.rs @@ -1,10 +1,13 @@ -use std::io; -use std::sync::atomic::AtomicBool; -use std::sync::{Arc, Mutex}; +use std::{ + io, + sync::{atomic::AtomicBool, Arc, Mutex}, +}; -use signal_hook::consts::TERM_SIGNALS; -use signal_hook::flag; -use signal_hook::iterator::{Signals, SignalsInfo}; +use signal_hook::{ + consts::TERM_SIGNALS, + flag, + iterator::{Signals, SignalsInfo}, +}; use crate::server; diff --git a/src/stdin.rs b/src/stdin.rs index f9a22e8..4a7f761 100644 --- a/src/stdin.rs +++ b/src/stdin.rs @@ -1,5 +1,7 @@ -use std::io; -use std::sync::{Arc, Mutex}; +use std::{ + io, + sync::{Arc, Mutex}, +}; use crate::server; From b3d1cec078557a1f8f47517d3121471e55577fa9 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 12 Aug 2023 11:44:35 +0200 Subject: [PATCH 08/13] feat: granular locking for proper concurrent access to server process --- CHANGELOG.md | 1 + src/cli/run/mod.rs | 25 +++++++++++-------------- src/server/process.rs | 40 ++++++++++++++++++++++------------------ src/signals.rs | 10 ++++------ src/stdin.rs | 17 +++++------------ 5 files changed, 43 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 553fd69..e2e02ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Export command no longer reads backups that do not contribute to the final state +* Running backups no longer block stdin input or shutdown ## [0.3.1](https://git.rustybever.be/Chewing_Bever/alex/src/tag/0.3.1) diff --git a/src/cli/run/mod.rs b/src/cli/run/mod.rs index 21c6a0a..4a11692 100644 --- a/src/cli/run/mod.rs +++ b/src/cli/run/mod.rs @@ -1,9 +1,6 @@ mod config; -use std::{ - path::PathBuf, - sync::{Arc, Mutex}, -}; +use std::{path::PathBuf, sync::Arc}; use clap::Args; use serde::{Deserialize, Serialize}; @@ -45,11 +42,15 @@ pub struct RunArgs { pub xmx: Option, } -fn backups_thread(counter: Arc>) { +fn backups_thread(server: Arc) { loop { let next_scheduled_time = { - let server = counter.lock().unwrap(); - server.backups.next_scheduled_time().unwrap() + server + .backups + .read() + .unwrap() + .next_scheduled_time() + .unwrap() }; let now = chrono::offset::Utc::now(); @@ -57,12 +58,8 @@ fn backups_thread(counter: Arc>) { std::thread::sleep((next_scheduled_time - now).to_std().unwrap()); } - { - let mut server = counter.lock().unwrap(); - - // We explicitely ignore the error here, as we don't want the thread to fail - let _ = server.backup(); - } + // We explicitely ignore the error here, as we don't want the thread to fail + let _ = server.backup(); } } @@ -90,7 +87,7 @@ impl RunCli { return Ok(()); } - let counter = Arc::new(Mutex::new(cmd.spawn()?)); + let counter = Arc::new(cmd.spawn()?); if !global.layers.is_empty() { let clone = Arc::clone(&counter); diff --git a/src/server/process.rs b/src/server/process.rs index 7748c1d..e7ff3d5 100644 --- a/src/server/process.rs +++ b/src/server/process.rs @@ -1,22 +1,21 @@ -use crate::backup::MetaManager; -use crate::server::Metadata; -use std::io::Write; -use std::process::Child; +use std::{io::Write, process::Child, sync::RwLock}; + +use crate::{backup::MetaManager, server::Metadata}; pub struct ServerProcess { - child: Child, - pub backups: MetaManager, + child: RwLock, + pub backups: RwLock>, } impl ServerProcess { pub fn new(manager: MetaManager, child: Child) -> ServerProcess { ServerProcess { - child, - backups: manager, + child: RwLock::new(child), + backups: RwLock::new(manager), } } - pub fn send_command(&mut self, cmd: &str) -> std::io::Result<()> { + pub fn send_command(&self, cmd: &str) -> std::io::Result<()> { match cmd.trim() { "stop" | "exit" => self.stop()?, "backup" => self.backup()?, @@ -26,29 +25,34 @@ impl ServerProcess { Ok(()) } - fn custom(&mut self, cmd: &str) -> std::io::Result<()> { - let mut stdin = self.child.stdin.as_ref().unwrap(); + fn custom(&self, cmd: &str) -> std::io::Result<()> { + let child = self.child.write().unwrap(); + let mut stdin = child.stdin.as_ref().unwrap(); stdin.write_all(format!("{}\n", cmd.trim()).as_bytes())?; stdin.flush()?; Ok(()) } - pub fn stop(&mut self) -> std::io::Result<()> { + pub fn stop(&self) -> std::io::Result<()> { self.custom("stop")?; - self.child.wait()?; + + self.child.write().unwrap().wait()?; Ok(()) } - pub fn kill(&mut self) -> std::io::Result<()> { - self.child.kill() + pub fn kill(&self) -> std::io::Result<()> { + self.child.write().unwrap().kill() } /// Perform a backup by disabling the server's save feature and flushing its data, before /// creating an archive file. - pub fn backup(&mut self) -> std::io::Result<()> { - let layer_name = String::from(self.backups.next_scheduled_layer().unwrap()); + pub fn backup(&self) -> std::io::Result<()> { + // We explicitely lock this entire function to prevent parallel backups + let mut backups = self.backups.write().unwrap(); + + let layer_name = String::from(backups.next_scheduled_layer().unwrap()); self.custom(&format!("say starting backup for layer '{}'", layer_name))?; // Make sure the server isn't modifying the files during the backup @@ -60,7 +64,7 @@ impl ServerProcess { std::thread::sleep(std::time::Duration::from_secs(10)); let start_time = chrono::offset::Utc::now(); - let res = self.backups.perform_backup_cycle(); + let res = backups.perform_backup_cycle(); // The server's save feature needs to be enabled again even if the archive failed to create self.custom("save-on")?; diff --git a/src/signals.rs b/src/signals.rs index 5462a75..87e5c73 100644 --- a/src/signals.rs +++ b/src/signals.rs @@ -1,6 +1,6 @@ use std::{ io, - sync::{atomic::AtomicBool, Arc, Mutex}, + sync::{atomic::AtomicBool, Arc}, }; use signal_hook::{ @@ -37,7 +37,7 @@ pub fn install_signal_handlers() -> io::Result<(Arc, SignalsInfo)> { /// Loop that handles terminating signals as they come in. pub fn handle_signals( signals: &mut SignalsInfo, - counter: Arc>, + server: Arc, ) -> io::Result<()> { let mut force = false; @@ -49,17 +49,15 @@ pub fn handle_signals( // This will currently not work, as the initial stop command will block the kill from // happening. if force { - let mut server = counter.lock().unwrap(); return server.kill(); } // The stop command runs in a separate thread to avoid blocking the signal handling loop. // After stopping the server, the thread terminates the process. else { - let clone = Arc::clone(&counter); + let clone = Arc::clone(&server); std::thread::spawn(move || { - let mut server = clone.lock().unwrap(); - let _ = server.stop(); + let _ = clone.stop(); std::process::exit(0); }); } diff --git a/src/stdin.rs b/src/stdin.rs index 4a7f761..8317671 100644 --- a/src/stdin.rs +++ b/src/stdin.rs @@ -1,11 +1,8 @@ -use std::{ - io, - sync::{Arc, Mutex}, -}; +use std::{io, sync::Arc}; use crate::server; -pub fn handle_stdin(counter: Arc>) { +pub fn handle_stdin(server: Arc) { let stdin = io::stdin(); let input = &mut String::new(); @@ -16,13 +13,9 @@ pub fn handle_stdin(counter: Arc>) { continue; }; - { - let mut server = counter.lock().unwrap(); - - if let Err(e) = server.send_command(input) { - println!("{}", e); - }; - } + if let Err(e) = server.send_command(input) { + println!("{}", e); + }; if input.trim() == "stop" { std::process::exit(0); From 5f6366078cc0e608f7fd16a33d95e7bc3ec09863 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 12 Aug 2023 13:46:46 +0200 Subject: [PATCH 09/13] fix: properly parse layers env var --- src/cli/mod.rs | 29 +++++++++++++++++++++++------ src/cli/run/mod.rs | 1 - src/server/mod.rs | 1 + 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4eaf6f8..0bf55f9 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -2,7 +2,7 @@ mod backup; mod config; mod run; -use std::path::PathBuf; +use std::{path::PathBuf, str::FromStr}; use clap::{Args, Parser, Subcommand}; use figment::{ @@ -83,7 +83,7 @@ impl Cli { } } - pub fn config(&self, args: &U) -> Result + pub fn config(&self, args: &U) -> crate::Result where T: Default + Serialize + for<'de> Deserialize<'de>, U: Serialize, @@ -92,11 +92,28 @@ impl Cli { .config_file .clone() .unwrap_or(PathBuf::from(Env::var_or("ALEX_CONFIG_FILE", ""))); - Figment::new() + + let mut figment = Figment::new() .merge(Serialized::defaults(T::default())) .merge(Toml::file(toml_file)) - .merge(Env::prefixed("ALEX_")) - .merge(Serialized::defaults(args)) - .extract() + .merge(Env::prefixed("ALEX_").ignore(&["ALEX_LAYERS"])); + + // Layers need to be parsed separately, as the env var format is different than the one + // serde expects + if let Some(layers_env) = Env::var("ALEX_LAYERS") { + let res = layers_env + .split(';') + .map(ManagerConfig::from_str) + .collect::>(); + + if res.iter().any(|e| e.is_err()) { + return Err(crate::other("Invalid layer configuration").into()); + } + + let layers: Vec<_> = res.iter().flatten().collect(); + figment = figment.merge(Serialized::default("layers", layers)); + } + + Ok(figment.merge(Serialized::defaults(args)).extract()?) } } diff --git a/src/cli/run/mod.rs b/src/cli/run/mod.rs index 4a11692..37ddd1b 100644 --- a/src/cli/run/mod.rs +++ b/src/cli/run/mod.rs @@ -66,7 +66,6 @@ fn backups_thread(server: Arc) { impl RunCli { pub fn run(&self, cli: &super::Cli, global: &super::Config) -> crate::Result<()> { let config: Config = cli.config(&self.args)?; - println!("{:?}", config); let (_, mut signals) = signals::install_signal_handlers()?; diff --git a/src/server/mod.rs b/src/server/mod.rs index 23a9ff5..2939af7 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use std::fmt; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize, Debug)] +#[serde(rename_all = "lowercase")] pub enum ServerType { Unknown, Paper, From 8f190c489b079f1495fc3acb2ff7b8d3b364b507 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 12 Aug 2023 13:58:13 +0200 Subject: [PATCH 10/13] chore: update changelog --- CHANGELOG.md | 2 ++ Dockerfile | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e02ad..652c541 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Export command no longer reads backups that do not contribute to the final state * Running backups no longer block stdin input or shutdown +* Env vars `ALEX_CONFIG_DIR`, `ALEX_WORLD_DIR` and `ALEX_BACKUP_DIR` renamed to + `ALEX_CONFIG`, `ALEX_WORLD` and `ALEX_BACKUP` respectively ## [0.3.1](https://git.rustybever.be/Chewing_Bever/alex/src/tag/0.3.1) diff --git a/Dockerfile b/Dockerfile index 7c63117..819cbf2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,9 +47,9 @@ COPY --from=builder /app/target/debug/alex /bin/alex RUN chmod +x /bin/alex # Default value to keep users from eating up all ram accidentally -ENV ALEX_CONFIG_DIR=/app/config \ - ALEX_WORLD_DIR=/app/worlds \ - ALEX_BACKUP_DIR=/app/backups \ +ENV ALEX_CONFIG=/app/config \ + ALEX_WORLD=/app/worlds \ + ALEX_BACKUP=/app/backups \ ALEX_SERVER=paper \ ALEX_XMS=1024 \ ALEX_XMX=2048 \ From 5bdd4e21b0e9d767045bc24d9875786a7fba2478 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 12 Aug 2023 14:09:22 +0200 Subject: [PATCH 11/13] chore(ci): modernize config --- .woodpecker/arch-release.yml | 14 +++++++------- .woodpecker/build.yml | 15 ++++++++------- .woodpecker/clippy.yml | 15 ++++++++------- .woodpecker/lint.yml | 15 ++++++++------- .woodpecker/release.yml | 15 +++++++-------- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/.woodpecker/arch-release.yml b/.woodpecker/arch-release.yml index ece2c52..1bd073f 100644 --- a/.woodpecker/arch-release.yml +++ b/.woodpecker/arch-release.yml @@ -3,11 +3,15 @@ matrix: - 'linux/amd64' # - linux/arm64 -platform: ${PLATFORM} -branches: [ main ] +labels: + platform: ${PLATFORM} + +when: + event: tag + skip_clone: true -pipeline: +steps: build: image: 'menci/archlinuxarm:base-devel' pull: true @@ -23,8 +27,6 @@ pipeline: - su builder - curl -OL "https://git.rustybever.be/Chewing_Bever/alex/raw/tag/$CI_COMMIT_TAG/PKGBUILD" - makepkg -s --noconfirm --needed - when: - event: tag publish: image: 'curlimages/curl' @@ -33,5 +35,3 @@ pipeline: - 'for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $VIETER_API_KEY" https://arch.r8r.be/bur/publish; done' secrets: - vieter_api_key - when: - event: tag diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index a1722be..768e2c8 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -3,19 +3,20 @@ matrix: - 'amd64' - 'arm64' -platform: "linux/${ARCH}" +labels: + platform: "linux/${ARCH}" -branches: - exclude: [main] +when: + branch: + exclude: [main] + event: push -pipeline: +steps: build: - image: 'rust:1.70-alpine3.18' + image: 'rust:1.71-alpine3.18' commands: - apk add --no-cache build-base - cargo build --verbose - cargo test --verbose # Binaries, even debug ones, should be statically compiled - '[ "$(readelf -d target/debug/alex | grep NEEDED | wc -l)" = 0 ]' - when: - event: [push] diff --git a/.woodpecker/clippy.yml b/.woodpecker/clippy.yml index 61b150d..5f23363 100644 --- a/.woodpecker/clippy.yml +++ b/.woodpecker/clippy.yml @@ -1,13 +1,14 @@ -platform: 'linux/amd64' +labels: + platform: 'linux/amd64' -branches: - exclude: [main] +when: + branch: + exclude: [ main ] + event: push -pipeline: +steps: clippy: - image: 'rust:1.70' + image: 'rust:1.71' commands: - rustup component add clippy - cargo clippy -- --no-deps -Dwarnings - when: - event: [push] diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index b74d26d..15e45dc 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -1,13 +1,14 @@ -platform: 'linux/amd64' +labels: + platform: 'linux/amd64' -branches: - exclude: [main] +when: + branch: + exclude: [ main ] + event: push -pipeline: +steps: lint: - image: 'rust:1.70' + image: 'rust:1.71' commands: - rustup component add rustfmt - cargo fmt -- --check - when: - event: [push] diff --git a/.woodpecker/release.yml b/.woodpecker/release.yml index f7d44db..892dced 100644 --- a/.woodpecker/release.yml +++ b/.woodpecker/release.yml @@ -3,20 +3,21 @@ matrix: - 'linux/amd64' - 'linux/arm64' -platform: ${PLATFORM} -branches: [ main ] +labels: + platform: ${PLATFORM} -pipeline: +when: + event: tag + +steps: build: - image: 'rust:1.70-alpine3.18' + image: 'rust:1.71-alpine3.18' commands: - apk add --no-cache build-base - cargo build --release --verbose # Ensure the release binary is also statically compiled - '[ "$(readelf -d target/release/alex | grep NEEDED | wc -l)" = 0 ]' - du -h target/release/alex - when: - event: tag publish: image: 'curlimages/curl' @@ -28,5 +29,3 @@ pipeline: --user "Chewing_Bever:$GITEA_PASSWORD" --upload-file target/release/alex https://git.rustybever.be/api/packages/Chewing_Bever/generic/alex/"${CI_COMMIT_TAG}"/alex-"$(echo '${PLATFORM}' | sed 's:/:-:g')" - when: - event: tag From f2e781dd5a7868fd7181e8d5d41427827ad7d26a Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 12 Aug 2023 14:50:06 +0200 Subject: [PATCH 12/13] chore: bump dependency versions --- Cargo.lock | 187 ++++++++++++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1854dec..e03b1d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,15 +54,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" dependencies = [ "anstyle", "windows-sys", @@ -104,6 +104,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bumpalo" version = "3.13.0" @@ -112,9 +118,12 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -140,9 +149,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.1" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ed2379f8603fa2b7509891660e802b88c70a79a6427a70abb5968054de2c28" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" dependencies = [ "clap_builder", "clap_derive", @@ -151,22 +160,21 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.1" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.3.1" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e9ef9a08ee1c0e1f2e162121665ac45ac3783b0f897db7244ae75ad9a8f65b" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", "proc-macro2", @@ -209,9 +217,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -244,9 +252,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", @@ -278,15 +286,15 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -321,61 +329,49 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys", -] - [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "io-lifetimes", "rustix", "windows-sys", ] [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "log" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" @@ -394,18 +390,18 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.17.2" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "pear" @@ -432,9 +428,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -454,31 +450,30 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "rustix" -version = "0.37.19" +version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", "windows-sys", @@ -486,24 +481,24 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" -version = "1.0.164" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", @@ -512,9 +507,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -532,9 +527,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", @@ -557,9 +552,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.18" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -568,9 +563,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" dependencies = [ "filetime", "libc", @@ -633,9 +628,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "utf8parse" @@ -657,9 +652,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -667,9 +662,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", @@ -682,9 +677,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -692,9 +687,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", @@ -705,9 +700,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "winapi" @@ -751,9 +746,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -808,24 +803,24 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f495880723d0999eb3500a9064d8dbcf836460b24c17df80ea7b5794053aac" +checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" dependencies = [ "memchr", ] [[package]] name = "xattr" -version = "0.2.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" dependencies = [ "libc", ] [[package]] name = "yansi" -version = "1.0.0-rc" +version = "1.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee746ad3851dd3bc40e4a028ab3b00b99278d929e48957bcb2d111874a7e43e" +checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" From 9ce2417528aec8b20942ca908a48d028ebcbd511 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 12 Aug 2023 15:06:58 +0200 Subject: [PATCH 13/13] chore: bump versions to 0.4.0 --- CHANGELOG.md | 2 ++ Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 652c541..44603b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/Chewing_Bever/alex/src/branch/dev) +## [0.4.0](https://git.rustybever.be/Chewing_Bever/alex/src/tag/0.4.0) + ### Added * Extract command for working with the output of export diff --git a/Cargo.toml b/Cargo.toml index 09a8b1b..f37c2ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alex" -version = "0.3.1" +version = "0.4.0" description = "Wrapper around Minecraft server processes, designed to complement Docker image installations." authors = ["Jef Roosens"] edition = "2021"