From 90f8ebd159564dfa6aa1f16845b36ebcf1b30ff2 Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Thu, 7 Aug 2025 14:55:50 +0800 Subject: [PATCH] feat(mono):intorduce bellatrix under orion-server for trigger build --- Cargo.toml | 1 + ceres/Cargo.toml | 1 + ceres/src/api_service/mono_api_service.rs | 37 ++- ceres/src/model/mr.rs | 7 + ceres/src/pack/import_repo.rs | 12 +- ceres/src/pack/mod.rs | 6 +- ceres/src/pack/monorepo.rs | 226 ++++++++++++++++-- ceres/src/protocol/mod.rs | 11 +- ceres/src/protocol/smart.rs | 31 ++- common/src/config.rs | 8 + config/config-workflow.toml | 11 +- config/config.toml | 11 +- gateway/src/https_server.rs | 8 +- gemini/src/p2p/client.rs | 2 +- jupiter/callisto/src/entity_ext/mega_mr.rs | 12 +- jupiter/src/storage/issue_storage.rs | 8 + jupiter/src/storage/mono_storage.rs | 5 +- jupiter/src/storage/mr_storage.rs | 2 + libra/src/command/diff.rs | 9 +- mercury/src/internal/object/tree.rs | 4 + mono/src/api/api_router.rs | 2 +- mono/src/api/conversation/conv_router.rs | 2 +- mono/src/api/issue/issue_router.rs | 2 +- mono/src/api/label/label_router.rs | 26 +- mono/src/api/mod.rs | 4 +- mono/src/api/mr/mr_router.rs | 26 +- mono/src/api/notes/note_router.rs | 2 +- mono/src/api/user/user_router.rs | 2 +- mono/src/cli.rs | 2 +- mono/src/commands/service/http.rs | 4 +- mono/src/commands/service/multi.rs | 4 +- mono/src/git_protocol/http.rs | 2 +- mono/src/git_protocol/ssh.rs | 2 +- .../{https_server.rs => http_server.rs} | 43 ++-- mono/src/server/mod.rs | 2 +- monobean/src/core/mega_core.rs | 3 +- moon/packages/types/generated.ts | 51 ++++ neptune/src/lib.rs | 2 +- neptune/src/neptune_engine.rs | 84 ++++--- orion-server/bellatrix/Cargo.toml | 15 ++ orion-server/bellatrix/src/lib.rs | 31 +++ .../bellatrix/src/orion_client/mod.rs | 37 +++ 42 files changed, 582 insertions(+), 178 deletions(-) rename mono/src/server/{https_server.rs => http_server.rs} (89%) create mode 100644 orion-server/bellatrix/Cargo.toml create mode 100644 orion-server/bellatrix/src/lib.rs create mode 100644 orion-server/bellatrix/src/orion_client/mod.rs diff --git a/Cargo.toml b/Cargo.toml index dc508943c..4cd3ced51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ saturn = { path = "saturn" } mono = { path = "mono" } observatory = { path = "extensions/observatory" } orion = { path = "orion" } +bellatrix = { path = "orion-server/bellatrix" } context = { path = "context" } neptune = { path = "neptune" } diff --git a/ceres/Cargo.toml b/ceres/Cargo.toml index 448aabb47..8fc2490cc 100644 --- a/ceres/Cargo.toml +++ b/ceres/Cargo.toml @@ -16,6 +16,7 @@ jupiter = { workspace = true } callisto = { workspace = true } mercury = { workspace = true } neptune = { workspace = true } +bellatrix = { workspace = true } anyhow = { workspace = true } tokio = { workspace = true, features = ["net", "process"] } diff --git a/ceres/src/api_service/mono_api_service.rs b/ceres/src/api_service/mono_api_service.rs index 6884da2b2..73db05e98 100644 --- a/ceres/src/api_service/mono_api_service.rs +++ b/ceres/src/api_service/mono_api_service.rs @@ -44,7 +44,6 @@ use async_trait::async_trait; use callisto::sea_orm_active_enums::ConvTypeEnum; use callisto::{mega_blob, mega_mr, mega_tree, raw_blob}; use common::errors::MegaError; -use neptune::neptune_engine::Diff; use jupiter::storage::base_storage::StorageConnector; use jupiter::storage::Storage; use jupiter::utils::converter::generate_git_keep_with_timestamp; @@ -53,6 +52,7 @@ use mercury::hash::SHA1; use mercury::internal::object::blob::Blob; use mercury::internal::object::commit::Commit; use mercury::internal::object::tree::{Tree, TreeItem, TreeItemMode}; +use neptune::neptune_engine::Diff; use crate::api_service::ApiHandler; use crate::model::git::CreateFileInfo; @@ -378,22 +378,21 @@ impl MonoApiService { /// /// # Returns /// String containing the unified diff output - pub async fn content_diff( - &self, - mr_link: &str, - ) -> Result { + pub async fn content_diff(&self, mr_link: &str) -> Result { let stg = self.storage.mr_storage(); - let mr = stg.get_mr(mr_link).await.unwrap().ok_or_else(|| { - GitError::CustomError(format!("Merge request not found: {mr_link}")) - })?; - - let old_blobs = self.get_commit_blobs(&mr.from_hash).await.map_err(|e| { - GitError::CustomError(format!("Failed to get old commit blobs: {e}")) - })?; - let new_blobs = self.get_commit_blobs(&mr.to_hash).await.map_err(|e| { - GitError::CustomError(format!("Failed to get new commit blobs: {e}")) - })?; + let mr = + stg.get_mr(mr_link).await.unwrap().ok_or_else(|| { + GitError::CustomError(format!("Merge request not found: {mr_link}")) + })?; + let old_blobs = self + .get_commit_blobs(&mr.from_hash) + .await + .map_err(|e| GitError::CustomError(format!("Failed to get old commit blobs: {e}")))?; + let new_blobs = self + .get_commit_blobs(&mr.to_hash) + .await + .map_err(|e| GitError::CustomError(format!("Failed to get new commit blobs: {e}")))?; let algo = "histogram".to_string(); // Default diff algorithm let filter_paths: Vec = Vec::new(); // No filtering by default @@ -428,13 +427,7 @@ impl MonoApiService { }; // Use the unified diff function that returns a single string - let diff_output = Diff::diff( - old_blobs, - new_blobs, - algo, - filter_paths, - read_content, - ).await; + let diff_output = Diff::diff(old_blobs, new_blobs, algo, filter_paths, read_content).await; Ok(diff_output) } diff --git a/ceres/src/model/mr.rs b/ceres/src/model/mr.rs index 3b1f9c472..988499a88 100644 --- a/ceres/src/model/mr.rs +++ b/ceres/src/model/mr.rs @@ -10,3 +10,10 @@ pub enum MrDiffFile { // path, old_hash, new_hash Modified(PathBuf, SHA1, SHA1), } + +#[derive(Serialize)] +pub struct BuckFile { + pub buck: SHA1, + pub buck_config: SHA1, + pub path: PathBuf, +} diff --git a/ceres/src/pack/import_repo.rs b/ceres/src/pack/import_repo.rs index d66e8d7c1..351f4e5db 100644 --- a/ceres/src/pack/import_repo.rs +++ b/ceres/src/pack/import_repo.rs @@ -24,7 +24,7 @@ use mercury::{hash::SHA1, internal::pack::encode::PackEncoder}; use crate::{ api_service::{mono_api_service::MonoApiService, ApiHandler}, - pack::PackHandler, + pack::RepoHandler, protocol::{ import_refs::{CommandType, RefCommand, Refs}, repo::Repo, @@ -38,7 +38,7 @@ pub struct ImportRepo { } #[async_trait] -impl PackHandler for ImportRepo { +impl RepoHandler for ImportRepo { async fn head_hash(&self) -> (String, Vec) { let result = self .storage @@ -292,6 +292,14 @@ impl PackHandler for ImportRepo { Ok(()) } + async fn save_or_update_mr(&self) -> Result<(), MegaError> { + Ok(()) + } + + async fn post_mr_operation(&self) -> Result<(), MegaError> { + Ok(()) + } + async fn check_commit_exist(&self, hash: &str) -> bool { self.storage .git_db_storage() diff --git a/ceres/src/pack/mod.rs b/ceres/src/pack/mod.rs index 33deca8db..7c9a3f1a6 100644 --- a/ceres/src/pack/mod.rs +++ b/ceres/src/pack/mod.rs @@ -37,7 +37,7 @@ pub mod import_repo; pub mod monorepo; #[async_trait] -pub trait PackHandler: Send + Sync + 'static { +pub trait RepoHandler: Send + Sync + 'static { async fn head_hash(&self) -> (String, Vec); async fn receiver_handler( @@ -116,6 +116,10 @@ pub trait PackHandler: Send + Sync + 'static { async fn update_refs(&self, refs: &RefCommand) -> Result<(), GitError>; + async fn save_or_update_mr(&self) -> Result<(), MegaError>; + + async fn post_mr_operation(&self) -> Result<(), MegaError>; + async fn check_commit_exist(&self, hash: &str) -> bool; async fn check_default_branch(&self) -> bool; diff --git a/ceres/src/pack/monorepo.rs b/ceres/src/pack/monorepo.rs index 452689f07..bc79664ff 100644 --- a/ceres/src/pack/monorepo.rs +++ b/ceres/src/pack/monorepo.rs @@ -1,6 +1,6 @@ use std::{ collections::{HashMap, HashSet}, - path::{Component, PathBuf}, + path::{Component, Path, PathBuf}, str::FromStr, sync::{ atomic::{AtomicUsize, Ordering}, @@ -16,7 +16,8 @@ use tokio::sync::{ }; use tokio_stream::wrappers::ReceiverStream; -use callisto::{mega_mr, raw_blob, sea_orm_active_enums::ConvTypeEnum}; +use bellatrix::{orion_client::OrionBuildRequest, Bellatrix}; +use callisto::{entity_ext::generate_link, mega_mr, raw_blob, sea_orm_active_enums::ConvTypeEnum}; use common::{ errors::MegaError, utils::{self, MEGA_BRANCH_NAME}, @@ -33,7 +34,9 @@ use mercury::{ }; use crate::{ - pack::PackHandler, + api_service::{mono_api_service::MonoApiService, ApiHandler}, + model::mr::BuckFile, + pack::RepoHandler, protocol::import_refs::{RefCommand, Refs}, }; @@ -42,11 +45,15 @@ pub struct MonoRepo { pub path: PathBuf, pub from_hash: String, pub to_hash: String, + // current_commit only exists when an unpack operation occurs. + // When only a branch is updated and the pack file is empty, this value will be null. pub current_commit: Arc>>, + pub mr_link: Arc>>, + pub bellatrix: Arc, } #[async_trait] -impl PackHandler for MonoRepo { +impl RepoHandler for MonoRepo { async fn head_hash(&self) -> (String, Vec) { let storage = self.storage.mono_storage(); @@ -278,10 +285,10 @@ impl PackHandler for MonoRepo { async fn update_refs(&self, refs: &RefCommand) -> Result<(), GitError> { let storage = self.storage.mono_storage(); let current_commit = self.current_commit.read().await; + let mr_link = self.fetch_or_new_mr_link().await.unwrap(); + let ref_name = utils::mr_ref_name(&mr_link); if let Some(c) = &*current_commit { - let mr_link = self.handle_mr(&c.format_message()).await.unwrap(); - let ref_name = utils::mr_ref_name(&mr_link); - if let Some(mut mr_ref) = storage.get_mr_ref(&ref_name).await.unwrap() { + if let Some(mut mr_ref) = storage.get_ref_by_name(&ref_name).await.unwrap() { mr_ref.ref_commit_hash = refs.new_id.clone(); mr_ref.ref_tree_hash = c.tree_id.to_string(); storage.update_ref(mr_ref).await.unwrap(); @@ -301,6 +308,57 @@ impl PackHandler for MonoRepo { Ok(()) } + async fn save_or_update_mr(&self) -> Result<(), MegaError> { + let storage = self.storage.mr_storage(); + let path_str = self.path.to_str().unwrap(); + + match storage.get_open_mr_by_path(path_str).await? { + Some(mr) => { + self.update_existing_mr(mr).await?; + } + None => { + let link_guard = self.mr_link.read().await; + let mr_link = link_guard.as_ref().unwrap(); + let commit_guard = self.current_commit.read().await; + let title = commit_guard.as_ref().unwrap().format_message(); + storage + .new_mr(path_str, mr_link, &title, &self.from_hash, &self.to_hash) + .await?; + } + }; + Ok(()) + } + + async fn post_mr_operation(&self) -> Result<(), MegaError> { + if self.bellatrix.enable_build() { + let link_guard = self.mr_link.read().await; + let mr = link_guard.as_ref().unwrap(); + + let buck_files = self.search_buck_under_mr(&self.path).await?; + if buck_files.is_empty() { + tracing::error!( + "Search BUCK file under {:?} failed, please manually check BUCK file exists!!", + self.path + ); + } else { + for buck_file in buck_files { + let req = OrionBuildRequest { + repo: buck_file.path.to_str().unwrap().to_string(), + buck_hash: buck_file.buck.to_string(), + buckconfig_hash: buck_file.buck_config.to_string(), + mr: mr.to_string(), + args: Some(vec![]), + }; + let bellatrix = self.bellatrix.clone(); + tokio::spawn(async move { + let _ = bellatrix.on_post_receive(req).await; + }); + } + } + } + Ok(()) + } + async fn check_commit_exist(&self, hash: &str) -> bool { self.storage .mono_storage() @@ -316,32 +374,26 @@ impl PackHandler for MonoRepo { } impl MonoRepo { - async fn handle_mr(&self, title: &str) -> Result { + async fn fetch_or_new_mr_link(&self) -> Result { let storage = self.storage.mr_storage(); let path_str = self.path.to_str().unwrap(); - - match storage.get_open_mr_by_path(path_str).await.unwrap() { - Some(mr) => { - let link = mr.link.clone(); - self.handle_existing_mr(mr).await?; - Ok(link) - } + let mr_link = match storage.get_open_mr_by_path(path_str).await.unwrap() { + Some(mr) => mr.link.clone(), None => { if self.from_hash == "0".repeat(40) { return Err(MegaError::with_message( "Can not init directory under monorepo directory!", )); } - let link = storage - .new_mr(path_str, title, &self.from_hash, &self.to_hash) - .await - .unwrap(); - Ok(link) + generate_link() } - } + }; + let mut lock = self.mr_link.write().await; + *lock = Some(mr_link.clone()); + Ok(mr_link) } - async fn handle_existing_mr(&self, mr: mega_mr::Model) -> Result<(), MegaError> { + async fn update_existing_mr(&self, mr: mega_mr::Model) -> Result<(), MegaError> { let mr_stg = self.storage.mr_storage(); let comment_stg = self.storage.conversation_storage(); if mr.from_hash == self.from_hash { @@ -376,4 +428,134 @@ impl MonoRepo { } Ok(()) } + + async fn search_buck_under_mr(&self, mr_path: &Path) -> Result, MegaError> { + let mut res = vec![]; + let mono_stg = self.storage.mono_storage(); + let mono_api_service = MonoApiService { + storage: self.storage.clone(), + }; + + let mut search_trees: Vec<(PathBuf, Tree)> = vec![]; + + let diff_trees = self.diff_trees_from_mr().await?; + for (path, new, old) in diff_trees { + match (new, old) { + (None, _) => { + continue; + } + (Some(sha1), _) => { + let tree = mono_stg.get_tree_by_hash(&sha1.to_string()).await?.unwrap(); + search_trees.push((path, tree.into())); + } + } + } + + for (path, tree) in search_trees { + if let Some(buck) = self.try_extract_buck(tree, &mr_path.join(path)) { + res.push(buck); + } + } + + // no buck file found + if res.is_empty() { + let mut path = Some(mr_path); + while let Some(p) = path { + if p.parent().is_some() { + if let Some(tree) = mono_api_service.search_tree_by_path(p).await.ok().flatten() + { + if let Some(buck) = self.try_extract_buck(tree, mr_path) { + return Ok(vec![buck]); + } + }; + } + path = p.parent(); + } + } + Ok(res) + } + + fn try_extract_buck(&self, tree: Tree, mr_path: &Path) -> Option { + let mut buck = None; + let mut buck_config = None; + for item in tree.tree_items { + if item.is_blob() && item.name == "BUCK" { + buck = Some(item.id) + } + if item.is_blob() && item.name == ".buckconfig" { + buck_config = Some(item.id) + } + } + match (buck, buck_config) { + (Some(buck), Some(buck_config)) => Some(BuckFile { + buck, + buck_config, + path: mr_path.to_path_buf(), + }), + _ => None, + } + } + + async fn diff_trees_from_mr( + &self, + ) -> Result, Option)>, MegaError> { + let mono_stg = self.storage.mono_storage(); + let from_c = mono_stg.get_commit_by_hash(&self.from_hash).await?.unwrap(); + let from_tree: Tree = mono_stg + .get_tree_by_hash(&from_c.tree) + .await? + .unwrap() + .into(); + let to_c = mono_stg.get_commit_by_hash(&self.to_hash).await?.unwrap(); + let to_tree: Tree = mono_stg.get_tree_by_hash(&to_c.tree).await?.unwrap().into(); + diff_trees(&to_tree, &from_tree) + } +} + +type DiffResult = Vec<(PathBuf, Option, Option)>; + +fn diff_trees(theirs: &Tree, base: &Tree) -> Result { + let their_items: HashMap<_, _> = get_plain_items(theirs).into_iter().collect(); + let base_items: HashMap<_, _> = get_plain_items(base).into_iter().collect(); + let all_paths: HashSet<_> = their_items.keys().chain(base_items.keys()).collect(); + + let mut diffs = Vec::new(); + + for path in all_paths { + let their_hash = their_items.get(path).cloned(); + let base_hash = base_items.get(path).cloned(); + if their_hash != base_hash { + diffs.push((path.clone(), their_hash, base_hash)); + } + } + Ok(diffs) +} + +fn get_plain_items(tree: &Tree) -> Vec<(PathBuf, SHA1)> { + let mut items = Vec::new(); + for item in tree.tree_items.iter() { + if item.is_tree() { + items.push((PathBuf::from(item.name.clone()), item.id)); + } + } + items +} + +#[cfg(test)] +mod test { + use std::path::{Component, Path}; + + #[test] + fn get_component_reverse() { + let reversed: Vec<_> = Path::new("/a/b/c/d.txt") + .components() + .filter_map(|c| match c { + Component::Normal(name) => Some(name.to_string_lossy().into_owned()), + _ => None, + }) + .rev() + .collect(); + + assert_eq!(vec!["d.txt", "c", "b", "a"], reversed); // ["d.txt", "c", "b", "a"] + } } diff --git a/ceres/src/protocol/mod.rs b/ceres/src/protocol/mod.rs index 9421e5ffb..7cd30952e 100644 --- a/ceres/src/protocol/mod.rs +++ b/ceres/src/protocol/mod.rs @@ -1,6 +1,7 @@ use core::fmt; use std::{path::PathBuf, str::FromStr, sync::Arc}; +use bellatrix::Bellatrix; use callisto::sea_orm_active_enums::RefTypeEnum; use common::{ errors::{MegaError, ProtocolError}, @@ -11,7 +12,7 @@ use jupiter::storage::Storage; use repo::Repo; use tokio::sync::RwLock; -use crate::pack::{import_repo::ImportRepo, monorepo::MonoRepo, PackHandler}; +use crate::pack::{import_repo::ImportRepo, monorepo::MonoRepo, RepoHandler}; pub mod import_refs; pub mod repo; @@ -137,18 +138,18 @@ impl SmartProtocol { } pub fn mock() -> Self { - let context = Storage::mock(); + let storage = Storage::mock(); SmartProtocol { transport_protocol: TransportProtocol::default(), capabilities: Vec::new(), path: PathBuf::new(), command_list: Vec::new(), service_type: None, - storage: context, + storage, } } - pub async fn pack_handler(&self) -> Result, ProtocolError> { + pub async fn repo_handler(&self) -> Result, ProtocolError> { let import_dir = self.storage.config().monorepo.import_dir.clone(); if self.path.starts_with(import_dir.clone()) { let storage = self.storage.git_db_storage(); @@ -180,6 +181,8 @@ impl SmartProtocol { from_hash: String::new(), to_hash: String::new(), current_commit: Arc::new(RwLock::new(None)), + mr_link: Arc::new(RwLock::new(None)), + bellatrix: Arc::new(Bellatrix::new(self.storage.config().build.clone())), }; if let Some(command) = self .command_list diff --git a/ceres/src/protocol/smart.rs b/ceres/src/protocol/smart.rs index 194eb273f..2e3e2a332 100644 --- a/ceres/src/protocol/smart.rs +++ b/ceres/src/protocol/smart.rs @@ -58,12 +58,12 @@ impl SmartProtocol { /// /// Finally, the constructed packet line stream is returned. pub async fn git_info_refs(&self) -> Result { - let pack_handler = self.pack_handler().await?; + let repo_handler = self.repo_handler().await?; let service_type = self.service_type.unwrap(); // The stream MUST include capability declarations behind a NUL on the first ref. - let (head_hash, git_refs) = pack_handler.head_hash().await; + let (head_hash, git_refs) = repo_handler.head_hash().await; let name = if head_hash == ZERO_ID { "capabilities^{}" } else { @@ -89,7 +89,7 @@ impl SmartProtocol { &mut self, upload_request: &mut Bytes, ) -> Result<(ReceiverStream>, BytesMut), ProtocolError> { - let pack_handler = self.pack_handler().await?; + let repo_handler = self.repo_handler().await?; let mut want: Vec = Vec::new(); let mut have: Vec = Vec::new(); @@ -142,7 +142,7 @@ impl SmartProtocol { let mut protocol_buf = BytesMut::new(); if have.is_empty() { - pack_data = pack_handler.full_pack(want.clone()).await.unwrap(); + pack_data = repo_handler.full_pack(want.clone()).await.unwrap(); add_pkt_line_string(&mut protocol_buf, String::from("NAK\n")); } else { if self.capabilities.contains(&Capability::MultiAckDetailed) { @@ -151,14 +151,14 @@ impl SmartProtocol { // and signals the identified common commits with ACK obj-id common lines for hash in &have { - if pack_handler.check_commit_exist(hash).await { + if repo_handler.check_commit_exist(hash).await { add_pkt_line_string(&mut protocol_buf, format!("ACK {hash} common\n")); if last_common_commit.is_empty() { last_common_commit = hash.to_string(); } } } - pack_data = pack_handler + pack_data = repo_handler .incremental_pack(want.clone(), have) .await .unwrap(); @@ -188,7 +188,7 @@ impl SmartProtocol { Ok((pack_data, protocol_buf)) } - pub fn git_receive_pack_protocol(&mut self, mut protocol_bytes: Bytes) { + pub fn parse_receive_pack_commands(&mut self, mut protocol_bytes: Bytes) { while !protocol_bytes.is_empty() { let (bytes_take, mut pkt_line) = read_pkt_line(&mut protocol_bytes); if bytes_take != 0 { @@ -210,24 +210,24 @@ impl SmartProtocol { ) -> Result { // After receiving the pack data from the sender, the receiver sends a report let mut report_status = BytesMut::new(); - let pack_handler = self.pack_handler().await?; + let repo_handler = self.repo_handler().await?; //1. unpack progress - let receiver = pack_handler + let receiver = repo_handler .unpack_stream(&self.storage.config().pack, data_stream) .await?; - let unpack_result = pack_handler.clone().receiver_handler(receiver).await; + let unpack_result = repo_handler.clone().receiver_handler(receiver).await; // write "unpack ok\n to report" add_pkt_line_string(&mut report_status, "unpack ok\n".to_owned()); - let mut default_exist = pack_handler.check_default_branch().await; + let mut default_exist = repo_handler.check_default_branch().await; //2. update each refs and build report for command in &mut self.command_list { if command.ref_type == RefTypeEnum::Tag { // just update if refs type is tag - pack_handler.update_refs(command).await.unwrap(); + repo_handler.update_refs(command).await.unwrap(); } else { // Updates can be unsuccessful for a number of reasons. // a.The reference can have changed since the reference discovery phase was originally sent, meaning someone pushed in the meantime. @@ -239,7 +239,7 @@ impl SmartProtocol { command.default_branch = true; default_exist = true; } - if let Err(e) = pack_handler.update_refs(command).await { + if let Err(e) = repo_handler.update_refs(command).await { command.failed(e.to_string()); } } @@ -250,6 +250,11 @@ impl SmartProtocol { } add_pkt_line_string(&mut report_status, command.get_status()); } + //3. update MR + repo_handler.save_or_update_mr().await.unwrap(); + //4. post operation + repo_handler.post_mr_operation().await.unwrap(); + report_status.put(&PKT_LINE_END_MARKER[..]); let length = report_status.len(); let mut buf = self.build_side_band_format(report_status, length); diff --git a/common/src/config.rs b/common/src/config.rs index c985f1e98..904eca651 100644 --- a/common/src/config.rs +++ b/common/src/config.rs @@ -93,6 +93,7 @@ pub struct Config { // Not used in mega app #[serde(default)] pub oauth: Option, + pub build: BuildConfig, } impl Config { @@ -120,6 +121,7 @@ impl Config { authentication: AuthConfig::default(), lfs: LFSConfig::default(), oauth: None, + build: BuildConfig::default(), } } @@ -598,6 +600,12 @@ impl Default for OauthConfig { } } +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct BuildConfig { + pub enable_build: bool, + pub orion_server: String, +} + #[cfg(test)] mod test { use super::*; diff --git a/config/config-workflow.toml b/config/config-workflow.toml index 76ba418ea..7120aa080 100644 --- a/config/config-workflow.toml +++ b/config/config-workflow.toml @@ -63,6 +63,15 @@ admin = "admin" # Set serveral root dirs in directory init root_dirs = ["third-party", "project", "doc", "release"] +[build] + +# enable build system trigger +enable_build = true + +# build system url +orion_server = "https://orion.gitmega.com" + + [pack] # The maximum memory used by decode # Support the following units/notations: K, M, G, T, KB, MB, GB, TB, KiB, MiB, GiB, TiB, `%` and decimal percentages @@ -119,4 +128,4 @@ cookie_domain = "localhost" campsite_api_domain = "http://api.gitmega.com" # allowed cors origins -allowed_cors_origins = ["http://local.gitmega.com", "http://app.gitmega.com"] \ No newline at end of file +allowed_cors_origins = ["http://local.gitmega.com", "https://app.gitmega.com", "http://app.gitmono.test"] \ No newline at end of file diff --git a/config/config.toml b/config/config.toml index 2b3b34d09..38d592770 100644 --- a/config/config.toml +++ b/config/config.toml @@ -63,6 +63,15 @@ admin = "admin" # Set serveral root dirs in directory init root_dirs = ["third-party", "project", "doc", "release", "model"] +[build] + +# enable build system trigger +enable_build = true + +# build system url +orion_server = "https://orion.gitmega.com" + + [pack] # The maximum memory used by decode # Support the following units/notations: K, M, G, T, KB, MB, GB, TB, KiB, MiB, GiB, TiB, `%` and decimal percentages @@ -119,4 +128,4 @@ cookie_domain = "localhost" campsite_api_domain = "http://api.gitmono.test:3001" # allowed cors origins -allowed_cors_origins = ["http://local.gitmega.com", "http://app.gitmega.com", "http://app.gitmono.test"] \ No newline at end of file +allowed_cors_origins = ["http://local.gitmega.com", "https://app.gitmega.com", "http://app.gitmono.test"] \ No newline at end of file diff --git a/gateway/src/https_server.rs b/gateway/src/https_server.rs index bcc098618..81964ddd7 100644 --- a/gateway/src/https_server.rs +++ b/gateway/src/https_server.rs @@ -13,7 +13,7 @@ use common::model::{CommonHttpOptions, P2pOptions}; use jupiter::storage::Storage; use mono::api::lfs::lfs_router; use mono::api::MonoApiServiceState; -use mono::server::https_server::{get_method_router, post_method_router, AppState}; +use mono::server::http_server::{get_method_router, post_method_router, ProtocolApiState}; use utoipa::OpenApi; use utoipa_axum::router::OpenApiRouter; @@ -51,16 +51,14 @@ pub async fn http_server(context: AppContext, options: HttpOptions) { } pub async fn app(storage: Storage, host: String, port: u16, p2p: P2pOptions) -> Router { - let state = AppState { - host: host.clone(), - port, + let state = ProtocolApiState { storage: storage.clone(), }; let mono_api_state = MonoApiServiceState { storage: storage.clone(), oauth_client: None, - store: None, + session_store: None, listen_addr: format!("http://{host}:{port}"), }; diff --git a/gemini/src/p2p/client.rs b/gemini/src/p2p/client.rs index 7d85ad1ff..c531b2c65 100644 --- a/gemini/src/p2p/client.rs +++ b/gemini/src/p2p/client.rs @@ -13,7 +13,7 @@ use ceres::lfs::handler; use ceres::lfs::handler::lfs_download_object; use ceres::lfs::lfs_structs::RequestObject; use ceres::pack::import_repo::ImportRepo; -use ceres::pack::PackHandler; +use ceres::pack::RepoHandler; use ceres::protocol::repo::Repo; use common::utils::generate_id; use dashmap::DashMap; diff --git a/jupiter/callisto/src/entity_ext/mega_mr.rs b/jupiter/callisto/src/entity_ext/mega_mr.rs index 85fd1b866..bf93f7123 100644 --- a/jupiter/callisto/src/entity_ext/mega_mr.rs +++ b/jupiter/callisto/src/entity_ext/mega_mr.rs @@ -1,7 +1,7 @@ use sea_orm::entity::prelude::*; use crate::{ - entity_ext::{generate_id, generate_link}, + entity_ext::generate_id, mega_mr::{self, Entity}, sea_orm_active_enums::MergeStatusEnum, }; @@ -52,11 +52,17 @@ impl Related for Entity { } impl mega_mr::Model { - pub fn new(path: String, title: String, from_hash: String, to_hash: String) -> Self { + pub fn new( + path: String, + title: String, + link: String, + from_hash: String, + to_hash: String, + ) -> Self { let now = chrono::Utc::now().naive_utc(); Self { id: generate_id(), - link: generate_link(), + link, title: title.to_owned(), status: MergeStatusEnum::Open, created_at: now, diff --git a/jupiter/src/storage/issue_storage.rs b/jupiter/src/storage/issue_storage.rs index 7023c18c5..964433c68 100644 --- a/jupiter/src/storage/issue_storage.rs +++ b/jupiter/src/storage/issue_storage.rs @@ -216,6 +216,14 @@ impl IssueStorage { Ok(res) } + pub async fn get_label_by_id(&self, id: i64) -> Result, MegaError> { + let model = label::Entity::find_by_id(id) + .one(self.get_connection()) + .await + .unwrap(); + Ok(model) + } + pub async fn list_labels_by_page( &self, page: Pagination, diff --git a/jupiter/src/storage/mono_storage.rs b/jupiter/src/storage/mono_storage.rs index 80c261126..dbae94072 100644 --- a/jupiter/src/storage/mono_storage.rs +++ b/jupiter/src/storage/mono_storage.rs @@ -113,7 +113,10 @@ impl MonoStorage { Ok(result) } - pub async fn get_mr_ref(&self, ref_name: &str) -> Result, MegaError> { + pub async fn get_ref_by_name( + &self, + ref_name: &str, + ) -> Result, MegaError> { let res = mega_refs::Entity::find() .filter(mega_refs::Column::RefName.eq(ref_name)) .one(self.get_connection()) diff --git a/jupiter/src/storage/mr_storage.rs b/jupiter/src/storage/mr_storage.rs index 8ee114e36..b445600f7 100644 --- a/jupiter/src/storage/mr_storage.rs +++ b/jupiter/src/storage/mr_storage.rs @@ -174,6 +174,7 @@ impl MrStorage { pub async fn new_mr( &self, path: &str, + link: &str, title: &str, from_hash: &str, to_hash: &str, @@ -181,6 +182,7 @@ impl MrStorage { let model = mega_mr::Model::new( path.to_owned(), title.to_owned(), + link.to_owned(), from_hash.to_owned(), to_hash.to_owned(), ); diff --git a/libra/src/command/diff.rs b/libra/src/command/diff.rs index da1a1091a..164af7df7 100644 --- a/libra/src/command/diff.rs +++ b/libra/src/command/diff.rs @@ -1,7 +1,7 @@ use std::{ fmt, io::{self, Write}, - path::{PathBuf}, + path::PathBuf, }; use clap::Parser; @@ -18,15 +18,16 @@ use similar; use crate::{ command::{ - get_target_commit, load_object, status::{self, changes_to_be_committed} + get_target_commit, load_object, + status::{self, changes_to_be_committed}, }, internal::head::Head, utils::{object_ext::TreeExt, path, util}, }; +use crate::utils::util::to_workdir_path; #[cfg(unix)] use std::process::{Command, Stdio}; -use crate::utils::util::{to_workdir_path}; #[derive(Parser, Debug)] pub struct DiffArgs { @@ -334,4 +335,4 @@ mod test { assert_eq!(blob.len(), 1); assert_eq!(blob[0].0, PathBuf::from("not_ignore")); } -} \ No newline at end of file +} diff --git a/mercury/src/internal/object/tree.rs b/mercury/src/internal/object/tree.rs index b65200c4a..71b3aa9f8 100644 --- a/mercury/src/internal/object/tree.rs +++ b/mercury/src/internal/object/tree.rs @@ -217,6 +217,10 @@ impl TreeItem { pub fn is_tree(&self) -> bool { self.mode == TreeItemMode::Tree } + + pub fn is_blob(&self) -> bool { + self.mode == TreeItemMode::Blob + } } /// A tree object is a Git object that represents a directory. It contains a list of entries, one diff --git a/mono/src/api/api_router.rs b/mono/src/api/api_router.rs index 231f1aa2c..04131b3b7 100644 --- a/mono/src/api/api_router.rs +++ b/mono/src/api/api_router.rs @@ -23,7 +23,7 @@ use crate::api::{ conversation::conv_router, issue::issue_router, label::label_router, mr::mr_router, notes::note_router, user::user_router, MonoApiServiceState, }; -use crate::{api::error::ApiError, server::https_server::GIT_TAG}; +use crate::{api::error::ApiError, server::http_server::GIT_TAG}; pub fn routers() -> OpenApiRouter { OpenApiRouter::new() diff --git a/mono/src/api/conversation/conv_router.rs b/mono/src/api/conversation/conv_router.rs index 887bb34e2..b7026d716 100644 --- a/mono/src/api/conversation/conv_router.rs +++ b/mono/src/api/conversation/conv_router.rs @@ -9,7 +9,7 @@ use common::model::CommonResult; use crate::api::conversation::{ContentPayload, ReactionRequest}; use crate::api::oauth::model::LoginUser; use crate::api::MonoApiServiceState; -use crate::{api::error::ApiError, server::https_server::CONV_TAG}; +use crate::{api::error::ApiError, server::http_server::CONV_TAG}; pub fn routers() -> OpenApiRouter { OpenApiRouter::new().nest( diff --git a/mono/src/api/issue/issue_router.rs b/mono/src/api/issue/issue_router.rs index 693b997bb..59e5bde0e 100644 --- a/mono/src/api/issue/issue_router.rs +++ b/mono/src/api/issue/issue_router.rs @@ -22,7 +22,7 @@ use crate::api::{ issue::{IssueDetailRes, ItemRes, NewIssue}, oauth::model::LoginUser, }; -use crate::{api::error::ApiError, server::https_server::ISSUE_TAG}; +use crate::{api::error::ApiError, server::http_server::ISSUE_TAG}; pub fn routers() -> OpenApiRouter { OpenApiRouter::new().nest( diff --git a/mono/src/api/label/label_router.rs b/mono/src/api/label/label_router.rs index e2e4735c9..b7e57340c 100644 --- a/mono/src/api/label/label_router.rs +++ b/mono/src/api/label/label_router.rs @@ -1,3 +1,4 @@ +use axum::extract::Path; use axum::{extract::State, Json}; use utoipa_axum::{router::OpenApiRouter, routes}; @@ -6,14 +7,15 @@ use common::model::{CommonPage, CommonResult, PageParams}; use crate::api::label::{LabelItem, NewLabel}; use crate::api::oauth::model::LoginUser; use crate::api::MonoApiServiceState; -use crate::{api::error::ApiError, server::https_server::LABEL_TAG}; +use crate::{api::error::ApiError, server::http_server::LABEL_TAG}; pub fn routers() -> OpenApiRouter { OpenApiRouter::new().nest( "/label", OpenApiRouter::new() .routes(routes!(new_label)) - .routes(routes!(fetch_label_list)), + .routes(routes!(fetch_label_list)) + .routes(routes!(fetch_label)), ) } @@ -62,3 +64,23 @@ async fn new_label( .await?; Ok(Json(CommonResult::success(Some(res.into())))) } + +/// Fetch label details +#[utoipa::path( + get, + params( + ("id", description = "Label's id"), + ), + path = "/{id}", + responses( + (status = 200, body = CommonResult>, content_type = "application/json") + ), + tag = LABEL_TAG +)] +async fn fetch_label( + state: State, + Path(id): Path, +) -> Result>, ApiError> { + let label = state.issue_stg().get_label_by_id(id).await?; + Ok(Json(CommonResult::success(label.map(|m| m.into())))) +} diff --git a/mono/src/api/mod.rs b/mono/src/api/mod.rs index e048c5094..49b9cce4d 100644 --- a/mono/src/api/mod.rs +++ b/mono/src/api/mod.rs @@ -58,7 +58,7 @@ pub type GithubClient< pub struct MonoApiServiceState { pub storage: Storage, pub oauth_client: Option, - pub store: Option, + pub session_store: Option, pub listen_addr: String, } @@ -70,7 +70,7 @@ impl FromRef for MemoryStore { impl FromRef for CampsiteApiStore { fn from_ref(state: &MonoApiServiceState) -> Self { - state.store.clone().unwrap() + state.session_store.clone().unwrap() } } diff --git a/mono/src/api/mr/mr_router.rs b/mono/src/api/mr/mr_router.rs index 57ccd1034..1eec2c04b 100644 --- a/mono/src/api/mr/mr_router.rs +++ b/mono/src/api/mr/mr_router.rs @@ -1,7 +1,8 @@ use std::{collections::HashMap, path::PathBuf}; use axum::{ - extract::{Path, State}, Json, + extract::{Path, State}, + Json, }; use jupiter::service::mr_service::MRService; use utoipa_axum::{router::OpenApiRouter, routes}; @@ -24,7 +25,7 @@ use crate::api::{ mr::{FilesChangedList, MRDetailRes, MrFilesRes, MuiTreeNode}, oauth::model::LoginUser, }; -use crate::{api::error::ApiError, server::https_server::MR_TAG}; +use crate::{api::error::ApiError, server::http_server::MR_TAG}; pub fn routers() -> OpenApiRouter { OpenApiRouter::new().nest( @@ -272,7 +273,6 @@ async fn mr_files_changed( Path(link): Path, state: State, ) -> Result>, ApiError> { - // let listen_addr = &state.listen_addr; let diff_res = state.monorepo().content_diff(&link).await?; let diff_files = extract_files_with_status(&diff_res); @@ -463,9 +463,9 @@ fn build_forest(paths: Vec) -> Vec { #[cfg(test)] mod test { - use std::collections::HashMap; use crate::api::mr::mr_router::{build_forest, extract_files_with_status}; use crate::api::mr::FilesChangedList; + use std::collections::HashMap; #[test] fn test_parse_diff_result_to_filelist() { @@ -519,7 +519,7 @@ mod test { fn test_mr_files_changed_logic() { // Test the core logic of mr_files_changed function // This tests the data transformation logic without needing the full state - + let sample_diff_output = r#"diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..abc1234 @@ -544,7 +544,7 @@ mod test { // Test extract_files_with_status let diff_files = extract_files_with_status(sample_diff_output); - + assert_eq!(diff_files.len(), 3); assert_eq!(diff_files.get("src/main.rs"), Some(&"new".to_string())); assert_eq!(diff_files.get("src/lib.rs"), Some(&"modified".to_string())); @@ -555,12 +555,12 @@ mod test { for (path, _) in diff_files { paths.push(path); } - + let mui_trees = build_forest(paths); - + // Verify the tree structure assert!(!mui_trees.is_empty()); - + // Check that we have the expected root nodes let root_labels: Vec<&str> = mui_trees.iter().map(|tree| tree.label.as_str()).collect(); assert!(root_labels.contains(&"src")); @@ -571,7 +571,7 @@ mod test { mui_trees, content: sample_diff_output.to_string(), }; - + assert!(!files_changed_list.mui_trees.is_empty()); assert_eq!(files_changed_list.content, sample_diff_output); } @@ -594,7 +594,7 @@ new file mode 100644 index 0000000..1234567 --- /dev/null +++ b/new_file.txt"#; - + let result = extract_files_with_status(additions_only); assert_eq!(result.len(), 1); assert_eq!(result.get("new_file.txt"), Some(&"new".to_string())); @@ -603,7 +603,7 @@ index 0000000..1234567 let deletions_only = r#"diff --git a/old_file.txt b/old_file.txt deleted file mode 100644 index 1234567..0000000"#; - + let result = extract_files_with_status(deletions_only); assert_eq!(result.len(), 1); assert_eq!(result.get("old_file.txt"), Some(&"deleted".to_string())); @@ -631,7 +631,7 @@ index 1234567..0000000"#; let result = build_forest(nested_paths); assert_eq!(result.len(), 1); // Should have one root "a" assert_eq!(result[0].label, "a"); - + // The tree should have nested structure let root = &result[0]; assert!(root.children.is_some()); diff --git a/mono/src/api/notes/note_router.rs b/mono/src/api/notes/note_router.rs index 9400d652e..08980b970 100644 --- a/mono/src/api/notes/note_router.rs +++ b/mono/src/api/notes/note_router.rs @@ -1,6 +1,6 @@ use crate::api::error::ApiError; use crate::api::MonoApiServiceState; -use crate::server::https_server::SYNC_NOTES_STATE_TAG; +use crate::server::http_server::SYNC_NOTES_STATE_TAG; use crate::api::notes::model::{ShowResponse, UpdateRequest}; diff --git a/mono/src/api/user/user_router.rs b/mono/src/api/user/user_router.rs index abd6d8161..4b84246c3 100644 --- a/mono/src/api/user/user_router.rs +++ b/mono/src/api/user/user_router.rs @@ -14,7 +14,7 @@ use crate::api::user::model::ListSSHKey; use crate::api::user::model::ListToken; use crate::api::MonoApiServiceState; use crate::api::{error::ApiError, oauth::model::LoginUser, util}; -use crate::{api::user::model::AddSSHKey, server::https_server::USER_TAG}; +use crate::{api::user::model::AddSSHKey, server::http_server::USER_TAG}; pub fn routers() -> OpenApiRouter { OpenApiRouter::new().nest( diff --git a/mono/src/cli.rs b/mono/src/cli.rs index 3bd12c0b6..6f8dbb9dc 100644 --- a/mono/src/cli.rs +++ b/mono/src/cli.rs @@ -32,7 +32,7 @@ pub fn parse(args: Option>) -> MegaResult { // Load configuration from the config file or default location let current_dir = env::current_dir()?; let base_dir = common::config::mega_base(); - let config_path = current_dir.join("config.toml"); + let config_path = current_dir.join("config/config.toml"); let config_path_alt = base_dir.join("etc/config.toml"); let config = if let Some(path) = matches.get_one::("config").cloned() { diff --git a/mono/src/commands/service/http.rs b/mono/src/commands/service/http.rs index 659f50f84..ba1c9fc7b 100644 --- a/mono/src/commands/service/http.rs +++ b/mono/src/commands/service/http.rs @@ -1,4 +1,4 @@ -use crate::server::https_server::{self}; +use crate::server::http_server::{self}; use clap::{ArgMatches, Args, Command, FromArgMatches}; use common::{errors::MegaResult, model::CommonHttpOptions}; use context::AppContext; @@ -13,7 +13,7 @@ pub(crate) async fn exec(ctx: AppContext, args: &ArgMatches) -> MegaResult { .unwrap(); tracing::info!("{server_matchers:#?}"); - https_server::start_http(ctx, server_matchers).await; + http_server::start_http(ctx, server_matchers).await; Ok(()) } diff --git a/mono/src/commands/service/multi.rs b/mono/src/commands/service/multi.rs index fe027ce6b..e8f74a771 100644 --- a/mono/src/commands/service/multi.rs +++ b/mono/src/commands/service/multi.rs @@ -1,7 +1,7 @@ use clap::{ArgMatches, Args, Command, FromArgMatches, ValueEnum}; use crate::server::{ - https_server::{self}, + http_server::{self}, ssh_server::{self, SshCustom, SshOptions}, }; use common::{errors::MegaResult, model::CommonHttpOptions}; @@ -43,7 +43,7 @@ pub(crate) async fn exec(ctx: AppContext, args: &ArgMatches) -> MegaResult { let context_clone = ctx.clone(); let http_server = if service_type.contains(&StartCommand::Http) { let http = server_matchers.http.clone(); - tokio::spawn(async move { https_server::start_http(context_clone, http).await }) + tokio::spawn(async move { http_server::start_http(context_clone, http).await }) } else { panic!("start params should provide! run like 'mega service multi http ssh'") }; diff --git a/mono/src/git_protocol/http.rs b/mono/src/git_protocol/http.rs index dce06bfbe..86a799c54 100644 --- a/mono/src/git_protocol/http.rs +++ b/mono/src/git_protocol/http.rs @@ -206,7 +206,7 @@ pub async fn git_receive_pack( // Process the data up to the "PACK" subsequence. if let Some(pos) = search_subsequence(&chunk, b"PACK") { chunk_buffer.extend_from_slice(&chunk[0..pos]); - pack_protocol.git_receive_pack_protocol(Bytes::copy_from_slice(&chunk_buffer)); + pack_protocol.parse_receive_pack_commands(Bytes::copy_from_slice(&chunk_buffer)); // Create a new stream from the remaining bytes and the rest of the data stream. let left_chunk_bytes = Bytes::copy_from_slice(&chunk[pos..]); let pack_stream = stream::once(async { Ok(left_chunk_bytes) }).chain(data_stream); diff --git a/mono/src/git_protocol/ssh.rs b/mono/src/git_protocol/ssh.rs index f30f5e0c2..b5da5e6f1 100644 --- a/mono/src/git_protocol/ssh.rs +++ b/mono/src/git_protocol/ssh.rs @@ -238,7 +238,7 @@ impl SshServer { let chunk = chunk.unwrap(); if let Some(pos) = search_subsequence(&chunk, b"PACK") { - smart_protocol.git_receive_pack_protocol(Bytes::copy_from_slice(&chunk[..pos])); + smart_protocol.parse_receive_pack_commands(Bytes::copy_from_slice(&chunk[..pos])); let remaining_bytes = Bytes::copy_from_slice(&chunk[pos..]); let remaining_stream = stream::once(async { Ok(remaining_bytes) }).chain(data_stream); diff --git a/mono/src/server/https_server.rs b/mono/src/server/http_server.rs similarity index 89% rename from mono/src/server/https_server.rs rename to mono/src/server/http_server.rs index 1c55be4ba..0badc92f9 100644 --- a/mono/src/server/https_server.rs +++ b/mono/src/server/http_server.rs @@ -33,10 +33,14 @@ use crate::api::MonoApiServiceState; use context::AppContext; #[derive(Clone)] -pub struct AppState { +pub struct ProtocolApiState { pub storage: Storage, - pub host: String, - pub port: u16, +} + +impl ProtocolApiState { + fn new(storage: Storage) -> Self { + Self { storage } + } } pub fn remove_git_suffix(uri: Uri, git_suffix: &str) -> PathBuf { @@ -51,7 +55,7 @@ pub async fn start_http(ctx: AppContext, options: CommonHttpOptions) { let server_url = format!("{host}:{port}"); let addr = SocketAddr::from_str(&server_url).unwrap(); - tracing::info!("http server start up!"); + tracing::info!("HTTP server started up!"); let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); axum::serve(listener, app.into_make_service()) @@ -89,18 +93,14 @@ pub async fn start_http(ctx: AppContext, options: CommonHttpOptions) { /// - POST end of `Regex::new(r"/git-upload-pack$")` /// - POST end of `Regex::new(r"/git-receive-pack$")` pub async fn app(storage: Storage, host: String, port: u16) -> Router { - let state = AppState { - host: host.clone(), - port, - storage: storage.clone(), - }; - + let state = ProtocolApiState::new(storage.clone()); let config = storage.config(); + let oauth_config = config.oauth.clone().unwrap_or_default(); let api_state = MonoApiServiceState { storage: storage.clone(), oauth_client: Some(oauth_client(oauth_config.clone()).unwrap()), - store: Some(CampsiteApiStore::new(oauth_config.campsite_api_domain)), + session_store: Some(CampsiteApiStore::new(oauth_config.campsite_api_domain)), listen_addr: format!("http://{host}:{port}"), }; @@ -155,7 +155,7 @@ lazy_static! { } pub async fn get_method_router( - state: State, + state: State, Query(params): Query, uri: Uri, ) -> Result, ProtocolError> { @@ -174,7 +174,7 @@ pub async fn get_method_router( } pub async fn post_method_router( - state: State, + state: State, uri: Uri, req: Request, ) -> Result { @@ -219,19 +219,4 @@ pub const USER_TAG: &str = "user"; struct ApiDoc; #[cfg(test)] -mod test { - use std::{fs, io::Write}; - use utoipa::OpenApi; - - use crate::server::https_server::ApiDoc; - - #[test] - fn generate_swagger_json() { - let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory"); - let temp_path = temp_dir.path().join("gitmono.json"); - let mut file = fs::File::create(temp_path).unwrap(); - let json = ApiDoc::openapi().to_pretty_json().unwrap(); - file.write_all(json.as_bytes()).unwrap(); - println!("{json}"); - } -} +mod test {} diff --git a/mono/src/server/mod.rs b/mono/src/server/mod.rs index cb358d2b5..9c8fc2e7b 100644 --- a/mono/src/server/mod.rs +++ b/mono/src/server/mod.rs @@ -1,2 +1,2 @@ -pub mod https_server; +pub mod http_server; pub mod ssh_server; diff --git a/monobean/src/core/mega_core.rs b/monobean/src/core/mega_core.rs index 1526c7c38..fc40e5b31 100644 --- a/monobean/src/core/mega_core.rs +++ b/monobean/src/core/mega_core.rs @@ -470,7 +470,7 @@ mod tests { use crate::config::MEGA_CONFIG_PATH; use async_channel::bounded; use common::config::LogConfig; - use common::config::{AuthConfig, DbConfig, LFSConfig, MonoConfig, PackConfig}; + use common::config::{AuthConfig, BuildConfig,DbConfig, LFSConfig, MonoConfig, PackConfig}; use gtk::gio; use gtk::glib; @@ -500,6 +500,7 @@ mod tests { authentication: AuthConfig::default(), lfs: LFSConfig::default(), oauth: None, + build: BuildConfig::default(), }; // make config saved in temp dir diff --git a/moon/packages/types/generated.ts b/moon/packages/types/generated.ts index aa61d6375..e277dab61 100644 --- a/moon/packages/types/generated.ts +++ b/moon/packages/types/generated.ts @@ -4688,6 +4688,8 @@ export type PostApiLabelListData = CommonResultCommonPageLabelItem export type PostApiLabelNewData = CommonResultString +export type GetApiLabelByIdData = CommonResultCommonPageLabelItem + export type GetApiLatestCommitParams = { refs?: string path?: string @@ -4713,6 +4715,8 @@ export type GetApiMrFilesListData = CommonResultVecMrFilesRes export type PostApiMrMergeData = CommonResultString +export type PostApiMrMergeNoAuthData = CommonResultString + export type PostApiMrReopenData = CommonResultString export type PostApiMrTitleData = CommonResultString @@ -13487,6 +13491,29 @@ export class Api extends HttpClient { + const base = 'GET:/api/v1/label/{id}' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (id: number) => dataTaggedQueryKey([base, id]), + request: (id: number, params: RequestParams = {}) => + this.request({ + path: `/api/v1/label/${id}`, + method: 'GET', + ...params + }) + } + }, + /** * No description * @@ -13726,6 +13753,30 @@ export class Api extends HttpClient { + const base = 'POST:/api/v1/mr/{link}/merge-no-auth' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (link: string) => dataTaggedQueryKey([base, link]), + request: (link: string, params: RequestParams = {}) => + this.request({ + path: `/api/v1/mr/${link}/merge-no-auth`, + method: 'POST', + ...params + }) + } + }, + /** * No description * diff --git a/neptune/src/lib.rs b/neptune/src/lib.rs index 6e8a0eb95..66cde9ad5 100644 --- a/neptune/src/lib.rs +++ b/neptune/src/lib.rs @@ -1,3 +1,3 @@ pub mod neptune_engine; -pub use neptune_engine::Diff; \ No newline at end of file +pub use neptune_engine::Diff; diff --git a/neptune/src/neptune_engine.rs b/neptune/src/neptune_engine.rs index 9d04b8d81..656ddb96f 100644 --- a/neptune/src/neptune_engine.rs +++ b/neptune/src/neptune_engine.rs @@ -1,14 +1,9 @@ -use std::collections::HashMap; -use std::{ - path::{PathBuf}, - fmt::Write -}; -use std::collections::HashSet; -use mercury::{ - hash::SHA1 -}; use infer; +use mercury::hash::SHA1; use path_absolutize::Absolutize; +use std::collections::HashMap; +use std::collections::HashSet; +use std::{fmt::Write, path::PathBuf}; /// The main diff engine responsible for computing and formatting file differences. /// @@ -52,40 +47,42 @@ impl Diff { algorithm: String, filter: Vec, read_content: F, - ) -> String - where + ) -> String + where F: Fn(&PathBuf, &SHA1) -> Vec, { - let (processed_files, old_blobs_map, new_blobs_map) = + let (processed_files, old_blobs_map, new_blobs_map) = Self::prepare_diff_data(old_blobs, new_blobs, &filter); - + let mut diff_results = Vec::new(); for file in processed_files { - if let Some(large_file_marker) = Self::is_large_file( - &file, - &old_blobs_map, - &new_blobs_map, - &read_content - ) { + if let Some(large_file_marker) = + Self::is_large_file(&file, &old_blobs_map, &new_blobs_map, &read_content) + { diff_results.push(large_file_marker); } else { - let diff = Self::diff_for_file_string(&file, &old_blobs_map, &new_blobs_map, algorithm.as_str(), &read_content); + let diff = Self::diff_for_file_string( + &file, + &old_blobs_map, + &new_blobs_map, + algorithm.as_str(), + &read_content, + ); diff_results.push(diff); } } - + diff_results.join("") } - /// Checks if a file is large and returns a message if it is. - fn is_large_file ( + fn is_large_file( file: &PathBuf, old_blobs: &HashMap, new_blobs: &HashMap, - read_content: &F - ) -> Option - where + read_content: &F, + ) -> Option + where F: Fn(&PathBuf, &SHA1) -> Vec, { // Check if file is large based on some criteria, e.g. number of lines @@ -113,7 +110,6 @@ impl Diff { } } - /// Extracts common diff preparation logic fn prepare_diff_data( old_blobs: Vec<(PathBuf, SHA1)>, @@ -124,8 +120,12 @@ impl Diff { let new_blobs_map: HashMap = new_blobs.into_iter().collect(); // union set - let union_files: HashSet = old_blobs_map.keys().chain(new_blobs_map.keys()).cloned().collect(); - + let union_files: HashSet = old_blobs_map + .keys() + .chain(new_blobs_map.keys()) + .cloned() + .collect(); + tracing::debug!( "old_blobs: {:?}, new_blobs: {:?}, union_files: {:?}", old_blobs_map.len(), @@ -149,7 +149,11 @@ impl Diff { new_blobs: &HashMap, ) -> bool { // Skip if not in filter paths - if !filter.is_empty() && !filter.iter().any(|path| Self::sub_of(file, path).unwrap_or(false)) { + if !filter.is_empty() + && !filter + .iter() + .any(|path| Self::sub_of(file, path).unwrap_or(false)) + { return false; } @@ -171,7 +175,7 @@ impl Diff { read_content: &dyn Fn(&PathBuf, &SHA1) -> Vec, ) -> String { let mut out = String::new(); - + // Look up hashes let new_hash = new_blobs.get(file); let old_hash = old_blobs.get(file); @@ -204,16 +208,21 @@ impl Diff { let _new_type = infer::get(&new_bytes); // Try UTF-8 first - match (String::from_utf8(old_bytes.clone()), String::from_utf8(new_bytes.clone())) { + match ( + String::from_utf8(old_bytes.clone()), + String::from_utf8(new_bytes.clone()), + ) { (Ok(old_text), Ok(new_text)) => { // a/ and b/ prefixes let (old_pref, new_pref) = if old_text.is_empty() { - ("/dev/null".to_string(), - format!("b/{}", file.display())) + ("/dev/null".to_string(), format!("b/{}", file.display())) } else if new_text.is_empty() { (format!("a/{}", file.display()), "/dev/null".to_string()) } else { - (format!("a/{}", file.display()), format!("b/{}", file.display())) + ( + format!("a/{}", file.display()), + format!("b/{}", file.display()), + ) }; writeln!(out, "--- {old_pref}").unwrap(); @@ -224,7 +233,8 @@ impl Diff { "@@ -1,{} +1,{} @@", old_text.lines().count(), new_text.lines().count(), - ).unwrap(); + ) + .unwrap(); for line in old_text.lines() { writeln!(out, "-{line}").unwrap(); } @@ -232,7 +242,7 @@ impl Diff { writeln!(out, "+{line}").unwrap(); } } - + // Binary fallback _ => { writeln!( diff --git a/orion-server/bellatrix/Cargo.toml b/orion-server/bellatrix/Cargo.toml new file mode 100644 index 000000000..54080b78c --- /dev/null +++ b/orion-server/bellatrix/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bellatrix" +version = "0.1.0" +edition = "2024" + +[lib] +name = "bellatrix" +path = "src/lib.rs" + +[dependencies] +common = { workspace = true } +reqwest = { workspace = true, features = ["json"]} +anyhow = { workspace = true } +serde = { workspace = true } +tracing = { workspace = true } \ No newline at end of file diff --git a/orion-server/bellatrix/src/lib.rs b/orion-server/bellatrix/src/lib.rs new file mode 100644 index 000000000..a107b3e26 --- /dev/null +++ b/orion-server/bellatrix/src/lib.rs @@ -0,0 +1,31 @@ +pub mod orion_client; + +use common::config::BuildConfig; + +use crate::orion_client::OrionBuildRequest; +use crate::orion_client::OrionClient; + +#[derive(Clone)] +pub struct Bellatrix { + orion: OrionClient, + build_config: BuildConfig, +} + +impl Bellatrix { + pub fn new(build_config: BuildConfig) -> Self { + let orion = OrionClient::new(build_config.orion_server.clone()); + Self { + orion, + build_config, + } + } + + pub fn enable_build(&self) -> bool { + self.build_config.enable_build + } + + pub async fn on_post_receive(&self, req: OrionBuildRequest) -> anyhow::Result<()> { + self.orion.trigger_build(req).await?; + Ok(()) + } +} diff --git a/orion-server/bellatrix/src/orion_client/mod.rs b/orion-server/bellatrix/src/orion_client/mod.rs new file mode 100644 index 000000000..874ab6b59 --- /dev/null +++ b/orion-server/bellatrix/src/orion_client/mod.rs @@ -0,0 +1,37 @@ +use serde::Serialize; + +#[derive(Serialize, Debug)] +pub struct OrionBuildRequest { + pub repo: String, + pub buck_hash: String, + pub buckconfig_hash: String, + pub mr: String, + pub args: Option>, +} + +#[derive(Clone)] +pub struct OrionClient { + base_url: String, + client: reqwest::Client, +} + +impl OrionClient { + pub fn new(base_url: impl Into) -> Self { + Self { + base_url: base_url.into(), + client: reqwest::Client::new(), + } + } + + pub async fn trigger_build(&self, req: OrionBuildRequest) -> anyhow::Result<()> { + let url = format!("{}/task", self.base_url); + tracing::info!("Try to trigger build with params:{:?}", req); + let res = self.client.post(&url).json(&req).send().await?; + if res.status().is_success() { + Ok(()) + } else { + tracing::error!("Failed to trigger build: {}", res.status()); + Err(anyhow::anyhow!("Failed to trigger build: {}", res.status())) + } + } +}