atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git
xtask/src/release.rsRust175 lines5.1 KB
1
use clap::{Args, Subcommand, ValueEnum};
2
use xshell::{Cmd, cmd};
3
4
use crate::{Result, sh};
5
6
#[derive(Args)]
7
pub struct ReleaseArgs {
8
    #[clap(subcommand)]
9
    cmd: ReleaseCommand,
10
}
11
12
#[derive(PartialEq, Subcommand)]
13
enum ReleaseCommand {
14
    /// Prepare the release of the matrix-sdk workspace.
15
    ///
16
    /// This command will update the `README.md`, update the `CHANGELOG.md` file
17
    /// using, and bump the versions in the `Cargo.toml` files.
18
    Prepare {
19
        /// What type of version bump we should perform.
20
        version: ReleaseVersion,
21
        /// Actually prepare a release. Dry-run mode is the default.
22
        #[clap(long)]
23
        execute: bool,
24
        /// The crate or package that should be released. Use this if you'd like
25
        /// to release only one specific crate. The default is to
26
        /// release all crates.
27
        #[clap(long)]
28
        package: Option<String>,
29
    },
30
    /// Publish the release.
31
    ///
32
    /// This command will create signed tags, publish the release on crates.io,
33
    /// and finally push the tags to the repo.
34
    Publish {
35
        /// Actually publish a release. Dry-run mode is the default
36
        #[clap(long)]
37
        execute: bool,
38
        /// The crate or package that should be released. Use this if you'd like
39
        /// to release only one specific crate. The default is to
40
        /// release all crates.
41
        #[clap(long)]
42
        package: Option<String>,
43
    },
44
    /// Get a list of interesting changes that happened in the last week.
45
    WeeklyReport,
46
}
47
48
#[derive(Clone, Copy, Debug, Default, PartialEq, ValueEnum)]
49
enum ReleaseVersion {
50
    // TODO: Add a Major variant here once we're stable.
51
    /// Create a new minor release.
52
    #[default]
53
    Minor,
54
    /// Create a new patch release.
55
    Patch,
56
    /// Create a new release candidate.
57
    Rc,
58
}
59
60
impl ReleaseVersion {
61
    fn as_str(&self) -> &str {
62
        match self {
63
            ReleaseVersion::Minor => "minor",
64
            ReleaseVersion::Patch => "patch",
65
            ReleaseVersion::Rc => "rc",
66
        }
67
    }
68
}
69
70
impl ReleaseArgs {
71
    pub fn run(self) -> Result<()> {
72
        check_prerequisites();
73
74
        match self.cmd {
75
            ReleaseCommand::Prepare { version, execute, package } => {
76
                prepare(version, execute, package)
77
            }
78
            ReleaseCommand::Publish { execute, package } => publish(execute, package),
79
            ReleaseCommand::WeeklyReport => weekly_report(),
80
        }
81
    }
82
}
83
84
fn check_prerequisites() {
85
    let sh = sh();
86
87
    if cmd!(sh, "cargo release --version").quiet().ignore_stdout().run().is_err() {
88
        eprintln!("This command requires cargo-release, please install it.");
89
        eprintln!(
90
            "More info can be found at: \
91
             https://github.com/crate-ci/cargo-release?tab=readme-ov-file#install"
92
        );
93
94
        std::process::exit(1);
95
    }
96
97
    if cmd!(sh, "gh version").quiet().ignore_stdout().run().is_err() {
98
        eprintln!("This command requires GitHub CLI, please install it.");
99
        eprintln!("More info can be found at: https://cli.github.com/");
100
101
        std::process::exit(1);
102
    }
103
}
104
105
fn append_options<'a>(command: Cmd<'a>, execute: &bool, package: &Option<String>) -> Cmd<'a> {
106
    let command = if *execute { command.arg("--execute") } else { command };
107
    if let Some(package) = package.as_deref() {
108
        command.args(["--package", package])
109
    } else {
110
        command.arg("--workspace")
111
    }
112
}
113
114
fn prepare(version: ReleaseVersion, execute: bool, package: Option<String>) -> Result<()> {
115
    let sh = sh();
116
117
    let cmd = cmd!(sh, "cargo release --no-publish --no-tag --no-push");
118
    let cmd = append_options(cmd, &execute, &package);
119
120
    let cmd = cmd.arg(version.as_str());
121
122
    cmd.run()?;
123
124
    if execute {
125
        eprintln!(
126
            "Please double check the changelogs and edit them if necessary, \
127
             publish the PR, and once it's merged, switch to `main` and pull the PR \
128
             and run `cargo xtask release publish`"
129
        );
130
    }
131
132
    Ok(())
133
}
134
135
fn publish(execute: bool, package: Option<String>) -> Result<()> {
136
    let sh = sh();
137
138
    let cmd = cmd!(sh, "cargo release tag");
139
    let cmd = append_options(cmd, &execute, &package);
140
    cmd.run()?;
141
142
    let cmd = cmd!(sh, "cargo release publish");
143
    let cmd = append_options(cmd, &execute, &package);
144
    cmd.run()?;
145
146
    let cmd = cmd!(sh, "cargo release push");
147
    let cmd = append_options(cmd, &execute, &package);
148
    cmd.run()?;
149
150
    Ok(())
151
}
152
153
fn weekly_report() -> Result<()> {
154
    const JSON_FIELDS: &str = "title,number,url,author";
155
156
    let sh = sh();
157
158
    let one_week_ago = cmd!(sh, "date -d '1 week ago' +%Y-%m-%d").read()?;
159
    let today = cmd!(sh, "date +%Y-%m-%d").read()?;
160
161
    let _env_pager = sh.push_env("GH_PAGER", "");
162
163
    let header = format!("# This Week in the Matrix Rust SDK ({today})\n\n");
164
    let template = "{{range .}}- {{.title}} ([#{{.number}}](https://github.com/matrix-org/matrix-rust-sdk/pull/{{.number}})){{\"\\n\\n\"}}{{end}}";
165
    let template = format!("{header}{template}");
166
167
    cmd!(
168
        sh,
169
        "gh pr list --search merged:>{one_week_ago} --limit 100 --json {JSON_FIELDS} --template {template}"
170
    )
171
    .quiet()
172
    .run()?;
173
174
    Ok(())
175
}