summaryrefslogtreecommitdiff
path: root/windowPatchsets.go
diff options
context:
space:
mode:
authorJeff Carr <[email protected]>2025-03-23 08:51:06 -0500
committerJeff Carr <[email protected]>2025-03-23 08:51:06 -0500
commitedc362f4b981ed466c8d9038c42e9bda8a5dbe41 (patch)
treeafd9e1e37c205481311757c4b5ffd21f3403125e /windowPatchsets.go
parent033e81bb224fc1369ac26cc4e5c38bf261b86b46 (diff)
rename
Diffstat (limited to 'windowPatchsets.go')
-rw-r--r--windowPatchsets.go384
1 files changed, 384 insertions, 0 deletions
diff --git a/windowPatchsets.go b/windowPatchsets.go
new file mode 100644
index 0000000..36ee7ba
--- /dev/null
+++ b/windowPatchsets.go
@@ -0,0 +1,384 @@
+// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
+// Use of this source code is governed by the GPL 3.0
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "os/exec"
+ "regexp"
+ "strings"
+ "sync"
+
+ "go.wit.com/gui"
+ "go.wit.com/lib/gadgets"
+ "go.wit.com/lib/protobuf/forgepb"
+ "go.wit.com/log"
+)
+
+type stdPatchsetTableWin struct {
+ sync.Mutex
+ win *gadgets.GenericWindow // the machines gui window
+ box *gui.Node // the machines gui parent box widget
+ TB *forgepb.PatchsetsTable // the gui table buffer
+ update bool // if the window should be updated
+}
+
+func (w *stdPatchsetTableWin) Toggle() {
+ if w == nil {
+ return
+ }
+ if w.win == nil {
+ return
+ }
+ w.win.Toggle()
+}
+
+func makePatchsetsWin() *stdPatchsetTableWin {
+ dwin := new(stdPatchsetTableWin)
+ dwin.win = gadgets.NewGenericWindow("forge current patchsets", "patchset options")
+ dwin.win.Custom = func() {
+ log.Info("test delete window here")
+ }
+ grid := dwin.win.Group.RawGrid()
+
+ grid.NewButton("ondisk", func() {
+ openPatchsets()
+ if me.psets == nil {
+ log.Info("No Patchsets loaded")
+ return
+ }
+ dwin.doPatchsetsTable(me.psets)
+ })
+
+ grid.NewButton("upstream", func() {
+ psets, err := me.forge.GetPatchesets()
+ if err != nil {
+ log.Info("Get Patchsets failed", err)
+ return
+ }
+
+ var foundnew bool
+ all := psets.All()
+ for all.Scan() {
+ pset := all.Next()
+ found := me.psets.FindByUuid(pset.Uuid)
+ if found == nil {
+ log.Info("new patchset", pset.Name, pset.Uuid)
+ pset.State = "new"
+ foundnew = true
+ } else {
+ log.Info("patchset already on disk", found.Name, found.State)
+ pset.State = found.State
+ if pset.State == "" {
+ pset.State = "new"
+ }
+ }
+ }
+ dwin.doPatchsetsTable(psets)
+ if foundnew {
+ log.Info("should save these here")
+ me.psets = psets
+ savePatchsets()
+ }
+ })
+
+ grid.NewButton("save", func() {
+ if me.psets == nil {
+ log.Info("No Patchsets loaded")
+ return
+ }
+ savePatchsets()
+ })
+
+ grid.NewButton("set patchset state", func() {
+ if me.psets == nil {
+ log.Info("No Patchsets loaded")
+ return
+ }
+ all := me.psets.All()
+ for all.Scan() {
+ pset := all.Next()
+ if pset.State == "" {
+ log.Info("What is up with?", pset.Name)
+ setPatchsetState(pset)
+ } else {
+ log.Info("patchset already had state", pset.Name, pset.State)
+ }
+ }
+ savePatchsets()
+ })
+
+ grid.NewButton("find commit hashes", func() {
+ if me.psets == nil {
+ log.Info("No Patchsets loaded")
+ return
+ }
+ all := me.psets.All()
+ for all.Scan() {
+ pset := all.Next()
+ if pset.State != "new" {
+ log.Info("patchset already had state", pset.Name, pset.State)
+ continue
+ }
+ if setNewCommitHash(pset) {
+ // everything in this patchset is applied
+ pset.State = "APPLIED"
+ }
+ }
+ savePatchsets()
+ })
+
+ grid.NewButton("show pending patches", func() {
+ if me.psets == nil {
+ log.Info("No Patchsets loaded")
+ return
+ }
+ notdone := new(forgepb.Patches)
+
+ all := me.psets.All()
+ for all.Scan() {
+ pset := all.Next()
+ AddNotDonePatches(notdone, pset)
+ }
+
+ for patch := range notdone.IterAll() {
+ comment := cleanSubject(patch.Comment)
+ log.Info("new patch:", patch.NewHash, "commithash:", patch.CommitHash, patch.RepoNamespace, comment)
+ }
+ // savePatchsets()
+ makePatchesWin(notdone)
+ })
+
+ // make a box at the bottom of the window for the protobuf table
+ dwin.box = dwin.win.Bottom.Box().SetProgName("TBOX")
+
+ // load and show the current patch sets
+ openPatchsets()
+ if me.psets == nil {
+ log.Info("Open Patchsets failed")
+ return dwin
+ }
+ dwin.doPatchsetsTable(me.psets)
+
+ return dwin
+}
+
+func (dwin *stdPatchsetTableWin) doPatchsetsTable(currentPatchsets *forgepb.Patchsets) {
+ dwin.Lock()
+ defer dwin.Unlock()
+ if dwin.TB != nil {
+ dwin.TB.Delete()
+ dwin.TB = nil
+ }
+
+ // display the protobuf
+ dwin.TB = AddPatchsetsPB(dwin.box, currentPatchsets)
+ f := func(pset *forgepb.Patchset) {
+ log.Info("Triggered. do something here", pset.Name)
+ /*
+ win := makePatchWindow(pset)
+ win.Show()
+ */
+ }
+ dwin.TB.Custom(f)
+}
+
+func AddPatchsetsPB(tbox *gui.Node, pb *forgepb.Patchsets) *forgepb.PatchsetsTable {
+ t := pb.NewTable("PatchsetsPB")
+ t.NewUuid()
+ t.SetParent(tbox)
+
+ t.AddStringFunc("#", func(p *forgepb.Patchset) string {
+ return fmt.Sprintf("%d", p.Patches.Len())
+ })
+
+ vp := t.AddButtonFunc("View Patchset", func(p *forgepb.Patchset) string {
+ return p.Name
+ })
+ vp.Custom = func(pset *forgepb.Patchset) {
+ log.Info("show patches here", pset.Name)
+ makePatchesWin(pset.Patches)
+ // patchwin := makePatchesWin()
+ // patchwin.doPatchesTable(pset.Patches)
+ /*
+ win := makePatchWindow(pset)
+ win.Show()
+ */
+ }
+
+ t.AddComment()
+ t.AddState()
+ t.AddHostname()
+
+ ctimef := func(p *forgepb.Patchset) string {
+ ctime := p.Ctime.AsTime()
+ return ctime.Format("2006/01/02 15:04")
+ }
+ t.AddStringFunc("ctime", ctimef)
+
+ /*
+ etimef := func(e *forgepb.Patchset) string {
+ etime := e.Etime.AsTime()
+ s := etime.Format("2006/01/02 15:04")
+ if strings.HasPrefix(s, "1970/") {
+ // just show a blank if it's not set
+ return ""
+ }
+ return s
+ }
+ t.AddStringFunc("etime", etimef)
+ */
+
+ t.AddStringFunc("Author", func(p *forgepb.Patchset) string {
+ return fmt.Sprintf("%s <%s>", p.GitAuthorName, p.GitAuthorEmail)
+ })
+
+ t.AddUuid()
+
+ newCommit := t.AddButtonFunc("new hash", func(p *forgepb.Patchset) string {
+ return "find"
+ })
+ newCommit.Custom = func(pset *forgepb.Patchset) {
+ log.Info("find new commits here", pset.Name)
+ // makePatchesWin(pset.Patches)
+ setNewCommitHash(pset)
+ }
+
+ t.ShowTable()
+ return t
+}
+
+func setPatchsetState(p *forgepb.Patchset) {
+ var bad bool
+ var good bool
+ var done bool = true
+
+ all := p.Patches.All()
+ for all.Scan() {
+ patch := all.Next()
+ // log.Info("patch:", patch.StartHash, patch.CommitHash, patch.RepoNamespace, patch.Filename)
+ repo := me.forge.FindByGoPath(patch.RepoNamespace)
+ if repo == nil {
+ log.Info("couldn't find repo", patch.RepoNamespace)
+ bad = true
+ continue
+ }
+ if _, err := repo.GetHashName(patch.CommitHash); err == nil {
+ // this patch has been applied
+ patch.Applied = true
+ done = true
+ continue
+ }
+ if name, err := repo.GetHashName(patch.StartHash); err == nil {
+ // it might be possible to apply this patch
+ log.Info("patch may be good:", patch.RepoNamespace, name, patch.CommitHash, patch.Filename)
+ good = true
+ } else {
+ // probably screwed up git trees
+ log.Info("patch with unknown origin:", patch.RepoNamespace, name, err, patch.CommitHash, patch.Filename)
+ bad = true
+ }
+ }
+ if bad {
+ p.State = "BAD"
+ return
+ }
+ if good {
+ p.State = "TRY"
+ return
+ }
+ if done {
+ p.State = "DONE"
+ return
+ }
+}
+
+func cleanSubject(line string) string {
+ // Regular expression to remove "Subject:" and "[PATCH...]" patterns
+ re := regexp.MustCompile(`(?i)^Subject:\s*(\[\s*PATCH[^\]]*\]\s*)?`)
+ cleaned := re.ReplaceAllString(line, "")
+ return strings.TrimSpace(cleaned)
+}
+
+func findCommitBySubject(subject string) (string, error) {
+ cmd := exec.Command("git", "log", "--pretty=format:%H %s", "--grep="+subject, "-i")
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ err := cmd.Run()
+ if err != nil {
+ return "", err
+ }
+
+ lines := strings.Split(out.String(), "\n")
+ for _, line := range lines {
+ if strings.Contains(strings.ToLower(line), strings.ToLower(subject)) {
+ return strings.Fields(line)[0], nil // return the commit hash
+ }
+ }
+ return "", fmt.Errorf("no commit found for subject: %s", subject)
+}
+
+func setNewCommitHash(p *forgepb.Patchset) bool {
+ var done bool = true
+ for patch := range p.Patches.IterAll() {
+ // parts := strings.Fields(patch.Comment)
+
+ repo := me.forge.FindByGoPath(patch.RepoNamespace)
+ if repo == nil {
+ log.Info("couldn't find repo", patch.RepoNamespace)
+ continue
+ }
+
+ comment := cleanSubject(patch.Comment)
+
+ if patch.NewHash != "na" {
+ log.Info("patch: newhash:", patch.NewHash, "commithash:", patch.CommitHash, patch.RepoNamespace, comment)
+ continue
+ }
+ done = false
+ os.Chdir(repo.GetFullPath())
+ newhash, err := findCommitBySubject(comment)
+ if err != nil {
+ log.Info("patch: not found hash:", patch.CommitHash, patch.RepoNamespace, comment, newhash, err)
+ continue
+ }
+ patch.NewHash = newhash
+ log.Info("patch: found hash:", patch.CommitHash, newhash, patch.RepoNamespace, comment)
+ }
+
+ return done
+}
+
+func AddNotDonePatches(notdone *forgepb.Patches, pset *forgepb.Patchset) {
+ for patch := range pset.Patches.IterAll() {
+ // parts := strings.Fields(patch.Comment)
+
+ repo := me.forge.FindByGoPath(patch.RepoNamespace)
+ if repo == nil {
+ log.Info("couldn't find repo", patch.RepoNamespace)
+ notdone.Append(patch)
+ continue
+ }
+
+ comment := cleanSubject(patch.Comment)
+
+ if patch.NewHash != "na" {
+ log.Info("already applied patch: newhash:", patch.NewHash, "commithash:", patch.CommitHash, patch.RepoNamespace, comment)
+ continue
+ }
+ os.Chdir(repo.GetFullPath())
+ newhash, err := findCommitBySubject(comment)
+ if err == nil {
+ patch.NewHash = newhash
+ log.Info("patch: found hash:", patch.CommitHash, newhash, patch.RepoNamespace, comment)
+ continue
+ }
+
+ // this patch has not been applied yet
+ log.Info("patch: not found hash:", patch.CommitHash, patch.RepoNamespace, comment, newhash, err)
+ notdone.Append(patch)
+ }
+}