Skip to content

Commit 3660f64

Browse files
feat: implement task_id for build triggers (#1902)
Signed-off-by: allure <1550220889@qq.com>
1 parent 2f0f864 commit 3660f64

5 files changed

Lines changed: 126 additions & 82 deletions

File tree

ceres/src/build_trigger/dispatcher.rs

Lines changed: 59 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::sync::Arc;
22

3-
use bellatrix::{Bellatrix, orion_client::OrionBuildRequest};
3+
use bellatrix::{
4+
Bellatrix,
5+
orion_client::{BuildInfo, OrionBuildRequest},
6+
};
47
use common::errors::MegaError;
58
use jupiter::storage::Storage;
69

@@ -17,91 +20,77 @@ impl BuildDispatcher {
1720
Self { storage, bellatrix }
1821
}
1922

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.
2723
pub async fn dispatch(&self, trigger: BuildTrigger) -> Result<i64, MegaError> {
2824
let trigger_payload = serde_json::to_value(&trigger.payload).map_err(|e| {
2925
tracing::error!("Failed to serialize payload: {}", e);
3026
MegaError::Other(format!("Failed to serialize payload: {}", e))
3127
})?;
3228

29+
// Determine task_id based on whether build system is enabled
30+
let task_id: Option<uuid::Uuid> = if self.bellatrix.enable_build() {
31+
// Extract data from payload using pattern matching
32+
let (cl_link, repo, builds_json, cl_id) = match &trigger.payload {
33+
BuildTriggerPayload::GitPush(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
34+
BuildTriggerPayload::Manual(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
35+
BuildTriggerPayload::Retry(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
36+
BuildTriggerPayload::Webhook(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
37+
BuildTriggerPayload::Schedule(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
38+
BuildTriggerPayload::WebEdit(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
39+
};
40+
41+
let builds: Vec<SerializableBuildInfo> = serde_json::from_value(builds_json.clone())
42+
.map_err(|e| {
43+
tracing::error!("Failed to deserialize builds from payload: {}", e);
44+
MegaError::Other(format!("Failed to deserialize builds from payload: {}", e))
45+
})?;
46+
47+
let bellatrix_builds: Vec<BuildInfo> = builds
48+
.into_iter()
49+
.map(|info| BuildInfo {
50+
changes: info.changes.into_iter().map(|s| s.into()).collect(),
51+
})
52+
.collect();
53+
54+
let req = OrionBuildRequest {
55+
cl_link: cl_link.to_string(),
56+
mount_path: repo.to_string(),
57+
cl: cl_id.unwrap_or(0),
58+
builds: bellatrix_builds,
59+
};
60+
61+
let task_id_str = self.bellatrix.on_post_receive(req).await.map_err(|e| {
62+
tracing::error!("Failed to dispatch build to Bellatrix: {}", e);
63+
MegaError::Other(format!("Failed to dispatch build to Bellatrix: {}", e))
64+
})?;
65+
66+
let task_uuid = uuid::Uuid::parse_str(&task_id_str).map_err(|e| {
67+
tracing::error!("Invalid task_id format '{}': {}", task_id_str, e);
68+
MegaError::Other(format!("Invalid task_id format '{}': {}", task_id_str, e))
69+
})?;
70+
71+
Some(task_uuid)
72+
} else {
73+
tracing::info!("BuildDispatcher: Build system disabled, skipping Orion call");
74+
None
75+
};
76+
77+
// Insert trigger record with task_id (complete record in one operation)
3378
let db_record = self
3479
.storage
3580
.build_trigger_storage()
3681
.insert(
3782
trigger.trigger_type.to_string(),
3883
trigger.trigger_source.to_string(),
3984
trigger_payload,
85+
task_id,
4086
)
4187
.await?;
4288

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-
BuildTriggerPayload::WebEdit(p) => (&p.cl_link, &p.repo, &p.builds, p.cl_id),
59-
};
60-
61-
let builds: Vec<SerializableBuildInfo> = serde_json::from_value(builds_json.clone())
62-
.map_err(|e| {
63-
tracing::error!("Failed to deserialize builds from payload: {}", e);
64-
MegaError::Other(format!("Failed to deserialize builds from payload: {}", e))
65-
})?;
66-
67-
let bellatrix_builds: Vec<bellatrix::orion_client::BuildInfo> = builds
68-
.into_iter()
69-
.enumerate()
70-
.map(|(idx, info)| {
71-
tracing::debug!(" Build [{}]: {} change(s)", idx + 1, info.changes.len());
72-
bellatrix::orion_client::BuildInfo {
73-
changes: info.changes.into_iter().map(|s| s.into()).collect(),
74-
}
75-
})
76-
.collect();
77-
78-
let req = OrionBuildRequest {
79-
cl_link: cl_link.to_string(),
80-
mount_path: repo.to_string(),
81-
cl: cl_id.unwrap_or(0),
82-
builds: bellatrix_builds,
83-
};
84-
85-
// Dispatch asynchronously
86-
let bellatrix = self.bellatrix.clone();
87-
let trigger_id = db_record.id;
88-
tokio::spawn(async move {
89-
match bellatrix.on_post_receive(req).await {
90-
Ok(_) => {
91-
tracing::info!(
92-
"BuildDispatcher: Build request sent to Bellatrix (Trigger ID: {})",
93-
trigger_id
94-
);
95-
}
96-
Err(err) => {
97-
tracing::error!(
98-
"BuildDispatcher: Failed to dispatch build (Trigger ID: {}): {}",
99-
trigger_id,
100-
err
101-
);
102-
}
103-
}
104-
});
89+
tracing::info!(
90+
"BuildDispatcher: Trigger persisted (ID: {}, Task ID: {:?})",
91+
db_record.id,
92+
task_id
93+
);
10594

10695
Ok(db_record.id)
10796
}

ceres/src/pack/monorepo.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,13 +1054,16 @@ impl MonoRepo {
10541054
triggered_by: self.username.clone(),
10551055
};
10561056

1057-
let _trigger_id = BuildTriggerService::handle_git_push_event(
1057+
if let Err(e) = BuildTriggerService::handle_git_push_event(
10581058
self.storage.clone(),
10591059
self.git_object_cache.clone(),
10601060
self.bellatrix.clone(),
10611061
event,
10621062
)
1063-
.await?;
1063+
.await
1064+
{
1065+
tracing::error!("Failed to trigger CI pipeline: {}", e);
1066+
}
10641067
}
10651068

10661069
let check_reg = CheckerRegistry::new(self.storage.clone().into(), self.username());

jupiter/src/storage/build_trigger_storage.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ pub struct BuildTriggerStorage {
1414
}
1515

1616
impl BuildTriggerStorage {
17-
/// Insert a new build trigger
17+
/// Insert a new build trigger with optional task_id.
18+
/// When task_id is provided, inserts a complete record in one operation.
1819
pub async fn insert(
1920
&self,
2021
trigger_type: String,
2122
trigger_source: String,
2223
trigger_payload: serde_json::Value,
24+
task_id: Option<uuid::Uuid>,
2325
) -> Result<build_triggers::Model, MegaError> {
2426
let now = chrono::Utc::now().naive_utc();
2527

@@ -29,7 +31,7 @@ impl BuildTriggerStorage {
2931
trigger_source: ActiveValue::Set(trigger_source),
3032
trigger_payload: ActiveValue::Set(trigger_payload),
3133
trigger_time: ActiveValue::Set(now),
32-
task_id: ActiveValue::NotSet,
34+
task_id: ActiveValue::Set(task_id),
3335
updated_at: ActiveValue::Set(now),
3436
};
3537

@@ -169,7 +171,7 @@ mod tests {
169171

170172
let inserted = storage
171173
.build_trigger_storage()
172-
.insert("git_push".to_string(), "user".to_string(), payload)
174+
.insert("git_push".to_string(), "user".to_string(), payload, None)
173175
.await
174176
.unwrap();
175177

@@ -185,6 +187,42 @@ mod tests {
185187
assert_eq!(retrieved.trigger_source, "user");
186188
}
187189

190+
#[tokio::test]
191+
async fn test_insert_with_task_id() {
192+
let temp_dir = tempdir().unwrap();
193+
let storage = test_storage(temp_dir.path()).await;
194+
195+
let payload = serde_json::json!({
196+
"type": "manual",
197+
"repo": "/test",
198+
"commit_hash": "abc123",
199+
"cl_link": "test_link",
200+
"builds": []
201+
});
202+
203+
let task_id = uuid::Uuid::new_v4();
204+
205+
let inserted = storage
206+
.build_trigger_storage()
207+
.insert(
208+
"manual".to_string(),
209+
"user".to_string(),
210+
payload,
211+
Some(task_id),
212+
)
213+
.await
214+
.unwrap();
215+
216+
let retrieved = storage
217+
.build_trigger_storage()
218+
.get_by_id(inserted.id)
219+
.await
220+
.unwrap()
221+
.unwrap();
222+
223+
assert_eq!(retrieved.task_id, Some(task_id));
224+
}
225+
188226
#[tokio::test]
189227
async fn test_get_recent() {
190228
let temp_dir = tempdir().unwrap();
@@ -201,7 +239,12 @@ mod tests {
201239
for _ in 0..3 {
202240
storage
203241
.build_trigger_storage()
204-
.insert("manual".to_string(), "user".to_string(), payload.clone())
242+
.insert(
243+
"manual".to_string(),
244+
"user".to_string(),
245+
payload.clone(),
246+
None,
247+
)
205248
.await
206249
.unwrap();
207250
}
@@ -229,7 +272,7 @@ mod tests {
229272

230273
storage
231274
.build_trigger_storage()
232-
.insert("manual".to_string(), "user".to_string(), payload)
275+
.insert("manual".to_string(), "user".to_string(), payload, None)
233276
.await
234277
.unwrap();
235278
}

orion-server/bellatrix/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ impl Bellatrix {
2323
self.build_config.enable_build
2424
}
2525

26-
pub async fn on_post_receive(&self, req: OrionBuildRequest) -> anyhow::Result<()> {
27-
self.orion.trigger_build(req).await?;
28-
Ok(())
26+
pub async fn on_post_receive(&self, req: OrionBuildRequest) -> anyhow::Result<String> {
27+
let task_id = self.orion.trigger_build(req).await?;
28+
Ok(task_id)
2929
}
3030
}

orion-server/bellatrix/src/orion_client/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ pub struct OrionBuildRequest {
4848
pub builds: Vec<BuildInfo>,
4949
}
5050

51+
/// Response from Orion task handler containing the assigned task ID.
52+
#[derive(Deserialize, Debug)]
53+
pub struct TaskResponse {
54+
pub task_id: String,
55+
}
56+
5157
#[derive(Clone)]
5258
pub(crate) struct OrionClient {
5359
base_url: String,
@@ -62,12 +68,15 @@ impl OrionClient {
6268
}
6369
}
6470

65-
pub async fn trigger_build(&self, req: OrionBuildRequest) -> anyhow::Result<()> {
71+
/// Trigger a build on Orion and return the assigned task ID.
72+
pub async fn trigger_build(&self, req: OrionBuildRequest) -> anyhow::Result<String> {
6673
let url = format!("{}/task", self.base_url);
6774
tracing::info!("Try to trigger build with params:{:?}", req);
6875
let res = self.client.post(&url).json(&req).send().await?;
6976
if res.status().is_success() {
70-
Ok(())
77+
let task_response: TaskResponse = res.json().await?;
78+
tracing::info!("Received task_id from Orion: {}", task_response.task_id);
79+
Ok(task_response.task_id)
7180
} else {
7281
tracing::error!("Failed to trigger build: {}", res.status());
7382
Err(anyhow::anyhow!("Failed to trigger build: {}", res.status()))

0 commit comments

Comments
 (0)