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:
parent
4b0c0eb09a
commit
db480241f1
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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>,
|
||||
|
@ -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>,
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user