Compare commits
No commits in common. "1a932bcde8017cf258b6485e88f1ccdb5a286659" and "91870055b0ec2ff487724adc1f48a23aa28e43c2" have entirely different histories.
1a932bcde8
...
91870055b0
17 changed files with 234 additions and 310 deletions
|
@ -1,9 +1,6 @@
|
|||
[webhook]
|
||||
url = "https://localhost:8080/webhook"
|
||||
|
||||
[storage]
|
||||
path = "./data"
|
||||
|
||||
[forge.default]
|
||||
forge_type = "ForgeJo"
|
||||
hostname = "git.example.net"
|
||||
|
|
|
@ -5,14 +5,13 @@ 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 repo_config = match crate::server::config::load::load(&repo_details, &forge).await {
|
||||
Ok(repo_config) => repo_config,
|
||||
let config = match crate::server::config::load::load(&repo_details, &forge).await {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
error!(?err, "Failed to load config");
|
||||
return;
|
||||
}
|
||||
};
|
||||
addr.do_send(LoadedConfig(repo_config));
|
||||
addr.do_send(LoadedConfig(config));
|
||||
}
|
||||
|
|
|
@ -68,26 +68,11 @@ impl Actor for RepoActor {
|
|||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct CloneRepo;
|
||||
impl Handler<CloneRepo> for RepoActor {
|
||||
pub struct StartRepo;
|
||||
impl Handler<StartRepo> for RepoActor {
|
||||
type Result = ();
|
||||
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");
|
||||
fn handle(&mut self, _msg: StartRepo, ctx: &mut Self::Context) -> Self::Result {
|
||||
info!(%self.details, "Starting Repo");
|
||||
let details = self.details.clone();
|
||||
let addr = ctx.address();
|
||||
let forge = self.forge.clone();
|
||||
|
@ -103,9 +88,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 repo_config = msg.0;
|
||||
info!(%self.details, %repo_config, "Config loaded");
|
||||
self.details.repo_config.replace(repo_config);
|
||||
let config = msg.0;
|
||||
info!(%self.details, %config, "Config loaded");
|
||||
self.details.config.replace(config);
|
||||
if self.webhook_id.is_none() {
|
||||
webhook::register(
|
||||
self.details.clone(),
|
||||
|
@ -126,7 +111,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.repo_config.clone() {
|
||||
if let Some(repo_config) = self.details.config.clone() {
|
||||
let forge = self.forge.clone();
|
||||
let addr = ctx.address();
|
||||
async move { forge.branches_validate_positions(repo_config, addr).await }
|
||||
|
@ -147,7 +132,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.repo_config.clone() else {
|
||||
let Some(repo_config) = self.details.config.clone() else {
|
||||
warn!("No config loaded");
|
||||
return;
|
||||
};
|
||||
|
@ -195,7 +180,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.repo_config.clone() else {
|
||||
let Some(repo_config) = self.details.config.clone() else {
|
||||
warn!("No config loaded");
|
||||
return;
|
||||
};
|
||||
|
|
|
@ -64,11 +64,11 @@ pub async fn unregister(
|
|||
) {
|
||||
info!(?webhook_id, "unregister webhook");
|
||||
let hostname = &repo_details.forge.hostname;
|
||||
let repo_path = repo_details.repo_path;
|
||||
let path = repo_details.repo;
|
||||
use secrecy::ExposeSecret;
|
||||
let token = repo_details.forge.token.expose_secret();
|
||||
let url = network::NetUrl::new(format!(
|
||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
||||
"https://{hostname}/api/v1/repos/{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.repo_config.clone() else {
|
||||
let Some(repo_config) = repo_details.config.clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -105,13 +105,13 @@ pub async fn register(
|
|||
|
||||
info!("Registering webhook");
|
||||
let hostname = &repo_details.forge.hostname;
|
||||
let repo_path = repo_details.repo_path;
|
||||
let path = repo_details.repo;
|
||||
use secrecy::ExposeSecret;
|
||||
let token = repo_details.forge.token.expose_secret();
|
||||
let url = network::NetUrl::new(format!(
|
||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
||||
"https://{hostname}/api/v1/repos/{path}/hooks?token={token}"
|
||||
));
|
||||
let repo_alias = &repo_details.repo_alias;
|
||||
let repo_alias = &repo_details.name;
|
||||
let headers = network::NetRequestHeaders::new().with("Content-Type", "application/json");
|
||||
let authorisation = WebhookAuth::generate();
|
||||
let body = json!({
|
||||
|
@ -153,13 +153,12 @@ async fn find_existing_webhooks(
|
|||
) -> Vec<WebhookId> {
|
||||
let mut ids: Vec<WebhookId> = vec![];
|
||||
let hostname = &repo_details.forge.hostname;
|
||||
let repo_path = &repo_details.repo_path;
|
||||
let path = &repo_details.repo;
|
||||
let mut page = 1;
|
||||
loop {
|
||||
use secrecy::ExposeSecret;
|
||||
let token = &repo_details.forge.token.expose_secret();
|
||||
let url =
|
||||
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?page={page}&token={token}");
|
||||
let url = format!("https://{hostname}/api/v1/repos/{path}/hooks?page={page}&token={token}");
|
||||
let net_url = network::NetUrl::new(url);
|
||||
let request = network::NetRequest::new(
|
||||
network::RequestMethod::Get,
|
||||
|
@ -214,7 +213,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.repo_config {
|
||||
if let Some(config) = &self.details.config {
|
||||
match push.branch(config.branches()) {
|
||||
None => warn!(
|
||||
?push,
|
||||
|
|
|
@ -31,10 +31,6 @@ 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
|
||||
}
|
||||
|
@ -263,7 +259,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 forge_name: ForgeName,
|
||||
pub name: ForgeName,
|
||||
pub forge_type: ForgeType,
|
||||
pub hostname: Hostname,
|
||||
pub user: User,
|
||||
|
@ -274,7 +270,7 @@ pub struct ForgeDetails {
|
|||
impl From<(&ForgeName, &ForgeConfig)> for ForgeDetails {
|
||||
fn from(forge: (&ForgeName, &ForgeConfig)) -> Self {
|
||||
Self {
|
||||
forge_name: forge.0.clone(),
|
||||
name: forge.0.clone(),
|
||||
forge_type: forge.1.forge_type.clone(),
|
||||
hostname: forge.1.hostname(),
|
||||
user: forge.1.user(),
|
||||
|
@ -323,38 +319,30 @@ impl Deref for BranchName {
|
|||
/// The derived information about a repo, used to interact with it
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RepoDetails {
|
||||
pub repo_alias: RepoAlias,
|
||||
pub repo_path: RepoPath,
|
||||
pub name: RepoAlias,
|
||||
pub repo: RepoPath,
|
||||
pub branch: BranchName,
|
||||
pub forge: ForgeDetails,
|
||||
pub repo_config: Option<RepoConfig>,
|
||||
pub gitdir: GitDir,
|
||||
pub config: Option<RepoConfig>,
|
||||
}
|
||||
impl RepoDetails {
|
||||
pub fn new(
|
||||
name: &RepoAlias,
|
||||
server_repo_config: &ServerRepoConfig,
|
||||
repo: &ServerRepoConfig,
|
||||
forge_name: &ForgeName,
|
||||
forge_config: &ForgeConfig,
|
||||
server_storage: &ServerStorage,
|
||||
forge: &ForgeConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
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()),
|
||||
),
|
||||
name: name.clone(),
|
||||
repo: RepoPath(repo.repo.clone()),
|
||||
config: repo.repo_config(),
|
||||
branch: BranchName(repo.branch.clone()),
|
||||
forge: ForgeDetails {
|
||||
forge_name: forge_name.clone(),
|
||||
forge_type: forge_config.forge_type.clone(),
|
||||
hostname: forge_config.hostname(),
|
||||
user: forge_config.user(),
|
||||
token: forge_config.token(),
|
||||
name: forge_name.clone(),
|
||||
forge_type: forge.forge_type.clone(),
|
||||
hostname: forge.hostname(),
|
||||
user: forge.user(),
|
||||
token: forge.token(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -362,11 +350,11 @@ impl RepoDetails {
|
|||
let repo_details = self;
|
||||
let user = &repo_details.forge.user;
|
||||
let hostname = &repo_details.forge.hostname;
|
||||
let repo_path = &repo_details.repo_path;
|
||||
let path = &repo_details.repo;
|
||||
use secrecy::ExposeSecret;
|
||||
let expose_secret = &repo_details.forge.token;
|
||||
let token = expose_secret.expose_secret();
|
||||
let origin = format!("https://{user}:{token}@{hostname}/{repo_path}.git");
|
||||
let origin = format!("https://{user}:{token}@{hostname}/{path}.git");
|
||||
origin.into()
|
||||
}
|
||||
}
|
||||
|
@ -375,12 +363,12 @@ impl Display for RepoDetails {
|
|||
write!(
|
||||
f,
|
||||
"{}/{} ({}): {}:{}/{} @ {}",
|
||||
self.forge.forge_name,
|
||||
self.repo_alias,
|
||||
self.forge.name,
|
||||
self.name,
|
||||
self.forge.forge_type,
|
||||
self.forge.hostname,
|
||||
self.forge.user,
|
||||
self.repo_path,
|
||||
self.repo,
|
||||
self.branch,
|
||||
)
|
||||
}
|
||||
|
@ -404,28 +392,142 @@ impl Display for ForgeType {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub struct GitDir(PathBuf);
|
||||
impl GitDir {
|
||||
#[cfg(feature = "forgejo")]
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new(pathbuf: &std::path::Path) -> Self {
|
||||
Self(pathbuf.to_path_buf())
|
||||
}
|
||||
#[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())
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
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(())
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::server::config::{BranchName, GitDir};
|
||||
use crate::server::config::BranchName;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ForgeFileError {
|
||||
|
@ -42,7 +42,7 @@ impl std::fmt::Display for ForgeBranchError {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum RepoCloneError {
|
||||
InvalidGitDir(GitDir),
|
||||
InvalidGitDir(std::path::PathBuf),
|
||||
Wait(std::io::Error),
|
||||
Spawn(std::io::Error),
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ pub async fn get_all(
|
|||
net: &Network,
|
||||
) -> Result<Vec<gitforge::Branch>, ForgeBranchError> {
|
||||
let hostname = &repo_details.forge.hostname;
|
||||
let repo_path = &repo_details.repo_path;
|
||||
let path = &repo_details.repo;
|
||||
use secrecy::ExposeSecret;
|
||||
let token = repo_details.forge.token.expose_secret();
|
||||
let url = network::NetUrl::new(format!(
|
||||
"https://{hostname}/api/v1/repos/{repo_path}/branches?token={token}"
|
||||
"https://{hostname}/api/v1/repos/{path}/branches?token={token}"
|
||||
));
|
||||
|
||||
info!(%url, "Listing branches");
|
||||
|
|
|
@ -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 repo_path = &repo_details.repo_path;
|
||||
let path = &repo_details.repo;
|
||||
|
||||
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/{repo_path}/commits?sha={branch_name}&{options}&token={token}&page={page}&limit={limit}"
|
||||
"https://{hostname}/api/v1/repos/{path}/commits?sha={branch_name}&{options}&token={token}&page={page}&limit={limit}"
|
||||
));
|
||||
|
||||
let request = network::NetRequest::new(
|
||||
|
|
|
@ -13,12 +13,12 @@ pub(super) async fn contents_get(
|
|||
file_path: &str,
|
||||
) -> Result<String, ForgeFileError> {
|
||||
let hostname = &repo_details.forge.hostname;
|
||||
let repo_path = &repo_details.repo_path;
|
||||
let path = &repo_details.repo;
|
||||
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/{repo_path}/contents/{file_path}?ref={branch}&token={token}"
|
||||
"https://{hostname}/api/v1/repos/{path}/contents/{file_path}?ref={branch}&token={token}"
|
||||
));
|
||||
|
||||
info!(%url, "Loading config");
|
||||
|
|
|
@ -2,6 +2,8 @@ mod branch;
|
|||
mod file;
|
||||
mod repo;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use actix::prelude::*;
|
||||
|
||||
use kxio::network::{self, Network};
|
||||
|
@ -9,7 +11,7 @@ use tracing::{error, warn};
|
|||
|
||||
use crate::server::{
|
||||
actors::repo::RepoActor,
|
||||
config::{BranchName, GitDir, RepoConfig, RepoDetails},
|
||||
config::{BranchName, RepoConfig, RepoDetails},
|
||||
gitforge::{self, RepoCloneError},
|
||||
types::GitRef,
|
||||
};
|
||||
|
@ -59,12 +61,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 repo_path = &repo_details.repo_path;
|
||||
let path = &repo_details.repo;
|
||||
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/{repo_path}/commits/{commit}/status?token={token}"
|
||||
"https://{hostname}/api/v1/repos/{path}/commits/{commit}/status?token={token}"
|
||||
));
|
||||
|
||||
let request = network::NetRequest::new(
|
||||
|
@ -100,7 +102,7 @@ impl super::ForgeLike for ForgeJoEnv {
|
|||
}
|
||||
}
|
||||
|
||||
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError> {
|
||||
fn repo_clone(&self, gitdir: PathBuf) -> Result<(), RepoCloneError> {
|
||||
repo::clone(&self.repo_details, gitdir)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::server::{
|
||||
config::{GitDir, RepoDetails},
|
||||
gitforge::RepoCloneError,
|
||||
};
|
||||
use crate::server::{config::RepoDetails, gitforge::RepoCloneError};
|
||||
|
||||
pub fn clone(repo_details: &RepoDetails, gitdir: GitDir) -> Result<(), RepoCloneError> {
|
||||
pub fn clone(repo_details: &RepoDetails, gitdir: PathBuf) -> Result<(), RepoCloneError> {
|
||||
let Some(gitdir) = gitdir.to_str() else {
|
||||
return Err(RepoCloneError::InvalidGitDir(gitdir));
|
||||
};
|
||||
let origin = repo_details.origin();
|
||||
// INFO: never log the command as it contains the API token within the 'origin'
|
||||
use secrecy::ExposeSecret;
|
||||
|
@ -15,7 +17,7 @@ pub fn clone(repo_details: &RepoDetails, gitdir: GitDir) -> Result<(), RepoClone
|
|||
gitdir
|
||||
)
|
||||
.into();
|
||||
let repo_name = &repo_details.repo_alias;
|
||||
let repo_name = &repo_details.name;
|
||||
info!("Cloning {repo_name} to {gitdir}");
|
||||
match gix::command::prepare(command.expose_secret())
|
||||
.with_shell_allow_argument_splitting()
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use crate::server::{
|
||||
actors::repo::RepoActor,
|
||||
config::{BranchName, GitDir, RepoConfig},
|
||||
config::{BranchName, RepoConfig},
|
||||
gitforge::{self, RepoCloneError},
|
||||
types::GitRef,
|
||||
};
|
||||
|
@ -52,7 +54,7 @@ impl super::ForgeLike for MockForgeEnv {
|
|||
todo!()
|
||||
}
|
||||
|
||||
fn repo_clone(&self, _gitdir: GitDir) -> Result<(), RepoCloneError> {
|
||||
fn repo_clone(&self, _gitdir: PathBuf) -> Result<(), RepoCloneError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use kxio::network::Network;
|
||||
|
||||
#[cfg(feature = "forgejo")]
|
||||
|
@ -17,7 +19,7 @@ mod errors;
|
|||
pub use errors::*;
|
||||
|
||||
use crate::server::{
|
||||
config::{BranchName, GitDir, RepoConfig, RepoDetails},
|
||||
config::{BranchName, RepoConfig, RepoDetails},
|
||||
types::GitRef,
|
||||
};
|
||||
|
||||
|
@ -55,7 +57,7 @@ pub trait ForgeLike {
|
|||
async fn commit_status(&self, commit: &Commit) -> CommitStatus;
|
||||
|
||||
/// Clones a repo to disk.
|
||||
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError>;
|
||||
fn repo_clone(&self, gitdir: PathBuf) -> Result<(), RepoCloneError>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::server::config::{
|
||||
ApiToken, BranchName, ForgeDetails, ForgeName, ForgeType, GitDir, Hostname, RepoAlias,
|
||||
RepoBranches, RepoConfig, RepoDetails, RepoPath, User,
|
||||
ApiToken, BranchName, ForgeDetails, ForgeName, ForgeType, Hostname, RepoAlias, RepoBranches,
|
||||
RepoConfig, RepoDetails, RepoPath, User,
|
||||
};
|
||||
|
||||
pub fn forge_details(n: u32, forge_type: ForgeType) -> ForgeDetails {
|
||||
ForgeDetails {
|
||||
forge_name: forge_name(n),
|
||||
name: forge_name(n),
|
||||
forge_type,
|
||||
hostname: hostname(n),
|
||||
user: user(n),
|
||||
|
@ -28,19 +28,13 @@ 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,
|
||||
repo_config: Option<RepoConfig>,
|
||||
gitdir: GitDir,
|
||||
) -> RepoDetails {
|
||||
pub fn repo_details(n: u32, forge: ForgeDetails, config: Option<RepoConfig>) -> RepoDetails {
|
||||
RepoDetails {
|
||||
repo_alias: repo_alias(n),
|
||||
repo_path: repo_path(n),
|
||||
gitdir,
|
||||
name: repo_alias(n),
|
||||
repo: repo_path(n),
|
||||
branch: branch_name(n),
|
||||
forge,
|
||||
repo_config,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +50,7 @@ pub fn repo_alias(n: u32) -> RepoAlias {
|
|||
RepoAlias(format!("repo-alias-{}", n))
|
||||
}
|
||||
|
||||
pub fn repo_config(n: u32) -> RepoConfig {
|
||||
pub fn config(n: u32) -> RepoConfig {
|
||||
RepoConfig::new(RepoBranches::new(
|
||||
format!("main-{n}"),
|
||||
format!("next-{n}"),
|
||||
|
|
|
@ -8,15 +8,11 @@ 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::repo_config(1)),
|
||||
GitDir::new(fs.cwd()),
|
||||
Some(common::config(1)),
|
||||
);
|
||||
let forge = Forge::new_forgejo(repo_details, net);
|
||||
assert_eq!(forge.name(), "forgejo");
|
||||
|
@ -24,9 +20,6 @@ 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);
|
||||
|
@ -41,8 +34,7 @@ async fn test_branches_get() {
|
|||
let repo_details = common::repo_details(
|
||||
1,
|
||||
common::forge_details(1, ForgeType::MockForge),
|
||||
Some(common::repo_config(1)),
|
||||
GitDir::new(fs.cwd()),
|
||||
Some(common::config(1)),
|
||||
);
|
||||
|
||||
let forge = Forge::new_forgejo(repo_details, net.clone());
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
filesystem::FileSystem,
|
||||
server::{
|
||||
actors::webhook,
|
||||
config::{ForgeConfig, ForgeName, RepoAlias, ServerStorage, Webhook},
|
||||
config::{ForgeConfig, ForgeName, RepoAlias, Webhook},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -55,13 +55,9 @@ 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_config)| {
|
||||
create_forge_repos(forge_config, forge_name, server_storage, webhook, &net)
|
||||
})
|
||||
.flat_map(|(forge_name, forge)| create_forge_repos(forge, forge_name, webhook, &net))
|
||||
.map(start_actor)
|
||||
.map(|(alias, addr)| webhook::AddWebhookRecipient(alias, addr.recipient()))
|
||||
.for_each(|msg| webhook_router.do_send(msg));
|
||||
|
@ -72,49 +68,35 @@ pub async fn start(fs: FileSystem, net: Network) {
|
|||
}
|
||||
|
||||
fn create_forge_repos(
|
||||
forge_config: &ForgeConfig,
|
||||
forge: &ForgeConfig,
|
||||
forge_name: ForgeName,
|
||||
server_storage: &ServerStorage,
|
||||
webhook: &Webhook,
|
||||
net: &Network,
|
||||
) -> Vec<(ForgeName, RepoAlias, RepoActor)> {
|
||||
let span = tracing::info_span!("Forge", %forge_name, %forge_config);
|
||||
let forge = forge.clone();
|
||||
let span = tracing::info_span!("Forge", %forge_name, %forge);
|
||||
let _guard = span.enter();
|
||||
info!("Creating Forge");
|
||||
forge_config
|
||||
forge
|
||||
.repos()
|
||||
.map(create_actor(
|
||||
forge_name,
|
||||
forge_config.clone(),
|
||||
server_storage,
|
||||
webhook,
|
||||
net,
|
||||
))
|
||||
.map(create_actor(forge_name, forge.clone(), webhook, net))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn create_actor(
|
||||
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,
|
||||
forge_name: ForgeName,
|
||||
forge: config::ForgeConfig,
|
||||
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, server_repo_config)| {
|
||||
let span = tracing::info_span!("Repo", %repo_name, %server_repo_config);
|
||||
move |(repo_name, repo)| {
|
||||
let span = tracing::info_span!("Repo", %repo_name, %repo);
|
||||
let _guard = span.enter();
|
||||
info!("Creating Repo");
|
||||
let actor = actors::repo::RepoActor::new(
|
||||
config::RepoDetails::new(
|
||||
&repo_name,
|
||||
server_repo_config,
|
||||
&forge_name,
|
||||
&forge_config,
|
||||
&server_storage,
|
||||
),
|
||||
config::RepoDetails::new(&repo_name, repo, &forge_name, &forge),
|
||||
webhook.clone(),
|
||||
net.clone(),
|
||||
);
|
||||
|
@ -131,7 +113,7 @@ fn start_actor(
|
|||
let _guard = span.enter();
|
||||
info!("Starting");
|
||||
let addr = actor.start();
|
||||
addr.do_send(actors::repo::CloneRepo);
|
||||
addr.do_send(actors::repo::StartRepo);
|
||||
info!("Started");
|
||||
(repo_alias, addr)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue