Skip to content

Commit

Permalink
feat: allow a ci dedicated repo to run workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
Vo Hoang Long committed Dec 1, 2024
1 parent ea91f11 commit 115ec15
Show file tree
Hide file tree
Showing 18 changed files with 296 additions and 155 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ chrono = "0.4"

itertools = "0.13.0"

derive_builder = "0.20.0"

[dev-dependencies]
insta = "1.26"
derive_builder = "0.20.0"
wiremock = "0.6.0"
base64 = "0.22.1"
tracing-test = "0.2.4"
Expand Down
52 changes: 39 additions & 13 deletions src/bin/bors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use std::time::Duration;

use anyhow::Context;
use bors::{
create_app, create_bors_process, create_github_client, load_repositories, BorsContext,
BorsGlobalEvent, CommandParser, PgDbClient, ServerState, TeamApiClient, WebhookSecret,
create_app, create_bors_process, create_github_client, create_github_client_from_access_token,
load_repositories, BorsContextBuilder, BorsGlobalEvent, CommandParser, PgDbClient, ServerState,
TeamApiClient, WebhookSecret,
};
use clap::Parser;
use sqlx::postgres::PgConnectOptions;
Expand All @@ -18,6 +19,8 @@ use tracing_subscriber::filter::EnvFilter;
/// How often should the bot check DB state, e.g. for handling timeouts.
const PERIODIC_REFRESH: Duration = Duration::from_secs(120);

const GITHUB_API_URL: &str = "https://api.github.com";

