No, sadly, the title is not a typo. See https://www.githubstatus.com/incidents/zsg1lk7w13cf for the context. I'll read with joy and popcorn through that root cause analysis. It makes literally zero sense what happened here, but for some completly bonkers reason GitHub completely messed up the merge queue with https://github.com/zed-industries/zed/pull/54632. I have no idea how it happened. It makes literally zero sense. A PR going into the merge queue should have the same LoC when getting out of it. GitHub obviously does not check this. GitHub causes extra work with a feature that is supposed to save time. Thanks, I guess. Release Notes: - N/A --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
199 lines
5.9 KiB
Rust
199 lines
5.9 KiB
Rust
use std::{path::PathBuf, rc::Rc};
|
|
|
|
use anyhow::{Context, Result};
|
|
use clap::{Parser, Subcommand};
|
|
|
|
use compliance::{
|
|
checks::Reporter,
|
|
git::{CommitsFromVersionToVersion, GetVersionTags, GitCommand, InfoForCommit, VersionTag},
|
|
github::{GithubApiClient as _, OctocrabClient, Repository},
|
|
report::ReportReviewSummary,
|
|
};
|
|
|
|
const MAX_CONCURRENT_REQUESTS: usize = 5;
|
|
|
|
#[derive(Parser)]
|
|
pub(crate) struct ComplianceArgs {
|
|
#[clap(subcommand)]
|
|
mode: ComplianceMode,
|
|
}
|
|
|
|
const IGNORE_LIST: &[&str] = &[
|
|
"75fa566511e3ae7d03cfd76008512080291bd81d", // GitHub nuked this PR out of orbit
|
|
];
|
|
|
|
#[derive(Subcommand)]
|
|
pub(crate) enum ComplianceMode {
|
|
// Check compliance for all commits between two version tags
|
|
Version(VersionArgs),
|
|
// Check compliance for a single commit
|
|
Single {
|
|
// The full commit SHA to check
|
|
commit_sha: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Parser)]
|
|
pub(crate) struct VersionArgs {
|
|
#[arg(value_parser = VersionTag::parse)]
|
|
// The version to be on the lookout for
|
|
version_tag: VersionTag,
|
|
#[arg(long)]
|
|
// The markdown file to write the compliance report to
|
|
report_path: PathBuf,
|
|
#[arg(long)]
|
|
// An optional branch to use instead of the determined version branch
|
|
branch: Option<String>,
|
|
}
|
|
|
|
impl VersionArgs {
|
|
pub(crate) fn version_tag(&self) -> &VersionTag {
|
|
&self.version_tag
|
|
}
|
|
|
|
fn version_head(&self) -> String {
|
|
self.branch
|
|
.clone()
|
|
.unwrap_or_else(|| self.version_tag().to_string())
|
|
}
|
|
}
|
|
|
|
async fn check_compliance_impl(args: ComplianceArgs) -> Result<()> {
|
|
let app_id = std::env::var("GITHUB_APP_ID").context("Missing GITHUB_APP_ID")?;
|
|
let key = std::env::var("GITHUB_APP_KEY").context("Missing GITHUB_APP_KEY")?;
|
|
|
|
let client = Rc::new(
|
|
OctocrabClient::new(
|
|
app_id.parse().context("Failed to parse app ID as int")?,
|
|
key.as_ref(),
|
|
Repository::ZED.owner(),
|
|
)
|
|
.await?,
|
|
);
|
|
|
|
println!("Initialized GitHub client for app ID {app_id}");
|
|
|
|
let args = match args.mode {
|
|
ComplianceMode::Version(version) => version,
|
|
ComplianceMode::Single { commit_sha } => {
|
|
let commit = GitCommand::run(InfoForCommit::new(&commit_sha))?;
|
|
|
|
return match Reporter::result_for_commit(commit, client).await {
|
|
Ok(review_success) => {
|
|
println!("Check for commit {commit_sha} succeeded. Result: {review_success}",);
|
|
Ok(())
|
|
}
|
|
|
|
Err(review_failure) => Err(anyhow::anyhow!(
|
|
"Check for commit {commit_sha} failed. Result: {review_failure}"
|
|
)),
|
|
};
|
|
}
|
|
};
|
|
|
|
let tag = args.version_tag();
|
|
|
|
let previous_version = GitCommand::run(GetVersionTags)?
|
|
.sorted()
|
|
.find_previous_minor_version(&tag)
|
|
.cloned()
|
|
.ok_or_else(|| {
|
|
anyhow::anyhow!(
|
|
"Could not find previous version for tag {tag}",
|
|
tag = tag.to_string()
|
|
)
|
|
})?;
|
|
|
|
println!(
|
|
"Checking compliance for version {} with version {} as base",
|
|
tag.version(),
|
|
previous_version.version()
|
|
);
|
|
|
|
let commits = GitCommand::run(CommitsFromVersionToVersion::new(
|
|
previous_version,
|
|
args.version_head(),
|
|
))?;
|
|
|
|
let Some(range) = commits.range() else {
|
|
anyhow::bail!("No commits found to check");
|
|
};
|
|
|
|
println!("Checking commit range {range}, {} total", commits.len());
|
|
|
|
let report = Reporter::new(commits, client.clone())
|
|
.generate_report(MAX_CONCURRENT_REQUESTS)
|
|
.await;
|
|
|
|
println!(
|
|
"Generated report for version {}",
|
|
args.version_tag().to_string()
|
|
);
|
|
|
|
let summary = report.summary();
|
|
|
|
println!(
|
|
"Applying compliance labels to {} pull requests",
|
|
summary.prs_with_errors()
|
|
);
|
|
|
|
let all_errors_known = report.errors().all(|error| {
|
|
error.is_unknown_error() && IGNORE_LIST.contains(&error.commit.sha().as_str())
|
|
});
|
|
|
|
for report in report.errors() {
|
|
if let Some(pr_number) = report.commit.pr_number()
|
|
&& let Ok(pull_request) = client.get_pull_request(&Repository::ZED, pr_number).await
|
|
&& pull_request.labels.is_none_or(|labels| {
|
|
labels
|
|
.iter()
|
|
.all(|label| label != compliance::github::PR_REVIEW_LABEL)
|
|
})
|
|
{
|
|
println!("Adding review label to PR {}...", pr_number);
|
|
|
|
client
|
|
.add_label_to_issue(
|
|
&Repository::ZED,
|
|
compliance::github::PR_REVIEW_LABEL,
|
|
pr_number,
|
|
)
|
|
.await?;
|
|
}
|
|
}
|
|
|
|
report.write_markdown(&args.report_path)?;
|
|
|
|
println!("Wrote compliance report to {}", args.report_path.display());
|
|
|
|
match summary.review_summary() {
|
|
ReportReviewSummary::MissingReviews => Err(anyhow::anyhow!(
|
|
"Compliance check failed, found {} commits not reviewed",
|
|
summary.not_reviewed
|
|
)),
|
|
ReportReviewSummary::MissingReviewsWithErrors if all_errors_known => {
|
|
println!(
|
|
"Compliance check failed with {} unreviewed commits, but all errors are known.",
|
|
summary.not_reviewed
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
ReportReviewSummary::MissingReviewsWithErrors => Err(anyhow::anyhow!(
|
|
"Compliance check failed with {} unreviewed commits and {} other issues",
|
|
summary.not_reviewed,
|
|
summary.errors
|
|
)),
|
|
ReportReviewSummary::NoIssuesFound => {
|
|
println!("No issues found, compliance check passed.");
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn check_compliance(args: ComplianceArgs) -> Result<()> {
|
|
tokio::runtime::Runtime::new()
|
|
.context("Failed to create tokio runtime")
|
|
.and_then(|handle| handle.block_on(check_compliance_impl(args)))
|
|
}
|