Skip to content

Commit 1d7d4d4

Browse files
Merge #1021
1021: Allow CI without requiring targets to be built/tested. r=Emilgardis a=Alexhuszagh Co-authored-by: Emil Gardström <[email protected]> Co-authored-by: Alex Huszagh <[email protected]>
2 parents a8082de + b3c7b40 commit 1d7d4d4

File tree

3 files changed

+126
-11
lines changed

3 files changed

+126
-11
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ jobs:
9797
env:
9898
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
9999
COMMIT_AUTHOR: ${{ github.event.head_commit.author.username }}
100+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
100101

101102
build:
102103
name: target (${{ matrix.pretty }},${{ matrix.os }})

xtask/src/ci/target_matrix.rs

Lines changed: 113 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1+
use std::process::Command;
2+
13
use clap::builder::BoolishValueParser;
24
use clap::Parser;
3-
use serde::Serialize;
5+
use cross::{shell::Verbosity, CommandExt};
6+
use serde::{Deserialize, Serialize};
47

58
use crate::util::{get_matrix, gha_output, gha_print, CiTarget, ImageTarget};
69

710
pub(crate) fn run(message: String, author: String) -> Result<(), color_eyre::Report> {
811
let mut matrix: Vec<CiTarget> = get_matrix().clone();
9-
if author == "bors[bot]" && message.starts_with("Try #") {
10-
if let Some((_, args)) = message.split_once(": ") {
11-
let app = TargetMatrixArgs::parse_from(args.split(' '));
12-
app.filter(&mut matrix);
13-
}
12+
let (prs, mut app) = if author == "bors[bot]" {
13+
process_bors_message(&message)?
1414
} else {
15-
gha_print("Running all targets.");
15+
(vec![], TargetMatrixArgs::default())
16+
};
17+
18+
if !prs.is_empty()
19+
&& prs.iter().try_fold(true, |b, pr| {
20+
Ok::<_, eyre::Report>(b && has_no_ci_target(pr)?)
21+
})?
22+
{
23+
app.none = true;
1624
}
1725

26+
app.filter(&mut matrix);
27+
1828
let matrix = matrix
1929
.iter()
2030
.map(|target| TargetMatrixElement {
@@ -32,12 +42,68 @@ pub(crate) fn run(message: String, author: String) -> Result<(), color_eyre::Rep
3242
std: target.std.map(|b| b as u8),
3343
})
3444
.collect::<Vec<_>>();
45+
3546
let json = serde_json::to_string(&matrix)?;
3647
gha_print(&json);
3748
gha_output("matrix", &json);
3849
Ok(())
3950
}
4051

52+
fn parse_gh_labels(pr: &str) -> cross::Result<Vec<String>> {
53+
#[derive(Deserialize)]
54+
struct PullRequest {
55+
labels: Vec<PullRequestLabels>,
56+
}
57+
58+
#[derive(Deserialize)]
59+
struct PullRequestLabels {
60+
name: String,
61+
}
62+
eyre::ensure!(
63+
pr.chars().all(|c| c.is_ascii_digit()),
64+
"pr should be a number, got {:?}",
65+
pr
66+
);
67+
let stdout = Command::new("gh")
68+
.args(["pr", "view", pr, "--json", "labels"])
69+
.run_and_get_stdout(&mut Verbosity::Quiet.into())?;
70+
let pr_info: PullRequest = serde_json::from_str(&stdout)?;
71+
Ok(pr_info.labels.into_iter().map(|l| l.name).collect())
72+
}
73+
74+
fn has_no_ci_target(pr: &str) -> cross::Result<bool> {
75+
Ok(parse_gh_labels(pr)?.contains(&"no-ci-targets".to_owned()))
76+
}
77+
78+
/// Returns the pr(s) associated with this bors commit and the app to use for processing
79+
fn process_bors_message(message: &str) -> cross::Result<(Vec<&str>, TargetMatrixArgs)> {
80+
if let Some(message) = message.strip_prefix("Try #") {
81+
let (pr, args) = message
82+
.split_once(':')
83+
.ok_or_else(|| eyre::eyre!("bors message must start with \"Try #:\""))?;
84+
let args = args.trim_start();
85+
let app = if !args.is_empty() {
86+
TargetMatrixArgs::parse_from(args.split(' '))
87+
} else {
88+
TargetMatrixArgs::default()
89+
};
90+
Ok((vec![pr], app))
91+
} else if let Some(message) = message.strip_prefix("Merge") {
92+
Ok((
93+
message
94+
.lines()
95+
.next()
96+
.unwrap_or_default()
97+
.split(" #")
98+
.skip(1)
99+
.collect(),
100+
TargetMatrixArgs::default(),
101+
))
102+
} else {
103+
eyre::bail!("unexpected bors commit message encountered")
104+
}
105+
}
106+
41107
#[derive(Serialize)]
42108
#[serde(rename_all = "kebab-case")]
43109
struct TargetMatrixElement<'a> {
@@ -63,7 +129,7 @@ struct TargetMatrixElement<'a> {
63129
std: Option<u8>,
64130
}
65131

