use std::path::{Path, PathBuf};
use anyhow::Context;
use chrono::NaiveDate;
use clap::builder::{NonEmptyStringValueParser, PathBufValueParser};
use git_cliff_core::config::Config as GitCliffConfig;
use release_plz_core::{ChangelogRequest, UpdateRequest};
use crate::config::Config;
use super::repo_command::RepoCommand;
#[derive(clap::Parser, Debug)]
pub struct Update {
#[arg(long, value_parser = PathBufValueParser::new())]
project_manifest: Option<PathBuf>,
#[arg(long, value_parser = PathBufValueParser::new())]
registry_project_manifest: Option<PathBuf>,
#[arg(
short,
long,
value_parser = NonEmptyStringValueParser::new()
)]
package: Option<String>,
#[arg(long, conflicts_with("release_date"))]
no_changelog: bool,
#[arg(
long,
conflicts_with("no_changelog"),
value_parser = NonEmptyStringValueParser::new()
)]
release_date: Option<String>,
#[arg(
long,
conflicts_with("registry_project_manifest"),
value_parser = NonEmptyStringValueParser::new()
)]
registry: Option<String>,
#[arg(short, long)]
update_deps: bool,
#[arg(
long,
env = "GIT_CLIFF_CONFIG",
value_name = "PATH",
conflicts_with("no_changelog"),
value_parser = PathBufValueParser::new()
)]
changelog_config: Option<PathBuf>,
#[arg(long)]
allow_dirty: bool,
#[arg(long, value_parser = NonEmptyStringValueParser::new())]
repo_url: Option<String>,
#[arg(
long,
value_name = "PATH",
value_parser = PathBufValueParser::new()
)]
config: Option<PathBuf>,
}
impl RepoCommand for Update {
fn optional_project_manifest(&self) -> Option<&Path> {
self.project_manifest.as_deref()
}
fn repo_url(&self) -> Option<&str> {
self.repo_url.as_deref()
}
}
impl Update {
pub fn config(&self) -> anyhow::Result<Config> {
super::parse_config(self.config.as_deref())
}
fn dependencies_update(&self, config: &Config) -> bool {
self.update_deps || config.workspace.update.dependencies_update == Some(true)
}
fn allow_dirty(&self, config: &Config) -> bool {
self.allow_dirty || config.workspace.update.allow_dirty == Some(true)
}
pub fn update_request(&self, config: Config) -> anyhow::Result<UpdateRequest> {
let project_manifest = self.project_manifest();
let mut update = UpdateRequest::new(project_manifest.clone())
.with_context(|| {
format!("Cannot find file {project_manifest:?}. Make sure you are inside a rust project or that --project-manifest points to a valid Cargo.toml file.")
})?
.with_dependencies_update(self.dependencies_update(&config))
.with_allow_dirty(self.allow_dirty(&config));
match self.get_repo_url(&config) {
Ok(repo_url) => {
update = update.with_repo_url(repo_url);
}
Err(e) => tracing::warn!("Cannot determine repo url. The changelog won't contain the release link. Error: {:?}", e),
}
if let Some(registry_project_manifest) = &self.registry_project_manifest {
update = update
.with_registry_project_manifest(registry_project_manifest.clone())
.with_context(|| {
format!("cannot find project manifest {registry_project_manifest:?}")
})?;
}
update = config.fill_update_config(self.no_changelog, update);
{
let release_date = self
.release_date
.as_ref()
.map(|date| {
NaiveDate::parse_from_str(date, "%Y-%m-%d")
.context("cannot parse release_date to y-m-d format")
})
.transpose()?;
let changelog_req = ChangelogRequest {
release_date,
changelog_config: self.changelog_config(&config)?,
};
update = update.with_changelog_req(changelog_req);
}
if let Some(package) = &self.package {
update = update.with_single_package(package.clone());
}
if let Some(registry) = &self.registry {
update = update.with_registry(registry.clone());
}
Ok(update)
}
fn changelog_config(&self, config: &Config) -> anyhow::Result<Option<GitCliffConfig>> {
let default_config_path = dirs::config_dir()
.context("cannot get config dir")?
.join("git-cliff")
.join(git_cliff_core::DEFAULT_CONFIG);
let path = match self.user_changelog_config(config) {
Some(provided_path) => {
if provided_path.exists() {
provided_path
} else {
anyhow::bail!("cannot read {:?}", provided_path)
}
}
None => &default_config_path,
};
let config = if path.exists() {
Some(GitCliffConfig::parse(path).context("failed to parse git cliff config file")?)
} else {
None
};
Ok(config)
}
fn user_changelog_config<'a>(&'a self, config: &'a Config) -> Option<&'a Path> {
self.changelog_config
.as_deref()
.or(config.workspace.update.changelog_config.as_deref())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn input_generates_correct_release_request() {
let update_args = Update {
project_manifest: None,
registry_project_manifest: None,
package: None,
no_changelog: false,
release_date: None,
registry: None,
update_deps: false,
changelog_config: None,
allow_dirty: false,
repo_url: None,
config: None,
};
let config: Config = toml::from_str("").unwrap();
let req = update_args.update_request(config).unwrap();
let pkg_config = req.get_package_config("aaa");
assert_eq!(pkg_config, release_plz_core::PackageUpdateConfig::default());
}
}