diff --git a/pkg/provider/github.go b/pkg/provider/github.go index ba91620..74c4ee2 100644 --- a/pkg/provider/github.go +++ b/pkg/provider/github.go @@ -1,13 +1,16 @@ package provider import ( + "bytes" "context" "errors" "fmt" + "log" "os" "regexp" "strconv" "strings" + "text/template" "time" "github.com/Masterminds/semver/v3" @@ -17,7 +20,13 @@ import ( "golang.org/x/oauth2" ) +// Taken from https://github.com/Masterminds/semver/blob/master/version.go licence by MIT +const semVerRegex string = `(?Pv?(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:\.(0|[1-9]\d*))?` + + `(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?` + + `(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)` + var PVERSION = "dev" +var DEFAULT_TAG_FORMAT = "{{.Version}}" // no "v" prefix because Version is asumed to have v as a prefix if strip_v_tag_prefix is false type GitHubRepository struct { owner string @@ -25,6 +34,8 @@ type GitHubRepository struct { stripVTagPrefix bool client *github.Client compareCommits bool + tagFormat *template.Template + tagRegex *regexp.Regexp } func (repo *GitHubRepository) Init(config map[string]string) error { @@ -70,7 +81,29 @@ func (repo *GitHubRepository) Init(config map[string]string) error { repo.compareCommits = true } + tagFormat := DEFAULT_TAG_FORMAT + if config["tag_format"] != "" { + tagFormat = config["tag_format"] + } + var err error + repo.tagFormat, err = template.New("tagFormat").Funcs(template.FuncMap{ + "env": func(key string) string { + if strings.HasPrefix(key, "SEMREL_") { + return os.Getenv(key) + } + return "" + }, + }).Parse(tagFormat) + if err != nil { + return fmt.Errorf("failed to parse tag_format: %w", err) + } + + var buf bytes.Buffer + repo.tagFormat.Execute(&buf, map[string]string{"Version": semVerRegex}) + + repo.tagRegex = regexp.MustCompile(buf.String()) + stripVTagPrefix := config["strip_v_tag_prefix"] repo.stripVTagPrefix, err = strconv.ParseBool(stripVTagPrefix) @@ -166,10 +199,18 @@ func (repo *GitHubRepository) GetReleases(rawRe string) ([]*semrel.Release, erro return nil, err } for _, r := range refs { + reformatForSemver := false tag := strings.TrimPrefix(r.GetRef(), "refs/tags/") if rawRe != "" && !re.MatchString(tag) { continue } + + if !repo.tagRegex.MatchString(tag) { + continue + } else { + reformatForSemver = true + } + objType := r.Object.GetType() if objType != "commit" && objType != "tag" { continue @@ -186,6 +227,16 @@ func (repo *GitHubRepository) GetReleases(rawRe string) ([]*semrel.Release, erro } foundSha = resTag.Object.GetSHA() } + + if reformatForSemver { + matches := mustExtractNamedGroups(repo.tagRegex, tag) + tag = matches["version"] + if tag == "" { + log.Printf("Skipping tag %s as it does not have a version or tag format does not contain a named capture group 'version'", tag) + continue + } + } + version, err := semver.NewVersion(tag) if err != nil { continue @@ -207,7 +258,10 @@ func (repo *GitHubRepository) CreateRelease(release *provider.CreateReleaseConfi prefix = "" } - tag := prefix + release.NewVersion + var buf bytes.Buffer + repo.tagFormat.Execute(&buf, map[string]string{"Version": prefix + release.NewVersion}) + + tag := buf.String() isPrerelease := release.Prerelease || semver.MustParse(release.NewVersion).Prerelease() != "" if release.Branch != release.SHA { @@ -240,3 +294,19 @@ func (repo *GitHubRepository) Name() string { func (repo *GitHubRepository) Version() string { return PVERSION } + +// ExtractNamedGroups returns a map of named capture groups +func mustExtractNamedGroups(re *regexp.Regexp, input string) map[string]string { + result := make(map[string]string) + match := re.FindStringSubmatch(input) + if match == nil { + return result + } + + for i, name := range re.SubexpNames() { + if i > 0 && name != "" { // Ignore index 0 (whole match) and unnamed groups + result[name] = match[i] + } + } + return result +}