Compare commits

...

34 Commits

Author SHA1 Message Date
Jeff Carr 3efb520fe0 first draft 2025-07-08 17:17:05 -05:00
Jeff Carr 0dea6a2553 detect repos to update 2025-07-08 15:48:22 -05:00
Jeff Carr 807049f6c7 add 'forge merge master' 2025-07-07 23:07:12 -05:00
Jeff Carr 86e513d845 minor 2025-07-07 20:39:34 -05:00
Jeff Carr 5d57a8968d s/GitPull/Pull/ 2025-07-07 20:27:10 -05:00
Jeff Carr e2d33ea496 move argv "sync" under "pull" 2025-07-07 20:14:24 -05:00
Jeff Carr d3938adf63 deprecate the "forge mode" concept
unfortunately, this was a bad idea. boo
	I wasted lots of time on this. live and learn
2025-07-07 19:58:34 -05:00
Jeff Carr 08b7f2406c more on 'forge pull' 2025-07-07 17:34:10 -05:00
Jeff Carr 041be81af1 start a "httpPB" scheme 2025-07-02 12:51:00 -05:00
Jeff Carr 4fc9d038ca working out testing 2025-07-01 19:03:55 -05:00
Jeff Carr 1ea9bdf841 deprecate old stuff 2025-07-01 18:54:41 -05:00
Jeff Carr 1d50f9eb69 rename 2025-07-01 18:37:11 -05:00
Jeff Carr d962ff8db0 deprecate me.found 2025-07-01 18:36:48 -05:00
Jeff Carr d2a0aa3098 more doFind() cleanups 2025-07-01 18:23:38 -05:00
Jeff Carr 47b7222445 todo: redo patch handling against forge.wit.com 2025-06-30 07:44:07 -05:00
Jeff Carr 2616d0d8b4 add a way to debug protobuf Marshal() errors 2025-06-29 02:48:47 -05:00
Jeff Carr 0219d69bfb make forge dir 2025-06-26 17:58:07 -05:00
Jeff Carr 391d47318b fix "patchable" repos button 2025-05-31 21:53:14 -05:00
Jeff Carr 1282c17e81 rearrange main window 2025-05-31 16:03:00 -05:00
Jeff Carr 9f367cb39b drop old code. rearrange buttons 2025-05-31 14:38:55 -05:00
Jeff Carr 2f8da5a8be move buttons around 2025-05-31 12:49:19 -05:00
Jeff Carr 912c5a9bb9 code rearrange 2025-05-31 11:35:49 -05:00
Jeff Carr 715b63b1c8 rename 2025-05-31 11:35:04 -05:00
Jeff Carr ad5d8dbb87 redo repos window 2025-05-31 11:31:11 -05:00
Jeff Carr 8a24141803 fix for --purge 2025-05-29 19:21:27 -05:00
Jeff Carr 1c733adfce minor 2025-05-23 03:27:43 -05:00
Jeff Carr 1defa8d582 use the local build by default 2025-04-30 14:40:05 -05:00
Jeff Carr f061bf9730 add commit --submit=false option 2025-04-29 17:49:05 -05:00
Jeff Carr 6d62858d69 code re-arrange 2025-04-29 17:18:02 -05:00
Jeff Carr cc8800bf60 merge this into a single window 2025-04-24 19:28:04 -05:00
Jeff Carr ef6da4aa17 use RillRepo() 2025-04-20 21:45:03 -05:00
Jeff Carr 52c9fece43 add 'forge sync' 2025-04-20 20:41:24 -05:00
Jeff Carr 06cf0f7d84 fix defective google "Git on Borg" repos 2025-04-04 06:07:56 -05:00
Jeff Carr 99e30376f0 button to show all patches 2025-03-25 13:17:00 -05:00
24 changed files with 1106 additions and 622 deletions

View File

@ -35,7 +35,7 @@ plugin:
# -cp ../../toolkits/gocui/gocui.so resources/
andlabs: clean install
forge --gui andlabs
forge --gui gocui --gui-verbose --gui-file ../../toolkits/andlabs/andlabs.so
gocui: install
forge --gui gocui --gui-verbose --gui-file ../../toolkits/gocui/gocui.so >/tmp/forge.log 2>&1
@ -50,10 +50,15 @@ goimports:
clean:
-rm -f forge go.*
# -rm -f ~/go/src/repos.pb
go-mod-clean --purge
go-mod-clean purge
identify-protobuf:
autogenpb --identify ~/go/src/repos.pb
devel:
forge clean devel --force --verbose
pull: install
forge pull dirty
FORGE_URL="http://forge.grid.wit.com:2520/" forge pull
# forge pull patches

View File

@ -49,3 +49,18 @@ make # this runs GO111MODULE=off go build insuring that y
## Debian packages:
Instructions are on https://mirrors.wit.com/
## possible 'git bug' integration ideas:
```
git pull origin +refs/bugs/\*:refs/bugs/\*
git pull origin +refs/identities/\*:refs/identities/\*
# remove the caches
rm -rf .git/git-bug
# rebuild the cache with any command
git bug user
```

View File

