aboutsummaryrefslogtreecommitdiff
path: root/xtask
diff options
context:
space:
mode:
Diffstat (limited to 'xtask')
-rw-r--r--xtask/src/dist.rs34
-rw-r--r--xtask/src/flags.rs9
-rw-r--r--xtask/src/main.rs8
-rw-r--r--xtask/src/release.rs50
-rw-r--r--xtask/src/release/changelog.rs159
-rw-r--r--xtask/src/tidy.rs2
6 files changed, 197 insertions, 65 deletions
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index f2503f807..12a7fea1e 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,8 @@ 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);
61 let target = get_target(); 69 let target = get_target();
62 if target.contains("-linux-gnu") || target.contains("-linux-musl") { 70 if target.contains("-linux-gnu") || target.contains("-linux-musl") {
63 env::set_var("CC", "clang"); 71 env::set_var("CC", "clang");
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs
index 34e447c2f..69b3cb9c1 100644
--- a/xtask/src/flags.rs
+++ b/xtask/src/flags.rs
@@ -36,8 +36,7 @@ xflags::xflags! {
36 optional --dry-run 36 optional --dry-run
37 } 37 }
38 cmd dist { 38 cmd dist {
39 optional --nightly 39 optional --client-patch-version version: String
40 optional --client version: String
41 } 40 }
42 cmd metrics { 41 cmd metrics {
43 optional --dry-run 42 optional --dry-run
@@ -84,9 +83,6 @@ pub struct Install {
84} 83}
85 84
86#[derive(Debug)] 85#[derive(Debug)]
87pub struct Lint;
88
89#[derive(Debug)]
90pub struct FuzzTests; 86pub struct FuzzTests;
91 87
92#[derive(Debug)] 88#[derive(Debug)]
@@ -101,8 +97,7 @@ pub struct Promote {
101 97
102#[derive(Debug)] 98#[derive(Debug)]
103pub struct Dist { 99pub struct Dist {
104 pub nightly: bool, 100 pub client_patch_version: Option<String>,
105 pub client: Option<String>,
106} 101}
107 102
108#[derive(Debug)] 103#[derive(Debug)]
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 0dbbde275..d0bef7b7a 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -27,8 +27,6 @@ use std::{
27use walkdir::{DirEntry, WalkDir}; 27use walkdir::{DirEntry, WalkDir};
28use xshell::{cmd, cp, pushd, pushenv}; 28use xshell::{cmd, cp, pushd, pushenv};
29 29
30use crate::dist::DistCmd;
31
32fn main() -> Result<()> { 30fn main() -> Result<()> {
33 let _d = pushd(project_root())?; 31 let _d = pushd(project_root())?;
34 32
@@ -42,9 +40,7 @@ fn main() -> Result<()> {
42 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), 40 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(),
43 flags::XtaskCmd::Release(cmd) => cmd.run(), 41 flags::XtaskCmd::Release(cmd) => cmd.run(),
44 flags::XtaskCmd::Promote(cmd) => cmd.run(), 42 flags::XtaskCmd::Promote(cmd) => cmd.run(),
45 flags::XtaskCmd::Dist(flags) => { 43 flags::XtaskCmd::Dist(cmd) => cmd.run(),
46 DistCmd { nightly: flags.nightly, client_version: flags.client }.run()
47 }
48 flags::XtaskCmd::Metrics(cmd) => cmd.run(), 44 flags::XtaskCmd::Metrics(cmd) => cmd.run(),
49 flags::XtaskCmd::Bb(cmd) => { 45 flags::XtaskCmd::Bb(cmd) => {
50 { 46 {
@@ -110,7 +106,7 @@ fn run_fuzzer() -> Result<()> {
110} 106}
111 107
112fn date_iso() -> Result<String> { 108fn date_iso() -> Result<String> {
113 let res = cmd!("date --iso --utc").read()?; 109 let res = cmd!("date -u +%Y-%m-%d").read()?;
114 Ok(res) 110 Ok(res)
115} 111}
116 112
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