// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 package main // An app to submit patches for the 30 GO GUI repos import ( "fmt" "os" "os/user" "path/filepath" "time" "go.wit.com/gui" "go.wit.com/lib/debugger" "go.wit.com/lib/gadgets" "go.wit.com/lib/gui/shell" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" ) func debug() { time.Sleep(5 * time.Second) for { now := time.Now() tmp := fmt.Sprintf("All (%d)", me.forge.Repos.Len()) me.repoAllB.SetLabel(tmp) findMergeToDevel() tmp = fmt.Sprintf("needs merge to devel (%d)", me.found.Len()) me.repoDevelMergeB.SetLabel(tmp) me.found = new(gitpb.Repos) all := me.forge.Repos.SortByFullPath() for all.Scan() { repo := all.Next() if me.forge.Config.IsReadOnly(repo.GetGoPath()) { continue } me.found.AppendByGoPath(repo) } tmp = fmt.Sprintf("writable (%d)", me.found.Len()) me.repoWritableB.SetLabel(tmp) doDirty() tmp = fmt.Sprintf("dirty (%d)", me.found.Len()) me.repoDirtyB.SetLabel(tmp) log.Printf("finished a forge scan here in (%s)\n", shell.FormatDuration(time.Since(now))) time.Sleep(90 * time.Second) } } func doGui() { if me.forge.Config.GetDefaultGui() == "" { me.forge.Config.DefaultGui = "gocui" me.forge.ConfigSave() } me.myGui = gui.New() me.myGui.InitEmbed(resources) me.myGui.SetAppDefaultPlugin(me.forge.Config.DefaultGui) // sets the default GUI plugin to use me.myGui.Default() mainWindow := gadgets.RawBasicWindow("Forge: (this kinda works sometimes)") mainWindow.Make() mainWindow.Show() mainWindow.Custom = func() { log.Warn("MAIN WINDOW CLOSE") now := time.Now() count := me.forge.RillReload() log.Info("Repo Reload count =", count) if count != 0 { me.forge.ConfigSave() } // this is just interesting to see how fast it is on various boxes // and with how many repos you are working with. On my current laptop // I have 320 repos and when I'm using it and most things are in memory // cache, it took: rill repos.Reload() took (2ms) // which is hard to believe when my ~/go/src is 13G with 424266 files // nevermind, there must be something wrong with the code right now log.Printf("rill repos.Reload() took (%s)\n", shell.FormatDuration(time.Since(now))) os.Exit(0) } drawWindow(mainWindow) // sits here forever debug() } func drawWindow(win *gadgets.BasicWindow) { var reposWin *repoWindow // this is the handle to the repo window box := win.Box() vbox := box.NewVerticalBox("BOX2") group1 := vbox.NewGroup("Forge Settings") grid := group1.NewGrid("buildOptions", 0, 0) // me.autoWorkingPwd = gadgets.NewOneLiner(grid, "working directory (pwd)") me.goSrcPwd = gadgets.NewOneLiner(grid, "repo src home") grid.NextRow() usr, _ := user.Current() homeDir, err := os.UserHomeDir() if err != nil { log.Warn("Error getting home directory:", err) homeDir = "/home/autotypist" } srcDir := filepath.Join(homeDir, "go/src") me.goSrcPwd.SetText(srcDir) // use ENV GIT_AUTHOR me.gitAuthor = gadgets.NewOneLiner(grid, "Git Author") grid.NextRow() if os.Getenv("GIT_AUTHOR_NAME") == "" { me.gitAuthor.SetText("ENV GIT_AUTHOR_NAME is unset") } else { author := os.Getenv("GIT_AUTHOR_NAME") author += " <" + os.Getenv("GIT_AUTHOR_EMAIL") + ">" me.gitAuthor.SetText(author) } // select the branch you want to test, build and develop against // this lets you select your user branch, but, when you are happy // you can merge everything into the devel branch and make sure it actually // works. Then, when that is good, merge and version everything in master me.setBranchB = grid.NewButton("git checkout", func() { win.Disable() defer win.Enable() if reposWin != nil { log.Info("reposWin == nil") reposWin.Hide() } if me.autoCreateBranches.Checked() { argv.Force = true } else { argv.Force = false } // do the checkout if err := doCheckoutShared(); err != nil { log.Info("checkout error:", err) } else { log.Info("checkout was ok") } }) /* me.setBranchB = grid.NewButton("git repos", func() { t := me.forge.Repos.NewTable() fp := t.AddFullPath("Full Path") fp.Custom( func() { log.Info("this is the full path") }) t.AddMasterVersion("master version") t.Show() }) */ me.newBranch = grid.NewDropdown() me.newBranch.AddText("master") me.newBranch.AddText("devel") me.newBranch.AddText(usr.Username) me.newBranch.SetText(usr.Username) me.argvCheckoutUser = true me.newBranch.Custom = func() { // toggle global values shared by the command line and the gui for doCheckout() me.argvCheckoutUser = false me.argvCheckoutDevel = false me.argvCheckoutMaster = false switch me.newBranch.String() { case "master": me.argvCheckoutMaster = true case "devel": me.argvCheckoutDevel = true default: me.argvCheckoutUser = true } log.Info("forged changed to default:", me.newBranch.String()) } // checking this will automatically make the branches off of devel me.autoCreateBranches = grid.NewCheckbox("auto create branches").SetChecked(true) grid.NextRow() group2 := vbox.NewGroup("Repos") grid = group2.RawGrid() me.repoAllB = grid.NewButton("All", func() { me.found = new(gitpb.Repos) all := me.forge.Repos.SortByFullPath() for all.Scan() { repo := all.Next() me.found.AppendByGoPath(repo) } makeStandardReposWindow(me.found) }) me.repoDirtyB = grid.NewButton("dirty", func() { me.found = new(gitpb.Repos) findDirty() makeStandardReposWindow(me.found) }) me.repoWritableB = grid.NewButton("writable", func() { me.found = new(gitpb.Repos) all := me.forge.Repos.SortByFullPath() for all.Scan() { repo := all.Next() if me.forge.Config.IsReadOnly(repo.GetGoPath()) { continue } me.found.AppendByGoPath(repo) } makeStandardReposWindow(me.found) }) me.repoDevelMergeB = grid.NewButton("needs merge to devel", func() { findMergeToDevel() makeStandardReposWindow(me.found) }) var problemsWin *repoProblemsWindow grid.NewButton("Repo Problems", func() { if problemsWin != nil { problemsWin.Toggle() return } problemsWin = makeRepoProblemsWindow() }) grid.NextRow() /* group2 = vbox.NewGroup("Repos with problems") grid = group2.RawGrid() grid.NewButton("devel is behind master", func() { log.Info("not done yet") }) grid.NewButton("user branch is remote", func() { log.Info("not done yet") }) grid.NewButton("unknown branches", func() { log.Info("not done yet") }) grid.NextRow() grid.NewButton("remote devel != local devel", func() { log.Info("not done yet") }) grid.NewButton("remote master != local master", func() { log.Info("not done yet") }) */ group2 = vbox.NewGroup("Merge") grid = group2.RawGrid() grid.NewButton("merge to devel", func() { win.Disable() defer win.Enable() mergeUserToDevel(me.autoCreateBranches.Checked()) }) grid.NewButton("merge to master", func() { win.Disable() defer win.Enable() mergeDevelToMaster(me.autoCreateBranches.Checked()) }) grid.NewButton("merge all", func() { win.Disable() defer win.Enable() me.argvCheckoutUser = false me.argvCheckoutDevel = true me.argvCheckoutMaster = false if err := doCheckoutShared(); err != nil { log.Info("checkout error:", err) } else { log.Info("checkout was ok") } mergeUserToDevel(me.autoCreateBranches.Checked()) me.argvCheckoutUser = false me.argvCheckoutDevel = false me.argvCheckoutMaster = true if err := doCheckoutShared(); err != nil { log.Info("checkout error:", err) } else { log.Info("checkout was ok") } mergeDevelToMaster(me.autoCreateBranches.Checked()) }) group3 := vbox.NewGroup("work in progress") grid = group3.RawGrid() grid.NewButton("forge ConfigSave()", func() { me.forge.ConfigSave() }) grid.NewButton("debugger()", func() { debugger.DebugWindow() }) var patchWin *patchesWindow grid.NewButton("Patches Window", func() { if patchWin != nil { patchWin.Toggle() return } patchWin = new(patchesWindow) patchWin.initWindow() patchWin.Show() }) /* grid.NewButton("Repo Window", func() { win.Disable() defer win.Enable() if reposWin != nil { if reposWin.Hidden() { reposWin.Show() } else { reposWin.Hide() } return } reposWin := new(repoWindow) reposWin.win = gadgets.RawBasicWindow("All git repositories in ~/go/src/") reposWin.win.Make() reposWin.box = reposWin.win.Box().NewBox("bw vbox", false) // me.reposwin.Draw() reposWin.win.Custom = func() { log.Warn("Repo Window close. hidden=true") // sets the hidden flag to false so Toggle() works reposWin.win.Hide() } reposWin.topbox = reposWin.repoMenu() reposWin.View = repolist.InitBox(me.forge, reposWin.box) reposWin.View.Enable() // need to update this logic reposWin.View.ScanRepositoriesOld() reposWin.win.Show() }) */ } // this is the magic that generates a window directly from the protocol buffer func makeStandardReposWindow(pb *gitpb.Repos) { t := pb.NewTable("testDirty") sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string { return r.GetGoPath() }) // t.Custom = func() { // log.Info("close grid?") // } sf.Custom = func(r *gitpb.Repo) { log.Info("do button click on", r.GetGoPath()) } t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time { return repo.NewestTime() }) t.AddMasterVersion() t.AddDevelVersion() t.AddUserVersion() t.AddCurrentBranchName() t.AddState() t.ShowTable() /* t.AddStringFunc("zood", func(m *zoopb.Machine) string { return findVersion(m, "zood") }) t.AddTimeFunc("age", func(m *zoopb.Machine) time.Time { return m.Laststamp.AsTime() }) */ } func findMergeToDevel() { me.found = new(gitpb.Repos) all := me.forge.Repos.SortByFullPath() for all.Scan() { repo := all.Next() // this sees if user has patches for devel. If it does, add it to me.found if repo.CountDiffObjects(repo.GetUserBranchName(), repo.GetDevelBranchName()) > 0 { me.found.AppendByGoPath(repo) } } now := time.Now() if me.found.Len() == 0 { log.Info("nothing to merge with devel") return } me.forge.PrintHumanTable(me.found) // check for merges from devel total, count, nope, _ := IsEverythingOnDevel() log.Printf("devel branch check. %d total repos. (%d ok) (%d not on devel branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now))) } func findMergeToMaster() { me.found = new(gitpb.Repos) all := me.forge.Repos.SortByFullPath() for all.Scan() { repo := all.Next() if me.forge.Config.IsReadOnly(repo.GetGoPath()) { continue } /* if repo.IsDirty() { continue } if repo.GetMasterVersion() != repo.GetDevelVersion() { me.found.AppendByGoPath(repo) continue } */ // this sees if devel is behind master. IT SHOULD NOT BE if repo.CountDiffObjects(repo.GetMasterBranchName(), repo.GetDevelBranchName()) == 0 { // everything is normal } else { repo.State = "DEVEL < MASTER" log.Info("SERIOUS ERROR. DEVEL BRANCH IS BEHIND MASTER", repo.GetGoPath()) } // this sees if devel has patches for master. If it does, add it to me.found if repo.CountDiffObjects(repo.GetDevelBranchName(), repo.GetMasterBranchName()) > 0 { me.found.AppendByGoPath(repo) } } now := time.Now() if me.found.Len() == 0 { log.Info("nothing to merge with master") return } me.forge.PrintHumanTable(me.found) // check for merges from devel total, count, nope, _ := IsEverythingOnMaster() log.Printf("Master branch check. %d total repos. (%d ok) (%d not on master branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now))) } func mergeDevelToMaster(doit bool) { findMergeToMaster() if !doit { return } all := me.found.SortByFullPath() for all.Scan() { repo := all.Next() log.Info("repo:", repo.GetGoPath()) if result, err := repo.MergeToMaster(); err == nil { log.Warn("THINGS SEEM OK", repo.GetFullPath()) for _, line := range result.Stdout { log.Warn("stdout:", line) } for _, line := range result.Stderr { log.Warn("stderr:", line) } } else { log.Warn("THINGS FAILED ", repo.GetFullPath()) log.Warn("err", err) if result == nil { break } for _, line := range result.Stdout { log.Warn("stdout:", line) } for _, line := range result.Stderr { log.Warn("stderr:", line) } log.Warn("THINGS FAILED ", repo.GetFullPath()) break } me.forge.SetConfigSave(true) // view.Update() } me.forge.ConfigSave() } func mergeUserToDevel(doit bool) { findMergeToDevel() if !doit { return } all := me.found.SortByFullPath() for all.Scan() { repo := all.Next() bruser := repo.GetUserBranchName() brdevel := repo.GetDevelBranchName() if repo.GetUserVersion() == "uerr" { // no user branch return } log.Info("trying", bruser, repo.GetUserVersion()) b1 := repo.CountDiffObjects(bruser, brdevel) // should be zero if b1 == 0 { // log.Info("User is already merged into Devel", repo.GetGoPath(), cmd) return } log.Info("merging user into devel repo:", repo.GetGoPath()) if result, err := repo.MergeToDevel(); err == nil { log.Warn("THINGS SEEM OK", repo.GetFullPath()) for _, line := range result.Stdout { log.Warn("stdout:", line) } for _, line := range result.Stderr { log.Warn("stderr:", line) } } else { log.Warn("THINGS FAILED ", repo.GetFullPath()) log.Warn("err", err) if result == nil { break } for _, line := range result.Stdout { log.Warn("stdout:", line) } for _, line := range result.Stderr { log.Warn("stderr:", line) } break } me.forge.SetConfigSave(true) // view.Update() } me.forge.ConfigSave() }