diff --git a/checkout.go b/checkout.go new file mode 100644 index 0000000..0b88250 --- /dev/null +++ b/checkout.go @@ -0,0 +1,63 @@ +package gitpb + +import "go.wit.com/log" + +func (repo *Repo) CheckoutMaster() bool { + bName := repo.GetMasterBranchName() + if repo.checkoutBranch(bName) { + return true + } + return false +} + +func (repo *Repo) CheckoutDevel() bool { + bName := repo.GetDevelBranchName() + if repo.checkoutBranch(bName) { + repo.UserBranchName = bName + return true + // switch ok + } + return false +} + +func (repo *Repo) CheckoutUser() bool { + bName := repo.GetUserBranchName() + if repo.checkoutBranch(bName) { + repo.UserBranchName = bName + return true + } + return false +} + +func (repo *Repo) BranchExists(bName string) bool { + // fixme after move to protobuf + return true +} + +func (repo *Repo) checkoutBranch(bName string) bool { + if !repo.BranchExists(bName) { + return false + } + if bName == "" { + return false + } + if repo.CheckDirty() { + log.Log(GITPB, repo.GetFullPath(), "is dirty") + return false + } + cmd := []string{"git", "checkout", bName} + r := repo.Run(cmd) + if r.Error != nil { + log.Log(GITPB, "git checkout error:", r.Error) + } + + realname := repo.GetCurrentBranchName() + realversion := repo.GetCurrentBranchVersion() + log.Log(GITPB, repo.GetFullPath(), "realname =", realname, "realversion =", realversion) + + if realname != bName { + log.Log(GITPB, "git checkout failed", repo.GetFullPath(), bName, "!=", realname) + return false + } + return true +} diff --git a/currentVersions.go b/currentVersions.go index d0a9c3f..4e9d843 100644 --- a/currentVersions.go +++ b/currentVersions.go @@ -6,9 +6,7 @@ package gitpb import ( "errors" "path/filepath" - "sort" "strings" - "time" "unicode" "go.wit.com/log" @@ -215,29 +213,3 @@ func trimNonNumericFromStart(s string) string { } return "" } - -func (all *GitTags) SortByAge() *GitTagIterator { - packs := all.selectAllGitTag() - - sort.Sort(GitTagAge(packs)) - - iterator := NewGitTagIterator(packs) - return iterator -} - -type GitTagAge []*GitTag - -func (a GitTagAge) Len() int { return len(a) } - -// sorts in ? order -func (a GitTagAge) Less(i, j int) bool { - if time.Since(a[i].Authordate.AsTime()) < time.Since(a[j].Authordate.AsTime()) { - return true - } - return false -} -func (a GitTagAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -func (repo *Repo) SetTargetVersion(target string) { - repo.TargetVersion = target -} diff --git a/goDep.isPrimitive.go b/goDep.isPrimitive.go new file mode 100644 index 0000000..a894ecd --- /dev/null +++ b/goDep.isPrimitive.go @@ -0,0 +1,48 @@ +package gitpb + +// only reads in the go.mod file. doesn't change anything + +import ( + "bufio" + "errors" + "os" + "path/filepath" + "strings" + + "go.wit.com/log" +) + +// Detect a 'Primative' package. Sets the isPrimative flag +// will return true if the repo is truly not dependent on _anything_ else +// like spew or lib/widget +// it assumes go mod ran init and tidy ran without error +func (repo *Repo) isPrimativeGoMod() (bool, error) { + // go mod init & go mod tidy ran without errors + log.Log(GITPB, "isPrimativeGoMod()", repo.FullPath) + tmp := filepath.Join(repo.FullPath, "go.mod") + gomod, err := os.Open(tmp) + if err != nil { + log.Log(GITPB, "missing go.mod", repo.FullPath) + return false, err + } + defer gomod.Close() + + scanner := bufio.NewScanner(gomod) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + parts := strings.Split(line, " ") + log.Log(GITPB, " gomod:", parts) + if len(parts) >= 1 { + log.Log(GITPB, " gomod: part[0] =", parts[0]) + if parts[0] == "require" { + log.Log(GITPB, " should return false here") + return false, errors.New("go.mod file is not primative") + } + + } + } + repo.GoPrimitive = true + repo.GoDeps = nil + return true, nil +} diff --git a/goDep.parseGoSum.go b/goDep.parseGoSum.go new file mode 100644 index 0000000..69e4826 --- /dev/null +++ b/goDep.parseGoSum.go @@ -0,0 +1,103 @@ +package gitpb + +// does processing on the go.mod and go.sum files + +import ( + "bufio" + "errors" + "os" + "path/filepath" + "strings" + + "go.wit.com/log" +) + +// reads and parses the go.sum file +// does not change anything +func (repo *Repo) ParseGoSum() (bool, error) { + // empty out what was there before + repo.GoDeps = nil + tmp := filepath.Join(repo.FullPath, "go.sum") + gosum, err := os.Open(tmp) + if err != nil { + log.Warn("missing go.sum", repo.FullPath) + return false, err + } + defer gosum.Close() + + scanner := bufio.NewScanner(gosum) + log.Info("gosum:", tmp) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + parts := strings.Split(line, " ") + if len(parts) == 3 { + godep := strings.TrimSpace(parts[0]) + version := strings.TrimSpace(parts[1]) + if strings.HasSuffix(version, "/go.mod") { + version = strings.TrimSuffix(version, "/go.mod") + } + new1 := GoDep{ + GoPath: godep, + Version: version, + } + if repo.GoDeps == nil { + repo.GoDeps = new(GoDeps) + } + repo.GoDeps.AppendUniqueGoPath(&new1) + } else { + return false, errors.New("go.sum parse error invalid: " + line) + } + } + + if err := scanner.Err(); err != nil { + repo.GoDeps = nil + return false, err + } + return true, nil +} + +// reads and parses the go.sum file +// is identical to the one above, change that +func (repo *Repo) UpdatePublished() (bool, error) { + // empty out what was there before + repo.Published = nil + tmp := filepath.Join(repo.FullPath, "go.sum") + gosum, err := os.Open(tmp) + if err != nil { + log.Warn("missing go.sum", repo.FullPath) + return false, err + } + defer gosum.Close() + + scanner := bufio.NewScanner(gosum) + log.Info("gosum:", tmp) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + parts := strings.Split(line, " ") + if len(parts) == 3 { + godep := strings.TrimSpace(parts[0]) + version := strings.TrimSpace(parts[1]) + if strings.HasSuffix(version, "/go.mod") { + version = strings.TrimSuffix(version, "/go.mod") + } + new1 := GoDep{ + GoPath: godep, + Version: version, + } + if repo.Published == nil { + repo.Published = new(GoDeps) + } + repo.Published.AppendUniqueGoPath(&new1) + } else { + return false, errors.New("go.sum parse error invalid: " + line) + } + } + + if err := scanner.Err(); err != nil { + repo.Published = nil + return false, err + } + return true, nil +} diff --git a/goDep.redoGoMod.go b/goDep.redoGoMod.go index 1313039..de689fd 100644 --- a/goDep.redoGoMod.go +++ b/goDep.redoGoMod.go @@ -3,10 +3,8 @@ package gitpb // does processing on the go.mod and go.sum files import ( - "bufio" "errors" "os" - "path/filepath" "strings" "go.wit.com/log" @@ -60,50 +58,6 @@ func (repo *Repo) RedoGoMod() (bool, error) { return false, errors.New("MakeRedomod() logic failed") } -// reads and parses the go.sum file -// does not change anything -func (repo *Repo) ParseGoSum() (bool, error) { - // empty out what was there before - repo.GoDeps = nil - tmp := filepath.Join(repo.FullPath, "go.sum") - gosum, err := os.Open(tmp) - if err != nil { - log.Warn("missing go.sum", repo.FullPath) - return false, err - } - defer gosum.Close() - - scanner := bufio.NewScanner(gosum) - log.Info("gosum:", tmp) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - - parts := strings.Split(line, " ") - if len(parts) == 3 { - godep := strings.TrimSpace(parts[0]) - version := strings.TrimSpace(parts[1]) - if strings.HasSuffix(version, "/go.mod") { - version = strings.TrimSuffix(version, "/go.mod") - } - new1 := GoDep{ - GoPath: godep, - Version: version, - } - if repo.GoDeps == nil { - repo.GoDeps = new(GoDeps) - } - repo.GoDeps.AppendUniqueGoPath(&new1) - } else { - return false, errors.New("go.sum parse error invalid: " + line) - } - } - - if err := scanner.Err(); err != nil { - repo.GoDeps = nil - return false, err - } - return true, nil -} func (repo *Repo) RepoType() string { if repo == nil { return "nil" @@ -139,50 +93,6 @@ func (repo *Repo) goListRepoType() string { return output } -// reads and parses the go.sum file -func (repo *Repo) UpdatePublished() (bool, error) { - // empty out what was there before - repo.Published = nil - tmp := filepath.Join(repo.FullPath, "go.sum") - gosum, err := os.Open(tmp) - if err != nil { - log.Warn("missing go.sum", repo.FullPath) - return false, err - } - defer gosum.Close() - - scanner := bufio.NewScanner(gosum) - log.Info("gosum:", tmp) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - - parts := strings.Split(line, " ") - if len(parts) == 3 { - godep := strings.TrimSpace(parts[0]) - version := strings.TrimSpace(parts[1]) - if strings.HasSuffix(version, "/go.mod") { - version = strings.TrimSuffix(version, "/go.mod") - } - new1 := GoDep{ - GoPath: godep, - Version: version, - } - if repo.Published == nil { - repo.Published = new(GoDeps) - } - repo.Published.AppendUniqueGoPath(&new1) - } else { - return false, errors.New("go.sum parse error invalid: " + line) - } - } - - if err := scanner.Err(); err != nil { - repo.Published = nil - return false, err - } - return true, nil -} - // returns true if the last published func (repo *Repo) GoDepsLen() int { if repo.GoDeps == nil { diff --git a/makePatches.go b/makePatches.go new file mode 100644 index 0000000..a94b5b1 --- /dev/null +++ b/makePatches.go @@ -0,0 +1,112 @@ +package gitpb + +import ( + "path/filepath" + "strings" + + "go.wit.com/lib/gui/shell" + "go.wit.com/log" +) + +type Patch struct { + GoPath string + Ref string + giturl string + comment string +} + +// move all this to repolist and gowit repos + +func (repo *Repo) GetPatches(oldname string, newname string) (int, []*Patch) { + var patchcount int + patches := make([]*Patch, 0, 0) + + if oldname == newname { + return 0, nil + } + // log.Info("repo userv, develv", userv, develv) + gitcmd := []string{"git", "log", "--oneline", oldname + ".." + newname} + log.Info("Run:", gitcmd) + r := repo.Run(gitcmd) + if r.Error != nil { + log.Info("git failed ", repo.GetGoPath(), "err =", r.Error) + return 0, nil + } + + // patches = strings.Split(output, "\n") + log.Info("Run:", r.Stdout) + for _, line := range r.Stdout { + line = strings.TrimSpace(line) + if line == "" { + continue + } + parts := strings.Split(line, " ") + newp := new(Patch) + newp.Ref = parts[0] + newp.comment = strings.Join(parts[1:], " ") + log.Info("Patch line:", line, repo.GetGoPath()) + patchcount += 1 + patches = append(patches, newp) + } + return patchcount, patches +} + +func (repo *Repo) GetUserPatches() (int, []*Patch) { + usern := repo.GetUserBranchName() + develn := repo.GetDevelBranchName() + userv := repo.GetUserVersion() + develv := repo.GetDevelVersion() + + if userv == develv { + return 0, nil + } + + c, all := repo.GetPatches(develn, usern) + log.Info("GetPatches() guireleaser", develn, usern, "count =", c) + return c, all +} + +func (repo *Repo) GetMasterPatches() (int, []*Patch) { + lasttag := repo.GetLastTag() + mastern := repo.GetMasterBranchName() + masterv := repo.GetMasterVersion() + + if lasttag == masterv { + return 0, nil + } + + c, all := repo.GetPatches(lasttag, mastern) + log.Info("GetPatches() guireleaser", lasttag, mastern, "count =", c) + return c, all +} + +func (all *Repos) MakePatchset(setdir string) bool { + loop := all.SortByGoPath() + for loop.Scan() { + repo := loop.Next() + log.Info("repo", repo.GetGoPath()) + userv := repo.GetUserVersion() + develv := repo.GetDevelVersion() + usern := repo.GetUserBranchName() + develn := repo.GetDevelBranchName() + if userv == develv { + // this repo is unchanged + continue + } + + repodir := filepath.Join(setdir, repo.GetGoPath()) + shell.Mkdir(repodir) + // git format-patch branch1..branch2 + gitcmd := []string{"git", "format-patch", "-o", repodir, develn + ".." + usern} + log.Info("Run:", gitcmd) + r := repo.Run(gitcmd) + log.Info("output =", r.Stdout) + if r.Error == nil { + log.Info("patches made okay for:", repo.GetGoPath()) + continue + } + log.Info("patches failed for:", repo.GetGoPath()) + return false + } + return true +} diff --git a/repo.new.go b/repo.new.go index dc7f17e..9404d7b 100644 --- a/repo.new.go +++ b/repo.new.go @@ -1,11 +1,9 @@ package gitpb import ( - "bufio" "errors" "os" "path/filepath" - "strings" "go.wit.com/log" ) @@ -15,16 +13,21 @@ import ( // TODO: try adding python, rails, perl, rust, other language things? // I probably will never have time to try that, but I'd take patches for anyone // that might see this note and feel so inclined. -func (all *Repos) NewGoPath(basepath string, gopath string) (*Repo, error) { +func (all *Repos) NewGoPath(basepath string, gopath string, url string) (*Repo, error) { + if gopath == "" { + return nil, errors.New("blank gopath") + } if r := all.FindByGoPath(gopath); r != nil { // already had this gopath - return r, nil + return nil, errors.New("duplicate gopath " + gopath) } + log.Info("gitpb.NewGoPath() Attempting to add new path", basepath, gopath) // if .git doesn't exist, error out here gitpath := filepath.Join(basepath, gopath, ".git") _, err := os.Stat(gitpath) if err != nil { + log.Warn("gitpb.NewGoPath() not a git directory", gitpath) return nil, err } @@ -32,6 +35,7 @@ func (all *Repos) NewGoPath(basepath string, gopath string) (*Repo, error) { newr := Repo{ FullPath: filepath.Join(basepath, gopath), GoPath: gopath, + URL: url, } newr.Tags = new(GitTags) // newr.UpdateGit() @@ -40,53 +44,21 @@ func (all *Repos) NewGoPath(basepath string, gopath string) (*Repo, error) { // newr.RedoGoMod() switch newr.goListRepoType() { + case "plugin": + newr.GoPlugin = true + case "protobuf": + newr.GoProtobuf = true case "library": newr.GoLibrary = true case "binary": newr.GoBinary = true } - all.AppendUniqueGoPath(&newr) - return &newr, nil -} - -// Detect a 'Primative' package. Sets the isPrimative flag -// will return true if the repo is truly not dependent on _anything_ else -// like spew or lib/widget -// it assumes go mod ran init and tidy ran without error -func (repo *Repo) isPrimativeGoMod() (bool, error) { - // go mod init & go mod tidy ran without errors - log.Log(GITPB, "isPrimativeGoMod()", repo.FullPath) - tmp := filepath.Join(repo.FullPath, "go.mod") - gomod, err := os.Open(tmp) - if err != nil { - log.Log(GITPB, "missing go.mod", repo.FullPath) - repo.GoDeps = nil - return false, err + if all.AppendUniqueGoPath(&newr) { + // worked + return &newr, nil } - defer gomod.Close() - - scanner := bufio.NewScanner(gomod) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - - parts := strings.Split(line, " ") - log.Log(GITPB, " gomod:", parts) - if len(parts) >= 1 { - log.Log(GITPB, " gomod: part[0] =", parts[0]) - if parts[0] == "require" { - log.Log(GITPB, " should return false here") - return false, errors.New("go.mod file is not primative") - } - - } - } - repo.GoPrimitive = true - return true, nil -} - -func (repo *Repo) SetMasterBranchName(bname string) { - repo.MasterBranchName = bname + return nil, errors.New("repo already exists: " + gopath) } func (repo *Repo) SetDevelBranchName(bname string) { diff --git a/repo.proto b/repo.proto index ec3b6fb..bd6e269 100644 --- a/repo.proto +++ b/repo.proto @@ -12,22 +12,25 @@ import "google/protobuf/timestamp.proto"; // Import the well-known type for Time // global settings for autogenpb `autogenpb:mutex` message Repo { // `autogenpb:marshal` - string fullPath = 1; // the actual path to the .git directory: '/home/devel/golang.org/x/tools' - google.protobuf.Timestamp lastPull = 2; // last time a git pull was done - string masterBranchName = 3; // git 'main' or 'master' branch name - string develBranchName = 4; // whatever the git 'devel' branch name is - string userBranchName = 5; // whatever your username branch is - GitTags tags = 6; // known tags - string goPath = 7; // `autogenpb:unique` // the logical path as used by golang: 'go.wit.com/apps/helloworld' - bool goLibrary = 8; // is this a golang library? - bool goBinary = 9; // is this a golang binary? - bool goPrimitive = 10; // if this is a golang primitive (only has go.mod) - bool goPlugin = 11; // is this a golang plugin? - GoDeps goDeps = 12; // what is in the go.sum file - google.protobuf.Timestamp lastGoDep = 13; // last time go.sum was processed - bool dirty = 14; // if git says things have been changed - GoDeps published = 15; // the last published go.mod/go.sum - string targetVersion = 16; // useful during the package release process + string fullPath = 1; // the actual path to the .git directory: '/home/devel/golang.org/x/tools' + google.protobuf.Timestamp lastPull = 2; // last time a git pull was done + string masterBranchName = 3; // git 'main' or 'master' branch name + string develBranchName = 4; // whatever the git 'devel' branch name is + string userBranchName = 5; // whatever your username branch is + GitTags tags = 6; // known tags + string goPath = 7; // `autogenpb:unique` // the logical path as used by golang: 'go.wit.com/apps/helloworld' + bool goLibrary = 8; // is this a golang library? + bool goBinary = 9; // is this a golang binary? + bool goPrimitive = 10; // if this is a golang primitive (only has go.mod) + bool goPlugin = 11; // is this a golang plugin? + GoDeps goDeps = 12; // what is in the go.sum file + google.protobuf.Timestamp lastGoDep = 13; // last time go.sum was processed + bool dirty = 14; // if git says things have been changed + GoDeps published = 15; // the last published go.mod/go.sum + string targetVersion = 16; // useful during the package release process + bool readOnly = 17; // tracks access to 'git push' + string URL = 18; // the URL. amazingly I didn't add this earlier. duh. + bool goProtobuf = 19; // autogen go files from .proto } message Repos { // `autogenpb:marshal` diff --git a/rill.go b/rill.go new file mode 100644 index 0000000..f1b6b37 --- /dev/null +++ b/rill.go @@ -0,0 +1,96 @@ +package gitpb + +// runs git, parses output +// types faster than you can + +import ( + "errors" + "sort" + "strings" + "time" + + "go.wit.com/log" +) + +func (all *GitTags) SortByAge() *GitTagIterator { + packs := all.selectAllGitTag() + + sort.Sort(GitTagAge(packs)) + + iterator := NewGitTagIterator(packs) + return iterator +} + +type GitTagAge []*GitTag + +func (a GitTagAge) Len() int { return len(a) } + +// sorts in ? order +func (a GitTagAge) Less(i, j int) bool { + if time.Since(a[i].Authordate.AsTime()) < time.Since(a[j].Authordate.AsTime()) { + return true + } + return false +} +func (a GitTagAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// rill is awesome. long live rill +// attempt scan with rill +func (all *Repos) rillGitPull() error { + loop := all.SortByGoPath() + for loop.Scan() { + t := loop.Next() + log.Info("repo", t.GoPath) + } + /* + packs := all.selectAllRepo() + // Convert a slice of user IDs into a channel + ids := rill.FromSlice(packs, nil) + + // Read users from the API. + // Concurrency = 20 + dirs := rill.Map(ids, 20, func(id string) (*Repo, error) { + return packs[id], nil + }) + + // Activate users. + // Concurrency = 10 + err := rill.ForEach(dirs, 10, func(repo *Repo) error { + // could do something here + // fmt.Printf("Repo found : %s\n", repo.GoPath) + // repo.Run([]string{"git", "pull"}) + return nil + }) + */ + return nil +} + +var ErrorMissingGitConfig error = errors.New("missing .git/config") +var ErrorGitPullOnLocal error = errors.New("git pull on local only branch") + +// checks to see if you can do a 'git pull' +func (repo *Repo) IsOnlyLocalTag(currentName string) bool { + log.Warn("IsOnlyLocalTag(currentName string) not re-implemented yet") + return false +} + +func (repo *Repo) GitPull() (string, error) { + currentName := repo.GetCurrentBranchName() + if repo.IsOnlyLocalTag(currentName) { + return "", ErrorGitPullOnLocal + } + var cmd []string + cmd = append(cmd, "git", "pull") + r := repo.Run(cmd) + output := strings.Join(r.Stdout, "\n") + if r.Error != nil { + output = "git error_,,,_a_,,,_b_,,,c" + } + if r.Error == nil { + log.Log(GITPBWARN, "git pull ran", repo.GetGoPath()) + log.Log(GITPBWARN, "git pull output", output) + } else { + log.Log(GITPBWARN, "git pull error", repo.GetGoPath(), r.Error) + } + return output, r.Error +} diff --git a/set.go b/set.go new file mode 100644 index 0000000..8821e0d --- /dev/null +++ b/set.go @@ -0,0 +1,13 @@ +package gitpb + +func (repo *Repo) SetReadOnly(b bool) { + repo.ReadOnly = b +} + +func (repo *Repo) SetTargetVersion(target string) { + repo.TargetVersion = target +} + +func (repo *Repo) SetMasterBranchName(s string) { + repo.MasterBranchName = s +}