Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New pull request assignment proposal #1719

Closed
wants to merge 10 commits into from

Conversation

apiraino
Copy link
Contributor

@apiraino apiraino commented Sep 12, 2023

This patchset is the implementation of a new workflow for assigning pull requests to the Rust project contributors.

2023-11-08 UPDATE: After a meeting with T-infra, it was remarked that this block of changes are hard to review, so we agreed a plan to split into smaller bites. This PR will stay as tracking issues for the work being split into smaller pull requests:

  • Step 1: Change pull request assignment algo (Add new PR review assignment #1745 ):

    • create new DB table, just store there "how busy ppl are"
    • add new mechanism to assign pull requests
    • add CLI tool to populate the new DB table and synchronize it. Make it so that it will speak HTTP instead of using public triagebot APIs.
  • Step 2: Implement the backoffice where people can set themselves on time-off.

    • TBD: standalone app or inside the Triagebot codebase?

A slightly old design document is at this HackMD link.

This proposal was discussed separately in a number of meetings with the compiler team (also mentioned to @Mark-Simulacrum).

Summary

The current pull request assignment is basically randomly assigned among team members. The new proposed workflow has the following features:

  • each Rust project contributor belonging to a team (I'd start will a small cohort of testers) can set their own review capacity and time off using a web backoffice
  • the web backoffice will persiste these data in a DB
  • everytime a pull request assignment is invoked with r? <team> or <team_member> the triagebot will check the current availability and workload of the candidates and assign the pull request to the team member less busy, excluding automatically those that are on time off in that moment.

Permissions and Authorization

This backoffice is a simple form listing all team members. It is served by the triagebot (from triagebot.infra.rust-lang.org/). Access to this backoffice is gated by a GitHub App. Users allowed to this backoffice:

  1. must be logged into GitHub
  2. must agree to the share some details with a GitHub App (only to read their profile)
  3. must be a Rust project team member
  4. the team they belong must be whitelisted in the env var NEW_PR_ASSIGNMENT_TEAMS (see documentation)

Team leaders are considered administrators and can see the preferences of every team member. Normal team members can only see their own preferences and those of team members that agree to share theirs within the team.

DEPLOYMENT

There are a few env vars to enable the new PR assignment and retrict access to this backoffice (see: .env.sample).

My idea would be to iterate on this pull request and iron out all the wrinkles visible without deployment:

  • Deploy this changes but keep the new PR assignment DISABLED. The web backoffice will still be accessible.
  • Team members can access that, start playing with with it fill their preferences
  • When we are all confident about these changes, we can flip the env variable USE_NEW_PR_ASSIGNMENT and have the team listed in NEW_PR_ASSIGNMENT_TEAMS use it for real.
  • After a first period of tests, add more teams

Of course, happy to receive inputs!

TODO

I also left a few notes about a few items to be implemented, in my opinion also in a second interation; see the documentation.


(I will iterate on the pull request description to add context)

Copy link
Contributor Author

@apiraino apiraino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few comments to provide more context.

Comment on lines +48 to +49
crypto-hash = { version = "0.3.4", default-features = false }
flate2 = { version = "1.0.27", default-features = false, features = ["zlib-ng"] }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new crates are used to decrypt and encrypt a digest set in the cookie for users returning to the backoffice

Comment on lines 731 to 738
if use_new_pr_assignment(teams, &candidates) {
log::debug!("Using NEW pull request assignment workflow");
assignee = new_find_reviewer_from_names(db_client, &candidates).await;
} else {
log::debug!("Using OLD pull request assignment workflow");
let username = old_find_reviewer_from_names(candidates);
assignee = Ok(ReviewCapacityUser::phony(username.unwrap()));
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new pull request assignment can be enabled or disengaged using an env var (USE_NEW_PR_ASSIGNMENT) when deploying the Triagebot. This conditional allows using the current workflow or the new one.

}
}

impl From<HashMap<String, String>> for ReviewCapacityUser {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this From parses the form from the backoffice. I'm not too happy about this code though ...

@@ -145,6 +182,230 @@ async fn serve_req(
.body(Body::from(triagebot::zulip::respond(&ctx, req).await))
.unwrap());
}

if req.uri.path() == "/review-settings" {
Copy link
Contributor Author

@apiraino apiraino Sep 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/review-settings is a new endpoint of the Triagebot that will allow access to the backoffice to set the pull request assignment preferences.

Here there is quite some code to authenticate the user. could probably be split and refactored a bit for better clarity

Comment on lines +498 to +504
fn create_cookie_content(user_login: &str, user_id: i64) -> serde_json::Value {
let auth_secret = std::env::var("BACKOFFICE_SECRET").expect("BACKOFFICE_SECRET is not set");
let exp = Utc::now() + Duration::minutes(30);
let digest = format!("{};{};{}", user_id, user_login, auth_secret);
let digest = hex_digest(Algorithm::SHA256, &digest.into_bytes());
serde_json::json!({"iss":"triagebot", "sub":user_login, "uid": user_id, "exp":exp, "checksum":digest})
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everytime users log in, a cookie with this content will be saved on their browser. If the cookie is not found, the user will be redirected to the GitHub auth login workflow again

@@ -17,6 +18,34 @@ pub fn index() -> Result<Response<Body>, hyper::Error> {
.unwrap())
}

pub async fn asset(file: &str) -> Result<Response<Body>, hyper::Error> {
Copy link
Contributor Author

@apiraino apiraino Sep 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backoffice started as an HTML page with a separate JavaScript and CSS file, so I thought to add a static asset endpoint. Then I moved everything to the server, the JavaScript was basically removed. scripts and stylesheets are now embedded in the HTML file (~4kb with gzip compression).

This asset path is now only used to serve 2 SVG. Unsure if we want to keep this new endpoint after all :)

Comment on lines +119 to +128
SELECT username, r.*, sum(r.max_assigned_prs - r.num_assigned_prs) as avail_slots
FROM review_capacity r
JOIN users on users.user_id=r.user_id
WHERE active = true
AND current_date NOT BETWEEN pto_date_start AND pto_date_end
AND username = ANY('{{ {} }}')
AND num_assigned_prs < max_assigned_prs
GROUP BY username, r.id
ORDER BY avail_slots DESC
LIMIT 1",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core of the new PR assignment is here: This SQL query will retrieve all active team members and sort them by "avail slots" (max # of PRs - # of PRs assigned)

This CLI tool insert or sync all members defined in
$NEW_PR_ASSIGNMENT_TEAMS into the review preferences DB table.
@apiraino apiraino marked this pull request as draft November 8, 2023 13:37
@apiraino
Copy link
Contributor Author

Closing and restarting from scratch in #1753

@apiraino apiraino closed this Dec 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant