From b3c7b408184b2b761fcd0f8ae236a1c93373a928 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Mon, 31 Oct 2022 11:11:15 -0500 Subject: [PATCH] Update xtask/src/ci/target_matrix.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Emil Gardström --- .github/workflows/ci.yml | 1 + xtask/src/ci/target_matrix.rs | 121 +++++++++++++++++++++++++++++++--- xtask/src/util.rs | 15 ++++- 3 files changed, 126 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35d6081ea..c024c0900 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,6 +97,7 @@ jobs: env: COMMIT_MESSAGE: ${{ github.event.head_commit.message }} COMMIT_AUTHOR: ${{ github.event.head_commit.author.username }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} build: name: target (${{ matrix.pretty }},${{ matrix.os }}) diff --git a/xtask/src/ci/target_matrix.rs b/xtask/src/ci/target_matrix.rs index 5739180ea..99c7641e6 100644 --- a/xtask/src/ci/target_matrix.rs +++ b/xtask/src/ci/target_matrix.rs @@ -1,20 +1,30 @@ +use std::process::Command; + use clap::builder::BoolishValueParser; use clap::Parser; -use serde::Serialize; +use cross::{shell::Verbosity, CommandExt}; +use serde::{Deserialize, Serialize}; use crate::util::{get_matrix, gha_output, gha_print, CiTarget, ImageTarget}; pub(crate) fn run(message: String, author: String) -> Result<(), color_eyre::Report> { let mut matrix: Vec = get_matrix().clone(); - if author == "bors[bot]" && message.starts_with("Try #") { - if let Some((_, args)) = message.split_once(": ") { - let app = TargetMatrixArgs::parse_from(args.split(' ')); - app.filter(&mut matrix); - } + let (prs, mut app) = if author == "bors[bot]" { + process_bors_message(&message)? } else { - gha_print("Running all targets."); + (vec![], TargetMatrixArgs::default()) + }; + + if !prs.is_empty() + && prs.iter().try_fold(true, |b, pr| { + Ok::<_, eyre::Report>(b && has_no_ci_target(pr)?) + })? + { + app.none = true; } + app.filter(&mut matrix); + let matrix = matrix .iter() .map(|target| TargetMatrixElement { @@ -32,12 +42,68 @@ pub(crate) fn run(message: String, author: String) -> Result<(), color_eyre::Rep std: target.std.map(|b| b as u8), }) .collect::>(); + let json = serde_json::to_string(&matrix)?; gha_print(&json); gha_output("matrix", &json); Ok(()) } +fn parse_gh_labels(pr: &str) -> cross::Result> { + #[derive(Deserialize)] + struct PullRequest { + labels: Vec, + } + + #[derive(Deserialize)] + struct PullRequestLabels { + name: String, + } + eyre::ensure!( + pr.chars().all(|c| c.is_ascii_digit()), + "pr should be a number, got {:?}", + pr + ); + let stdout = Command::new("gh") + .args(["pr", "view", pr, "--json", "labels"]) + .run_and_get_stdout(&mut Verbosity::Quiet.into())?; + let pr_info: PullRequest = serde_json::from_str(&stdout)?; + Ok(pr_info.labels.into_iter().map(|l| l.name).collect()) +} + +fn has_no_ci_target(pr: &str) -> cross::Result { + Ok(parse_gh_labels(pr)?.contains(&"no-ci-targets".to_owned())) +} + +/// Returns the pr(s) associated with this bors commit and the app to use for processing +fn process_bors_message(message: &str) -> cross::Result<(Vec<&str>, TargetMatrixArgs)> { + if let Some(message) = message.strip_prefix("Try #") { + let (pr, args) = message + .split_once(':') + .ok_or_else(|| eyre::eyre!("bors message must start with \"Try #:\""))?; + let args = args.trim_start(); + let app = if !args.is_empty() { + TargetMatrixArgs::parse_from(args.split(' ')) + } else { + TargetMatrixArgs::default() + }; + Ok((vec![pr], app)) + } else if let Some(message) = message.strip_prefix("Merge") { + Ok(( + message + .lines() + .next() + .unwrap_or_default() + .split(" #") + .skip(1) + .collect(), + TargetMatrixArgs::default(), + )) + } else { + eyre::bail!("unexpected bors commit message encountered") + } +} + #[derive(Serialize)] #[serde(rename_all = "kebab-case")] struct TargetMatrixElement<'a> { @@ -63,7 +129,7 @@ struct TargetMatrixElement<'a> { std: Option, } -#[derive(Parser, Debug)] +#[derive(Parser, Debug, Default, PartialEq, Eq)] #[clap(no_binary_name = true)] struct TargetMatrixArgs { #[clap(long, short, num_args = 0..)] @@ -84,7 +150,11 @@ struct TargetMatrixArgs { impl TargetMatrixArgs { pub fn filter(&self, matrix: &mut Vec) { + if self == &TargetMatrixArgs::default() { + gha_print("Running all targets."); + } if self.none { + gha_print("Running no targets."); std::mem::take(matrix); return; } @@ -196,4 +266,39 @@ mod tests { let matrix = run(["--none"]); assert_eq!(&Vec::::new(), &matrix); } + + #[test] + fn prs() { + assert_eq!( + process_bors_message("Merge #1337\n1337: merge").unwrap().0, + vec!["1337"] + ); + assert_eq!( + process_bors_message("Merge #1337 #42\n1337: merge\n42: merge 2") + .unwrap() + .0, + vec!["1337", "42"] + ); + assert_eq!( + // the trailing space is intentional + process_bors_message("Try #1337: \n").unwrap().0, + vec!["1337"] + ); + } + + #[test] + fn full_invocation() { + let (prs, app) = process_bors_message("Try #1337: ").unwrap(); + assert_eq!(prs, vec!["1337"]); + assert_eq!(app, TargetMatrixArgs::default()); + let (prs, app) = process_bors_message("Try #1337: --std 1").unwrap(); + assert_eq!(prs, vec!["1337"]); + assert_eq!( + app, + TargetMatrixArgs { + std: Some(true), + ..TargetMatrixArgs::default() + } + ); + } } diff --git a/xtask/src/util.rs b/xtask/src/util.rs index 3e87e3899..edac27ee4 100644 --- a/xtask/src/util.rs +++ b/xtask/src/util.rs @@ -276,14 +276,23 @@ pub fn project_dir(msg_info: &mut MessageInfo) -> cross::Result { Ok(cargo_metadata(msg_info)?.workspace_root) } +macro_rules! gha_output { + ($fmt:literal$(, $args:expr)* $(,)?) => { + #[cfg(not(test))] + println!($fmt $(, $args)*); + #[cfg(test)] + eprintln!($fmt $(,$args)*); + }; +} + // note: for GHA actions we need to output these tags no matter the verbosity level pub fn gha_print(content: &str) { - println!("{}", content) + gha_output!("{}", content); } // note: for GHA actions we need to output these tags no matter the verbosity level pub fn gha_error(content: &str) { - println!("::error {}", content) + gha_output!("::error {}", content); } #[track_caller] @@ -292,7 +301,7 @@ pub fn gha_output(tag: &str, content: &str) { // https://github.com/actions/toolkit/issues/403 panic!("output `{tag}` contains newlines, consider serializing with json and deserializing in gha with fromJSON()") } - println!("::set-output name={tag}::{}", content) + gha_output!("::set-output name={tag}::{}", content); } pub fn read_dockerfiles(msg_info: &mut MessageInfo) -> cross::Result> {