diff --git a/README.md b/README.md index 45c34a6..b2def29 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,38 @@ The `version` and `commit` fields are managed by Gommitizen. The `version_files` `version_files` is a list of strings. Each string contains the path of the file and the name of the variable that contains the version. The path and the name of the variable are separated by a colon (`:`). The path is relative to the root of the project. Tha name of the variable can be replace by a regular expression to find the version in the file (remember to scape the special characters and group the version part of the expression with parentheses like in the example). +### Hooks + +Example: + +```json +{ + "version": "0.18.1", + "commit": "72929b90547b8527e22e402b6784e0c7f5812428", + "version_files": [ + "Chart.yaml:version", + "other-version.txt:version", + "a-file-that-need-a-regex.txt:^version=([0-9]+\\.[0-9]+\\.[0-9]+)$" + ], + "prefix": "my-prj", + "hooks": { + "pre-bump": "echo 'pre-bump hook'", + "post-bump": "echo 'post-bump hook'", + "post-changelog": "echo 'post-changelog hook'", + "pre-changelog": "echo 'pre-changelog hook'" + } +} +``` + +There are four hooks available: + +- `pre-bump`: Runs before the bump process. +- `post-bump`: Runs after the bump process. +- `pre-changelog`: Runs before the changelog generation. +- `post-changelog`: Runs after the changelog generation. + +The hooks are shell commands that are executed in the root of the project. These are all optional fields. + ## Development To run the project in development mode, run: diff --git a/internal/cmd/bump.go b/internal/cmd/bump.go index 0b0e9ad..e8e7605 100644 --- a/internal/cmd/bump.go +++ b/internal/cmd/bump.go @@ -108,6 +108,12 @@ func bumpByConfig(configVersionPath string, createChangelog bool, incrementType // If the file has been modified, update the version if incrementType != "none" { + // Running pre-bump scripts + err = config.RunHook("pre-bump") + if err != nil { + return []string{}, "", fmt.Errorf("pre bump scripts: %s", err) + } + newVersion, newVersionStr, err := bumpmanager.IncrementVersion(config.Version, incrementType) if err != nil { return []string{}, "", fmt.Errorf("increment version: %s", err) @@ -125,12 +131,31 @@ func bumpByConfig(configVersionPath string, createChangelog bool, incrementType return []string{}, "", fmt.Errorf("update version: %s", err) } + // Running post-bump scripts + err = config.RunHook("post-bump") + if err != nil { + return []string{}, "", fmt.Errorf("post bump scripts: %s", err) + } + if createChangelog { + // Running pre-changelog scripts + err = config.RunHook("pre-changelog") + if err != nil { + return []string{}, "", fmt.Errorf("pre changelog scripts: %s", err) + } + + slog.Info("Generating changelog...") changelogFilePath, err := changelog.Apply(config.GetDirPath(), config.Version, cvCommits) if err != nil { return []string{}, "", fmt.Errorf("update changelog: %s", err) } modifiedFiles = append(modifiedFiles, changelogFilePath) + + // Running post-changelog scripts + err = config.RunHook("post-changelog") + if err != nil { + return []string{}, "", fmt.Errorf("post changelog scripts: %s", err) + } } slog.Info("Commit messages:") diff --git a/internal/config/output.go b/internal/config/output.go index f5b6837..f021823 100644 --- a/internal/config/output.go +++ b/internal/config/output.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "gopkg.in/yaml.v3" ) @@ -72,38 +73,28 @@ func configVersionFilter(configVersions []*ConfigVersion, fields []string, outpu cvw := ConfigVersionWrapper{ DirPath: configVersion.GetDirPath(), FilePath: configVersion.GetFilePath(), - ConfigVersion: make(map[string]interface{}, 0), + ConfigVersion: make(map[string]interface{}), } val := reflect.ValueOf(configVersion).Elem() typ := val.Type() - if len(fields) > 0 { - for _, field := range fields { - xField, ok := typ.FieldByName(field) - if !ok { - continue // Si el campo no existe, pasamos al siguiente - } - xValue := val.FieldByName(field) - if xValue.IsValid() && xValue.CanInterface() { - xTag := xField.Tag.Get(outputFormat) - if xTag == "" { - xTag = field - } - cvw.ConfigVersion[xTag] = xValue.Interface() - } + if len(fields) == 0 { + fields = getAllFieldNames(typ) + } + + for _, field := range fields { + xField, ok := typ.FieldByName(field) + if !ok { + continue } - } else { - for i := 0; i < val.NumField(); i++ { - xField := typ.Field(i) - xValue := val.Field(i) - if xValue.IsValid() && xValue.CanInterface() { - xTag := xField.Tag.Get(outputFormat) - if xTag == "" { - xTag = xField.Name - } - cvw.ConfigVersion[xTag] = xValue.Interface() + xValue := val.FieldByName(field) + if xValue.IsValid() && xValue.CanInterface() { + xTag := strings.Split(xField.Tag.Get(outputFormat), ",")[0] + if xTag == "" { + xTag = field } + cvw.ConfigVersion[xTag] = xValue.Interface() } } @@ -112,3 +103,11 @@ func configVersionFilter(configVersions []*ConfigVersion, fields []string, outpu return wrapper } + +func getAllFieldNames(typ reflect.Type) []string { + var fields []string + for i := 0; i < typ.NumField(); i++ { + fields = append(fields, typ.Field(i).Name) + } + return fields +} diff --git a/internal/config/version.go b/internal/config/version.go index 3f1ad7e..d7a4df1 100644 --- a/internal/config/version.go +++ b/internal/config/version.go @@ -6,6 +6,7 @@ import ( "fmt" "log/slog" "os" + "os/exec" "path/filepath" "regexp" "strings" @@ -14,10 +15,11 @@ import ( type ConfigVersion struct { dirPath string - Version string `json:"version" yaml:"version" plain:"version"` - Commit string `json:"commit" yaml:"commit" plain:"commit"` - VersionFiles []string `json:"version_files" yaml:"version_files" plain:"version_files"` - TagPrefix string `json:"tag_prefix" yaml:"tag_prefix" plain:"tag_prefix"` + Version string `json:"version" yaml:"version" plain:"version"` + Commit string `json:"commit" yaml:"commit" plain:"commit"` + VersionFiles []string `json:"version_files" yaml:"version_files" plain:"version_files"` + TagPrefix string `json:"tag_prefix" yaml:"tag_prefix" plain:"tag_prefix"` + Hooks map[string]string `json:"hooks,omitempty" yaml:"hooks,omitempty"` } func NewConfigVersion(dirPath string, version string, commit string, tagPrefix string) *ConfigVersion { @@ -81,6 +83,24 @@ func (v ConfigVersion) GetTagVersion() string { return v.Version } +func (v *ConfigVersion) RunHook(hookName string) error { + hook, ok := v.Hooks[hookName] + if !ok { + slog.Debug(fmt.Sprintf("hook %s not found", hookName)) + return nil + } + + output, err := exec.Command("bash", "-c", hook).CombinedOutput() + if err != nil { + return fmt.Errorf("run hook %s: %v", hookName, err) + } + + // TODO: Pretty log info with colors + slog.Info(fmt.Sprintf("\n\033[32mHook %s output:\n%s\033[0m", hookName, string(output))) + + return nil +} + func (v *ConfigVersion) UpdateVersion(newVersion string, lastCommit string) ([]string, error) { modifiedFiles := make([]string, 0)