Radish alpha
r
Git libraries for Radicle
Radicle
Git (anonymous pull)
Log in to clone via SSH
git-storage: remove owner and config
Fintan Halpenny committed 3 years ago
commit 0730453ac5a898353504626b122b15f99b877b88
parent ee7a9029ce2c1e5e2632e0a095ecf5b36ac392a4
7 files changed +35 -351
modified git-storage/src/backend/read.rs
@@ -3,13 +3,12 @@
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
// Linking Exception. For full terms see the included LICENSE file.

-
use std::{path::Path, str::FromStr};
+
use std::path::Path;

use git_ext::{error::is_not_found_err, Oid};
use std_ext::result::ResultExt as _;

use crate::{
-
    config::Config,
    glob,
    odb::{self, Blob, Commit, Object, Tag, Tree},
    refdb::{self, Reference, References, ReferencesGlob},
@@ -45,25 +44,12 @@ impl Read {

        Ok(Self { raw })
    }
-

-
    /// Read the identifier from the underlying [`Config`]. The identifier is
-
    /// expected to exist already.
-
    ///
-
    /// See [`crate::config::Owner`] for more information on the identifier.
-
    pub fn rad_identifier<Id>(&self) -> Result<Id, error::Identifier>
-
    where
-
        Id: FromStr,
-
        Id::Err: std::error::Error + Send + Sync + 'static,
-
    {
-
        let config = Config::try_from(&self.raw)?;
-
        Ok(config.rad_identifier::<Id>()?)
-
    }
}

pub mod error {
    use thiserror::Error;

-
    use crate::{config, refdb::error::ParseReference};
+
    use crate::refdb::error::ParseReference;

