forked from kemitix/git-next
feat: Webhook query paths include forge alias
This allows for more than one forge to be configured and for the webhook to correctly route incoming messages.
This commit is contained in:
parent
17148e74b6
commit
206e64cd5b
17 changed files with 119 additions and 80 deletions
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ApiToken, BranchName, ForgeDetails, ForgeName, ForgeType, Hostname, RepoAlias, RepoBranches,
|
ApiToken, BranchName, ForgeAlias, ForgeDetails, ForgeType, Hostname, RepoAlias, RepoBranches,
|
||||||
RepoConfig, RepoConfigSource, RepoPath, User,
|
RepoConfig, RepoConfigSource, RepoPath, User,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ pub fn hostname(n: u32) -> Hostname {
|
||||||
Hostname::new(format!("hostname-{}", n))
|
Hostname::new(format!("hostname-{}", n))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn forge_name(n: u32) -> ForgeName {
|
pub fn forge_name(n: u32) -> ForgeAlias {
|
||||||
ForgeName::new(format!("forge-name-{}", n))
|
ForgeAlias::new(format!("forge-name-{}", n))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn branch_name(n: u32) -> BranchName {
|
pub fn branch_name(n: u32) -> BranchName {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{ApiToken, ForgeConfig, ForgeName, ForgeType, Hostname, User};
|
use crate::{ApiToken, ForgeAlias, ForgeConfig, ForgeType, Hostname, User};
|
||||||
|
|
||||||
/// The derived information about a Forge, used to create interactions with it
|
/// The derived information about a Forge, used to create interactions with it
|
||||||
#[derive(Clone, Default, Debug, derive_more::Constructor, derive_with::With)]
|
#[derive(Clone, Default, Debug, derive_more::Constructor, derive_with::With)]
|
||||||
pub struct ForgeDetails {
|
pub struct ForgeDetails {
|
||||||
forge_name: ForgeName,
|
forge_alias: ForgeAlias,
|
||||||
forge_type: ForgeType,
|
forge_type: ForgeType,
|
||||||
hostname: Hostname,
|
hostname: Hostname,
|
||||||
user: User,
|
user: User,
|
||||||
|
@ -12,8 +12,8 @@ pub struct ForgeDetails {
|
||||||
// Private SSH Key Path
|
// Private SSH Key Path
|
||||||
}
|
}
|
||||||
impl ForgeDetails {
|
impl ForgeDetails {
|
||||||
pub const fn forge_name(&self) -> &ForgeName {
|
pub const fn forge_alias(&self) -> &ForgeAlias {
|
||||||
&self.forge_name
|
&self.forge_alias
|
||||||
}
|
}
|
||||||
pub const fn forge_type(&self) -> ForgeType {
|
pub const fn forge_type(&self) -> ForgeType {
|
||||||
self.forge_type
|
self.forge_type
|
||||||
|
@ -28,10 +28,10 @@ impl ForgeDetails {
|
||||||
&self.token
|
&self.token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<(&ForgeName, &ForgeConfig)> for ForgeDetails {
|
impl From<(&ForgeAlias, &ForgeConfig)> for ForgeDetails {
|
||||||
fn from(forge: (&ForgeName, &ForgeConfig)) -> Self {
|
fn from(forge: (&ForgeAlias, &ForgeConfig)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
forge_name: forge.0.clone(),
|
forge_alias: forge.0.clone(),
|
||||||
forge_type: forge.1.forge_type(),
|
forge_type: forge.1.forge_type(),
|
||||||
hostname: forge.1.hostname(),
|
hostname: forge.1.hostname(),
|
||||||
user: forge.1.user(),
|
user: forge.1.user(),
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// The name of a Forge to connect to
|
/// The name of a Forge to connect to
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq, derive_more::Constructor, derive_more::Display)]
|
#[derive(
|
||||||
pub struct ForgeName(String);
|
Clone, Default, Debug, Hash, PartialEq, Eq, derive_more::Constructor, derive_more::Display,
|
||||||
impl From<&ForgeName> for PathBuf {
|
)]
|
||||||
fn from(value: &ForgeName) -> Self {
|
pub struct ForgeAlias(String);
|
||||||
|
impl From<&ForgeAlias> for PathBuf {
|
||||||
|
fn from(value: &ForgeAlias) -> Self {
|
||||||
Self::from(&value.0)
|
Self::from(&value.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub use api_token::ApiToken;
|
||||||
pub use branch_name::BranchName;
|
pub use branch_name::BranchName;
|
||||||
pub use forge_config::ForgeConfig;
|
pub use forge_config::ForgeConfig;
|
||||||
pub use forge_details::ForgeDetails;
|
pub use forge_details::ForgeDetails;
|
||||||
pub use forge_name::ForgeName;
|
pub use forge_name::ForgeAlias;
|
||||||
pub use forge_type::ForgeType;
|
pub use forge_type::ForgeType;
|
||||||
pub use git_dir::GitDir;
|
pub use git_dir::GitDir;
|
||||||
pub use host_name::Hostname;
|
pub use host_name::Hostname;
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::{
|
||||||
use kxio::fs::FileSystem;
|
use kxio::fs::FileSystem;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::{ForgeConfig, ForgeName};
|
use crate::{ForgeAlias, ForgeConfig, RepoAlias};
|
||||||
|
|
||||||
#[derive(Debug, derive_more::From, derive_more::Display)]
|
#[derive(Debug, derive_more::From, derive_more::Display)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -43,10 +43,10 @@ impl ServerConfig {
|
||||||
toml::from_str(&str).map_err(Into::into)
|
toml::from_str(&str).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn forges(&self) -> impl Iterator<Item = (ForgeName, &ForgeConfig)> {
|
pub fn forges(&self) -> impl Iterator<Item = (ForgeAlias, &ForgeConfig)> {
|
||||||
self.forge
|
self.forge
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, forge)| (ForgeName::new(name.clone()), forge))
|
.map(|(alias, forge)| (ForgeAlias::new(alias.clone()), forge))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn storage(&self) -> &ServerStorage {
|
pub const fn storage(&self) -> &ServerStorage {
|
||||||
|
@ -84,8 +84,9 @@ pub struct Webhook {
|
||||||
url: String,
|
url: String,
|
||||||
}
|
}
|
||||||
impl Webhook {
|
impl Webhook {
|
||||||
pub fn url(&self) -> WebhookUrl {
|
pub fn url(&self, forge_alias: &ForgeAlias, repo_alias: &RepoAlias) -> WebhookUrl {
|
||||||
WebhookUrl(self.url.clone())
|
let base_url = &self.url;
|
||||||
|
WebhookUrl(format!("{base_url}/{forge_alias}/{repo_alias}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -252,7 +252,7 @@ mod forge_details {
|
||||||
|
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
|
|
||||||
use crate::{ApiToken, ForgeConfig, ForgeDetails, ForgeName, ForgeType, Hostname, User};
|
use crate::{ApiToken, ForgeAlias, ForgeConfig, ForgeDetails, ForgeType, Hostname, User};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_forge_name() {
|
fn should_return_forge_name() {
|
||||||
|
@ -260,11 +260,11 @@ mod forge_details {
|
||||||
let hostname = Hostname::new("localhost".to_string());
|
let hostname = Hostname::new("localhost".to_string());
|
||||||
let user = User::new("bob".to_string());
|
let user = User::new("bob".to_string());
|
||||||
let token = ApiToken::new("alpha".to_string().into());
|
let token = ApiToken::new("alpha".to_string().into());
|
||||||
let forge_name = ForgeName::new("gamma".to_string());
|
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||||
let forge_details =
|
let forge_details =
|
||||||
ForgeDetails::new(forge_name.clone(), forge_type, hostname, user, token);
|
ForgeDetails::new(forge_name.clone(), forge_type, hostname, user, token);
|
||||||
|
|
||||||
let result = forge_details.forge_name();
|
let result = forge_details.forge_alias();
|
||||||
|
|
||||||
assert_eq!(result, &forge_name);
|
assert_eq!(result, &forge_name);
|
||||||
}
|
}
|
||||||
|
@ -274,7 +274,7 @@ mod forge_details {
|
||||||
let hostname = Hostname::new("localhost".to_string());
|
let hostname = Hostname::new("localhost".to_string());
|
||||||
let user = User::new("bob".to_string());
|
let user = User::new("bob".to_string());
|
||||||
let token = ApiToken::new("alpha".to_string().into());
|
let token = ApiToken::new("alpha".to_string().into());
|
||||||
let forge_name = ForgeName::new("gamma".to_string());
|
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||||
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token);
|
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token);
|
||||||
|
|
||||||
let result = forge_details.forge_type();
|
let result = forge_details.forge_type();
|
||||||
|
@ -287,7 +287,7 @@ mod forge_details {
|
||||||
let hostname = Hostname::new("localhost".to_string());
|
let hostname = Hostname::new("localhost".to_string());
|
||||||
let user = User::new("bob".to_string());
|
let user = User::new("bob".to_string());
|
||||||
let token = ApiToken::new("alpha".to_string().into());
|
let token = ApiToken::new("alpha".to_string().into());
|
||||||
let forge_name = ForgeName::new("gamma".to_string());
|
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||||
let forge_details =
|
let forge_details =
|
||||||
ForgeDetails::new(forge_name, forge_type, hostname.clone(), user, token);
|
ForgeDetails::new(forge_name, forge_type, hostname.clone(), user, token);
|
||||||
|
|
||||||
|
@ -301,7 +301,7 @@ mod forge_details {
|
||||||
let hostname = Hostname::new("localhost".to_string());
|
let hostname = Hostname::new("localhost".to_string());
|
||||||
let user = User::new("bob".to_string());
|
let user = User::new("bob".to_string());
|
||||||
let token = ApiToken::new("alpha".to_string().into());
|
let token = ApiToken::new("alpha".to_string().into());
|
||||||
let forge_name = ForgeName::new("gamma".to_string());
|
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||||
let forge_details =
|
let forge_details =
|
||||||
ForgeDetails::new(forge_name, forge_type, hostname, user.clone(), token);
|
ForgeDetails::new(forge_name, forge_type, hostname, user.clone(), token);
|
||||||
|
|
||||||
|
@ -315,7 +315,7 @@ mod forge_details {
|
||||||
let hostname = Hostname::new("localhost".to_string());
|
let hostname = Hostname::new("localhost".to_string());
|
||||||
let user = User::new("bob".to_string());
|
let user = User::new("bob".to_string());
|
||||||
let token = ApiToken::new("alpha".to_string().into());
|
let token = ApiToken::new("alpha".to_string().into());
|
||||||
let forge_name = ForgeName::new("gamma".to_string());
|
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||||
let forge_details =
|
let forge_details =
|
||||||
ForgeDetails::new(forge_name, forge_type, hostname, user, token.clone());
|
ForgeDetails::new(forge_name, forge_type, hostname, user, token.clone());
|
||||||
|
|
||||||
|
@ -329,7 +329,7 @@ mod forge_details {
|
||||||
let hostname = Hostname::new("localhost".to_string());
|
let hostname = Hostname::new("localhost".to_string());
|
||||||
let user = User::new("bob".to_string());
|
let user = User::new("bob".to_string());
|
||||||
let token = ApiToken::new("alpha".to_string().into());
|
let token = ApiToken::new("alpha".to_string().into());
|
||||||
let forge_name = ForgeName::new("gamma".to_string());
|
let forge_name = ForgeAlias::new("gamma".to_string());
|
||||||
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token);
|
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token);
|
||||||
|
|
||||||
let result = forge_details.with_hostname(Hostname::new("remotehost".to_string()));
|
let result = forge_details.with_hostname(Hostname::new("remotehost".to_string()));
|
||||||
|
@ -342,7 +342,7 @@ mod forge_details {
|
||||||
let hostname = Hostname::new("localhost".to_string());
|
let hostname = Hostname::new("localhost".to_string());
|
||||||
let user = User::new("bob".to_string());
|
let user = User::new("bob".to_string());
|
||||||
let token = ApiToken::new("alpha".to_string().into());
|
let token = ApiToken::new("alpha".to_string().into());
|
||||||
let forge_name = ForgeName::new("gamma".to_string());
|
let forge_alias = ForgeAlias::new("gamma".to_string());
|
||||||
let forge_config = ForgeConfig::new(
|
let forge_config = ForgeConfig::new(
|
||||||
forge_type,
|
forge_type,
|
||||||
"localhost".to_string(),
|
"localhost".to_string(),
|
||||||
|
@ -351,9 +351,9 @@ mod forge_details {
|
||||||
BTreeMap::new(),
|
BTreeMap::new(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let forge_details = ForgeDetails::from((&forge_name, &forge_config));
|
let forge_details = ForgeDetails::from((&forge_alias, &forge_config));
|
||||||
|
|
||||||
assert_eq!(forge_details.forge_name(), &forge_name);
|
assert_eq!(forge_details.forge_alias(), &forge_alias);
|
||||||
assert_eq!(forge_details.hostname(), &hostname);
|
assert_eq!(forge_details.hostname(), &hostname);
|
||||||
assert_eq!(forge_details.user(), &user);
|
assert_eq!(forge_details.user(), &user);
|
||||||
assert_eq!(forge_details.token().expose_secret(), token.expose_secret());
|
assert_eq!(forge_details.token().expose_secret(), token.expose_secret());
|
||||||
|
@ -362,13 +362,13 @@ mod forge_details {
|
||||||
mod forge_name {
|
mod forge_name {
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::ForgeName;
|
use crate::ForgeAlias;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_convert_to_pathbuf() {
|
fn should_convert_to_pathbuf() {
|
||||||
let forge_name = ForgeName::new("alpha".to_string());
|
let forge_alias = ForgeAlias::new("alpha".to_string());
|
||||||
|
|
||||||
let pathbuf: PathBuf = (&forge_name).into();
|
let pathbuf: PathBuf = (&forge_alias).into();
|
||||||
|
|
||||||
assert_eq!(pathbuf, PathBuf::new().join("alpha"));
|
assert_eq!(pathbuf, PathBuf::new().join("alpha"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ impl ForgeJo {
|
||||||
}
|
}
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl git::ForgeLike for ForgeJo {
|
impl git::ForgeLike for ForgeJo {
|
||||||
fn name(&self) -> String {
|
fn forge_alias(&self) -> String {
|
||||||
"forgejo".to_string()
|
"forgejo".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ impl MockForgeEnv {
|
||||||
}
|
}
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl git::ForgeLike for MockForgeEnv {
|
impl git::ForgeLike for MockForgeEnv {
|
||||||
fn name(&self) -> String {
|
fn forge_alias(&self) -> String {
|
||||||
"mock".to_string()
|
"mock".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod github;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mock_name() {
|
fn test_mock_name() {
|
||||||
let forge = Forge::new_mock();
|
let forge = Forge::new_mock();
|
||||||
assert_eq!(forge.name(), "mock");
|
assert_eq!(forge.forge_alias(), "mock");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -30,5 +30,5 @@ fn test_forgejo_name() {
|
||||||
config::GitDir::new(fs.base()),
|
config::GitDir::new(fs.base()),
|
||||||
);
|
);
|
||||||
let forge = Forge::new_forgejo(repo_details, net);
|
let forge = Forge::new_forgejo(repo_details, net);
|
||||||
assert_eq!(forge.name(), "forgejo");
|
assert_eq!(forge.forge_alias(), "forgejo");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate as git;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait ForgeLike {
|
pub trait ForgeLike {
|
||||||
fn name(&self) -> String;
|
fn forge_alias(&self) -> String;
|
||||||
|
|
||||||
/// Checks the results of any (e.g. CI) status checks for the commit.
|
/// Checks the results of any (e.g. CI) status checks for the commit.
|
||||||
async fn commit_status(&self, commit: &git::Commit) -> git::commit::Status;
|
async fn commit_status(&self, commit: &git::Commit) -> git::commit::Status;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use git_next_config::{
|
use git_next_config::{
|
||||||
BranchName, ForgeConfig, ForgeDetails, ForgeName, GitDir, RepoAlias, RepoConfig, RepoPath,
|
BranchName, ForgeAlias, ForgeConfig, ForgeDetails, GitDir, RepoAlias, RepoConfig, RepoPath,
|
||||||
ServerRepoConfig,
|
ServerRepoConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use super::{Generation, GitRemote};
|
||||||
/// The derived information about a repo, used to interact with it
|
/// The derived information about a repo, used to interact with it
|
||||||
#[derive(Clone, Default, Debug, derive_more::Display, derive_with::With)]
|
#[derive(Clone, Default, Debug, derive_more::Display, derive_with::With)]
|
||||||
#[display("gen-{}:{}:{}/{}:{}@{}/{}@{}", generation, forge.forge_type(),
|
#[display("gen-{}:{}:{}/{}:{}@{}/{}@{}", generation, forge.forge_type(),
|
||||||
forge.forge_name(), repo_alias, forge.user(), forge.hostname(), repo_path,
|
forge.forge_alias(), repo_alias, forge.user(), forge.hostname(), repo_path,
|
||||||
branch)]
|
branch)]
|
||||||
pub struct RepoDetails {
|
pub struct RepoDetails {
|
||||||
pub generation: Generation,
|
pub generation: Generation,
|
||||||
|
@ -24,7 +24,7 @@ impl RepoDetails {
|
||||||
generation: Generation,
|
generation: Generation,
|
||||||
repo_alias: &RepoAlias,
|
repo_alias: &RepoAlias,
|
||||||
server_repo_config: &ServerRepoConfig,
|
server_repo_config: &ServerRepoConfig,
|
||||||
forge_name: &ForgeName,
|
forge_alias: &ForgeAlias,
|
||||||
forge_config: &ForgeConfig,
|
forge_config: &ForgeConfig,
|
||||||
gitdir: GitDir,
|
gitdir: GitDir,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -36,7 +36,7 @@ impl RepoDetails {
|
||||||
branch: server_repo_config.branch(),
|
branch: server_repo_config.branch(),
|
||||||
gitdir,
|
gitdir,
|
||||||
forge: ForgeDetails::new(
|
forge: ForgeDetails::new(
|
||||||
forge_name.clone(),
|
forge_alias.clone(),
|
||||||
forge_config.forge_type(),
|
forge_config.forge_type(),
|
||||||
forge_config.hostname(),
|
forge_config.hostname(),
|
||||||
forge_config.user(),
|
forge_config.user(),
|
||||||
|
|
|
@ -92,7 +92,7 @@ mod repo_details {
|
||||||
use std::{collections::BTreeMap, path::PathBuf};
|
use std::{collections::BTreeMap, path::PathBuf};
|
||||||
|
|
||||||
use git_next_config::{
|
use git_next_config::{
|
||||||
ForgeConfig, ForgeName, ForgeType, GitDir, Hostname, RepoAlias, RepoPath, ServerRepoConfig,
|
ForgeAlias, ForgeConfig, ForgeType, GitDir, Hostname, RepoAlias, RepoPath, ServerRepoConfig,
|
||||||
};
|
};
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ mod repo_details {
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
&ForgeName::new("default".to_string()),
|
&ForgeAlias::new("default".to_string()),
|
||||||
&ForgeConfig::new(
|
&ForgeConfig::new(
|
||||||
ForgeType::MockForge,
|
ForgeType::MockForge,
|
||||||
"host".to_string(),
|
"host".to_string(),
|
||||||
|
@ -140,7 +140,7 @@ mod repo_details {
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
&ForgeName::new("default".to_string()),
|
&ForgeAlias::new("default".to_string()),
|
||||||
&ForgeConfig::new(
|
&ForgeConfig::new(
|
||||||
ForgeType::MockForge,
|
ForgeType::MockForge,
|
||||||
"host".to_string(),
|
"host".to_string(),
|
||||||
|
|
|
@ -20,7 +20,7 @@ use kxio::network::Network;
|
||||||
use tracing::{debug, info, warn, Instrument};
|
use tracing::{debug, info, warn, Instrument};
|
||||||
|
|
||||||
#[derive(Debug, derive_more::Display)]
|
#[derive(Debug, derive_more::Display)]
|
||||||
#[display("{}:{}:{}", generation, repo_details.forge.forge_name(), repo_details.repo_alias)]
|
#[display("{}:{}:{}", generation, repo_details.forge.forge_alias(), repo_details.repo_alias)]
|
||||||
pub struct RepoActor {
|
pub struct RepoActor {
|
||||||
generation: git::Generation,
|
generation: git::Generation,
|
||||||
message_token: MessageToken,
|
message_token: MessageToken,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use git_next_config::{
|
use git_next_config::{
|
||||||
server::{Webhook, WebhookUrl},
|
server::{Webhook, WebhookUrl},
|
||||||
BranchName, RepoAlias, RepoBranches,
|
BranchName, ForgeAlias, RepoAlias, RepoBranches,
|
||||||
};
|
};
|
||||||
use git_next_git as git;
|
use git_next_git as git;
|
||||||
use kxio::network::{self, json};
|
use kxio::network::{self, json};
|
||||||
|
@ -75,7 +75,9 @@ pub async fn register(
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let webhook_url = webhook.url();
|
let forge_alias = repo_details.forge.forge_alias();
|
||||||
|
let repo_alias = &repo_details.repo_alias;
|
||||||
|
let webhook_url = webhook.url(forge_alias, repo_alias);
|
||||||
// remove any lingering webhooks for the same URL
|
// remove any lingering webhooks for the same URL
|
||||||
let existing_webhook_ids = find_existing_webhooks(&repo_details, &webhook_url, &net).await;
|
let existing_webhook_ids = find_existing_webhooks(&repo_details, &webhook_url, &net).await;
|
||||||
for webhook_id in existing_webhook_ids {
|
for webhook_id in existing_webhook_ids {
|
||||||
|
@ -89,7 +91,6 @@ pub async fn register(
|
||||||
let url = network::NetUrl::new(format!(
|
let url = network::NetUrl::new(format!(
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
||||||
));
|
));
|
||||||
let repo_alias = &repo_details.repo_alias;
|
|
||||||
let headers = network::NetRequestHeaders::new().with("Content-Type", "application/json");
|
let headers = network::NetRequestHeaders::new().with("Content-Type", "application/json");
|
||||||
let authorisation = WebhookAuth::generate();
|
let authorisation = WebhookAuth::generate();
|
||||||
let body = json!({
|
let body = json!({
|
||||||
|
@ -98,7 +99,7 @@ pub async fn register(
|
||||||
"branch_filter": format!("{{{},{},{}}}", repo_config.branches().main(), repo_config.branches().next(), repo_config.branches().dev()),
|
"branch_filter": format!("{{{},{},{}}}", repo_config.branches().main(), repo_config.branches().next(), repo_config.branches().dev()),
|
||||||
"config": {
|
"config": {
|
||||||
"content_type": "json",
|
"content_type": "json",
|
||||||
"url": format!("{}/{}", webhook_url.as_ref(), repo_alias),
|
"url": webhook_url.as_ref(),
|
||||||
},
|
},
|
||||||
"events": [ "push" ],
|
"events": [ "push" ],
|
||||||
"type": "forgejo"
|
"type": "forgejo"
|
||||||
|
@ -156,7 +157,7 @@ async fn find_existing_webhooks(
|
||||||
}
|
}
|
||||||
for hook in list {
|
for hook in list {
|
||||||
if let Some(existing_url) = hook.config.get("url") {
|
if let Some(existing_url) = hook.config.get("url") {
|
||||||
if existing_url.starts_with(webhook_url.as_ref()) {
|
if existing_url == webhook_url.as_ref() {
|
||||||
ids.push(hook.id());
|
ids.push(hook.id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,12 +313,15 @@ struct HeadCommit {
|
||||||
#[derive(Message, Debug, Clone, derive_more::Constructor)]
|
#[derive(Message, Debug, Clone, derive_more::Constructor)]
|
||||||
#[rtype(result = "()")]
|
#[rtype(result = "()")]
|
||||||
pub struct WebhookMessage {
|
pub struct WebhookMessage {
|
||||||
// forge // TODO: (#58) differentiate between multiple forges
|
forge_alias: ForgeAlias,
|
||||||
repo_alias: RepoAlias,
|
repo_alias: RepoAlias,
|
||||||
authorisation: WebhookAuth,
|
authorisation: WebhookAuth,
|
||||||
body: Body,
|
body: Body,
|
||||||
}
|
}
|
||||||
impl WebhookMessage {
|
impl WebhookMessage {
|
||||||
|
pub const fn forge_alias(&self) -> &ForgeAlias {
|
||||||
|
&self.forge_alias
|
||||||
|
}
|
||||||
pub const fn repo_alias(&self) -> &RepoAlias {
|
pub const fn repo_alias(&self) -> &RepoAlias {
|
||||||
&self.repo_alias
|
&self.repo_alias
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use actix::prelude::*;
|
||||||
|
|
||||||
use config::server::{ServerConfig, ServerStorage, Webhook};
|
use config::server::{ServerConfig, ServerStorage, Webhook};
|
||||||
use git_next_config::{
|
use git_next_config::{
|
||||||
self as config, ForgeConfig, ForgeName, GitDir, RepoAlias, ServerRepoConfig,
|
self as config, ForgeAlias, ForgeConfig, GitDir, RepoAlias, ServerRepoConfig,
|
||||||
};
|
};
|
||||||
use git_next_git::{Generation, RepoDetails, Repository};
|
use git_next_git::{Generation, RepoDetails, Repository};
|
||||||
use git_next_repo_actor::{CloneRepo, RepoActor};
|
use git_next_repo_actor::{CloneRepo, RepoActor};
|
||||||
|
@ -99,11 +99,13 @@ impl Handler<ServerConfig> for Server {
|
||||||
let webhook = server_config.webhook();
|
let webhook = server_config.webhook();
|
||||||
|
|
||||||
// Forge Actors
|
// Forge Actors
|
||||||
for (forge_name, forge_config) in server_config.forges() {
|
for (forge_alias, forge_config) in server_config.forges() {
|
||||||
self.create_forge_repos(forge_config, forge_name.clone(), server_storage, webhook)
|
self.create_forge_repos(forge_config, forge_alias.clone(), server_storage, webhook)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| self.start_actor(a))
|
.map(|a| self.start_actor(a))
|
||||||
.map(|(alias, addr)| AddWebhookRecipient(alias, addr.recipient()))
|
.map(|(repo_alias, addr)| {
|
||||||
|
AddWebhookRecipient::new(forge_alias.clone(), repo_alias, addr.recipient())
|
||||||
|
})
|
||||||
.for_each(|msg| webhook_router.do_send(msg));
|
.for_each(|msg| webhook_router.do_send(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,10 +148,10 @@ impl Server {
|
||||||
fn create_forge_repos(
|
fn create_forge_repos(
|
||||||
&self,
|
&self,
|
||||||
forge_config: &ForgeConfig,
|
forge_config: &ForgeConfig,
|
||||||
forge_name: ForgeName,
|
forge_name: ForgeAlias,
|
||||||
server_storage: &ServerStorage,
|
server_storage: &ServerStorage,
|
||||||
webhook: &Webhook,
|
webhook: &Webhook,
|
||||||
) -> Vec<(ForgeName, RepoAlias, RepoActor)> {
|
) -> Vec<(ForgeAlias, RepoAlias, RepoActor)> {
|
||||||
let span =
|
let span =
|
||||||
tracing::info_span!("create_forge_repos", name = %forge_name, config = %forge_config);
|
tracing::info_span!("create_forge_repos", name = %forge_name, config = %forge_config);
|
||||||
|
|
||||||
|
@ -170,11 +172,11 @@ impl Server {
|
||||||
|
|
||||||
fn create_actor(
|
fn create_actor(
|
||||||
&self,
|
&self,
|
||||||
forge_name: ForgeName,
|
forge_name: ForgeAlias,
|
||||||
forge_config: ForgeConfig,
|
forge_config: ForgeConfig,
|
||||||
server_storage: &ServerStorage,
|
server_storage: &ServerStorage,
|
||||||
webhook: &Webhook,
|
webhook: &Webhook,
|
||||||
) -> impl Fn((RepoAlias, &ServerRepoConfig)) -> (ForgeName, RepoAlias, RepoActor) {
|
) -> impl Fn((RepoAlias, &ServerRepoConfig)) -> (ForgeAlias, RepoAlias, RepoActor) {
|
||||||
let server_storage = server_storage.clone();
|
let server_storage = server_storage.clone();
|
||||||
let webhook = webhook.clone();
|
let webhook = webhook.clone();
|
||||||
let net = self.net.clone();
|
let net = self.net.clone();
|
||||||
|
@ -219,7 +221,7 @@ impl Server {
|
||||||
|
|
||||||
fn start_actor(
|
fn start_actor(
|
||||||
&self,
|
&self,
|
||||||
actor: (ForgeName, RepoAlias, RepoActor),
|
actor: (ForgeAlias, RepoAlias, RepoActor),
|
||||||
) -> (RepoAlias, Addr<RepoActor>) {
|
) -> (RepoAlias, Addr<RepoActor>) {
|
||||||
let (forge_name, repo_alias, actor) = actor;
|
let (forge_name, repo_alias, actor) = actor;
|
||||||
let span = tracing::info_span!("start_actor", forge = %forge_name, repo = %repo_alias);
|
let span = tracing::info_span!("start_actor", forge = %forge_name, repo = %repo_alias);
|
||||||
|
|
|
@ -2,20 +2,21 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use git_next_config::RepoAlias;
|
use derive_more::Constructor;
|
||||||
|
use git_next_config::{ForgeAlias, RepoAlias};
|
||||||
use git_next_repo_actor::webhook::WebhookMessage;
|
use git_next_repo_actor::webhook::WebhookMessage;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
pub struct WebhookRouter {
|
pub struct WebhookRouter {
|
||||||
span: tracing::Span,
|
span: tracing::Span,
|
||||||
repos: HashMap<RepoAlias, Recipient<WebhookMessage>>,
|
recipients: HashMap<ForgeAlias, HashMap<RepoAlias, Recipient<WebhookMessage>>>,
|
||||||
}
|
}
|
||||||
impl WebhookRouter {
|
impl WebhookRouter {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let span = tracing::info_span!("WebhookRouter");
|
let span = tracing::info_span!("WebhookRouter");
|
||||||
Self {
|
Self {
|
||||||
span,
|
span,
|
||||||
repos: Default::default(),
|
recipients: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,24 +29,39 @@ impl Handler<WebhookMessage> for WebhookRouter {
|
||||||
|
|
||||||
fn handle(&mut self, msg: WebhookMessage, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: WebhookMessage, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
let _gaurd = self.span.enter();
|
let _gaurd = self.span.enter();
|
||||||
|
let forge_alias = msg.forge_alias();
|
||||||
let repo_alias = msg.repo_alias();
|
let repo_alias = msg.repo_alias();
|
||||||
debug!(repo = %repo_alias, "Router...");
|
debug!(forge = %forge_alias, repo = %repo_alias, "Router...");
|
||||||
if let Some(recipient) = self.repos.get(repo_alias) {
|
let Some(forge_repos) = self.recipients.get(forge_alias) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(recipient) = forge_repos.get(repo_alias) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
info!(repo = %repo_alias, "Sending to Recipient");
|
info!(repo = %repo_alias, "Sending to Recipient");
|
||||||
recipient.do_send(msg);
|
recipient.do_send(msg);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Message)]
|
#[derive(Message, Constructor)]
|
||||||
#[rtype(result = "()")]
|
#[rtype(result = "()")]
|
||||||
pub struct AddWebhookRecipient(pub RepoAlias, pub Recipient<WebhookMessage>);
|
pub struct AddWebhookRecipient {
|
||||||
|
pub forge_alias: ForgeAlias,
|
||||||
|
pub repo_alias: RepoAlias,
|
||||||
|
pub recipient: Recipient<WebhookMessage>,
|
||||||
|
}
|
||||||
impl Handler<AddWebhookRecipient> for WebhookRouter {
|
impl Handler<AddWebhookRecipient> for WebhookRouter {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: AddWebhookRecipient, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: AddWebhookRecipient, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
let _gaurd = self.span.enter();
|
let _gaurd = self.span.enter();
|
||||||
info!(repo = %msg.0, "Register Recipient");
|
info!(forge = %msg.forge_alias, repo = %msg.repo_alias, "Register Recipient");
|
||||||
self.repos.insert(msg.0, msg.1);
|
if !self.recipients.contains_key(&msg.forge_alias) {
|
||||||
|
self.recipients
|
||||||
|
.insert(msg.forge_alias.clone(), HashMap::new());
|
||||||
|
}
|
||||||
|
self.recipients
|
||||||
|
.get_mut(&msg.forge_alias)
|
||||||
|
.map(|repos| repos.insert(msg.repo_alias, msg.recipient));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::net::SocketAddr;
|
||||||
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
|
|
||||||
use git_next_config::RepoAlias;
|
use git_next_config::{ForgeAlias, RepoAlias};
|
||||||
use git_next_repo_actor::webhook::{self, WebhookAuth, WebhookMessage};
|
use git_next_repo_actor::webhook::{self, WebhookAuth, WebhookMessage};
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
use warp::reject::Rejection;
|
use warp::reject::Rejection;
|
||||||
|
@ -15,17 +15,19 @@ pub async fn start(socket_addr: SocketAddr, address: actix::prelude::Recipient<W
|
||||||
let route = warp::post()
|
let route = warp::post()
|
||||||
.map(move || address.clone())
|
.map(move || address.clone())
|
||||||
.and(warp::path::param())
|
.and(warp::path::param())
|
||||||
// .and(warp::query::raw())
|
.and(warp::path::param())
|
||||||
.and(warp::header::headers_cloned())
|
.and(warp::header::headers_cloned())
|
||||||
.and(warp::body::bytes())
|
.and(warp::body::bytes())
|
||||||
.and_then(
|
.and_then(
|
||||||
|recipient: Recipient<WebhookMessage>,
|
|recipient: Recipient<WebhookMessage>,
|
||||||
path: String,
|
forge_alias: String,
|
||||||
|
repo_alias: String,
|
||||||
// query: String,
|
// query: String,
|
||||||
headers: warp::http::HeaderMap,
|
headers: warp::http::HeaderMap,
|
||||||
body: bytes::Bytes| async move {
|
body: bytes::Bytes| async move {
|
||||||
info!("POST received");
|
info!("POST received");
|
||||||
let repo_alias = RepoAlias::new(path);
|
let forge_alias = ForgeAlias::new(forge_alias);
|
||||||
|
let repo_alias = RepoAlias::new(repo_alias);
|
||||||
let bytes = body.to_vec();
|
let bytes = body.to_vec();
|
||||||
let body = webhook::Body::new(String::from_utf8_lossy(&bytes).to_string());
|
let body = webhook::Body::new(String::from_utf8_lossy(&bytes).to_string());
|
||||||
headers.get("Authorization").map_or_else(
|
headers.get("Authorization").map_or_else(
|
||||||
|
@ -34,10 +36,22 @@ pub async fn start(socket_addr: SocketAddr, address: actix::prelude::Recipient<W
|
||||||
Err(warp::reject())
|
Err(warp::reject())
|
||||||
},
|
},
|
||||||
|authorisation_header| {
|
|authorisation_header| {
|
||||||
info!(?repo_alias, ?authorisation_header, "Received webhook",);
|
info!(
|
||||||
|
forge = %forge_alias,
|
||||||
|
repo = %repo_alias,
|
||||||
|
?authorisation_header,
|
||||||
|
"Received webhook",
|
||||||
|
);
|
||||||
|
// TODO: (#86) Authorization isn't presented consistently, allow each forge
|
||||||
|
// to parse the authorization from the request
|
||||||
match parse_auth(authorisation_header) {
|
match parse_auth(authorisation_header) {
|
||||||
Ok(authorisation) => {
|
Ok(authorisation) => {
|
||||||
let message = WebhookMessage::new(repo_alias, authorisation, body);
|
let message = WebhookMessage::new(
|
||||||
|
forge_alias,
|
||||||
|
repo_alias,
|
||||||
|
authorisation,
|
||||||
|
body,
|
||||||
|
);
|
||||||
recipient
|
recipient
|
||||||
.try_send(message)
|
.try_send(message)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
|
|
Loading…
Reference in a new issue