Skip to content

Commit

Permalink
Merge pull request #5 from bvieira/prepare-commit
Browse files Browse the repository at this point in the history
Validate commit message action
  • Loading branch information
bvieira authored Sep 1, 2020
2 parents 6910d2c + c323081 commit 0513536
Show file tree
Hide file tree
Showing 12 changed files with 451 additions and 47 deletions.
9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ release:
make build
@zip -j bin/git-sv_$(VERSION)_$(BUILDOS)_$(BUILDARCH).zip bin/$(BUILDOS)_$(BUILDARCH)/$(BIN)

## release-all: prepare linux, darwin and windows binary for release
## release-all: prepare linux, darwin and windows binary for release (requires sv4git)
release-all:
@rm -rf bin
BUILDOS=linux make release
BUILDOS=darwin make release
BUILDOS=windows make release

VERSION=$(shell git sv nv) BUILDOS=linux make release
VERSION=$(shell git sv nv) BUILDOS=darwin make release
VERSION=$(shell git sv nv) BUILDOS=windows make release
85 changes: 55 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ download the latest release and add the binary on your path

you can config using the environment variables

| Variable | description | default |
| --------- | ----------| ----------|
|MAJOR_VERSION_TYPES|types used to bump major version||
|MINOR_VERSION_TYPES|types used to bump minor version|feat|
|PATCH_VERSION_TYPES|types used to bump patch version|build,ci,docs,fix,perf,refactor,style,test|
|INCLUDE_UNKNOWN_TYPE_AS_PATCH|force patch bump on unknown type|true|
|BRAKING_CHANGE_PREFIXES|list of prefixes that will be used to identify a breaking change|BREAKING CHANGE:,BREAKING CHANGES:|
|ISSUEID_PREFIXES|list of prefixes that will be used to identify an issue id|jira:,JIRA:,Jira:|
|TAG_PATTERN|tag version pattern|%d.%d.%d|
|RELEASE_NOTES_TAGS|release notes headers for each visible type|fix:Bug Fixes,feat:Features|
| Variable | description | default |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| MAJOR_VERSION_TYPES | types used to bump major version | |
| MINOR_VERSION_TYPES | types used to bump minor version | feat |
| PATCH_VERSION_TYPES | types used to bump patch version | build,ci,docs,fix,perf,refactor,style,test |
| INCLUDE_UNKNOWN_TYPE_AS_PATCH | force patch bump on unknown type | true |
| BRAKING_CHANGE_PREFIXES | list of prefixes that will be used to identify a breaking change | BREAKING CHANGE:,BREAKING CHANGES: |
| ISSUEID_PREFIXES | list of prefixes that will be used to identify an issue id | jira:,JIRA:,Jira: |
| TAG_PATTERN | tag version pattern | %d.%d.%d |
| RELEASE_NOTES_TAGS | release notes headers for each visible type | fix:Bug Fixes,feat:Features |
| VALIDATE_MESSAGE_SKIP_BRANCHES | ignore branches from this list on validate commit message | master,develop |
| COMMIT_MESSAGE_TYPES | list of valid commit types for commit message | build,ci,chore,docs,feat,fix,perf,refactor,revert,style,test |
| ISSUE_KEY_NAME | metadata key name used on validate commit message hook to enhance footer, if blank footer will not be added | jira |
| BRANCH_ISSUE_REGEX | regex to extract issue id from branch name, must have 3 groups (prefix, id, posfix), if blank footer will not be added | ^([a-z]+\\/)?([A-Z]+-[0-9]+)(-.*)? |

### Running

Expand Down Expand Up @@ -55,15 +59,36 @@ git-sv rn -h

##### Available commands

| Variable | description | has options |
| --------- | ---------- | :----------: |
| current-version, cv | get last released version from git | :x: |
| next-version, nv | generate the next version based on git commit messages | :x: |
| commit-log, cl | list all commit logs since last version as jsons | :heavy_check_mark: |
| release-notes, rn | generate release notes | :heavy_check_mark: |
| changelog, cgl | generate changelog | :heavy_check_mark: |
| tag, tg | generate tag with version based on git commit messages | :x: |
| help, h | Shows a list of commands or help for one command | :x: |
| Variable | description | has options |
| ---------------------------- | ------------------------------------------------------------- | :----------------: |
| current-version, cv | get last released version from git | :x: |
| next-version, nv | generate the next version based on git commit messages | :x: |
| commit-log, cl | list all commit logs since last version as jsons | :heavy_check_mark: |
| release-notes, rn | generate release notes | :heavy_check_mark: |
| changelog, cgl | generate changelog | :heavy_check_mark: |
| tag, tg | generate tag with version based on git commit messages | :x: |
| validate-commit-message, vcm | use as prepare-commit-message hook to validate commit message | :heavy_check_mark: |
| help, h | Shows a list of commands or help for one command | :x: |

