diff --git a/cmd/commands/init.go b/cmd/commands/init.go new file mode 100644 index 0000000..6b18710 --- /dev/null +++ b/cmd/commands/init.go @@ -0,0 +1,61 @@ +package commands + +import ( + "context" + "errors" + "fmt" + "os" + + "github.com/thumbrise/commitlint-scope/pkg/validator" + "github.com/urfave/cli/v3" +) + +var ( + ErrFileExists = errors.New("file already exists") + ErrOpen = errors.New("cannot open file") + ErrWrite = errors.New("cannot write content") +) + +const InitConfigData = `#$schema: https://github.com/thumbrise/commitlint-scope/blob/main/docs/schema/config.json + +# Scope parsing customization. Not required, if you follow common conventional header. In example: 'type!(scope): subject' +#scopeRegex: ^[a-z]+(?:\((?P[^)]+)\))?!?:\s +patterns: + "auth": [ "services/auth/**" ] + "migrations": [ "database/migrations/*.sql" ] + "frontend": [ "**/assets/**", "**/frontend/**" ] + "docs": [ "**/*.md" ] +` + +const ( + InitConfigFileName = validator.ConfigName + ".yml" + InitConfigFileMode = 0o600 +) + +var InitCMD = &cli.Command{ + Name: "init", + Usage: "Initialize a config file", + Action: func(ctx context.Context, cmd *cli.Command) error { + f, err := os.OpenFile(InitConfigFileName, os.O_WRONLY|os.O_CREATE|os.O_EXCL, InitConfigFileMode) + if err != nil { + if errors.Is(err, os.ErrExist) { + return fmt.Errorf("%w: %s", ErrFileExists, InitConfigFileName) + } + + return fmt.Errorf("%w: %w", ErrOpen, err) + } + + defer func() { + if closeErr := f.Close(); closeErr != nil && err == nil { + _, _ = fmt.Fprintf(cmd.ErrWriter, "%s: %s", ErrWrite, closeErr) + } + }() + + _, err = f.WriteString(InitConfigData) + if err != nil { + return fmt.Errorf("%w: %w", ErrWrite, err) + } + + return nil + }, +} diff --git a/cmd/root.go b/cmd/root.go index e5702c8..b6c6ec1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,6 +16,7 @@ var Root = &cli.Command{ Description: `commitlint-scope - a linter that checks if declared commit scopes match the changed files`, Commands: []*cli.Command{ commands.RunCMD, + commands.InitCMD, }, Suggest: true, ExitErrHandler: func(ctx context.Context, command *cli.Command, err error) { diff --git a/pkg/validator/config.go b/pkg/validator/config.go index ef027c0..1cb60b8 100644 --- a/pkg/validator/config.go +++ b/pkg/validator/config.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/viper" ) -const configName = ".commitlint-scope" +const ConfigName = ".commitlint-scope" type Config struct { ScopeRegex *regexp.Regexp `mapstructure:"scopeRegex"` @@ -21,7 +21,7 @@ var ErrConfigRead = errors.New("error reading config") func LoadConfig() (Config, error) { v := viper.New() - v.SetConfigName(configName) + v.SetConfigName(ConfigName) v.AddConfigPath(".") v.SetDefault("scopeRegex", regexp.MustCompile(`^[a-z]+(?:\((?P[^)]+)\))?!?:\s`))