Skip to content

Commit a19d69d

Browse files
authored
Sync and Release automation (rust-lang#13694)
Based on rust-lang/rust-clippy#13693 Adds 2 subcommands to `cargo dev`: - `cargo dev sync update_nightly`: Which updates the nightly versions in `rust-toolchain` and `clippy_utils/README.md` - `cargo dev release bump_version`: Bumps the version in all relevant `Cargo.toml` files Those are pulled out of rust-lang/rust-clippy#12759, which I'll rebase on this. Next step is to update the documentation, which I'll partly pull out of rust-lang/rust-clippy#12762 r? @blyxyas (as you reviewed the first PR in the chain and were assigned to the second one) cc rust-lang/rust-clippy#13556 changelog: none
2 parents cfd17d4 + 93d5ccd commit a19d69d

File tree

16 files changed

+263
-154
lines changed

16 files changed

+263
-154
lines changed

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
[package]
22
name = "clippy"
3+
# begin autogenerated version
34
version = "0.1.84"
5+
# end autogenerated version
46
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
57
repository = "https://github.com/rust-lang/rust-clippy"
68
readme = "README.md"

clippy_config/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
[package]
22
name = "clippy_config"
3+
# begin autogenerated version
34
version = "0.1.84"
5+
# end autogenerated version
46
edition = "2021"
57
publish = false
68

clippy_dev/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2021"
66

77
[dependencies]
88
aho-corasick = "1.0"
9+
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
910
clap = { version = "4.4", features = ["derive"] }
1011
indoc = "1.0"
1112
itertools = "0.12"

clippy_dev/src/dogfood.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{clippy_project_root, exit_if_err};
1+
use crate::utils::{clippy_project_root, exit_if_err};
22
use std::process::Command;
33

44
/// # Panics

clippy_dev/src/fmt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::clippy_project_root;
1+
use crate::utils::clippy_project_root;
22
use itertools::Itertools;
33
use rustc_lexer::{TokenKind, tokenize};
44
use shell_escape::escape;

clippy_dev/src/lib.rs

+3-59
Original file line numberDiff line numberDiff line change
@@ -14,69 +14,13 @@
1414
extern crate rustc_driver;
1515
extern crate rustc_lexer;
1616

17-
use std::io;
18-
use std::path::PathBuf;
19-
use std::process::{self, ExitStatus};
20-
2117
pub mod dogfood;
2218
pub mod fmt;
2319
pub mod lint;
2420
pub mod new_lint;
21+
pub mod release;
2522
pub mod serve;
2623
pub mod setup;
24+
pub mod sync;
2725
pub mod update_lints;
28-
29-
#[cfg(not(windows))]
30-
static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
31-
#[cfg(windows)]
32-
static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
33-
34-
/// Returns the path to the `cargo-clippy` binary
35-
///
36-
/// # Panics
37-
///
38-
/// Panics if the path of current executable could not be retrieved.
39-
#[must_use]
40-
pub fn cargo_clippy_path() -> PathBuf {
41-
let mut path = std::env::current_exe().expect("failed to get current executable name");
42-
path.set_file_name(CARGO_CLIPPY_EXE);
43-
path
44-
}
45-
46-
/// Returns the path to the Clippy project directory
47-
///
48-
/// # Panics
49-
///
50-
/// Panics if the current directory could not be retrieved, there was an error reading any of the
51-
/// Cargo.toml files or ancestor directory is the clippy root directory
52-
#[must_use]
53-
pub fn clippy_project_root() -> PathBuf {
54-
let current_dir = std::env::current_dir().unwrap();
55-
for path in current_dir.ancestors() {
56-
let result = std::fs::read_to_string(path.join("Cargo.toml"));
57-
if let Err(err) = &result {
58-
if err.kind() == io::ErrorKind::NotFound {
59-
continue;
60-
}
61-
}
62-
63-
let content = result.unwrap();
64-
if content.contains("[package]\nname = \"clippy\"") {
65-
return path.to_path_buf();
66-
}
67-
}
68-
panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
69-
}
70-
71-
/// # Panics
72-
/// Panics if given command result was failed.
73-
pub fn exit_if_err(status: io::Result<ExitStatus>) {
74-
match status.expect("failed to run command").code() {
75-
Some(0) => {},
76-
Some(n) => process::exit(n),
77-
None => {
78-
eprintln!("Killed by signal");
79-
process::exit(1);
80-
},
81-
}
82-
}
26+
pub mod utils;

clippy_dev/src/lint.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{cargo_clippy_path, exit_if_err};
1+
use crate::utils::{cargo_clippy_path, exit_if_err};
22
use std::process::{self, Command};
33
use std::{env, fs};
44

clippy_dev/src/main.rs

+40-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![warn(rust_2018_idioms, unused_lifetimes)]
44

55
use clap::{Args, Parser, Subcommand};
6-
use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints};
6+
use clippy_dev::{dogfood, fmt, lint, new_lint, release, serve, setup, sync, update_lints, utils};
77
use std::convert::Infallible;
88

