forked from kemitix/git-next
refactor: move ForgeLike to git
This commit is contained in:
parent
639223fcaa
commit
64cbe36dac
16 changed files with 220 additions and 202 deletions
|
@ -1,12 +1,13 @@
|
|||
use git_next_config::BranchName;
|
||||
use git_next_config as config;
|
||||
use git_next_git as git;
|
||||
|
||||
use kxio::network::{self, Network};
|
||||
use tracing::error;
|
||||
|
||||
pub async fn get_all(
|
||||
repo_details: &git::RepoDetails,
|
||||
net: &Network,
|
||||
) -> Result<Vec<BranchName>, crate::branch::Error> {
|
||||
) -> Result<Vec<config::BranchName>, git::branch::Error> {
|
||||
let hostname = &repo_details.forge.hostname();
|
||||
let repo_path = &repo_details.repo_path;
|
||||
use secrecy::ExposeSecret;
|
||||
|
@ -28,13 +29,13 @@ pub async fn get_all(
|
|||
let result = net.get::<Vec<Branch>>(request).await;
|
||||
let response = result.map_err(|e| {
|
||||
error!(?e, "Failed to list branches");
|
||||
crate::branch::Error::NoneFound // BranchListNotAvailable
|
||||
git::branch::Error::NoneFound // BranchListNotAvailable
|
||||
})?;
|
||||
let branches = response
|
||||
.response_body()
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(BranchName::from)
|
||||
.map(config::BranchName::from)
|
||||
.collect::<Vec<_>>();
|
||||
Ok(branches)
|
||||
}
|
||||
|
@ -43,7 +44,7 @@ pub async fn get_all(
|
|||
struct Branch {
|
||||
name: String,
|
||||
}
|
||||
impl From<Branch> for BranchName {
|
||||
impl From<Branch> for config::BranchName {
|
||||
fn from(value: Branch) -> Self {
|
||||
Self::new(value.name)
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use git_next_config::{BranchName, RepoConfig};
|
||||
//
|
||||
use crate as forge;
|
||||
use git::ForgeLike as _;
|
||||
use git_next_config as config;
|
||||
use git_next_git as git;
|
||||
|
||||
use kxio::network;
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
use crate::{forgejo::ForgeJoEnv, validation, ForgeLike as _};
|
||||
|
||||
pub async fn validate_positions(
|
||||
forge: &ForgeJoEnv,
|
||||
forge: &forge::forgejo::ForgeJoEnv,
|
||||
repository: &git::OpenRepository,
|
||||
repo_config: RepoConfig,
|
||||
) -> validation::Result {
|
||||
repo_config: config::RepoConfig,
|
||||
) -> git::validation::Result {
|
||||
let repo_details = &forge.repo_details;
|
||||
// Collect Commit Histories for `main`, `next` and `dev` branches
|
||||
let commit_histories = get_commit_histories(repo_details, &repo_config, &forge.net).await;
|
||||
|
@ -17,7 +19,7 @@ pub async fn validate_positions(
|
|||
Ok(commit_histories) => commit_histories,
|
||||
Err(err) => {
|
||||
error!(?err, "Failed to get commit histories");
|
||||
return Err(validation::Error::Network(Box::new(err)));
|
||||
return Err(git::validation::Error::Network(Box::new(err)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -27,7 +29,7 @@ pub async fn validate_positions(
|
|||
"No commits on main branch '{}'",
|
||||
repo_config.branches().main()
|
||||
);
|
||||
return Err(validation::Error::BranchHasNoCommits(
|
||||
return Err(git::validation::Error::BranchHasNoCommits(
|
||||
repo_config.branches().main(),
|
||||
));
|
||||
};
|
||||
|
@ -40,7 +42,7 @@ pub async fn validate_positions(
|
|||
repo_config.branches().main(),
|
||||
repo_config.branches().main(),
|
||||
);
|
||||
return Err(validation::Error::DevBranchNotBasedOn(
|
||||
return Err(git::validation::Error::DevBranchNotBasedOn(
|
||||
repo_config.branches().main(),
|
||||
));
|
||||
}
|
||||
|
@ -50,7 +52,7 @@ pub async fn validate_positions(
|
|||
"No commits on next branch '{}",
|
||||
repo_config.branches().next()
|
||||
);
|
||||
return Err(validation::Error::BranchHasNoCommits(
|
||||
return Err(git::validation::Error::BranchHasNoCommits(
|
||||
repo_config.branches().next(),
|
||||
));
|
||||
};
|
||||
|
@ -65,12 +67,12 @@ pub async fn validate_positions(
|
|||
git::push::Force::From(next.clone().into()),
|
||||
) {
|
||||
warn!(?err, "Failed to reset next to main");
|
||||
return Err(validation::Error::FailedToResetBranch {
|
||||
return Err(git::validation::Error::FailedToResetBranch {
|
||||
branch: repo_config.branches().next(),
|
||||
commit: next,
|
||||
});
|
||||
}
|
||||
return Err(validation::Error::BranchReset(
|
||||
return Err(git::validation::Error::BranchReset(
|
||||
repo_config.branches().next(),
|
||||
));
|
||||
}
|
||||
|
@ -93,12 +95,12 @@ pub async fn validate_positions(
|
|||
git::push::Force::From(next.clone().into()),
|
||||
) {
|
||||
warn!(?err, "Failed to reset next to main");
|
||||
return Err(validation::Error::FailedToResetBranch {
|
||||
return Err(git::validation::Error::FailedToResetBranch {
|
||||
branch: repo_config.branches().next(),
|
||||
commit: next,
|
||||
});
|
||||
}
|
||||
return Err(validation::Error::BranchReset(
|
||||
return Err(git::validation::Error::BranchReset(
|
||||
repo_config.branches().next(),
|
||||
));
|
||||
}
|
||||
|
@ -107,7 +109,7 @@ pub async fn validate_positions(
|
|||
"No commits on next branch '{}'",
|
||||
repo_config.branches().next()
|
||||
);
|
||||
return Err(validation::Error::BranchHasNoCommits(
|
||||
return Err(git::validation::Error::BranchHasNoCommits(
|
||||
repo_config.branches().next(),
|
||||
));
|
||||
};
|
||||
|
@ -121,7 +123,7 @@ pub async fn validate_positions(
|
|||
repo_config.branches().dev(),
|
||||
repo_config.branches().next()
|
||||
);
|
||||
return Err(validation::Error::DevBranchNotBasedOn(
|
||||
return Err(git::validation::Error::DevBranchNotBasedOn(
|
||||
repo_config.branches().next(),
|
||||
)); // dev is not based on next
|
||||
}
|
||||
|
@ -131,12 +133,12 @@ pub async fn validate_positions(
|
|||
"No commits on dev branch '{}'",
|
||||
repo_config.branches().dev()
|
||||
);
|
||||
return Err(validation::Error::BranchHasNoCommits(
|
||||
return Err(git::validation::Error::BranchHasNoCommits(
|
||||
repo_config.branches().dev(),
|
||||
));
|
||||
};
|
||||
|
||||
Ok(validation::Positions {
|
||||
Ok(git::validation::Positions {
|
||||
main,
|
||||
next,
|
||||
dev,
|
||||
|
@ -146,7 +148,7 @@ pub async fn validate_positions(
|
|||
|
||||
async fn get_commit_histories(
|
||||
repo_details: &git::RepoDetails,
|
||||
repo_config: &RepoConfig,
|
||||
repo_config: &config::RepoConfig,
|
||||
net: &network::Network,
|
||||
) -> Result<git::commit::Histories, network::NetworkError> {
|
||||
let main =
|
||||
|
@ -180,7 +182,7 @@ async fn get_commit_histories(
|
|||
#[tracing::instrument(fields(%branch_name),skip_all)]
|
||||
async fn get_commit_history(
|
||||
repo_details: &git::RepoDetails,
|
||||
branch_name: &BranchName,
|
||||
branch_name: &config::BranchName,
|
||||
find_commits: Vec<git::Commit>,
|
||||
net: &kxio::network::Network,
|
||||
) -> Result<Vec<git::Commit>, network::NetworkError> {
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use git_next_config::BranchName;
|
||||
use git_next_config as config;
|
||||
use git_next_git as git;
|
||||
|
||||
use kxio::network::{self, Network};
|
||||
use tracing::{error, warn};
|
||||
|
||||
pub(super) async fn contents_get(
|
||||
repo_details: &git::RepoDetails,
|
||||
net: &Network,
|
||||
branch: &BranchName,
|
||||
branch: &config::BranchName,
|
||||
file_path: &str,
|
||||
) -> Result<String, crate::file::Error> {
|
||||
) -> Result<String, git::file::Error> {
|
||||
let hostname = &repo_details.forge.hostname();
|
||||
let repo_path = &repo_details.repo_path;
|
||||
let api_token = &repo_details.forge.token();
|
||||
|
@ -31,7 +32,7 @@ pub(super) async fn contents_get(
|
|||
let result = net.get::<ForgeContentsResponse>(request).await;
|
||||
let response = result.map_err(|e| {
|
||||
warn!(?e, "");
|
||||
crate::file::Error::NotFound(file_path.to_string())
|
||||
git::file::Error::NotFound(file_path.to_string())
|
||||
})?;
|
||||
let status = response.status_code();
|
||||
let contents = match response.response_body() {
|
||||
|
@ -39,30 +40,30 @@ pub(super) async fn contents_get(
|
|||
// we need to decode (see encoding field) the value of 'content' from the response
|
||||
match body.content_type {
|
||||
ForgeContentsType::File => decode_config(body),
|
||||
_ => Err(crate::file::Error::NotFile(file_path.to_string())),
|
||||
_ => Err(git::file::Error::NotFile(file_path.to_string())),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
error!(%status, "Failed to fetch repo config file");
|
||||
Err(crate::file::Error::Unknown(status.to_string()))
|
||||
Err(git::file::Error::Unknown(status.to_string()))
|
||||
}
|
||||
}?;
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
fn decode_config(body: ForgeContentsResponse) -> Result<String, crate::file::Error> {
|
||||
fn decode_config(body: ForgeContentsResponse) -> Result<String, git::file::Error> {
|
||||
use base64::Engine;
|
||||
match body.encoding.as_str() {
|
||||
"base64" => {
|
||||
let decoded = base64::engine::general_purpose::STANDARD
|
||||
.decode(body.content)
|
||||
.map_err(|_| crate::file::Error::DecodeFromBase64)?;
|
||||
.map_err(|_| git::file::Error::DecodeFromBase64)?;
|
||||
let decoded =
|
||||
String::from_utf8(decoded).map_err(|_| crate::file::Error::DecodeFromUtf8)?;
|
||||
String::from_utf8(decoded).map_err(|_| git::file::Error::DecodeFromUtf8)?;
|
||||
|
||||
Ok(decoded)
|
||||
}
|
||||
encoding => Err(crate::file::Error::UnknownEncoding(encoding.to_string())),
|
||||
encoding => Err(git::file::Error::UnknownEncoding(encoding.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
pub mod branch;
|
||||
mod file;
|
||||
|
||||
use git::OpenRepository;
|
||||
use git_next_config::{BranchName, GitDir, RepoConfig};
|
||||
use git_next_config as config;
|
||||
use git_next_git as git;
|
||||
|
||||
use kxio::network::{self, Network};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
use crate::validation;
|
||||
|
||||
struct ForgeJo;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ForgeJoEnv {
|
||||
|
@ -30,35 +28,35 @@ impl ForgeJoEnv {
|
|||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl super::ForgeLike for ForgeJoEnv {
|
||||
impl git::ForgeLike for ForgeJoEnv {
|
||||
fn name(&self) -> String {
|
||||
"forgejo".to_string()
|
||||
}
|
||||
|
||||
async fn branches_get_all(&self) -> Result<Vec<BranchName>, crate::branch::Error> {
|
||||
async fn branches_get_all(&self) -> Result<Vec<config::BranchName>, git::branch::Error> {
|
||||
branch::get_all(&self.repo_details, &self.net).await
|
||||
}
|
||||
|
||||
async fn file_contents_get(
|
||||
&self,
|
||||
branch: &BranchName,
|
||||
branch: &config::BranchName,
|
||||
file_path: &str,
|
||||
) -> Result<String, crate::file::Error> {
|
||||
) -> Result<String, git::file::Error> {
|
||||
file::contents_get(&self.repo_details, &self.net, branch, file_path).await
|
||||
}
|
||||
|
||||
async fn branches_validate_positions(
|
||||
&self,
|
||||
repository: git::OpenRepository,
|
||||
repo_config: RepoConfig,
|
||||
) -> validation::Result {
|
||||
repo_config: config::RepoConfig,
|
||||
) -> git::validation::Result {
|
||||
branch::validate_positions(self, &repository, repo_config).await
|
||||
}
|
||||
|
||||
fn branch_reset(
|
||||
&self,
|
||||
repository: &git::OpenRepository,
|
||||
branch_name: BranchName,
|
||||
branch_name: config::BranchName,
|
||||
to_commit: git::GitRef,
|
||||
force: git::push::Force,
|
||||
) -> git::push::Result {
|
||||
|
@ -110,7 +108,10 @@ impl super::ForgeLike for ForgeJoEnv {
|
|||
}
|
||||
}
|
||||
|
||||
fn repo_clone(&self, gitdir: GitDir) -> Result<OpenRepository, git::repository::Error> {
|
||||
fn repo_clone(
|
||||
&self,
|
||||
gitdir: config::GitDir,
|
||||
) -> Result<git::OpenRepository, git::repository::Error> {
|
||||
let repository = if !gitdir.exists() {
|
||||
info!("Local copy not found - cloning...");
|
||||
self.repo.git_clone(&self.repo_details)?
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use git::OpenRepository;
|
||||
use git_next_config::{BranchName, GitDir, RepoConfig};
|
||||
use git_next_git as git;
|
||||
use kxio::network::Network;
|
||||
|
||||
|
@ -13,44 +11,6 @@ mod github;
|
|||
|
||||
mod mock_forge;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ForgeLike {
|
||||
fn name(&self) -> String;
|
||||
|
||||
/// Returns a list of all branches in the repo.
|
||||
async fn branches_get_all(&self) -> Result<Vec<BranchName>, branch::Error>;
|
||||
|
||||
/// Returns the contents of the file.
|
||||
async fn file_contents_get(
|
||||
&self,
|
||||
branch: &BranchName,
|
||||
file_path: &str,
|
||||
) -> Result<String, file::Error>;
|
||||
|
||||
/// Assesses the relative positions of the main, next and dev branch and updates their
|
||||
/// positions as needed.
|
||||
async fn branches_validate_positions(
|
||||
&self,
|
||||
repository: OpenRepository,
|
||||
repo_config: RepoConfig,
|
||||
) -> validation::Result;
|
||||
|
||||
/// Moves a branch to a new commit.
|
||||
fn branch_reset(
|
||||
&self,
|
||||
repository: &OpenRepository,
|
||||
branch_name: BranchName,
|
||||
to_commit: git::GitRef,
|
||||
force: git::push::Force,
|
||||
) -> git::push::Result;
|
||||
|
||||
/// Checks the results of any (e.g. CI) status checks for the commit.
|
||||
async fn commit_status(&self, commit: &git::Commit) -> git::commit::Status;
|
||||
|
||||
/// Clones a repo to disk.
|
||||
fn repo_clone(&self, gitdir: GitDir) -> Result<OpenRepository, git::repository::Error>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Forge {
|
||||
Mock(mock_forge::MockForgeEnv),
|
||||
|
@ -78,7 +38,7 @@ impl Forge {
|
|||
}
|
||||
}
|
||||
impl std::ops::Deref for Forge {
|
||||
type Target = dyn ForgeLike;
|
||||
type Target = dyn git::ForgeLike;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Mock(env) => env,
|
||||
|
@ -90,65 +50,5 @@ impl std::ops::Deref for Forge {
|
|||
}
|
||||
}
|
||||
|
||||
pub mod branch {
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
pub enum Error {
|
||||
#[display("Branch not found: {}", 0)]
|
||||
NotFound(git_next_config::BranchName),
|
||||
#[display("Unable to find any branches")]
|
||||
NoneFound,
|
||||
}
|
||||
impl std::error::Error for Error {}
|
||||
}
|
||||
|
||||
pub mod file {
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
pub enum Error {
|
||||
#[display("File not found: {}", 0)]
|
||||
NotFound(String),
|
||||
#[display("Unable to parse file contents")]
|
||||
ParseContent,
|
||||
#[display("Unable to decode from base64")]
|
||||
DecodeFromBase64,
|
||||
#[display("Unable to decoce from UTF-8")]
|
||||
DecodeFromUtf8,
|
||||
#[display("Unknown file encoding: {}", 0)]
|
||||
UnknownEncoding(String),
|
||||
#[display("Not a file: {}", 0)]
|
||||
NotFile(String),
|
||||
#[display("Unknown error (status: {})", 0)]
|
||||
Unknown(String),
|
||||
}
|
||||
impl std::error::Error for Error {}
|
||||
}
|
||||
|
||||
pub mod validation {
|
||||
use git_next_config::BranchName;
|
||||
use git_next_git as git;
|
||||
|
||||
pub type Result = core::result::Result<Positions, Error>;
|
||||
|
||||
pub struct Positions {
|
||||
pub main: git::Commit,
|
||||
pub next: git::Commit,
|
||||
pub dev: git::Commit,
|
||||
pub dev_commit_history: Vec<git::Commit>,
|
||||
}
|
||||
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
pub enum Error {
|
||||
Network(Box<kxio::network::NetworkError>),
|
||||
#[display("Failed to Reset Branch {branch} to {commit}")]
|
||||
FailedToResetBranch {
|
||||
branch: BranchName,
|
||||
commit: git::Commit,
|
||||
},
|
||||
BranchReset(BranchName),
|
||||
BranchHasNoCommits(BranchName),
|
||||
DevBranchNotBasedOn(BranchName),
|
||||
}
|
||||
impl std::error::Error for Error {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
#![cfg(not(tarpaulin_include))]
|
||||
|
||||
use git::OpenRepository;
|
||||
use git_next_config::{BranchName, GitDir, RepoConfig};
|
||||
use git_next_config as config;
|
||||
use git_next_git as git;
|
||||
|
||||
use crate::{branch, file, validation};
|
||||
|
||||
struct MockForge;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MockForgeEnv;
|
||||
|
@ -16,35 +14,35 @@ impl MockForgeEnv {
|
|||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl super::ForgeLike for MockForgeEnv {
|
||||
impl git::ForgeLike for MockForgeEnv {
|
||||
fn name(&self) -> String {
|
||||
"mock".to_string()
|
||||
}
|
||||
|
||||
async fn branches_get_all(&self) -> Result<Vec<BranchName>, branch::Error> {
|
||||
async fn branches_get_all(&self) -> Result<Vec<config::BranchName>, git::branch::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn file_contents_get(
|
||||
&self,
|
||||
_branch: &BranchName,
|
||||
_branch: &config::BranchName,
|
||||
_file_path: &str,
|
||||
) -> Result<String, file::Error> {
|
||||
) -> Result<String, git::file::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn branches_validate_positions(
|
||||
&self,
|
||||
_repository: OpenRepository,
|
||||
_repo_config: RepoConfig,
|
||||
) -> validation::Result {
|
||||
_repository: git::OpenRepository,
|
||||
_repo_config: config::RepoConfig,
|
||||
) -> git::validation::Result {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn branch_reset(
|
||||
&self,
|
||||
_repository: &OpenRepository,
|
||||
_branch_name: BranchName,
|
||||
_repository: &git::OpenRepository,
|
||||
_branch_name: config::BranchName,
|
||||
_to_commit: git::GitRef,
|
||||
_force: git::push::Force,
|
||||
) -> git::push::Result {
|
||||
|
@ -55,7 +53,10 @@ impl super::ForgeLike for MockForgeEnv {
|
|||
todo!()
|
||||
}
|
||||
|
||||
fn repo_clone(&self, _gitdir: GitDir) -> Result<OpenRepository, git::repository::Error> {
|
||||
fn repo_clone(
|
||||
&self,
|
||||
_gitdir: config::GitDir,
|
||||
) -> Result<OpenRepository, git::repository::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//
|
||||
use assert2::let_assert;
|
||||
|
||||
use git_next_config::{self as config, ForgeType, RepoConfigSource};
|
||||
use kxio::network::{MockNetwork, StatusCode};
|
||||
|
||||
use git_next_config as config;
|
||||
use git_next_git as git;
|
||||
|
||||
use kxio::network::{MockNetwork, StatusCode};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
@ -18,9 +18,12 @@ fn test_name() {
|
|||
let repo_details = git::common::repo_details(
|
||||
1,
|
||||
git::Generation::new(),
|
||||
config::common::forge_details(1, ForgeType::MockForge),
|
||||
Some(config::common::repo_config(1, RepoConfigSource::Repo)),
|
||||
GitDir::new(fs.base()),
|
||||
config::common::forge_details(1, config::ForgeType::MockForge),
|
||||
Some(config::common::repo_config(
|
||||
1,
|
||||
config::RepoConfigSource::Repo,
|
||||
)),
|
||||
config::GitDir::new(fs.base()),
|
||||
);
|
||||
let forge = Forge::new_forgejo(repo_details, net, repo);
|
||||
assert_eq!(forge.name(), "forgejo");
|
||||
|
@ -46,9 +49,12 @@ async fn test_branches_get() {
|
|||
let repo_details = git::common::repo_details(
|
||||
1,
|
||||
git::Generation::new(),
|
||||
config::common::forge_details(1, ForgeType::MockForge),
|
||||
Some(config::common::repo_config(1, RepoConfigSource::Repo)),
|
||||
GitDir::new(fs.base()),
|
||||
config::common::forge_details(1, config::ForgeType::MockForge),
|
||||
Some(config::common::repo_config(
|
||||
1,
|
||||
config::RepoConfigSource::Repo,
|
||||
)),
|
||||
config::GitDir::new(fs.base()),
|
||||
);
|
||||
|
||||
let forge = Forge::new_forgejo(repo_details, net.clone(), repo);
|
||||
|
@ -58,5 +64,5 @@ async fn test_branches_get() {
|
|||
let_assert!(Some(requests) = net.mocked_requests());
|
||||
assert_eq!(requests.len(), 1);
|
||||
|
||||
assert_eq!(branches, vec![BranchName::new("string")]);
|
||||
assert_eq!(branches, vec![config::BranchName::new("string")]);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ tracing = { workspace = true }
|
|||
# git
|
||||
# # gix = { workspace = true }
|
||||
gix = { workspace = true }
|
||||
# async-trait = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
|
||||
# fs/network
|
||||
kxio = { workspace = true }
|
||||
|
|
8
crates/git/src/branch.rs
Normal file
8
crates/git/src/branch.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
#[derive(Debug, derive_more::Display)]
|
||||
pub enum Error {
|
||||
#[display("Branch not found: {}", 0)]
|
||||
NotFound(git_next_config::BranchName),
|
||||
#[display("Unable to find any branches")]
|
||||
NoneFound,
|
||||
}
|
||||
impl std::error::Error for Error {}
|
18
crates/git/src/file.rs
Normal file
18
crates/git/src/file.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
#[derive(Debug, derive_more::Display)]
|
||||
pub enum Error {
|
||||
#[display("File not found: {}", 0)]
|
||||
NotFound(String),
|
||||
#[display("Unable to parse file contents")]
|
||||
ParseContent,
|
||||
#[display("Unable to decode from base64")]
|
||||
DecodeFromBase64,
|
||||
#[display("Unable to decoce from UTF-8")]
|
||||
DecodeFromUtf8,
|
||||
#[display("Unknown file encoding: {}", 0)]
|
||||
UnknownEncoding(String),
|
||||
#[display("Not a file: {}", 0)]
|
||||
NotFile(String),
|
||||
#[display("Unknown error (status: {})", 0)]
|
||||
Unknown(String),
|
||||
}
|
||||
impl std::error::Error for Error {}
|
43
crates/git/src/forge_like.rs
Normal file
43
crates/git/src/forge_like.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use crate as git;
|
||||
use git_next_config as config;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ForgeLike {
|
||||
fn name(&self) -> String;
|
||||
|
||||
/// Returns a list of all branches in the repo.
|
||||
async fn branches_get_all(&self) -> Result<Vec<config::BranchName>, git::branch::Error>;
|
||||
|
||||
/// Returns the contents of the file.
|
||||
async fn file_contents_get(
|
||||
&self,
|
||||
branch: &config::BranchName,
|
||||
file_path: &str,
|
||||
) -> Result<String, git::file::Error>;
|
||||
|
||||
/// Assesses the relative positions of the main, next and dev branch and updates their
|
||||
/// positions as needed.
|
||||
async fn branches_validate_positions(
|
||||
&self,
|
||||
repository: git::OpenRepository,
|
||||
repo_config: config::RepoConfig,
|
||||
) -> git::validation::Result;
|
||||
|
||||
/// Moves a branch to a new commit.
|
||||
fn branch_reset(
|
||||
&self,
|
||||
repository: &git::OpenRepository,
|
||||
branch_name: config::BranchName,
|
||||
to_commit: git::GitRef,
|
||||
force: git::push::Force,
|
||||
) -> git::push::Result;
|
||||
|
||||
/// Checks the results of any (e.g. CI) status checks for the commit.
|
||||
async fn commit_status(&self, commit: &git::Commit) -> git::commit::Status;
|
||||
|
||||
/// Clones a repo to disk.
|
||||
fn repo_clone(
|
||||
&self,
|
||||
gitdir: config::GitDir,
|
||||
) -> Result<git::OpenRepository, git::repository::Error>;
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
//
|
||||
pub mod branch;
|
||||
pub mod commit;
|
||||
pub mod common;
|
||||
pub mod fetch;
|
||||
pub mod file;
|
||||
mod forge_like;
|
||||
mod generation;
|
||||
mod git_ref;
|
||||
mod git_remote;
|
||||
|
@ -9,11 +12,13 @@ pub mod push;
|
|||
mod repo_details;
|
||||
pub mod repository;
|
||||
pub mod validate;
|
||||
pub mod validation;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use commit::Commit;
|
||||
pub use forge_like::ForgeLike;
|
||||
pub use generation::Generation;
|
||||
pub use git_ref::GitRef;
|
||||
pub use git_remote::GitRemote;
|
||||
|
|
25
crates/git/src/validation.rs
Normal file
25
crates/git/src/validation.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use crate as git;
|
||||
use git_next_config::BranchName;
|
||||
|
||||
pub type Result = core::result::Result<Positions, Error>;
|
||||
|
||||
pub struct Positions {
|
||||
pub main: git::Commit,
|
||||
pub next: git::Commit,
|
||||
pub dev: git::Commit,
|
||||
pub dev_commit_history: Vec<git::Commit>,
|
||||
}
|
||||
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
pub enum Error {
|
||||
Network(Box<kxio::network::NetworkError>),
|
||||
#[display("Failed to Reset Branch {branch} to {commit}")]
|
||||
FailedToResetBranch {
|
||||
branch: BranchName,
|
||||
commit: git::Commit,
|
||||
},
|
||||
BranchReset(BranchName),
|
||||
BranchHasNoCommits(BranchName),
|
||||
DevBranchNotBasedOn(BranchName),
|
||||
}
|
||||
impl std::error::Error for Error {}
|
|
@ -1,6 +1,8 @@
|
|||
use actix::prelude::*;
|
||||
|
||||
use git_next_forge as forge;
|
||||
use git_next_git::RepoDetails;
|
||||
use git_next_git as git;
|
||||
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::load;
|
||||
|
@ -9,7 +11,7 @@ use super::{LoadedConfig, RepoActor};
|
|||
|
||||
/// Loads the [RepoConfig] from the `.git-next.toml` file in the repository
|
||||
#[tracing::instrument(skip_all, fields(branch = %repo_details.branch))]
|
||||
pub async fn load(repo_details: RepoDetails, addr: Addr<RepoActor>, forge: forge::Forge) {
|
||||
pub async fn load(repo_details: git::RepoDetails, addr: Addr<RepoActor>, forge: forge::Forge) {
|
||||
info!("Loading .git-next.toml from repo");
|
||||
let repo_config = match load::load(&repo_details, &forge).await {
|
||||
Ok(repo_config) => repo_config,
|
||||
|
|
|
@ -10,47 +10,43 @@ mod tests;
|
|||
use std::time::Duration;
|
||||
|
||||
use actix::prelude::*;
|
||||
use git::OpenRepository;
|
||||
use git_next_config::{server::Webhook, ForgeType, RepoConfig, RepoConfigSource};
|
||||
|
||||
use git_next_forge as forge;
|
||||
use git_next_git as git;
|
||||
|
||||
use kxio::network::Network;
|
||||
use tracing::{debug, info, warn, Instrument};
|
||||
|
||||
// use crate::{actors::repo::webhook::WebhookAuth, config::Webhook, gitforge};
|
||||
|
||||
use crate::webhook::WebhookAuth;
|
||||
|
||||
use self::webhook::WebhookId;
|
||||
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
#[display("{}:{}:{}", generation, details.forge.forge_name(), details.repo_alias)]
|
||||
pub struct RepoActor {
|
||||
generation: git::Generation,
|
||||
message_token: MessageToken,
|
||||
details: git::RepoDetails,
|
||||
webhook: Webhook,
|
||||
webhook_id: Option<WebhookId>, // INFO: if [None] then no webhook is configured
|
||||
webhook_auth: Option<WebhookAuth>, // INFO: if [None] then no webhook is configured
|
||||
webhook: git_next_config::server::Webhook,
|
||||
webhook_id: Option<webhook::WebhookId>, // INFO: if [None] then no webhook is configured
|
||||
webhook_auth: Option<webhook::WebhookAuth>, // INFO: if [None] then no webhook is configured
|
||||
last_main_commit: Option<git::Commit>,
|
||||
last_next_commit: Option<git::Commit>,
|
||||
last_dev_commit: Option<git::Commit>,
|
||||
repository: Option<OpenRepository>,
|
||||
repository: Option<git::OpenRepository>,
|
||||
net: Network,
|
||||
forge: forge::Forge,
|
||||
}
|
||||
impl RepoActor {
|
||||
pub fn new(
|
||||
details: git::RepoDetails,
|
||||
webhook: Webhook,
|
||||
webhook: git_next_config::server::Webhook,
|
||||
generation: git::Generation,
|
||||
net: Network,
|
||||
repo: git::Repository,
|
||||
) -> Self {
|
||||
let forge = match details.forge.forge_type() {
|
||||
#[cfg(feature = "forgejo")]
|
||||
ForgeType::ForgeJo => forge::Forge::new_forgejo(details.clone(), net.clone(), repo),
|
||||
ForgeType::MockForge => forge::Forge::new_mock(),
|
||||
git_next_config::ForgeType::ForgeJo => {
|
||||
forge::Forge::new_forgejo(details.clone(), net.clone(), repo)
|
||||
}
|
||||
git_next_config::ForgeType::MockForge => forge::Forge::new_mock(),
|
||||
};
|
||||
debug!(?forge, "new");
|
||||
Self {
|
||||
|
@ -133,7 +129,7 @@ impl Handler<LoadConfigFromRepo> for RepoActor {
|
|||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
struct LoadedConfig(RepoConfig);
|
||||
struct LoadedConfig(git_next_config::RepoConfig);
|
||||
impl Handler<LoadedConfig> for RepoActor {
|
||||
type Result = ();
|
||||
#[tracing::instrument(name = "RepoActor::LoadedConfig", skip_all, fields(repo = %self.details, branches = %msg.0))]
|
||||
|
@ -191,7 +187,7 @@ impl Handler<ValidateRepo> for RepoActor {
|
|||
.branches_validate_positions(repository, repo_config)
|
||||
.await
|
||||
{
|
||||
Ok(forge::validation::Positions {
|
||||
Ok(git::validation::Positions {
|
||||
main,
|
||||
next,
|
||||
dev,
|
||||
|
@ -265,7 +261,7 @@ impl Handler<StartMonitoring> for RepoActor {
|
|||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct WebhookRegistered(WebhookId, WebhookAuth);
|
||||
pub struct WebhookRegistered(webhook::WebhookId, webhook::WebhookAuth);
|
||||
impl Handler<WebhookRegistered> for RepoActor {
|
||||
type Result = ();
|
||||
#[tracing::instrument(name = "RepoActor::WebhookRegistered", skip_all, fields(repo = %self.details, webhook_id = %msg.0))]
|
||||
|
@ -296,8 +292,10 @@ impl Handler<AdvanceMainTo> for RepoActor {
|
|||
async move {
|
||||
branch::advance_main(msg.0, &repo_config, &forge, &repository).await;
|
||||
match repo_config.source() {
|
||||
RepoConfigSource::Repo => addr.do_send(LoadConfigFromRepo),
|
||||
RepoConfigSource::Server => addr.do_send(ValidateRepo { message_token }),
|
||||
git_next_config::RepoConfigSource::Repo => addr.do_send(LoadConfigFromRepo),
|
||||
git_next_config::RepoConfigSource::Server => {
|
||||
addr.do_send(ValidateRepo { message_token })
|
||||
}
|
||||
}
|
||||
}
|
||||
.in_current_span()
|
||||
|
|
|
@ -1,27 +1,34 @@
|
|||
use git_next_config::{self as config, BranchName, RepoConfig};
|
||||
use git_next_config as config;
|
||||
use git_next_forge as forge;
|
||||
use git_next_git::RepoDetails;
|
||||
use git_next_git as git;
|
||||
|
||||
use tracing::error;
|
||||
|
||||
pub async fn load(details: &RepoDetails, forge: &forge::Forge) -> Result<RepoConfig, Error> {
|
||||
pub async fn load(
|
||||
details: &git::RepoDetails,
|
||||
forge: &forge::Forge,
|
||||
) -> Result<config::RepoConfig, Error> {
|
||||
let contents = forge
|
||||
.file_contents_get(&details.branch, ".git-next.toml")
|
||||
.await?;
|
||||
let config = RepoConfig::load(&contents)?;
|
||||
let config = config::RepoConfig::load(&contents)?;
|
||||
let config = validate(config, forge).await?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
#[derive(Debug, derive_more::From, derive_more::Display)]
|
||||
pub enum Error {
|
||||
File(forge::file::Error),
|
||||
File(git::file::Error),
|
||||
Config(config::server::Error),
|
||||
Toml(toml::de::Error),
|
||||
Forge(forge::branch::Error),
|
||||
BranchNotFound(BranchName),
|
||||
Forge(git::branch::Error),
|
||||
BranchNotFound(config::BranchName),
|
||||
}
|
||||
|
||||
pub async fn validate(config: RepoConfig, forge: &forge::Forge) -> Result<RepoConfig, Error> {
|
||||
pub async fn validate(
|
||||
config: config::RepoConfig,
|
||||
forge: &forge::Forge,
|
||||
) -> Result<config::RepoConfig, Error> {
|
||||
let branches = forge.branches_get_all().await.map_err(|e| {
|
||||
error!(?e, "Failed to list branches");
|
||||
Error::Forge(e)
|
||||
|
|
Loading…
Reference in a new issue