Compare commits

..

No commits in common. "bbe193139c548626640f320e1e444877113818ec" and "165d577a3d876ba36070afa45419283e32dfdb8a" have entirely different histories.

13 changed files with 635 additions and 745 deletions

View file

@ -1,37 +0,0 @@
name: build
on: push
env:
DEV_SHELL_NAME: CI
CI: true
jobs:
build-cargo:
runs-on: flakes-action
steps:
- uses: actions/checkout@v3
name: Checkout
- name:
run: |
nix develop -c cargo build --release
build-nix:
runs-on: flakes-action
steps:
- uses: actions/checkout@v3
name: Checkout
- name:
run: |
nix build
check-nix:
runs-on: flakes-action
steps:
- uses: actions/checkout@v3
name: Checkout
- name: Metadata
run: |
nix flake metadata
- name: Statix
run: |
nix develop -c statix check
- name: Flake check
run: |
nix flake check

1028
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -3,36 +3,31 @@ name = "bakare"
version = "0.1.0" version = "0.1.0"
authors = ["Cyryl Płotnicki <cyplo@cyplo.dev>"] authors = ["Cyryl Płotnicki <cyplo@cyplo.dev>"]
edition = "2021" edition = "2021"
rust-version = "1.63" rust-version = "1.56"
license = "AGPL-3.0" license = "AGPL-3.0"
description = "modern and simple, yet efficient backup solution" description = "modern and simple, yet efficient backup solution"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
base64 = "0.21" base64 = "0.13"
blake = "2" blake = "2"
chacha20poly1305 = "0.10" chacha20poly1305 = "0.9"
fail = "0.5" fail = "0.4"
femme = "2" femme = "2"
hex = "0.4" hex = "0.4"
log = "0.4" log = "0.4"
nix = "0.23"
rand = "0.8" rand = "0.8"
reed-solomon = "0.2" reed-solomon = "0.2"
seahorse = "2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1" serde_json = "1"
sha2 = "0.10" sha2 = "0.9"
tempfile = "3" tempfile = "3"
uuid = { version = "1", features = ["v4"] } uuid = { version = "0.8", features = ["v4"] }
walkdir = "2" walkdir = "2"
[dependencies.nix]
version = "0.27"
default-features = false
features = ["process"]
[dev-dependencies] [dev-dependencies]
criterion = "0.5" criterion = "0.3"
pretty_assertions = "1" pretty_assertions = "1"
proptest = "1" proptest = "1"
two-rusty-forks = "0.4" two-rusty-forks = "0.4"

View file

