feat(config): User can specify git directory to use for a repo
All checks were successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful

Closes kemitix/git-next#53

Does not include using this information.
This commit is contained in:
Paul Campbell 2024-04-19 18:15:27 +01:00
parent 50a969ede6
commit 704853017b
4 changed files with 43 additions and 31 deletions

View file

@ -62,6 +62,7 @@ tokio = { version = "1.37", features = ["full"] }
[dev-dependencies] [dev-dependencies]
# Testing # Testing
assert2 = "0.3" assert2 = "0.3"
pretty_assertions = "1.4"
test-log = "0.2" test-log = "0.2"
anyhow = "1.0" anyhow = "1.0"

View file

@ -9,5 +9,5 @@ token = "API-Token"
# path to private SSH key for user? # path to private SSH key for user?
[forge.default.repos] [forge.default.repos]
hello = { repo = "user/hello", branch = "main" } # maps to https://git.example.net/user/hello on the branch 'main' hello = { repo = "user/hello", branch = "main", gitdir = "/opt/git/projects/user/hello.git" } # maps to https://git.example.net/user/hello on the branch 'main'
world = { repo = "user/world", branch = "master" } # maps to the 'master' branch world = { repo = "user/world", branch = "master", main = "master", next = "upcoming", "dev" = "develop" } # maps to the 'master' branch

View file

