aboutsummaryrefslogtreecommitdiffstats
path: root/xtask/src/release.rs
diff options
context:
space:
mode:
Diffstat (limited to 'xtask/src/release.rs')
-rw-r--r--xtask/src/release.rs174
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)
+ }
+}