refactor(vcs/generic): promote Gerrit checks as the generic variant
This commit is incomplete as we did not generalize enough the commit status states. We are not able to describe yet failure or more complicated cases. Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
This commit is contained in:
parent
7e84133ad0
commit
a915b664fb
13 changed files with 321 additions and 278 deletions
|
@ -4,15 +4,14 @@ pub mod stdenvs;
|
|||
pub use self::nixpkgs::NixpkgsStrategy;
|
||||
pub use self::stdenvs::Stdenvs;
|
||||
use crate::message::buildjob::BuildJob;
|
||||
use crate::vcs::commit_status::CommitStatusError;
|
||||
use crate::vcs::generic::CheckRunOptions;
|
||||
use crate::vcs::generic::{CheckRun, CommitStatusError};
|
||||
|
||||
pub type StepResult<T> = Result<T, Error>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EvaluationComplete {
|
||||
pub builds: Vec<BuildJob>,
|
||||
pub checks: Vec<CheckRunOptions>,
|
||||
pub checks: Vec<CheckRun>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -10,9 +10,8 @@ use crate::nixenv::HydraNixEnv;
|
|||
use crate::outpathdiff::{OutPathDiff, PackageArch};
|
||||
use crate::tagger::{MaintainerPrTagger, PkgsAddedRemovedTagger, RebuildTagger, StdenvTagger};
|
||||
use crate::tasks::eval::{stdenvs::Stdenvs, Error, EvaluationComplete, StepResult};
|
||||
use crate::vcs::commit_status::CommitStatus;
|
||||
use crate::vcs::generic::{
|
||||
CheckRunOptions, CheckRunState, Conclusion, State, VersionControlSystemAPI,
|
||||
CheckResult, CheckRun, CheckRunState, CommitStatus, VersionControlSystemAPI,
|
||||
};
|
||||
|
||||
use std::path::Path;
|
||||
|
@ -196,28 +195,37 @@ impl<'a> NixpkgsStrategy<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn performance_stats(&self) -> Vec<CheckRunOptions> {
|
||||
fn performance_stats(&self) -> Vec<CheckRun> {
|
||||
if let Some(ref rebuildsniff) = self.outpath_diff {
|
||||
if let Some(_report) = rebuildsniff.performance_diff() {
|
||||
return vec![CheckRunOptions {
|
||||
name: "Evaluation Performance Report".to_owned(),
|
||||
completed_at: Some(
|
||||
if let Some(report) = rebuildsniff.performance_diff() {
|
||||
return vec![CheckRun {
|
||||
check_name: "Evaluation Performance Report".to_owned(),
|
||||
finished_timestamp: Some(
|
||||
Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
|
||||
),
|
||||
started_at: None,
|
||||
conclusion: Some(Conclusion::Success),
|
||||
status: Some(CheckRunState::Completed),
|
||||
details_url: None,
|
||||
started_timestamp: None,
|
||||
scheduled_timestamp: None,
|
||||
label_name: Some("Performance-Impact".to_owned()),
|
||||
status_description: Some("Succeded.".to_owned()),
|
||||
status: CheckRunState::Completed,
|
||||
external_id: None,
|
||||
head_sha: self.job.change.head_sha.clone(),
|
||||
// FIXME: before going into production, let's reintroduce this as a pastebin?
|
||||
// output: Some(Output {
|
||||
// title: "Evaluator Performance Report".to_string(),
|
||||
// summary: "".to_string(),
|
||||
// text: Some(report.markdown()),
|
||||
// annotations: None,
|
||||
// images: None,
|
||||
// }),
|
||||
results: vec![CheckResult {
|
||||
external_id: None,
|
||||
category: crate::vcs::generic::Category::Info,
|
||||
summary: String::new(),
|
||||
message: Some(report.markdown()),
|
||||
tags: vec![],
|
||||
links: vec![],
|
||||
code_pointers: vec![],
|
||||
}],
|
||||
change: None,
|
||||
patchset: None,
|
||||
attempt: None,
|
||||
check_description: None,
|
||||
check_link: None,
|
||||
is_ai_powered: Some(false),
|
||||
status_link: None,
|
||||
// FIXME: head_sha: self.job.change.head_sha.clone(),
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
@ -316,7 +324,7 @@ impl<'a> NixpkgsStrategy<'a> {
|
|||
String::from("large change, skipping automatic review requests"),
|
||||
gist_url,
|
||||
);
|
||||
status.set(State::Success).await?;
|
||||
status.set(CheckRunState::Completed).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -328,7 +336,7 @@ impl<'a> NixpkgsStrategy<'a> {
|
|||
String::from("matching changed paths to changed attrs..."),
|
||||
gist_url,
|
||||
);
|
||||
status.set(State::Success).await?;
|
||||
status.set(CheckRunState::Completed).await?;
|
||||
|
||||
if let Ok(ref maint) = m {
|
||||
self.request_reviews(maint).await;
|
||||
|
@ -359,7 +367,7 @@ impl<'a> NixpkgsStrategy<'a> {
|
|||
String::from("config.nix: checkMeta = true"),
|
||||
None,
|
||||
);
|
||||
status.set(State::Pending).await?;
|
||||
status.set(CheckRunState::Completed).await?;
|
||||
|
||||
let nixenv = HydraNixEnv::new(self.nix.clone(), dir.to_path_buf(), true);
|
||||
match nixenv.execute_with_stats().await {
|
||||
|
@ -374,7 +382,7 @@ impl<'a> NixpkgsStrategy<'a> {
|
|||
try_build.dedup();
|
||||
|
||||
status.set_url(None);
|
||||
status.set(State::Success).await?;
|
||||
status.set(CheckRunState::Completed).await?;
|
||||
|
||||
if !try_build.is_empty() && try_build.len() <= 20 {
|
||||
// In the case of trying to merge master in to
|
||||
|
@ -405,7 +413,7 @@ impl<'a> NixpkgsStrategy<'a> {
|
|||
.ok()
|
||||
.map(|pp| pp.uri),
|
||||
);
|
||||
status.set(State::Failure).await?;
|
||||
// status.set(State::Failure).await?;
|
||||
Err(Error::Fail(String::from(
|
||||
"Failed to validate package metadata.",
|
||||
)))
|
||||
|
@ -427,12 +435,12 @@ impl<'a> NixpkgsStrategy<'a> {
|
|||
status: &mut CommitStatus,
|
||||
) -> StepResult<()> {
|
||||
status
|
||||
.set_with_description("Checking original stdenvs", State::Pending)
|
||||
.set_with_description("Checking original stdenvs", CheckRunState::Scheduled)
|
||||
.await?;
|
||||
self.check_stdenvs_before(dir).await;
|
||||
|
||||
status
|
||||
.set_with_description("Checking original out paths", State::Pending)
|
||||
.set_with_description("Checking original out paths", CheckRunState::Scheduled)
|
||||
.await?;
|
||||
self.check_outpaths_before(dir).await?;
|
||||
|
||||
|
@ -461,12 +469,12 @@ impl<'a> NixpkgsStrategy<'a> {
|
|||
.await;
|
||||
|
||||
status
|
||||
.set_with_description("Checking new stdenvs", State::Pending)
|
||||
.set_with_description("Checking new stdenvs", CheckRunState::Scheduled)
|
||||
.await?;
|
||||
self.check_stdenvs_after().await;
|
||||
|
||||
status
|
||||
.set_with_description("Checking new out paths", State::Pending)
|
||||
.set_with_description("Checking new out paths", CheckRunState::Scheduled)
|
||||
.await?;
|
||||
self.check_outpaths_after().await?;
|
||||
|
||||
|
@ -613,7 +621,7 @@ impl<'a> NixpkgsStrategy<'a> {
|
|||
self.update_stdenv_labels().await;
|
||||
|
||||
status
|
||||
.set_with_description("Calculating Changed Outputs", State::Pending)
|
||||
.set_with_description("Calculating Changed Outputs", CheckRunState::Scheduled)
|
||||
.await?;
|
||||
|
||||
self.update_new_package_labels().await;
|
||||
|
|
|
@ -9,8 +9,10 @@ use crate::stats::{self, Event};
|
|||
use crate::systems;
|
||||
use crate::tasks::eval;
|
||||
use crate::utils::pastebin::PersistedPastebin;
|
||||
use crate::vcs::commit_status::{CommitStatus, CommitStatusError};
|
||||
use crate::vcs::generic::{Issue, IssueState, State, VersionControlSystemAPI};
|
||||
use crate::vcs::generic::{
|
||||
AugmentedVCSApi, CheckRunState, CommitStatus, CommitStatusError, Issue, IssueState,
|
||||
VersionControlSystemAPI,
|
||||
};
|
||||
use crate::vcs::gerrit::http::GerritHTTPApi;
|
||||
use crate::worker;
|
||||
|
||||
|
@ -93,7 +95,11 @@ impl<E: stats::SysEvents + 'static + Sync + Send> worker::SimpleWorker for Evalu
|
|||
let _enter = span.enter();
|
||||
|
||||
let vcs_api: Arc<dyn VersionControlSystemAPI> = match self.vcs {
|
||||
SupportedVCS::Gerrit => Arc::new(GerritHTTPApi),
|
||||
// TODO: make it easier to build an augmented vcs api handle.
|
||||
SupportedVCS::Gerrit => Arc::new(AugmentedVCSApi {
|
||||
minimal_api: GerritHTTPApi,
|
||||
statcheck_api: crate::vcs::generic::http::StatcheckHTTPApi,
|
||||
}),
|
||||
};
|
||||
|
||||
OneEval::new(
|
||||
|
@ -146,7 +152,7 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
&self,
|
||||
description: String,
|
||||
url: Option<String>,
|
||||
state: State,
|
||||
state: CheckRunState,
|
||||
) -> Result<(), CommitStatusError> {
|
||||
let description = if description.len() >= 140 {
|
||||
warn!(
|
||||
|
@ -192,15 +198,17 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
Ok(r) => Ok(r),
|
||||
// Handle error cases which expect us to post statuses
|
||||
// to github. Convert Eval Errors in to Result<_, CommitStatusWrite>
|
||||
Err(EvalWorkerError::EvalError(eval::Error::Fail(msg))) => {
|
||||
Err(self.update_status(msg, None, State::Failure).await)
|
||||
}
|
||||
Err(EvalWorkerError::EvalError(eval::Error::Fail(msg))) => Err(self
|
||||
.update_status(msg, None, CheckRunState::Completed)
|
||||
.await),
|
||||
Err(EvalWorkerError::EvalError(eval::Error::FailWithPastebin(msg, title, content))) => {
|
||||
let pastebin = self
|
||||
.make_pastebin(chan, &title, content)
|
||||
.await
|
||||
.map(|pp| pp.uri);
|
||||
Err(self.update_status(msg, pastebin, State::Failure).await)
|
||||
Err(self
|
||||
.update_status(msg, pastebin, CheckRunState::Completed)
|
||||
.await)
|
||||
}
|
||||
Err(
|
||||
EvalWorkerError::EvalError(eval::Error::CommitStatusWrite(e))
|
||||
|
@ -307,7 +315,7 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
);
|
||||
|
||||
overall_status
|
||||
.set_with_description("Starting", State::Pending)
|
||||
.set_with_description("Starting", CheckRunState::Running)
|
||||
.await?;
|
||||
|
||||
evaluation_strategy.pre_clone().await?;
|
||||
|
@ -317,7 +325,7 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
.project(&job.repo.full_name, job.repo.clone_url.clone());
|
||||
|
||||
overall_status
|
||||
.set_with_description("Cloning project", State::Pending)
|
||||
.set_with_description("Cloning project", CheckRunState::Running)
|
||||
.await?;
|
||||
|
||||
info!("Working on {}", job.change.number);
|
||||
|
@ -337,7 +345,7 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
.set_with_description(
|
||||
"The branch you have targeted is a read-only mirror for channels. \
|
||||
Please target release-* or master.",
|
||||
State::Error,
|
||||
CheckRunState::Completed,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -348,7 +356,7 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
overall_status
|
||||
.set_with_description(
|
||||
format!("Checking out {}", &target_branch).as_ref(),
|
||||
State::Pending,
|
||||
CheckRunState::Running,
|
||||
)
|
||||
.await?;
|
||||
info!("Checking out target branch {}", &target_branch);
|
||||
|
@ -371,14 +379,14 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
.await;
|
||||
|
||||
overall_status
|
||||
.set_with_description("Fetching PR", State::Pending)
|
||||
.set_with_description("Fetching PR", CheckRunState::Running)
|
||||
.await?;
|
||||
|
||||
co.fetch_change(&job.change).unwrap();
|
||||
|
||||
if !co.commit_exists(job.change.head_sha.as_ref()) {
|
||||
overall_status
|
||||
.set_with_description("Commit not found", State::Error)
|
||||
.set_with_description("Commit not found", CheckRunState::Running)
|
||||
.await?;
|
||||
|
||||
info!("Commit {} doesn't exist", job.change.head_sha);
|
||||
|
@ -388,12 +396,12 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
evaluation_strategy.after_fetch(&co);
|
||||
|
||||
overall_status
|
||||
.set_with_description("Merging PR", State::Pending)
|
||||
.set_with_description("Merging PR", CheckRunState::Running)
|
||||
.await?;
|
||||
|
||||
if co.merge_commit(job.change.head_sha.as_ref()).is_err() {
|
||||
overall_status
|
||||
.set_with_description("Failed to merge", State::Failure)
|
||||
.set_with_description("Failed to merge", CheckRunState::Running)
|
||||
.await?;
|
||||
|
||||
info!("Failed to merge {}", job.change.head_sha);
|
||||
|
@ -407,7 +415,7 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
|
||||
info!("Got path: {:?}, building", refpath);
|
||||
overall_status
|
||||
.set_with_description("Beginning Evaluations", State::Pending)
|
||||
.set_with_description("Beginning Evaluations", CheckRunState::Running)
|
||||
.await?;
|
||||
|
||||
let mut all_good = true;
|
||||
|
@ -422,19 +430,19 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
);
|
||||
|
||||
status
|
||||
.set(State::Pending)
|
||||
.set(CheckRunState::Running)
|
||||
.await
|
||||
.expect("Failed to set status on eval strategy");
|
||||
|
||||
let state: State;
|
||||
let state: CheckRunState;
|
||||
let gist_url: Option<String>;
|
||||
match check.execute(Path::new(&refpath)).await {
|
||||
Ok(_) => {
|
||||
state = State::Success;
|
||||
state = CheckRunState::Completed;
|
||||
gist_url = None;
|
||||
}
|
||||
Err(mut out) => {
|
||||
state = State::Failure;
|
||||
state = CheckRunState::Completed;
|
||||
gist_url = self
|
||||
.make_pastebin(
|
||||
chan,
|
||||
|
@ -452,7 +460,7 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
.await
|
||||
.expect("Failed to set status on eval strategy");
|
||||
|
||||
if state != State::Success {
|
||||
if state != CheckRunState::Completed {
|
||||
all_good = false;
|
||||
}
|
||||
}
|
||||
|
@ -471,11 +479,11 @@ impl<'a, E: stats::SysEvents + 'static> OneEval<'a, E> {
|
|||
response.extend(schedule_builds(complete.builds, &auto_schedule_build_archs));
|
||||
|
||||
overall_status
|
||||
.set_with_description("^.^!", State::Success)
|
||||
.set_with_description("^.^!", CheckRunState::Completed)
|
||||
.await?;
|
||||
} else {
|
||||
overall_status
|
||||
.set_with_description("Complete, with errors", State::Failure)
|
||||
.set_with_description("Complete, with errors", CheckRunState::Completed)
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
//! Set of generic structures to abstract over a VCS in a richful way.
|
||||
//! Not all VCS can represent the full set of states, so implementations
|
||||
//! will have to downgrade richer values to the closest representation.
|
||||
//!
|
||||
//! Gerrit is the first-class supported model.
|
||||
use futures_util::future::BoxFuture;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::message::{Change, Repo};
|
||||
|
||||
use super::commit_status::CommitStatusError;
|
||||
|
||||
pub enum IssueState {
|
||||
Open,
|
||||
Closed,
|
||||
}
|
||||
|
||||
pub struct Account {
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
pub struct Issue {
|
||||
pub title: String,
|
||||
pub number: u64,
|
||||
pub repo: Repo,
|
||||
pub state: IssueState,
|
||||
pub created_by: Account,
|
||||
}
|
||||
|
||||
pub struct Repository {}
|
||||
pub struct ChangeReviewers {
|
||||
pub entity_reviewers: Vec<String>,
|
||||
pub team_reviewers: Vec<String>,
|
||||
}
|
||||
|
||||
impl Issue {
|
||||
#[must_use]
|
||||
pub fn is_wip(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VersionControlSystemAPI: Sync + Send {
|
||||
fn get_repository(&self, repo: &crate::message::Repo) -> Repository;
|
||||
fn get_changes(&self, repo: &crate::message::Repo) -> BoxFuture<Vec<Change>>;
|
||||
fn get_change(&self, repo: &crate::message::Repo, number: u64) -> BoxFuture<Option<Change>>;
|
||||
fn get_issue(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
) -> BoxFuture<Result<Issue, String>>;
|
||||
fn update_labels(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
add: &[String],
|
||||
remove: &[String],
|
||||
) -> BoxFuture<()>;
|
||||
fn get_existing_reviewers(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
) -> BoxFuture<ChangeReviewers>;
|
||||
fn request_reviewers(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
entity_reviewers: Vec<String>,
|
||||
team_reviewers: Vec<String>,
|
||||
) -> BoxFuture<()>;
|
||||
fn create_commit_statuses(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
sha: String,
|
||||
state: State,
|
||||
context: String,
|
||||
description: String,
|
||||
target_url: String,
|
||||
) -> BoxFuture<Result<(), CommitStatusError>>;
|
||||
fn create_check_statuses(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
checks: Vec<CheckRunOptions>,
|
||||
) -> BoxFuture<()>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CheckRunState {
|
||||
Runnable,
|
||||
Running,
|
||||
Scheduled,
|
||||
Completed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum State {
|
||||
Pending,
|
||||
Error,
|
||||
Failure,
|
||||
Success,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Conclusion {
|
||||
Skipped,
|
||||
Success,
|
||||
Failure,
|
||||
Neutral,
|
||||
Cancelled,
|
||||
TimedOut,
|
||||
ActionRequired,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
pub struct CheckRunOptions {
|
||||
pub name: String,
|
||||
pub head_sha: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub details_url: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub external_id: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub status: Option<CheckRunState>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub started_at: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub conclusion: Option<Conclusion>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub completed_at: Option<String>,
|
||||
}
|
173
ofborg/src/vcs/generic/api.rs
Normal file
173
ofborg/src/vcs/generic/api.rs
Normal file
|
@ -0,0 +1,173 @@
|
|||
//! Set of generic structures to abstract over a VCS in a richful way.
|
||||
//! Not all VCS can represent the full set of states, so implementations
|
||||
//! will have to downgrade richer values to the closest representation.
|
||||
//!
|
||||
//! Gerrit is the first-class supported model.
|
||||
use futures_util::future::BoxFuture;
|
||||
|
||||
use crate::message::{Change, Repo};
|
||||
|
||||
use super::{commit_status::CommitStatusError, http::StatcheckHTTPApi, CheckRun, CheckRunState};
|
||||
|
||||
pub enum IssueState {
|
||||
Open,
|
||||
Closed,
|
||||
}
|
||||
|
||||
pub struct Account {
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
pub struct Issue {
|
||||
pub title: String,
|
||||
pub number: u64,
|
||||
pub repo: Repo,
|
||||
pub state: IssueState,
|
||||
pub created_by: Account,
|
||||
}
|
||||
|
||||
pub struct Repository {}
|
||||
pub struct ChangeReviewers {
|
||||
pub entity_reviewers: Vec<String>,
|
||||
pub team_reviewers: Vec<String>,
|
||||
}
|
||||
|
||||
impl Issue {
|
||||
#[must_use]
|
||||
pub fn is_wip(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MinimalVersionControlSystemAPI: Sync + Send {
|
||||
fn get_repository(&self, repo: &crate::message::Repo) -> Repository;
|
||||
fn get_changes(&self, repo: &crate::message::Repo) -> BoxFuture<Vec<Change>>;
|
||||
fn get_change(&self, repo: &crate::message::Repo, number: u64) -> BoxFuture<Option<Change>>;
|
||||
fn get_issue(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
) -> BoxFuture<Result<Issue, String>>;
|
||||
fn update_labels(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
add: &[String],
|
||||
remove: &[String],
|
||||
) -> BoxFuture<()>;
|
||||
fn get_existing_reviewers(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
) -> BoxFuture<ChangeReviewers>;
|
||||
fn request_reviewers(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
entity_reviewers: Vec<String>,
|
||||
team_reviewers: Vec<String>,
|
||||
) -> BoxFuture<()>;
|
||||
}
|
||||
|
||||
pub trait VersionControlSystemAPI: Sync + Send + MinimalVersionControlSystemAPI {
|
||||
fn create_commit_statuses(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
sha: String,
|
||||
state: CheckRunState,
|
||||
context: String,
|
||||
description: String,
|
||||
target_url: String,
|
||||
) -> BoxFuture<Result<(), CommitStatusError>>;
|
||||
fn create_check_statuses(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
checks: Vec<CheckRun>,
|
||||
) -> BoxFuture<()>;
|
||||
}
|
||||
|
||||
/// This is an augmented VCS API expanded by our internal status & checks API server.
|
||||
/// This means that a VCS does not need to implement more than the basics to benefit automatically
|
||||
/// from our own status & checks server.
|
||||
/// You can still implement replication from our custom status & checks to your original VCS if you
|
||||
/// want.
|
||||
pub struct AugmentedVCSApi<A: MinimalVersionControlSystemAPI> {
|
||||
pub minimal_api: A,
|
||||
pub statcheck_api: StatcheckHTTPApi,
|
||||
}
|
||||
|
||||
/// This is a forwarder implementation to `minimal_api`.
|
||||
impl<A: MinimalVersionControlSystemAPI> MinimalVersionControlSystemAPI for AugmentedVCSApi<A> {
|
||||
fn get_issue(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
) -> BoxFuture<Result<Issue, String>> {
|
||||
self.minimal_api.get_issue(repo, number)
|
||||
}
|
||||
|
||||
fn get_change(&self, repo: &crate::message::Repo, number: u64) -> BoxFuture<Option<Change>> {
|
||||
self.minimal_api.get_change(repo, number)
|
||||
}
|
||||
|
||||
fn get_changes(&self, repo: &crate::message::Repo) -> BoxFuture<Vec<Change>> {
|
||||
self.minimal_api.get_changes(repo)
|
||||
}
|
||||
|
||||
fn get_repository(&self, repo: &crate::message::Repo) -> Repository {
|
||||
self.minimal_api.get_repository(repo)
|
||||
}
|
||||
|
||||
fn update_labels(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
add: &[String],
|
||||
remove: &[String],
|
||||
) -> BoxFuture<()> {
|
||||
self.minimal_api.update_labels(repo, number, add, remove)
|
||||
}
|
||||
|
||||
fn get_existing_reviewers(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
) -> BoxFuture<ChangeReviewers> {
|
||||
self.minimal_api.get_existing_reviewers(repo, number)
|
||||
}
|
||||
|
||||
fn request_reviewers(
|
||||
&self,
|
||||
repo: &crate::message::Repo,
|
||||
number: u64,
|
||||
entity_reviewers: Vec<String>,
|
||||
team_reviewers: Vec<String>,
|
||||
) -> BoxFuture<()> {
|
||||
self.minimal_api
|
||||
.request_reviewers(repo, number, entity_reviewers, team_reviewers)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: MinimalVersionControlSystemAPI> VersionControlSystemAPI for AugmentedVCSApi<A> {
|
||||
fn create_check_statuses(
|
||||
&self,
|
||||
_repo: &crate::message::Repo,
|
||||
_checks: Vec<CheckRun>,
|
||||
) -> BoxFuture<()> {
|
||||
// Create all checks in parallel.
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn create_commit_statuses(
|
||||
&self,
|
||||
_repo: &crate::message::Repo,
|
||||
_sha: String,
|
||||
_state: CheckRunState,
|
||||
_context: String,
|
||||
_description: String,
|
||||
_target_url: String,
|
||||
) -> BoxFuture<Result<(), CommitStatusError>> {
|
||||
// Create the commit status.
|
||||
todo!();
|
||||
}
|
||||
}
|
|
@ -1,94 +1,74 @@
|
|||
use crate::vcs::generic::CheckRunState;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Gerrit is used as the generic model for CI checks.
|
||||
/// Downgrading those to your VCS is possible.
|
||||
/// If your VCS is richer than Gerrit, feel free to discuss how to extend this.
|
||||
/// Port from <https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/api/checks.ts>
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
enum RunStatus {
|
||||
pub enum CheckRunState {
|
||||
Runnable,
|
||||
Running,
|
||||
Scheduled,
|
||||
Completed,
|
||||
}
|
||||
|
||||
impl From<RunStatus> for CheckRunState {
|
||||
fn from(value: RunStatus) -> Self {
|
||||
match value {
|
||||
RunStatus::Runnable => CheckRunState::Runnable,
|
||||
RunStatus::Running => CheckRunState::Running,
|
||||
RunStatus::Scheduled => CheckRunState::Scheduled,
|
||||
RunStatus::Completed => CheckRunState::Completed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CheckRunState> for RunStatus {
|
||||
fn from(value: CheckRunState) -> Self {
|
||||
match value {
|
||||
CheckRunState::Runnable => Self::Runnable,
|
||||
CheckRunState::Running => Self::Running,
|
||||
CheckRunState::Scheduled => Self::Scheduled,
|
||||
CheckRunState::Completed => Self::Completed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
struct CheckRun {
|
||||
pub struct CheckRun {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
change: Option<u64>,
|
||||
pub change: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
patchset: Option<u64>,
|
||||
pub patchset: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
attempt: Option<u64>,
|
||||
pub attempt: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
external_id: Option<String>,
|
||||
check_name: String,
|
||||
pub external_id: Option<String>,
|
||||
pub check_name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
check_description: Option<String>,
|
||||
pub check_description: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
check_link: Option<String>,
|
||||
pub check_link: Option<String>,
|
||||
// defaults to false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
is_ai_powered: Option<bool>,
|
||||
status: RunStatus,
|
||||
pub is_ai_powered: Option<bool>,
|
||||
pub status: CheckRunState,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
status_description: Option<String>,
|
||||
pub status_description: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
status_link: Option<String>,
|
||||
pub status_link: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
label_name: Option<String>,
|
||||
pub label_name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
scheduled_timestamp: Option<String>,
|
||||
pub scheduled_timestamp: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
started_timestamp: Option<String>,
|
||||
pub started_timestamp: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
finished_timestamp: Option<String>,
|
||||
pub finished_timestamp: Option<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
results: Vec<CheckResult>,
|
||||
pub results: Vec<CheckResult>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
struct CheckResult {
|
||||
pub struct CheckResult {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
external_id: Option<String>,
|
||||
category: Category,
|
||||
summary: String,
|
||||
pub external_id: Option<String>,
|
||||
pub category: Category,
|
||||
pub summary: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
message: Option<String>,
|
||||
pub message: Option<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
tags: Vec<Tag>,
|
||||
pub tags: Vec<Tag>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
links: Vec<Link>,
|
||||
pub links: Vec<Link>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
code_pointers: Vec<CodePointer>,
|
||||
pub code_pointers: Vec<CodePointer>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
enum Category {
|
||||
pub enum Category {
|
||||
Success,
|
||||
Info,
|
||||
Warning,
|
||||
|
@ -97,7 +77,7 @@ enum Category {
|
|||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
enum TagColor {
|
||||
pub enum TagColor {
|
||||
Gray,
|
||||
Yellow,
|
||||
Pink,
|
||||
|
@ -107,7 +87,7 @@ enum TagColor {
|
|||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
struct Tag {
|
||||
pub struct Tag {
|
||||
name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
tooltip: Option<String>,
|
||||
|
@ -116,7 +96,7 @@ struct Tag {
|
|||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
struct Link {
|
||||
pub struct Link {
|
||||
url: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
tooltip: Option<String>,
|
||||
|
@ -125,14 +105,14 @@ struct Link {
|
|||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
struct CodePointer {
|
||||
pub struct CodePointer {
|
||||
path: String,
|
||||
range: CommentRange,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
enum LinkIcon {
|
||||
pub enum LinkIcon {
|
||||
External,
|
||||
Image,
|
||||
History,
|
||||
|
@ -147,7 +127,7 @@ enum LinkIcon {
|
|||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
struct CommentRange {
|
||||
pub struct CommentRange {
|
||||
// 1-based
|
||||
start_line: u64,
|
||||
// 0-based
|
|
@ -2,9 +2,7 @@ use std::sync::Arc;
|
|||
|
||||
use tracing::warn;
|
||||
|
||||
use crate::vcs::generic::State;
|
||||
|
||||
use super::generic::VersionControlSystemAPI;
|
||||
use super::{CheckRunState, VersionControlSystemAPI};
|
||||
|
||||
pub struct CommitStatus {
|
||||
api: Arc<dyn VersionControlSystemAPI>,
|
||||
|
@ -45,7 +43,7 @@ impl CommitStatus {
|
|||
pub async fn set_with_description(
|
||||
&mut self,
|
||||
description: &str,
|
||||
state: State,
|
||||
state: CheckRunState,
|
||||
) -> Result<(), CommitStatusError> {
|
||||
self.set_description(description.to_owned());
|
||||
self.set(state).await
|
||||
|
@ -55,7 +53,7 @@ impl CommitStatus {
|
|||
self.description = description;
|
||||
}
|
||||
|
||||
pub async fn set(&self, state: State) -> Result<(), CommitStatusError> {
|
||||
pub async fn set(&self, state: CheckRunState) -> Result<(), CommitStatusError> {
|
||||
let desc = if self.description.len() >= 140 {
|
||||
warn!(
|
||||
"description is over 140 char; truncating: {:?}",
|
18
ofborg/src/vcs/generic/http.rs
Normal file
18
ofborg/src/vcs/generic/http.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
//! REST API bindings for our custom status & checks API server
|
||||
//! It is tailored for Gerrit, this is on purpose. Gerrit is the most featureful VCS supported.
|
||||
|
||||
use super::{CheckResult, CheckRun};
|
||||
|
||||
pub struct StatcheckHTTPApi;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(clippy::unused_async)] // FIXME
|
||||
impl StatcheckHTTPApi {
|
||||
pub(crate) async fn create_new_check(&self, _check: CheckRun) -> Option<CheckRun> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub(crate) async fn update_result(&self, _result: CheckResult) -> Option<()> {
|
||||
todo!();
|
||||
}
|
||||
}
|
8
ofborg/src/vcs/generic/mod.rs
Normal file
8
ofborg/src/vcs/generic/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
pub mod api;
|
||||
pub mod checks;
|
||||
pub mod commit_status;
|
||||
pub mod http;
|
||||
|
||||
pub use api::*;
|
||||
pub use checks::*;
|
||||
pub use commit_status::*;
|
6
ofborg/src/vcs/gerrit/client.rs
Normal file
6
ofborg/src/vcs/gerrit/client.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use super::{http::GerritHTTPApi, ssh::GerritSSHApi};
|
||||
|
||||
pub struct GerritClient {
|
||||
http_api: GerritHTTPApi,
|
||||
ssh_api: GerritSSHApi,
|
||||
}
|
|
@ -3,37 +3,16 @@
|
|||
|
||||
use futures_util::FutureExt;
|
||||
|
||||
use crate::vcs::generic::VersionControlSystemAPI;
|
||||
use crate::vcs::generic::MinimalVersionControlSystemAPI;
|
||||
|
||||
use super::{data_structures::Account, http::GerritHTTPApi};
|
||||
|
||||
impl VersionControlSystemAPI for GerritHTTPApi {
|
||||
// The next three APIs are todo!() because they cannot be implemented in Gerrit.
|
||||
impl MinimalVersionControlSystemAPI for GerritHTTPApi {
|
||||
// The next API is todo!() because they cannot be implemented in Gerrit.
|
||||
// Gerrit does not offer any way to get this information out.
|
||||
// GerritHTTPApi needs to return something like Unsupported
|
||||
// and we need to compose a GerritHTTPApi with a GerritForge which contains an implementation
|
||||
// of check statuses and commit statuses and an issue tracker.
|
||||
fn create_check_statuses(
|
||||
&self,
|
||||
_repo: &crate::message::Repo,
|
||||
_checks: Vec<crate::vcs::generic::CheckRunOptions>,
|
||||
) -> futures_util::future::BoxFuture<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn create_commit_statuses(
|
||||
&self,
|
||||
_repo: &crate::message::Repo,
|
||||
_sha: String,
|
||||
_state: crate::vcs::generic::State,
|
||||
_context: String,
|
||||
_description: String,
|
||||
_target_url: String,
|
||||
) -> futures_util::future::BoxFuture<Result<(), crate::vcs::commit_status::CommitStatusError>>
|
||||
{
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn get_issue(
|
||||
&self,
|
||||
_repo: &crate::message::Repo,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub mod checks;
|
||||
//pub mod client;
|
||||
pub mod data_structures;
|
||||
pub mod http;
|
||||
pub mod r#impl;
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
pub mod commit_status;
|
||||
pub mod generic;
|
||||
pub mod gerrit;
|
||||
|
|
Loading…
Reference in a new issue