Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Larg PR - Adds support for features #3 , #19 , and #20 #27

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@

#Others
.DS_Store

# Temporary files
.*~
32 changes: 26 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ description = "A conventional changelog for the rest of us"
regex = "*"
time = "*"
clap = "*"
semver = "*"
toml = "*"
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ USAGE:
clog [FLAGS] [OPTIONS]

FLAGS:
--from-latest-tag uses the latest tag as starting point (ignores other --from parameters)
--from-latest-tag use latest tag as start (instead of --from)
-h, --help Prints help information
--major Increment major version by one (Sets minor and patch to 0)
--minor Increment minor version by one (Sets patch to 0)
--patch Increment patch version by one
-v, --version Prints version information
-h, --help Prints this message

OPTIONS:
--subtitle=subtitle e.g. crazy-release-title
--from=from e.g. 12a8546
-r, --repository=repository e.g. https://github.com/thoughtram/clog
--setversion=setversion e.g. 1.0.1
--subtitle=subtitle e.g. crazy-release-title
--to=to e.g. 8057684 (Defaults to HEAD when omitted)
--from=from e.g. 12a8546

```

Try it!
Expand Down
132 changes: 132 additions & 0 deletions src/clogconfig.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::borrow::ToOwned;
use std::fmt::Display;
use std::env;

use clap::ArgMatches;
use toml::{Value, Parser};

use git;
use common::CommitType;
use CLOG_CONFIG_FILE;

pub struct ClogConfig {
pub grep: String,
pub format: String,
pub repo: String,
pub version: String,
pub subtitle: String,
pub from: String,
pub to: String,
}

pub type ConfigResult = Result<ClogConfig, Box<Display>>;

impl ClogConfig {
pub fn from_matches(matches: &ArgMatches) -> ConfigResult {
// compute version early, so we can exit on error
let version = {
// less typing later...
let (major, minor, patch) = (matches.is_present("major"), matches.is_present("minor"), matches.is_present("patch"));
if matches.is_present("setversion") {
matches.value_of("setversion").unwrap().to_owned()
} else if major || minor || patch {
match git::get_latest_tag_ver() {
Ok(ref mut v) => {
// if-else may be quicker, but it's longer mentally, and this isn't slow
match (major, minor, patch) {
(true,_,_) => { v.major += 1; v.minor = 0; v.patch = 0; },
(_,true,_) => { v.minor += 1; v.patch = 0; },
(_,_,true) => { v.patch += 1; },
_ => unreachable!()
}
format!("v{}", v)
},
Err(e) => {
return Err(Box::new(format!("Error: {}\n\n\tEnsure the tag format follows Semantic Versioning such as N.N.N\n\tor set the version manually with --setversion <version>" , e )));
}
}
} else {
// Use short hash
(&git::get_last_commit()[0..8]).to_owned()
}
};

let cwd = match env::current_dir() {
Ok(d) => d,
Err(e) => return Err(Box::new(e)),
};

let cfg_file = Path::new(&cwd).join(CLOG_CONFIG_FILE);
let mut toml_from_latest = None;
let mut toml_repo = None;
let mut toml_subtitle = None;

if let Ok(ref mut toml_f) = File::open(cfg_file){
let mut toml_s = String::with_capacity(100);

if let Err(e) = toml_f.read_to_string(&mut toml_s) {
return Err(Box::new(e))
}

toml_s.shrink_to_fit();

let mut toml = Parser::new(&toml_s[..]);

let toml_table = match toml.parse() {
Some(table) => table,
None => {
return Err(Box::new(format!("Error parsing file {}\n\nPlease check the format or specify the options manually", CLOG_CONFIG_FILE)))
}
};

let clog_table = match toml_table.get("clog") {
Some(table) => table,
None => {
return Err(Box::new(format!("Error parsing file {}\n\nPlease check the format or specify the options manually", CLOG_CONFIG_FILE)))
}
};

toml_from_latest = clog_table.lookup("from-latest-tag").unwrap_or(&Value::Boolean(false)).as_bool();
toml_repo = match clog_table.lookup("repository") {
Some(val) => Some(val.as_str().unwrap_or("").to_owned()),
None => Some("".to_owned())
};
toml_subtitle = match clog_table.lookup("subtitle") {
Some(val) => Some(val.as_str().unwrap_or("").to_owned()),
None => Some("".to_owned())
};
};

let from = if matches.is_present("from-latest-tag") || toml_from_latest.unwrap_or(false) {
git::get_latest_tag()
} else if let Some(from) = matches.value_of("from") {
from.to_owned()
} else {
"".to_owned()
};

let repo = match matches.value_of("repository") {
Some(repo) => repo.to_owned(),
None => toml_repo.unwrap_or("".to_owned())
};

let subtitle = match matches.value_of("subtitle") {
Some(title) => title.to_owned(),
None => toml_subtitle.unwrap_or("".to_owned())
};

Ok(ClogConfig{
grep: format!("{}BREAKING'", CommitType::all_aliases().iter().fold(String::new(),|acc, al| acc + &format!("^{}|", al)[..])),
format: "%H%n%s%n%b%n==END==".to_owned(),
repo: repo,
version: version,
subtitle: subtitle,
from: from,
to: matches.value_of("to").unwrap_or("HEAD").to_owned(),
})
}

}
17 changes: 12 additions & 5 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
use std::fmt;
use std::collections::HashMap;

#[derive(Debug, PartialEq, Clone)]
pub enum CommitType {
Feature,
Fix,
Unknown
// Creates an enum where the poritions inside the '(' and ')' act as aliases for that
// commit type. This macro auto-generates an "Unknown" variant for failures, no need to specify
//
// Later you can call CommitType::Fix.aliases() to get all the aliases as a Vec<'static str>
// or CommitType::all_aliases() to get a Vec<'static str> of all aliases
// This macro also implements std::str::FromStr to allow things like "feat".parse<CommitType>();
commit_type_enum!{
#[derive(Debug, PartialEq, Clone)]
pub enum CommitType {
Feature ( feat, ft ),
Fix ( fix, fx )
}
}

#[derive(Clone)]
Expand Down
4 changes: 0 additions & 4 deletions src/format_util.rs

This file was deleted.

55 changes: 30 additions & 25 deletions src/git.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,56 @@
use std::process::Command;
use common:: { LogEntry };
use common::CommitType;
use std::borrow::ToOwned;

#[derive(Debug)]
pub struct LogReaderConfig {
pub grep: String,
pub format: String,
pub from: Option<String>,
pub to: String
}
use semver;

use clogconfig::ClogConfig;
use common::{ LogEntry, CommitType };

pub fn get_latest_tag () -> String {
pub fn get_latest_tag() -> String {
let output = Command::new("git")
.arg("rev-list")
.arg("--tags")
.arg("--max-count=1")
.output().unwrap_or_else(|e| panic!("Failed to run git rev-list with error: {}",e));
.output().unwrap_or_else(|e| panic!("Failed to run 'git rev-list' with error: {}",e));
let buf = String::from_utf8_lossy(&output.stdout);

buf.trim_matches('\n').to_owned()
}

pub fn get_last_commit () -> String {
pub fn get_latest_tag_ver() -> Result<semver::Version, semver::ParseError> {
let output = Command::new("git")
.arg("describe")
.arg("--tags")
.arg("--abbrev=0")
.output().unwrap_or_else(|e| panic!("Failed to run 'git describe' with error: {}",e));

let v_string = String::from_utf8_lossy(&output.stdout);
semver::Version::parse(&v_string[..].trim_left_matches(|c| c == 'v' || c == 'V'))
}

pub fn get_last_commit() -> String {
let output = Command::new("git")
.arg("rev-parse")
.arg("HEAD")
.output().unwrap_or_else(|e| panic!("Failed to run git rev-parse with error: {}", e));
.output().unwrap_or_else(|e| panic!("Failed to run 'git rev-parse' with error: {}", e));

String::from_utf8_lossy(&output.stdout).into_owned()
}

pub fn get_log_entries (config:LogReaderConfig) -> Vec<LogEntry>{
pub fn get_log_entries(config: &ClogConfig) -> Vec<LogEntry>{

let range = match config.from {
Some(ref from) => format!("{}..{}", from, config.to),
None => "HEAD".to_owned()
let range = match &config.from[..] {
"" => "HEAD".to_owned(),
_ => format!("{}..{}", config.from, config.to)
};

let output = Command::new("git")
.arg("log")
.arg("-E")
.arg(&format!("--grep={}",config.grep))
.arg(&format!("--format={}", "%H%n%s%n%b%n==END=="))
.arg(&format!("--grep={}", config.grep))
.arg(&format!("--format={}", config.format))
.arg(&range)
.output().unwrap_or_else(|e| panic!("Failed to run git log with error: {}", e));
.output().unwrap_or_else(|e| panic!("Failed to run 'git log' with error: {}", e));

String::from_utf8_lossy(&output.stdout)
.split("\n==END==\n")
Expand All @@ -63,11 +69,10 @@ fn parse_raw_commit(commit_str:&str) -> LogEntry {
let (subject, component, commit_type) =
match lines.next().and_then(|s| commit_pattern.captures(s)) {
Some(caps) => {
let commit_type = match caps.at(1) {
Some("feat") => CommitType::Feature,
Some("fix") => CommitType::Fix,
_ => CommitType::Unknown
};
// The macro that made the CommitType automatically implements std::str::FromStr
// with all aliases or falls back to CommitType::Unknown on failure so we can
// call unwrap().
let commit_type = caps.at(1).unwrap_or("").parse::<CommitType>().unwrap();
let component = caps.at(2);
let subject = caps.at(3);
(subject, component, commit_type)
Expand Down
Loading