Skip to content

Commit

Permalink
feat(cli): add shell completions
Browse files Browse the repository at this point in the history
coco commits are now args instead of subcommands
  • Loading branch information
oknozor committed Oct 24, 2020
1 parent 1f0671d commit fa24d64
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 230 deletions.
37 changes: 32 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ features related to the conventional commit specification. Anything else shall b
- [Cargo](#cargo)
- [Archlinux](#archlinux)
- [Binaries](#Binaries)
- [Shell completions](#Shell-completions)
- [Coco Commits](#Coco-Commits)
- [Breaking changes](#Breaking-changes)
- [Cog commands](#Cog-commands)
Expand Down Expand Up @@ -87,9 +88,35 @@ At the moment Cocogitto comes with two binaries `coco` and `cog`.
- `cog` does everything else : check your
repo history against the spec, edit malformed commit messages, generate changelog and bump versions etc.

### Shell completions

Before getting started you might want to install shell completions for `cog` and `coco` commands.
Supported shells are `bash`, `elvish`, `fish` and `zsh`.

Example installing completions:

```
# Bash
$ cog generate-completions bash > ~/.local/share/bash-completion/completions/cog
$ coco generate-completions bash > ~ bash > ~/.local/share/bash-completion/completions/coco
# Bash (macOS/Homebrew)
$ cog generate-completions bash > ~ bash > $(brew --prefix)/etc/bash_completion.d/cog.bash-completion
$ coco generate-completions bash > ~ bash > $(brew --prefix)/etc/bash_completion.d/coco.bash-completion
# Fish
$ mkdir -p ~/.config/fish/completions
$ cog generate-completions bash > ~ fish > ~/.config/fish/completions/cog.fish
$ coco generate-completions bash > ~ fish > ~/.config/fish/completions/coco.fish
# Zsh
$ cog generate-completions bash > ~ zsh > ~/.zfunc/_cog
$ coco generate-completions bash > ~ zsh > ~/.zfunc/_coco
```

## Coco Commits

`coco` allows you to easily create commits respecting the conventional specification. It comes with a set of sub-commands
`coco` allows you to easily create commits respecting the conventional specification. It comes with a set of predefined arguments
named after conventional commit types : `style`, `build`, `refactor`, `ci`, `fix`, `test`, `perf`, `chore`, `feat`, `revert`, `docs`.

Conventional commits are structured as follow :
Expand All @@ -102,14 +129,14 @@ Conventional commits are structured as follow :
[optional footer(s)]
```

All `coco` subcommands follows the same structure :
All `coco` commit commands follows the same structure :

```
coco {type} {message} [optional scope] [optional body] [optional footer]
```

The only difference you need to remember is that `coco` commit scope comes after the commit description. This allows
to use positional arguments instead of typing flags (ex: `coco -t {type} -s {scope} -m {message}... and so on`)
using positional arguments instead of typing flags (ex: `coco -t {type} -s {scope} -m {message}... and so on`)

For instance if you want to create the following commit : `feat: add awesome feature` you would run this :

Expand Down Expand Up @@ -236,7 +263,7 @@ cog log --author "Paul Delafosse" "Mike Lubinets" --type feat --scope cli --no-e

### Generate changelogs

There is two way to generate changelog with `cog` :
There are two way to generate changelog with `cog` :
- To stdout with `cog changelog`.

```
Expand All @@ -259,7 +286,7 @@ There is two way to generate changelog with `cog` :
d4aa61 - change config name to cog.toml - Paul Delafosse
```

- To your repo `CHANGELOG.md` file with `cog bump` (see [Auto bump](#auto-bump)).
- To your repo `CHANGELOG.md` file with `cog bump`.


### Auto bump
Expand Down
2 changes: 1 addition & 1 deletion cog.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ post_bump_hooks = [
# An optional list of additional allowed commit type
# `coco {commit_type}` commit command will be generated at runtime
[commit_types]
ex = { changelog_title = "This is the markdown title for `ex` commit type", help_message = "This is the cli `--help` message for `ex` commits" }
ex = { changelog_title = "This is the markdown title for `ex` commit type" }
121 changes: 77 additions & 44 deletions src/bin/coco.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use anyhow::Result;
use clap::{App, AppSettings, Arg, SubCommand};
use clap::{App, AppSettings, Arg, Shell, SubCommand};
use cocogitto::CocoGitto;

const APP_SETTINGS: &[AppSettings] = &[
AppSettings::SubcommandRequiredElseHelp,
AppSettings::ArgsNegateSubcommands,
AppSettings::SubcommandsNegateReqs,
AppSettings::UnifiedHelpMessage,
AppSettings::ColoredHelp,
AppSettings::VersionlessSubcommands,
Expand All @@ -17,52 +18,84 @@ const SUBCOMMAND_SETTINGS: &[AppSettings] = &[
AppSettings::DeriveDisplayOrder,
];

const GENERATE_COMPLETIONS: &str = "generate-completions";

fn main() -> Result<()> {
let cocogitto = CocoGitto::get()?;

let matches = App::new("Cocogito")
.settings(APP_SETTINGS)
.version(env!("CARGO_PKG_VERSION"))
.author("Paul D. <paul.delafosse@protonmail.com>")
.about("A conventional commit compliant, changelog and commit generator")
.long_about("Conventional Commit Git Terminal Overlord is a tool to help you use the conventional commit specification")
.subcommands(CocoGitto::get_commit_metadata()
.iter()
.map(|(commit_type, commit_config)| {
SubCommand::with_name(commit_type.get_key_str())
.settings(SUBCOMMAND_SETTINGS)
.about(commit_config.help_message.as_str())
.help(commit_config.help_message.as_str())
.arg(Arg::with_name("message").help("The commit message"))
.arg(Arg::with_name("scope").help("The scope of the commit message"))
.arg(Arg::with_name("body").help("The body of the commit message"))
.arg(Arg::with_name("footer").help("The footer of the commit message"))
.arg(
Arg::with_name("breaking-change")
.help("BREAKING CHANGE commit")
.short("B")
.long("breaking-change"),
)
})
.collect::<Vec<App>>()
).get_matches();
let matches = app().get_matches();

if let Some(subcommand) = matches.subcommand_matches(GENERATE_COMPLETIONS) {
let for_shell = match subcommand.value_of("type").unwrap() {
"bash" => Shell::Bash,
"elvish" => Shell::Elvish,
"fish" => Shell::Fish,
"zsh" => Shell::Zsh,
_ => unreachable!(),
};
app().gen_completions_to("coco", for_shell, &mut std::io::stdout());
} else {
let commit_type = matches.value_of("type").unwrap().to_string();
let message = matches.value_of("message").unwrap().to_string();
let scope = matches.value_of("scope").map(|scope| scope.to_string());
let body = matches.value_of("body").map(|body| body.to_string());
let footer = matches.value_of("footer").map(|footer| footer.to_string());
let breaking_change = matches.is_present("breaking-change");

if let Some(commit_subcommand) = matches.subcommand_name() {
if let Some(args) = matches.subcommand_matches(commit_subcommand) {
let message = args.value_of("message").unwrap().to_string();
let scope = args.value_of("scope").map(|scope| scope.to_string());
let body = args.value_of("body").map(|body| body.to_string());
let footer = args.value_of("footer").map(|footer| footer.to_string());
let breaking_change = args.is_present("breaking-change");
cocogitto.conventional_commit(
commit_subcommand,
scope,
message,
body,
footer,
breaking_change,
)?;
}
cocogitto.conventional_commit(
&commit_type,
scope,
message,
body,
footer,
breaking_change,
)?;
}

Ok(())
}

fn app<'a, 'b>() -> App<'a, 'b> {
let keys = CocoGitto::get_commit_metadata()
.iter()
.map(|(commit_type, _)| commit_type.get_key_str())
.collect::<Vec<&str>>();

App::new("Coco")
.settings(APP_SETTINGS)
.version(env!("CARGO_PKG_VERSION"))
.author("Paul D. <paul.delafosse@protonmail.com>")
.about("A command line tool to create conventional commits")
.arg(
Arg::with_name("type")
.help("The type of the commit message")
.possible_values(keys.as_slice())
.required(true),
)
.arg(
Arg::with_name("message")
.help("The type of the commit message")
.required(true),
)
.arg(Arg::with_name("scope").help("The scope of the commit message"))
.arg(Arg::with_name("body").help("The body of the commit message"))
.arg(Arg::with_name("footer").help("The footer of the commit message"))
.arg(
Arg::with_name("breaking-change")
.help("BREAKING CHANGE commit")
.short("B")
.long("breaking-change"),
)
.subcommand(
SubCommand::with_name(GENERATE_COMPLETIONS)
.settings(SUBCOMMAND_SETTINGS)
.about("Generate shell completions")
.arg(
Arg::with_name("type")
.possible_values(&["bash", "elvish", "fish", "zsh"])
.required(true)
.takes_value(true)
.help("Type of completions to generate"),
),
)
}

0 comments on commit fa24d64

Please sign in to comment.