From 1f59c2e585b9d78b12959247fbef0bde63e6ea0f Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Sun, 10 Mar 2024 21:48:05 +0800 Subject: [PATCH] feat: add pipeline_new_pr api --- server/src/api.rs | 103 +++++++++++++++++++++++++++++++++++++------ server/src/main.rs | 3 +- server/src/routes.rs | 16 +++++++ 3 files changed, 108 insertions(+), 14 deletions(-) diff --git a/server/src/api.rs b/server/src/api.rs index 8fcfd2c..38aad59 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -1,10 +1,12 @@ use crate::{ + github::get_packages_from_pr, job::get_crab_github_installation, models::{NewJob, NewPipeline, Pipeline}, ALL_ARCH, ARGS, }; +use anyhow::anyhow; use anyhow::Context; -use buildit_utils::github::update_abbs; +use buildit_utils::github::{get_archs, update_abbs}; use common::JobSource; use diesel::{ r2d2::{ConnectionManager, Pool}, @@ -15,23 +17,29 @@ use tracing::warn; pub async fn pipeline_new( pool: Pool>, git_branch: &str, + git_sha: Option<&str>, packages: &str, archs: &str, source: &JobSource, ) -> anyhow::Result { - // resolve branch name to commit hash - update_abbs(git_branch, &ARGS.abbs_path) - .await - .context("Failed to update ABBS tree")?; + // resolve branch name to commit hash if not specified + let git_sha = match git_sha { + Some(git_sha) => git_sha.to_string(), + None => { + update_abbs(git_branch, &ARGS.abbs_path) + .await + .context("Failed to update ABBS tree")?; - let output = tokio::process::Command::new("git") - .arg("rev-parse") - .arg("HEAD") - .current_dir(&ARGS.abbs_path) - .output() - .await - .context("Failed to resolve branch to git commit")?; - let git_sha = String::from_utf8_lossy(&output.stdout).trim().to_string(); + let output = tokio::process::Command::new("git") + .arg("rev-parse") + .arg("HEAD") + .current_dir(&ARGS.abbs_path) + .output() + .await + .context("Failed to resolve branch to git commit")?; + String::from_utf8_lossy(&output.stdout).trim().to_string() + } + }; // sanitize archs arg let mut archs: Vec<&str> = archs.split(",").collect(); @@ -40,6 +48,11 @@ pub async fn pipeline_new( archs.extend(ALL_ARCH.iter()); archs.retain(|arch| *arch != "mainline"); } + for arch in &archs { + if !ALL_ARCH.contains(arch) && arch != &"mainline" { + return Err(anyhow!("Architecture {arch} is not supported")); + } + } archs.sort(); archs.dedup(); @@ -121,3 +134,67 @@ pub async fn pipeline_new( Ok(pipeline.id) } + +pub async fn pipeline_new_pr( + pool: Pool>, + pr: u64, + archs: Option<&str>, +) -> anyhow::Result { + match octocrab::instance() + .pulls("AOSC-Dev", "aosc-os-abbs") + .get(pr) + .await + { + Ok(pr) => { + // If the pull request has been merged, + // build and push packages based on stable + let (git_branch, git_sha) = if pr.merged_at.is_some() { + ( + "stable", + pr.merge_commit_sha + .as_ref() + .context("merge_commit_sha should not be None")?, + ) + } else { + (pr.head.ref_field.as_str(), &pr.head.sha) + }; + + if pr.head.repo.as_ref().and_then(|x| x.fork).unwrap_or(false) { + return Err(anyhow!("Failed to create job: Pull request is a fork")); + } + + update_abbs(git_branch, &ARGS.abbs_path) + .await + .context("Failed to update ABBS tree")?; + + // find lines starting with #buildit + let packages = get_packages_from_pr(&pr); + if !packages.is_empty() { + let archs = if let Some(archs) = archs { + archs.to_string() + } else { + let path = &ARGS.abbs_path; + + get_archs(path, &packages).join(",") + }; + + pipeline_new( + pool, + git_branch, + Some(&git_sha), + &packages.join(","), + &archs, + &JobSource::Github(pr.number), + ) + .await + } else { + return Err(anyhow!( + "Please list packages to build in pr info starting with '#buildit'" + )); + } + } + Err(err) => { + return Err(anyhow!("Failed to get pr info: {err}")); + } + } +} diff --git a/server/src/main.rs b/server/src/main.rs index 47e2f58..8ba7a04 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -3,8 +3,8 @@ use axum::{routing::get, Router}; use diesel::pg::PgConnection; use diesel::r2d2::ConnectionManager; use diesel::r2d2::Pool; -use server::routes::ping; use server::routes::pipeline_new; +use server::routes::{ping, pipeline_new_pr}; use server::ARGS; use tower_http::services::{ServeDir, ServeFile}; @@ -25,6 +25,7 @@ async fn main() -> anyhow::Result<()> { let app = Router::new() .route("/api/ping", get(ping)) .route("/api/pipeline/new", post(pipeline_new)) + .route("/api/pipeline/new_pr", post(pipeline_new_pr)) .fallback_service(serve_dir) .with_state(pool) .layer(tower_http::trace::TraceLayer::new_for_http()); diff --git a/server/src/routes.rs b/server/src/routes.rs index 61f72fc..5118b49 100644 --- a/server/src/routes.rs +++ b/server/src/routes.rs @@ -52,6 +52,7 @@ pub async fn pipeline_new( let pipeline_id = api::pipeline_new( pool, &payload.git_branch, + None, &payload.packages, &payload.archs, &common::JobSource::Manual, @@ -59,3 +60,18 @@ pub async fn pipeline_new( .await?; Ok(Json(PipelineNewResponse { id: pipeline_id })) } + +#[derive(Deserialize)] +pub struct PipelineNewPRRequest { + pr: u64, + archs: Option, +} + +pub async fn pipeline_new_pr( + State(pool): State>>, + Json(payload): Json, +) -> Result, AnyhowError> { + let pipeline_id = + api::pipeline_new_pr(pool, payload.pr, payload.archs.as_ref().map(|s| s.as_str())).await?; + Ok(Json(PipelineNewResponse { id: pipeline_id })) +}