@ -1,13 +1,29 @@
{ {
"nodes": { "nodes": {
"crate2nix": {
"flake": false,
"locked": {
"lastModified": 1634898841,
"narHash": "sha256-CZgjBo0rYeQHiIfnFD5wj9vmI/O24IaCT0yzPnW0FSQ=",
"owner": "kolloch",
"repo": "crate2nix",
"rev": "d8566765a23c5f8f8e50a726bb0db7957452b5e8",
"type": "github"
},
"original": {
"owner": "kolloch",
"repo": "crate2nix",
"type": "github"
}
},
"flake-compat": { "flake-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1696426674, "lastModified": 1627913399,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "narHash": "sha256-hY8g6H2KFL8ownSiFeMOjwPC8P0ueXpCVEbxgda3pko=",
"owner": "edolstra", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "rev": "12c64ca55c1014cdc1b16ed5a804aa8576601ff2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -16,85 +32,87 @@
"type": "github" "type": "github"
} }
}, },
"naersk": { "flake-utils": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": { "locked": {
"lastModified": 1698420672, "lastModified": 1623875721,
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=", "narHash": "sha256-A8BU7bjS5GirpAUv4QA+QnJ4CceLHkcXdRp4xITDB0s=",
"owner": "nix-community", "owner": "numtide",
"repo": "naersk", "repo": "flake-utils",
"rev": "aeb58d5e8faead8980a807c840232697982d47b9", "rev": "f7e004a55b120c02ecb6219596820fcd32ca8772",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "numtide",
"repo": "naersk", "repo": "flake-utils",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1699065553, "lastModified": 1634758644,
"narHash": "sha256-j8UmH8fqXcOgL6WrlMcvV2m2XQ6OzU0IBucyuJ0vnyQ=", "narHash": "sha256-H3UW/msC6wadg28lcgZv2Ge/P7dWxesL6i37a0GOeyM=",
"owner": "NixOS", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "8ab9c53eee434651ce170dee1d9727b974e9a6b6", "rev": "70904d4a9927a4d6e05c72c4aaac4370e05107f3",
"type": "github" "type": "github"
}, },
"original": { "original": {
"id": "nixpkgs", "owner": "nixos",
"type": "indirect" "ref": "nixos-21.05",
"repo": "nixpkgs",
"type": "github"
} }
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1699065553, "lastModified": 1628186154,
"narHash": "sha256-j8UmH8fqXcOgL6WrlMcvV2m2XQ6OzU0IBucyuJ0vnyQ=", "narHash": "sha256-r2d0wvywFnL9z4iptztdFMhaUIAaGzrSs7kSok0PgmE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "8ab9c53eee434651ce170dee1d9727b974e9a6b6", "rev": "06552b72346632b6943c8032e57e702ea12413bf",
"type": "github" "type": "github"
}, },
"original": { "original": {
"id": "nixpkgs", "owner": "NixOS",
"type": "indirect" "repo": "nixpkgs",
"type": "github"
} }
}, },
"root": { "root": {
"inputs": { "inputs": {
"crate2nix": "crate2nix",
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"naersk": "naersk", "nixpkgs": "nixpkgs",
"nixpkgs": "nixpkgs_2", "rust-overlay": "rust-overlay",
"utils": "utils" "utils": "utils"
} }
}, },
"systems": { "rust-overlay": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs_2"
},
"locked": { "locked": {
"lastModified": 1681028828, "lastModified": 1634869268,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "narHash": "sha256-RVAcEFlFU3877Mm4q/nbXGEYTDg/wQNhzmXGMTV6wBs=",
"owner": "nix-systems", "owner": "oxalica",
"repo": "default", "repo": "rust-overlay",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "rev": "c02c2d86354327317546501af001886fbb53d374",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-systems", "owner": "oxalica",
"repo": "default", "repo": "rust-overlay",
"type": "github" "type": "github"
} }
}, },
"utils": { "utils": {
"inputs": {
"systems": "systems"
},
"locked": { "locked": {
"lastModified": 1694529238, "lastModified": 1634851050,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", "narHash": "sha256-N83GlSGPJJdcqhUxSCS/WwW5pksYf3VP1M13cDRTSVA=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "rev": "c91f3de5adaf1de973b797ef7485e441a65b8935",
"type": "github" "type": "github"
}, },
"original": { "original": {

132
flake.nix
View file

@ -1,59 +1,95 @@
{ {
description = "Bakare: modern and simple, yet efficient backup solution"; description = "A simple yet robust commandline random password generator.";
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-21.05";
utils.url = "github:numtide/flake-utils"; utils.url = "github:numtide/flake-utils";
naersk.url = "github:nix-community/naersk"; rust-overlay.url = "github:oxalica/rust-overlay";
crate2nix = {
url = "github:kolloch/crate2nix";
flake = false;
};
flake-compat = { flake-compat = {
url = "github:edolstra/flake-compat"; url = "github:edolstra/flake-compat";
flake = false; flake = false;
}; };
}; };
outputs = { outputs = { self, nixpkgs, utils, rust-overlay, crate2nix, ... }:
self, let
nixpkgs, name = "bakare";
utils, rustChannel = "stable";
naersk, in
flake-compat, utils.lib.eachDefaultSystem
}: (system:
utils.lib.eachDefaultSystem (system: let let
pkgs = nixpkgs.legacyPackages."${system}"; # Imports
naersk-lib = naersk.lib."${system}"; pkgs = import nixpkgs {
in rec { inherit system;
# `nix build` overlays = [
packages.bakare = naersk-lib.buildPackage { rust-overlay.overlay
pname = "bakare"; (self: super: {
root = ./.; # Because rust-overlay bundles multiple rust packages into one
}; # derivation, specify that mega-bundle here, so that crate2nix
defaultPackage = packages.bakare; # will use them automatically.
rustc = self.rust-bin.${rustChannel}.latest.default;
cargo = self.rust-bin.${rustChannel}.latest.default;
})
];
};
inherit (import "${crate2nix}/tools.nix" { inherit pkgs; })
generatedCargoNix;
# `nix run` # Create the cargo2nix project
apps.bakare = utils.lib.mkApp {drv = packages.bakare;}; project = pkgs.callPackage
defaultApp = apps.bakare; (generatedCargoNix {
inherit name;
src = ./.;
})
{
# Individual crate overrides go here
# Example: https://github.com/balsoft/simple-osd-daemons/blob/6f85144934c0c1382c7a4d3a2bbb80106776e270/flake.nix#L28-L50
defaultCrateOverrides = pkgs.defaultCrateOverrides // {
# The himalaya crate itself is overriden here. Typically we
# configure non-Rust dependencies (see below) here.
${name} = oldAttrs: {
inherit buildInputs nativeBuildInputs;
};
};
};
buildInputs = with pkgs; [ openssl.dev cacert openssh zlib ];
nativeBuildInputs = with pkgs; [ rustc cargo pkgconfig git ];
in
rec {
packages.${name} = project.rootCrate.build;
# `nix build`
defaultPackage = packages.${name};
# `nix run`
apps.${name} = utils.lib.mkApp {
inherit name;
drv = packages.${name};
};
defaultApp = apps.${name};
# `nix develop`
devShell = pkgs.mkShell
{
inputsFrom = builtins.attrValues self.packages.${system};
buildInputs = buildInputs ++ (with pkgs;
[
nixpkgs-fmt
cargo-watch
cargo-edit
cargo-outdated
pkgs.rust-bin.${rustChannel}.latest.rust-analysis
pkgs.rust-bin.${rustChannel}.latest.rls
]);
RUST_SRC_PATH = "${pkgs.rust-bin.${rustChannel}.latest.rust-src}/lib/rustlib/src/rust/library";
};
}
);
}
# `nix develop`
devShell = pkgs.mkShell {
nativeBuildInputs = with pkgs; [
cacert
cargo
cargo-edit
cargo-mutants
cargo-nextest
cargo-outdated
cargo-release
cargo-watch
clippy
git
llvmPackages_13.llvm
nixpkgs-fmt
openssh
openssl
pkg-config
rustc
rustfmt
statix
];
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
};
});
}

