-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Josh preparations #12759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
flip1995
wants to merge
4
commits into
rust-lang:master
Choose a base branch
from
flip1995:josh-automation
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Josh preparations #12759
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,248 @@ | ||
use crate::utils::{FileUpdater, update_text_region_fn}; | ||
use chrono::offset::Utc; | ||
use std::fmt::Write; | ||
use std::process; | ||
use std::process::exit; | ||
|
||
pub fn update_nightly() { | ||
use xshell::{Shell, cmd}; | ||
|
||
const JOSH_FILTER: &str = ":rev(0450db33a5d8587f7c1d4b6d233dac963605766b:prefix=src/tools/clippy):/src/tools/clippy"; | ||
const JOSH_PORT: &str = "42042"; | ||
const TOOLCHAIN_TOML: &str = "rust-toolchain.toml"; | ||
const UTILS_README: &str = "clippy_utils/README.md"; | ||
|
||
fn start_josh() -> impl Drop { | ||
// Create a wrapper that stops it on drop. | ||
struct Josh(process::Child); | ||
impl Drop for Josh { | ||
fn drop(&mut self) { | ||
#[cfg(unix)] | ||
{ | ||
// Try to gracefully shut it down. | ||
process::Command::new("kill") | ||
.args(["-s", "INT", &self.0.id().to_string()]) | ||
.output() | ||
.expect("failed to SIGINT josh-proxy"); | ||
// Sadly there is no "wait with timeout"... so we just give it some time to finish. | ||
std::thread::sleep(std::time::Duration::from_secs(1)); | ||
// Now hopefully it is gone. | ||
if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() { | ||
return; | ||
} | ||
} | ||
// If that didn't work (or we're not on Unix), kill it hard. | ||
eprintln!("I have to kill josh-proxy the hard way, let's hope this does not break anything."); | ||
self.0.kill().expect("failed to SIGKILL josh-proxy"); | ||
} | ||
} | ||
|
||
// Determine cache directory. | ||
let local_dir = { | ||
let user_dirs = directories::ProjectDirs::from("org", "rust-lang", "clippy-josh").unwrap(); | ||
user_dirs.cache_dir().to_owned() | ||
}; | ||
println!("Using local cache directory: {}", local_dir.display()); | ||
|
||
// Start josh, silencing its output. | ||
let mut cmd = process::Command::new("josh-proxy"); | ||
cmd.arg("--local").arg(local_dir); | ||
cmd.arg("--remote").arg("https://github.com"); | ||
cmd.arg("--port").arg(JOSH_PORT); | ||
cmd.arg("--no-background"); | ||
cmd.stdout(process::Stdio::null()); | ||
cmd.stderr(process::Stdio::null()); | ||
let josh = cmd | ||
.spawn() | ||
.expect("failed to start josh-proxy, make sure it is installed"); | ||
// Give it some time so hopefully the port is open. | ||
std::thread::sleep(std::time::Duration::from_secs(1)); | ||
|
||
Josh(josh) | ||
} | ||
|
||
fn rustc_hash() -> String { | ||
let sh = Shell::new().expect("failed to create shell"); | ||
// Make sure we pick up the updated toolchain (usually rustup pins the toolchain | ||
// inside a single cargo/rustc invocation via this env var). | ||
sh.set_var("RUSTUP_TOOLCHAIN", ""); | ||
cmd!(sh, "rustc --version --verbose") | ||
.read() | ||
.expect("failed to run `rustc -vV`") | ||
.lines() | ||
.find(|line| line.starts_with("commit-hash:")) | ||
.expect("failed to parse `rustc -vV`") | ||
.split_whitespace() | ||
.last() | ||
.expect("failed to get commit from `rustc -vV`") | ||
.to_string() | ||
} | ||
|
||
fn assert_clean_repo(sh: &Shell) { | ||
if !cmd!(sh, "git status --untracked-files=no --porcelain") | ||
.read() | ||
.expect("failed to run git status") | ||
.is_empty() | ||
{ | ||
eprintln!("working directory must be clean before running `cargo dev sync pull`"); | ||
exit(1); | ||
} | ||
} | ||
|
||
pub fn rustc_pull() { | ||
const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; | ||
|
||
let sh = Shell::new().expect("failed to create shell"); | ||
|
||
assert_clean_repo(&sh); | ||
|
||
// Update rust-toolchain file | ||
let date = Utc::now().format("%Y-%m-%d").to_string(); | ||
let update = &mut update_text_region_fn( | ||
let toolchain_update = &mut update_text_region_fn( | ||
"# begin autogenerated nightly\n", | ||
"# end autogenerated nightly", | ||
|dst| { | ||
writeln!(dst, "channel = \"nightly-{date}\"").unwrap(); | ||
}, | ||
); | ||
let readme_update = &mut update_text_region_fn( | ||
"<!-- begin autogenerated nightly -->\n", | ||
"<!-- end autogenerated nightly -->", | ||
|dst| { | ||
writeln!(dst, "```\nnightly-{date}\n```").unwrap(); | ||
}, | ||
); | ||
|
||
let mut updater = FileUpdater::default(); | ||
updater.update_file("rust-toolchain.toml", update); | ||
updater.update_file("clippy_utils/README.md", update); | ||
updater.update_file(TOOLCHAIN_TOML, toolchain_update); | ||
updater.update_file(UTILS_README, readme_update); | ||
|
||
let message = format!("Bump nightly version -> {date}"); | ||
cmd!( | ||
sh, | ||
"git commit --no-verify -m {message} -- {TOOLCHAIN_TOML} {UTILS_README}" | ||
) | ||
.run() | ||
.expect("FAILED to commit rust-toolchain.toml file, something went wrong"); | ||
|
||
let commit = rustc_hash(); | ||
|
||
// Make sure josh is running in this scope | ||
{ | ||
let _josh = start_josh(); | ||
|
||
// Fetch given rustc commit. | ||
cmd!( | ||
sh, | ||
"git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git" | ||
) | ||
.run() | ||
.inspect_err(|_| { | ||
// Try to un-do the previous `git commit`, to leave the repo in the state we found it. | ||
cmd!(sh, "git reset --hard HEAD^") | ||
.run() | ||
.expect("FAILED to clean up again after failed `git fetch`, sorry for that"); | ||
}) | ||
.expect("FAILED to fetch new commits, something went wrong"); | ||
} | ||
|
||
// This should not add any new root commits. So count those before and after merging. | ||
let num_roots = || -> u32 { | ||
cmd!(sh, "git rev-list HEAD --max-parents=0 --count") | ||
.read() | ||
.expect("failed to determine the number of root commits") | ||
.parse::<u32>() | ||
.unwrap() | ||
}; | ||
let num_roots_before = num_roots(); | ||
|
||
// Merge the fetched commit. | ||
cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") | ||
.run() | ||
.expect("FAILED to merge new commits, something went wrong"); | ||
|
||
// Check that the number of roots did not increase. | ||
if num_roots() != num_roots_before { | ||
eprintln!("Josh created a new root commit. This is probably not the history you want."); | ||
exit(1); | ||
} | ||
} | ||
|
||
pub(crate) const PUSH_PR_DESCRIPTION: &str = "Sync from Clippy commit:"; | ||
|
||
pub fn rustc_push(rustc_path: String, github_user: &str, branch: &str, force: bool) { | ||
let sh = Shell::new().expect("failed to create shell"); | ||
|
||
assert_clean_repo(&sh); | ||
|
||
// Prepare the branch. Pushing works much better if we use as base exactly | ||
// the commit that we pulled from last time, so we use the `rustc --version` | ||
// to find out which commit that would be. | ||
let base = rustc_hash(); | ||
|
||
println!("Preparing {github_user}/rust (base: {base})..."); | ||
sh.change_dir(rustc_path); | ||
if !force | ||
&& cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}") | ||
.ignore_stderr() | ||
.read() | ||
.is_ok() | ||
{ | ||
eprintln!( | ||
"The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again." | ||
); | ||
exit(1); | ||
} | ||
cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}") | ||
.run() | ||
.expect("failed to fetch base commit"); | ||
let force_flag = if force { "--force" } else { "" }; | ||
cmd!( | ||
sh, | ||
"git push https://github.com/{github_user}/rust {base}:refs/heads/{branch} {force_flag}" | ||
) | ||
.ignore_stdout() | ||
.ignore_stderr() // silence the "create GitHub PR" message | ||
.run() | ||
.expect("failed to push base commit to the new branch"); | ||
|
||
// Make sure josh is running in this scope | ||
{ | ||
let _josh = start_josh(); | ||
|
||
// Do the actual push. | ||
println!("Pushing Clippy changes..."); | ||
cmd!( | ||
sh, | ||
"git push http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git HEAD:{branch}" | ||
) | ||
.run() | ||
.expect("failed to push changes to Josh"); | ||
|
||
// Do a round-trip check to make sure the push worked as expected. | ||
cmd!( | ||
sh, | ||
"git fetch http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git {branch}" | ||
) | ||
.ignore_stderr() | ||
.read() | ||
.expect("failed to fetch the branch from Josh"); | ||
} | ||
|
||
let head = cmd!(sh, "git rev-parse HEAD") | ||
.read() | ||
.expect("failed to get HEAD commit"); | ||
let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD") | ||
.read() | ||
.expect("failed to get FETCH_HEAD"); | ||
if head != fetch_head { | ||
eprintln!("Josh created a non-roundtrip push! Do NOT merge this into rustc!"); | ||
exit(1); | ||
} | ||
println!("Confirmed that the push round-trips back to Clippy properly. Please create a rustc PR:"); | ||
let description = format!("{}+rust-lang/rust-clippy@{head}", PUSH_PR_DESCRIPTION.replace(' ', "+")); | ||
println!( | ||
// Open PR with `subtree update` title to silence the `no-merges` triagebot check | ||
// See https://github.com/rust-lang/rust/pull/114157 | ||
" https://github.com/rust-lang/rust/compare/{github_user}:{branch}?quick_pull=1&title=Clippy+subtree+update&body=r?+@ghost%0A%0A{description}" | ||
); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.