Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ jobs:
# Presumably used to log into the GitHub Container Registry, as per https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions.
id-token: write
container:
image: rust:1.88.0-alpine
image: rust:1.92.0-alpine
steps:
- uses: actions/checkout@v4
- name: Install deps
run: apk add docker gcc g++
- name: Build binaries
# Copy file to rename for the release action
run: cargo build --release && cp target/release/pr-metadata-validator pr-metadata-validator-musl-${{ github.ref_name }}
run: cargo build --release && cp target/release/pr-metadata-validator pr-metadata-validator-musl-${{ github.ref_name }} && cp target/release/dummy-code-of-conduct-validator dummy-code-of-conduct-validator-musl-${{ github.ref_name }}
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
Expand Down Expand Up @@ -64,4 +64,6 @@ jobs:
generate_release_notes: true
tag_name: ${{ github.ref_name }}
target_commitish: ${{ github.base_ref }}
files: pr-metadata-validator-musl-${{ github.ref_name }}
files: |
dummy-code-of-conduct-validator-musl-${{ github.ref_name }}
pr-metadata-validator-musl-${{ github.ref_name }}
40 changes: 40 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ axum = { version = "0.8.4", features = ["macros", "original-uri"] }
case_insensitive_string = { version = "0.2.10", features = ["serde"] }
chrono = "0.4.41"
chrono-tz = "0.10.3"
const_format = "0.2.35"
dotenv = "0.15.0"
# Until https://github.com/johnstonskj/rust-email_address/pull/43 is merged and released.
email_address = { git = "https://github.com/illicitonion/rust-email_address.git", rev = "12cd9762a166b79a227beaa90b2f60a768d7c55c" }
Expand Down Expand Up @@ -44,6 +45,7 @@ serde_json = "1"
serde_urlencoded = "0.7.1"
sheets = "0.7.0"
slack-with-types = "0.1.1"
strum_macros = "0.27.2"
time = "0.3.44"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tower = "0.5.2"
Expand Down
2 changes: 2 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "1.92.0"
73 changes: 73 additions & 0 deletions src/bin/dummy-code-of-conduct-validator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/// This binary exists to be a lightweight teaching version of the pr-metadata-validator.
/// Its purpose is to train trainees in the idea that bots will comment on their PRs, and they should follow their advice.
/// It is installed in https://github.com/CodeYourFuture/github_issues_prs_practice as a GitHub Action.
use std::process::exit;

use const_format::concatcp;
use trainee_tracker::{
octocrab::octocrab_for_token,
pr_comments::{PullRequest, close_existing_comments, leave_tagged_comment},
};

#[tokio::main]
async fn main() {
let Ok([_argv0, pr_url]) = <[_; _]>::try_from(std::env::args().collect::<Vec<_>>()) else {
eprintln!("Expected one arg - PR URL");
exit(1);
};
let pr_metadata = PullRequest::from_html_url(&pr_url).expect("Failed to parse PR URL");
let github_token =
std::env::var("GH_TOKEN").expect("GH_TOKEN wasn't set - must be set to a GitHub API token");
let octocrab = octocrab_for_token(github_token.to_owned()).expect("Failed to get octocrab");

let pr_from_rest = octocrab
.pulls(&pr_metadata.org, &pr_metadata.repo)
.get(pr_metadata.number)
.await
.expect("Failed to get PR");
if pr_from_rest
.body
.unwrap_or_default()
.ends_with(EXPECTED_SUFFIX)
{
let result = close_existing_comments(&octocrab, &pr_metadata, TAG).await;
if let Err(err) = result {
eprintln!("Failed to close existing comments: {:?}", err);
}
} else {
leave_tagged_comment(&octocrab, &pr_metadata, &[TAG], COMMENT_TO_LEAVE.to_owned())
.await
.expect("Failed to leave comment");
}
}

const EXPECTED_SUFFIX: &str = "I agree to follow the code of conduct for this organisation.";

const TAG: &str = "dummy-code-of-conduct-validator";

const COMMENT_TO_LEAVE: &str = concatcp!(
COMMENT_TO_LEAVE_PREFIX,
EXPECTED_SUFFIX,
COMMENT_TO_LEAVE_SUFFIX
);

const COMMENT_TO_LEAVE_PREFIX: &str = r#"This is a comment from a bot.

You should read it, make sure you understand it, and take the action it suggests.

If you don't understand the action it suggests, ask a volunteer or another trainee for help.

## ⚠️ Problem detected

In this repository, all pull request descriptions must end with the sentence:

> "#;

const COMMENT_TO_LEAVE_SUFFIX: &str = r#"

Your pull request description does not currently end with this sentence.

Please edit your pull request description to add this sentence at the end.