    #[derive(Debug, Error)]
    pub enum Init {
@@ -74,8 +60,6 @@ pub mod error {
    #[derive(Debug, Error)]
    pub enum Identifier {
        #[error(transparent)]
-
        Config(#[from] config::Error),
-
        #[error(transparent)]
        Git(#[from] git2::Error),
    }

modified git-storage/src/backend/write.rs
@@ -3,20 +3,14 @@
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
// Linking Exception. For full terms see the included LICENSE file.

-
use std::{
-
    marker::PhantomData,
-
    path::{Path, PathBuf},
-
    str::FromStr,
-
};
+
use std::path::Path;

use either::Either;

use git_ext::{error::is_not_found_err, Oid};
use git_ref_format::{Qualified, RefStr, RefString};
-
use std_ext::Void;

use crate::{
-
    config::{self, Config},
    odb,
    refdb::{
        self,
@@ -42,23 +36,14 @@ pub mod error;
/// For write access to the refdb see [`refdb::Write`].
///
/// To construct the `Write` storage use [`Read::open`].
-
pub struct Write<Owner> {
+
pub struct Write {
    inner: Read,
-
    owner: Owner,
    info: UserInfo,
}

-
impl<O> Write<O>
-
where
-
    O: config::Owner,
-
    <O::RadIdentifier as FromStr>::Err: std::error::Error + Send + Sync + 'static,
-
{
+
impl Write {
    /// Open the [`Write`] storage, initialising it if it doesn't exist.
    ///
-
    /// Note that a [`Write`] is tied to the [`config::Owner`] with which it was
-
    /// initialised, attempting to open it with a different one (that is, a
-
    /// different owner) will return an error.
-
    ///
    /// # Concurrency
    ///
    /// [`Write`] can be sent between threads, but it can't be shared between
@@ -66,71 +51,38 @@ where
    /// the same way two `git` processes can access the same repository.
    /// However, if you need multiple [`Write`]s to be shared between
    /// threads, use a [`crate::Pool`] instead.
-
    pub fn open<P: AsRef<Path>>(path: P, info: UserInfo, owner: O) -> Result<Self, error::Init> {
+
    pub fn open<P: AsRef<Path>>(path: P, info: UserInfo) -> Result<Self, error::Init> {
        crate::init();

        let path = path.as_ref();
        let raw = match git2::Repository::open_bare(path) {
            Err(e) if is_not_found_err(&e) => {
-
                let mut backend = git2::Repository::init_opts(
+
                let backend = git2::Repository::init_opts(
                    path,
                    git2::RepositoryInitOptions::new()
                        .bare(true)
                        .no_reinit(true)
                        .external_template(false),
                )?;
-
                Config::init(&mut backend, &owner, info.clone())?;
-

                Ok(backend)
            },
            Ok(repo) => Ok(repo),
            Err(e) => Err(e),
        }?;

-
        let config = Config::try_from(&raw)?;
-
        let actual = config.rad_identifier::<O::RadIdentifier>()?;
-
        let current = owner.rad_identifier();
-

-
        if actual != current {
-
            return Err(error::Init::OwnerMismatch {
-
                current: current.to_string(),
-
                actual: actual.to_string(),
-
            });
-
        }
-

        Ok(Self {
            inner: Read { raw },
-
            owner,
            info,
        })
    }
-

-
    /// Get the underlying [`Config`] of the [`Write`] storage.
-
    pub fn config(&self) -> Result<Config<O>, config::Error> {
-
        Config::try_from(self)
-
    }
-

-
    /// Get the read-only [`Config`] of the [`Write`] storage.
-
    pub fn config_readonly(&self) -> Result<Config<PhantomData<Void>>, git2::Error> {
-
        Config::try_from(&self.inner.raw)
-
    }
-

-
    pub(crate) fn config_path(&self) -> PathBuf {
-
        config::path(&self.inner.raw)
-
    }
}

-
impl<O> Write<O> {
+
impl Write {
    /// Return a read-only handle of the storage.
    pub fn read_only(&self) -> &Read {
        &self.inner
    }

-
    /// Return the owner of the storage.
-
    pub fn owner(&self) -> &O {
-
        &self.owner
-
    }
-

    /// Return the [`UserInfo`] of the storage.
    pub fn info(&self) -> &UserInfo {
        &self.info
@@ -143,7 +95,7 @@ impl<O> Write<O> {

// refdb impls

-
impl<'a, S> refdb::Read for &'a Write<S> {
+
impl<'a> refdb::Read for &'a Write {
    type FindRef = <&'a Read as refdb::Read>::FindRef;
    type FindRefs = <&'a Read as refdb::Read>::FindRefs;
    type FindRefOid = <&'a Read as refdb::Read>::FindRefOid;
@@ -172,7 +124,7 @@ impl<'a, S> refdb::Read for &'a Write<S> {
    }
}

-
impl<'a, S> refdb::Write for &'a Write<S> {
+
impl<'a> refdb::Write for &'a Write {
    type UpdateError = error::Update;

    fn update<'b, U>(&mut self, updates: U) -> Result<refdb::write::Applied<'b>, Self::UpdateError>
@@ -217,13 +169,13 @@ impl<'a, S> refdb::Write for &'a Write<S> {

/// An internal struct combining a [`Write`] and [`git2::Transaction`].
// TODO: include optional namespace
-
struct Transaction<'a, O> {
-
    refdb: &'a Write<O>,
+
struct Transaction<'a> {
+
    refdb: &'a Write,
    txn: git2::Transaction<'a>,
}

-
impl<'a, S> Transaction<'a, S> {
-
    pub fn new(refdb: &'a Write<S>) -> Result<Self, git2::Error> {
+
impl<'a> Transaction<'a> {
+
    pub fn new(refdb: &'a Write) -> Result<Self, git2::Error> {
        let txn = refdb.inner.raw.transaction()?;
        Ok(Self { refdb, txn })
    }
@@ -551,7 +503,7 @@ impl<'a, S> Transaction<'a, S> {

// odb impls

-
impl<S> odb::Read for Write<S> {
+
impl odb::Read for Write {
    type FindObj = <Read as odb::Read>::FindObj;
    type FindBlob = <Read as odb::Read>::FindBlob;
    type FindCommit = <Read as odb::Read>::FindCommit;
@@ -579,7 +531,7 @@ impl<S> odb::Read for Write<S> {
    }
}

-
impl<S> odb::Write for Write<S> {
+
impl odb::Write for Write {
    type WriteBlob = git2::Error;
    type WriteCommit = git2::Error;
    type WriteTag = git2::Error;
modified git-storage/src/backend/write/error.rs
@@ -10,11 +10,7 @@ use super::*;
#[derive(Debug, Error)]
pub enum Init {
    #[error(transparent)]
-
    Config(#[from] config::Error),
-
    #[error(transparent)]
    Git(#[from] git2::Error),
-
    #[error("the current identifier '{current}' does not match the identifier used to create the storage '{actual}'")]
-
    OwnerMismatch { current: String, actual: String },
}

#[derive(Debug, Error)]
deleted git-storage/src/config.rs
@@ -1,225 +0,0 @@
-
// Copyright © 2019-2020 The Radicle Foundation <hello@radicle.foundation>
-
//
-
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
-
// Linking Exception. For full terms see the included LICENSE file.
-

-
//! The [git config][config] for a particular storage.
-
//!
-
//! As well as encapsulating the git config, it also holds the [`Owner`] of the
-
//! config.
-
//!
-
//! A [`Config`] can either be constructed by it's [`Config::init`] constuctor
-
//! or by its `TryFrom` instances for [`git2::Repository`] and [`Write`].
-
//!
-
//! [config]: https://git-scm.com/docs/git-config
-

-
use std::{convert::TryFrom, marker::PhantomData, path::PathBuf, str::FromStr};
-

-
use git_ext::is_not_found_err;
-
use std_ext::prelude::*;
-
use thiserror::Error;
-

-
use crate::{signature::UserInfo, Write};
-

-
const CONFIG_USER_NAME: &str = "user.name";
-
const CONFIG_USER_EMAIL: &str = "user.email";
-
const CONFIG_RAD_ID: &str = "rad.identifier";
-

-
#[derive(Debug, Error)]
-
#[non_exhaustive]
-
pub enum Error {
-
    #[error("storage was already initialised with identifier {0}")]
-
    AlreadyInitialised(String),
-

-
    #[error("could not parse the identifier found at 'rad.self' in the storage config")]
-
    Identifier(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
-

-
    #[error("could not parse the identifier found at 'rad.peerid' in the storage config")]
-
    RadSelf(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
-

-
    #[error(transparent)]
-
    Git(#[from] git2::Error),
-
}
-

-
/// The `RadIdentifier` is expected to uniquely identify the owner of this
-
/// repository. The value should be unique and should only be able to be
-
/// constructed by the owner. For example, this could be a signing key unique to
-
/// the owner.
-
pub trait Owner {
-
    type RadIdentifier: ToString + FromStr + PartialEq;
-

-
    fn rad_identifier(&self) -> Self::RadIdentifier;
-
}
-

-
/// The _local_ config for the give [`git2::Repository`].
-
///
-
/// This is typically `$GIT_DIR/.git/config` for non-bare, and `$GIT_DIR/config`
-
/// for bare repositories.
-
pub fn path(repo: &git2::Repository) -> PathBuf {
-
    repo.path().join("config")
-
}
-

-
/// A git config paired with the owner information.
-
///
-
/// The config is initialised with the following config keys:
-
///
-
///   * `user.name`: the provided name of the user.
-
///   * `user.email`: the provided email of the user. This does not necessarily
-
///     have to be a valid email, see [`UserInfo`].
-
///   * `rad.identifier`: the unqiue value identifying the owner of this
-
///     `Config`.
-
///
-
/// # Constructors
-
///
-
///   * [`Config::init`]
-
///   * `TryFrom<&'a Write>`
-
///   * `TryFrom<git2::Repository`
-
pub struct Config<'a, O> {
-
    inner: git2::Config,
-
    owner: &'a O,
-
    info: UserInfo,
-
}
-

-
impl<'a, O> TryFrom<&'a Write<O>> for Config<'a, O>
-
where
-
    O: Owner,
-
    <O::RadIdentifier as FromStr>::Err: std::error::Error + Send + Sync + 'static,
-
{
-
    type Error = Error;
-

-
    fn try_from(storage: &'a Write<O>) -> Result<Self, Self::Error> {
-
        let inner = git2::Config::open(&storage.config_path())?;
-
        let mut this = Self {
-
            inner,
-
            owner: storage.owner(),
-
            info: storage.info().clone(),
-
        };
-
        this.guard_key_change()?;
-
        this.ensure_reflog()?;
-

-
        Ok(this)
-
    }
-
}
-

-
impl TryFrom<&git2::Repository> for Config<'_, PhantomData<Void>> {
-
    type Error = git2::Error;
-

-
    fn try_from(repo: &git2::Repository) -> Result<Self, Self::Error> {
-
        let inner = git2::Config::open(&self::path(repo))?.snapshot()?;
-
        let name = inner.get_string(CONFIG_USER_NAME)?;
-
        let email = inner.get_string(CONFIG_USER_EMAIL)?;
-
        Ok(Self {
-
            inner,
-
            owner: &PhantomData,
-
            info: UserInfo { name, email },
-
        })
-
    }
-
}
-

-
impl<'a, O> Config<'a, O>
-
where
-
    O: Owner,
-
    <O::RadIdentifier as FromStr>::Err: std::error::Error + Send + Sync + 'static,
-
{
-
    fn guard_key_change(&self) -> Result<(), Error> {
-
        let configured_identifier = self
-
            .rad_identifier::<O::RadIdentifier>()
-
            .map(Some)
-
            .or_matches::<Error, _, _>(
-
                |err| matches!(err, Error::Git(e) if is_not_found_err(e)),
-
                || Ok(None),
-
            )?;
-
        let owner_identifier = self.owner.rad_identifier();
-
        match configured_identifier {
-
            Some(initialised_with) if initialised_with != owner_identifier => {
-
                Err(Error::AlreadyInitialised(initialised_with.to_string()))
-
            },
-

-
            _ => Ok(()),
-
        }
-
    }
-

-
    fn ensure_reflog(&mut self) -> Result<(), Error> {
-
        if let Err(e) = self.inner.get_bool("core.logAllRefUpdates") {
-
            return if is_not_found_err(&e) {
-
                Ok(self.inner.set_bool("core.logAllRefUpdates", true)?)
-
            } else {
-
                Err(e.into())
-
            };
-
        }
-

-
        Ok(())
-
    }
-

-
    /// Initialise the [`Config`] with the given `owner` and `info`.
-
    ///
-
    /// # Errors
-
    ///
-
    ///   * The identifier of the `owner` differs from the existing identifier
-
    ///     stored
-
    pub fn init(repo: &mut git2::Repository, owner: &'a O, info: UserInfo) -> Result<Self, Error> {
-
        let identifier = owner.rad_identifier();
-
        let config = git2::Config::open(&self::path(repo))?;
-
        let mut this = Config {
-
            inner: config,
-
            owner,
-
            info,
-
        };
-
        this.guard_key_change()?;
-
        this.ensure_reflog()?;
-
        this.set_identifier(identifier)?;
-
        this.set_user_info()?;
-

-
        Ok(this)
-
    }
-

-
    fn set_user_info(&mut self) -> Result<(), Error> {
-
        self.inner.set_str(CONFIG_USER_NAME, &self.info.name)?;
-
        self.inner.set_str(CONFIG_USER_EMAIL, &self.info.email)?;
-

-
        Ok(())
-
    }
-

-
    fn set_identifier(&mut self, rad_identifier: O::RadIdentifier) -> Result<(), Error> {
-
        self.inner
-
            .set_str(CONFIG_RAD_ID, &rad_identifier.to_string())
-
            .map_err(Error::from)
-
    }
-
}
-

-
impl<O> Config<'_, O> {
-
    pub fn user(&self) -> &UserInfo {
-
        &self.info
-
    }
-

-
    pub fn user_name(&self) -> Result<String, Error> {
-
        self.inner.get_string(CONFIG_USER_NAME).map_err(Error::from)
-
    }
-

-
    pub fn user_email(&self) -> Result<String, Error> {
-
        self.inner
-
            .get_string(CONFIG_USER_EMAIL)
-
            .map_err(Error::from)
-
    }
-

-
    pub fn rad_identifier<Id>(&self) -> Result<Id, Error>
-
    where
-
        Id: FromStr,
-
        Id::Err: std::error::Error + Send + Sync + 'static,
-
    {
-
        self.inner
-
            .get_string(CONFIG_RAD_ID)
-
            .map_err(Error::from)
-
            .and_then(|peer_id| {
-
                peer_id
-
                    .parse()
-
                    .map_err(|err| Error::Identifier(Box::new(err)))
-
            })
-
    }
-
}
-

-
impl Config<'_, PhantomData<Void>> {
-
    pub fn readonly(repo: &git2::Repository) -> Result<Self, git2::Error> {
-
        Self::try_from(repo)
-
    }
-
}
modified git-storage/src/lib.rs
@@ -3,12 +3,9 @@

//! # `git-storage`
//!
-
//! This crate provides access to git [references][refs] and [objects][objs], in
-
//! a Radicle context. The particular difference for Radicle is that a storage
-
//! must be *owned*, generally using a signing key.
+
//! This crate provides access to git [references][refs] and [objects][objs].
//!
-
//! To first initialise the storage use [`Write::open`]. This will intialise the
-
//! underlying git repository and its [`config`].
+
//! To first initialise the storage use [`Write::open`].
//!
//! After the storage is initialised, use [`Write::open`] or [`Read::open`] for
//! read-write or read-only access to the underlying storage. These structs will
@@ -44,7 +41,6 @@ extern crate async_trait;
extern crate radicle_git_ext as git_ext;
extern crate radicle_std_ext as std_ext;

-
pub mod config;
pub mod glob;

pub mod pool;
modified git-storage/src/pool.rs
@@ -7,7 +7,6 @@ use std::{
    marker::PhantomData,
    ops::{Deref, DerefMut},
    path::PathBuf,
-
    str::FromStr,
    sync::Arc,
};

@@ -16,7 +15,7 @@ use parking_lot::RwLock;
use std_ext::Void;
use thiserror::Error;

-
use crate::{config, read, signature::UserInfo, write, Read, Write};
+
use crate::{read, signature::UserInfo, write, Read, Write};

#[derive(Debug, Error)]
#[non_exhaustive]
@@ -75,11 +74,7 @@ impl<S> AsMut<S> for PooledRef<S> {
    }
}

-
impl<S> AsRef<Read> for PooledRef<Write<S>>
-
where
-
    S: config::Owner,
-
    <S::RadIdentifier as FromStr>::Err: std::error::Error + Send + Sync + 'static,
-
{
+
impl AsRef<Read> for PooledRef<Write> {
    fn as_ref(&self) -> &Read {
        self.0.read_only()
    }
@@ -100,8 +95,7 @@ impl Initialised {
    }
}

-
pub struct Writer<S> {
-
    signer: S,
+
pub struct Writer {
    init: Initialised,
}

@@ -113,7 +107,7 @@ pub struct Config<W> {
}

pub type ReadConfig = Config<PhantomData<Void>>;
-
pub type WriteConfig<S> = Config<Writer<S>>;
+
pub type WriteConfig = Config<Writer>;

impl ReadConfig {
    pub fn new(root: PathBuf, info: UserInfo) -> Self {
@@ -124,11 +118,11 @@ impl ReadConfig {
        }
    }

-
    pub fn write<S>(self, signer: S, init: Initialised) -> WriteConfig<S> {
+
    pub fn write(self, init: Initialised) -> WriteConfig {
        Config {
            root: self.root,
            info: self.info,
-
            write: Writer { signer, init },
+
            write: Writer { init },
        }
    }
}
@@ -144,36 +138,23 @@ impl Manager<Read, InitError> for ReadConfig {
    }
}

-
impl<S> WriteConfig<S>
-
where
-
    S: config::Owner,
-
    <S::RadIdentifier as FromStr>::Err: std::error::Error + Send + Sync + 'static,
-
{
-
    pub fn new(root: PathBuf, info: UserInfo, signer: S, init: Initialised) -> Self {
+
impl WriteConfig {
+
    pub fn new(root: PathBuf, info: UserInfo, init: Initialised) -> Self {
        Self {
            root,
            info,
-
            write: Writer { signer, init },
+
            write: Writer { init },
        }
    }

-
    fn mk_storage(&self) -> Result<Write<S>, InitError>
-
    where
-
        S: Clone,
-
    {
-
        Write::open(&self.root, self.info.clone(), self.write.signer.clone())
-
            .map_err(InitError::from)
+
    fn mk_storage(&self) -> Result<Write, InitError> {
+
        Write::open(&self.root, self.info.clone()).map_err(InitError::from)
    }
}

#[async_trait]
-
impl<S> Manager<Write<S>, InitError> for WriteConfig<S>
-
where
-
    S: Clone + Send + Sync + 'static,
-
    S: config::Owner,
-
    <S::RadIdentifier as FromStr>::Err: std::error::Error + Send + Sync + 'static,
-
{
-
    async fn create(&self) -> Result<Write<S>, InitError> {
+
impl Manager<Write, InitError> for WriteConfig {
+
    async fn create(&self) -> Result<Write, InitError> {
        let initialised = self.write.init.0.read();
        if *initialised {
            self.mk_storage()
@@ -189,7 +170,7 @@ where
        }
    }

-
    async fn recycle(&self, _: &mut Write<S>) -> RecycleResult<InitError> {
+
    async fn recycle(&self, _: &mut Write) -> RecycleResult<InitError> {
        Ok(())
    }
}
modified git-storage/src/signature.rs
@@ -5,9 +5,9 @@
pub struct UserInfo {
    /// Provided `name` of the user.
    pub name: String,
-
    /// Proivded `email` of the user. Note that this does not necessarily have
-
    /// to be an email, but will be used as the email field in [`crate::config`]
-
    /// and the [`git2::Signature`].
+
    /// Proivded `email` of the user. Note that this does not
+
    /// necessarily have to be an email, but will be used as the email
+
    /// field in the [`git2::Signature`].
    pub email: String,
}