99
fn main() {
@@ -23,9 +23,9 @@ fn main() {
2323
if print_only {
2424
update_lints::print_lints();
2525
} else if check {
26-
update_lints::update(update_lints::UpdateMode::Check);
26+
update_lints::update(utils::UpdateMode::Check);
2727
} else {
28-
update_lints::update(update_lints::UpdateMode::Change);
28+
update_lints::update(utils::UpdateMode::Change);
2929
}
3030
},
3131
DevCommand::NewLint {
@@ -35,7 +35,7 @@ fn main() {
3535
r#type,
3636
msrv,
3737
} => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) {
38-
Ok(()) => update_lints::update(update_lints::UpdateMode::Change),
38+
Ok(()) => update_lints::update(utils::UpdateMode::Change),
3939
Err(e) => eprintln!("Unable to create lint: {e}"),
4040
},
4141
DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
@@ -75,6 +75,12 @@ fn main() {
7575
uplift,
7676
} => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift),
7777
DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason),
78+
DevCommand::Sync(SyncCommand { subcommand }) => match subcommand {
79+
SyncSubcommand::UpdateNightly => sync::update_nightly(),
80+
},
81+
DevCommand::Release(ReleaseCommand { subcommand }) => match subcommand {
82+
ReleaseSubcommand::BumpVersion => release::bump_version(),
83+
},
7884
}
7985
}
8086

@@ -225,6 +231,10 @@ enum DevCommand {
225231
/// The reason for deprecation
226232
reason: String,
227233
},
234+
/// Sync between the rust repo and the Clippy repo
235+
Sync(SyncCommand),
236+
/// Manage Clippy releases
237+
Release(ReleaseCommand),
228238
}
229239

230240
#[derive(Args)]
@@ -291,3 +301,29 @@ enum RemoveSubcommand {
291301
/// Remove the tasks added with 'cargo dev setup vscode-tasks'
292302
VscodeTasks,
293303
}
304+
305+
#[derive(Args)]
306+
struct SyncCommand {
307+
#[command(subcommand)]
308+
subcommand: SyncSubcommand,
309+
}
310+
311+
#[derive(Subcommand)]
312+
enum SyncSubcommand {
313+
#[command(name = "update_nightly")]
314+
/// Update nightly version in rust-toolchain and `clippy_utils`
315+
UpdateNightly,
316+
}
317+
318+
#[derive(Args)]
319+
struct ReleaseCommand {
320+
#[command(subcommand)]
321+
subcommand: ReleaseSubcommand,
322+
}
323+
324+
#[derive(Subcommand)]
325+
enum ReleaseSubcommand {
326+
#[command(name = "bump_version")]
327+
/// Bump the version in the Cargo.toml files
328+
BumpVersion,
329+
}

clippy_dev/src/new_lint.rs

+3-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::clippy_project_root;
1+
use crate::utils::{clippy_project_root, clippy_version};
22
use indoc::{formatdoc, writedoc};
33
use std::fmt;
44
use std::fmt::Write as _;
@@ -186,23 +186,8 @@ fn to_camel_case(name: &str) -> String {
186186
}
187187