@ -4,6 +4,7 @@ use std::{
collections::HashMap, collections::HashMap,
fmt::{Display, Formatter}, fmt::{Display, Formatter},
ops::Deref, ops::Deref,
path::PathBuf,
}; };
use secrecy::ExposeSecret; use secrecy::ExposeSecret;
@ -16,7 +17,7 @@ use crate::filesystem::FileSystem;
#[derive(Debug, PartialEq, Eq, Deserialize)] #[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct ServerConfig { pub struct ServerConfig {
webhook: Webhook, webhook: Webhook,
forge: HashMap<String, Forge>, forge: HashMap<String, ForgeConfig>,
} }
impl ServerConfig { impl ServerConfig {
pub(crate) fn load(fs: &FileSystem) -> Result<Self, OneOf<(std::io::Error, toml::de::Error)>> { pub(crate) fn load(fs: &FileSystem) -> Result<Self, OneOf<(std::io::Error, toml::de::Error)>> {
@ -24,7 +25,7 @@ impl ServerConfig {
toml::from_str(&str).map_err(OneOf::new) toml::from_str(&str).map_err(OneOf::new)
} }
pub(crate) fn forges(&self) -> impl Iterator<Item = (ForgeName, &Forge)> { pub(crate) fn forges(&self) -> impl Iterator<Item = (ForgeName, &ForgeConfig)> {
self.forge self.forge
.iter() .iter()
.map(|(name, forge)| (ForgeName(name.clone()), forge)) .map(|(name, forge)| (ForgeName(name.clone()), forge))
@ -119,16 +120,16 @@ impl Display for RepoBranches {
/// Defines a Forge to connect to /// Defines a Forge to connect to
/// Maps from `git-next-server.toml` at `forge.{forge}` /// Maps from `git-next-server.toml` at `forge.{forge}`
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Forge { pub struct ForgeConfig {
forge_type: ForgeType, forge_type: ForgeType,
hostname: String, hostname: String,
user: String, user: String,
token: String, token: String,
// API Token // API Token
// Private SSH Key Path // Private SSH Key Path
repos: HashMap<String, Repo>, repos: HashMap<String, ServerRepoConfig>,
} }
impl Forge { impl ForgeConfig {
#[allow(dead_code)] #[allow(dead_code)]
pub const fn forge_type(&self) -> &ForgeType { pub const fn forge_type(&self) -> &ForgeType {
&self.forge_type &self.forge_type
@ -146,29 +147,30 @@ impl Forge {
ApiToken(self.token.clone().into()) ApiToken(self.token.clone().into())
} }
pub fn repos(&self) -> impl Iterator<Item = (RepoAlias, &Repo)> { pub fn repos(&self) -> impl Iterator<Item = (RepoAlias, &ServerRepoConfig)> {
self.repos self.repos
.iter() .iter()
.map(|(name, repo)| (RepoAlias(name.clone()), repo)) .map(|(name, repo)| (RepoAlias(name.clone()), repo))
} }
} }
impl Display for Forge { impl Display for ForgeConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} - {}@{}", self.forge_type, self.user, self.hostname) write!(f, "{} - {}@{}", self.forge_type, self.user, self.hostname)
} }
} }
/// Defines a Repo within a Forge to be monitored by the server /// Defines a Repo within a ForgeConfig to be monitored by the server
/// Maps from `git-next-server.toml` at `forge.{forge}.repos.{name}` /// Maps from `git-next-server.toml` at `forge.{forge}.repos.{name}`
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Repo { pub struct ServerRepoConfig {
repo: String, repo: String,
branch: String, branch: String,
gitdir: Option<PathBuf>,
main: Option<String>, main: Option<String>,
next: Option<String>, next: Option<String>,
dev: Option<String>, dev: Option<String>,
} }
impl Repo { impl ServerRepoConfig {
#[allow(dead_code)] #[allow(dead_code)]
pub fn repo(&self) -> RepoPath { pub fn repo(&self) -> RepoPath {
RepoPath(self.repo.clone()) RepoPath(self.repo.clone())
@ -193,12 +195,12 @@ impl Repo {
} }
} }
#[cfg(test)] #[cfg(test)]
impl AsRef<Self> for Repo { impl AsRef<Self> for ServerRepoConfig {
fn as_ref(&self) -> &Self { fn as_ref(&self) -> &Self {
self self
} }
} }
impl Display for Repo { impl Display for ServerRepoConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} - {}", self.repo, self.branch) write!(f, "{} - {}", self.repo, self.branch)
} }
@ -259,8 +261,8 @@ pub struct ForgeDetails {
// API Token // API Token
// Private SSH Key Path // Private SSH Key Path
} }
impl From<(&ForgeName, &Forge)> for ForgeDetails { impl From<(&ForgeName, &ForgeConfig)> for ForgeDetails {
fn from(forge: (&ForgeName, &Forge)) -> Self { fn from(forge: (&ForgeName, &ForgeConfig)) -> Self {
Self { Self {
name: forge.0.clone(), name: forge.0.clone(),
forge_type: forge.1.forge_type.clone(), forge_type: forge.1.forge_type.clone(),
@ -318,7 +320,12 @@ pub struct RepoDetails {
pub config: Option<RepoConfig>, pub config: Option<RepoConfig>,
} }
impl RepoDetails { impl RepoDetails {
pub fn new(name: &RepoAlias, repo: &Repo, forge_name: &ForgeName, forge: &Forge) -> Self { pub fn new(
name: &RepoAlias,
repo: &ServerRepoConfig,
forge_name: &ForgeName,
forge: &ForgeConfig,
) -> Self {
Self { Self {
name: name.clone(), name: name.clone(),
repo: RepoPath(repo.repo.clone()), repo: RepoPath(repo.repo.clone()),
@ -372,13 +379,14 @@ impl Display for ForgeType {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_eq;
use crate::filesystem::FileSystem; use crate::filesystem::FileSystem;
use super::*; use super::*;
#[test] #[test]
#[cfg(feature = "forgejo")] fn load_should_parse_server_config() -> Result<(), OneOf<(std::io::Error, toml::de::Error)>> {
fn test_server_config_load() -> Result<(), OneOf<(std::io::Error, toml::de::Error)>> {
let fs = FileSystem::new_temp().map_err(OneOf::new)?; let fs = FileSystem::new_temp().map_err(OneOf::new)?;
fs.write_file( fs.write_file(
"git-next-server.toml", "git-next-server.toml",
@ -387,13 +395,13 @@ mod tests {
url = "http://localhost:9909/webhook" url = "http://localhost:9909/webhook"
[forge.default] [forge.default]
forge_type = "ForgeJo" forge_type = "MockForge"
hostname = "git.example.net" hostname = "git.example.net"
user = "Bob" user = "Bob"
token = "API-Token" token = "API-Token"
[forge.default.repos] [forge.default.repos]
hello = { repo = "user/hello", branch = "main" } hello = { repo = "user/hello", branch = "main", gitdir = "/opt/git/user/hello.git" }
world = { repo = "user/world", branch = "master", main = "main", next = "next", dev = "dev" } world = { repo = "user/world", branch = "master", main = "main", next = "next", dev = "dev" }
[forge.default.repos.sam] [forge.default.repos.sam]
@ -412,17 +420,18 @@ mod tests {
}, },
forge: HashMap::from([( forge: HashMap::from([(
"default".to_string(), "default".to_string(),
Forge { ForgeConfig {
forge_type: ForgeType::ForgeJo, forge_type: ForgeType::MockForge,
hostname: "git.example.net".to_string(), hostname: "git.example.net".to_string(),
user: "Bob".to_string(), user: "Bob".to_string(),
token: "API-Token".to_string(), token: "API-Token".to_string(),
repos: HashMap::from([ repos: HashMap::from([
( (
"hello".to_string(), "hello".to_string(),
Repo { ServerRepoConfig {
repo: "user/hello".to_string(), repo: "user/hello".to_string(),
branch: "main".to_string(), branch: "main".to_string(),
gitdir: Some("/opt/git/user/hello.git".into()),
main: None, main: None,
next: None, next: None,
dev: None, dev: None,
@ -430,9 +439,10 @@ mod tests {
), ),
( (
"world".to_string(), "world".to_string(),
Repo { ServerRepoConfig {
repo: "user/world".to_string(), repo: "user/world".to_string(),
branch: "master".to_string(), branch: "master".to_string(),
gitdir: None,
main: Some("main".to_string()), main: Some("main".to_string()),
next: Some("next".to_string()), next: Some("next".to_string()),
dev: Some("dev".to_string()), dev: Some("dev".to_string()),
@ -440,9 +450,10 @@ mod tests {
), ),
( (
"sam".to_string(), "sam".to_string(),
Repo { ServerRepoConfig {
repo: "user/sam".to_string(), repo: "user/sam".to_string(),
branch: "main".to_string(), branch: "main".to_string(),
gitdir: None,
main: Some("master".to_string()), main: Some("master".to_string()),
next: Some("upcoming".to_string()), next: Some("upcoming".to_string()),
dev: Some("sam-dev".to_string()), dev: Some("sam-dev".to_string()),

View file

@ -15,11 +15,11 @@ use crate::{
filesystem::FileSystem, filesystem::FileSystem,
server::{ server::{
actors::webhook, actors::webhook,
config::{Forge, ForgeName, RepoAlias, Webhook}, config::{ForgeConfig, ForgeName, RepoAlias, Webhook},
}, },
}; };
use self::{actors::repo::RepoActor, config::Repo}; use self::{actors::repo::RepoActor, config::ServerRepoConfig};
pub fn init(fs: FileSystem) { pub fn init(fs: FileSystem) {
let file_name = "git-next-server.toml"; let file_name = "git-next-server.toml";
@ -68,7 +68,7 @@ pub async fn start(fs: FileSystem, net: Network) {
} }
fn create_forge_repos( fn create_forge_repos(
forge: &Forge, forge: &ForgeConfig,
forge_name: ForgeName, forge_name: ForgeName,
webhook: &Webhook, webhook: &Webhook,
net: &Network, net: &Network,
@ -85,10 +85,10 @@ fn create_forge_repos(
fn create_actor( fn create_actor(
forge_name: ForgeName, forge_name: ForgeName,
forge: config::Forge, forge: config::ForgeConfig,
webhook: &Webhook, webhook: &Webhook,
net: &Network, net: &Network,
) -> impl Fn((RepoAlias, &Repo)) -> (ForgeName, RepoAlias, RepoActor) { ) -> impl Fn((RepoAlias, &ServerRepoConfig)) -> (ForgeName, RepoAlias, RepoActor) {
let webhook = webhook.clone(); let webhook = webhook.clone();
let net = net.clone(); let net = net.clone();
move |(repo_name, repo)| { move |(repo_name, repo)| {