meta: use long crate names

To help with clarity and potential publishing of the crates, the full
`radicle-` prefix is used for the crate names.

Note that it wasn't chosen got `git-ref-format` and `git-trailers`
since they are truly general libraries (bar the feature flag for link
specific reference names).

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
This commit is contained in:
Fintan Halpenny
2022-09-15 17:58:14 +01:00
parent ff300c2946
commit ce7dc3890a
36 changed files with 10 additions and 511 deletions
+4 -4
View File
@@ -1,13 +1,13 @@
[workspace]
members = [
"git-ext",
"git-ref-format",
"git-trailers",
"git-types",
"link-git",
"macros",
"radicle-git-ext",
"radicle-git-types",
"radicle-macros",
"radicle-std-ext",
# TODO: port gitd-lib over
# "cli/gitd-lib",
"std-ext",
"test",
]
@@ -38,4 +38,4 @@ features = ["derive"]
optional = true
[dependencies.radicle-std-ext]
path = "../std-ext"
path = "../radicle-std-ext"
@@ -27,13 +27,13 @@ version = "0.13"
features = ["std", "derive"]
[dependencies.radicle-git-ext]
path = "../git-ext"
path = "../radicle-git-ext"
[dependencies.radicle-macros]
path = "../macros"
path = "../radicle-macros"
[dependencies.radicle-std-ext]
path = "../std-ext"
path = "../radicle-std-ext"
[dependencies.serde]
version = "1.0"
@@ -17,4 +17,4 @@ quote = "1"
syn = "1"
[dependencies.radicle-git-ext]
path = "../git-ext"
path = "../radicle-git-ext"
+1 -1
View File
@@ -12,7 +12,7 @@ test = true
doc = false
[dev-dependencies.git-ext-test]
path = "../git-ext/t"
path = "../radicle-git-ext/t"
features = ["test"]
[dev-dependencies.git-ref-format-test]
-48
View File
@@ -1,48 +0,0 @@
[package]
name = "it-helpers"
version = "0.1.0"
edition = "2021"
license = "GPL-3.0-or-later"
publish = false
description = "Integration test helpers"
[lib]
doctest = false
test = false
[dependencies]
anyhow = "1"
futures = "0.3"
once_cell = "1.10"
tempfile = "3.3"
tokio = "1.13"
tracing = "0.1"
either = "1.6"
[dependencies.git2]
version = "0.13.24"
default-features = false
features = ["vendored-libgit2"]
#
# workspace dependencies
#
[dependencies.git-ref-format]
path = "../../git-ref-format"
[dependencies.librad]
path = "../../librad"
[dependencies.link-async]
path = "../../link-async"
[dependencies.lnk-clib]
path = "../../cli/lnk-clib"
[dependencies.radicle-git-ext]
path = "../../git-ext"
[dependencies.test-helpers]
path = "../test-helpers"
-27
View File
@@ -1,27 +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.
use git_ref_format::Qualified;
#[tracing::instrument(skip(repo))]
pub fn create_commit(
repo: &git2::Repository,
on_branch: Qualified,
) -> Result<git2::Oid, git2::Error> {
let empty_tree = {
let mut index = repo.index()?;
let oid = index.write_tree()?;
repo.find_tree(oid).unwrap()
};
let author = git2::Signature::now("The Animal", "animal@muppets.com").unwrap();
repo.commit(
Some(on_branch.as_str()),
&author,
&author,
"Initial commit",
&empty_tree,
&[],
)
}
-10
View File
@@ -1,10 +0,0 @@
#[macro_use]
extern crate tracing;
pub mod fixed;
pub mod git;
pub mod layout;
pub mod ssh;
pub mod testnet;
pub mod tmp;
pub mod working_copy;
-68
View File
@@ -1,68 +0,0 @@
// Copyright © 2021 The Radicle Link Contributors
//
// 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::process::{Command, Stdio};
use lnk_clib::keys::ssh::SshAuthSock;
use test_helpers::tempdir::WithTmpDir;
pub type TmpSshSock = WithTmpDir<SshAuthSock>;
pub fn ssh_auth_sock() -> TmpSshSock {
WithTmpDir::new(|path| -> anyhow::Result<SshAuthSock> {
let sock = path.join("tmp.sock");
Ok(SshAuthSock::Uds(sock))
})
.unwrap()
}
/// Run a computation with a forked `ssh-agent` on a temporary file handle.
///
/// Once the computation is finished, the `ssh-agent` is killed by getting its
/// PID and running the equivalent of `SSH_AGENT_PID=<pid> ssh-agent -k`.
/// This is a best effort of resource cleanup, but has no guarantees if the
/// parsing of the PID or the killin of the agent fail.
pub fn with_ssh_agent<F, T>(callback: F) -> anyhow::Result<T>
where
F: FnOnce(SshAuthSock) -> anyhow::Result<T>,
{
let sock = ssh_auth_sock();
let path = match &*sock {
SshAuthSock::Uds(path) => path,
_ => unreachable!(),
};
let agent = Command::new("ssh-agent").arg("-a").arg(path).output()?;
anyhow::ensure!(agent.status.success(), agent.status);
let pid = agent_pid(&agent.stdout)?;
let res = callback((*sock).clone());
kill_agent_pid(pid)?;
res
}
/// Kill the ssh-agent running on the given PID.
fn kill_agent_pid(pid: &str) -> anyhow::Result<()> {
debug!(pid = %pid, "killing ssh-agent");
let status = Command::new("ssh-agent")
.env("SSH_AGENT_PID", pid)
.args(["-k"])
.stdout(Stdio::null())
.status()?;
debug!(status = %status, "status of killing agent");
Ok(())
}
/// Get the PID of the launched ssh-agent.
///
/// It gets the PID by stripping the output from the command using the text
/// `"echo Agent pid "`.
fn agent_pid(out: &[u8]) -> anyhow::Result<&str> {
const PREFIX: &str = "SSH_AGENT_PID=";
const SEP: u8 = b';';
let pid = out
.split(|b| b == &SEP)
.find_map(|bs| std::str::from_utf8(bs).ok()?.trim().strip_prefix(PREFIX))
.ok_or_else(|| anyhow::anyhow!("could not find SSH_AGENT_PID"))?;
Ok(pid)
}
-47
View File
@@ -1,47 +0,0 @@
// Copyright © 2021 The Radicle Link Contributors
// SPDX-License-Identifier: GPLv3-or-later
use std::io;
use librad::{git::storage::Storage, paths::Paths, SecretKey};
use test_helpers::tempdir::WithTmpDir;
pub type TmpPaths = WithTmpDir<Paths>;
pub fn paths() -> TmpPaths {
WithTmpDir::new(|path| -> Result<_, io::Error> {
let paths = Paths::from_root(path)?;
Ok::<_, io::Error>(paths)
})
.unwrap()
}
type TmpRepo = WithTmpDir<git2::Repository>;
pub fn repo() -> anyhow::Result<TmpRepo> {
Ok(WithTmpDir::new(|path| {
let setup = || {
let repo = git2::Repository::init(path)?;
// We need to set user info to _something_, but that doesn't have to
// be valid, as we're using a shared repo with many keys
let mut config = repo.config()?;
config.set_str("user.name", "shared")?;
config.set_str("user.email", "not.relevant@for.testing")?;
Ok(repo)
};
setup().map_err(|e: git2::Error| io::Error::new(io::ErrorKind::Other, e))
})?)
}
pub type TmpStorage = WithTmpDir<Storage>;
pub fn storage(signer: SecretKey) -> TmpStorage {
WithTmpDir::new(|path| -> Result<_, io::Error> {
let paths = Paths::from_root(path)?;
let storage =
Storage::open(&paths, signer).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok::<_, io::Error>(storage)
})
.unwrap()
}
-301
View File
@@ -1,301 +0,0 @@
use git_ref_format::{lit, name, refspec, Qualified, RefStr, RefString};
use librad::{
git::{
local::url::LocalUrl,
types::{
remote::{LocalFetchspec, LocalPushspec},
Fetchspec,
Force,
Refspec,
Remote,
},
},
git_ext as ext,
net::{peer::Peer, protocol::RequestPullGuard},
refspec_pattern,
PeerId,
Signer,
};
use crate::fixed::TestProject;
/// A remote in the working copy
pub enum WorkingRemote {
/// A remote representing a remote peer, named `PeerId::encode_id`
Peer(PeerId),
/// A remote representing the local peer, named "rad"
Rad,
}
impl From<PeerId> for WorkingRemote {
fn from(p: PeerId) -> Self {
WorkingRemote::Peer(p)
}
}
impl WorkingRemote {
fn fetchspec(&self) -> Fetchspec {
match self {
Self::Peer(peer_id) => {
let name = RefString::try_from(format!("{}", peer_id)).expect("peer is refstring");
let dst = RefString::from(Qualified::from(lit::refs_remotes(name.clone())))
.with_pattern(refspec::STAR);
let src = RefString::from(Qualified::from(lit::refs_remotes(name)))
.and(name::HEADS)
.with_pattern(refspec::STAR);
let refspec = Refspec {
src,
dst,
force: Force::True,
};
refspec.into_fetchspec()
},
Self::Rad => {
let name = RefString::try_from("rad").unwrap();
let src =
RefString::from_iter([name::REFS, name::HEADS]).with_pattern(refspec::STAR);
Refspec {
src,
dst: RefString::from(Qualified::from(lit::refs_remotes(name)))
.with_pattern(refspec::STAR),
force: Force::True,
}
.into_fetchspec()
},
}
}
fn remote_ref(&self, branch: &RefStr) -> RefString {
let name = match self {
Self::Rad => name::RAD.to_owned(),
Self::Peer(peer_id) => {
RefString::try_from(peer_id.to_string()).expect("peer id is refstring")
},
};
RefString::from(Qualified::from(lit::refs_remotes(name))).join(branch)
}
}
/// A `WorkingCopy` for test driving interactions with the monorepo where one
/// needs to update the tree of a project.
///
/// Remotes are named after the peer ID, except in the case of the remote
/// representing the local Peer ID - which is called "rad".
pub struct WorkingCopy<'a, S, G> {
repo: git2::Repository,
_repo_path: tempfile::TempDir,
peer: &'a Peer<S, G>,
project: &'a TestProject,
}
impl<'a, S, G> WorkingCopy<'a, S, G>
where
S: Signer + Clone,
G: RequestPullGuard,
{
/// Create a new working copy. This initializes a git repository and then
/// fetches the state of the local peer into `refs/remotes/rad/*`.
pub fn new(
project: &'a TestProject,
peer: &'a Peer<S, G>,
) -> Result<WorkingCopy<'a, S, G>, anyhow::Error> {
let repo_path = tempfile::tempdir()?;
let repo = git2::Repository::init(repo_path.as_ref())?;
let mut copy = WorkingCopy {
peer,
project,
repo,
_repo_path: repo_path,
};
copy.fetch(WorkingRemote::Rad)?;
Ok(copy)
}
/// Fetch changes from the monorepo into the working copy. The fetchspec
/// used depends on the peer ID.
///
/// * If `from` is `WorkingRemote::Peer` then `refs/remotes/<peer
/// ID>/refs/*:refs/remotes/<peer ID>/heads/*`
/// * If `from` is `WorkingRemote::Rad` then
/// `refs/heads/*:refs/remotes/rad/*`
///
/// I.e. changes from remote peers end up in a remote called
/// `PeerId::encode_id` whilst changes from the local peer end up in a
/// remote called "rad".
pub fn fetch(&mut self, from: WorkingRemote) -> Result<(), anyhow::Error> {
let fetchspec = from.fetchspec();
let url = LocalUrl::from(self.project.project.urn());
let mut remote = Remote::rad_remote(url, fetchspec);
let _ = remote.fetch(self.peer.clone(), &self.repo, LocalFetchspec::Configured)?;
Ok(())
}
/// Push changes from `refs/heads/*` to the local peer
pub fn push(&mut self) -> Result<(), anyhow::Error> {
let url = LocalUrl::from(self.project.project.urn());
let name = RefString::try_from("rad").unwrap();
let fetchspec = Refspec {
src: RefString::from_iter([name::REFS, name::HEADS]).with_pattern(refspec::STAR),
dst: RefString::from(Qualified::from(lit::refs_remotes(name)))
.with_pattern(refspec::STAR),
force: Force::True,
}
.into_fetchspec();
let mut remote = Remote::rad_remote(url, fetchspec);
let _ = remote.push(
self.peer.clone(),
&self.repo,
LocalPushspec::Matching {
pattern: refspec_pattern!("refs/heads/*"),
force: Force::True,
},
)?;
Ok(())
}
/// Create a new commit on top of whichever commit is the head of
/// `on_branch`. If the branch does not exist this will create it.
pub fn commit(
&mut self,
message: &str,
on_branch: Qualified,
) -> Result<git2::Oid, anyhow::Error> {
let branch_name = on_branch.non_empty_components().2;
let parent = match self.repo.find_branch(&branch_name, git2::BranchType::Local) {
Ok(b) => b.get().target().and_then(|o| self.repo.find_commit(o).ok()),
Err(e) if ext::error::is_not_found_err(&e) => None,
Err(e) => return Err(anyhow::Error::from(e)),
};
let empty_tree = {
let mut index = self.repo.index()?;
let oid = index.write_tree()?;
self.repo.find_tree(oid).unwrap()
};
let author = git2::Signature::now("The Animal", "animal@muppets.com").unwrap();
let parents = match &parent {
Some(p) => vec![p],
None => Vec::new(),
};
self.repo
.commit(
Some(&on_branch),
&author,
&author,
message,
&empty_tree,
&parents,
)
.map_err(anyhow::Error::from)
}
pub fn commit_and_push(
&mut self,
message: &str,
on_branch: Qualified,
) -> Result<git2::Oid, anyhow::Error> {
let id = self.commit(message, on_branch)?;
self.push()?;
Ok(id)
}
/// Create a branch at `refs/heads/<branch>` which tracks the given remote.
/// The remote branch name depends on `from`.
///
/// * If `from` is `WorkingCopy::Rad` then `refs/remotes/rad/<branch>`
/// * If `from` is `WorkingCopy::Peer(peer_id)` then `refs/remotes/<peer
/// id>/<branch>`
pub fn create_remote_tracking_branch(
&self,
from: WorkingRemote,
branch: &RefStr,
) -> Result<(), anyhow::Error> {
let target = self
.repo
.find_reference(from.remote_ref(branch).as_str())?
.target()
.ok_or_else(|| anyhow::anyhow!("remote ref is not a direct reference"))?;
let commit = self.repo.find_commit(target)?;
self.repo.branch(branch.as_str(), &commit, false)?;
Ok(())
}
/// Fast forward the local branch `refs/heads/<branch>` to whatever is
/// pointed to by `refs/remotes/<remote>/<branch>`
///
/// * If `from` is `WorkingRemote::Peer(peer_id)` then `remote` is
/// `peer_id.encode_id()`
/// * If `from` is `WorkingRemote::Rad` then `remote` is `"rad"`
///
/// # Errors
///
/// * If the local branch does not exist
/// * If the remote branch does not exist
/// * If either of the branches does not point at a commit
/// * If the remote branch is not a descendant of the local branch
pub fn fast_forward_to(&self, from: WorkingRemote, branch: &RefStr) -> anyhow::Result<()> {
let remote_ref = from.remote_ref(branch);
let remote_target = self
.repo
.find_reference(&remote_ref)?
.target()
.ok_or_else(|| anyhow::anyhow!("remote ref had no target"))?;
let local_ref = RefString::from(Qualified::from(lit::refs_heads(branch)));
let local_target = self
.repo
.find_reference(&local_ref)?
.target()
.ok_or_else(|| anyhow::anyhow!("local ref had no target"))?;
if !self.repo.graph_descendant_of(remote_target, local_target)? {
anyhow::bail!("remote ref was not a descendant of local ref");
} else {
self.repo
.reference(&local_ref, remote_target, true, "fast forward")?;
}
Ok(())
}
/// Create a new commit which merges `refs/heads/<branch>` and
/// `refs/remotes/<remote>/<branch>`
///
/// this will create a new commit with two parents, one for the remote
/// branch and one for the local branch
///
/// # Errors
///
/// * If the remote branch does not exist
/// * If the local branch does not exist
/// * If either of the references does not point to a commit
pub fn merge_remote(&self, remote: PeerId, branch: &RefStr) -> anyhow::Result<git2::Oid> {
let peer_branch = WorkingRemote::Peer(remote).remote_ref(branch);
let peer_commit = self
.repo
.find_reference(peer_branch.as_ref())?
.peel_to_commit()?;
let local_branch = Qualified::from(lit::refs_heads(branch));
let local_commit = self
.repo
.find_reference(local_branch.as_ref())?
.peel_to_commit()?;
let message = format!("merge {} into {}", peer_branch, local_branch);
let empty_tree = {
let mut index = self.repo.index()?;
let oid = index.write_tree()?;
self.repo.find_tree(oid).unwrap()
};
let author = git2::Signature::now("The Animal", "animal@muppets.com").unwrap();
let parents = vec![&peer_commit, &local_commit];
self.repo
.commit(
Some(&local_branch),
&author,
&author,
&message,
&empty_tree,
&parents,
)
.map_err(anyhow::Error::from)
}
}