Skip to content

Commit

Permalink
everything
Browse files Browse the repository at this point in the history
  • Loading branch information
apiraino committed Sep 11, 2023
1 parent a50b73a commit aab031e
Show file tree
Hide file tree
Showing 17 changed files with 1,351 additions and 32 deletions.
27 changes: 27 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,30 @@ GITHUB_WEBHOOK_SECRET=MUST_BE_CONFIGURED
# for logging, refer to this document: https://rust-lang-nursery.github.io/rust-cookbook/development_tools/debugging/config_log.html
# `RUSTC_LOG` is not required to run the application, but it makes local development easier
# RUST_LOG=MUST_BE_CONFIGURED

# Your local triagebot will be reachable from the internet using this URL
# This is useful if you configure a webhook from GitHub and want to use your local triagebot as receiving end of the GitHub webhooks.
TRIAGEBOT_HOST=http://7e9ea9dc.ngrok-free.app

# These are identifiers about your Github App for the pull request prefs backoffice (see pr_prefs_backoffice.md). Needed only if
# want to hack locally on the backoffice.
# CLIENT_ID is public
# CLIENT_SECRET must be kept, well, secret.
CLIENT_ID="xxx"
CLIENT_SECRET="yyy"

# Flag to enable the new pull request assignment workflow (see pr_prefs_backoffice.md).
# If this env var is UNSET, the old pull request assignment is used (basically random assignment).
# If this env var is SET, the new pull request assignment reads the Rust contributors preferences and assigns PRs accordingly.
USE_NEW_PR_ASSIGNMENT=yay

# A comma separated list of teams that are allowed to use the new PR assignment workflow.
# Used to limit the number of users during the test phase.
# Team name matches names in the rust-lang/team repository:
# https://github.com/rust-lang/team/tree/master/teams
NEW_PR_ASSIGNMENT_TEAMS=compiler,compiler-contributors

# This is a secret used to create a checksum of the login cookie when accessing
# the pull request preferences backoffice. Only the server knows this secret.
# Could be generated with something like: openssl rand -hex 20
BACKOFFICE_SECRET=xxx
90 changes: 88 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ rust_team_data = { git = "https://github.com/rust-lang/team" }
glob = "0.3.0"
toml = "0.5.1"
hyper = { version = "0.14.4", features = ["server", "stream"]}
tokio = { version = "1.7.1", features = ["macros", "time", "rt"] }
tokio = { version = "1.7.1", features = ["fs", "macros", "time", "rt"] }
futures = { version = "0.3", default-features = false, features = ["std"] }
async-trait = "0.1.31"
uuid = { version = "0.8", features = ["v4", "serde"] }
Expand All @@ -45,6 +45,8 @@ ignore = "0.4.18"
postgres-types = { version = "0.2.4", features = ["derive"] }
cron = { version = "0.12.0" }
bytes = "1.1.0"
crypto-hash = { version = "0.3.4", default-features = false }
flate2 = { version = "1.0.27", default-features = false, features = ["zlib-ng"] }

[dependencies.serde]
version = "1"
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ The general overview of what you will need to do:
6. Add a `triagebot.toml` file to the main branch of your GitHub repo with whichever services you want to try out.
7. Try interacting with your repo, such as issuing `@rustbot` commands or interacting with PRs and issues (depending on which services you enabled in `triagebot.toml`). Watch the logs from the server to see what's going on.

## The pull requests assignment preferences backoffice

This is an administrative backoffice targeted at the Rust project contributors to set their preferences for pull request assignment.

Read all the documentation [here](./pr_prefs_backoffice.md).

### Configure a database

To use Postgres, you will need to install it and configure it:
Expand Down
39 changes: 39 additions & 0 deletions pr_prefs_backoffice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Pull request assignment preferences backoffice

This is an administrative backoffice targeted at the Rust project contributors to set their preferences for pull request assignment.

When assigning the review of pull requests, this backoffice allows contributors to:
- set themselves on leave for any amount of time. During this time off contributors won't be assigned any new pull request
- set the maximum number of pull requests assigned to them
- set the desired number of days before a pull request assigned for review to the contributor might be considered for a reminder
- allow a flag to make their own preferences visible to all team members or only to team leaders and system administrators

This is a mostly static web page server-side generated, using the least amount possible of JavaScript.

This backoffice will set one cookie (`triagebot.session`) to understand if a user is already logged in. The cookie expires after
1 hour and is renewed at every access. The cookie is set to `Secure=true`, `HttpOnly` and `SameSite=Strict`.

Access authorization is handled by GitHub, so users will need to be logged in GitHub and authorize this Github Application.