@ -48,8 +48,6 @@ func savePatchset(pset *forgepb.Patchset) error {
// re-run git CheckDirty() on everything
func IsAnythingDirty() bool {
me.found = new(gitpb.Repos)
findAll() // select all the repos
doCheckDirtyAndConfigSave()
found := findDirty()
if found.Len() == 0 {

36
argv.go
View File

@ -17,13 +17,15 @@ var argv args
type args struct {
Checkout *CheckoutCmd `arg:"subcommand:checkout" help:"switch branches using 'git checkout'"`
Clean *CleanCmd `arg:"subcommand:clean" help:"start over at the beginning"`
Commit *EmptyCmd `arg:"subcommand:commit" help:"'git commit' but errors out if on wrong branch"`
Commit *CommitCmd `arg:"subcommand:commit" help:"'git commit' but errors out if on wrong branch"`
Config *ConfigCmd `arg:"subcommand:config" help:"show your .config/forge/ settings"`
Dirty *DirtyCmd `arg:"subcommand:dirty" help:"show repos git says are dirty"`
Debug *DebugCmd `arg:"subcommand:debug" help:"debug forge"`
Dirty *DirtyCmd `arg:"subcommand:dirty" help:"show dirty git repos"`
GitFetch *FindCmd `arg:"subcommand:fetch" help:"run 'git fetch master'"`
List *FindCmd `arg:"subcommand:list" help:"print a table of the current repos"`
Merge *MergeCmd `arg:"subcommand:merge" help:"merge branches"`
Patch *PatchCmd `arg:"subcommand:patch" help:"make patchsets"`
GitPull *FindCmd `arg:"subcommand:pull" help:"run 'git pull'"`
Pull *PullCmd `arg:"subcommand:pull" help:"run 'git pull'"`
URL string `arg:"--connect" help:"forge url"`
All bool `arg:"--all" help:"git commit --all"`
Build string `arg:"--build" help:"build a repo"`
@ -33,12 +35,15 @@ type args struct {
Verbose bool `arg:"--verbose" help:"show more output"`
Bash bool `arg:"--bash" help:"generate bash completion"`
BashAuto []string `arg:"--auto-complete" help:"todo: move this to go-arg"`
// Show string `arg:"--show" help:"show a repo"`
}
type EmptyCmd struct {
}
type CommitCmd struct {
Submit bool `arg:"--submit" default:"true" help:"submit the patches to forge"`
}
type testCmd string
type CleanCmd struct {
@ -62,6 +67,13 @@ type PatchCmd struct {
Submit string `arg:"--submit" help:"submit your commits"`
}
type PullCmd struct {
Test *EmptyCmd `arg:"subcommand:test" help:"list repos that need 'git pull'"`
Dirty *EmptyCmd `arg:"subcommand:dirty" help:"only check dirty repos"`
Patches *EmptyCmd `arg:"subcommand:patches" help:"only check repos with patches"`
Sync *SyncCmd `arg:"subcommand:sync" help:"sync repos with upstream"`
}
type ConfigAddCmd struct {
Path string `arg:"--path" help:"absolute path of the git repo"`
GoPath string `arg:"--gopath" help:"GO path of the git repo"`
@ -85,12 +97,27 @@ type ConfigCmd struct {
Register string `arg:"--register" help:"register your git URL (foo.com/mystuff) or (github.com/foo/bar)"`
}
type DebugCmd struct {
Config *EmptyCmd `arg:"subcommand:config" help:"used to debug protobuf Marshal() if things go wrong"`
}
type CheckoutCmd struct {
User *FindCmd `arg:"subcommand:user" help:"git checkout user"`
Devel *FindCmd `arg:"subcommand:devel" help:"git checkout devel"`
Master *FindCmd `arg:"subcommand:master" help:"git checkout master"`
}
type MergeCmd struct {
Devel *FindCmd `arg:"subcommand:devel" help:"merge user to devel"`
Master *FindCmd `arg:"subcommand:master" help:"merge devel to master"`
Publish *EmptyCmd `arg:"subcommand:publish" help:"increment versions and publish master branch"`
}
type SyncCmd struct {
Clean *EmptyCmd `arg:"subcommand:clean" help:"sync everything to upstream master"`
User *EmptyCmd `arg:"subcommand:user" help:"sync everything to user"`
}
type DirtyCmd struct {
}
@ -101,6 +128,7 @@ type FindCmd struct {
Private bool `arg:"--private" help:"your private repos from your .config/forge/"`
Dirty bool `arg:"--dirty" help:"only use dirty git repos"`
User bool `arg:"--user" help:"show repos on the user branch"`
Full bool `arg:"--full" help:"show full repo names"`
// ReadOnly bool `arg:"--readonly" help:"include read-only repos"`
}

View File

@ -34,23 +34,31 @@ func (args) doBashAuto() {
case "commit":
fmt.Println("--all")
case "config":
fmt.Println("add fix list")
fmt.Println("add fix list debug")
case "delete":
deleteMatch()
case "debug":
fmt.Println("config")
case "dirty":
fmt.Println("")
case "examine":
fmt.Println("fix")
case "list":
fmt.Println("--all --mine --favorites --private")
fmt.Println("--full")
case "merge":
fmt.Println("devel master")
case "pull":
fmt.Println("--force")
fmt.Println("dirty clean list patches sync --force")
case "--find":
fmt.Println("foo bar")
case "patch":
fmt.Println("get list --submit show")
case "user":
fmt.Println("--force")
case "devel":
fmt.Println("--force")
case "sync":
fmt.Println("clean user --force")
case "master":
fmt.Println("")
case "verify":
@ -58,7 +66,7 @@ func (args) doBashAuto() {
default:
if argv.BashAuto[0] == ARGNAME {
// list the subcommands here
fmt.Println("--bash list checkout clean commit config dirty fetch patch pull")
fmt.Println("--bash list checkout commit config dirty debug fetch merge patch pull")
}
}
os.Exit(0)

3
build
View File

@ -3,3 +3,6 @@
# include forgeConfig
mkdir -p files/usr/bin/
cp ../utils/wit-utils/forgeConfig/forgeConfig files/usr/bin/
mkdir -p files/usr/share/bash-completion/completions/
forge --bash > files/usr/share/bash-completion/completions/forge

View File

@ -5,11 +5,11 @@ package main
import (
"fmt"
"os"
"path/filepath"
"time"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
@ -80,7 +80,7 @@ func IsEverythingOnUser() (int, int, int, error) {
}
func doGitReset() {
all := me.found.SortByFullPath()
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
@ -119,6 +119,14 @@ func rillCheckoutUser(repo *gitpb.Repo) error {
// trys to figure out if there is still something to update
func doAllCheckoutUser() error {
now := time.Now()
if argv.Force {
log.Info("going to force create user branches")
if err := makeUserBranches(); err != nil {
return err
}
}
me.forge.RillFuncError(rillCheckoutUser)
count := me.forge.RillReload()
if count != 0 {
@ -129,16 +137,16 @@ func doAllCheckoutUser() error {
log.Printf("User branch check. %d total repos. (%d ok) (%d not on user branch) (%s)\n", total, count, nope, shell.FormatDuration(time.Since(now)))
if err != nil {
// display all repos not on user
me.found = new(gitpb.Repos)
found := new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
}
}
me.forge.PrintHumanTable(me.found)
log.Printf("There are %d repos that are NOT on the user branch\n", me.found.Len())
me.forge.PrintHumanTable(found)
log.Printf("There are %d repos that are NOT on the user branch\n", found.Len())
return err
}
return nil
@ -160,6 +168,10 @@ func rillCheckoutDevel(repo *gitpb.Repo) error {
// is every repo on the devel branch?
func doAllCheckoutDevel() error {
now := time.Now()
if argv.Force {
log.Info("going to force create devel branches")
makeDevelBranches()
}
log.Info("going to rill:")
me.forge.RillFuncError(rillCheckoutDevel)
count := me.forge.RillReload()
@ -171,16 +183,16 @@ func doAllCheckoutDevel() error {
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)))
if err != nil {
// display all repos not on user
me.found = new(gitpb.Repos)
found := new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if repo.GetCurrentBranchName() != repo.GetDevelBranchName() {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
}
}
me.forge.PrintHumanTable(me.found)
log.Printf("There are %d repos that are NOT on the devel branch\n", me.found.Len())
me.forge.PrintHumanTable(found)
log.Printf("There are %d repos that are NOT on the devel branch\n", found.Len())
return err
}
return nil
@ -191,12 +203,19 @@ func rillCheckoutMaster(repo *gitpb.Repo) error {
// never do dirty repos
return nil
}
if repo.GetCurrentBranchName() == repo.GetMasterBranchName() {
// repo is already on master
// 'giterr' means something is very wrong with this repo
if repo.GetMasterVersion() == "giterr" {
repo.CheckoutMaster()
log.Info("master == giterr. BAD REPO", repo.GetFullPath())
log.Info("master == giterr. BAD REPO", repo.GetFullPath())
log.Info("master == giterr. BAD REPO", repo.GetFullPath())
cmd := []string{"git", "checkout", "main"} // todo: figure out main
repo.RunVerbose(cmd)
os.Exit(-1)
return nil
}
if repo.GetUserVersion() == "uerr" {
repo.CheckoutMaster()
if repo.GetCurrentBranchName() == repo.GetMasterBranchName() {
// repo is already on master
return nil
}
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
@ -231,81 +250,43 @@ func doAllCheckoutMaster() error {
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)))
if err != nil {
// display all repos not on master
me.found = new(gitpb.Repos)
found := new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if repo.GetCurrentBranchName() != repo.GetMasterBranchName() {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
}
}
me.forge.PrintHumanTable(me.found)
log.Printf("There are %d repos that are NOT on the master branch\n", me.found.Len())
me.forge.PrintHumanTable(found)
log.Printf("There are %d repos that are NOT on the master branch\n", found.Len())
return err
}
return nil
}
// shared this with the GUI and the command line?
func doCheckoutShared() error {
if me.argvCheckoutUser {
// log.Info("Starting git checkout user")
if argv.Force {
log.Info("going to force create user branches")
if err := makeUserBranches(); err != nil {
return err
}
}
// this uses rill and is super fast
doAllCheckoutUser()
return nil
}
if me.argvCheckoutDevel {
// log.Info("Starting git checkout devel")
if argv.Force {
log.Info("going to force create devel branches")
makeDevelBranches()
}
// this uses rill and is super fast
doAllCheckoutDevel()
return nil
}
if me.argvCheckoutMaster {
log.Info("Starting git checkout master")
doAllCheckoutMaster()
return nil
}
log.Info("Forge didn't know what branches to checkout")
return nil
}
// trys to figure out if there is still something to update
// todo: redo this logic as it is terrible
func doCheckout() error {
if argv.Checkout.User != nil {
me.argvCheckoutUser = true
me.forge.Config.Mode = forgepb.ForgeMode_USER
me.forge.Config.ConfigSave()
if err := doAllCheckoutUser(); err != nil {
badExit(err)
}
}
if argv.Checkout.Devel != nil {
me.argvCheckoutDevel = true
me.forge.Config.Mode = forgepb.ForgeMode_DEVEL
me.forge.Config.ConfigSave()
if err := doAllCheckoutDevel(); err != nil {
badExit(err)
}
}
if argv.Checkout.Master != nil {
me.argvCheckoutMaster = true
me.forge.Config.Mode = forgepb.ForgeMode_MASTER
me.forge.Config.ConfigSave()
if err := doAllCheckoutMaster(); err != nil {
badExit(err)
}
}
if err := doCheckoutShared(); err != nil {
badExit(err)
}
okExit("")
badExit(fmt.Errorf("did not specify what branch to checkout"))
return nil
}

View File

@ -24,6 +24,9 @@ func doCommit() {
}
newpatches = true
}
if !argv.Commit.Submit {
okExit("")
}
if newpatches {
// if there are enw patches, autocommit them
_, err := me.forge.SubmitDevelPatchSet("forge auto commit")
@ -42,8 +45,9 @@ func doCommit() {
}
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
me.found.Append(repo)
me.forge.PrintHumanTable(me.found)
found := new(gitpb.Repos)
found.Append(repo)
me.forge.PrintHumanTable(found)
log.Info("")
log.Info("wrong branch. Can not commit on", repo.GetCurrentBranchName())
log.Info("")
@ -69,8 +73,9 @@ func doCommit() {
func doCommitRepo(repo *gitpb.Repo) error {
if repo.GetCurrentBranchName() != repo.GetUserBranchName() {
me.found.Append(repo)
me.forge.PrintHumanTable(me.found)
found := new(gitpb.Repos)
found.Append(repo)
me.forge.PrintHumanTable(found)
log.Info("")
log.Info("wrong branch. Can not commit on", repo.GetCurrentBranchName())
log.Info("")

168
doDebug.go Normal file
View File

@ -0,0 +1,168 @@
// 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"
"io"
"log"
"reflect"
"unicode/utf8"
"go.wit.com/lib/protobuf/bugpb"
"go.wit.com/lib/protobuf/forgepb"
"golang.org/x/text/encoding/charmap"
"google.golang.org/protobuf/proto"
)
func doDebug() {
me.forge = forgepb.InitPB()
me.forge.ScanGoSrc()
if err := me.forge.ConfigSave(); err != nil {
if err := me.forge.Repos.ConfigSave(); err != nil {
err := ValidateProtoUTF8(me.forge.Repos)
if err != nil {
log.Printf("Protobuf UTF-8 validation failed: %v\n", err)
}
if err := bugpb.SanitizeProtoUTF8(me.forge.Repos); err != nil {
log.Fatalf("Sanitization failed: %v", err)
}
}
// badExit(err)
}
me.forge.SetConfigSave(true)
me.forge.Exit()
okExit("this never runs")
}
// ValidateProtoUTF8 checks all string fields in a proto.Message recursively.
func ValidateProtoUTF8(msg proto.Message) error {
return validateValue(reflect.ValueOf(msg), "")
}
func validateValue(val reflect.Value, path string) error {
if !val.IsValid() {
return nil
}
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return nil
}
return validateValue(val.Elem(), path)
}
switch val.Kind() {
case reflect.Struct:
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := val.Type().Field(i)
fieldPath := fmt.Sprintf("%s.%s", path, fieldType.Name)
if err := validateValue(field, fieldPath); err != nil {
return err
}
}
case reflect.String:
s := val.String()
if !utf8.ValidString(s) {
return fmt.Errorf("invalid UTF-8 string at %s: %q", path, s)
}
case reflect.Slice:
if val.Type().Elem().Kind() == reflect.Uint8 {
return nil // skip []byte
}
for i := 0; i < val.Len(); i++ {
if err := validateValue(val.Index(i), fmt.Sprintf("%s[%d]", path, i)); err != nil {
return err
}
}
case reflect.Map:
for _, key := range val.MapKeys() {
valItem := val.MapIndex(key)
if err := validateValue(valItem, fmt.Sprintf("%s[%v]", path, key)); err != nil {
return err
}
}
}
return nil
}
// SanitizeProtoUTF8 fixes all invalid UTF-8 strings in a proto.Message recursively.
func SanitizeProtoUTF8(msg proto.Message) error {
return sanitizeValue(reflect.ValueOf(msg), "")
}
func sanitizeValue(val reflect.Value, path string) error {
if !val.IsValid() {
return nil
}
if val.Kind() == reflect.Ptr {
if val.IsNil() {
return nil
}
return sanitizeValue(val.Elem(), path)
}
switch val.Kind() {
case reflect.Struct:
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := val.Type().Field(i)
if !field.CanSet() {
continue
}
if err := sanitizeValue(field, fmt.Sprintf("%s.%s", path, fieldType.Name)); err != nil {
return err
}
}
case reflect.String:
s := val.String()
if !utf8.ValidString(s) {
utf8Str, err := latin1ToUTF8(s)
if err != nil {
return fmt.Errorf("failed to convert %s to UTF-8: %v", path, err)
}
val.SetString(utf8Str)
}
case reflect.Slice:
if val.Type().Elem().Kind() == reflect.Uint8 {
return nil // skip []byte
}
for i := 0; i < val.Len(); i++ {
if err := sanitizeValue(val.Index(i), fmt.Sprintf("%s[%d]", path, i)); err != nil {
return err
}
}
case reflect.Map:
for _, key := range val.MapKeys() {
valItem := val.MapIndex(key)
newItem := reflect.New(valItem.Type()).Elem()
newItem.Set(valItem)
if err := sanitizeValue(newItem, fmt.Sprintf("%s[%v]", path, key)); err != nil {
return err
}
val.SetMapIndex(key, newItem)
}
}
return nil
}
func latin1ToUTF8(input string) (string, error) {
reader := charmap.ISO8859_1.NewDecoder().Reader(bytes.NewReader([]byte(input)))
result, err := io.ReadAll(reader)
if err != nil {
return "", err
}
return string(result), nil
}

View File

@ -12,7 +12,6 @@ import (
)
func doDirty() {
findAll() // select all the repos
doCheckDirtyAndConfigSave()
found := findDirty()
if argv.Verbose {
@ -26,7 +25,7 @@ func straightCheckDirty() int {
var count int
// var total int
// now := time.Now()
for repo := range me.found.IterAll() {
for repo := range me.forge.Repos.IterAll() {
// total += 1
if repo.IsDirty() {
count += 1
@ -52,7 +51,7 @@ func doCheckDirtyAndConfigSave() {
now := time.Now()
me.forge.RillFuncError(doCheckDirty)
end := straightCheckDirty()
log.Printf("dirty check (%d dirty repos) (%d total repos) took:%s\n", end, me.found.Len(), shell.FormatDuration(time.Since(now)))
log.Printf("dirty check (%d dirty repos) (%d total repos) took:%s\n", end, me.forge.Repos.Len(), shell.FormatDuration(time.Since(now)))
if start != end {
// todo: use internal forgepb configsave flag. should work?

View File

@ -8,36 +8,47 @@ import (
)
// this populates a slice of protobuf records representing each git repo
// var me.found []*gitpb.Repo
// var found []*gitpb.Repo
//
// so, it makes a subset of repos that are then used performing actions on
//
// by default, it adds every repo
func doFind() *gitpb.Repos {
if argv.List == nil {
return findAll()
}
if argv.List.Mine {
return findMine()
}
if argv.List.Dirty {
return findDirty()
}
return findAll()
}
func (f *FindCmd) findRepos() *gitpb.Repos {
if f == nil {
findMine()
return me.found
return findMine()
}
if f.All {
findAll()
return me.found
return findAll()
}
if f.Private {
findPrivate()
return me.found
return findPrivate()
}
if f.Mine {
findMine()
return me.found
return findMine()
}
if f.Favorites {
findFavorites()
return me.found
return findFavorites()
}
if f.Dirty {
@ -45,47 +56,55 @@ func (f *FindCmd) findRepos() *gitpb.Repos {
}
if f.User {
findUser()
return me.found
return findUser()
}
findAll()
return me.found
return findAll()
}
func findPrivate() {
func findPrivate() *gitpb.Repos {
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
if me.forge.Config.IsPrivate(repo.GetGoPath()) {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
}
}
return found
}
// finds repos that are writable
func findMine() {
func findMine() *gitpb.Repos {
found := gitpb.NewRepos()
// log.Printf("get mine %s\n", me.forge.GetGoSrc())
for repo := range me.forge.Repos.IterByFullPath() {
if me.forge.Config.IsWritable(repo.GetGoPath()) {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
}
}
return found
}
// finds repos the user has marked as favorites in the forge .config
func findFavorites() {
func findFavorites() *gitpb.Repos {
found := gitpb.NewRepos()
// log.Printf("get favorites %s\n", me.forge.GetGoSrc())
for repo := range me.forge.Repos.IterByFullPath() {
if me.forge.Config.IsFavorite(repo.GetGoPath()) {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
}
}
return found
}
// finds repos that git is reporting as dirty
func findDirty() *gitpb.Repos {
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
if repo.IsDirty() {
found.AppendByGoPath(repo)
@ -94,39 +113,62 @@ func findDirty() *gitpb.Repos {
return found
}
func findAll() {
func findAll() *gitpb.Repos {
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
}
return found
}
func findUser() {
func find50() *gitpb.Repos {
count := 0
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
found.AppendByGoPath(repo)
if count > 50 {
return found
}
count += 1
}
return found
}
func findUser() *gitpb.Repos {
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
if repo.GetCurrentBranchName() == repo.GetUserBranchName() {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
}
}
return found
}
func findPublishable() {
func findPublishable() *gitpb.Repos {
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
if repo.GetTargetVersion() == "" {
continue
}
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
}
return found
}
func findReposWithPatches() {
func findReposWithPatches() *gitpb.Repos {
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
if repo.GetTargetVersion() != "" {
// add everything that has a target version set
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
continue
}
if repo.IsDirty() {
// always add dirty branches
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
continue
}
if repo.GetUserVersion() == "" || repo.GetUserVersion() == "uerr" {
@ -134,18 +176,27 @@ func findReposWithPatches() {
continue
}
if repo.GetUserVersion() != repo.GetDevelVersion() {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
continue
}
// ignore read-only repos for checks below here
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
continue
}
// show anything that differs between 'devel' & 'master' branches
if repo.GetDevelVersion() != repo.GetMasterVersion() {
found.AppendByGoPath(repo)
continue
}
// this is an old test to see if the current 'last tag' is accurate and should be removed
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
continue
}
if repo.GetLastTag() != repo.GetMasterVersion() {
me.found.AppendByGoPath(repo)
found.AppendByGoPath(repo)
repo.FindLastTag()
continue
}
}
return found
}

172
doGui.go
View File

@ -102,16 +102,9 @@ func doGui() {
func drawWindow(win *gadgets.GenericWindow) {
grid := win.Group.RawGrid()
me.goSrcPwd = gadgets.NewOneLiner(grid, "repo src home")
me.goSrcPwd = gadgets.NewOneLiner(grid, "Working Directory")
grid.NewLabel("")
var howtoWin *gadgets.GenericWindow
me.demoB = grid.NewButton("Howto", func() {
if howtoWin != nil {
howtoWin.Toggle()
return
}
howtoWin = makeHowtoWin()
})
grid.NextRow()
me.goSrcPwd.SetText(me.forge.GetGoSrc())
@ -128,59 +121,12 @@ func drawWindow(win *gadgets.GenericWindow) {
me.gitAuthor.SetText(author)
}
group1 := win.Stack.NewGroup("Forge Mode")
grid = group1.RawGrid()
me.forgeMode = grid.NewLabel("")
me.forgeMode.SetText(me.forge.GetMode())
me.newBranch = grid.NewDropdown()
me.newBranch.AddText("master")
me.newBranch.AddText("devel")
me.newBranch.AddText(me.forge.Config.GetUsername())
me.newBranch.Custom = func() {
me.setBranchB.Disable()
// toggle global values shared by the command line and the gui for doCheckout()
switch me.newBranch.String() {
case "master":
if me.forge.Config.Mode != forgepb.ForgeMode_MASTER {
me.setBranchB.Enable()
}
case "devel":
if me.forge.Config.Mode != forgepb.ForgeMode_DEVEL {
me.setBranchB.Enable()
}
default:
if me.forge.Config.Mode != forgepb.ForgeMode_USER {
me.setBranchB.Enable()
}
}
}
// 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("Switch mode", func() {
win.Disable()
defer win.Enable()
switch me.newBranch.String() {
case "master":
forgeSwitchMode(forgepb.ForgeMode_MASTER)
case "devel":
forgeSwitchMode(forgepb.ForgeMode_DEVEL)
default:
forgeSwitchMode(forgepb.ForgeMode_USER)
}
me.setBranchB.Disable()
})
me.setBranchB.Disable()
grid.NextRow()
groupM := win.Stack.NewGroup("Mode Windows")
gridM := groupM.RawGrid()
// groupM := hbox.NewGroup("Windows")
// gridM := groupM.RawGrid()
// hbox := win.Stack.Box().Horizontal()
gridM := win.Stack.RawGrid()
var releaseWin *gadgets.GenericWindow
gridM.NewButton("Release Window", func() {
@ -202,18 +148,21 @@ func drawWindow(win *gadgets.GenericWindow) {
patches = makePatchsetsWin()
})
// the user mode "hack Window"
var hackWin *gadgets.GenericWindow
gridM.NewButton("Hack Window", func() {
if hackWin != nil {
hackWin.Toggle()
var insertWin *gadgets.GenericWindow
s := fmt.Sprintf("Repos (%d)", me.forge.Repos.Len())
me.reposWinB = gridM.NewButton(s, func() {
// if the window exists, just toggle it open or closed
if insertWin != nil {
insertWin.Toggle()
return
}
hackWin = makeHackModeWindow()
insertWin = makeReposWinNew()
})
var reposWin *gadgets.GenericWindow
me.reposWinB = gridM.NewButton("Repos", func() {
// var reposWin *gadgets.GenericWindow
var reposWin *stdReposTableWin
gridM.NewButton("Fix Repos", func() {
if reposWin != nil {
reposWin.Toggle()
return
@ -237,7 +186,7 @@ func drawWindow(win *gadgets.GenericWindow) {
all := me.psets.All()
for all.Scan() {
pset := all.Next()
AddNotDonePatches(notdone, pset)
AddNotDonePatches(notdone, pset, false)
}
for patch := range notdone.IterAll() {
@ -248,64 +197,24 @@ func drawWindow(win *gadgets.GenericWindow) {
patchesWin = makePatchesWin(notdone)
})
// set the initial button state based on the last
// forge mode the user saved in the config file
switch me.forge.Config.Mode {
case forgepb.ForgeMode_MASTER:
me.newBranch.SetText("master")
case forgepb.ForgeMode_DEVEL:
me.newBranch.SetText("devel")
case forgepb.ForgeMode_USER:
me.newBranch.SetText(me.forge.Config.GetUsername())
default:
me.newBranch.SetText(me.forge.Config.GetUsername())
}
gridM.NextRow()
var howtoWin *gadgets.GenericWindow
gridM.NewButton("Tutorial", func() {
if howtoWin != nil {
howtoWin.Toggle()
return
}
howtoWin = makeHowtoWin()
})
forgeSwitchMode(me.forge.Config.Mode)
}
// verify the GUI button disable/enable settings
func forgeVerifyGuiState() {
me.forgeMode.SetText(me.forge.GetMode())
me.argvCheckoutUser = false
me.argvCheckoutDevel = false
me.argvCheckoutMaster = false
switch me.forge.Config.Mode {
case forgepb.ForgeMode_MASTER:
me.argvCheckoutMaster = true
me.newBranch.SetText("master")
case forgepb.ForgeMode_DEVEL:
me.argvCheckoutDevel = true
me.newBranch.SetText("devel")
case forgepb.ForgeMode_USER:
me.newBranch.SetText(me.forge.Config.GetUsername())
me.argvCheckoutUser = true
default:
me.newBranch.SetText(me.forge.Config.GetUsername())
me.argvCheckoutUser = true
}
}
// sets the text in the labels in the window
// and hides and shows the buttons
func forgeSwitchMode(newMode forgepb.ForgeMode) {
if newMode == me.forge.Config.Mode {
log.Info("you are already on", newMode.String())
forgeVerifyGuiState() // doing this here initializes the button state
return
}
me.forge.Config.Mode = newMode
forgeVerifyGuiState() // update the button states
me.forge.Config.ConfigSave()
gridM.NextRow()
gridM.NewLabel("")
}
// this is the magic that generates a window directly from the protocol buffer
func makeStandardReposGrid(pb *gitpb.Repos) *gitpb.ReposTable {
t := pb.NewTable("testDirty")
t.NewUuid()
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
return r.GetGoPath()
})
@ -347,8 +256,7 @@ func findMergeToDevel() *gitpb.Repos {
found := gitpb.NewRepos()
for repo := range me.forge.Repos.IterByFullPath() {
// this sees if user has patches for devel. If it does, add it to me.found
// this sees if user has patches for devel. If it does, add it to found
if repo.CountDiffObjects(repo.GetUserBranchName(), repo.GetDevelBranchName()) > 0 {
found.AppendByGoPath(repo)
}
@ -365,8 +273,9 @@ func findMergeToDevel() *gitpb.Repos {
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)))
return found
}
func findMergeToMaster() {
me.found = new(gitpb.Repos)
func findMergeToMaster() *gitpb.Repos {
found := new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
@ -395,30 +304,31 @@ func findMergeToMaster() {
// 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)
found.AppendByGoPath(repo)
}
}
now := time.Now()
if me.found.Len() == 0 {
if found.Len() == 0 {
log.Info("nothing to merge with master")
return
return found
}
me.forge.PrintHumanTable(me.found)
me.forge.PrintHumanTable(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)))
return found
}
func mergeDevelToMaster(doit bool) {
findMergeToMaster()
found := findMergeToMaster()
if !doit {
return
}
all := me.found.SortByFullPath()
all := found.SortByFullPath()
for all.Scan() {
repo := all.Next()
log.Info("repo:", repo.GetGoPath())

View File

@ -31,12 +31,12 @@ func doPatch() error {
// if no option is given to patch, list out the
// repos that have patches ready in them
findReposWithPatches()
if me.found.Len() == 0 {
found := findReposWithPatches()
if found.Len() == 0 {
log.Info("you currently have no patches in your user branches")
return nil
}
me.forge.PrintHumanTable(me.found)
me.forge.PrintHumanTable(found)
return nil
}

155
doPull.go
View File

@ -4,6 +4,7 @@
package main
import (
"fmt"
"time"
"go.wit.com/lib/gui/shell"
@ -42,16 +43,87 @@ func rillPull(repo *gitpb.Repo) error {
// is every repo on the devel branch?
func doGitPullNew() {
now := time.Now()
pullcount := me.forge.RillFuncError(rillPull)
count := me.forge.RillReload()
if count != 0 {
me.forge.ConfigSave()
func doGitPullNew() error {
if argv.Pull == nil {
return fmt.Errorf("not really 'fetch pull'")
}
total, count, nope, _ := IsEverythingOnMaster()
log.Printf("Master branch check. %d total repos. (%d git pulled) (%d not on master branch) (%s) git pull total=%d\n", total, count, nope, shell.FormatDuration(time.Since(now)), pullcount)
if argv.Force {
now := time.Now()
pullcount := me.forge.RillFuncError(rillPull)
count := me.forge.RillReload()
if count != 0 {
me.forge.ConfigSave()
}
total, count, nope, _ := IsEverythingOnMaster()
log.Printf("Master branch check. %d total repos. (%d git pulled) (%d not on master branch) (%s) git pull total=%d\n", total, count, nope, shell.FormatDuration(time.Since(now)), pullcount)
return nil
}
check := gitpb.NewRepos()
if argv.Pull.Dirty != nil {
check = findDirty()
}
if argv.Pull.Patches != nil {
check = findReposWithPatches()
}
if check.Len() == 0 {
// check = doFind()
check = findAll()
// check = find50()
// check = findMine()
}
me.forge.PrintHumanTableFull(check)
found, err := me.forge.LookupPB(check)
if err != nil {
log.Info("LookupPB() failed", err, "len(check)=", check.Len())
return err
}
// me.forge.PrintHumanTableFull(found)
// check to see if the repos need to be updated
update := gitpb.NewRepos()
if found.Len() == 0 {
return nil
}
log.Info("found.Len() ==", found.Len())
for repo := range found.IterAll() {
masterb := repo.GetMasterBranchName()
if masterb == "" {
log.Info(repo.GetNamespace(), "master branch blank")
continue
}
a := repo.GetLocalHash(masterb)
ns := repo.GetNamespace()
repo2 := me.forge.Repos.FindByNamespace(ns)
if repo2 == nil {
log.Info("repo namespace does not exist", a, repo.Namespace)
continue
}
b := repo2.GetLocalHash(repo2.GetMasterBranchName())
if b == a {
continue
}
log.Info(a, "!=", b, repo.Namespace)
update.AppendByNamespace(repo2)
}
if update.Len() == 0 {
// nothing to update
return nil
}
if _, err := me.forge.UpdatePB(update); err != nil {
log.Info("UpdatePB() failed", err, "len(check)=", update.Len())
return err
}
return nil
}
/*
@ -100,3 +172,70 @@ func doGitFetch() {
me.forge.ConfigSave()
}
}
func doMergeDevel() (*gitpb.Repos, error) {
var err error
done := gitpb.NewRepos()
found := findMergeToDevel()
for repo := range found.IterAll() {
if repo.CheckDirty() {
log.Info("repo is dirty", repo.GetGoPath())
continue
}
log.Info("Starting merge on", repo.GetGoPath())
if repo.CheckoutDevel() {
log.Info("checkout devel failed", repo.GetGoPath())
err = fmt.Errorf("checkout devel failed")
break
}
if _, err := repo.MergeToDevel(); err != nil {
log.Info("merge from user failed", repo.GetGoPath(), err)
err = fmt.Errorf("merge from user failed")
// log.Info(strings.Join(r.Stdout, "\n"))
// log.Info(strings.Join(r.Stderr, "\n"))
break
}
done.Append(repo)
/*
if repo.CheckoutMaster() {
log.Info("checkout master failed", repo.GetGoPath())
return
}
if _, err := repo.MergeToMaster(); err != nil {
log.Info("merge from devel failed", repo.GetGoPath(), err)
return
}
*/
}
return done, err
}
func doMergeMaster() (*gitpb.Repos, error) {
var err error
done := gitpb.NewRepos()
found := findMergeToMaster()
for repo := range found.IterAll() {
if repo.CheckDirty() {
log.Info("repo is dirty", repo.GetGoPath())
continue
}
log.Info("Starting merge on", repo.GetGoPath())
if repo.CheckoutMaster() {
log.Info("checkout devel failed", repo.GetGoPath())
err = fmt.Errorf("checkout devel failed")
break
}
if _, err := repo.MergeToMaster(); err != nil {
log.Info("merge from user failed", repo.GetGoPath(), err)
err = fmt.Errorf("merge from user failed")
// log.Info(strings.Join(r.Stdout, "\n"))
// log.Info(strings.Join(r.Stderr, "\n"))
break
}
done.Append(repo)
}
return done, err
}

115
doSync.go Normal file
View File

@ -0,0 +1,115 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
package main
import (
"fmt"
"path/filepath"
"time"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
// trys to figure out if there is still something to update
func doSync() error {
if argv.Pull.Sync.Clean != nil {
return doSyncClean()
}
if argv.Pull.Sync.User != nil {
return doSyncUser()
}
return fmt.Errorf("nothing to do")
}
func doSyncClean() error {
if err := doAllCheckoutMaster(); err != nil {
return err
}
if _, _, _, err := IsEverythingOnMaster(); err != nil {
log.Info("Not all repos are on the master branch")
return err
}
// force everything
argv.Force = true
if err := doCleanUser(); err != nil {
return err
}
if err := doCleanDevel(); err != nil {
return err
}
now := time.Now()
pullcount := me.forge.RillFuncError(rillPull)
count := me.forge.RillReload()
if count != 0 {
me.forge.ConfigSave()
}
total, count, nope, _ := IsEverythingOnMaster()
log.Printf("doSyncClean() ok. %d total repos. (%d git pulled) (%d not on master branch) (%s) git pull total=%d\n", total, count, nope, shell.FormatDuration(time.Since(now)), pullcount)
return nil
}
func doSyncUser() error {
if count, err := me.forge.RillRepo(10, 20, syncDevelBranch); err != nil {
log.Info("RillFunc() failed", err)
return err
} else {
log.Info("Rill syncDevelBranch() ok count =", count)
}
argv.Force = true
if err := doAllCheckoutUser(); err != nil {
return err
}
return nil
}
func syncDevelBranch(repo *gitpb.Repo) error {
branch := repo.GetDevelBranchName()
if repo.Exists(filepath.Join(".git/refs/heads", branch)) {
return nil
}
if repo.Exists(filepath.Join(".git/refs/remotes/origin", branch)) {
cmd := []string{"git", "checkout", branch}
err := repo.RunVerbose(cmd)
return err
}
cmd := []string{"git", "branch", branch}
repo.RunVerbose(cmd)
cmd = []string{"git", "checkout", branch}
err := repo.RunVerbose(cmd)
return err
}
func syncDevelBranches() error {
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
branch := repo.GetDevelBranchName()
if repo.Exists(filepath.Join(".git/refs/heads", branch)) {
continue
}
if repo.Exists(filepath.Join(".git/refs/remotes/origin", branch)) {
cmd := []string{"git", "checkout", branch}
repo.RunVerbose(cmd)
continue
}
cmd := []string{"git", "branch", branch}
repo.RunVerbose(cmd)
cmd = []string{"git", "checkout", branch}
repo.RunVerbose(cmd)
}
return nil
}

45
main.go
View File

@ -7,6 +7,7 @@ package main
import (
"embed"
"fmt"
"os"
"strings"
@ -64,9 +65,14 @@ func main() {
}
me.urlbase = strings.Trim(me.urlbase, "/") // track down why trailing '/' makes http POST not work
// internally debugging can be triggered here before Init()
if argv.Debug != nil {
doDebug()
okExit("")
}
// load the ~/.config/forge/ config
me.forge = forgepb.Init()
me.found = new(gitpb.Repos)
// first find the repos or gopaths to operate on
if argv.Config != nil {
@ -112,7 +118,6 @@ func main() {
}
if argv.Clean.GitReset != nil {
findAll() // select all the repos
doGitReset()
okExit("reset")
}
@ -134,7 +139,31 @@ func main() {
okExit("")
}
if argv.GitPull != nil {
if argv.Merge != nil {
if argv.Merge.Devel != nil {
if _, err := doMergeDevel(); err != nil {
badExit(err)
}
okExit("devel merge ok")
}
if argv.Merge.Master != nil {
if _, err := doMergeMaster(); err != nil {
badExit(err)
}
okExit("master merge ok")
}
badExit(fmt.Errorf("merge what?"))
}
if argv.Pull != nil {
if argv.Pull.Sync != nil {
if err := doSync(); err != nil {
badExit(err)
}
okExit("")
}
doGitPullNew()
okExit("")
}
@ -142,7 +171,11 @@ func main() {
if argv.List != nil {
found := argv.List.findRepos()
// print out the repos
me.forge.PrintHumanTable(found)
if argv.List.Full {
me.forge.PrintHumanTableFull(found)
} else {
me.forge.PrintHumanTable(found)
}
okExit("")
}
@ -163,7 +196,8 @@ func main() {
// nothing else was specified to be done,
// then just list the table to stdout
if gui.NoGui() {
me.forge.PrintHumanTable(me.found)
found := doFind()
me.forge.PrintHumanTable(found)
okExit("")
}
@ -171,7 +205,6 @@ func main() {
// basically, if you run just 'forge' it should open the GUI
// if opening the GUI, always check git for dirty repos
findAll() // select all the repos
doCheckDirtyAndConfigSave()
doGui()
okExit("")

View File

@ -8,7 +8,6 @@ import (
"go.wit.com/gui"
"go.wit.com/lib/gadgets"
"go.wit.com/lib/protobuf/forgepb"
"go.wit.com/lib/protobuf/gitpb"
)
var me *mainType
@ -26,53 +25,21 @@ type mainType struct {
pp *arg.Parser // for parsing the command line args. Yay to alexf lint!
forge *forgepb.Forge // for holding the forge protobuf files
myGui *gui.Node // the gui toolkit handle
found *gitpb.Repos // stores the list of repos to process things on
psets *forgepb.Patchsets // the locally stored on disk patchsets
foundPaths []string // stores gopaths to act on (when doing go-clone)
configSave bool // if the config file should be saved after finishing
urlbase string // base URL
// our view of the repositories
// patchWin *patchesWindow
mainWindow *gadgets.BasicWindow
mainbox *gui.Node // the main box. enable/disable this
autoDryRun *gui.Node // checkbox for --dry-run
goSrcPwd *gadgets.OneLiner // what is being used as primary directory for your work
gitAuthor *gadgets.OneLiner // ENV GIT_AUTHOR NAME and EMAIL
// the main box. enable/disable this
mainbox *gui.Node
// the window from the /lib/gui/gowit package
// lw *gadgets.BasicWindow
// #### Sorting options for the repolist
// autoHidePerfect *gui.Node
// autoHideReadOnly *gui.Node
// checkbox for --dry-run
autoDryRun *gui.Node
// checkbox to enable intermittent scanning
// if checked, it will check all your repos for changes
autoScanReposCB *gui.Node
goSrcPwd *gadgets.OneLiner // what is being used as primary directory for your work
gitAuthor *gadgets.OneLiner // ENV GIT_AUTHOR NAME and EMAIL
forgeMode *gui.Node // is the user in 'master', 'devel' or 'user' branches
// these hold the branches that the user can switch all
// the repositories to them
newBranch *gui.Node // deprecate?
setBranchB *gui.Node // deprecate?
// these hold the branches that the user can switch all the repositories to them
reposWinB *gui.Node // button that opens the repos window
repoAllB *gui.Node // "all" repos button
repoDirtyB *gui.Node // "dirty" repos button
repoDevelMergeB *gui.Node // "merge to devel" repos button
repoWritableB *gui.Node // "what repos are writable" repos button
demoB *gui.Node // opens the demo
// modeReleaseW *gui.Node // opens the release window
// modePatchW *gui.Node // opens the patch window
// modeUserW *gui.Node // opens the user/hack window
argvCheckoutUser bool // shared between the GUI and the command line tools
argvCheckoutDevel bool // shared between the GUI and the command line tools
argvCheckoutMaster bool // shared between the GUI and the command line tools
}

View File

@ -24,7 +24,7 @@ type foundWindow struct {
dirtyOL *gadgets.OneLiner
readonlyOL *gadgets.OneLiner
rw *gadgets.OneLiner
// checkB *gui.Node
found *gitpb.Repos
}
func (r *foundWindow) Hidden() bool {
@ -67,9 +67,8 @@ func (r *foundWindow) initWindow() {
})
group1.NewButton("all", func() {
log.Info("find all here")
me.found = new(gitpb.Repos)
findAll()
me.forge.PrintHumanTable(me.found)
found := findAll()
me.forge.PrintHumanTable(found)
})
r.grid = r.stack.RawGrid()
@ -80,7 +79,7 @@ func (r *foundWindow) initWindow() {
}
func (r *foundWindow) listRepos() {
for repo := range me.found.IterAll() {
for repo := range r.found.IterAll() {
r.addRepo(repo)
}
}

View File

@ -1,115 +0,0 @@
// 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 (
"go.wit.com/lib/debugger"
"go.wit.com/lib/gadgets"
"go.wit.com/log"
)
func makeHackModeWindow() *gadgets.GenericWindow {
win := gadgets.NewGenericWindow("git user branch / Hack Mode Window", "This is a work in progress")
grid := win.Group.RawGrid()
grid.NewButton("git pull", func() {
log.Info("todo: run git pull on each repo")
})
me.repoDevelMergeB = grid.NewButton("merge", func() {
found := findMergeToDevel()
_, box := makeStandardReposWindow("repos to merge from user to devel", found)
hbox := box.Box().Horizontal()
hbox.NewButton("merge all", func() {
win.Disable()
defer win.Enable()
all := found.SortByFullPath()
for all.Scan() {
repo := all.Next()
if repo.CheckDirty() {
log.Info("repo is dirty", repo.GetGoPath())
continue
}
log.Info("Starting merge on", repo.GetGoPath())
if repo.CheckoutDevel() {
log.Info("checkout devel failed", repo.GetGoPath())
return
}
if _, err := repo.MergeToDevel(); err != nil {
log.Info("merge from user failed", repo.GetGoPath(), err)
// log.Info(strings.Join(r.Stdout, "\n"))
// log.Info(strings.Join(r.Stderr, "\n"))
return
}
if repo.CheckoutMaster() {
log.Info("checkout master failed", repo.GetGoPath())
return
}
if _, err := repo.MergeToMaster(); err != nil {
log.Info("merge from devel failed", repo.GetGoPath(), err)
return
}
}
})
})
grid.NextRow()
group2 := win.Stack.NewGroup("Merge")
grid = group2.RawGrid()
grid.NewButton("merge to devel", func() {
win.Disable()
defer win.Enable()
mergeUserToDevel(true)
})
grid.NewButton("merge to master", func() {
win.Disable()
defer win.Enable()
mergeDevelToMaster(true)
})
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(true)
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(true)
})
group3 := win.Stack.NewGroup("work in progress")
grid = group3.RawGrid()
grid.NewButton("forge ConfigSave()", func() {
me.forge.ConfigSave()
})
grid.NewButton("debugger()", func() {
debugger.DebugWindow()
})
return win
}

View File

@ -41,14 +41,15 @@ func makeHowtoWin() *gadgets.GenericWindow {
grid.NewLabel("") // a stupid way to add padding
grid.NextRow()
howtoWin.Group.NewLabel("Working dir: " + me.forge.GetGoSrc())
// howtoWin.Group.NewLabel("Working dir: " + me.forge.GetGoSrc())
grid = howtoWin.Group.RawGrid()
grid.NewButton("Download", func() {
grid.NewButton("Download into "+me.forge.GetGoSrc(), func() {
howtoWin.Disable()
defer howtoWin.Enable()
downloadForge()
})
grid.NewButton("Build", func() {
grid.NewButton("Build forge & GUI GO plugins", func() {
howtoWin.Disable()
defer howtoWin.Enable()
buildForge()

View File

@ -57,7 +57,24 @@ func makePatchesWin(patches *forgepb.Patches) *stdPatchTableWin {
grid.NewLabel(fmt.Sprintf("total repos"))
grid.NextRow()
grid.NewButton("reload", func() {
grid.NewButton("show all", 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, true)
}
for patch := range notdone.IterAll() {
comment := cleanSubject(patch.Comment)
log.Info("new patch:", patch.NewHash, "commithash:", patch.CommitHash, patch.RepoNamespace, comment)
}
dwin.doPatchesTable(notdone)
})
grid.NewButton("Apply All", func() {
var count int
@ -135,6 +152,11 @@ func AddPatchesPB(tbox *gui.Node, pb *forgepb.Patches) *forgepb.PatchesTable {
t.SetParent(tbox)
gitam := t.AddButtonFunc("apply", func(p *forgepb.Patch) string {
rn := p.RepoNamespace
if repo := me.forge.FindByGoPath(rn); repo == nil {
// log.Info("Could not figure out repo path", rn)
return ""
}
return "git am"
})
gitam.Custom = func(p *forgepb.Patch) {

View File

@ -145,7 +145,7 @@ func makePatchsetsWin() *stdPatchsetTableWin {
all := me.psets.All()
for all.Scan() {
pset := all.Next()
AddNotDonePatches(notdone, pset)
AddNotDonePatches(notdone, pset, false)
}
for patch := range notdone.IterAll() {
@ -266,7 +266,7 @@ func setPatchsetState(p *forgepb.Patchset) {
// 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)
log.Info("could not find repo", patch.RepoNamespace)
bad = true
continue
}
@ -332,7 +332,7 @@ func setNewCommitHash(p *forgepb.Patchset) bool {
repo := me.forge.FindByGoPath(patch.RepoNamespace)
if repo == nil {
log.Info("couldn't find repo", patch.RepoNamespace)
log.Info("could not find repo", patch.RepoNamespace)
continue
}
@ -356,7 +356,7 @@ func setNewCommitHash(p *forgepb.Patchset) bool {
return done
}
func AddNotDonePatches(notdone *forgepb.Patches, pset *forgepb.Patchset) {
func AddNotDonePatches(notdone *forgepb.Patches, pset *forgepb.Patchset, full bool) {
for patch := range pset.Patches.IterAll() {
comment := cleanSubject(patch.Comment)
@ -367,8 +367,10 @@ func AddNotDonePatches(notdone *forgepb.Patches, pset *forgepb.Patchset) {
repo := me.forge.FindByGoPath(patch.RepoNamespace)
if repo == nil {
log.Info("couldn't find repo", patch.RepoNamespace)
notdone.Append(patch)
log.Info("could not find repo", patch.RepoNamespace)
if full {
notdone.AppendByCommitHash(patch) // double check to ensure the commit hash isn't added twice
}
continue
}

View File

@ -10,160 +10,22 @@ import (
"os"
"time"
"go.wit.com/lib/debugger"
"go.wit.com/lib/gadgets"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
func makeReposWin() *gadgets.GenericWindow {
win := gadgets.NewGenericWindow("git repos", "All about git repos")
func makeReposWin() *stdReposTableWin {
rwin := new(stdReposTableWin)
win := gadgets.NewGenericWindow("find errors and try to fix them", "types of errors of some sort or another")
rwin.win = win
grid := win.Group.RawGrid()
me.repoDirtyB = grid.NewButton("dirty", func() {
doCheckDirtyAndConfigSave()
found := findDirty()
tb, box := makeStandardReposWindow("dirty repos", found)
hbox := box.Box().Horizontal()
hbox.NewButton("commit all", func() {
for repo := range found.IterByFullPath() {
log.Info("do commit here on", repo.GetGoPath())
}
log.Info("TODO: fix this")
log.Info("run 'forge commit --all'")
})
hbox.NewButton("update table", func() {
me.forge.PrintHumanTable(found)
found2 := findDirty()
me.forge.PrintHumanTable(found2)
// tb.Update()
// tb.UpdateTable(found2)
})
hbox.NewButton("delete table", func() {
tb.Delete()
})
})
// win.Top.NewGroup("misc (works in progress)")
var writeWin *gadgets.GenericWindow
me.repoWritableB = grid.NewButton("writable", func() {
// if the window exists, just toggle it open or closed
if writeWin != nil {
writeWin.Toggle()
return
}
// make the window for the first time
found := new(gitpb.Repos)
for repo := range me.forge.Repos.IterByFullPath() {
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
continue
}
found.AppendByGoPath(repo)
}
writeWin, _ = makeWritableWindow(found)
writeWin.Win.Custom = func() {
log.Info("closing window. could do somethine here")
}
})
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("All repos", me.found)
})
var insertWin *gadgets.GenericWindow
me.repoWritableB = grid.NewButton("insert test", func() {
// if the window exists, just toggle it open or closed
if insertWin != nil {
insertWin.Toggle()
return
}
insertWin = makeWindowForPB()
insertWin.Win.Custom = func() {
log.Info("test delete window here")
}
grid := insertWin.Group.RawGrid()
var t *gitpb.ReposTable
grid.NewButton("dirty", func() {
if t != nil {
t.Delete()
t = nil
}
found := findDirty()
// display the protobuf
t = addWindowPB(insertWin, found)
f := func(repo *gitpb.Repo) {
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
}
t.Custom(f)
log.Info("table has uuid", t.GetUuid())
})
grid.NewButton("all", func() {
if t != nil {
t.Delete()
t = nil
}
found := new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
found.AppendByGoPath(repo)
}
// display the protobuf
t = addWindowPB(insertWin, found)
f := func(repo *gitpb.Repo) {
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
}
t.Custom(f)
log.Info("table has uuid", t.GetUuid())
})
grid.NewButton("writeable", func() {
if t != nil {
t.Delete()
t = nil
}
found := new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
continue
}
found.AppendByGoPath(repo)
}
// make the window for the first time
t = addWindowPB(insertWin, found)
f := func(repo *gitpb.Repo) {
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
}
t.Custom(f)
log.Info("table has uuid", t.GetUuid())
})
})
grid.NewButton("Configure", func() {
log.Info("add a forge config window here")
})
win.Stack.NewGroup("misc (works in progress)")
grid = win.Stack.RawGrid()
// grid = win.Top.RawGrid()
grid = win.Group.RawGrid()
var found *gitpb.Repos
var txt string
@ -199,7 +61,7 @@ func makeReposWin() *gadgets.GenericWindow {
hbox.NewButton("test", func() {
})
t := makeStandardReposGrid(found)
t := makeDevelBehindMaster(found)
t.SetParent(box)
t.ShowTable()
})
@ -248,9 +110,8 @@ func makeReposWin() *gadgets.GenericWindow {
t.ShowTable()
})
grid.NewButton("unknown branches", func() {
log.Info("unknown branches not done yet")
})
rwin.boxTB = win.Bottom.Box()
grid.NextRow()
found = develRemoteProblem()
@ -268,7 +129,146 @@ func makeReposWin() *gadgets.GenericWindow {
})
grid.NextRow()
return win
makeHackModeWindow(rwin)
return rwin
}
// table of devel errors behind master
func makeDevelBehindMaster(pb *gitpb.Repos) *gitpb.ReposTable {
t := pb.NewTable("testDirty")
t.NewUuid()
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
return r.GetGoPath()
})
sf.Custom = func(r *gitpb.Repo) {
log.Info("merge master into devel here", r.GetGoPath())
}
t.AddTimeFunc("age", func(repo *gitpb.Repo) time.Time {
return repo.NewestTime()
})
t.AddMasterVersion()
t.AddDevelVersion()
t.AddState()
return t
}
// default window for active running droplets
func (rwin *stdReposTableWin) doReposTable(pb *gitpb.Repos) {
rwin.Lock()
defer rwin.Unlock()
if rwin.TB != nil {
rwin.TB.Delete()
rwin.TB = nil
}
rwin.pb = pb
t := makeStandardReposGrid(pb)
t.SetParent(rwin.boxTB)
t.ShowTable()
rwin.TB = t
}
func makeHackModeWindow(stdwin *stdReposTableWin) {
group := stdwin.win.Top.NewGroup("This is a work in progress")
grid := group.RawGrid()
grid.NewButton("git pull", func() {
log.Info("todo: run git pull on each repo")
})
me.repoDevelMergeB = grid.NewButton("merge", func() {
found := findMergeToDevel()
_, box := makeStandardReposWindow("repos to merge from user to devel", found)
hbox := box.Box().Horizontal()
hbox.NewButton("merge all", func() {
stdwin.win.Disable()
defer stdwin.win.Enable()
all := found.SortByFullPath()
for all.Scan() {
repo := all.Next()
if repo.CheckDirty() {
log.Info("repo is dirty", repo.GetGoPath())
continue
}
log.Info("Starting merge on", repo.GetGoPath())
if repo.CheckoutDevel() {
log.Info("checkout devel failed", repo.GetGoPath())
return
}
if _, err := repo.MergeToDevel(); err != nil {
log.Info("merge from user failed", repo.GetGoPath(), err)
// log.Info(strings.Join(r.Stdout, "\n"))
// log.Info(strings.Join(r.Stderr, "\n"))
return
}
if repo.CheckoutMaster() {
log.Info("checkout master failed", repo.GetGoPath())
return
}
if _, err := repo.MergeToMaster(); err != nil {
log.Info("merge from devel failed", repo.GetGoPath(), err)
return
}
}
})
})
grid.NextRow()
group2 := stdwin.win.Top.NewGroup("Merge")
grid = group2.RawGrid()
grid.NewButton("merge to devel", func() {
stdwin.win.Disable()
defer stdwin.win.Enable()
mergeUserToDevel(true)
})
grid.NewButton("merge to master", func() {
stdwin.win.Disable()
defer stdwin.win.Enable()
mergeDevelToMaster(true)
})
grid.NewButton("merge all", func() {
stdwin.win.Disable()
defer stdwin.win.Enable()
if err := doAllCheckoutDevel(); err != nil {
log.Info("checkout error:", err)
} else {
log.Info("checkout was ok")
}
mergeUserToDevel(true)
if err := doAllCheckoutMaster(); err != nil {
log.Info("checkout error:", err)
} else {
log.Info("checkout was ok")
}
mergeDevelToMaster(true)
})
grid.NewButton("show dirty repos on win.Bottom", func() {
log.Info("try to show dirty repos on bottom")
found := findDirty()
stdwin.doReposTable(found)
})
group3 := stdwin.win.Top.NewGroup("work in progress")
grid = group3.RawGrid()
grid.NewButton("forge ConfigSave()", func() {
me.forge.ConfigSave()
})
grid.NewButton("debugger()", func() {
debugger.DebugWindow()
})
}
func develBehindMasterProblem() *gitpb.Repos {
@ -389,38 +389,3 @@ func makeWritableWindow(pb *gitpb.Repos) (*gadgets.GenericWindow, *gitpb.ReposTa
t.ShowTable()
return win, t
}
func makeWindowForPB() *gadgets.GenericWindow {
win := gadgets.NewGenericWindow("Raw PB View", "Configure")
return win
}
func addWindowPB(win *gadgets.GenericWindow, pb *gitpb.Repos) *gitpb.ReposTable {
t := pb.NewTable("testForgeRepos")
t.NewUuid()
tbox := win.Bottom.Box().SetProgName("TBOX")
t.SetParent(tbox)
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
return r.GetGoPath()
})
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()
f := func(repo *gitpb.Repo) string {
log.Info("repo =", repo.GetGoPath(), repo.GetCurrentVersion())
return repo.GetGoPath()
}
t.AddButtonFunc("cur version", f)
t.ShowTable()
return t
}

185
windowReposNew.go Normal file
View File

@ -0,0 +1,185 @@
// 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 (
"sync"
"time"
"go.wit.com/gui"
"go.wit.com/lib/gadgets"
"go.wit.com/lib/protobuf/gitpb"
"go.wit.com/log"
)
type stdReposTableWin struct {
sync.Mutex
win *gadgets.GenericWindow // the machines gui window
boxTB *gui.Node // the machines gui parent box widget
TB *gitpb.ReposTable // the gui table buffer
pb *gitpb.Repos // the current repos protobuf
update bool // if the window should be updated
}
func (w *stdReposTableWin) Toggle() {
if w == nil {
return
}
if w.win == nil {
return
}
w.win.Toggle()
}
func makeWindowForPB() *gadgets.GenericWindow {
win := gadgets.NewGenericWindow("Forge Repos Protobuf View", "Display Git Repositories")
return win
}
func makeReposWinNew() *gadgets.GenericWindow {
insertWin := makeWindowForPB()
insertWin.Win.Custom = func() {
log.Info("test delete window here")
}
grid := insertWin.Group.RawGrid()
var t *gitpb.ReposTable
grid.NewButton("dirty", func() {
if t != nil {
t.Delete()
t = nil
}
found := findDirty()
// display the protobuf
t = addWindowPB(insertWin, found)
f := func(repo *gitpb.Repo) {
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
}
t.Custom(f)
log.Info("table has uuid", t.GetUuid())
})
grid.NewButton("to publish", func() {
if t != nil {
t.Delete()
t = nil
}
found := findReposWithPatches()
me.forge.PrintHumanTable(found)
// make the window for the first time
t = addWindowPB(insertWin, found)
f := func(repo *gitpb.Repo) {
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
}
t.Custom(f)
log.Info("table has uuid", t.GetUuid())
})
grid.NewButton("favorites", func() {
if t != nil {
t.Delete()
t = nil
}
found := new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if !me.forge.Config.IsFavorite(repo.GetGoPath()) {
continue
}
found.AppendByGoPath(repo)
}
// make the window for the first time
t = addWindowPB(insertWin, found)
f := func(repo *gitpb.Repo) {
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
}
t.Custom(f)
log.Info("table has uuid", t.GetUuid())
})
grid.NewButton("writeable", func() {
if t != nil {
t.Delete()
t = nil
}
found := new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
if me.forge.Config.IsReadOnly(repo.GetGoPath()) {
continue
}
found.AppendByGoPath(repo)
}
// make the window for the first time
t = addWindowPB(insertWin, found)
f := func(repo *gitpb.Repo) {
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
}
t.Custom(f)
log.Info("table has uuid", t.GetUuid())
})
grid.NewButton("all", func() {
if t != nil {
t.Delete()
t = nil
}
found := new(gitpb.Repos)
all := me.forge.Repos.SortByFullPath()
for all.Scan() {
repo := all.Next()
found.AppendByGoPath(repo)
}
// display the protobuf
t = addWindowPB(insertWin, found)
f := func(repo *gitpb.Repo) {
log.Info("got to ReposTable.Custom() id =", repo.GetGoPath(), repo.GetCurrentVersion())
}
t.Custom(f)
log.Info("table has uuid", t.GetUuid())
})
return insertWin
}
func addWindowPB(win *gadgets.GenericWindow, pb *gitpb.Repos) *gitpb.ReposTable {
t := pb.NewTable("testForgeRepos")
t.NewUuid()
tbox := win.Bottom.Box().SetProgName("TBOX")
t.SetParent(tbox)
sf := t.AddStringFunc("repo", func(r *gitpb.Repo) string {
return r.GetGoPath()
})
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()
// hmm := t.AddMasterVersion()
// hmm.SetTitle("Master")
t.AddDevelVersion()
t.AddUserVersion()
t.AddCurrentBranchName()
t.AddState()
f := func(repo *gitpb.Repo) string {
log.Info("repo =", repo.GetGoPath(), repo.GetCurrentVersion())
return repo.GetGoPath()
}
t.AddButtonFunc("cur version", f)
t.ShowTable()
return t
}