Skip to content

Commit 3326a9f

Browse files
committed
Allow using glob aliases for custom try jobs
1 parent 0412507 commit 3326a9f

File tree

5 files changed

+116
-34
lines changed

5 files changed

+116
-34
lines changed

Diff for: src/ci/citool/Cargo.lock

+7
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ dependencies = [
107107
"build_helper",
108108
"clap",
109109
"csv",
110+
"glob-match",
110111
"insta",
111112
"serde",
112113
"serde_json",
@@ -308,6 +309,12 @@ dependencies = [
308309
"wasi",
309310
]
310311

312+
[[package]]
313+
name = "glob-match"
314+
version = "0.2.1"
315+
source = "registry+https://github.com/rust-lang/crates.io-index"
316+
checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d"
317+
311318
[[package]]
312319
name = "hashbrown"
313320
version = "0.15.2"

Diff for: src/ci/citool/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ edition = "2021"
77
anyhow = "1"
88
clap = { version = "4.5", features = ["derive"] }
99
csv = "1"
10+
glob-match = "0.2"
1011
serde = { version = "1", features = ["derive"] }
1112
serde_yaml = "0.9"
1213
serde_json = "1"

Diff for: src/ci/citool/src/jobs.rs

+33-26
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use crate::{GitHubContext, utils};
2-
use serde_yaml::Value;
1+
#[cfg(test)]
2+
mod tests;
3+
34
use std::collections::BTreeMap;
4-
use std::path::Path;
55

66
use serde_yaml::Value;
77

@@ -65,13 +65,19 @@ pub struct JobDatabase {
6565
}
6666

6767
impl JobDatabase {
68-
fn find_auto_job_by_name(&self, name: &str) -> Option<Job> {
69-
self.auto_jobs.iter().find(|j| j.name == name).cloned()
68+
/// Find `auto` jobs that correspond to the passed `pattern`.
69+
/// Patterns are matched using the glob syntax.
70+
/// For example `dist-*` matches all jobs starting with `dist-`.
71+
fn find_auto_jobs_by_pattern(&self, pattern: &str) -> Vec<Job> {
72+
self.auto_jobs
73+
.iter()
74+
.filter(|j| glob_match::glob_match(pattern, &j.name))
75+
.cloned()
76+
.collect()
7077
}
7178
}
7279

73-
pub fn load_job_db(path: &Path) -> anyhow::Result<JobDatabase> {
74-
let db = utils::read_to_string(path)?;
80+
pub fn load_job_db(db: &str) -> anyhow::Result<JobDatabase> {
7581
let mut db: Value = serde_yaml::from_str(&db)?;
7682

7783
// We need to expand merge keys (<<), because serde_yaml can't deal with them
@@ -114,7 +120,7 @@ pub enum RunType {
114120
/// Workflows that run after a push to a PR branch
115121
PullRequest,
116122
/// Try run started with @bors try
117-
TryJob { custom_jobs: Option<Vec<String>> },
123+
TryJob { job_patterns: Option<Vec<String>> },
118124
/// Merge attempt workflow
119125
AutoJob,
120126
}
@@ -130,28 +136,29 @@ fn calculate_jobs(
130136
) -> anyhow::Result<Vec<GithubActionsJob>> {
131137
let (jobs, prefix, base_env) = match run_type {
132138
RunType::PullRequest => (db.pr_jobs.clone(), "PR", &db.envs.pr_env),
133-
RunType::TryJob { custom_jobs } => {
134-
let jobs = if let Some(custom_jobs) = custom_jobs {
135-
if custom_jobs.len() > MAX_TRY_JOBS_COUNT {
136-
return Err(anyhow::anyhow!(
137-
"It is only possible to schedule up to {MAX_TRY_JOBS_COUNT} custom jobs, received {} custom jobs",
138-
custom_jobs.len()
139-
));
140-
}
141-
142-
let mut jobs = vec![];
143-
let mut unknown_jobs = vec![];
144-
for custom_job in custom_jobs {
145-
if let Some(job) = db.find_auto_job_by_name(custom_job) {
146-
jobs.push(job);
139+
RunType::TryJob { job_patterns } => {
140+
let jobs = if let Some(patterns) = job_patterns {
141+
let mut jobs: Vec<Job> = vec![];
142+
let mut unknown_patterns = vec![];
143+
for pattern in patterns {
144+
let matched_jobs = db.find_auto_jobs_by_pattern(pattern);
145+
if matched_jobs.is_empty() {
146+
unknown_patterns.push(pattern.clone());
147147
} else {
148-
unknown_jobs.push(custom_job.clone());
148+
jobs.extend(matched_jobs);
149149
}
150150
}
151-
if !unknown_jobs.is_empty() {
151+
if !unknown_patterns.is_empty() {
152+
return Err(anyhow::anyhow!(
153+
"Patterns `{}` did not match any auto jobs",
154+
unknown_patterns.join(", ")
155+
));
156+
}
157+
if jobs.len() > MAX_TRY_JOBS_COUNT {
152158
return Err(anyhow::anyhow!(
153-
"Custom job(s) `{}` not found in auto jobs",
154-
unknown_jobs.join(", ")
159+
"It is only possible to schedule up to {MAX_TRY_JOBS_COUNT} custom jobs, received {} custom jobs expanded from {} pattern(s)",
160+
jobs.len(),
161+
patterns.len()
155162
));
156163
}
157164
jobs

Diff for: src/ci/citool/src/jobs/tests.rs

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use crate::jobs::{JobDatabase, load_job_db};
2+
3+
#[test]
4+
fn lookup_job_pattern() {
5+
let db = load_job_db(
6+
r#"
7+
envs:
8+
pr:
9+
try:
10+
auto:
11+
12+
pr:
13+
try:
14+
auto:
15+
- name: dist-a
16+
os: ubuntu
17+
env: {}
18+
- name: dist-a-alt
19+
os: ubuntu
20+
env: {}
21+
- name: dist-b
22+
os: ubuntu
23+
env: {}
24+
- name: dist-b-alt
25+
os: ubuntu
26+
env: {}
27+
- name: test-a
28+
os: ubuntu
29+
env: {}
30+
- name: test-a-alt
31+
os: ubuntu
32+
env: {}
33+
- name: test-i686
34+
os: ubuntu
35+
env: {}
36+
- name: dist-i686
37+
os: ubuntu
38+
env: {}
39+
- name: test-msvc-i686-1
40+
os: ubuntu
41+
env: {}
42+
- name: test-msvc-i686-2
43+
os: ubuntu
44+
env: {}
45+
"#,
46+
)
47+
.unwrap();
48+
check_pattern(&db, "dist-*", &["dist-a", "dist-a-alt", "dist-b", "dist-b-alt", "dist-i686"]);
49+
check_pattern(&db, "*-alt", &["dist-a-alt", "dist-b-alt", "test-a-alt"]);
50+
check_pattern(&db, "dist*-alt", &["dist-a-alt", "dist-b-alt"]);
51+
check_pattern(
52+
&db,
53+
"*i686*",
54+
&["test-i686", "dist-i686", "test-msvc-i686-1", "test-msvc-i686-2"],
55+
);
56+
}
57+
58+
#[track_caller]
59+
fn check_pattern(db: &JobDatabase, pattern: &str, expected: &[&str]) {
60+
let jobs =
61+
db.find_auto_jobs_by_pattern(pattern).into_iter().map(|j| j.name).collect::<Vec<_>>();
62+
63+
assert_eq!(jobs, expected);
64+
}

Diff for: src/ci/citool/src/main.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,21 @@ impl GitHubContext {
3535
fn get_run_type(&self) -> Option<RunType> {
3636
match (self.event_name.as_str(), self.branch_ref.as_str()) {
3737
("pull_request", _) => Some(RunType::PullRequest),
38-
("push", "refs/heads/try-perf") => Some(RunType::TryJob { custom_jobs: None }),
38+
("push", "refs/heads/try-perf") => Some(RunType::TryJob { job_patterns: None }),
3939
("push", "refs/heads/try" | "refs/heads/automation/bors/try") => {
40-
let custom_jobs = self.get_custom_jobs();
41-
let custom_jobs = if !custom_jobs.is_empty() { Some(custom_jobs) } else { None };
42-
Some(RunType::TryJob { custom_jobs })
40+
let patterns = self.get_try_job_patterns();
41+
let patterns = if !patterns.is_empty() { Some(patterns) } else { None };
42+
Some(RunType::TryJob { job_patterns: patterns })
4343
}
4444
("push", "refs/heads/auto") => Some(RunType::AutoJob),
4545
_ => None,
4646
}
4747
}
4848

49-
/// Tries to parse names of specific CI jobs that should be executed in the form of
50-
/// try-job: <job-name>
49+
/// Tries to parse patterns of CI jobs that should be executed in the form of
50+
/// try-job: <job-pattern>
5151
/// from the commit message of the passed GitHub context.
52-
fn get_custom_jobs(&self) -> Vec<String> {
52+
fn get_try_job_patterns(&self) -> Vec<String> {
5353
if let Some(ref msg) = self.commit_message {
5454
msg.lines()
5555
.filter_map(|line| line.trim().strip_prefix("try-job: "))
@@ -180,7 +180,10 @@ pub enum JobType {
180180
fn main() -> anyhow::Result<()> {
181181
let args = Args::parse();
182182
let default_jobs_file = Path::new(JOBS_YML_PATH);
183-
let load_db = |jobs_path| jobs::load_job_db(jobs_path).context("Cannot load jobs.yml");
183+
let load_db = |jobs_path| {
184+
let db = utils::read_to_string(jobs_path)?;
185+
Ok::<_, anyhow::Error>(jobs::load_job_db(&db).context("Cannot load jobs.yml")?)
186+
};
184187

185188
match args {
186189
Args::CalculateJobMatrix { jobs_file } => {

0 commit comments

Comments
 (0)