Skip to content

Commit

Permalink
docs: expanded function commenting
Browse files Browse the repository at this point in the history
  • Loading branch information
SKalt committed Nov 15, 2020
1 parent 11a9761 commit a678b25
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 78 deletions.
1 change: 1 addition & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ nfpms:
# licence: UNLICENSED
homepage: https://github.com/skalt/git-cc
description: a git extension to help write conventional commits
depends: [git]
formats:
- apk
- deb
Expand Down
42 changes: 14 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,32 @@ go install github.com/skalt/git-cc
```

</details>

<!-- TODO: with npm -->
<!-- TODO: with yarn -->
<!-- TODO: with pnpm -->

<!-- TODO: with pip -->
<!-- TODO: with pipenv -->
<!-- TODO: with poetry -->
<!-- TODO: with conda -->

<!-- TODO: with cargo? -->

<!-- TODO: with apt -->
<!-- TODO: with rpm -->
<!-- TODO: with brew -->
<!-- TODO: with git? -->

<!-- TODO: deb/apk/rpm instructions -->

## Usage

<!-- TODO: gif/video of usage -->
```sh
# you can invoke either of the below to
# interactively write a conventional commit
git cc # in its entirety
git cc feat # from the commit-type onwards
git cc 'feat(scope)' # from the scope onwards
git conventional-commit
git cc

git cc feat # start after the commit-type
git cc 'feat(scope)' # start after the scope

