Skip to content

Commit

Permalink
feat(cli/bump): description flag
Browse files Browse the repository at this point in the history
This allows for optionally specifying a description that can be included
in changelogs.
  • Loading branch information
justinrubek committed May 15, 2024
1 parent ab09a65 commit f53b1e7
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 5 deletions.
56 changes: 52 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use bomper::{
use console::{style, Style};
use gix::refs::transaction::PreviousValue;
use similar::{ChangeTag, TextDiff};
use std::{fmt, io::Write, path::PathBuf};
use std::{fmt, io::Write, path::PathBuf, process::Command};

pub struct App {
pub args: BaseArgs,
Expand All @@ -36,7 +36,19 @@ impl App {

let increment = opts.options.determine_increment(&commits, &tag.version)?;
let new_version = increment_version(tag.version.clone(), increment);
let changelog_entry = generate_changelog_entry(&commits, &new_version.to_string());
let version_description = if opts.description {
match prompt_for_description()? {
Some(description) => Some(description),
None => {
println!("Aborting bump due to empty description");
return Ok(());
}
}
} else {
None
};
let changelog_entry =
generate_changelog_entry(&commits, &new_version.to_string(), version_description);

let replacement = VersionReplacement {
old_version: tag.version.to_string(),
Expand Down Expand Up @@ -75,13 +87,14 @@ impl App {
.ok_or_else(|| Error::VersionNotFound(version.clone()))?;
let commits =
get_commits_between_tags(&repo, &version_range[1], &version_range[0])?;
let changelog_entry = generate_changelog_entry(&commits, &version.to_string());
let changelog_entry =
generate_changelog_entry(&commits, &version.to_string(), None);
println!("{}", changelog_entry);
}
None => {
let tag = get_latest_tag(&repo)?;
let commits = get_commits_since_tag(&repo, &tag)?;
let changelog_entry = generate_changelog_entry(&commits, "unreleased");
let changelog_entry = generate_changelog_entry(&commits, "unreleased", None);
let path = std::path::PathBuf::from("CHANGELOG.md");
if opts.no_decorations {
match opts.only_current_version {
Expand Down Expand Up @@ -325,3 +338,38 @@ fn print_diff(original: String, new: String, context: String) {
}
}
}

const DESCRIPTION_HELP: &[u8] = br#"
# Please enter a description for this version change"
# All lines starting with '#' will be ignored
# The contents of this file will be inserted into the changelog markdown"#;

fn prompt_for_description() -> Result<Option<String>> {
let mut file = tempfile::NamedTempFile::new_in(".")?;
file.write_all(DESCRIPTION_HELP)?;

let editor = std::env::var("EDITOR").map_err(|_| Error::EditorNotSet)?;
Command::new(editor)
.arg(file.path())
.status()
.expect("failed to edit changelog description file");

let description = std::fs::read_to_string(file.path())?;
let description = description
.lines()
.filter(|line| !line.trim().starts_with('#'))
.collect::<Vec<_>>()
.join("\n");

if description.is_empty() {
Ok(None)
} else {
// check if the file only contains whitespace
let description = description.trim();
if description.is_empty() {
Ok(None)
} else {
Ok(Some(description.to_string()))
}
}
}
3 changes: 3 additions & 0 deletions src/changelog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const TEMPLATE: &str = include_str!("templates/changelog_entry.md");
pub struct ChangelogEntry<'a> {
pub version: &'a str,
pub commits: HashMap<String, Vec<ChangelogCommit>>,
pub description: Option<String>,
}

#[derive(Clone, Debug, serde::Serialize)]
Expand Down Expand Up @@ -37,6 +38,7 @@ pub fn display_commit_type(commit_type: &CommitType) -> String {
pub fn generate_changelog_entry<'a, I: IntoIterator<Item = &'a Commit>>(
commits: I,
version: &str,
description: Option<String>,
) -> String {
let mut env = minijinja::Environment::new();
env.add_template("changelog_entry", TEMPLATE).unwrap();
Expand All @@ -56,6 +58,7 @@ pub fn generate_changelog_entry<'a, I: IntoIterator<Item = &'a Commit>>(
let entry = ChangelogEntry {
version,
commits: typed_commits,
description,
};

let template = env.get_template("changelog_entry").unwrap();
Expand Down
4 changes: 4 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ pub(crate) struct RawBump {
pub(crate) struct Bump {
#[clap(flatten)]
pub options: BumpOptions,

/// Whether to prompt for a hand-written summary message
#[arg(short, long)]
pub description: bool,
}

#[derive(clap::Args, Debug)]
Expand Down
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ pub enum Error {
#[error("invalid toml: {0}")]
TomlDeserialize(#[from] toml::de::Error),

#[error("EDITOR environment variable not set")]
EditorNotSet,
#[error("invalid replacement count: {0}")]
InvalidReplacementCount(usize),
#[error("invalid cargo.toml: {0}")]
Expand Down
5 changes: 4 additions & 1 deletion src/templates/changelog_entry.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## {{ entry.version }}
{% for commit_type, commits in entry.commits|items -%}
{% if entry.description %}
{{ entry.description }}
{% endif -%}
{% for commit_type, commits in entry.commits|items %}
#### {{ commit_type }}
{% for commit in commits -%}
- {{ commit.summary }} - ({{ commit.hash }}) - {{ commit.author }}
Expand Down

0 comments on commit f53b1e7

Please sign in to comment.