View file

@ -1,12 +1,12 @@
(import (import
( (
let let
lock = builtins.fromJSON (builtins.readFile ./flake.lock); lock = builtins.fromJSON (builtins.readFile ./flake.lock);
in in
fetchTarball { fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
sha256 = lock.nodes.flake-compat.locked.narHash; sha256 = lock.nodes.flake-compat.locked.narHash;
} }
) )
{ {
src = ./.; src = ./.;

View file

@ -6,8 +6,9 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use chacha20poly1305::{aead::Aead, Key, KeyInit, XChaCha20Poly1305, XNonce}; use chacha20poly1305::aead::{Aead, NewAead};
use nix::unistd::getpid; use chacha20poly1305::{Key, XChaCha20Poly1305, XNonce}; // Or `XChaCha20Poly1305`
use uuid::Uuid; use uuid::Uuid;
use crate::index::item::IndexItem; use crate::index::item::IndexItem;
@ -17,6 +18,7 @@ use crate::repository::ItemId;
use anyhow::Result; use anyhow::Result;
use anyhow::*; use anyhow::*;
use lock::Lock; use lock::Lock;
use nix::unistd::getpid;
use std::{cmp::max, io::Write}; use std::{cmp::max, io::Write};
impl Index { impl Index {

View file

@ -96,14 +96,14 @@ impl Lock {
let lock_file_path = Lock::lock_file_path(index_directory, lock_id)?; let lock_file_path = Lock::lock_file_path(index_directory, lock_id)?;
fail_point!("create-lock-file", |e: Option<String>| Err(anyhow!(e.unwrap()))); fail_point!("create-lock-file", |e: Option<String>| Err(anyhow!(e.unwrap())));
let mut file = File::create(lock_file_path)?; let mut file = File::create(lock_file_path)?;
let lock_id_text = lock_id.as_hyphenated().to_string(); let lock_id_text = lock_id.to_hyphenated().to_string();
let lock_id_bytes = lock_id_text.as_bytes(); let lock_id_bytes = lock_id_text.as_bytes();
Ok(file.write_all(lock_id_bytes)?) Ok(file.write_all(lock_id_bytes)?)
} }
fn lock_file_path(path: &Path, lock_id: Uuid) -> Result<PathBuf> { fn lock_file_path(path: &Path, lock_id: Uuid) -> Result<PathBuf> {
let file_name = format!("{}{}", lock_id, FILE_EXTENSION); let file_name = format!("{}{}", lock_id, FILE_EXTENSION);
Ok(path.join(file_name)) Ok(path.join(&file_name))
} }
} }

View file

@ -1,13 +1 @@
use seahorse::App; fn main() {}
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let app = App::new(env!("CARGO_PKG_NAME"))
.description(env!("CARGO_PKG_DESCRIPTION"))
.author(env!("CARGO_PKG_AUTHORS"))
.version(env!("CARGO_PKG_VERSION"))
.action(|c| println!("Hello, {:?}", c.args));
app.run(args);
}

View file

@ -39,7 +39,7 @@ impl RepositoryItem {
let original_source_path = Path::new(self.original_source_path()); let original_source_path = Path::new(self.original_source_path());
let source_path_relative = original_source_path.strip_prefix("/")?; let source_path_relative = original_source_path.strip_prefix("/")?;
let target_path = save_to.join(source_path_relative); let target_path = save_to.join(&source_path_relative);
let parent = target_path let parent = target_path
.parent() .parent()
.ok_or_else(|| anyhow!("cannot compute parent path for {}", &target_path.to_string_lossy()))?; .ok_or_else(|| anyhow!("cannot compute parent path for {}", &target_path.to_string_lossy()))?;

View file

@ -43,15 +43,13 @@ pub struct RepositoryItemIterator<'a> {
//TODO: move to serializers::base64 //TODO: move to serializers::base64
mod base64 { mod base64 {
use ::base64; use ::base64;
use base64::{engine::general_purpose, Engine};
use serde::{de, Deserialize, Deserializer, Serializer}; use serde::{de, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
{ {
let encoded = general_purpose::STANDARD.encode(bytes); serializer.serialize_str(&base64::encode(bytes))
serializer.serialize_str(&encoded)
} }
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error> pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
@ -59,7 +57,7 @@ mod base64 {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let s = <&str>::deserialize(deserializer)?; let s = <&str>::deserialize(deserializer)?;
general_purpose::STANDARD.decode(s).map_err(de::Error::custom) base64::decode(s).map_err(de::Error::custom)
} }
} }
@ -99,7 +97,7 @@ impl Debug for ItemId {
} }
} }
impl Repository { impl<'a> Repository {
pub fn init(path: &Path, secret: &str) -> Result<Repository> { pub fn init(path: &Path, secret: &str) -> Result<Repository> {
fs::create_dir_all(path)?; fs::create_dir_all(path)?;
let mut index = Index::new()?; let mut index = Index::new()?;
@ -131,7 +129,7 @@ impl Repository {
pub fn store(&mut self, source_path: &Path) -> Result<()> { pub fn store(&mut self, source_path: &Path) -> Result<()> {
let id = Repository::calculate_id(source_path)?; let id = Repository::calculate_id(source_path)?;
let destination = self.data_dir()?; let destination = self.data_dir()?;
let destination = destination.join(id.to_string()); let destination = destination.join(&id.to_string());
if !source_path.metadata()?.is_file() { if !source_path.metadata()?.is_file() {
return Ok(()); return Ok(());
@ -141,9 +139,9 @@ impl Repository {
.ok_or_else(|| anyhow!("cannot compute parent path for {}", &destination.to_string_lossy()))?; .ok_or_else(|| anyhow!("cannot compute parent path for {}", &destination.to_string_lossy()))?;
fs::create_dir_all(parent)?; fs::create_dir_all(parent)?;
if !destination.exists() { if !destination.exists() {
fs::copy(source_path, &destination)?; fs::copy(&source_path, &destination)?;
} }
let relative_path = destination.strip_prefix(self.path())?; let relative_path = destination.strip_prefix(&self.path())?;
self.index.remember(source_path, &relative_path.to_string_lossy(), id); self.index.remember(source_path, &relative_path.to_string_lossy(), id);
Ok(()) Ok(())
} }

View file

@ -50,7 +50,7 @@ pub mod in_memory {
restore_engine.restore(&item.unwrap())?; restore_engine.restore(&item.unwrap())?;
let source_file_relative_path = Path::new(source_file_full_path).strip_prefix("/")?; let source_file_relative_path = Path::new(source_file_full_path).strip_prefix("/")?;
let restored_file_path = restore_target.path().join(source_file_relative_path); let restored_file_path = restore_target.path().join(&source_file_relative_path);
assert_target_file_contents(&restored_file_path, contents) assert_target_file_contents(&restored_file_path, contents)
} }
@ -67,7 +67,7 @@ pub mod in_memory {
let restore_engine = restore::Engine::new(&mut restore_repository, restore_target.path())?; let restore_engine = restore::Engine::new(&mut restore_repository, restore_target.path())?;
restore_engine.restore(&old_item.unwrap())?; restore_engine.restore(&old_item.unwrap())?;
let source_file_relative_path = Path::new(source_file_full_path).strip_prefix("/")?; let source_file_relative_path = Path::new(source_file_full_path).strip_prefix("/")?;
let restored_file_path = restore_target.path().join(source_file_relative_path); let restored_file_path = restore_target.path().join(&source_file_relative_path);
assert_target_file_contents(&restored_file_path, old_contents) assert_target_file_contents(&restored_file_path, old_contents)
} }

View file

@ -22,7 +22,7 @@ mod must {
let dir = tempdir()?; let dir = tempdir()?;
let repository_path = dir.path(); let repository_path = dir.path();
let repository_path = repository_path.join(format!("repository-{}", getpid())); let repository_path = repository_path.join(&format!("repository-{}", getpid()));
let secret = "some secret"; let secret = "some secret";
Repository::init(&repository_path, secret)?; Repository::init(&repository_path, secret)?;