Skip to content

Commit 180db7b

Browse files
authored
Merge branch 'web3infra-foundation:main' into main
2 parents e905f67 + 0e42b24 commit 180db7b

File tree

89 files changed

+4605
-1124
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+4605
-1124
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ smallvec = "1.15.1"
7878
bytes = "1.11"
7979
chrono = { version = "0.4.43", features = ["serde"] }
8080
hex = "0.4.3"
81+
sha2 = "0.10.9"
8182

8283
idgenerator = "2.0.0"
8384
config = "0.15.19"

api-model/src/buck2/types.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,75 @@ impl ProjectRelativePath {
3939
opt.map(|s| Self(s.to_owned()))
4040
}
4141
}
42+
43+
/// Supported read modes for log APIs.
44+
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq, Eq, Default)]
45+
#[serde(rename_all = "lowercase")]
46+
pub enum LogReadMode {
47+
#[default]
48+
Full,
49+
Segment,
50+
}
51+
52+
/// Log stream event emitted by Orion worker/build processing.
53+
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
54+
pub struct LogEvent {
55+
pub task_id: String,
56+
pub repo_name: String,
57+
pub build_id: String,
58+
pub line: String,
59+
pub is_end: bool,
60+
}
61+
62+
/// Log segment read result.
63+
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
64+
pub struct LogSegment {
65+
pub build_id: String,
66+
pub offset: u64,
67+
pub len: usize,
68+
pub data: String,
69+
pub next_offset: u64,
70+
pub file_size: u64,
71+
/// Whether we reached end of file
72+
pub eof: bool,
73+
}
74+
75+
/// Query parameters for target log APIs.
76+
#[derive(Debug, Clone, Deserialize, ToSchema)]
77+
pub struct TargetLogQuery {
78+
#[serde(default)]
79+
pub r#type: LogReadMode,
80+
pub offset: Option<usize>,
81+
pub limit: Option<usize>,
82+
}
83+
84+
/// Query parameters for task history log APIs.
85+
#[derive(Debug, Clone, Deserialize, ToSchema)]
86+
pub struct TaskHistoryQuery {
87+
pub task_id: String,
88+
pub build_id: String,
89+
pub repo: String,
90+
pub start: Option<usize>,
91+
pub end: Option<usize>,
92+
}
93+
94+
/// Log lines response for history reads.
95+
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
96+
pub struct LogLinesResponse {
97+
pub data: Vec<String>,
98+
pub len: usize,
99+
}
100+
101+
/// Log lines response for target reads.
102+
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
103+
pub struct TargetLogLinesResponse {
104+
pub data: Vec<String>,
105+
pub len: usize,
106+
pub build_id: String,
107+
}
108+
109+
/// Error response for log-related APIs.
110+
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
111+
pub struct LogErrorResponse {
112+
pub message: String,
113+
}

api-model/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
pub mod buck2;
22
pub mod common;
33
pub mod git;
4-
pub mod orion;

api-model/src/orion/log.rs

Lines changed: 0 additions & 72 deletions
This file was deleted.