If you are successful in doing this, this comment will get automatically hidden within about a minute.
"#;
4 changes: 2 additions & 2 deletions src/bin/match-pr-to-assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use trainee_tracker::{
#[tokio::main]
async fn main() {
let Ok([_argv0, github_token, pr_link]) =
<[_; 3]>::try_from(std::env::args().collect::<Vec<_>>())
<[_; _]>::try_from(std::env::args().collect::<Vec<_>>())
else {
eprintln!("Expected two args - github token and PR link");
exit(1);
Expand All @@ -31,7 +31,7 @@ async fn main() {
_pull,
pr_number_str,
],
) = <[_; 7]>::try_from(pr_link.split('/').collect::<Vec<_>>())
) = <[_; _]>::try_from(pr_link.split('/').collect::<Vec<_>>())
else {
panic!("Couldn't parse GitHub PR link {}", pr_link);
};
Expand Down
64 changes: 29 additions & 35 deletions src/bin/pr-metadata-validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,19 @@ use trainee_tracker::{
course::match_prs_to_assignments,
newtypes::Region,
octocrab::octocrab_for_token,
pr_comments::{PullRequest, close_existing_comments, leave_tagged_comment},
prs::get_prs,
};

const ARBITRARY_REGION: Region = Region(String::new());

#[tokio::main]
async fn main() {
let Ok([_argv0, pr_url]) = <[_; 2]>::try_from(std::env::args().collect::<Vec<_>>()) else {
let Ok([_argv0, pr_url]) = <[_; _]>::try_from(std::env::args().collect::<Vec<_>>()) else {
eprintln!("Expected one arg - PR URL");
exit(1);
};
let pr_parts: Vec<_> = pr_url.split("/").collect();
let (github_org_name, module_name, pr_number) = match pr_parts.as_slice() {
[
_http,
_scheme,
_domain,
github_org_name,
module_name,
_pull,
number,
] => (
(*github_org_name).to_owned(),
(*module_name).to_owned(),
number.parse::<u64>().expect("Failed to parse PR number"),
),
_ => {
eprintln!("Failed to parse PR URL");
exit(1);
}
};
let pr = PullRequest::from_html_url(&pr_url).expect("Failed to parse PR URL");

// TODO: Fetch this from classplanner or somewhere when we have access to a useful API.
let known_region_aliases = KnownRegions(btreemap! {
Expand All @@ -57,7 +39,7 @@ async fn main() {
std::env::var("GH_TOKEN").expect("GH_TOKEN wasn't set - must be set to a GitHub API token");
let octocrab = octocrab_for_token(github_token).expect("Failed to get octocrab");

let course_schedule = make_fake_course_schedule(module_name.clone());
let course_schedule = make_fake_course_schedule(pr.repo.clone());

let course = CourseScheduleWithRegisterSheetId {
name: "itp".to_owned(),
Expand All @@ -67,15 +49,23 @@ async fn main() {
let result = validate_pr(
&octocrab,
course,
&module_name,
&github_org_name,
pr_number,
&pr.repo,
&pr.org,
pr.number,
&known_region_aliases,
)
.await
.expect("Failed to validate PR");
let message = match result {

const PR_METADATA_VALIDATOR_LABEL: &str = "pr-metadata-validator";

let message = match &result {
ValidationResult::Ok => {
if let Err(err) =
close_existing_comments(&octocrab, &pr, PR_METADATA_VALIDATOR_LABEL).await
{
eprintln!("Failed to close existing comments: {:?}", err);
}
exit(0);
}
ValidationResult::CouldNotMatch => COULD_NOT_MATCH_COMMENT,
Expand All @@ -90,26 +80,29 @@ async fn main() {
"{message}\n\nIf this PR is not coursework, please add the NotCoursework label (and message on Slack in #cyf-curriculum or it will probably not be noticed).\n\nIf this PR needs reviewed, please add the 'Needs Review' label to this PR after you have resolved the issues listed above."
);
eprintln!("{}", full_message);
octocrab
.issues(&github_org_name, &module_name)
.create_comment(pr_number, full_message)
.await
.expect("Failed to create comment with validation error");
leave_tagged_comment(
&octocrab,
&pr,
&[PR_METADATA_VALIDATOR_LABEL, &result.to_string()],
full_message,
)
.await
.expect("Failed to create comment with validation error");
let remove_label_response = octocrab
.issues(&github_org_name, &module_name)
.remove_label(pr_number, "Needs Review")
.issues(&pr.org, &pr.repo)
.remove_label(pr.number, "Needs Review")
.await;
match remove_label_response {
Ok(_) => {
println!(
"Found issues for PR #{}, notified and removed label",
pr_number
pr.number
);
}
Err(octocrab::Error::GitHub { source, .. }) if source.status_code == 404 => {
println!(
"Found issues for PR #{}, notified and label already removed",
pr_number
pr.number
);
// The only time this API 404s is if the label is already removed. Continue without error.
}
Expand Down Expand Up @@ -138,6 +131,7 @@ const UNKNOWN_REGION_COMMENT: &str = r#"Your PR's title didn't contain a known r

Please check the expected title format, and make sure your region is in the correct place and spelled correctly."#;

#[derive(strum_macros::Display)]
enum ValidationResult {
Ok,
BodyTemplateNotFilledOut,
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod google_groups;
pub mod mentoring;
pub mod newtypes;
pub mod octocrab;
pub mod pr_comments;
pub mod prs;
pub mod register;
pub mod reviewer_staff_info;
Expand Down
Loading