diff --git a/.github/workflows/orion-server-deploy.yml b/.github/workflows/orion-server-deploy.yml new file mode 100644 index 000000000..7bdcf33c0 --- /dev/null +++ b/.github/workflows/orion-server-deploy.yml @@ -0,0 +1,60 @@ +name: Orion server deploy +on: + push: + branches: + - main + paths: + - '.github/workflows/orion-server-deploy.yml' + - 'orion-server/**' + +env: + AWS_ECS_CLUSTER_NAME: ${{ secrets.AWS_ECS_CLUSTER_NAME }} + AWS_ECS_WEB_SERVICE_NAME: ${{ secrets.AWS_ECS_WEB_SERVICE_NAME }} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + orion-server-deploy: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.base.ref }} + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Login to Amazon ECR Public + id: login-ecr-public + uses: aws-actions/amazon-ecr-login@v2 + with: + registry-type: public + + - name: Build, tag, and push docker image to Amazon ECR Public + env: + REGISTRY: ${{ steps.login-ecr-public.outputs.registry }} + REGISTRY_ALIAS: m8q5m4u3 + REPOSITORY: mega + IMAGE_TAG: orion-server-0.1.0-pre-release + run: | + docker buildx build -t $REGISTRY/$REGISTRY_ALIAS/$REPOSITORY:$IMAGE_TAG -f orion-server/Dockerfile . + docker push $REGISTRY/$REGISTRY_ALIAS/$REPOSITORY:$IMAGE_TAG + + - name: Force ECS redeploy + run: | + aws ecs update-service \ + --cluster ${{ secrets.AWS_ECS_CLUSTER_NAME }} \ + --service orion-server-dev-service-om0caign \ + --force-new-deployment + env: + AWS_REGION: ap-southeast-2 + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/ceres/src/api_service/mono_api_service.rs b/ceres/src/api_service/mono_api_service.rs index f171dda77..3fe9ba0b7 100644 --- a/ceres/src/api_service/mono_api_service.rs +++ b/ceres/src/api_service/mono_api_service.rs @@ -20,6 +20,7 @@ use mercury::internal::object::tree::{Tree, TreeItem, TreeItemMode}; use crate::api_service::ApiHandler; use crate::model::git::CreateFileInfo; +use crate::model::mr::MrDiffFile; #[derive(Clone)] pub struct MonoApiService { @@ -421,6 +422,74 @@ impl MonoApiService { .await?; Ok(()) } + + pub async fn mr_files_list( + &self, + old_files: Vec<(PathBuf, SHA1)>, + new_files: Vec<(PathBuf, SHA1)>, + ) -> Result, MegaError> { + let old_files: HashMap = old_files.into_iter().collect(); + let new_files: HashMap = new_files.into_iter().collect(); + let unions: HashSet = old_files.keys().chain(new_files.keys()).cloned().collect(); + let mut res = vec![]; + for path in unions { + let old_hash = old_files.get(&path); + let new_hash = new_files.get(&path); + match (old_hash, new_hash) { + (None, None) => {} + (None, Some(new)) => res.push(MrDiffFile::New(path, *new)), + (Some(old), None) => res.push(MrDiffFile::Deleted(path, *old)), + (Some(old), Some(new)) => { + if old == new { + continue; + } else { + res.push(MrDiffFile::Modified(path, *old, *new)); + } + } + } + } + Ok(res) + } + + pub async fn get_commit_blobs( + &self, + commit_hash: &str, + ) -> Result, MegaError> { + let mut res = vec![]; + let mono_storage = self.storage.services.mono_storage.clone(); + let commit = mono_storage.get_commit_by_hash(commit_hash).await?; + if let Some(commit) = commit { + let tree = mono_storage.get_tree_by_hash(&commit.tree).await?; + if let Some(tree) = tree { + let tree: Tree = tree.into(); + res = self.traverse_tree(tree).await?; + } + } + Ok(res) + } + + async fn traverse_tree(&self, root_tree: Tree) -> Result, MegaError> { + let mut result = vec![]; + let mut stack = vec![(PathBuf::new(), root_tree)]; + + while let Some((base_path, tree)) = stack.pop() { + for item in tree.tree_items { + let path = base_path.join(&item.name); + if item.is_tree() { + let child = self + .storage + .mono_storage() + .get_tree_by_hash(&item.id.to_string()) + .await? + .unwrap(); + stack.push((path.clone(), child.into())); + } else { + result.push((path, item.id)); + } + } + } + Ok(result) + } } #[cfg(test)] @@ -428,7 +497,7 @@ mod test { use std::path::PathBuf; #[test] - pub fn test() { + pub fn test_path() { let mut full_path = PathBuf::from("/project/rust/mega"); for _ in 0..3 { let cloned_path = full_path.clone(); // Clone full_path diff --git a/ceres/src/model/mod.rs b/ceres/src/model/mod.rs index c2bf1c3ee..62f977232 100644 --- a/ceres/src/model/mod.rs +++ b/ceres/src/model/mod.rs @@ -1 +1,2 @@ pub mod git; +pub mod mr; \ No newline at end of file diff --git a/ceres/src/model/mr.rs b/ceres/src/model/mr.rs new file mode 100644 index 000000000..3b1f9c472 --- /dev/null +++ b/ceres/src/model/mr.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +use mercury::hash::SHA1; + +#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] +pub enum MrDiffFile { + New(PathBuf, SHA1), + Deleted(PathBuf, SHA1), + // path, old_hash, new_hash + Modified(PathBuf, SHA1, SHA1), +} diff --git a/mono/src/api/api_router.rs b/mono/src/api/api_router.rs index e93e4206d..65ed7ccb5 100644 --- a/mono/src/api/api_router.rs +++ b/mono/src/api/api_router.rs @@ -289,6 +289,18 @@ pub async fn get_blob_file( } } +// Get tree as file +#[utoipa::path( + get, + path = "/file/tree", + params( + TreeQuery + ), + responses( + (status = 200, body = CommonResult>, content_type = "application/json") + ), + tag = GIT_TAG +)] pub async fn get_tree_file( state: State, Query(query): Query, diff --git a/mono/src/api/mr/mod.rs b/mono/src/api/mr/mod.rs index 41155218c..2f37f523f 100644 --- a/mono/src/api/mr/mod.rs +++ b/mono/src/api/mr/mod.rs @@ -1,3 +1,6 @@ +use std::str::FromStr; + +use ceres::model::mr::MrDiffFile; use serde::{Deserialize, Serialize}; use callisto::{ @@ -106,3 +109,32 @@ impl MuiTreeNode { pub struct SaveCommentRequest { pub content: String, } + +#[derive(Serialize, ToSchema)] +pub struct MrFilesRes { + pub path: String, + pub sha: String, + pub action: String, +} + +impl From for MrFilesRes { + fn from(value: MrDiffFile) -> Self { + match value { + MrDiffFile::New(path, sha) => Self { + path: path.to_string_lossy().to_string(), + sha: sha.to_string(), + action: String::from_str("new").unwrap(), + }, + MrDiffFile::Deleted(path, sha) => Self { + path: path.to_string_lossy().to_string(), + sha: sha.to_string(), + action: String::from_str("deleted").unwrap(), + }, + MrDiffFile::Modified(path, _, new) => Self { + path: path.to_string_lossy().to_string(), + sha: new.to_string(), + action: String::from_str("modified").unwrap(), + }, + } + } +} diff --git a/mono/src/api/mr/mr_router.rs b/mono/src/api/mr/mr_router.rs index 96ed84324..cfe417d97 100644 --- a/mono/src/api/mr/mr_router.rs +++ b/mono/src/api/mr/mr_router.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf}; use axum::{ extract::{Path, State}, @@ -14,10 +14,12 @@ use common::{ use saturn::ActionEnum; use crate::api::{ - api_common, - api_common::model::{AssigneeUpdatePayload, LabelUpdatePayload, ListPayload}, + api_common::{ + self, + model::{AssigneeUpdatePayload, LabelUpdatePayload, ListPayload}, + }, issue::ItemRes, - mr::{FilesChangedList, MRDetail, MuiTreeNode, SaveCommentRequest}, + mr::{FilesChangedList, MRDetail, MrFilesRes, MuiTreeNode, SaveCommentRequest}, oauth::model::LoginUser, }; use crate::api::{util, MonoApiServiceState}; @@ -33,6 +35,7 @@ pub fn routers() -> OpenApiRouter { .routes(routes!(close_mr)) .routes(routes!(reopen_mr)) .routes(routes!(mr_files_changed)) + .routes(routes!(mr_files_list)) .routes(routes!(save_comment)) .routes(routes!(delete_comment)) .routes(routes!(labels)) @@ -247,6 +250,45 @@ async fn mr_files_changed( Ok(Json(res)) } +/// Get Merge Request file list +#[utoipa::path( + get, + params( + ("link", description = "MR link"), + ), + path = "/{link}/files-list", + responses( + (status = 200, body = CommonResult>, content_type = "application/json") + ), + tag = MR_TAG +)] +async fn mr_files_list( + Path(link): Path, + state: State, +) -> Result>>, ApiError> { + let mr = state + .mr_stg() + .get_mr(&link) + .await? + .ok_or(MegaError::with_message("MR Not Found"))?; + + let stg = state.monorepo(); + let old_files = stg.get_commit_blobs(&mr.from_hash).await?; + let new_files = stg.get_commit_blobs(&mr.to_hash).await?; + let mr_diff_files = stg.mr_files_list(old_files, new_files.clone()).await?; + + let mr_base = PathBuf::from(mr.path); + let res = mr_diff_files + .into_iter() + .map(|m| { + let mut item: MrFilesRes = m.into(); + item.path = mr_base.join(item.path).to_string_lossy().to_string(); + item + }) + .collect::>(); + Ok(Json(CommonResult::success(Some(res)))) +} + /// Add new comment on Merge Request #[utoipa::path( post,