// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 package main // checks that repos are in a "normal" state import ( "errors" "fmt" "path/filepath" "strings" "time" "go.wit.com/lib/config" "go.wit.com/lib/env" "go.wit.com/lib/fhelp" "go.wit.com/lib/gui/shell" "go.wit.com/lib/protobuf/forgepb" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" "google.golang.org/protobuf/proto" ) // when in normal and requestiong "normal" again, do more stuff func doNormalAll() (string, error) { var s string var err error me.curpatches = forgepb.NewPatches() me.curpatches.Filename = config.MakeCacheFilename("forge", "curpatches") if err := me.curpatches.Load(); err != nil { panic("no file") } psets := forgepb.NewSets() newpb, _, _ := psets.HttpPostVerbose(myServer(), "get") footer, err := doPatchGet(newpb) if footer == "" { log.Info(footer) } // forces patching to be done in 'NORMAL' mode // forge is too new to be able to handle anything else if !isPatchingSafe() { return "not safe", errors.New("not safe to work on patches") } s, err = doPatchProcess() return s, err } func (a *args) DoNormal() (string, error) { env.Set("modeDir", "jwc") env.Save() panic("donormal. yay") return doNormalAttempt() } // try to switch to "Normal" mode func doNormalAttempt() (string, error) { start := time.Now() err := me.forge.DoAllCheckoutUser(argv.Force) me.forge.RescanRepos() // looks for new dirs, checks existing repos for changes dur := time.Since(start) log.Printf("Checked out %d user braches in %s\n", me.forge.Repos.Len(), shell.FormatDuration(dur)) if err != nil { return "not everything is 'normal' yet", err } me.forge.SetMode(forgepb.ForgeMode_NORMAL) log.Info("normal mode on") return "normal mode on", nil } // a quick check run each time when in "normal" mode func doNormalStatus() bool { me.forge.CheckDirtyQuiet() var count int stats := me.forge.RillRepos(checkNormalRepoState) notnormal := gitpb.NewRepos() for path, stat := range stats { dur := stat.End.Sub(stat.Start) if dur > 10*time.Second { log.Infof("%s checkNormalRepoState() took a long time (%s)\n", path, shell.FormatDuration(dur)) } if stat.Err == nil { continue } repo := me.forge.Repos.FindByFullPath(path) if repo == nil { log.Info("path deleted while running?", path) continue } notnormal.Clone(repo) if stat.Err == ErrorLocalDevelBranch { if argv.Fix { bname := repo.GetDevelBranchName() s := fmt.Sprintf("repair the %s branch on %s", bname, repo.FullPath) if fhelp.QuestionUser(s) { // todo, check to make sure we aren't on this branch repo.RunVerbose([]string{"git", "checkout", repo.GetMasterBranchName()}) repo.RunVerbose([]string{"git", "branch", "-D", bname}) repo.RunVerbose([]string{"git", "checkout", bname}) } } } if stat.Err == ErrorLocalMasterBranch { if argv.Fix { bname := repo.GetMasterBranchName() s := fmt.Sprintf("repair the %s branch on %s", bname, repo.FullPath) if fhelp.QuestionUser(s) { // todo, check to make sure we aren't on this branch repo.RunVerbose([]string{"git", "branch", "-D", bname}) repo.RunVerbose([]string{"git", "checkout", bname}) } } } if stat.Err == ErrorLocalBehindDevel { log.Info(path, "local branch is behind devel?") } // log.Infof("%-60s, %-60s %v %s\n", stat.Start, stat.End.String(), dur, path) // log.Infof("%-30v %s %v\n", dur, path, stat.Err) // log.Info("got path", path, stat.Err) count += 1 } if notnormal.Len() > 0 { notnormal.SortNamespace() footer := me.forge.NormalCheckTB(notnormal) log.Info("not normal:" + footer) return false } if count > 0 { log.Info("Some repos are not in a 'normal' state. error count =", count) log.Info("TODO: list the repos here. forge patch repos?") found := findWorkRepos() found.SortNamespace() if found.Len() == 0 { log.Info("you currently have no repos with patches") // return "you currently have no repos with patches", nil } footer := me.forge.PrintDefaultTB(found) log.Info("repos with patches or unsaved changes: " + footer) // return "repos with patches or unsaved changes: " + footer, nil config.SetChanged("repos", true) return false } return true } // 99% of the time, the repos during development should be on your user branch. // error out if it's not // this checks to see if a devel and user branch exist // this needs to run each time in case repos were added manually by the user // this also verifies that func checkNormalRepoState(repo *gitpb.Repo) error { var err error tmp := filepath.Join(env.Get("gopath"), repo.GetNamespace()) if tmp != repo.FullPath { log.Infof("checkNormalRepoState() %s != %s\n", repo.FullPath, tmp) if strings.HasPrefix(repo.FullPath, env.Get("gopath")) { tmp = strings.TrimPrefix(repo.FullPath, env.Get("gopath")) tmp = strings.Trim(tmp, "/") repo.Namespace = tmp err = log.Errorf("namespace set to filepath") } } else { // log.Infof("%s == %s\n", repo.FullPath, tmp) } tmp = strings.Trim(repo.Namespace, "/") if tmp != repo.Namespace { err = log.Errorf("junk in ns %s", repo.Namespace) repo.Namespace = tmp } if repo.GetMasterBranchName() == "" { me.forge.VerifyBranchNames(repo) log.Info("ABNORMAL: master branch name was blank in", repo.GetFullPath()) } if repo.GetMasterBranchName() == "" { me.forge.VerifyBranchNames(repo) err = log.Errorf("master branch name blank") } if repo.GetDevelBranchName() == "" { me.forge.VerifyBranchNames(repo) err = log.Errorf("devel branch name blank") } if repo.GetUserBranchName() == "" { me.forge.VerifyBranchNames(repo) err = log.Errorf("user branch name blank") } if repo.GetGoPath() == repo.GetNamespace() { // log.Info(repo.FullPath, "gopath == namespace", repo.GetGoPath(), repo.GetNamespace()) } else { log.Info(repo.FullPath, "gopath != namespace", repo.GetGoPath(), repo.GetNamespace()) } repo.MakeLocalDevelBranch() if !repo.VerifyRemoteAndLocalBranches(repo.GetDevelBranchName()) { return ErrorLocalDevelBranch } if !repo.VerifyRemoteAndLocalBranches(repo.GetMasterBranchName()) { return ErrorLocalMasterBranch } if repo.Tags.Master == nil { if found := repo.GetRemoteTag(repo.GetMasterBranchName()); found != nil { // log.Info("found master tag ", repo.FullPath, found) repo.Tags.Master = proto.Clone(found).(*gitpb.GitTag) config.SetChanged("repos", true) } else { log.Info("not found master tag (Reload() ?)", repo.FullPath) } } if repo.IsBranchRemote(repo.GetUserBranchName()) { if env.Verbose() { log.Info("user branch is remote:", repo.FullPath) } } // check to see if the user branch is behind the devel branch if repo.GetUserVersion() != repo.GetDevelVersion() { uver := repo.NewCompareTag(repo.GetUserBranchName()) dver := repo.NewCompareTag(repo.GetDevelBranchName()) if uver == nil { log.Info(repo.FullPath, "uver == nil") // make user here (should have already happened) return ErrorNoUserBranch } if dver == nil { log.Info(repo.FullPath, "dver == nil") // make dev here (should have already happened) return ErrorNoDevelBranch } /* // THIS IS WRONG LOGIC if len(dver.GreaterThan(uver)) == 0 { log.Info(repo.FullPath, "usr < dev") repo.State = "usr < dev" // check if nothing new exists in user, then delete return ErrorLocalBehindDevel } else { repo.State = "normal" } */ // everything is fine } if repo.GetCurrentBranchName() != repo.GetUserBranchName() { log.Infof("changing to user(%s) branch: %s\n", repo.GetUserBranchName(), repo.FullPath) repo.CheckoutUser() repo.ReloadCheck() err = log.Errorf("now on user branch") } // hopefully this can be deprecated soon if repo.Namespace != repo.GetGoPath() { log.Info(repo.Namespace, repo.GetGoPath()) } // verify read-only if me.forge.Config.IsReadOnly(repo.GetNamespace()) != repo.GetReadOnly() { repo.ReadOnly = me.forge.Config.IsReadOnly(repo.GetNamespace()) log.Info("readonly bit wrong", repo.FullPath) err = log.Errorf("readonly bit wrong") } return err }