diff options
author | Toby Vincent <tobyv13@gmail.com> | 2023-06-03 21:34:21 -0500 |
---|---|---|
committer | Toby Vincent <tobyv13@gmail.com> | 2023-06-03 21:34:21 -0500 |
commit | c6e8c15b39781ce48cd3f0d829ab5c6574ccd01b (patch) | |
tree | fa8966f5d2058a349bd0b9b5dd5b0cf9854bbb39 /xtask/src/release.rs | |
parent | e5dd5a260c48ee7f2d22e75433e463eb092ec698 (diff) |
build: add xtask release command
Diffstat (limited to 'xtask/src/release.rs')
-rw-r--r-- | xtask/src/release.rs | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/xtask/src/release.rs b/xtask/src/release.rs new file mode 100644 index 0000000..8bf4444 --- /dev/null +++ b/xtask/src/release.rs @@ -0,0 +1,174 @@ +use std::process::{Command, Stdio}; + +use anyhow::Result; +use clap::{Args, Subcommand}; +use semver::Version; + +use self::version::{Bump, Level, Replacement}; + +mod version; + +#[derive(Debug, Clone, Args)] +pub struct Release { + #[command(subcommand)] + step: Option<Step>, + + /// Level of version bump version. + #[arg(global = true, required = false)] + level: version::Level, + + /// Options passed to git commit. + #[arg(global = true, last = true)] + git_commit_args: Vec<String>, +} + +impl Release { + pub fn run(self) -> Result<()> { + match self.step { + Some(step) => step.run(), + None => { + let bump = Step::bump(self.level)?; + + println!("Bumped version: {bump}"); + + Ok(()) + } + } + } +} + +#[derive(Debug, Clone, Subcommand)] +pub enum Step { + /// Bump version in package files and commit changes. + Bump { + #[arg(from_global)] + level: version::Level, + }, + + /// Make a release commit. + Commit { + #[arg(from_global)] + git_commit_args: Vec<String>, + }, + + /// Create git tag for release. + Tag { + #[arg(from_global)] + level: version::Level, + }, +} + +impl Step { + pub fn run(self) -> Result<()> { + match self { + Step::Bump { level } => { + let bump = Self::bump(level)?; + println!("Bumped version: {bump}"); + } + Step::Commit { git_commit_args } => Self::commit(git_commit_args)?, + Step::Tag { level } => { + let stdout = Command::new("git") + .arg("describe") + .arg("--abbrev=0") + .output()? + .stdout; + + let prev = std::str::from_utf8(&stdout)?.parse()?; + let next = level.bump(&prev); + Self::tag(prev, next)?; + } + }; + + Ok(()) + } + + pub fn bump(level: Level) -> Result<Bump> { + let mut bump = Bump::from(level); + + bump.bump_file("README.md", &[Replacement::Version])?; + bump.bump_file("pkg/archlinux/projectr/PKGBUILD", &[Replacement::Version])?; + bump.bump_file( + "pkg/archlinux/projectr-bin/PKGBUILD", + &[Replacement::Version], + )?; + + let stdout = std::process::Command::new("git") + .arg("describe") + .arg("--long") + .arg("--abbrev=7") + .output()? + .stdout; + let pkgver = std::str::from_utf8(&stdout)? + .trim() + .trim_start_matches('v') + .replacen("-g", ".g", 1) + .replacen('-', "-r", 1) + .replace('-', "."); + + bump.bump_file( + "pkg/archlinux/projectr-git/PKGBUILD", + &[Replacement::FindLine( + |l| l.starts_with("pkgver="), + format!("pkgver={pkgver}"), + )], + )?; + + bump.bump_file( + "CHANGELOG.md", + &[ + Replacement::Append( + "## [Unreleased]".to_string(), + format!( + "\n## [{}] - {}", + bump.next, + chrono::Utc::now().format("%Y-%m-%d") + ), + ), + Replacement::Append( + "[Unreleased]: https://git.sr.ht/~tobyvin/projectr/log/HEAD".to_string(), + format!( + "[{0}]: https://git.sr.ht/~tobyvin/projectr/log/v{0}", + bump.next + ), + ), + ], + )?; + + Ok(bump) + } + + pub fn commit(git_commit_args: Vec<String>) -> Result<()> { + let git_commit = Command::new("git") + .arg("commit") + .args(git_commit_args) + .status()?; + + anyhow::ensure!(git_commit.success(), "Failed to commit changes"); + + Ok(()) + } + + pub fn tag(from: Version, to: Version) -> Result<String> { + let tag_name = format!("v{}", to); + + let shortlog_child = Command::new("git") + .arg("shortlog") + .arg(format!("v{}..HEAD", from)) + .arg("--abbrev=7") + .stdout(Stdio::piped()) + .spawn()?; + + let git_commit = Command::new("git") + .arg("tag") + .arg("-s") + .arg(&tag_name) + .arg("--file") + .arg("-") + .stdin(Stdio::from(shortlog_child.stdout.unwrap())) // Pipe through. + .status()?; + + anyhow::ensure!(git_commit.success(), "Failed to commit changes"); + + Ok(tag_name) + } +} |