1
1
mirror of https://github.com/orhun/git-cliff.git synced 2024-09-11 06:55:38 +03:00

feat(cli): generate changelog from JSON context

This commit is contained in:
Jan Buchar 2024-08-04 22:19:37 +02:00
parent 4b0c0eb09a
commit db480241f1
5 changed files with 79 additions and 48 deletions

View File

@ -18,7 +18,10 @@ use crate::remote::github::GitHubClient;
use crate::remote::gitlab::GitLabClient;
use crate::template::Template;
use std::collections::HashMap;
use std::io::Write;
use std::io::{
Read,
Write,
};
use std::time::{
SystemTime,
UNIX_EPOCH,
@ -39,8 +42,16 @@ pub struct Changelog<'a> {
impl<'a> Changelog<'a> {
/// Constructs a new instance.
pub fn new(releases: Vec<Release<'a>>, config: &'a Config) -> Result<Self> {
let mut changelog = Changelog::build(releases, config)?;
changelog.process_commits();
changelog.process_releases();
changelog.add_remote_data()?;
Ok(changelog)
}
fn build(releases: Vec<Release<'a>>, config: &'a Config) -> Result<Self> {
let trim = config.changelog.trim.unwrap_or(true);
let mut changelog = Self {
Ok(Self {
releases,
header_template: match &config.changelog.header {
Some(header) => Some(Template::new(header.to_string(), trim)?),
@ -61,11 +72,12 @@ impl<'a> Changelog<'a> {
},
config,
additional_context: HashMap::new(),
};
changelog.process_commits();
changelog.process_releases();
changelog.add_remote_data()?;
Ok(changelog)
})
}
/// Constructs an instance from a serialized context object.
pub fn from_context<R: Read>(input: &mut R, config: &'a Config) -> Result<Self> {
Changelog::build(serde_json::from_reader(input)?, config)
}
/// Adds a key value pair to the template context.

View File

@ -37,7 +37,7 @@ static SHA1_REGEX: Lazy<Regex> = lazy_regex!(r#"^\b([a-f0-9]{40})\b (.*)$"#);
/// Object representing a link
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(rename_all(serialize = "camelCase"))]
pub struct Link {
/// Text of the link.
pub text: String,
@ -98,7 +98,7 @@ impl<'a> From<CommitSignature<'a>> for Signature {
/// Common commit object that is parsed from a repository.
#[derive(Debug, Default, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(rename_all(serialize = "camelCase"))]
pub struct Commit<'a> {
/// Commit ID.
pub id: String,

View File

@ -24,7 +24,7 @@ use serde::{
/// Representation of a release.
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(rename_all(serialize = "camelCase"))]
pub struct Release<'a> {
/// Release version, git tag.
pub version: Option<String>,

View File

@ -231,6 +231,15 @@ pub struct Opt {
/// Prints changelog context as JSON.
#[arg(short = 'x', long, help_heading = Some("FLAGS"))]
pub context: bool,
/// Generates changelog from a JSON context.
#[arg(
long,
help_heading = Some("FLAGS"),
value_name = "PATH",
value_parser = Opt::parse_dir,
num_args = 0..=1,
)]
pub from_context: Option<PathBuf>,
/// Strips the given parts from the changelog.
#[arg(short, long, value_name = "PART", value_enum)]
pub strip: Option<Strip>,

View File

@ -496,49 +496,59 @@ pub fn run(mut args: Opt) -> Result<()> {
if args.ignore_tags.is_some() {
config.git.ignore_tags.clone_from(&args.ignore_tags);
}
// Process the repositories.
let repositories = args.repository.clone().unwrap_or(vec![env::current_dir()?]);
let mut releases = Vec::<Release>::new();
for repository in repositories {
// Skip commits
let mut skip_list = Vec::new();
let ignore_file = repository.join(IGNORE_FILE);
if ignore_file.exists() {
let contents = fs::read_to_string(ignore_file)?;
let commits = contents
.lines()
.filter(|v| !(v.starts_with('#') || v.trim().is_empty()))
.map(|v| String::from(v.trim()))
.collect::<Vec<String>>();
skip_list.extend(commits);
}
if let Some(ref skip_commit) = args.skip_commit {
skip_list.extend(skip_commit.clone());
}
if let Some(commit_parsers) = config.git.commit_parsers.as_mut() {
for sha1 in skip_list {
commit_parsers.insert(0, CommitParser {
sha: Some(sha1.to_string()),
skip: Some(true),
..Default::default()
})
}
}
// Process the repository.
let repository = Repository::init(repository)?;
releases.extend(process_repository(
Box::leak(Box::new(repository)),
&mut config,
&args,
)?);
}
// Process commits and releases for the changelog.
if let Some(BumpOption::Specific(bump_type)) = args.bump {
config.bump.bump_type = Some(bump_type)
}
let mut changelog = Changelog::new(releases, &config)?;
let mut changelog: Changelog = if let Some(context_path) = args.from_context {
if context_path == Path::new("-") {
Changelog::from_context(&mut io::stdin(), &config)?
} else {
Changelog::from_context(&mut File::open(context_path)?, &config)?
}
} else {
// Process the repositories.
let repositories =
args.repository.clone().unwrap_or(vec![env::current_dir()?]);
let mut releases = Vec::<Release>::new();
for repository in repositories {
// Skip commits
let mut skip_list = Vec::new();
let ignore_file = repository.join(IGNORE_FILE);
if ignore_file.exists() {
let contents = fs::read_to_string(ignore_file)?;
let commits = contents
.lines()
.filter(|v| !(v.starts_with('#') || v.trim().is_empty()))
.map(|v| String::from(v.trim()))
.collect::<Vec<String>>();
skip_list.extend(commits);
}
if let Some(ref skip_commit) = args.skip_commit {
skip_list.extend(skip_commit.clone());
}
if let Some(commit_parsers) = config.git.commit_parsers.as_mut() {
for sha1 in skip_list {
commit_parsers.insert(0, CommitParser {
sha: Some(sha1.to_string()),
skip: Some(true),
..Default::default()
})
}
}
// Process the repository.
let repository = Repository::init(repository)?;
releases.extend(process_repository(
Box::leak(Box::new(repository)),
&mut config,
&args,
)?);
}
Changelog::new(releases, &config)?
};
// Print the result.
let mut out: Box<dyn io::Write> = if let Some(path) = &args.output {