# or validate your conventional commit
git cc feat: added conventional commits # ok! creates a commit
git cc feat add a typo # starts interaction at the scope
git cc 'feat(cli): added a conventional commit' # ok! creates a commit
git cc feat add a typo # starts interaction at the scope
git cc -m "invalid(stuff): should return 1"
```

<!--
## Why Conventional Commits?
The cool kids are doing it.
Plus, `cc`s make good, `grep`pable changelogs. -->

## Why An Interactive Cli?
## Why write conventional commits through an interactive CLI?
Figuring out what to write for an informative commit can be difficult.
The convential commits standard helps figure out what to write.
An interactive prompts helps with following the standard.
As a bonus, conventional commits facilitate generating changelogs and semantic version increments.
An interactive command-line application helps with following the standard.

<details><summary>Some parts of the conventional commit standard require quoting to work on the command-line.</summary>

Expand All @@ -66,6 +51,7 @@ Prior art:
- [`committizen`][commitizen]
- [`commitsar`][commitsar]

<!-- links -->
[cc-standard]: https://www.conventionalcommits.org/en/v1.0.0/

[commitizen]: https://github.com/commitizen/cz-cli
Expand Down
30 changes: 10 additions & 20 deletions cmd/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ func versionMode() {
fmt.Printf("git-cc %s\n", version)
}

// construct a shell `git commit` command with flags delegated from the git-cc
// cli
func getGitCommitCmd(cmd *cobra.Command) []string {
commitCmd := []string{}
// TODO: check message not passed
noEdit, _ := cmd.Flags().GetBool("no-edit")
message, _ := cmd.Flags().GetString("message")
for _, name := range boolFlags {
Expand All @@ -42,6 +43,7 @@ func getGitCommitCmd(cmd *cobra.Command) []string {
return commitCmd
}

// run a potentially interactive `git commit`
func doCommit(message string, dryRun bool, commitParams []string) {
f := config.GetCommitMessageFile()
file, err := os.Create(f)
Expand Down Expand Up @@ -71,6 +73,7 @@ func doCommit(message string, dryRun bool, commitParams []string) {
}
}

// run the conventional-commit helper logic. This may/not break into the TUI.
func mainMode(cmd *cobra.Command, args []string) {
cfg := config.Lookup(config.Init())
commitParams := getGitCommitCmd(cmd)
Expand All @@ -91,9 +94,7 @@ func mainMode(cmd *cobra.Command, args []string) {

cc := &parser.CC{}
message, _ := cmd.Flags().GetString("message")
// mPassed := false
if len(message) > 0 {
// mPassed = true
message += " "
}
message = message + strings.Join(args, " ")
Expand Down Expand Up @@ -131,6 +132,7 @@ func mainMode(cmd *cobra.Command, args []string) {
var Cmd = &cobra.Command{
Use: "git-cc",
Short: "write conventional commits",
// not using cobra subcommands since they prevent passing arbitrary arguments
Run: func(cmd *cobra.Command, args []string) {
version, _ := cmd.Flags().GetBool("version")
if version {
Expand All @@ -156,23 +158,11 @@ func init() {
Cmd.Flags().Bool("dry-run", false, "Only print the resulting conventional commit message; don't commit.")
Cmd.Flags().StringP("message", "m", "", "pass a complete conventional commit. If valid, it'll be committed without editing.")
Cmd.Flags().Bool("version", false, "print the version")
// TODO: use git commit's flags; see https://git-scm.com/docs/git-commit
// --amend ... might be better manually?
// --no-edit
// -C <commit>
// --reuse-message=<commit>
// Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) when creating the commit.
// -c <commit>
// --reedit-message=<commit>
// Like -C, but with -c the editor is invoked, so that the user can further edit the commit message.
// --fixup=<commit>
// Construct a commit message for use with rebase --autosquash. The commit message will be the subject line from the specified commit with a prefix of "fixup! ". See git-rebase[1] for details.
// --squash=<commit>
// Construct a commit message for use with rebase --autosquash. The commit message subject line is taken from the specified commit with a prefix of "squash! ". Can be used with additional commit message options (-m/-c/-C/-F). See git-rebase[1] for details.
// -short
// When doing a dry-run, give the output in the short-format. See git-status[1] for details. Implies --dry-run.
// --cleanup=<mode>
// This option determines how the supplied commit message should be cleaned up before committing. The <mode> can be strip, whitespace, verbatim, scissors or default.
// TODO: accept more of git commit's flags; see https://git-scm.com/docs/git-commit
// likely: --cleanup=<mode>
// more difficult, and possibly better done manually: --amend, -C <commit>
// --reuse-message=<commit>, -c <commit>, --reedit-message=<commit>,
// --fixup=<commit>, --squash=<commit>
Cmd.Flags().String("author", "", "delegated to git-commit")
Cmd.Flags().String("date", "", "delegated to git-commit")
Cmd.Flags().BoolP("all", "a", false, "see the git-commit docs for --all|-a")
Expand Down
19 changes: 14 additions & 5 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,21 @@ import (
"github.com/spf13/cobra/doc"
)

// Long: ,
// run when the CLI is passed --generate-shell-completion [bash|fish|powershell|zsh]
func generateShellCompletion(cmd *cobra.Command, args []string) {
var shell string
if len(args) > 0 {
switch len(args) {
case 1:
shell = args[0]
} else {
break
case 0:
shell = path.Base(os.Getenv("SHELL"))
break
default:
log.Fatalf(
"expecting one argument, bash|fish|powershell|zsh; %d args passed (%+v)",
len(args), args,
)
}
switch shell {
case "bash":
Expand All @@ -35,6 +43,7 @@ func generateShellCompletion(cmd *cobra.Command, args []string) {
}
}

// put a manpage in the first available location on the manpath
func generateManPage(cmd *cobra.Command, args []string) {
root := cmd.Root()
header := &doc.GenManHeader{
Expand All @@ -57,6 +66,6 @@ func generateManPage(cmd *cobra.Command, args []string) {
}
if err != nil {
log.Fatal(err)
}
// done, otherwise
} // else we're done; Cmd#Run handles exiting 0.
// IDEA: consider adding a --dry-run option, perhaps printing to stdout.
}
14 changes: 7 additions & 7 deletions cmd/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"fmt"
"os"
"strings"

tea "github.com/charmbracelet/bubbletea"
Expand All @@ -22,7 +21,7 @@ const ( // the order of the components
shortDescriptionIndex
breakingChangeIndex
// body omitted -- performed by GIT_EDITOR
doneIndex
nIndices // the number of indices
)

var (
Expand All @@ -35,7 +34,7 @@ type InputComponent interface {
}

type model struct {
commit [doneIndex]string
commit [nIndices]string
viewing componentIndex

typeInput type_selector.Model
Expand All @@ -46,10 +45,12 @@ type model struct {
choice chan string
}

// returns whether the minimum requirements for a conventional commit are met.
func (m model) ready() bool {
return len(m.commit[commitTypeIndex]) > 0 && len(m.commit[shortDescriptionIndex]) > 0
}

// returns the context portion of the CC header, e.g `type(scope): `.
func (m model) contextValue() string {
result := strings.Builder{}
result.WriteString(m.commit[commitTypeIndex])
Expand All @@ -64,6 +65,8 @@ func (m model) contextValue() string {
result.WriteString(": ")
return result.String()
}

// Returns a pretty-printed CC string. The model should be `.ready()` before you call `.value()`.
func (m model) value() string {
result := strings.Builder{}
result.WriteString(m.contextValue())
Expand Down Expand Up @@ -109,7 +112,7 @@ func initialModel(choice chan string, cc *parser.CC, cfg config.Cfg) model {
}
}
}
commit := [doneIndex]string{
commit := [nIndices]string{
cc.Type,
cc.Scope,
cc.Description,
Expand Down Expand Up @@ -209,9 +212,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
return m, cmd
}
case doneIndex:
fmt.Printf("%d > done", m.viewing)
os.Exit(1)
}
return m, cmd
default:
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package main

import "github.com/skalt/git-cc/cmd"

// provided by goreleaser; see .goreleaser.yml
var version string = "no version provided"

// TODO: var version *string
func main() {
// here's where I'd do an ldflags injection
cmd.SetVersion(version)
Expand Down
25 changes: 16 additions & 9 deletions pkg/config/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,20 @@ func Lookup(cfg *viper.Viper) Cfg {
}
return data
}

func getGitVar(var_name string) (string, error) {
cmd := exec.Command("git", "var", var_name)
func stdoutFrom(args ...string) (string, error) {
cmd := exec.Command(args[0], args[1:]...)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
return out.String(), err
}

func getGitVar(var_name string) (string, error) {
out, err := stdoutFrom("git", "var", var_name)
if err != nil {
return "", err
} else {
return strings.TrimRight(out.String(), " \t\r\n"), err
return strings.TrimRight(out, " \t\r\n"), err
}
}

Expand All @@ -124,23 +128,26 @@ func GetGitEditor() string {
}

func GetCommitMessageFile() string {
cmd := exec.Command("git", "rev-parse", "--absolute-git-dir")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
out, err := stdoutFrom("git", "rev-parse", "--absolute-git-dir")
if err != nil {
log.Fatal(err)
}
return strings.TrimRight(out.String(), " \t\r\n") + string(os.PathSeparator) + "COMMIT_EDITMSG"
return strings.Join(
[]string{strings.TrimRight(out, " \t\r\n"), "COMMIT_EDITMSG"},
string(os.PathSeparator),
)
}

// interactively edit the config file, if any was used.
func EditCfgFile(cfg *viper.Viper) Cfg {
editCmd := []string{}
// sometimes $EDITOR can be a script with spaces, like `code --wait`
for _, part := range strings.Split(GetEditor(), " ") {
if part != "" {
editCmd = append(editCmd, part)
}
}
// TODO: if no config file is present, either fail or create one.
editCmd = append(editCmd, cfg.ConfigFileUsed())
cmd := exec.Command(editCmd[0], editCmd[1:]...)
cmd.Stdin, cmd.Stdout = os.Stdin, os.Stderr
Expand Down
11 changes: 5 additions & 6 deletions pkg/description_editor/description_editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ func (m Model) Focus() tea.Cmd {
m.input.Focus()
return nil
}
func (m Model) Blur() {
m.input.Blur()
}
func (m Model) Value() string {
return m.input.Value()
}
Expand All @@ -61,14 +58,16 @@ func NewModel(lengthLimit int, value string, enforced bool) Model {

func viewCounter(m Model) string {
current := m.prefixLen + len(m.input.Value())
paddedFormat := fmt.Sprintf("(%%%dd/%d)", len(fmt.Sprintf("%d", m.lengthLimit)), m.lengthLimit)
paddedFormat := fmt.Sprintf(
"(%%%dd/%d)", len(fmt.Sprintf("%d", m.lengthLimit)), m.lengthLimit,
)
view := fmt.Sprintf(paddedFormat, current)
if current < m.lengthLimit {
return config.Faint(view)
} else if current == m.lengthLimit {
return view // render in a warning color termenv.String(view).
} else {
return termenv.String(view).Underline().String() // render in an alert color
} else { // render in an alert color
return termenv.String(view).Underline().String()
}
}

Expand Down
6 changes: 5 additions & 1 deletion pkg/scope_selector/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Model struct {
input single_select.Model
}

// the method for determining if the current input matches an option.
func match(m *single_select.Model, query string, option string) bool {
if option == "new scope" {
for _, opt := range m.Options {
Expand All @@ -27,13 +28,16 @@ func match(m *single_select.Model, query string, option string) bool {
return single_select.MatchStart(m, query, option)
}
}

// given options from config, add the leading "unscoped" and trailing "new scope" options
func makeOptions(options []map[string]string) []map[string]string {
return append(append(
[]map[string]string{{"": "unscoped; affects the entire project"}},
options...,
), map[string]string{"new scope": "edit a new scope into your configuration file"})
}

// should return two slices of string of equal size.
func makeOptHintPair(options []map[string]string) ([]string, []string) {
values, hints := []string{}, []string{}
for _, option := range options {
Expand All @@ -52,7 +56,7 @@ func NewModel(cc *parser.CC, cfg config.Cfg) Model {
cc.Scope,
makeOptions(cfg.Scopes),
match,
), // TODO: Option to add new scope?
),
}
}

Expand Down
Loading

0 comments on commit a678b25

Please sign in to comment.