api-model/src/orion/mod.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use std::{
2+
path::{Path, PathBuf},
3+
sync::Arc,
4+
};
5+
6+
use common::errors::MegaError;
7+
use git_internal::hash::ObjectHash;
8+
use jupiter::storage::Storage;
9+
10+
use crate::{
11+
api_service::{cache::GitObjectCache, mono_api_service::MonoApiService},
12+
build_trigger::{SerializableBuildInfo, SerializableStatus, TriggerContext},
13+
model::change_list::{ClDiffFile, ClFilesRes},
14+
};
15+
16+
pub struct ChangesCalculator {
17+
storage: Storage,
18+
git_object_cache: Arc<GitObjectCache>,
19+
}
20+
21+
impl ChangesCalculator {
22+
pub fn new(storage: Storage, git_object_cache: Arc<GitObjectCache>) -> Self {
23+
Self {
24+
storage,
25+
git_object_cache,
26+
}
27+
}
28+
29+
pub async fn get_builds_for_commit(
30+
&self,
31+
context: &TriggerContext,
32+
) -> Result<Vec<SerializableBuildInfo>, MegaError> {
33+
let old_files = self.get_commit_blobs(&context.from_hash).await?;
34+
let new_files = self.get_commit_blobs(&context.commit_hash).await?;
35+
let diff_files = self.cl_files_list(old_files, new_files).await?;
36+
37+
let changes = self.build_changes(&context.repo_path, diff_files)?;
38+
39+
Ok(vec![SerializableBuildInfo { changes }])
40+
}
41+
42+
fn build_changes(
43+
&self,
44+
cl_path: &str,
45+
cl_diff_files: Vec<ClDiffFile>,
46+
) -> Result<Vec<SerializableStatus>, MegaError> {
47+
let cl_base = PathBuf::from(cl_path);
48+
let path_str = cl_base.to_str().ok_or_else(|| {
49+
MegaError::Other(format!("CL base path is not valid UTF-8: {:?}", cl_base))
50+
})?;
51+
52+
let changes = cl_diff_files
53+
.into_iter()
54+
.map(|m| {
55+
let mut item: ClFilesRes = m.into();
56+
item.path = cl_base.join(item.path).to_string_lossy().to_string();
57+
item
58+
})
59+
.collect::<Vec<_>>();
60+
61+
let counter_changes = changes
62+
.iter()
63+
.filter(|&s| PathBuf::from(&s.path).starts_with(&cl_base))
64+
.map(|s| {
65+
let rel = Path::new(&s.path)
66+
.strip_prefix(path_str)
67+
.map_err(|_| {
68+
MegaError::Other(format!("Invalid project-relative path: {}", s.path))
69+
})?
70+
.to_string_lossy()
71+
.replace('\\', "/")
72+
.trim_start_matches('/')
73+
.to_string();
74+
75+
let status = if s.action == "new" {
76+
SerializableStatus::Added(rel)
77+
} else if s.action == "deleted" {
78+
SerializableStatus::Removed(rel)
79+
} else if s.action == "modified" {
80+
SerializableStatus::Modified(rel)
81+
} else {
82+
return Err(MegaError::Other(format!(
83+
"Unsupported change action: {}",
84+
s.action
85+
)));
86+
};
87+
Ok(status)
88+
})
89+
.collect::<Result<Vec<_>, MegaError>>()?;
90+
91+
Ok(counter_changes)
92+
}
93+
94+
async fn get_commit_blobs(
95+
&self,
96+
commit_hash: &str,
97+
) -> Result<Vec<(PathBuf, ObjectHash)>, MegaError> {
98+
let api_service = MonoApiService {
99+
storage: self.storage.clone(),
100+
git_object_cache: self.git_object_cache.clone(),
101+
};
102+
api_service.get_commit_blobs(commit_hash).await
103+
}
104+
105+
async fn cl_files_list(
106+
&self,
107+
old_files: Vec<(PathBuf, ObjectHash)>,
108+
new_files: Vec<(PathBuf, ObjectHash)>,
109+
) -> Result<Vec<ClDiffFile>, MegaError> {
110+
let api_service = MonoApiService {
111+
storage: self.storage.clone(),
112+
git_object_cache: self.git_object_cache.clone(),
113+
};
114+
api_service.cl_files_list(old_files, new_files).await
115+
}
116+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use std::sync::Arc;
2+
3+
use bellatrix::{Bellatrix, orion_client::OrionBuildRequest};
4+
use common::errors::MegaError;
5+
use jupiter::storage::Storage;
6+
7+
use crate::build_trigger::{BuildTrigger, BuildTriggerPayload, SerializableBuildInfo};
8+
9+
/// Handles dispatching build triggers to the build execution layer (Bellatrix/Orion).
10+
pub struct BuildDispatcher {
11+
storage: Storage,
12+
bellatrix: Arc<Bellatrix>,
13+
}
14+
15+
impl BuildDispatcher {
16+
pub fn new(storage: Storage, bellatrix: Arc<Bellatrix>) -> Self {
17+
Self { storage, bellatrix }
18+
}
19+
20+
/// Dispatch a build trigger.
21+
///
22+
/// This method:
23+
/// 1. Persists the trigger to the database
24+
/// 2. Sends the build request to Bellatrix/Orion asynchronously
25+
///
26+
/// Returns the ID of the created trigger record.
27+
pub async fn dispatch(&self, trigger: BuildTrigger) -> Result<i64, MegaError> {
28+
let trigger_payload = serde_json::to_value(&trigger.payload).map_err(|e| {
29+
tracing::error!("Failed to serialize payload: {}", e);
30+
MegaError::Other(format!("Failed to serialize payload: {}", e))
31+
})?;
32+
33+
let db_record = self
34+
.storage
35+
.build_trigger_storage()
36+
.insert(
37+
trigger.trigger_type.to_string(),
38+
trigger.trigger_source.to_string(),
39+
trigger_payload,
40+
)
41+
.await?;
42+
43+
// Persist to database
44+
tracing::info!("BuildDispatcher: Persisted trigger ID: {}", db_record.id);
45+
46+
if !self.bellatrix.enable_build() {
47+
tracing::info!("BuildDispatcher: Completed (build system disabled)");
48+
return Ok(db_record.id);
49+
}
50+
51+
// Extract data from payload using pattern matching
52+
let (cl_link, repo, builds_json, cl_id) = match &trigger.payload {
53+
BuildTriggerPayload::GitPush(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
54+
BuildTriggerPayload::Manual(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
55+
BuildTriggerPayload::Retry(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
56+
BuildTriggerPayload::Webhook(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
57+
BuildTriggerPayload::Schedule(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
58+
};
59+
60+
let builds: Vec<SerializableBuildInfo> = serde_json::from_value(builds_json.clone())
61+
.map_err(|e| {
62+
tracing::error!("Failed to deserialize builds from payload: {}", e);
63+
MegaError::Other(format!("Failed to deserialize builds from payload: {}", e))
64+
})?;
65+
66+
let bellatrix_builds: Vec<bellatrix::orion_client::BuildInfo> = builds
67+
.into_iter()
68+
.enumerate()
69+
.map(|(idx, info)| {
70+
tracing::debug!(" Build [{}]: {} change(s)", idx + 1, info.changes.len());
71+
bellatrix::orion_client::BuildInfo {
72+
changes: info.changes.into_iter().map(|s| s.into()).collect(),
73+
}
74+
})
75+
.collect();
76+
77+
let req = OrionBuildRequest {
78+
cl_link: cl_link.to_string(),
79+
mount_path: repo.to_string(),
80+
cl: cl_id.unwrap_or(0),
81+
builds: bellatrix_builds,
82+
};
83+
84+
// Dispatch asynchronously
85+
let bellatrix = self.bellatrix.clone();
86+
let trigger_id = db_record.id;
87+
tokio::spawn(async move {
88+
match bellatrix.on_post_receive(req).await {
89+
Ok(_) => {
90+
tracing::info!(
91+
"BuildDispatcher: Build request sent to Bellatrix (Trigger ID: {})",
92+
trigger_id
93+
);
94+
}
95+
Err(err) => {
96+
tracing::error!(
97+
"BuildDispatcher: Failed to dispatch build (Trigger ID: {}): {}",
98+
trigger_id,
99+
err
100+
);
101+
}
102+
}
103+
});
104+
105+
Ok(db_record.id)
106+
}
107+
}

0 commit comments

Comments
 (0)