158 lines
4.1 KiB
Rust
158 lines
4.1 KiB
Rust
use deadpool_redis::cluster::Pool as RedisPool;
|
|
use parsefile::{Pipeline, TriggerEvent};
|
|
|
|
use crate::{
|
|
bare::GitBare,
|
|
errors::GitError,
|
|
sync::{HookTask, TaskType},
|
|
};
|
|
|
|
const PIPELINE_FILE: &str = "pipeline.yaml";
|
|
#[derive(Debug)]
|
|
pub enum CiCheckOutcome {
|
|
Enqueued,
|
|
NoPipelineFile,
|
|
NotTriggered,
|
|
}
|
|
fn ci_queue_keys(repo_id: uuid::Uuid) -> (String, String) {
|
|
let hash_tag = format!("{{ci:{}}}", repo_id);
|
|
(
|
|
format!("{}:pending", hash_tag),
|
|
format!("{}:processing", hash_tag),
|
|
)
|
|
}
|
|
pub async fn check_and_enqueue(
|
|
bare: &GitBare,
|
|
repo_id: uuid::Uuid,
|
|
event: &TriggerEvent,
|
|
redis_pool: &RedisPool,
|
|
) -> Result<CiCheckOutcome, GitError> {
|
|
let output = bare.git_command_trusted_stdout(vec![
|
|
"show".to_string(),
|
|
format!("HEAD:{}", PIPELINE_FILE),
|
|
]);
|
|
|
|
let content = match output {
|
|
Ok(c) => c,
|
|
Err(_) => return Ok(CiCheckOutcome::NoPipelineFile),
|
|
};
|
|
|
|
let pipeline = parsefile::parse_from_str(&content).map_err(|e| {
|
|
GitError::Internal(format!("failed to parse {}: {}", PIPELINE_FILE, e))
|
|
})?;
|
|
|
|
if !pipeline.should_run(event) {
|
|
return Ok(CiCheckOutcome::NotTriggered);
|
|
}
|
|
|
|
enqueue_ci_task(repo_id, event, &pipeline, redis_pool)
|
|
.await
|
|
.map_err(|e| {
|
|
GitError::Internal(format!("failed to enqueue CI task: {}", e))
|
|
})?;
|
|
|
|
Ok(CiCheckOutcome::Enqueued)
|
|
}
|
|
|
|
async fn enqueue_ci_task(
|
|
repo_id: uuid::Uuid,
|
|
event: &TriggerEvent,
|
|
pipeline: &Pipeline,
|
|
redis_pool: &RedisPool,
|
|
) -> Result<(), String> {
|
|
let hook_task = HookTask {
|
|
id: uuid::Uuid::new_v4().to_string(),
|
|
repo_id: repo_id.to_string(),
|
|
task_type: TaskType::Sync,
|
|
payload: serde_json::json!({
|
|
"ci": true,
|
|
"pipeline_name": pipeline.name,
|
|
"trigger": event_variant_name(event),
|
|
}),
|
|
created_at: chrono::Utc::now(),
|
|
retry_count: 0,
|
|
};
|
|
|
|
let task_json = serde_json::to_string(&hook_task)
|
|
.map_err(|e| format!("serialize error: {}", e))?;
|
|
|
|
let (pending_key, _) = ci_queue_keys(repo_id);
|
|
|
|
let redis = redis_pool
|
|
.get()
|
|
.await
|
|
.map_err(|e| format!("redis pool: {}", e))?;
|
|
let mut conn: deadpool_redis::cluster::Connection = redis;
|
|
|
|
redis::cmd("LPUSH")
|
|
.arg(&pending_key)
|
|
.arg(&task_json)
|
|
.query_async::<()>(&mut conn)
|
|
.await
|
|
.map_err(|e| format!("LPUSH error: {}", e))?;
|
|
|
|
tracing::info!(
|
|
repo_id = %repo_id,
|
|
pipeline = %pipeline.name,
|
|
trigger = %event_variant_name(event),
|
|
"CI task enqueued"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn event_variant_name(event: &TriggerEvent) -> &'static str {
|
|
match event {
|
|
TriggerEvent::PushBranch(_) => "push_branch",
|
|
TriggerEvent::PushTag(_) => "push_tag",
|
|
TriggerEvent::PullRequest { .. } => "pull_request",
|
|
}
|
|
}
|
|
pub async fn poll_ci_task_for_repo(
|
|
redis_pool: &RedisPool,
|
|
repo_id: uuid::Uuid,
|
|
block_timeout_secs: usize,
|
|
) -> Option<String> {
|
|
let (pending_key, processing_key) = ci_queue_keys(repo_id);
|
|
|
|
let redis = redis_pool.get().await.ok()?;
|
|
let mut conn: deadpool_redis::cluster::Connection = redis;
|
|
|
|
redis::cmd("BLMOVE")
|
|
.arg(&pending_key)
|
|
.arg(&processing_key)
|
|
.arg("RIGHT")
|
|
.arg("LEFT")
|
|
.arg(block_timeout_secs)
|
|
.query_async::<Option<String>>(&mut conn)
|
|
.await
|
|
.ok()
|
|
.flatten()
|
|
}
|
|
pub async fn ack_ci_task(
|
|
redis_pool: &RedisPool,
|
|
repo_id: uuid::Uuid,
|
|
task_json: &str,
|
|
) {
|
|
let (_, processing_key) = ci_queue_keys(repo_id);
|
|
|
|
let redis = match redis_pool.get().await {
|
|
Ok(c) => c,
|
|
Err(e) => {
|
|
tracing::warn!(error = %e, "CI ack: failed to get redis connection");
|
|
return;
|
|
}
|
|
};
|
|
let mut conn: deadpool_redis::cluster::Connection = redis;
|
|
|
|
if let Err(e) = redis::cmd("LREM")
|
|
.arg(&processing_key)
|
|
.arg(1)
|
|
.arg(task_json)
|
|
.query_async::<()>(&mut conn)
|
|
.await
|
|
{
|
|
tracing::warn!(error = %e, "CI ack: LREM failed");
|
|
}
|
|
}
|