@@ -45,8 +45,10 @@ use std::{
4545
4646use api_model:: common:: Pagination ;
4747use async_trait:: async_trait;
48+ use bellatrix:: Bellatrix ;
4849use bytes:: Bytes ;
4950use callisto:: {
51+ entity_ext:: generate_link,
5052 mega_cl, mega_refs, mega_tag, mega_tree,
5153 sea_orm_active_enums:: { ConvTypeEnum , MergeStatusEnum , QueueFailureTypeEnum , QueueStatusEnum } ,
5254} ;
@@ -89,13 +91,14 @@ use crate::{
8991 ApiHandler , buck_tree_builder:: BuckCommitBuilder , cache:: GitObjectCache ,
9092 state:: ProtocolApiState , tree_ops,
9193 } ,
94+ build_trigger:: BuildTriggerService ,
9295 model:: {
9396 buck:: {
9497 CompletePayload , CompleteResponse , DEFAULT_MODE , FileChange ,
9598 FileToUpload as ApiFileToUpload , ManifestPayload , ManifestResponse ,
9699 } ,
97100 change_list:: { ClDiffFile , UpdateBranchStatusRes } ,
98- git:: { CreateEntryInfo , EditFilePayload , EditFileResult } ,
101+ git:: { CreateEntryInfo , EditCLMode , EditFilePayload , EditFileResult } ,
99102 tag:: TagInfo ,
100103 third_party:: { ThirdPartyClient , ThirdPartyRepoTrait } ,
101104 } ,
@@ -398,15 +401,34 @@ impl ApiHandler for MonoApiService {
398401 . apply_update_result ( & result, & payload. commit_message , None )
399402 . await ?;
400403
404+ let username = payload
405+ . author_username
406+ . clone ( )
407+ . unwrap_or ( "Anonymous" . to_string ( ) ) ;
408+
409+ let cl = self
410+ . find_or_create_cl_for_edit (
411+ payload. mode ,
412+ & new_commit_id,
413+ & payload. commit_message ,
414+ & payload. path ,
415+ & username,
416+ )
417+ . await ?;
401418 self . storage
402419 . mono_service
403420 . save_blobs ( & new_commit_id, vec ! [ new_blob. clone( ) ] )
404421 . await ?;
405422
423+ if !payload. skip_build {
424+ self . trigger_build_for_cl ( & cl. link ) . await ?;
425+ }
426+
406427 Ok ( EditFileResult {
407428 commit_id : new_commit_id,
408429 new_oid : new_blob. id . to_string ( ) ,
409430 path : payload. path ,
431+ cl_link : Some ( cl. link ) ,
410432 } )
411433 }
412434
@@ -1027,6 +1049,145 @@ impl MonoApiService {
10271049 }
10281050 }
10291051
1052+ async fn create_new_cl (
1053+ & self ,
1054+ repo_path : & str ,
1055+ commit_message : & str ,
1056+ to_hash : & str ,
1057+ username : & str ,
1058+ ) -> Result < mega_cl:: Model , GitError > {
1059+ let cl_link = generate_link ( ) ;
1060+
1061+ let from_hash = self
1062+ . storage
1063+ . mono_storage ( )
1064+ . get_main_ref ( repo_path)
1065+ . await ?
1066+ . ok_or_else ( || MegaError :: Other ( "Main ref not found" . to_string ( ) ) ) ?
1067+ . ref_commit_hash ;
1068+
1069+ // Create and return a new CL model
1070+ let cl = self
1071+ . storage
1072+ . cl_storage ( )
1073+ . new_cl_model (
1074+ repo_path,
1075+ & cl_link,
1076+ commit_message,
1077+ & from_hash,
1078+ to_hash,
1079+ username,
1080+ )
1081+ . await
1082+ . map_err ( |e| GitError :: CustomError ( format ! ( "Failed to create CL: {}" , e) ) ) ?;
1083+
1084+ Ok ( cl)
1085+ }
1086+
1087+ /// Finds or creates a Change List (CL) for file edits.
1088+ /// This method determines whether to create a new CL or reuse an existing one based on the
1089+ /// [`EditCLMode`] passed in `mode`. For example, `EditCLMode::ForceCreate` will always create
1090+ /// a new CL, while `EditCLMode::TryReuse` will attempt to find an open CL for the given user
1091+ /// and path and only create a new one if none exists.
1092+ ///
1093+ /// # Arguments
1094+ /// * `mode` - Controls whether to force creation of a new CL (`ForceCreate`) or try to reuse an
1095+ /// existing open CL for the user and path when possible (`TryReuse`).
1096+ /// * `to_hash` - The commit hash representing the new state after the edit.
1097+ /// * `commit_message` - The message describing the changes made in the CL.
1098+ /// * `file_path` - The path of the file being edited.
1099+ /// * `username` - The username of the user performing the edit.
1100+ ///
1101+ /// # Returns
1102+ /// A `Result` containing the `mega_cl::Model` representing the found or created CL on success,
1103+ /// or a `GitError` on failure.
1104+ async fn find_or_create_cl_for_edit (
1105+ & self ,
1106+ mode : EditCLMode ,
1107+ to_hash : & str ,
1108+ commit_message : & str ,
1109+ file_path : & str ,
1110+ username : & str ,
1111+ ) -> Result < mega_cl:: Model , GitError > {
1112+ let path = PathBuf :: from ( file_path) ;
1113+ let repo_path = path
1114+ . parent ( )
1115+ . map ( |p| p. to_string_lossy ( ) . to_string ( ) )
1116+ . unwrap_or_else ( || "/" . to_string ( ) ) ;
1117+
1118+ let storage = self . storage . cl_storage ( ) ;
1119+ match mode {
1120+ EditCLMode :: ForceCreate => {
1121+ self . create_new_cl ( & repo_path, commit_message, to_hash, username)
1122+ . await
1123+ }
1124+ EditCLMode :: TryReuse ( None ) => {
1125+ if let Some ( existing_cl) =
1126+ storage
1127+ . get_open_cl_by_path ( & repo_path, username)
1128+ . await
1129+ . map_err ( |e| GitError :: CustomError ( format ! ( "Failed to fetch CL: {}" , e) ) ) ?
1130+ {
1131+ storage
1132+ . update_cl_to_hash ( existing_cl. clone ( ) , to_hash)
1133+ . await ?;
1134+ Ok ( existing_cl)
1135+ } else {
1136+ self . create_new_cl ( & repo_path, commit_message, to_hash, username)
1137+ . await
1138+ }
1139+ }
1140+ EditCLMode :: TryReuse ( Some ( link) ) => {
1141+ match self . storage . cl_storage ( ) . get_cl ( & link) . await {
1142+ Ok ( Some ( model) ) => {
1143+ storage. update_cl_to_hash ( model. clone ( ) , to_hash) . await ?;
1144+ Ok ( model)
1145+ }
1146+ _ => {
1147+ self . create_new_cl ( & repo_path, commit_message, to_hash, username)
1148+ . await
1149+ }
1150+ }
1151+ }
1152+ }
1153+ }
1154+
1155+ /// Triggers a build for the specified Change List (CL).
1156+ /// It spawns a background task to handle the build process, ensuring that the main application flow remains responsive.
1157+ /// # Arguments
1158+ /// * `cl_link` - The unique identifier (link) of the CL for which the build is to be triggered.
1159+ ///
1160+ /// # Returns
1161+ /// A `Result` indicating success or failure. Returns `Ok(())` if the build was successfully triggered,
1162+ /// or a `GitError` if an error occurred during the process.
1163+ async fn trigger_build_for_cl ( & self , cl_link : & str ) -> Result < ( ) , GitError > {
1164+ let config = self . storage . config ( ) ;
1165+ let bellatrix = Bellatrix :: new ( config. build . clone ( ) ) ;
1166+ let cl = self
1167+ . storage
1168+ . cl_storage ( )
1169+ . get_cl ( cl_link)
1170+ . await
1171+ . map_err ( |e| GitError :: CustomError ( format ! ( "Failed to get CL: {}" , e) ) ) ?
1172+ . ok_or_else ( || GitError :: CustomError ( "CL not found" . to_string ( ) ) ) ?;
1173+
1174+ let storage = self . storage . clone ( ) ;
1175+ let git_cache = self . git_object_cache . clone ( ) ;
1176+
1177+ // Spawn a background task to handle the build process
1178+ tokio:: spawn ( async move {
1179+ let _ = BuildTriggerService :: build_by_context (
1180+ storage,
1181+ git_cache,
1182+ Arc :: new ( bellatrix) ,
1183+ cl. into ( ) ,
1184+ )
1185+ . await ;
1186+ } ) ;
1187+
1188+ Ok ( ( ) )
1189+ }
1190+
10301191 async fn create_annotated_tag_mono (
10311192 & self ,
10321193 repo_path : Option < String > ,
0 commit comments