package gitpb // runs git, parses output // types faster than you can import ( "errors" "path/filepath" "regexp" "strconv" "strings" "unicode" "go.wit.com/log" ) func (repo *Repo) reloadVersions() { repo.setMasterVersion() repo.setDevelVersion() repo.setUserVersion() repo.setLastTag() repo.setCurrentBranchName() repo.setCurrentBranchVersion() } func (repo *Repo) setMasterVersion() { bname := repo.GetMasterBranchName() v, err := repo.gitVersionByName(bname) /* count := repo.LenGitTags() log.Info(repo.GetGoPath(), "tag count", count) repo.UpdateGitTags() count = repo.LenGitTags() log.Info(repo.GetGoPath(), "tag count", count) */ if err == nil { repo.MasterVersion = v } else { log.Log(WARN, "gitpb.GitMasterVersion() error:", err) repo.MasterVersion = "giterr" } } func (repo *Repo) setDevelVersion() { bname := repo.GetDevelBranchName() v, err := repo.gitVersionByName(bname) if err == nil { repo.DevelVersion = v } else { log.Log(WARN, "gitpb.GitDevelVersion() error:", err) repo.DevelVersion = "deverr" } } func (repo *Repo) setUserVersion() { bname := repo.GetUserBranchName() v, err := repo.gitVersionByName(bname) if err == nil { repo.UserVersion = v } else { log.Log(WARN, "gitpb.GitUserVersion() error:", err) repo.UserVersion = "uerr" } } /* now tracked in repo.Reload() func (repo *Repo) GetCurrentBranchName() string { r := repo.RunQuiet([]string{"git", "branch", "--show-current"}) output := strings.Join(r.Stdout, "\n") if r.Error != nil { log.Log(WARN, "GetCurrentBranchName() not in a git repo?", r.Error, repo.GetGoPath()) log.Log(WARN, "GetCurrentBranchName() output might have worked anyway:", output) } return strings.TrimSpace(output) } */ // this is used often. probably move everything to this // returns things like // v0.2.2 // v0.22.39-1-g2141737 // v0.23-dirty // mystuff func (repo *Repo) GetCurrentVersion() string { if repo == nil { return "" } bver := repo.GetCurrentBranchVersion() if repo.CheckDirty() { bver = bver + "-dirty" } return bver } func (repo *Repo) gitDescribeByHash(hash string) (string, error) { if hash == "" { return "", errors.New("hash was blank") } r := repo.RunQuiet([]string{"git", "describe", "--tags", "--always", hash}) out := strings.Join(r.Stdout, "\n") if r.Error != nil { log.Warn("not in a git repo or bad hash?", r.Error, repo.GetGoPath()) return out, r.Error } return out, r.Error } // this should get the most recent tag func (repo *Repo) GetLastTagVersion() string { return repo.LastTag } func (repo *Repo) DebianReleaseVersion() string { lasttag := repo.GetLastTagVersion() newv := trimNonNumericFromStart(lasttag) if newv == "" { newv = "0.0" if lasttag != "" { newv += "-" + lasttag } } return newv } func (repo *Repo) DebianCurrentVersion() string { cbversion := repo.GetCurrentBranchVersion() newv := trimNonNumericFromStart(cbversion) if newv == "" { newv = "0.0" } if repo.CheckDirty() { newv += "-dirty" } return newv } func (repo *Repo) gitVersionByName(name string) (string, error) { name = strings.TrimSpace(name) if name == "" { // git will return the current tag r := repo.RunQuiet([]string{"git", "describe", "--tags", "--always"}) output := strings.Join(r.Stdout, "\n") if r.Error != nil { log.Log(WARN, "gitDescribeByName() output might have worked anyway:", output) log.Log(WARN, "gitDescribeByName() not in a git repo?", r.Error, repo.GetGoPath()) return "", r.Error } return strings.TrimSpace(output), nil } if !repo.IsBranch(name) { // tag does not exist log.Log(WARN, "LocalTagExists()", name, "did not exist") return "", errors.New("gitDescribeByName() git fatal: Not a valid object name: " + name) } cmd := []string{"git", "describe", "--tags", "--always", name} result := repo.RunQuiet(cmd) output := strings.Join(result.Stdout, "\n") if result.Error != nil { log.Log(WARN, "cmd =", cmd) log.Log(WARN, "err =", result.Error) log.Log(WARN, "output (might have worked with error?) =", output) log.Log(WARN, "not in a git repo or bad tag?", repo.GetGoPath()) return "", result.Error } return strings.TrimSpace(output), nil } // find a branch name // will find "master" or "devel" // will also find "v0.1.1" // or will find "patches-from-foo" // will return *any* match on any git branch because it doesn't // matter much here yet // eventually this will be worked out by forge in some future code that hasn't been made yet func (repo *Repo) IsBranch(findname string) bool { loop := repo.Tags.All() for loop.Scan() { t := loop.Next() // log.Info("LocalTagExists() tag:", t.Refname) tagname := t.Refname if strings.HasPrefix(tagname, "refs/remotes") { continue } path, filename := filepath.Split(tagname) log.Log(INFO, "gitpb.IsBranch() tag:", path, filename, "from", repo.GetGoPath()) if filename == findname { log.Log(INFO, "gitpb.IsBranch() found tag:", path, filename, "from", repo.GetGoPath()) return true } } log.Log(INFO, "did not find tag:", findname, "in", repo.GetGoPath()) return false } // todo: redo this and above. both are messed up. ignore for now until things are stable func (repo *Repo) IsLocalBranch(findname string) bool { loop := repo.Tags.All() for loop.Scan() { t := loop.Next() // log.Info("LocalTagExists() tag:", t.Refname) tagname := t.Refname if strings.HasPrefix(tagname, "refs/heads") { continue } path, filename := filepath.Split(tagname) log.Log(INFO, "gitpb.IsBranch() tag:", path, filename, "from", repo.GetGoPath()) if filename == findname { log.Log(INFO, "gitpb.IsBranch() found tag:", path, filename, "from", repo.GetGoPath()) return true } } log.Log(INFO, "did not find tag:", findname, "in", repo.GetGoPath()) return false } func trimNonNumericFromStart(s string) string { for i, r := range s { if unicode.IsDigit(r) { return s[i:] } } return "" } func normalizeVersion(s string) string { // reg, err := regexp.Compile("[^a-zA-Z0-9]+") parts := strings.Split(s, "-") if len(parts) == 0 { return "" } reg, err := regexp.Compile("[^0-9.]+") if err != nil { log.Log(WARN, "normalizeVersion() regexp.Compile() ERROR =", err) return parts[0] } clean := reg.ReplaceAllString(parts[0], "") log.Log(INFO, "normalizeVersion() s =", clean) return clean } // golang doesn't seem to really support v0.1 and seems to want v0.1.0 // TODO: confirm this. (as of Dec 2024, this appears to be the case -- jcarr ) // // personally I hope GO stays with the vX.X.X version scheme. it's a good system. // // if the version is "57", convert it to v0.0.57 for GO func splitVersion(version string) (a, b, c string) { tmp := normalizeVersion(version) parts := strings.Split(tmp, ".") switch len(parts) { case 1: return "", "", parts[0] // converts someone using version "57" to "v0.0.57" case 2: return parts[0], parts[1], "" // converts someone using version "1.2" to "v1.2.0" default: return parts[0], parts[1], parts[2] } } func splitInts(ver string) (int, int, int) { major, minor, revision := splitVersion(ver) a, _ := strconv.Atoi(major) b, _ := strconv.Atoi(minor) c, _ := strconv.Atoi(revision) return a, b, c } // changes the target minor. v0.1.3 becomes v0.2.0 func (repo *Repo) IncrementTargetMinor() { lasttag := repo.GetLastTag() // var major, minor, revision string major, minor, revision := splitInts(lasttag) minor += 1 revision = 0 newa := strconv.Itoa(major) newb := strconv.Itoa(minor) newc := strconv.Itoa(revision) repo.SetTargetVersion("v" + newa + "." + newb + "." + newc) } // changes the target revision. v0.1.3 becomes v0.1.4 func (repo *Repo) IncrementTargetRevision() bool { // first try just going from the last tag repo.incrementRevision(repo.GetLastTag()) if !isNewerVersion(repo.GetMasterVersion(), repo.GetTargetVersion()) { log.Printf("master version() %s is higher than target version %s\n", repo.GetMasterVersion(), repo.GetTargetVersion()) repo.incrementRevision(repo.GetMasterVersion()) } if !isNewerVersion(repo.GetLastTag(), repo.GetTargetVersion()) { log.Printf("last tag versn() %s is higher than target version %s\n", repo.GetLastTag(), repo.GetTargetVersion()) return false } if !isNewerVersion(repo.GetMasterVersion(), repo.GetTargetVersion()) { log.Printf("master version() %s is higher than target version %s\n", repo.GetMasterVersion(), repo.GetTargetVersion()) return false } return true } func (repo *Repo) incrementRevision(lasttag string) { major, minor, revision := splitInts(lasttag) revision += 1 newa := strconv.Itoa(major) newb := strconv.Itoa(minor) newc := strconv.Itoa(revision) repo.SetTargetVersion("v" + newa + "." + newb + "." + newc) } // makes sure the new target version to be released is greater // than the current master version // this is just a sanity check, but this can actually fail sometimes // if other things failed terribly in prior cases // gitpb v.3.1.4 // A = major = 3 // B = minor = 1 // C = revision = 4 func isNewerVersion(oldver, newver string) bool { olda, oldb, oldc := splitInts(oldver) newa, newb, newc := splitInts(newver) if newa < olda { return false } if newb < oldb { return false } if newc <= oldc { return false } return true }