Skip to content

Commit 115ec15

Browse files
author
Vo Hoang Long
committed
feat: allow a ci dedicated repo to run workflow
1 parent ea91f11 commit 115ec15

File tree

18 files changed

+296
-155
lines changed

18 files changed

+296
-155
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ chrono = "0.4"
5454

5555
itertools = "0.13.0"
5656

57+
derive_builder = "0.20.0"
58+
5759
[dev-dependencies]
5860
insta = "1.26"
59-
derive_builder = "0.20.0"
6061
wiremock = "0.6.0"
6162
base64 = "0.22.1"
6263
tracing-test = "0.2.4"

src/bin/bors.rs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ use std::time::Duration;
66

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

22+
const GITHUB_API_URL: &str = "https://api.github.com";
23+
2124
#[derive(clap::Parser)]
2225
struct Opts {
2326
/// Github App ID.
@@ -39,6 +42,10 @@ struct Opts {
3942
/// Prefix used for bot commands in PR comments.
4043
#[arg(long, env = "CMD_PREFIX", default_value = "@bors")]
4144
cmd_prefix: String,
45+
46+
/// Prefix used for bot commands in PR comments.
47+
#[arg(long, env = "CI_ACCESS_TOKEN")]
48+
ci_access_token: Option<String>,
4249
}
4350

4451
/// Starts a server that receives GitHub webhooks and generates events into a queue
@@ -81,15 +88,25 @@ fn try_main(opts: Opts) -> anyhow::Result<()> {
8188
let db = runtime
8289
.block_on(initialize_db(&opts.db))
8390
.context("Cannot initialize database")?;
84-
let team_api = TeamApiClient::default();
85-
let (client, loaded_repos) = runtime.block_on(async {
86-
let client = create_github_client(
87-
opts.app_id.into(),
88-
"https://api.github.com".to_string(),
89-
opts.private_key.into(),
90-
)?;
91-
let repos = load_repositories(&client, &team_api).await?;
92-
Ok::<_, anyhow::Error>((client, repos))
91+
let team_api_client = TeamApiClient::default();
92+
let client = create_github_client(
93+
opts.app_id.into(),
94+
GITHUB_API_URL.to_string(),
95+
opts.private_key.into(),
96+
)?;
97+
let ci_client = match opts.ci_access_token {
98+
Some(access_token) => {
99+
let client = create_github_client_from_access_token(
100+
GITHUB_API_URL.to_string(),
101+
access_token.into(),
102+
)?;
103+
Some(client)
104+
}
105+
None => None,
106+
};
107+
let loaded_repos = runtime.block_on(async {
108+
let repos = load_repositories(&client, ci_client.clone(), &team_api_client).await?;
109+
Ok::<_, anyhow::Error>(repos)
93110
})?;
94111

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

111-
let ctx = BorsContext::new(CommandParser::new(opts.cmd_prefix), Arc::new(db), repos);
112-
let (repository_tx, global_tx, bors_process) = create_bors_process(ctx, client, team_api);
128+
let ctx = BorsContextBuilder::default()
129+
.parser(CommandParser::new(opts.cmd_prefix))
130+
.db(Arc::new(db))
131+
.repositories(repos)
132+
.gh_client(client)
133+
.ci_client(ci_client)
134+
.team_api_client(team_api_client)
135+
.build()
136+
.unwrap();
137+
138+
let (repository_tx, global_tx, bors_process) = create_bors_process(ctx);
113139

114140
let refresh_tx = global_tx.clone();
115141
let refresh_process = async move {

src/bors/command/parser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum CommandPart<'a> {
2222
KeyValue { key: &'a str, value: &'a str },
2323
}
2424

25+
#[derive(Clone)]
2526
pub struct CommandParser {
2627
prefix: String,
2728
}

src/bors/comment.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,23 @@ pub fn try_build_in_progress_comment() -> Comment {
5555
pub fn cant_find_last_parent_comment() -> Comment {
5656
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())
5757
}
58+
59+
pub fn no_try_build_in_progress_comment() -> Comment {
60+
Comment::new(":exclamation: There is currently no try build in progress.".to_string())
61+
}
62+
63+
pub fn unclean_try_build_cancelled_comment() -> Comment {
64+
Comment::new(
65+
"Try build was cancelled. It was not possible to cancel some workflows.".to_string(),
66+
)
67+
}
68+
69+
pub fn try_build_cancelled_comment(workflow_urls: impl Iterator<Item = String>) -> Comment {
70+
let mut try_build_cancelled_comment = r#"Try build cancelled.
71+
Cancelled workflows:"#
72+
.to_string();
73+
for url in workflow_urls {
74+
try_build_cancelled_comment += format!("\n- {}", url).as_str();
75+
}
76+
Comment::new(try_build_cancelled_comment)
77+
}

src/bors/context.rs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,24 @@ use std::{
33
sync::{Arc, RwLock},
44
};
55

6-
use crate::{bors::command::CommandParser, github::GithubRepoName, PgDbClient};
6+
use derive_builder::Builder;
7+
use octocrab::Octocrab;
8+
9+
use crate::{bors::command::CommandParser, github::GithubRepoName, PgDbClient, TeamApiClient};
710

811
use super::RepositoryState;
912

13+
#[derive(Builder)]
1014
pub struct BorsContext {
1115
pub parser: CommandParser,
1216
pub db: Arc<PgDbClient>,
17+
#[builder(field(
18+
ty = "HashMap<GithubRepoName, Arc<RepositoryState>>",
19+
build = "RwLock::new(self.repositories.clone())"
20+
))]
1321
pub repositories: RwLock<HashMap<GithubRepoName, Arc<RepositoryState>>>,
14-
}
15-
16-
impl BorsContext {
17-
pub fn new(
18-
parser: CommandParser,
19-
db: Arc<PgDbClient>,
20-
repositories: HashMap<GithubRepoName, Arc<RepositoryState>>,
21-
) -> Self {
22-
let repositories = RwLock::new(repositories);
23-
Self {
24-
parser,
25-
db,
26-
repositories,
27-
}
28-
}
22+
pub gh_client: Octocrab,
23+
#[builder(default)]
24+
pub ci_client: Option<Octocrab>,
25+
pub team_api_client: TeamApiClient,
2926
}

src/bors/handlers/mod.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::sync::Arc;
22

33
use anyhow::Context;
4-
use octocrab::Octocrab;
54
use tracing::Instrument;
65

76
use crate::bors::command::{BorsCommand, CommandParseError};
@@ -17,7 +16,7 @@ use crate::bors::handlers::workflow::{
1716
handle_check_suite_completed, handle_workflow_completed, handle_workflow_started,
1817
};
1918
use crate::bors::{BorsContext, Comment, RepositoryState};
20-
use crate::{load_repositories, PgDbClient, TeamApiClient};
19+
use crate::{load_repositories, PgDbClient};
2120

2221
#[cfg(test)]
2322
use crate::tests::util::TestSyncMarker;
@@ -142,16 +141,12 @@ pub static WAIT_FOR_REFRESH: TestSyncMarker = TestSyncMarker::new();
142141
pub async fn handle_bors_global_event(
143142
event: BorsGlobalEvent,
144143
ctx: Arc<BorsContext>,
145-
gh_client: &Octocrab,
146-
team_api_client: &TeamApiClient,
147144
) -> anyhow::Result<()> {
148145
let db = Arc::clone(&ctx.db);
149146
match event {
150147
BorsGlobalEvent::InstallationsChanged => {
151148
let span = tracing::info_span!("Installations changed");
152-
reload_repos(ctx, gh_client, team_api_client)
153-
.instrument(span)
154-
.await?;
149+
reload_repos(ctx).instrument(span).await?;
155150
}
156151
BorsGlobalEvent::Refresh => {
157152
let span = tracing::info_span!("Refresh");
@@ -161,7 +156,7 @@ pub async fn handle_bors_global_event(
161156
let repo = Arc::clone(&repo);
162157
async {
163158
let subspan = tracing::info_span!("Repo", repo = repo.repository().to_string());
164-
refresh_repository(repo, Arc::clone(&db), team_api_client)
159+
refresh_repository(repo, Arc::clone(&db), &ctx.team_api_client)
165160
.instrument(subspan)
166161
.await
167162
}
@@ -274,12 +269,9 @@ async fn handle_comment(
274269
Ok(())
275270
}
276271

277-
async fn reload_repos(
278-
ctx: Arc<BorsContext>,
279-
gh_client: &Octocrab,
280-
team_api_client: &TeamApiClient,
281-
) -> anyhow::Result<()> {
282-
let reloaded_repos = load_repositories(gh_client, team_api_client).await?;
272+
async fn reload_repos(ctx: Arc<BorsContext>) -> anyhow::Result<()> {
273+
let reloaded_repos =
274+
load_repositories(&ctx.gh_client, ctx.ci_client.clone(), &ctx.team_api_client).await?;
283275
let mut repositories = ctx.repositories.write().unwrap();
284276
for repo in repositories.values() {
285277
if !reloaded_repos.contains_key(repo.repository()) {

0 commit comments

Comments
 (0)