188188
pub(crate) fn get_stabilization_version() -> String {
189-
fn parse_manifest(contents: &str) -> Option<String> {
190-
let version = contents
191-
.lines()
192-
.filter_map(|l| l.split_once('='))
193-
.find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?;
194-
let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else {
195-
return None;
196-
};
197-
let (minor, patch) = version.split_once('.')?;
198-
Some(format!(
199-
"{}.{}.0",
200-
minor.parse::<u32>().ok()?,
201-
patch.parse::<u32>().ok()?
202-
))
203-
}
204-
let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`");
205-
parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
189+
let (minor, patch) = clippy_version();
190+
format!("{minor}.{patch}.0")
206191
}
207192

208193
fn get_test_file_contents(lint_name: &str, msrv: bool) -> String {

clippy_dev/src/release.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::fmt::Write;
2+
use std::path::Path;
3+
4+
use crate::utils::{UpdateMode, clippy_version, replace_region_in_file};
5+
6+
const CARGO_TOML_FILES: [&str; 4] = [
7+
"clippy_config/Cargo.toml",
8+
"clippy_lints/Cargo.toml",
9+
"clippy_utils/Cargo.toml",
10+
"Cargo.toml",
11+
];
12+
13+
pub fn bump_version() {
14+
let (minor, mut patch) = clippy_version();
15+
patch += 1;
16+
for file in &CARGO_TOML_FILES {
17+
replace_region_in_file(
18+
UpdateMode::Change,
19+
Path::new(file),
20+
"# begin autogenerated version\n",
21+
"# end autogenerated version",
22+
|res| {
23+
writeln!(res, "version = \"0.{minor}.{patch}\"").unwrap();
24+
},
25+
);
26+
}
27+
}

clippy_dev/src/sync.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use std::fmt::Write;
2+
use std::path::Path;
3+
4+
use chrono::offset::Utc;
5+
6+
use crate::utils::{UpdateMode, replace_region_in_file};
7+
8+
pub fn update_nightly() {
9+
// Update rust-toolchain nightly version
10+
let date = Utc::now().format("%Y-%m-%d").to_string();
11+
replace_region_in_file(
12+
UpdateMode::Change,
13+
Path::new("rust-toolchain"),
14+
"# begin autogenerated nightly\n",
15+
"# end autogenerated nightly",
16+
|res| {
17+
writeln!(res, "channel = \"nightly-{date}\"").unwrap();
18+
},
19+
);
20+
21+
// Update clippy_utils nightly version
22+
replace_region_in_file(
23+
UpdateMode::Change,
24+
Path::new("clippy_utils/README.md"),
25+
"<!-- begin autogenerated nightly -->\n",
26+
"<!-- end autogenerated nightly -->",
27+
|res| {
28+
writeln!(res, "```").unwrap();
29+
writeln!(res, "nightly-{date}").unwrap();
30+
writeln!(res, "```").unwrap();
31+
},
32+
);
33+
}

clippy_dev/src/update_lints.rs

+1-70
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::clippy_project_root;
1+
use crate::utils::{UpdateMode, clippy_project_root, exit_with_failure, replace_region_in_file};
22
use aho_corasick::AhoCorasickBuilder;
33
use itertools::Itertools;
44
use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape};
@@ -17,12 +17,6 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u
1717

1818
const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
1919

20-
#[derive(Clone, Copy, PartialEq, Eq)]
21-
pub enum UpdateMode {
22-
Check,
23-
Change,
24-
}
25-
2620
/// Runs the `update_lints` command.
2721
///
2822
/// This updates various generated values from the lint source code.
@@ -511,14 +505,6 @@ fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str)
511505
}
512506
}
513507

514-
fn exit_with_failure() {
515-
println!(
516-
"Not all lints defined properly. \
517-
Please run `cargo dev update_lints` to make sure all lints are defined properly."
518-
);
519-
std::process::exit(1);
520-
}
521-
522508
/// Lint data parsed from the Clippy source code.
523509
#[derive(Clone, PartialEq, Eq, Debug)]
524510
struct Lint {
@@ -851,61 +837,6 @@ fn remove_line_splices(s: &str) -> String {
851837
});
852838
res
853839
}
854-
855-
/// Replaces a region in a file delimited by two lines matching regexes.
856-
///
857-
/// `path` is the relative path to the file on which you want to perform the replacement.
858-
///
859-
/// See `replace_region_in_text` for documentation of the other options.
860-
///
861-
/// # Panics
862-
///
863-
/// Panics if the path could not read or then written
864-
fn replace_region_in_file(
865-
update_mode: UpdateMode,
866-
path: &Path,
867-
start: &str,
868-
end: &str,
869-
write_replacement: impl FnMut(&mut String),
870-
) {
871-
let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
872-
let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
873-
Ok(x) => x,
874-
Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()),
875-
};
876-
877-
match update_mode {
878-
UpdateMode::Check if contents != new_contents => exit_with_failure(),
879-
UpdateMode::Check => (),
880-
UpdateMode::Change => {
881-
if let Err(e) = fs::write(path, new_contents.as_bytes()) {
882-
panic!("Cannot write to `{}`: {e}", path.display());
883-
}
884-
},
885-
}
886-
}
887-
888-
/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
889-
/// were found, or the missing delimiter if not.
890-
fn replace_region_in_text<'a>(
891-
text: &str,
892-
start: &'a str,
893-
end: &'a str,
894-
mut write_replacement: impl FnMut(&mut String),
895-
) -> Result<String, &'a str> {
896-
let (text_start, rest) = text.split_once(start).ok_or(start)?;
897-
let (_, text_end) = rest.split_once(end).ok_or(end)?;
898-
899-
let mut res = String::with_capacity(text.len() + 4096);
900-
res.push_str(text_start);
901-
res.push_str(start);
902-
write_replacement(&mut res);
903-
res.push_str(end);
904-
res.push_str(text_end);
905-
906-
Ok(res)
907-
}
908-
909840
fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
910841
match OpenOptions::new().create_new(true).write(true).open(new_name) {
911842
Ok(file) => drop(file),

0 commit comments

Comments
 (0)