##### Use validate-commit-message as prepare-commit-msg hook

Configure your .git/hooks/prepare-commit-msg

```bash
#!/bin/sh

COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3

git sv vcm --path "$(pwd)" --file $COMMIT_MSG_FILE --source $COMMIT_SOURCE
```

tip: you can configure a directory as your global git templates using the command below, check [git config docs](https://git-scm.com/docs/git-config#Documentation/git-config.txt-inittemplateDir) for more information!

```bash
git config --global init.templatedir '<YOUR TEMPLATE DIR>'
```

## Development

Expand All @@ -77,17 +102,17 @@ make

#### Make configs

| Variable | description|
| --------- | ----------|
| BUILDOS | build OS |
| BUILDARCH | build arch |
| ECHOFLAGS | flags used on echo |
| BUILDENVS | var envs used on build |
| BUILDFLAGS | flags used on build |

| Parameters | description|
| --------- | ----------|
| args | parameters that will be used on run |
| Variable | description |
| ---------- | ---------------------- |
| BUILDOS | build OS |
| BUILDARCH | build arch |
| ECHOFLAGS | flags used on echo |
| BUILDENVS | var envs used on build |
| BUILDFLAGS | flags used on build |

| Parameters | description |
| ---------- | ----------------------------------- |
| args | parameters that will be used on run |

```bash
#variables
Expand Down
20 changes: 12 additions & 8 deletions cmd/git-sv/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ import (

// Config env vars for cli configuration
type Config struct {
MajorVersionTypes []string `envconfig:"MAJOR_VERSION_TYPES" default:""`
MinorVersionTypes []string `envconfig:"MINOR_VERSION_TYPES" default:"feat"`
PatchVersionTypes []string `envconfig:"PATCH_VERSION_TYPES" default:"build,ci,docs,fix,perf,refactor,style,test"`
IncludeUnknownTypeAsPatch bool `envconfig:"INCLUDE_UNKNOWN_TYPE_AS_PATCH" default:"true"`
BreakingChangePrefixes []string `envconfig:"BRAKING_CHANGE_PREFIXES" default:"BREAKING CHANGE:,BREAKING CHANGES:"`
IssueIDPrefixes []string `envconfig:"ISSUEID_PREFIXES" default:"jira:,JIRA:,Jira:"`
TagPattern string `envconfig:"TAG_PATTERN" default:"%d.%d.%d"`
ReleaseNotesTags map[string]string `envconfig:"RELEASE_NOTES_TAGS" default:"fix:Bug Fixes,feat:Features"`
MajorVersionTypes []string `envconfig:"MAJOR_VERSION_TYPES" default:""`
MinorVersionTypes []string `envconfig:"MINOR_VERSION_TYPES" default:"feat"`
PatchVersionTypes []string `envconfig:"PATCH_VERSION_TYPES" default:"build,ci,docs,fix,perf,refactor,style,test"`
IncludeUnknownTypeAsPatch bool `envconfig:"INCLUDE_UNKNOWN_TYPE_AS_PATCH" default:"true"`
BreakingChangePrefixes []string `envconfig:"BRAKING_CHANGE_PREFIXES" default:"BREAKING CHANGE:,BREAKING CHANGES:"`
IssueIDPrefixes []string `envconfig:"ISSUEID_PREFIXES" default:"jira:,JIRA:,Jira:"`
TagPattern string `envconfig:"TAG_PATTERN" default:"%d.%d.%d"`
ReleaseNotesTags map[string]string `envconfig:"RELEASE_NOTES_TAGS" default:"fix:Bug Fixes,feat:Features"`
ValidateMessageSkipBranches []string `envconfig:"VALIDATE_MESSAGE_SKIP_BRANCHES" default:"master,develop"`
CommitMessageTypes []string `envconfig:"COMMIT_MESSAGE_TYPES" default:"build,ci,chore,docs,feat,fix,perf,refactor,revert,style,test"`
IssueKeyName string `envconfig:"ISSUE_KEY_NAME" default:"jira"`
BranchIssueRegex string `envconfig:"BRANCH_ISSUE_REGEX" default:"^([a-z]+\\/)?([A-Z]+-[0-9]+)(-.*)?"`
}

func loadConfig() Config {
Expand Down
57 changes: 57 additions & 0 deletions cmd/git-sv/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"sort"
"sv4git/sv"
"time"
Expand Down Expand Up @@ -228,3 +230,58 @@ func changelogHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor, rnP
return nil
}
}

func validateCommitMessageHandler(git sv.Git, validateMessageProcessor sv.ValidateMessageProcessor) func(c *cli.Context) error {
return func(c *cli.Context) error {
branch := git.Branch()
if validateMessageProcessor.SkipBranch(branch) {
warn("commit message validation skipped, branch in ignore list...")
return nil
}

filepath := fmt.Sprintf("%s/%s", c.String("path"), c.String("file"))

commitMessage, err := readFile(filepath)
if err != nil {
return fmt.Errorf("failed to read commit message, error: %s", err.Error())
}

if err := validateMessageProcessor.Validate(commitMessage); err != nil {
return fmt.Errorf("invalid commit message, error: %s", err.Error())
}

msg, err := validateMessageProcessor.Enhance(branch, commitMessage)
if err != nil {
warn("could not enhance commit message, %s", err.Error())
return nil
}
if msg == "" {
return nil
}

if err := appendOnFile(msg, filepath); err != nil {
return fmt.Errorf("failed to append meta-informations on footer, error: %s", err.Error())
}

return nil
}
}

func readFile(filepath string) (string, error) {
f, err := ioutil.ReadFile(filepath)
if err != nil {
return "", err
}
return string(f), nil
}

func appendOnFile(message, filepath string) error {
f, err := os.OpenFile(filepath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()

_, err = f.WriteString(message)
return err
}
7 changes: 7 additions & 0 deletions cmd/git-sv/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "fmt"

func warn(format string, values ...interface{}) {
fmt.Printf("WARN: "+format+"\n", values...)
}
12 changes: 12 additions & 0 deletions cmd/git-sv/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func main() {
semverProcessor := sv.NewSemVerCommitsProcessor(cfg.IncludeUnknownTypeAsPatch, cfg.MajorVersionTypes, cfg.MinorVersionTypes, cfg.PatchVersionTypes)
releasenotesProcessor := sv.NewReleaseNoteProcessor(cfg.ReleaseNotesTags)
outputFormatter := sv.NewOutputFormatter()
validateMessageProcessor := sv.NewValidateMessageProcessor(cfg.ValidateMessageSkipBranches, cfg.CommitMessageTypes, cfg.IssueKeyName, cfg.BranchIssueRegex)

app := cli.NewApp()
app.Name = "sv"
Expand Down Expand Up @@ -66,6 +67,17 @@ func main() {
Usage: "generate tag with version based on git commit messages",
Action: tagHandler(git, semverProcessor),
},
{
Name: "validate-commit-message",
Aliases: []string{"vcm"},
Usage: "use as prepare-commit-message hook to validate message",
Action: validateCommitMessageHandler(git, validateMessageProcessor),
Flags: []cli.Flag{
&cli.StringFlag{Name: "path", Required: true, Usage: "git working directory"},
&cli.StringFlag{Name: "file", Required: true, Usage: "name of the file that contains the commit log message"},
&cli.StringFlag{Name: "source", Required: true, Usage: "source of the commit message"},
},
},
}

apperr := app.Run(os.Args)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ require (
github.com/Masterminds/semver v1.5.0
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0
github.com/urfave/cli/v2 v2.1.1
github.com/urfave/cli/v2 v2.2.0
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
11 changes: 11 additions & 0 deletions sv/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Git interface {
Log(initialTag, endTag string) ([]GitCommitLog, error)
Tag(version semver.Version) error
Tags() ([]GitTag, error)
Branch() string
}

// GitCommitLog description of a single commit log
Expand Down Expand Up @@ -115,6 +116,16 @@ func (g GitImpl) Tags() ([]GitTag, error) {
return parseTagsOutput(string(out))
}

// Branch get git branch
func (GitImpl) Branch() string {
cmd := exec.Command("git", "symbolic-ref", "--short", "HEAD")
out, err := cmd.CombinedOutput()
if err != nil {
return ""
}
return strings.TrimSpace(strings.Trim(string(out), "\n"))
}

func parseTagsOutput(input string) ([]GitTag, error) {
scanner := bufio.NewScanner(strings.NewReader(input))
var result []GitTag
Expand Down
4 changes: 2 additions & 2 deletions sv/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ func Test_parseTagsOutput(t *testing.T) {
want []GitTag
wantErr bool
}{
{"with date", "2020-05-01 18:00:00 -0300#1.0.0", []GitTag{GitTag{Name: "1.0.0", Date: date("2020-05-01 18:00:00 -0300")}}, false},
{"without date", "#1.0.0", []GitTag{GitTag{Name: "1.0.0", Date: time.Time{}}}, false},
{"with date", "2020-05-01 18:00:00 -0300#1.0.0", []GitTag{{Name: "1.0.0", Date: date("2020-05-01 18:00:00 -0300")}}, false},
{"without date", "#1.0.0", []GitTag{{Name: "1.0.0", Date: time.Time{}}}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
Loading

0 comments on commit 0513536

Please sign in to comment.