66-
#[derive(Parser, Debug)]
132+
#[derive(Parser, Debug, Default, PartialEq, Eq)]
67133
#[clap(no_binary_name = true)]
68134
struct TargetMatrixArgs {
69135
#[clap(long, short, num_args = 0..)]
@@ -84,7 +150,11 @@ struct TargetMatrixArgs {
84150

85151
impl TargetMatrixArgs {
86152
pub fn filter(&self, matrix: &mut Vec<CiTarget>) {
153+
if self == &TargetMatrixArgs::default() {
154+
gha_print("Running all targets.");
155+
}
87156
if self.none {
157+
gha_print("Running no targets.");
88158
std::mem::take(matrix);
89159
return;
90160
}
@@ -196,4 +266,39 @@ mod tests {
196266
let matrix = run(["--none"]);
197267
assert_eq!(&Vec::<CiTarget>::new(), &matrix);
198268
}
269+
270+
#[test]
271+
fn prs() {
272+
assert_eq!(
273+
process_bors_message("Merge #1337\n1337: merge").unwrap().0,
274+
vec!["1337"]
275+
);
276+
assert_eq!(
277+
process_bors_message("Merge #1337 #42\n1337: merge\n42: merge 2")
278+
.unwrap()
279+
.0,
280+
vec!["1337", "42"]
281+
);
282+
assert_eq!(
283+
// the trailing space is intentional
284+
process_bors_message("Try #1337: \n").unwrap().0,
285+
vec!["1337"]
286+
);
287+
}
288+
289+
#[test]
290+
fn full_invocation() {
291+
let (prs, app) = process_bors_message("Try #1337: ").unwrap();
292+
assert_eq!(prs, vec!["1337"]);
293+
assert_eq!(app, TargetMatrixArgs::default());
294+
let (prs, app) = process_bors_message("Try #1337: --std 1").unwrap();
295+
assert_eq!(prs, vec!["1337"]);
296+
assert_eq!(
297+
app,
298+
TargetMatrixArgs {
299+
std: Some(true),
300+
..TargetMatrixArgs::default()
301+
}
302+
);
303+
}
199304
}

xtask/src/util.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,14 +276,23 @@ pub fn project_dir(msg_info: &mut MessageInfo) -> cross::Result<PathBuf> {
276276
Ok(cargo_metadata(msg_info)?.workspace_root)
277277
}
278278

279+
macro_rules! gha_output {
280+
($fmt:literal$(, $args:expr)* $(,)?) => {
281+
#[cfg(not(test))]
282+
println!($fmt $(, $args)*);
283+
#[cfg(test)]
284+
eprintln!($fmt $(,$args)*);
285+
};
286+
}
287+
279288
// note: for GHA actions we need to output these tags no matter the verbosity level
280289
pub fn gha_print(content: &str) {
281-
println!("{}", content)
290+
gha_output!("{}", content);
282291
}
283292

284293
// note: for GHA actions we need to output these tags no matter the verbosity level
285294
pub fn gha_error(content: &str) {
286-
println!("::error {}", content)
295+
gha_output!("::error {}", content);
287296
}
288297

289298
#[track_caller]
@@ -292,7 +301,7 @@ pub fn gha_output(tag: &str, content: &str) {
292301
// https://github.com/actions/toolkit/issues/403
293302
panic!("output `{tag}` contains newlines, consider serializing with json and deserializing in gha with fromJSON()")
294303
}
295-
println!("::set-output name={tag}::{}", content)
304+
gha_output!("::set-output name={tag}::{}", content);
296305
}
297306

298307
pub fn read_dockerfiles(msg_info: &mut MessageInfo) -> cross::Result<Vec<(PathBuf, String)>> {

0 commit comments

Comments
 (0)