add cl status, improve cl pack

cl-status
Ondřej Hruška 1 day ago
parent df234e2407
commit c25d593aab
  1. 3
      changelog/entries/Add "cl status".md
  2. 106
      src/action_pack.rs
  3. 14
      src/action_status.rs
  4. 26
      src/main.rs
  5. 28
      src/store.rs

@ -0,0 +1,3 @@
# New features
- Add command line option `cl status` (#SW-4716)
- Change `cl pack` to show a preview of the rendered changelog before asking for version.

@ -1,21 +1,56 @@
use crate::AppContext; use crate::AppContext;
use crate::config::ChannelName; use crate::config::ChannelName;
use crate::git::get_branch_name; use crate::git::{BranchName, get_branch_name};
use crate::store::{Release, Store}; use crate::store::{Release, Store};
use anyhow::bail; use anyhow::bail;
use colored::Colorize; use colored::Colorize;
/// Perform the action of packing changelog entries for a release pub fn pack_resolve_and_show_preview(
pub(crate) fn cl_pack(ctx: AppContext, channel: Option<ChannelName>) -> anyhow::Result<()> { ctx: &AppContext,
let mut store = Store::new(&ctx, false)?; user_chosen_channel: Option<ChannelName>,
let branch = get_branch_name(&ctx); branch: Option<&BranchName>,
) -> anyhow::Result<Option<(Release, ChannelName)>> {
let channel = resolve_channel(&ctx, user_chosen_channel, branch)?;
let store = Store::new(&ctx, false)?;
let unreleased = store.find_unreleased_changes(&channel)?;
if unreleased.is_empty() {
eprintln!("No unreleased changes.");
return Ok(None);
}
println!();
println!("Changes waiting for release:");
for entry in &unreleased {
println!("+ {}", entry.cyan());
}
println!();
let (channel_detected, channel_explicit) = match channel { let release = Release {
version: "Unreleased".to_string(),
entries: unreleased,
};
let rendered = store.render_release(&release)?;
println!("\nPreview:\n\n{}", rendered);
Ok(Some((release, channel)))
}
/// Resolve channel from current branch or other context info, ask if needed
fn resolve_channel(
ctx: &AppContext,
user_chosen_channel: Option<ChannelName>,
branch: Option<&BranchName>,
) -> anyhow::Result<ChannelName> {
let (channel_detected, channel_explicit) = match user_chosen_channel {
Some(ch) => (Some(ch), true), // passed via flag already Some(ch) => (Some(ch), true), // passed via flag already
None => ( None => (
branch branch
.as_ref() .as_ref()
.map(|b| b.parse_channel(&ctx)) .map(|b| b.parse_channel(ctx))
.transpose()? .transpose()?
.flatten(), .flatten(),
false, false,
@ -28,22 +63,6 @@ pub(crate) fn cl_pack(ctx: AppContext, channel: Option<ChannelName>) -> anyhow::
bail!("No such channel: {ch}"); bail!("No such channel: {ch}");
} }
// If the branch is named rel/3.40, this can extract 3.40.
// TODO try to get something better from git!
let version_base = branch
.as_ref()
.map(|b| b.parse_version(&ctx))
.transpose()?
.flatten();
// TODO detect version from git query?
// TODO remove this
eprintln!(
"Branch name: {:?}, channel: {:?}, version: {:?}",
branch, channel_detected, version_base
);
// Ask for the channel // Ask for the channel
let channel = if ctx.config.channels.len() > 1 { let channel = if ctx.config.channels.len() > 1 {
if channel_explicit { if channel_explicit {
@ -66,19 +85,31 @@ pub(crate) fn cl_pack(ctx: AppContext, channel: Option<ChannelName>) -> anyhow::
}; };
println!("Channel: {}", channel.green().bold()); println!("Channel: {}", channel.green().bold());
let unreleased = store.find_unreleased_changes(&channel)?; Ok(channel)
}
if unreleased.is_empty() { /// Perform the action of packing changelog entries for a release
eprintln!("No unreleased changes."); pub(crate) fn cl_pack(
ctx: AppContext,
user_chosen_channel: Option<ChannelName>,
) -> anyhow::Result<()> {
let branch = get_branch_name(&ctx);
let Some((mut release, channel)) =
pack_resolve_and_show_preview(&ctx, user_chosen_channel, branch.as_ref())?
else {
// No changes
return Ok(()); return Ok(());
} };
println!(); let mut store = Store::new(&ctx, false)?;
println!("Changes waiting for release:");
for entry in &unreleased { // If the branch is named rel/3.40, this can extract 3.40.
println!("+ {}", entry.cyan()); // TODO try to get something better from git!
} let version_base = branch
println!(); .as_ref()
.map(|b| b.parse_version(&ctx))
.transpose()?
.flatten();
// Ask for the version // Ask for the version
let mut version = version_base.unwrap_or_default(); let mut version = version_base.unwrap_or_default();
@ -99,14 +130,7 @@ pub(crate) fn cl_pack(ctx: AppContext, channel: Option<ChannelName>) -> anyhow::
} }
} }
let release = Release { release.version = version;
version,
entries: unreleased,
};
let rendered = store.render_release(&release)?;
println!("\n\nPreview:\n\n{}\n", rendered);
if !inquire::Confirm::new("Continue - write to changelog file?") if !inquire::Confirm::new("Continue - write to changelog file?")
.with_default(true) .with_default(true)

@ -0,0 +1,14 @@
use crate::AppContext;
use crate::action_pack::pack_resolve_and_show_preview;
use crate::config::ChannelName;
use crate::git::{get_branch_name};
/// Perform the action of packing changelog entries for a release
pub(crate) fn cl_status(
ctx: AppContext,
user_chosen_channel: Option<ChannelName>,
) -> anyhow::Result<()> {
let branch = get_branch_name(&ctx);
pack_resolve_and_show_preview(&ctx, user_chosen_channel, branch.as_ref())?;
Ok(())
}

@ -1,6 +1,7 @@
use crate::action_init::{cl_init, ClInit}; use crate::action_init::{ClInit, cl_init};
use crate::action_log::cl_log; use crate::action_log::cl_log;
use crate::action_pack::cl_pack; use crate::action_pack::cl_pack;
use crate::action_status::cl_status;
use crate::config::{ChannelName, Config}; use crate::config::{ChannelName, Config};
use anyhow::bail; use anyhow::bail;
use clap::builder::NonEmptyStringValueParser; use clap::builder::NonEmptyStringValueParser;
@ -17,6 +18,8 @@ mod action_pack;
mod action_init; mod action_init;
mod action_status;
mod store; mod store;
mod utils; mod utils;
@ -47,6 +50,12 @@ fn main_try() -> anyhow::Result<()> {
.flatten() .flatten()
.unwrap_or_else(|| "cl".to_string()); .unwrap_or_else(|| "cl".to_string());
let optional_channel_arg = clap::Arg::new("CHANNEL")
.short('x')
.long("channel")
.value_parser(NonEmptyStringValueParser::new())
.required(false);
let args = clap::Command::new(&binary_name) let args = clap::Command::new(&binary_name)
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
.author(env!("CARGO_PKG_AUTHORS")) .author(env!("CARGO_PKG_AUTHORS"))
@ -57,11 +66,12 @@ fn main_try() -> anyhow::Result<()> {
clap::Command::new("pack") clap::Command::new("pack")
.visible_alias("release") .visible_alias("release")
.about("Pack changelog entries to a changelog section") .about("Pack changelog entries to a changelog section")
.arg(clap::Arg::new("CHANNEL") .arg(optional_channel_arg.clone()),
.short('x') )
.long("channel") .subcommand(
.value_parser(NonEmptyStringValueParser::new()) clap::Command::new("status")
.required(false)), .about("Show outstanding change entries on the current channel (or specified channel)")
.arg(optional_channel_arg),
) )
.subcommand(clap::Command::new("add") .subcommand(clap::Command::new("add")
.visible_alias("log") .visible_alias("log")
@ -133,6 +143,10 @@ fn main_try() -> anyhow::Result<()> {
let channel: Option<ChannelName> = subargs.get_one("CHANNEL").cloned(); let channel: Option<ChannelName> = subargs.get_one("CHANNEL").cloned();
cl_pack(ctx, channel)?; cl_pack(ctx, channel)?;
} }
Some(("status", subargs)) => {
let channel: Option<ChannelName> = subargs.get_one("CHANNEL").cloned();
cl_status(ctx, channel)?;
}
None | Some(("add", _)) => cl_log(ctx)?, None | Some(("add", _)) => cl_log(ctx)?,
// TODO: status, flush // TODO: status, flush
Some((other, _)) => { Some((other, _)) => {

@ -382,22 +382,32 @@ struct ChannelReleaseStore {
impl ChannelReleaseStore { impl ChannelReleaseStore {
/// Load from a versions file /// Load from a versions file
fn load(releases_file: PathBuf, channel_name: ChannelName) -> anyhow::Result<Self> { fn load(releases_file: PathBuf, channel_name: ChannelName) -> anyhow::Result<Self> {
println!(
"Loading versions for channel {} from: {}",
channel_name,
releases_file.display()
);
let releases = if !releases_file.exists() { let releases = if !releases_file.exists() {
// File did not exist yet, create it - this catches error with write access early // File did not exist yet, create it - this catches error with write access early
let mut f = OpenOptions::new() let mut f = OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
.open(&releases_file)?; .open(&releases_file)
f.write_all("[]".as_bytes())?; .with_context(|| {
format!("Failed to open channel file: {}", releases_file.display())
})?;
f.write_all("[]".as_bytes()).with_context(|| {
format!(
"Failed to write into channel file: {}",
releases_file.display()
)
})?;
Default::default() Default::default()
} else { } else {
let channel_json = read_to_string(&releases_file)?; let channel_json = read_to_string(&releases_file).with_context(|| {
serde_json::from_str::<ReleaseList>(&channel_json)? format!("Failed to read channel file: {}", releases_file.display())
})?;
serde_json::from_str::<ReleaseList>(&channel_json).with_context(|| {
format!(
"Failed to parse content of channel file: {}",
releases_file.display()
)
})?
}; };
Ok(Self { Ok(Self {

Loading…
Cancel
Save