Skip to content

Commit

Permalink
feat!: add config lookupAndParse, env option
Browse files Browse the repository at this point in the history
* precedence is env, 4 config files, then default config
* --config flag has highest precedence
  • Loading branch information
muthukrishnan24 committed Jan 31, 2022
1 parent 3931b36 commit 446e516
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 65 deletions.
140 changes: 77 additions & 63 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,32 @@ import (
"path/filepath"

"golang.org/x/mod/semver"
yaml "gopkg.in/yaml.v3"
yaml "gopkg.in/yaml.v2"

"github.com/conventionalcommit/commitlint/internal"
"github.com/conventionalcommit/commitlint/internal/registry"
"github.com/conventionalcommit/commitlint/lint"
)

const (
// ConfigFile represent default config file name
ConfigFile = "commitlint.yaml"
)
const commitlintConfig = "COMMITLINT_CONFIG"

var configFiles = []string{
".commitlint.yml",
".commitlint.yaml",
"commitlint.yml",
"commitlint.yaml",
}

// GetConfig gets the config path according to the precedence
// if needed parses given config file and returns config instance
func GetConfig(confPath string) (*lint.Config, error) {
confFilePath, useDefault, err := getConfigPath(confPath)
// Parse parse given file in confPath, and return Config instance, error if any
func Parse(confPath string) (*lint.Config, error) {
confPath = filepath.Clean(confPath)
confBytes, err := os.ReadFile(confPath)
if err != nil {
return nil, err
}

if useDefault {
return defConf, nil
}

conf, err := Parse(confFilePath)
conf := &lint.Config{}
err = yaml.UnmarshalStrict(confBytes, conf)
if err != nil {
return nil, err
}
Expand All @@ -49,55 +50,18 @@ func GetConfig(confPath string) (*lint.Config, error) {
return conf, nil
}

// getConfigPath returns config file path following below order
// 1. commitlint.yaml in current directory
// 2. confFilePath parameter
// 3. use default config
func getConfigPath(confFilePath string) (confPath string, isDefault bool, retErr error) {
// get current directory
currentDir, err := os.Getwd()
if err != nil {
return "", false, err
}

// check if conf file exists in current directory
currentDirConf := filepath.Join(currentDir, ConfigFile)
if _, err1 := os.Stat(currentDirConf); !os.IsNotExist(err1) {
return currentDirConf, false, nil
}

// if confFilePath empty,
// means no config in current directory or config flag is empty
// use default config
if confFilePath == "" {
return "", true, nil
}
return filepath.Clean(confFilePath), false, nil
}

// Parse parse given file in confPath, and return Config instance, error if any
func Parse(confPath string) (*lint.Config, error) {
confPath = filepath.Clean(confPath)
confBytes, err := os.ReadFile(confPath)
if err != nil {
return nil, err
}

conf := &lint.Config{}
err = yaml.Unmarshal(confBytes, conf)
if err != nil {
return nil, err
}
return conf, nil
}

// Validate validates given config instance, it checks the following
// If formatters, rules are registered/known
// If arguments to rules are valid
// If version is valid and atleast minimum than commitlint version used
func Validate(conf *lint.Config) []error {
var errs []error

err := isValidVersion(conf.MinVersion)
if err != nil {
errs = append(errs, err)
}

if conf.Formatter == "" {
errs = append(errs, errors.New("formatter is empty"))
} else {
Expand All @@ -107,16 +71,10 @@ func Validate(conf *lint.Config) []error {
}
}

err := isValidVersion(conf.MinVersion)
if err != nil {
errs = append(errs, err)
}

for ruleName, r := range conf.Rules {
// Check Severity Level of rule config
switch r.Severity {
case lint.SeverityError:
case lint.SeverityWarn:
case lint.SeverityError, lint.SeverityWarn:
default:
errs = append(errs, fmt.Errorf("unknown severity level '%s' for rule '%s'", r.Severity, ruleName))
}
Expand All @@ -136,6 +94,56 @@ func Validate(conf *lint.Config) []error {
return errs
}

// LookupAndParse gets the config path according to the precedence
// if exists, parses the config file and returns config instance
func LookupAndParse() (*lint.Config, error) {
confFilePath, useDefault, err := lookupConfigPath()
if err != nil {
return nil, err
}

if useDefault {
return defConf, nil
}

conf, err := Parse(confFilePath)
if err != nil {
return nil, err
}
return conf, nil
}

// lookupConfigPath returns config file path following below order
// 1. env path
// 2. commitlint.yaml in current directory
// 3. use default config
func lookupConfigPath() (confPath string, isDefault bool, retErr error) {
envConf := os.Getenv(commitlintConfig)
if envConf != "" {
envConf = filepath.Clean(envConf)
if _, err1 := os.Stat(envConf); !os.IsNotExist(err1) {
return envConf, false, nil
}
}

// get current directory
currentDir, err := os.Getwd()
if err != nil {
return "", false, err
}

// check if conf file exists in current directory
for _, confFile := range configFiles {
currentDirConf := filepath.Join(currentDir, confFile)
if _, err1 := os.Stat(currentDirConf); !os.IsNotExist(err1) {
return currentDirConf, false, nil
}
}

// default config
return "", true, nil
}

// WriteToFile util func to write config object to given file
func WriteToFile(outFilePath string, conf *lint.Config) (retErr error) {
f, err := os.Create(outFilePath)
Expand All @@ -158,6 +166,12 @@ func WriteToFile(outFilePath string, conf *lint.Config) (retErr error) {
}()

enc := yaml.NewEncoder(w)
defer func() {
err := enc.Close()
if retErr == nil && err != nil {
retErr = err
}
}()
return enc.Encode(conf)
}

Expand Down
21 changes: 19 additions & 2 deletions internal/cmd/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"os"
"path/filepath"

"github.com/conventionalcommit/commitlint/config"
"github.com/conventionalcommit/commitlint/lint"
Expand Down Expand Up @@ -30,8 +31,8 @@ func runLint(confFilePath, fileInput string) (lintResult string, hasError bool,
return resStr, hasErrorSeverity(res), nil
}

func getLinter(confFilePath string) (*lint.Linter, lint.Formatter, error) {
conf, err := config.GetConfig(confFilePath)
func getLinter(confParam string) (*lint.Linter, lint.Formatter, error) {
conf, err := getConfig(confParam)
if err != nil {
return nil, nil, err
}
Expand All @@ -49,6 +50,21 @@ func getLinter(confFilePath string) (*lint.Linter, lint.Formatter, error) {
return linter, format, nil
}

func getConfig(confParam string) (*lint.Config, error) {
if confParam != "" {
confParam = filepath.Clean(confParam)
return config.Parse(confParam)
}

// If config param is empty, lookup for defaults
conf, err := config.LookupAndParse()
if err != nil {
return nil, err
}

return conf, nil
}

func getCommitMsg(fileInput string) (string, error) {
commitMsg, err := readStdInPipe()
if err != nil {
Expand All @@ -64,6 +80,7 @@ func getCommitMsg(fileInput string) (string, error) {
fileInput = "./.git/COMMIT_EDITMSG"
}

fileInput = filepath.Clean(fileInput)
inBytes, err := os.ReadFile(fileInput)
if err != nil {
return "", err
Expand Down

0 comments on commit 446e516

Please sign in to comment.