Compare commits

...

12 commits

Author SHA1 Message Date
1a932bcde8 refactor: rename forge as forge_config
Some checks failed
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline failed
ci/woodpecker/push/tag-created Pipeline was successful
2024-04-23 07:41:39 +01:00
17f5f62e61 feat: Clone repo when starting repo
Some checks failed
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline failed
2024-04-23 07:41:39 +01:00
fc1c79620c refactor: rename name to forge_name for consistency
All checks were successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
2024-04-23 06:53:34 +01:00
8b56a185d3 refactor: rename name to repo_alias for consistency
All checks were successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
2024-04-23 06:50:52 +01:00
32d54cfc1c refactor: rename config variables,etc as repo_config
All checks were successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
2024-04-21 19:36:37 +01:00
ee8b1e9fce fix(config): deserialise gitdir as PathBuf, but expose it as GitDir
All checks were successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
2024-04-21 19:33:42 +01:00
b71aece8c9 feat: add gitdir to RepoDetails type
All checks were successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
2024-04-21 18:50:30 +01:00
a024c3de5e feat: use GitDir in place of raw PathBuf
All checks were successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
2024-04-21 18:47:07 +01:00
56e253b545 feat(config): add GitDir type
All checks were successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
2024-04-21 18:38:47 +01:00
e10561f853 refactor(server/config): move tests into their own file
All checks were successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
2024-04-21 18:24:14 +01:00
18143c17fd refactor: use 'repo_path' use consistently
All checks were successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
2024-04-20 21:03:17 +01:00
1170510c44 refactor: use 'server_repo_config' consistently
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
2024-04-20 21:03:12 +01:00
17 changed files with 308 additions and 232 deletions

View file

@ -1,6 +1,9 @@
[webhook]
url = "https://localhost:8080/webhook"
[storage]
path = "./data"
[forge.default]
forge_type = "ForgeJo"
hostname = "git.example.net"

View file

