aboutsummaryrefslogtreecommitdiff
path: root/xtask
diff options
context:
space:
mode:
Diffstat (limited to 'xtask')
-rw-r--r--xtask/src/dist.rs35
-rw-r--r--xtask/src/flags.rs14
-rw-r--r--xtask/src/main.rs10
-rw-r--r--xtask/src/pre_cache.rs79
-rw-r--r--xtask/src/release.rs50
-rw-r--r--xtask/src/release/changelog.rs159
-rw-r--r--xtask/src/tidy.rs2
7 files changed, 198 insertions, 151 deletions
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index f2503f807..b6aa2f52a 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -7,26 +7,31 @@ use std::{
7 7
8use anyhow::Result; 8use anyhow::Result;
9use flate2::{write::GzEncoder, Compression}; 9use flate2::{write::GzEncoder, Compression};
10use xshell::{cmd, cp, mkdir_p, pushd, read_file, rm_rf, write_file}; 10use xshell::{cmd, cp, mkdir_p, pushd, pushenv, read_file, rm_rf, write_file};
11 11
12use crate::{date_iso, project_root}; 12use crate::{date_iso, flags, project_root};
13 13
14pub(crate) struct DistCmd { 14impl flags::Dist {
15 pub(crate) nightly: bool,
16 pub(crate) client_version: Option<String>,
17}
18
19impl DistCmd {
20 pub(crate) fn run(self) -> Result<()> { 15 pub(crate) fn run(self) -> Result<()> {
16 let stable =
17 std::env::var("GITHUB_REF").unwrap_or_default().as_str() == "refs/heads/release";
18
21 let dist = project_root().join("dist"); 19 let dist = project_root().join("dist");
22 rm_rf(&dist)?; 20 rm_rf(&dist)?;
23 mkdir_p(&dist)?; 21 mkdir_p(&dist)?;
24 22
25 if let Some(version) = self.client_version { 23 if let Some(patch_version) = self.client_patch_version {
26 let release_tag = if self.nightly { "nightly".to_string() } else { date_iso()? }; 24 let version = if stable {
25 format!("0.2.{}", patch_version)
26 } else {
27 // A hack to make VS Code prefer nightly over stable.
28 format!("0.3.{}", patch_version)
29 };
30 let release_tag = if stable { date_iso()? } else { "nightly".to_string() };
27 dist_client(&version, &release_tag)?; 31 dist_client(&version, &release_tag)?;
28 } 32 }
29 dist_server()?; 33 let release_channel = if stable { "stable" } else { "nightly" };
34 dist_server(release_channel)?;
30 Ok(()) 35 Ok(())
31 } 36 }
32} 37}
@@ -39,7 +44,9 @@ fn dist_client(version: &str, release_tag: &str) -> Result<()> {
39 44
40 patch 45 patch
41 .replace(r#""version": "0.4.0-dev""#, &format!(r#""version": "{}""#, version)) 46 .replace(r#""version": "0.4.0-dev""#, &format!(r#""version": "{}""#, version))
42 .replace(r#""releaseTag": null"#, &format!(r#""releaseTag": "{}""#, release_tag)); 47 .replace(r#""releaseTag": null"#, &format!(r#""releaseTag": "{}""#, release_tag))
48 .replace(r#""$generated-start": false,"#, "")
49 .replace(",\n \"$generated-end\": false", "");
43 50
44 if nightly { 51 if nightly {
45 patch.replace( 52 patch.replace(
@@ -57,7 +64,9 @@ fn dist_client(version: &str, release_tag: &str) -> Result<()> {
57 Ok(()) 64 Ok(())
58} 65}
59 66
60fn dist_server() -> Result<()> { 67fn dist_server(release_channel: &str) -> Result<()> {
68 let _e = pushenv("RUST_ANALYZER_CHANNEL", release_channel);
69 let _e = pushenv("CARGO_PROFILE_RELEASE_LTO", "true");
61 let target = get_target(); 70 let target = get_target();
62 if target.contains("-linux-gnu") || target.contains("-linux-musl") { 71 if target.contains("-linux-gnu") || target.contains("-linux-musl") {
63 env::set_var("CC", "clang"); 72 env::set_var("CC", "clang");
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs
index 4cd2b1ddb..69b3cb9c1 100644
--- a/xtask/src/flags.rs
+++ b/xtask/src/flags.rs
@@ -28,7 +28,6 @@ xflags::xflags! {
28 } 28 }
29 29
30 cmd fuzz-tests {} 30 cmd fuzz-tests {}
31 cmd pre-cache {}
32 31
33 cmd release { 32 cmd release {
34 optional --dry-run 33 optional --dry-run
@@ -37,8 +36,7 @@ xflags::xflags! {
37 optional --dry-run 36 optional --dry-run
38 } 37 }
39 cmd dist { 38 cmd dist {
40 optional --nightly 39 optional --client-patch-version version: String
41 optional --client version: String
42 } 40 }
43 cmd metrics { 41 cmd metrics {
44 optional --dry-run 42 optional --dry-run
@@ -63,7 +61,6 @@ pub enum XtaskCmd {
63 Help(Help), 61 Help(Help),
64 Install(Install), 62 Install(Install),
65 FuzzTests(FuzzTests), 63 FuzzTests(FuzzTests),
66 PreCache(PreCache),
67 Release(Release), 64 Release(Release),
68 Promote(Promote), 65 Promote(Promote),
69 Dist(Dist), 66 Dist(Dist),
@@ -86,15 +83,9 @@ pub struct Install {
86} 83}
87 84
88#[derive(Debug)] 85#[derive(Debug)]
89pub struct Lint;
90
91#[derive(Debug)]
92pub struct FuzzTests; 86pub struct FuzzTests;
93 87
94#[derive(Debug)] 88#[derive(Debug)]
95pub struct PreCache;
96
97#[derive(Debug)]
98pub struct Release { 89pub struct Release {
99 pub dry_run: bool, 90 pub dry_run: bool,
100} 91}
@@ -106,8 +97,7 @@ pub struct Promote {
106 97
107#[derive(Debug)] 98#[derive(Debug)]
108pub struct Dist { 99pub struct Dist {
109 pub nightly: bool, 100 pub client_patch_version: Option<String>,
110 pub client: Option<String>,
111} 101}
112 102
113#[derive(Debug)] 103#[derive(Debug)]
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 960927fc0..d0bef7b7a 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -18,7 +18,6 @@ mod install;
18mod release; 18mod release;
19mod dist; 19mod dist;
20mod metrics; 20mod metrics;
21mod pre_cache;
22 21
23use anyhow::{bail, Result}; 22use anyhow::{bail, Result};
24use std::{ 23use std::{
@@ -28,8 +27,6 @@ use std::{
28use walkdir::{DirEntry, WalkDir}; 27use walkdir::{DirEntry, WalkDir};
29use xshell::{cmd, cp, pushd, pushenv}; 28use xshell::{cmd, cp, pushd, pushenv};
30 29
31use crate::dist::DistCmd;
32
33fn main() -> Result<()> { 30fn main() -> Result<()> {
34 let _d = pushd(project_root())?; 31 let _d = pushd(project_root())?;
35 32
@@ -41,12 +38,9 @@ fn main() -> Result<()> {
41 } 38 }
42 flags::XtaskCmd::Install(cmd) => cmd.run(), 39 flags::XtaskCmd::Install(cmd) => cmd.run(),
43 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), 40 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(),
44 flags::XtaskCmd::PreCache(cmd) => cmd.run(),
45 flags::XtaskCmd::Release(cmd) => cmd.run(), 41 flags::XtaskCmd::Release(cmd) => cmd.run(),
46 flags::XtaskCmd::Promote(cmd) => cmd.run(), 42 flags::XtaskCmd::Promote(cmd) => cmd.run(),
47 flags::XtaskCmd::Dist(flags) => { 43 flags::XtaskCmd::Dist(cmd) => cmd.run(),
48 DistCmd { nightly: flags.nightly, client_version: flags.client }.run()
49 }
50 flags::XtaskCmd::Metrics(cmd) => cmd.run(), 44 flags::XtaskCmd::Metrics(cmd) => cmd.run(),
51 flags::XtaskCmd::Bb(cmd) => { 45 flags::XtaskCmd::Bb(cmd) => {
52 { 46 {
@@ -112,7 +106,7 @@ fn run_fuzzer() -> Result<()> {
112} 106}
113 107
114fn date_iso() -> Result<String> { 108fn date_iso() -> Result<String> {
115 let res = cmd!("date --iso --utc").read()?; 109 let res = cmd!("date -u +%Y-%m-%d").read()?;
116 Ok(res) 110 Ok(res)
117} 111}
118 112
diff --git a/xtask/src/pre_cache.rs b/xtask/src/pre_cache.rs
deleted file mode 100644
index b456224fd..000000000
--- a/xtask/src/pre_cache.rs
+++ /dev/null
@@ -1,79 +0,0 @@
1use std::{
2 fs::FileType,
3 path::{Path, PathBuf},
4};
5
6use anyhow::Result;
7use xshell::rm_rf;
8
9use crate::flags;
10
11impl flags::PreCache {
12 /// Cleans the `./target` dir after the build such that only
13 /// dependencies are cached on CI.
14 pub(crate) fn run(self) -> Result<()> {
15 let slow_tests_cookie = Path::new("./target/.slow_tests_cookie");
16 if !slow_tests_cookie.exists() {
17 panic!("slow tests were skipped on CI!")
18 }
19 rm_rf(slow_tests_cookie)?;
20
21 for path in read_dir("./target/debug", FileType::is_file)? {
22 // Can't delete yourself on windows :-(
23 if !path.ends_with("xtask.exe") {
24 rm_rf(&path)?
25 }
26 }
27
28 rm_rf("./target/.rustc_info.json")?;
29
30 let to_delete = read_dir("./crates", FileType::is_dir)?
31 .into_iter()
32 .map(|path| path.file_name().unwrap().to_string_lossy().replace('-', "_"))
33 .collect::<Vec<_>>();
34
35 for &dir in ["./target/debug/deps", "target/debug/.fingerprint"].iter() {
36 for path in read_dir(dir, |_file_type| true)? {
37 if path.ends_with("xtask.exe") {
38 continue;
39 }
40 let file_name = path.file_name().unwrap().to_string_lossy();
41 let (stem, _) = match rsplit_once(&file_name, '-') {
42 Some(it) => it,
43 None => {
44 rm_rf(path)?;
45 continue;
46 }
47 };
48 let stem = stem.replace('-', "_");
49 if to_delete.contains(&stem) {
50 rm_rf(path)?;
51 }
52 }
53 }
54
55 Ok(())
56 }
57}
58fn read_dir(path: impl AsRef<Path>, cond: impl Fn(&FileType) -> bool) -> Result<Vec<PathBuf>> {
59 read_dir_impl(path.as_ref(), &cond)
60}
61
62fn read_dir_impl(path: &Path, cond: &dyn Fn(&FileType) -> bool) -> Result<Vec<PathBuf>> {
63 let mut res = Vec::new();
64 for entry in path.read_dir()? {
65 let entry = entry?;
66 let file_type = entry.file_type()?;
67 if cond(&file_type) {
68 res.push(entry.path())
69 }
70 }
71 Ok(res)
72}
73
74fn rsplit_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
75 let mut split = haystack.rsplitn(2, delim);
76 let suffix = split.next()?;
77 let prefix = split.next()?;
78 Some((prefix, suffix))
79}
diff --git a/xtask/src/release.rs b/xtask/src/release.rs
index dde5d14ee..452f351d0 100644
--- a/xtask/src/release.rs
+++ b/xtask/src/release.rs
@@ -1,4 +1,4 @@
1use std::fmt::Write; 1mod changelog;
2 2
3use xshell::{cmd, cp, pushd, read_dir, write_file}; 3use xshell::{cmd, cp, pushd, read_dir, write_file};
4 4
@@ -10,7 +10,16 @@ impl flags::Release {
10 cmd!("git switch release").run()?; 10 cmd!("git switch release").run()?;
11 cmd!("git fetch upstream --tags --force").run()?; 11 cmd!("git fetch upstream --tags --force").run()?;
12 cmd!("git reset --hard tags/nightly").run()?; 12 cmd!("git reset --hard tags/nightly").run()?;
13 cmd!("git push").run()?; 13 // The `release` branch sometimes has a couple of cherry-picked
14 // commits for patch releases. If that's the case, just overwrite
15 // it. As we are setting `release` branch to an up-to-date `nightly`
16 // tag, this shouldn't be problematic in general.
17 //
18 // Note that, as we tag releases, we don't worry about "losing"
19 // commits -- they'll be kept alive by the tag. More generally, we
20 // don't care about historic releases all that much, it's fine even
21 // to delete old tags.
22 cmd!("git push --force").run()?;
14 } 23 }
15 codegen::docs()?; 24 codegen::docs()?;
16 25
@@ -38,42 +47,7 @@ impl flags::Release {
38 let tags = cmd!("git tag --list").read()?; 47 let tags = cmd!("git tag --list").read()?;
39 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); 48 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();
40 49
41 let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?; 50 let contents = changelog::get_changelog(changelog_n, &commit, prev_tag, &today)?;
42 let mut git_log_summary = String::new();
43 for line in git_log.lines() {
44 let line = line.trim_start();
45 if let Some(p) = line.find(':') {
46 if let Ok(pr) = line[..p].parse::<u32>() {
47 writeln!(git_log_summary, "* pr:{}[]{}", pr, &line[p + 1..]).unwrap();
48 }
49 }
50 }
51
52 let contents = format!(
53 "\
54= Changelog #{}
55:sectanchors:
56:page-layout: post
57
58Commit: commit:{}[] +
59Release: release:{}[]
60
61== Sponsors
62
63**Become a sponsor:** On https://opencollective.com/rust-analyzer/[OpenCollective] or
64https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
65
66== New Features
67
68{}
69
70== Fixes
71
72== Internal Improvements
73",
74 changelog_n, commit, today, git_log_summary
75 );
76
77 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); 51 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
78 write_file(&path, &contents)?; 52 write_file(&path, &contents)?;
79 53
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs
new file mode 100644
index 000000000..ffcae2cf7
--- /dev/null
+++ b/xtask/src/release/changelog.rs
@@ -0,0 +1,159 @@
1use std::fmt::Write;
2use std::{env, iter};
3
4use anyhow::{bail, Result};
5use xshell::cmd;
6
7pub(crate) fn get_changelog(
8 changelog_n: usize,
9 commit: &str,
10 prev_tag: &str,
11 today: &str,
12) -> Result<String> {
13 let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?;
14 let mut features = String::new();
15 let mut fixes = String::new();
16 let mut internal = String::new();
17 let mut others = String::new();
18 for line in git_log.lines() {
19 let line = line.trim_start();
20 if let Some(p) = line.find(':') {
21 let pr = &line[..p];
22 if let Ok(pr_num) = pr.parse::<u32>() {
23 let accept = "Accept: application/vnd.github.v3+json";
24 let token = match env::var("GITHUB_TOKEN") {
25 Ok(token) => token,
26 Err(_) => bail!("Please obtain a personal access token from https://github.com/settings/tokens and set the `GITHUB_TOKEN` environment variable."),
27 };
28 let authorization = format!("Authorization: token {}", token);
29 let pr_url = "https://api.github.com/repos/rust-analyzer/rust-analyzer/issues";
30
31 // we don't use an HTTPS client or JSON parser to keep the build times low
32 let pr_json =
33 cmd!("curl -s -H {accept} -H {authorization} {pr_url}/{pr}").read()?;
34 let pr_title = cmd!("jq .title").stdin(&pr_json).read()?;
35 let pr_title = unescape(&pr_title[1..pr_title.len() - 1]);
36 let pr_comment = cmd!("jq .body").stdin(pr_json).read()?;
37
38 let comments_json =
39 cmd!("curl -s -H {accept} -H {authorization} {pr_url}/{pr}/comments").read()?;
40 let pr_comments = cmd!("jq .[].body").stdin(comments_json).read()?;
41
42 let l = iter::once(pr_comment.as_str())
43 .chain(pr_comments.lines())
44 .rev()
45 .find_map(|it| {
46 let it = unescape(&it[1..it.len() - 1]);
47 it.lines().find_map(parse_changelog_line)
48 })
49 .into_iter()
50 .next()
51 .unwrap_or_else(|| parse_title_line(&pr_title));
52 let s = match l.kind {
53 PrKind::Feature => &mut features,
54 PrKind::Fix => &mut fixes,
55 PrKind::Internal => &mut internal,
56 PrKind::Other => &mut others,
57 PrKind::Skip => continue,
58 };
59 writeln!(s, "* pr:{}[] {}", pr_num, l.message.as_deref().unwrap_or(&pr_title))
60 .unwrap();
61 }
62 }
63 }
64
65 let contents = format!(
66 "\
67= Changelog #{}
68:sectanchors:
69:page-layout: post
70
71Commit: commit:{}[] +
72Release: release:{}[]
73
74== Sponsors
75
76**Become a sponsor:** On https://opencollective.com/rust-analyzer/[OpenCollective] or
77https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
78
79== New Features
80
81{}
82
83== Fixes
84
85{}
86
87== Internal Improvements
88
89{}
90
91== Others
92
93{}
94",
95 changelog_n, commit, today, features, fixes, internal, others
96 );
97 Ok(contents)
98}
99
100#[derive(Clone, Copy)]
101enum PrKind {
102 Feature,
103 Fix,
104 Internal,
105 Other,
106 Skip,
107}
108
109struct PrInfo {
110 message: Option<String>,
111 kind: PrKind,
112}
113
114fn unescape(s: &str) -> String {
115 s.replace(r#"\""#, "").replace(r#"\n"#, "\n").replace(r#"\r"#, "")
116}
117
118fn parse_changelog_line(s: &str) -> Option<PrInfo> {
119 let parts = s.splitn(3, ' ').collect::<Vec<_>>();
120 if parts.len() < 2 || parts[0] != "changelog" {
121 return None;
122 }
123 let message = parts.get(2).map(|it| it.to_string());
124 let kind = match parts[1].trim_end_matches(':') {
125 "feature" => PrKind::Feature,
126 "fix" => PrKind::Fix,
127 "internal" => PrKind::Internal,
128 "skip" => PrKind::Skip,
129 _ => {
130 let kind = PrKind::Other;
131 let message = format!("{} {}", parts[1], message.unwrap_or_default());
132 return Some(PrInfo { kind, message: Some(message) });
133 }
134 };
135 let res = PrInfo { kind, message };
136 Some(res)
137}
138
139fn parse_title_line(s: &str) -> PrInfo {
140 let lower = s.to_ascii_lowercase();
141 const PREFIXES: [(&str, PrKind); 5] = [
142 ("feat: ", PrKind::Feature),
143 ("feature: ", PrKind::Feature),
144 ("fix: ", PrKind::Fix),
145 ("internal: ", PrKind::Internal),
146 ("minor: ", PrKind::Skip),
147 ];
148
149 for &(prefix, kind) in &PREFIXES {
150 if lower.starts_with(prefix) {
151 let message = match &kind {
152 PrKind::Skip => None,
153 _ => Some(s[prefix.len()..].to_string()),
154 };
155 return PrInfo { kind, message };
156 }
157 }
158 PrInfo { kind: PrKind::Other, message: Some(s.to_string()) }
159}
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 50d9efccd..c3c785eff 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -224,7 +224,7 @@ Apache-2.0 OR BSL-1.0
224Apache-2.0 OR MIT 224Apache-2.0 OR MIT
225Apache-2.0/MIT 225Apache-2.0/MIT
226BSD-3-Clause 226BSD-3-Clause
227CC0-1.0 227CC0-1.0 OR Artistic-2.0
228ISC 228ISC
229MIT 229MIT
230MIT / Apache-2.0 230MIT / Apache-2.0