Access to this backoffice is granted only to GitHub users that are members of a Rust project team (teams are defined [here](https://github.com/rust-lang/team/tree/HEAD/teams)). Only specific Rust teams are allowed to use
this backoffice (mostly for testing purposes, to switch users to the new workflow slowly). Teams allowed are defined in the env var `NEW_PR_ASSIGNMENT_TEAMS` (see `.env.sample`).

Teams members are expected to set their own review preferences using this backoffice. In case a team member didn't yet set their own preferences, these defaults will be applied:
- Max 5 pull requests assigned (see constant `PREF_MAX_ASSIGNED_PRS`)
- 20 days before a notification is sent for a pull request assigned that is waiting for review (see constant `PREF_ALLOW_PING_AFTER_DAYS`)

## How to locally run this backoffice

- Configure a webhook pointing to a local instance of the triagebot. Follow the instructions [in the README](https://github.com/rust-lang/triagebot#configure-webhook-forwarding).
- Configure a repository under your GitHub username and configure the same webhook URL in the "Webhooks" settings of the repository.
- Create a GiHub Application and configure the callback URL ([here](https://github.com/settings/apps)) pointing to your proxied triagebot backoffice using the path to the backoffice (ex. `http://7e9ea9dc.ngrok.io/github-hook/review-settings`)
- Start your local triagebot: load the environment variable from a file (make a copy of `.env.sample`) and run `RUST_LOG=DEBUG cargo run --bin triagebot`

## TODO

- [ ] Handle cleanup of the preferences DB table for team members not existing anymore in the teams .toml: delete their assignments, PRs go back to the pool of those needing an assignee
- [ ] Cache somehow teams .toml download from github to avoid retrieving those `.toml` files too often
- [ ] maybe more input validation, see `validate_data()` in `./src/main.rs`
- [ ] Now we are handling contributors workload for a single team. Some contributors work across teams. Make this backoffice aware of other teams and show the actual workload of contributors


8 changes: 8 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub(crate) struct Config {
pub(crate) note: Option<NoteConfig>,
pub(crate) mentions: Option<MentionsConfig>,
pub(crate) no_merges: Option<NoMergesConfig>,
pub(crate) review_prefs: Option<ReviewPrefsConfig>,
}

#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
Expand Down Expand Up @@ -151,6 +152,12 @@ pub(crate) struct ShortcutConfig {
_empty: (),
}

#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
pub(crate) struct ReviewPrefsConfig {
#[serde(default)]
_empty: (),
}

#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
pub(crate) struct PrioritizeConfig {
pub(crate) label: String,
Expand Down Expand Up @@ -423,6 +430,7 @@ mod tests {
review_submitted: None,
mentions: None,
no_merges: None,
review_prefs: None
}
);
}
Expand Down
15 changes: 15 additions & 0 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,5 +272,20 @@ CREATE UNIQUE INDEX jobs_name_scheduled_at_unique_index
ON jobs (
name, scheduled_at
);
",
"
CREATE table review_capacity (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id BIGINT REFERENCES users(user_id),
checksum TEXT NOT NULL,
assigned_prs INT[] NOT NULL,
num_assigned_prs INTEGER,
max_assigned_prs INTEGER,
pto_date_start date,
pto_date_end date,
active boolean default true,
allow_ping_after_days INTEGER,
publish_prefs boolean NOT NULL DEFAULT false
);
",
];
28 changes: 28 additions & 0 deletions src/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use std::{
};
use tracing as log;

use crate::Config;

#[derive(Debug, PartialEq, Eq, serde::Deserialize)]
pub struct User {
pub login: String,
Expand Down Expand Up @@ -167,6 +169,23 @@ impl GithubClient {
let (body, _req_dbg) = self.send_req(req).await?;
Ok(serde_json::from_slice(&body)?)
}

pub async fn get_team_members<'a>(
&self,
admins: &mut Vec<String>,
members: &mut Vec<String>,
team: &str,
) {
let req = self.get(&format!(
"https://raw.githubusercontent.com/rust-lang/team/HEAD/teams/{}",
team,
));
let (contents, _) = self.send_req(req).await.unwrap();
let body = String::from_utf8_lossy(&contents).to_string();
let mut config: Config = toml::from_str(&body).unwrap();
members.append(&mut config.people.members);
admins.append(&mut config.people.leads);
}
}

impl User {
Expand Down Expand Up @@ -2046,6 +2065,15 @@ impl GithubClient {
.await
.with_context(|| format!("{} failed to get repo", full_name))
}

pub async fn get_profile(&self, access_token: &str) -> anyhow::Result<User> {
let c = self
.client
.get("https://api.github.com/user")
.header("Authorization", format!("Bearer {}", access_token))
.header("User-Agent", "rust-lang-triagebot");
self.json::<User>(c).await
}
}

#[derive(Debug, serde::Deserialize)]
Expand Down
4 changes: 4 additions & 0 deletions src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ mod notify_zulip;
mod ping;
mod prioritize;
mod relabel;
pub mod review_prefs;
mod review_submitted;
mod rfc_helper;
pub mod rustc_commits;
Expand Down Expand Up @@ -157,13 +158,16 @@ macro_rules! issue_handlers {
//
// This is for events that happen only on issues (e.g. label changes).
// Each module in the list must contain the functions `parse_input` and `handle_input`.
// - `parse_input` should parse and validate the input, return an object with everything needed to perform an action
// - `handle_input`: performs the action (optionally) using the input object received
issue_handlers! {
assign,
autolabel,
major_change,
mentions,
no_merges,
notify_zulip,
review_prefs,
}

macro_rules! command_handlers {
Expand Down
Loading

0 comments on commit aab031e

Please sign in to comment.