@ -5,13 +5,14 @@ use crate::server::{config::RepoDetails, gitforge};
use super::{LoadedConfig, RepoActor};
/// Loads the [RepoConfig] from the `.git-next.toml` file in the repository
pub async fn load(repo_details: RepoDetails, addr: Addr<RepoActor>, forge: gitforge::Forge) {
let config = match crate::server::config::load::load(&repo_details, &forge).await {
Ok(config) => config,
let repo_config = match crate::server::config::load::load(&repo_details, &forge).await {
Ok(repo_config) => repo_config,
Err(err) => {
error!(?err, "Failed to load config");
return;
}
};
addr.do_send(LoadedConfig(config));
addr.do_send(LoadedConfig(repo_config));
}

View file

@ -68,11 +68,26 @@ impl Actor for RepoActor {
#[derive(Message)]
#[rtype(result = "()")]
pub struct StartRepo;
impl Handler<StartRepo> for RepoActor {
pub struct CloneRepo;
impl Handler<CloneRepo> for RepoActor {
type Result = ();
fn handle(&mut self, _msg: StartRepo, ctx: &mut Self::Context) -> Self::Result {
info!(%self.details, "Starting Repo");
fn handle(&mut self, _msg: CloneRepo, ctx: &mut Self::Context) -> Self::Result {
info!(%self.details, "Clone/Update Repo");
let gitdir = self.details.gitdir.clone();
match self.forge.repo_clone(gitdir) {
Ok(_) => ctx.address().do_send(LoadConfigFromRepo),
Err(err) => warn!(?err, "Could not Clone repo"),
}
}
}
#[derive(Message)]
#[rtype(result = "()")]
pub struct LoadConfigFromRepo;
impl Handler<LoadConfigFromRepo> for RepoActor {
type Result = ();
fn handle(&mut self, _msg: LoadConfigFromRepo, ctx: &mut Self::Context) -> Self::Result {
info!(%self.details, "Loading .git-next.toml from repo");
let details = self.details.clone();
let addr = ctx.address();
let forge = self.forge.clone();
@ -88,9 +103,9 @@ struct LoadedConfig(pub RepoConfig);
impl Handler<LoadedConfig> for RepoActor {
type Result = ();
fn handle(&mut self, msg: LoadedConfig, ctx: &mut Self::Context) -> Self::Result {
let config = msg.0;
info!(%self.details, %config, "Config loaded");
self.details.config.replace(config);
let repo_config = msg.0;
info!(%self.details, %repo_config, "Config loaded");
self.details.repo_config.replace(repo_config);
if self.webhook_id.is_none() {
webhook::register(
self.details.clone(),
@ -111,7 +126,7 @@ pub struct ValidateRepo;
impl Handler<ValidateRepo> for RepoActor {
type Result = ();
fn handle(&mut self, _msg: ValidateRepo, ctx: &mut Self::Context) -> Self::Result {
if let Some(repo_config) = self.details.config.clone() {
if let Some(repo_config) = self.details.repo_config.clone() {
let forge = self.forge.clone();
let addr = ctx.address();
async move { forge.branches_validate_positions(repo_config, addr).await }
@ -132,7 +147,7 @@ pub struct StartMonitoring {
impl Handler<StartMonitoring> for RepoActor {
type Result = ();
fn handle(&mut self, msg: StartMonitoring, ctx: &mut Self::Context) -> Self::Result {
let Some(repo_config) = self.details.config.clone() else {
let Some(repo_config) = self.details.repo_config.clone() else {
warn!("No config loaded");
return;
};
@ -180,7 +195,7 @@ pub struct AdvanceMainTo(pub gitforge::Commit);
impl Handler<AdvanceMainTo> for RepoActor {
type Result = ();
fn handle(&mut self, msg: AdvanceMainTo, ctx: &mut Self::Context) -> Self::Result {
let Some(repo_config) = self.details.config.clone() else {
let Some(repo_config) = self.details.repo_config.clone() else {
warn!("No config loaded");
return;
};

View file

@ -64,11 +64,11 @@ pub async fn unregister(
) {
info!(?webhook_id, "unregister webhook");
let hostname = &repo_details.forge.hostname;
let path = repo_details.repo;
let repo_path = repo_details.repo_path;
use secrecy::ExposeSecret;
let token = repo_details.forge.token.expose_secret();
let url = network::NetUrl::new(format!(
"https://{hostname}/api/v1/repos/{path}/hooks/{webhook_id}?token={token}"
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
));
let request = network::NetRequest::new(
network::RequestMethod::Delete,
@ -92,7 +92,7 @@ pub async fn register(
addr: actix::prelude::Addr<super::RepoActor>,
net: network::Network,
) {
let Some(repo_config) = repo_details.config.clone() else {
let Some(repo_config) = repo_details.repo_config.clone() else {
return;
};
@ -105,13 +105,13 @@ pub async fn register(
info!("Registering webhook");
let hostname = &repo_details.forge.hostname;
let path = repo_details.repo;
let repo_path = repo_details.repo_path;
use secrecy::ExposeSecret;
let token = repo_details.forge.token.expose_secret();
let url = network::NetUrl::new(format!(
"https://{hostname}/api/v1/repos/{path}/hooks?token={token}"
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
));
let repo_alias = &repo_details.name;
let repo_alias = &repo_details.repo_alias;
let headers = network::NetRequestHeaders::new().with("Content-Type", "application/json");
let authorisation = WebhookAuth::generate();
let body = json!({
@ -153,12 +153,13 @@ async fn find_existing_webhooks(
) -> Vec<WebhookId> {
let mut ids: Vec<WebhookId> = vec![];
let hostname = &repo_details.forge.hostname;
let path = &repo_details.repo;
let repo_path = &repo_details.repo_path;
let mut page = 1;
loop {
use secrecy::ExposeSecret;
let token = &repo_details.forge.token.expose_secret();
let url = format!("https://{hostname}/api/v1/repos/{path}/hooks?page={page}&token={token}");
let url =
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?page={page}&token={token}");
let net_url = network::NetUrl::new(url);
let request = network::NetRequest::new(
network::RequestMethod::Get,
@ -213,7 +214,7 @@ impl Handler<WebhookMessage> for RepoActor {
match serde_json::from_str::<Push>(body) {
Err(err) => debug!(?err, %body, "Not a 'push'"),
Ok(push) => {
if let Some(config) = &self.details.config {
if let Some(config) = &self.details.repo_config {
match push.branch(config.branches()) {
None => warn!(
?push,

View file

@ -31,6 +31,10 @@ impl ServerConfig {
.map(|(name, forge)| (ForgeName(name.clone()), forge))
}
pub const fn storage(&self) -> &ServerStorage {
&self.storage
}
pub const fn webhook(&self) -> &Webhook {
&self.webhook
}
@ -259,7 +263,7 @@ impl secrecy::ExposeSecret<String> for ApiToken {
/// The derived information about a Forge, used to create interactions with it
#[derive(Clone, Debug)]
pub struct ForgeDetails {
pub name: ForgeName,
pub forge_name: ForgeName,
pub forge_type: ForgeType,
pub hostname: Hostname,
pub user: User,
@ -270,7 +274,7 @@ pub struct ForgeDetails {
impl From<(&ForgeName, &ForgeConfig)> for ForgeDetails {
fn from(forge: (&ForgeName, &ForgeConfig)) -> Self {
Self {
name: forge.0.clone(),
forge_name: forge.0.clone(),
forge_type: forge.1.forge_type.clone(),
hostname: forge.1.hostname(),
user: forge.1.user(),
@ -319,30 +323,38 @@ impl Deref for BranchName {
/// The derived information about a repo, used to interact with it
#[derive(Clone, Debug)]
pub struct RepoDetails {
pub name: RepoAlias,
pub repo: RepoPath,
pub repo_alias: RepoAlias,
pub repo_path: RepoPath,
pub branch: BranchName,
pub forge: ForgeDetails,
pub config: Option<RepoConfig>,
pub repo_config: Option<RepoConfig>,
pub gitdir: GitDir,
}
impl RepoDetails {
pub fn new(
name: &RepoAlias,
repo: &ServerRepoConfig,
server_repo_config: &ServerRepoConfig,
forge_name: &ForgeName,
forge: &ForgeConfig,
forge_config: &ForgeConfig,
server_storage: &ServerStorage,
) -> Self {
Self {
name: name.clone(),
repo: RepoPath(repo.repo.clone()),
config: repo.repo_config(),
branch: BranchName(repo.branch.clone()),
repo_alias: name.clone(),
repo_path: RepoPath(server_repo_config.repo.clone()),
repo_config: server_repo_config.repo_config(),
branch: BranchName(server_repo_config.branch.clone()),
gitdir: GitDir(
server_storage
.path
.join(forge_name.to_string())
.join(name.to_string()),
),
forge: ForgeDetails {
name: forge_name.clone(),
forge_type: forge.forge_type.clone(),
hostname: forge.hostname(),
user: forge.user(),
token: forge.token(),
forge_name: forge_name.clone(),
forge_type: forge_config.forge_type.clone(),
hostname: forge_config.hostname(),
user: forge_config.user(),
token: forge_config.token(),
},
}
}
@ -350,11 +362,11 @@ impl RepoDetails {
let repo_details = self;
let user = &repo_details.forge.user;
let hostname = &repo_details.forge.hostname;
let path = &repo_details.repo;
let repo_path = &repo_details.repo_path;
use secrecy::ExposeSecret;
let expose_secret = &repo_details.forge.token;
let token = expose_secret.expose_secret();
let origin = format!("https://{user}:{token}@{hostname}/{path}.git");
let origin = format!("https://{user}:{token}@{hostname}/{repo_path}.git");
origin.into()
}
}
@ -363,12 +375,12 @@ impl Display for RepoDetails {
write!(
f,
"{}/{} ({}): {}:{}/{} @ {}",
self.forge.name,
self.name,
self.forge.forge_name,
self.repo_alias,
self.forge.forge_type,
self.forge.hostname,
self.forge.user,
self.repo,
self.repo_path,
self.branch,
)
}
@ -392,142 +404,28 @@ impl Display for ForgeType {
}
}
#[cfg(feature = "forgejo")]
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use crate::filesystem::FileSystem;
use super::*;
#[test]
fn load_should_parse_server_config() -> Result<(), OneOf<(std::io::Error, toml::de::Error)>> {
let fs = FileSystem::new_temp().map_err(OneOf::new)?;
fs.write_file(
"git-next-server.toml",
r#"
[webhook]
url = "http://localhost:9909/webhook"
[storage]
path = "/opt/git-next/data"
[forge.default]
forge_type = "MockForge"
hostname = "git.example.net"
user = "Bob"
token = "API-Token"
[forge.default.repos]
hello = { repo = "user/hello", branch = "main", gitdir = "/opt/git/user/hello.git" }
world = { repo = "user/world", branch = "master", main = "main", next = "next", dev = "dev" }
[forge.default.repos.sam]
repo = "user/sam"
branch = "main"
main = "master"
next = "upcoming"
dev = "sam-dev"
"#,
)
.map_err(OneOf::new)?;
let config = ServerConfig::load(&fs)?;
let expected = ServerConfig {
webhook: Webhook {
url: "http://localhost:9909/webhook".to_string(),
},
storage: ServerStorage {
path: "/opt/git-next/data".into(),
},
forge: HashMap::from([(
"default".to_string(),
ForgeConfig {
forge_type: ForgeType::MockForge,
hostname: "git.example.net".to_string(),
user: "Bob".to_string(),
token: "API-Token".to_string(),
repos: HashMap::from([
(
"hello".to_string(),
ServerRepoConfig {
repo: "user/hello".to_string(),
branch: "main".to_string(),
gitdir: Some("/opt/git/user/hello.git".into()),
main: None,
next: None,
dev: None,
},
),
(
"world".to_string(),
ServerRepoConfig {
repo: "user/world".to_string(),
branch: "master".to_string(),
gitdir: None,
main: Some("main".to_string()),
next: Some("next".to_string()),
dev: Some("dev".to_string()),
},
),
(
"sam".to_string(),
ServerRepoConfig {
repo: "user/sam".to_string(),
branch: "main".to_string(),
gitdir: None,
main: Some("master".to_string()),
next: Some("upcoming".to_string()),
dev: Some("sam-dev".to_string()),
},
),
]),
},
)]),
};
assert_eq!(config, expected, "ServerConfig");
if let Some(forge) = config.forge.get("world") {
if let Some(repo) = forge.repos.get("sam") {
let repo_config = repo.repo_config();
let expected = Some(RepoConfig {
branches: RepoBranches {
main: "master".to_string(),
next: "upcoming".to_string(),
dev: "sam-dev".to_string(),
},
});
assert_eq!(repo_config, expected, "RepoConfig");
}
}
Ok(())
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct GitDir(PathBuf);
impl GitDir {
#[cfg(test)]
pub(crate) fn new(pathbuf: &std::path::Path) -> Self {
Self(pathbuf.to_path_buf())
}
#[test]
fn test_repo_config_load() -> Result<(), OneOf<(toml::de::Error,)>> {
let toml = r#"
[branches]
main = "main"
next = "next"
dev = "dev"
[options]
"#;
let config = RepoConfig::load(toml)?;
assert_eq!(
config,
RepoConfig {
branches: RepoBranches {
main: "main".to_string(),
next: "next".to_string(),
dev: "dev".to_string(),
},
}
);
Ok(())
#[allow(dead_code)] // TODO:
pub const fn pathbuf(&self) -> &PathBuf {
&self.0
}
}
impl std::fmt::Display for GitDir {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl From<&str> for GitDir {
fn from(value: &str) -> Self {
Self(value.into())
}
}
#[cfg(test)]
mod tests;

134
src/server/config/tests.rs Normal file
View file

@ -0,0 +1,134 @@
use pretty_assertions::assert_eq;
use crate::filesystem::FileSystem;
use super::*;
#[test]
fn load_should_parse_server_config() -> Result<(), OneOf<(std::io::Error, toml::de::Error)>> {
let fs = FileSystem::new_temp().map_err(OneOf::new)?;
fs.write_file(
"git-next-server.toml",
r#"
[webhook]
url = "http://localhost:9909/webhook"
[storage]
path = "/opt/git-next/data"
[forge.default]
forge_type = "MockForge"
hostname = "git.example.net"
user = "Bob"
token = "API-Token"
[forge.default.repos]
hello = { repo = "user/hello", branch = "main", gitdir = "/opt/git/user/hello.git" }
world = { repo = "user/world", branch = "master", main = "main", next = "next", dev = "dev" }
[forge.default.repos.sam]
repo = "user/sam"
branch = "main"
main = "master"
next = "upcoming"
dev = "sam-dev"
"#,
)
.map_err(OneOf::new)?;
let config = ServerConfig::load(&fs)?;
let expected = ServerConfig {
webhook: Webhook {
url: "http://localhost:9909/webhook".to_string(),
},
storage: ServerStorage {
path: "/opt/git-next/data".into(),
},
forge: HashMap::from([(
"default".to_string(),
ForgeConfig {
forge_type: ForgeType::MockForge,
hostname: "git.example.net".to_string(),
user: "Bob".to_string(),
token: "API-Token".to_string(),
repos: HashMap::from([
(
"hello".to_string(),
ServerRepoConfig {
repo: "user/hello".to_string(),
branch: "main".to_string(),
gitdir: Some("/opt/git/user/hello.git".into()),
main: None,
next: None,
dev: None,
},
),
(
"world".to_string(),
ServerRepoConfig {
repo: "user/world".to_string(),
branch: "master".to_string(),
gitdir: None,
main: Some("main".to_string()),
next: Some("next".to_string()),
dev: Some("dev".to_string()),
},
),
(
"sam".to_string(),
ServerRepoConfig {
repo: "user/sam".to_string(),
branch: "main".to_string(),
gitdir: None,
main: Some("master".to_string()),
next: Some("upcoming".to_string()),
dev: Some("sam-dev".to_string()),
},
),
]),
},
)]),
};
assert_eq!(config, expected, "ServerConfig");
if let Some(forge) = config.forge.get("world") {
if let Some(repo) = forge.repos.get("sam") {
let repo_config = repo.repo_config();
let expected = Some(RepoConfig {
branches: RepoBranches {
main: "master".to_string(),
next: "upcoming".to_string(),
dev: "sam-dev".to_string(),
},
});
assert_eq!(repo_config, expected, "RepoConfig");
}
}
Ok(())
}
#[test]
fn test_repo_config_load() -> Result<(), OneOf<(toml::de::Error,)>> {
let toml = r#"
[branches]
main = "main"
next = "next"
dev = "dev"
[options]
"#;
let config = RepoConfig::load(toml)?;
assert_eq!(
config,
RepoConfig {
branches: RepoBranches {
main: "main".to_string(),
next: "next".to_string(),
dev: "dev".to_string(),
},
}
);
Ok(())
}

View file

@ -1,4 +1,4 @@
use crate::server::config::BranchName;
use crate::server::config::{BranchName, GitDir};
#[derive(Debug)]
pub enum ForgeFileError {
@ -42,7 +42,7 @@ impl std::fmt::Display for ForgeBranchError {
#[derive(Debug)]
pub enum RepoCloneError {
InvalidGitDir(std::path::PathBuf),
InvalidGitDir(GitDir),
Wait(std::io::Error),
Spawn(std::io::Error),
}

View file

@ -11,11 +11,11 @@ pub async fn get_all(
net: &Network,
) -> Result<Vec<gitforge::Branch>, ForgeBranchError> {
let hostname = &repo_details.forge.hostname;
let path = &repo_details.repo;
let repo_path = &repo_details.repo_path;
use secrecy::ExposeSecret;
let token = repo_details.forge.token.expose_secret();
let url = network::NetUrl::new(format!(
"https://{hostname}/api/v1/repos/{path}/branches?token={token}"
"https://{hostname}/api/v1/repos/{repo_path}/branches?token={token}"
));
info!(%url, "Listing branches");

View file

@ -160,7 +160,7 @@ async fn get_commit_history(
net: &kxio::network::Network,
) -> Result<Vec<gitforge::Commit>, network::NetworkError> {
let hostname = &repo_details.forge.hostname;
let path = &repo_details.repo;
let repo_path = &repo_details.repo_path;
let mut page = 1;
let limit = match find_commits.is_empty() {
@ -174,7 +174,7 @@ async fn get_commit_history(
use secrecy::ExposeSecret;
let token = api_token.expose_secret();
let url = network::NetUrl::new(format!(
"https://{hostname}/api/v1/repos/{path}/commits?sha={branch_name}&{options}&token={token}&page={page}&limit={limit}"
"https://{hostname}/api/v1/repos/{repo_path}/commits?sha={branch_name}&{options}&token={token}&page={page}&limit={limit}"
));
let request = network::NetRequest::new(

View file

@ -13,12 +13,12 @@ pub(super) async fn contents_get(
file_path: &str,
) -> Result<String, ForgeFileError> {
let hostname = &repo_details.forge.hostname;
let path = &repo_details.repo;
let repo_path = &repo_details.repo_path;
let api_token = &repo_details.forge.token;
use secrecy::ExposeSecret;
let token = api_token.expose_secret();
let url = network::NetUrl::new(format!(
"https://{hostname}/api/v1/repos/{path}/contents/{file_path}?ref={branch}&token={token}"
"https://{hostname}/api/v1/repos/{repo_path}/contents/{file_path}?ref={branch}&token={token}"
));
info!(%url, "Loading config");

View file

@ -2,8 +2,6 @@ mod branch;
mod file;
mod repo;
use std::path::PathBuf;
use actix::prelude::*;
use kxio::network::{self, Network};
@ -11,7 +9,7 @@ use tracing::{error, warn};
use crate::server::{
actors::repo::RepoActor,
config::{BranchName, RepoConfig, RepoDetails},
config::{BranchName, GitDir, RepoConfig, RepoDetails},
gitforge::{self, RepoCloneError},
types::GitRef,
};
@ -61,12 +59,12 @@ impl super::ForgeLike for ForgeJoEnv {
async fn commit_status(&self, commit: &gitforge::Commit) -> gitforge::CommitStatus {
let repo_details = &self.repo_details;
let hostname = &repo_details.forge.hostname;
let path = &repo_details.repo;
let repo_path = &repo_details.repo_path;
let api_token = &repo_details.forge.token;
use secrecy::ExposeSecret;
let token = api_token.expose_secret();
let url = network::NetUrl::new(format!(
"https://{hostname}/api/v1/repos/{path}/commits/{commit}/status?token={token}"
"https://{hostname}/api/v1/repos/{repo_path}/commits/{commit}/status?token={token}"
));
let request = network::NetRequest::new(
@ -102,7 +100,7 @@ impl super::ForgeLike for ForgeJoEnv {
}
}
fn repo_clone(&self, gitdir: PathBuf) -> Result<(), RepoCloneError> {
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError> {
repo::clone(&self.repo_details, gitdir)
}
}

View file

@ -1,13 +1,11 @@
use std::path::PathBuf;
use tracing::{info, warn};
use crate::server::{config::RepoDetails, gitforge::RepoCloneError};
use crate::server::{
config::{GitDir, RepoDetails},
gitforge::RepoCloneError,
};
pub fn clone(repo_details: &RepoDetails, gitdir: PathBuf) -> Result<(), RepoCloneError> {
let Some(gitdir) = gitdir.to_str() else {
return Err(RepoCloneError::InvalidGitDir(gitdir));
};
pub fn clone(repo_details: &RepoDetails, gitdir: GitDir) -> Result<(), RepoCloneError> {
let origin = repo_details.origin();
// INFO: never log the command as it contains the API token within the 'origin'
use secrecy::ExposeSecret;
@ -17,7 +15,7 @@ pub fn clone(repo_details: &RepoDetails, gitdir: PathBuf) -> Result<(), RepoClon
gitdir
)
.into();
let repo_name = &repo_details.name;
let repo_name = &repo_details.repo_alias;
info!("Cloning {repo_name} to {gitdir}");
match gix::command::prepare(command.expose_secret())
.with_shell_allow_argument_splitting()

View file

@ -1,8 +1,6 @@
use std::path::PathBuf;
use crate::server::{
actors::repo::RepoActor,
config::{BranchName, RepoConfig},
config::{BranchName, GitDir, RepoConfig},
gitforge::{self, RepoCloneError},
types::GitRef,
};
@ -54,7 +52,7 @@ impl super::ForgeLike for MockForgeEnv {
todo!()
}
fn repo_clone(&self, _gitdir: PathBuf) -> Result<(), RepoCloneError> {
fn repo_clone(&self, _gitdir: GitDir) -> Result<(), RepoCloneError> {
todo!()
}
}

View file

@ -1,7 +1,5 @@
#![allow(dead_code)]
use std::path::PathBuf;
use kxio::network::Network;
#[cfg(feature = "forgejo")]
@ -19,7 +17,7 @@ mod errors;
pub use errors::*;
use crate::server::{
config::{BranchName, RepoConfig, RepoDetails},
config::{BranchName, GitDir, RepoConfig, RepoDetails},
types::GitRef,
};
@ -57,7 +55,7 @@ pub trait ForgeLike {
async fn commit_status(&self, commit: &Commit) -> CommitStatus;
/// Clones a repo to disk.
fn repo_clone(&self, gitdir: PathBuf) -> Result<(), RepoCloneError>;
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError>;
}
#[derive(Clone)]

View file

@ -1,11 +1,11 @@
use crate::server::config::{
ApiToken, BranchName, ForgeDetails, ForgeName, ForgeType, Hostname, RepoAlias, RepoBranches,
RepoConfig, RepoDetails, RepoPath, User,
ApiToken, BranchName, ForgeDetails, ForgeName, ForgeType, GitDir, Hostname, RepoAlias,
RepoBranches, RepoConfig, RepoDetails, RepoPath, User,
};
pub fn forge_details(n: u32, forge_type: ForgeType) -> ForgeDetails {
ForgeDetails {
name: forge_name(n),
forge_name: forge_name(n),
forge_type,
hostname: hostname(n),
user: user(n),
@ -28,13 +28,19 @@ pub fn hostname(n: u32) -> Hostname {
pub fn forge_name(n: u32) -> ForgeName {
ForgeName(format!("forge-name-{}", n))
}
pub fn repo_details(n: u32, forge: ForgeDetails, config: Option<RepoConfig>) -> RepoDetails {
pub fn repo_details(
n: u32,
forge: ForgeDetails,
repo_config: Option<RepoConfig>,
gitdir: GitDir,
) -> RepoDetails {
RepoDetails {
name: repo_alias(n),
repo: repo_path(n),
repo_alias: repo_alias(n),
repo_path: repo_path(n),
gitdir,
branch: branch_name(n),
forge,
config,
repo_config,
}
}
@ -50,7 +56,7 @@ pub fn repo_alias(n: u32) -> RepoAlias {
RepoAlias(format!("repo-alias-{}", n))
}
pub fn config(n: u32) -> RepoConfig {
pub fn repo_config(n: u32) -> RepoConfig {
RepoConfig::new(RepoBranches::new(
format!("main-{n}"),
format!("next-{n}"),

View file

@ -8,11 +8,15 @@ use super::*;
#[test]
fn test_name() {
let Ok(fs) = kxio::filesystem::FileSystem::new_temp() else {
panic!("fs")
};
let net = Network::new_mock();
let repo_details = common::repo_details(
1,
common::forge_details(1, ForgeType::MockForge),
Some(common::config(1)),
Some(common::repo_config(1)),
GitDir::new(fs.cwd()),
);
let forge = Forge::new_forgejo(repo_details, net);
assert_eq!(forge.name(), "forgejo");
@ -20,6 +24,9 @@ fn test_name() {
#[test_log::test(tokio::test)]
async fn test_branches_get() {
let Ok(fs) = kxio::filesystem::FileSystem::new_temp() else {
panic!("fs")
};
let mut net = MockNetwork::new();
let hostname = common::hostname(1);
let path = common::repo_path(1);
@ -34,7 +41,8 @@ async fn test_branches_get() {
let repo_details = common::repo_details(
1,
common::forge_details(1, ForgeType::MockForge),
Some(common::config(1)),
Some(common::repo_config(1)),
GitDir::new(fs.cwd()),
);
let forge = Forge::new_forgejo(repo_details, net.clone());

View file

@ -15,7 +15,7 @@ use crate::{
filesystem::FileSystem,
server::{
actors::webhook,
config::{ForgeConfig, ForgeName, RepoAlias, Webhook},
config::{ForgeConfig, ForgeName, RepoAlias, ServerStorage, Webhook},
},
};
@ -55,9 +55,13 @@ pub async fn start(fs: FileSystem, net: Network) {
let webhook_router = webhook::WebhookRouter::new().start();
let webhook = server_config.webhook();
let server_storage = server_config.storage();
server_config
.forges()
.flat_map(|(forge_name, forge)| create_forge_repos(forge, forge_name, webhook, &net))
.flat_map(|(forge_name, forge_config)| {
create_forge_repos(forge_config, forge_name, server_storage, webhook, &net)
})
.map(start_actor)
.map(|(alias, addr)| webhook::AddWebhookRecipient(alias, addr.recipient()))
.for_each(|msg| webhook_router.do_send(msg));
@ -68,35 +72,49 @@ pub async fn start(fs: FileSystem, net: Network) {
}
fn create_forge_repos(
forge: &ForgeConfig,
forge_config: &ForgeConfig,
forge_name: ForgeName,
server_storage: &ServerStorage,
webhook: &Webhook,
net: &Network,
) -> Vec<(ForgeName, RepoAlias, RepoActor)> {
let forge = forge.clone();
let span = tracing::info_span!("Forge", %forge_name, %forge);
let span = tracing::info_span!("Forge", %forge_name, %forge_config);
let _guard = span.enter();
info!("Creating Forge");
forge
forge_config
.repos()
.map(create_actor(forge_name, forge.clone(), webhook, net))
.map(create_actor(
forge_name,
forge_config.clone(),
server_storage,
webhook,
net,
))
.collect::<Vec<_>>()
}
fn create_actor(
forge_name: ForgeName,
forge: config::ForgeConfig,
forge_name: ForgeName, // TODO: check we need this, is it not in forge_config?
forge_config: config::ForgeConfig, // TODO: rename forge as forge_config
server_storage: &ServerStorage,
webhook: &Webhook,
net: &Network,
) -> impl Fn((RepoAlias, &ServerRepoConfig)) -> (ForgeName, RepoAlias, RepoActor) {
let server_storage = server_storage.clone();
let webhook = webhook.clone();
let net = net.clone();
move |(repo_name, repo)| {
let span = tracing::info_span!("Repo", %repo_name, %repo);
move |(repo_name, server_repo_config)| {
let span = tracing::info_span!("Repo", %repo_name, %server_repo_config);
let _guard = span.enter();
info!("Creating Repo");
let actor = actors::repo::RepoActor::new(
config::RepoDetails::new(&repo_name, repo, &forge_name, &forge),
config::RepoDetails::new(
&repo_name,
server_repo_config,
&forge_name,
&forge_config,
&server_storage,
),
webhook.clone(),
net.clone(),
);
@ -113,7 +131,7 @@ fn start_actor(
let _guard = span.enter();
info!("Starting");
let addr = actor.start();
addr.do_send(actors::repo::StartRepo);
addr.do_send(actors::repo::CloneRepo);
info!("Started");
(repo_alias, addr)
}