Skip to content

Commit f8fb3ef

Browse files
authored
Rollup merge of rust-lang#69624 - ehuss:toolstate-beta-regress, r=Mark-Simulacrum
Toolstate: Don't block beta week on already broken tools. This changes it so that tools are allowed to be broken entering the beta week if they are already broken. This restores the original behavior before the changes in rust-lang#69332. Closes rust-lang#68458
2 parents 0255561 + 22d840e commit f8fb3ef

File tree

1 file changed

+133
-63
lines changed

1 file changed

+133
-63
lines changed

Diff for: src/bootstrap/toolstate.rs

+133-63
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::env;
66
use std::fmt;
77
use std::fs;
88
use std::io::{Seek, SeekFrom};
9-
use std::path::PathBuf;
9+
use std::path::{Path, PathBuf};
1010
use std::process::Command;
1111
use std::time;
1212

@@ -24,7 +24,7 @@ const OS: Option<&str> = None;
2424

2525
type ToolstateData = HashMap<Box<str>, ToolState>;
2626

27-
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
27+
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd)]
2828
#[serde(rename_all = "kebab-case")]
2929
/// Whether a tool can be compiled, tested or neither
3030
pub enum ToolState {
@@ -143,10 +143,31 @@ pub struct ToolStateCheck;
143143
impl Step for ToolStateCheck {
144144
type Output = ();
145145

146-
/// Runs the `linkchecker` tool as compiled in `stage` by the `host` compiler.
146+
/// Checks tool state status.
147147
///
148-
/// This tool in `src/tools` will verify the validity of all our links in the
149-
/// documentation to ensure we don't have a bunch of dead ones.
148+
/// This is intended to be used in the `checktools.sh` script. To use
149+
/// this, set `save-toolstates` in `config.toml` so that tool status will
150+
/// be saved to a JSON file. Then, run `x.py test --no-fail-fast` for all
151+
/// of the tools to populate the JSON file. After that is done, this
152+
/// command can be run to check for any status failures, and exits with an
153+
/// error if there are any.
154+
///
155+
/// This also handles publishing the results to the `history` directory of
156+
/// the toolstate repo https://github.com/rust-lang-nursery/rust-toolstate
157+
/// if the env var `TOOLSTATE_PUBLISH` is set. Note that there is a
158+
/// *separate* step of updating the `latest.json` file and creating GitHub
159+
/// issues and comments in `src/ci/publish_toolstate.sh`, which is only
160+
/// performed on master. (The shell/python code is intended to be migrated
161+
/// here eventually.)
162+
///
163+
/// The rules for failure are:
164+
/// * If the PR modifies a tool, the status must be test-pass.
165+
/// NOTE: There is intent to change this, see
166+
/// https://github.com/rust-lang/rust/issues/65000.
167+
/// * All "stable" tools must be test-pass on the stable or beta branches.
168+
/// * During beta promotion week, a PR is not allowed to "regress" a
169+
/// stable tool. That is, the status is not allowed to get worse
170+
/// (test-pass to test-fail or build-fail).
150171
fn run(self, builder: &Builder<'_>) {
151172
if builder.config.dry_run {
152173
return;
@@ -171,6 +192,8 @@ impl Step for ToolStateCheck {
171192
}
172193

173194
check_changed_files(&toolstates);
195+
checkout_toolstate_repo();
196+
let old_toolstate = read_old_toolstate();
174197

175198
for (tool, _) in STABLE_TOOLS.iter() {
176199
let state = toolstates[*tool];
@@ -180,11 +203,24 @@ impl Step for ToolStateCheck {
180203
did_error = true;
181204
eprintln!("error: Tool `{}` should be test-pass but is {}", tool, state);
182205
} else if in_beta_week {
183-
did_error = true;
184-
eprintln!(
185-
"error: Tool `{}` should be test-pass but is {} during beta week.",
186-
tool, state
187-
);
206+
let old_state = old_toolstate
207+
.iter()
208+
.find(|ts| ts.tool == *tool)
209+
.expect("latest.json missing tool")
210+
.state();
211+
if state < old_state {
212+
did_error = true;
213+
eprintln!(
214+
"error: Tool `{}` has regressed from {} to {} during beta week.",
215+
tool, old_state, state
216+
);
217+
} else {
218+
eprintln!(
219+
"warning: Tool `{}` is not test-pass (is `{}`), \
220+
this should be fixed before beta is branched.",
221+
tool, state
222+
);
223+
}
188224
}
189225
}
190226
}
@@ -247,6 +283,70 @@ impl Builder<'_> {
247283
}
248284
}
249285

286+
fn toolstate_repo() -> String {
287+
env::var("TOOLSTATE_REPO")
288+
.unwrap_or_else(|_| "https://github.com/rust-lang-nursery/rust-toolstate.git".to_string())
289+
}
290+
291+
/// Directory where the toolstate repo is checked out.
292+
const TOOLSTATE_DIR: &str = "rust-toolstate";
293+
294+
/// Checks out the toolstate repo into `TOOLSTATE_DIR`.
295+
fn checkout_toolstate_repo() {
296+
if let Ok(token) = env::var("TOOLSTATE_REPO_ACCESS_TOKEN") {
297+
prepare_toolstate_config(&token);
298+
}
299+
if Path::new(TOOLSTATE_DIR).exists() {
300+
eprintln!("Cleaning old toolstate directory...");
301+
t!(fs::remove_dir_all(TOOLSTATE_DIR));
302+
}
303+
304+
let status = Command::new("git")
305+
.arg("clone")
306+
.arg("--depth=1")
307+
.arg(toolstate_repo())
308+
.arg(TOOLSTATE_DIR)
309+
.status();
310+
let success = match status {
311+
Ok(s) => s.success(),
312+
Err(_) => false,
313+
};
314+
if !success {
315+
panic!("git clone unsuccessful (status: {:?})", status);
316+
}
317+
}
318+
319+
/// Sets up config and authentication for modifying the toolstate repo.
320+
fn prepare_toolstate_config(token: &str) {
321+
fn git_config(key: &str, value: &str) {
322+
let status = Command::new("git").arg("config").arg("--global").arg(key).arg(value).status();
323+
let success = match status {
324+
Ok(s) => s.success(),
325+
Err(_) => false,
326+
};
327+
if !success {
328+
panic!("git config key={} value={} successful (status: {:?})", key, value, status);
329+
}
330+
}
331+
332+
// If changing anything here, then please check that src/ci/publish_toolstate.sh is up to date
333+
// as well.
334+
git_config("user.email", "7378925+rust-toolstate-update@users.noreply.github.com");
335+
git_config("user.name", "Rust Toolstate Update");
336+
git_config("credential.helper", "store");
337+
338+
let credential = format!("https://{}:x-oauth-basic@github.com\n", token,);
339+
let git_credential_path = PathBuf::from(t!(env::var("HOME"))).join(".git-credentials");
340+
t!(fs::write(&git_credential_path, credential));
341+
}
342+
343+
/// Reads the latest toolstate from the toolstate repo.
344+
fn read_old_toolstate() -> Vec<RepoState> {
345+
let latest_path = Path::new(TOOLSTATE_DIR).join("_data").join("latest.json");
346+
let old_toolstate = t!(fs::read(latest_path));
347+
t!(serde_json::from_slice(&old_toolstate))
348+
}
349+
250350
/// This function `commit_toolstate_change` provides functionality for pushing a change
251351
/// to the `rust-toolstate` repository.
252352
///
@@ -274,45 +374,7 @@ impl Builder<'_> {
274374
/// * See <https://help.github.com/articles/about-commit-email-addresses/>
275375
/// if a private email by GitHub is wanted.
276376
fn commit_toolstate_change(current_toolstate: &ToolstateData, in_beta_week: bool) {
277-
fn git_config(key: &str, value: &str) {
278-
let status = Command::new("git").arg("config").arg("--global").arg(key).arg(value).status();
279-
let success = match status {
280-
Ok(s) => s.success(),
281-
Err(_) => false,
282-
};
283-
if !success {
284-
panic!("git config key={} value={} successful (status: {:?})", key, value, status);
285-
}
286-
}
287-
288-
// If changing anything here, then please check that src/ci/publish_toolstate.sh is up to date
289-
// as well.
290-
git_config("user.email", "7378925+rust-toolstate-update@users.noreply.github.com");
291-
git_config("user.name", "Rust Toolstate Update");
292-
git_config("credential.helper", "store");
293-
294-
let credential = format!(
295-
"https://{}:x-oauth-basic@github.com\n",
296-
t!(env::var("TOOLSTATE_REPO_ACCESS_TOKEN")),
297-
);
298-
let git_credential_path = PathBuf::from(t!(env::var("HOME"))).join(".git-credentials");
299-
t!(fs::write(&git_credential_path, credential));
300-
301-
let status = Command::new("git")
302-
.arg("clone")
303-
.arg("--depth=1")
304-
.arg(t!(env::var("TOOLSTATE_REPO")))
305-
.status();
306-
let success = match status {
307-
Ok(s) => s.success(),
308-
Err(_) => false,
309-
};
310-
if !success {
311-
panic!("git clone successful (status: {:?})", status);
312-
}
313-
314-
let old_toolstate = t!(fs::read("rust-toolstate/_data/latest.json"));
315-
let old_toolstate: Vec<RepoState> = t!(serde_json::from_slice(&old_toolstate));
377+
let old_toolstate = read_old_toolstate();
316378

317379
let message = format!("({} CI update)", OS.expect("linux/windows only"));
318380
let mut success = false;
@@ -322,7 +384,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData, in_beta_week: bool
322384

323385
// `git commit` failing means nothing to commit.
324386
let status = t!(Command::new("git")
325-
.current_dir("rust-toolstate")
387+
.current_dir(TOOLSTATE_DIR)
326388
.arg("commit")
327389
.arg("-a")
328390
.arg("-m")
@@ -334,7 +396,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData, in_beta_week: bool
334396
}
335397

336398
let status = t!(Command::new("git")
337-
.current_dir("rust-toolstate")
399+
.current_dir(TOOLSTATE_DIR)
338400
.arg("push")
339401
.arg("origin")
340402
.arg("master")
@@ -347,14 +409,14 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData, in_beta_week: bool
347409
eprintln!("Sleeping for 3 seconds before retrying push");
348410
std::thread::sleep(std::time::Duration::from_secs(3));
349411
let status = t!(Command::new("git")
350-
.current_dir("rust-toolstate")
412+
.current_dir(TOOLSTATE_DIR)
351413
.arg("fetch")
352414
.arg("origin")
353415
.arg("master")
354416
.status());
355417
assert!(status.success());
356418
let status = t!(Command::new("git")
357-
.current_dir("rust-toolstate")
419+
.current_dir(TOOLSTATE_DIR)
358420
.arg("reset")
359421
.arg("--hard")
360422
.arg("origin/master")
@@ -375,18 +437,12 @@ fn change_toolstate(
375437
let mut regressed = false;
376438
for repo_state in old_toolstate {
377439
let tool = &repo_state.tool;
378-
let state = if cfg!(target_os = "linux") {
379-
&repo_state.linux
380-
} else if cfg!(windows) {
381-
&repo_state.windows
382-
} else {
383-
unimplemented!()
384-
};
440+
let state = repo_state.state();
385441
let new_state = current_toolstate[tool.as_str()];
386442

387-
if new_state != *state {
443+
if new_state != state {
388444
eprintln!("The state of `{}` has changed from `{}` to `{}`", tool, state, new_state);
389-
if (new_state as u8) < (*state as u8) {
445+
if new_state < state {
390446
if !["rustc-guide", "miri", "embedded-book"].contains(&tool.as_str()) {
391447
regressed = true;
392448
}
@@ -403,7 +459,9 @@ fn change_toolstate(
403459

404460
let toolstate_serialized = t!(serde_json::to_string(&current_toolstate));
405461

406-
let history_path = format!("rust-toolstate/history/{}.tsv", OS.expect("linux/windows only"));
462+
let history_path = Path::new(TOOLSTATE_DIR)
463+
.join("history")
464+
.join(format!("{}.tsv", OS.expect("linux/windows only")));
407465
let mut file = t!(fs::read_to_string(&history_path));
408466
let end_of_first_line = file.find('\n').unwrap();
409467
file.insert_str(end_of_first_line, &format!("\n{}\t{}", commit.trim(), toolstate_serialized));
@@ -418,3 +476,15 @@ struct RepoState {
418476
commit: String,
419477
datetime: String,
420478
}
479+
480+
impl RepoState {
481+
fn state(&self) -> ToolState {
482+
if cfg!(target_os = "linux") {
483+
self.linux
484+
} else if cfg!(windows) {
485+
self.windows
486+
} else {
487+
unimplemented!()
488+
}
489+
}
490+
}

0 commit comments

Comments
 (0)