#[derive(clap::Parser)]
struct Opts {
/// Github App ID.
Expand All @@ -39,6 +42,10 @@ struct Opts {
/// Prefix used for bot commands in PR comments.
#[arg(long, env = "CMD_PREFIX", default_value = "@bors")]
cmd_prefix: String,

/// Prefix used for bot commands in PR comments.
#[arg(long, env = "CI_ACCESS_TOKEN")]
ci_access_token: Option<String>,
}

/// Starts a server that receives GitHub webhooks and generates events into a queue
Expand Down Expand Up @@ -81,15 +88,25 @@ fn try_main(opts: Opts) -> anyhow::Result<()> {
let db = runtime
.block_on(initialize_db(&opts.db))
.context("Cannot initialize database")?;
let team_api = TeamApiClient::default();
let (client, loaded_repos) = runtime.block_on(async {
let client = create_github_client(
opts.app_id.into(),
"https://api.github.com".to_string(),
opts.private_key.into(),
)?;
let repos = load_repositories(&client, &team_api).await?;
Ok::<_, anyhow::Error>((client, repos))
let team_api_client = TeamApiClient::default();
let client = create_github_client(
opts.app_id.into(),
GITHUB_API_URL.to_string(),
opts.private_key.into(),
)?;
let ci_client = match opts.ci_access_token {
Some(access_token) => {
let client = create_github_client_from_access_token(
GITHUB_API_URL.to_string(),
access_token.into(),
)?;
Some(client)
}
None => None,
};
let loaded_repos = runtime.block_on(async {
let repos = load_repositories(&client, ci_client.clone(), &team_api_client).await?;
Ok::<_, anyhow::Error>(repos)
})?;

let mut repos = HashMap::default();
Expand All @@ -108,8 +125,17 @@ fn try_main(opts: Opts) -> anyhow::Result<()> {
repos.insert(name, Arc::new(repo));
}

let ctx = BorsContext::new(CommandParser::new(opts.cmd_prefix), Arc::new(db), repos);
let (repository_tx, global_tx, bors_process) = create_bors_process(ctx, client, team_api);
let ctx = BorsContextBuilder::default()
.parser(CommandParser::new(opts.cmd_prefix))
.db(Arc::new(db))
.repositories(repos)
.gh_client(client)
.ci_client(ci_client)
.team_api_client(team_api_client)
.build()
.unwrap();

let (repository_tx, global_tx, bors_process) = create_bors_process(ctx);

let refresh_tx = global_tx.clone();
let refresh_process = async move {
Expand Down
1 change: 1 addition & 0 deletions src/bors/command/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum CommandPart<'a> {
KeyValue { key: &'a str, value: &'a str },
}

#[derive(Clone)]
pub struct CommandParser {
prefix: String,
}
Expand Down
20 changes: 20 additions & 0 deletions src/bors/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,23 @@ pub fn try_build_in_progress_comment() -> Comment {
pub fn cant_find_last_parent_comment() -> Comment {
Comment::new(":exclamation: There was no previous build. Please set an explicit parent or remove the `parent=last` argument to use the default parent.".to_string())
}

pub fn no_try_build_in_progress_comment() -> Comment {
Comment::new(":exclamation: There is currently no try build in progress.".to_string())
}

pub fn unclean_try_build_cancelled_comment() -> Comment {
Comment::new(
"Try build was cancelled. It was not possible to cancel some workflows.".to_string(),
)
}

pub fn try_build_cancelled_comment(workflow_urls: impl Iterator<Item = String>) -> Comment {
let mut try_build_cancelled_comment = r#"Try build cancelled.
Cancelled workflows:"#
.to_string();
for url in workflow_urls {
try_build_cancelled_comment += format!("\n- {}", url).as_str();
}
Comment::new(try_build_cancelled_comment)
}
29 changes: 13 additions & 16 deletions src/bors/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,24 @@ use std::{
sync::{Arc, RwLock},
};

use crate::{bors::command::CommandParser, github::GithubRepoName, PgDbClient};
use derive_builder::Builder;
use octocrab::Octocrab;

use crate::{bors::command::CommandParser, github::GithubRepoName, PgDbClient, TeamApiClient};

use super::RepositoryState;

#[derive(Builder)]
pub struct BorsContext {
pub parser: CommandParser,
pub db: Arc<PgDbClient>,
#[builder(field(
ty = "HashMap<GithubRepoName, Arc<RepositoryState>>",
build = "RwLock::new(self.repositories.clone())"
))]
pub repositories: RwLock<HashMap<GithubRepoName, Arc<RepositoryState>>>,
}

impl BorsContext {
pub fn new(
parser: CommandParser,
db: Arc<PgDbClient>,
repositories: HashMap<GithubRepoName, Arc<RepositoryState>>,
) -> Self {
let repositories = RwLock::new(repositories);
Self {
parser,
db,
repositories,
}
}
pub gh_client: Octocrab,
#[builder(default)]
pub ci_client: Option<Octocrab>,
pub team_api_client: TeamApiClient,
}
20 changes: 6 additions & 14 deletions src/bors/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::sync::Arc;

use anyhow::Context;
use octocrab::Octocrab;
use tracing::Instrument;

use crate::bors::command::{BorsCommand, CommandParseError};
Expand All @@ -17,7 +16,7 @@ use crate::bors::handlers::workflow::{
handle_check_suite_completed, handle_workflow_completed, handle_workflow_started,
};
use crate::bors::{BorsContext, Comment, RepositoryState};
use crate::{load_repositories, PgDbClient, TeamApiClient};
use crate::{load_repositories, PgDbClient};

#[cfg(test)]
use crate::tests::util::TestSyncMarker;
Expand Down Expand Up @@ -142,16 +141,12 @@ pub static WAIT_FOR_REFRESH: TestSyncMarker = TestSyncMarker::new();
pub async fn handle_bors_global_event(
event: BorsGlobalEvent,
ctx: Arc<BorsContext>,
gh_client: &Octocrab,
team_api_client: &TeamApiClient,
) -> anyhow::Result<()> {
let db = Arc::clone(&ctx.db);
match event {
BorsGlobalEvent::InstallationsChanged => {
let span = tracing::info_span!("Installations changed");
reload_repos(ctx, gh_client, team_api_client)
.instrument(span)
.await?;
reload_repos(ctx).instrument(span).await?;
}
BorsGlobalEvent::Refresh => {
let span = tracing::info_span!("Refresh");
Expand All @@ -161,7 +156,7 @@ pub async fn handle_bors_global_event(
let repo = Arc::clone(&repo);
async {
let subspan = tracing::info_span!("Repo", repo = repo.repository().to_string());
refresh_repository(repo, Arc::clone(&db), team_api_client)
refresh_repository(repo, Arc::clone(&db), &ctx.team_api_client)
.instrument(subspan)
.await
}
Expand Down Expand Up @@ -274,12 +269,9 @@ async fn handle_comment(
Ok(())
}

async fn reload_repos(
ctx: Arc<BorsContext>,
gh_client: &Octocrab,
team_api_client: &TeamApiClient,
) -> anyhow::Result<()> {
let reloaded_repos = load_repositories(gh_client, team_api_client).await?;
async fn reload_repos(ctx: Arc<BorsContext>) -> anyhow::Result<()> {
let reloaded_repos =
load_repositories(&ctx.gh_client, ctx.ci_client.clone(), &ctx.team_api_client).await?;
let mut repositories = ctx.repositories.write().unwrap();
for repo in repositories.values() {
if !reloaded_repos.contains_key(repo.repository()) {
Expand Down
Loading

0 comments on commit 115ec15

Please sign in to comment.