Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions ceres/src/merge_checker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,32 @@ pub enum CheckType {
CodeReview,
}

impl CheckType {
pub fn display_name(&self) -> &'static str {
match self {
CheckType::GpgSignature => "Gpg signature",
CheckType::BranchProtection => "Branch protection",
CheckType::CommitMessage => "Commit message",
CheckType::MrSync => "Mr sync",
CheckType::MergeConflict => "Merge conflict",
CheckType::CiStatus => "Ci status",
CheckType::CodeReview => "Code review",
}
}

pub fn description(&self) -> &'static str {
match self {
CheckType::GpgSignature => "Verify whether the commit has a valid GPG signature and the key is trusted",
CheckType::BranchProtection => "Ensure the merge target complies with branch protection policies, such as no direct merges to main and requiring squash or rebase",
CheckType::CommitMessage => "Verify whether the commit message follows Conventional Commits or the internal agreed-upon format",
CheckType::MrSync => "Ensure the MR is based on the latest commit of the target branch and determine whether a rebase is required",
CheckType::MergeConflict => "The pull request must not have any unresolved merge conflicts",
CheckType::CiStatus => "Verify that all required continuous integration pipelines have passed",
CheckType::CodeReview => "Ensure the required reviewers have approved the merge request",
}
}
}

impl From<CheckTypeEnum> for CheckType {
fn from(value: CheckTypeEnum) -> Self {
match value {
Expand Down
11 changes: 11 additions & 0 deletions jupiter/src/storage/mr_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,4 +298,15 @@ impl MrStorage {
.await?;
Ok(())
}

pub async fn get_check_result(
&self,
mr_link: &str,
) -> Result<Vec<check_result::Model>, MegaError> {
let models = check_result::Entity::find()
.filter(check_result::Column::MrLink.eq(mr_link))
.all(self.get_connection())
.await?;
Ok(models)
}
}
68 changes: 64 additions & 4 deletions mono/src/api/mr/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::str::FromStr;

use ceres::model::mr::MrDiffFile;
use ceres::{merge_checker::CheckType, model::mr::MrDiffFile};
use jupiter::model::mr_dto::MRDetails;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use utoipa::ToSchema;
use uuid::Uuid;

use crate::api::{conversation::ConversationItem, label::LabelItem};
use callisto::sea_orm_active_enums::MergeStatusEnum;
use callisto::{check_result, sea_orm_active_enums::MergeStatusEnum};
use common::model::CommonPage;
use neptune::model::diff_model::DiffItem;

Expand Down Expand Up @@ -131,7 +131,7 @@ impl From<MrDiffFile> for MrFilesRes {
}
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema)]
pub enum MergeStatus {
Open,
Merged,
Expand All @@ -157,3 +157,63 @@ impl From<MergeStatus> for MergeStatusEnum {
}
}
}

#[derive(Serialize, ToSchema)]
pub struct MergeBoxRes {
pub merge_requirements: Option<MergeRequirements>,
}

impl MergeBoxRes {
pub fn from_condition(conditions: Vec<Condition>) -> Self {
let mut state = RequirementsState::MERGEABLE;
for cond in &conditions {
if cond.result == ConditionResult::FAILED {
state = RequirementsState::UNMERGEABLE
}
}
MergeBoxRes {
merge_requirements: Some(MergeRequirements { conditions, state }),
}
}
}

#[derive(Serialize, ToSchema)]
pub struct MergeRequirements {
pub conditions: Vec<Condition>,
pub state: RequirementsState,
}

#[derive(Serialize, ToSchema)]
pub struct Condition {
#[serde(rename = "type")]
pub condition_type: CheckType,
pub display_name: String,
pub description: String,
pub message: String,
pub result: ConditionResult,
}

impl From<check_result::Model> for Condition {
fn from(value: check_result::Model) -> Self {
let check_type: CheckType = value.check_type_code.into();
Self {
condition_type: check_type.clone(),
display_name: check_type.clone().display_name().to_string(),
description: check_type.description().to_string(),
message: value.message,
result: ConditionResult::PASSED,
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema)]
pub enum RequirementsState {
UNMERGEABLE,
MERGEABLE,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema)]
pub enum ConditionResult {
FAILED,
PASSED,
}
46 changes: 44 additions & 2 deletions mono/src/api/mr/mr_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ use axum::{
extract::{Path, State},
Json,
};
use jupiter::service::mr_service::MRService;
use utoipa_axum::{router::OpenApiRouter, routes};

use callisto::sea_orm_active_enums::{ConvTypeEnum, MergeStatusEnum};
use common::{
errors::MegaError,
model::{CommonPage, CommonResult, PageParams},
};
use jupiter::service::mr_service::MRService;

use crate::api::{
api_common::{
Expand All @@ -22,7 +22,7 @@ use crate::api::{
conversation::ContentPayload,
issue::ItemRes,
label::LabelUpdatePayload,
mr::{FilesChangedList, MRDetailRes, MrFilesRes, MuiTreeNode},
mr::{Condition, FilesChangedList, MRDetailRes, MergeBoxRes, MrFilesRes, MuiTreeNode},
oauth::model::LoginUser,
};
use crate::api::{mr::FilesChangedPage, MonoApiServiceState};
Expand All @@ -35,6 +35,7 @@ pub fn routers() -> OpenApiRouter<MonoApiServiceState> {
.routes(routes!(fetch_mr_list))
.routes(routes!(mr_detail))
.routes(routes!(merge))
.routes(routes!(merge_box))
.routes(routes!(merge_no_auth))
.routes(routes!(close_mr))
.routes(routes!(reopen_mr))
Expand Down Expand Up @@ -359,6 +360,47 @@ async fn mr_files_list(
Ok(Json(CommonResult::success(Some(res))))
}

/// Get Merge Box to check merge status
#[utoipa::path(
get,
params(
("link", description = "MR link"),
),
path = "/{link}/merge-box",
responses(
(status = 200, body = CommonResult<MergeBoxRes>, content_type = "application/json")
),
tag = MR_TAG
)]
#[axum::debug_handler]
async fn merge_box(
Path(link): Path<String>,
state: State<MonoApiServiceState>,
) -> Result<Json<CommonResult<MergeBoxRes>>, ApiError> {
let mr = state
.mr_stg()
.get_mr(&link)
.await?
.ok_or(MegaError::with_message("MR Not Found"))?;

let res = match mr.status {
MergeStatusEnum::Open => {
let check_res: Vec<Condition> = state
.mr_stg()
.get_check_result(&link)
.await?
.into_iter()
.map(|m| m.into())
.collect();
MergeBoxRes::from_condition(check_res)
}
MergeStatusEnum::Merged | MergeStatusEnum::Closed => MergeBoxRes {
merge_requirements: None,
},
};
Ok(Json(CommonResult::success(Some(res))))
}

/// Add new comment on Merge Request
#[utoipa::path(
post,
Expand Down
2 changes: 1 addition & 1 deletion mono/src/server/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub async fn app(storage: Storage, host: String, port: u16) -> Router {
let origins: Vec<HeaderValue> = oauth_config
.allowed_cors_origins
.into_iter()
.map(|x| x.parse::<HeaderValue>().unwrap())
.map(|x| x.trim().parse::<HeaderValue>().unwrap())
.collect();

// add RequestDecompressionLayer for handle gzip encode
Expand Down
Loading
Loading