From c53da5a9a1da1b29db24d4e1ce2b294514d99ac2 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Tue, 17 Dec 2024 00:00:49 -0600 Subject: [PATCH] smarter and faster mtime logic --- age.go | 70 +++++++++++++++++++++++++++++++++ changed.go | 97 ++++++++++++++++++++++++++++++++++++++++++++++ goDep.redoGoMod.go | 24 +----------- reload.go | 39 +++++++++++++++++++ repo.new.go | 78 +++---------------------------------- repo.proto | 31 +++++++++++++++ rill.go | 15 +++++++ shell.go | 11 ------ 8 files changed, 259 insertions(+), 106 deletions(-) create mode 100644 age.go create mode 100644 changed.go create mode 100644 reload.go diff --git a/age.go b/age.go new file mode 100644 index 0000000..d70c863 --- /dev/null +++ b/age.go @@ -0,0 +1,70 @@ +package gitpb + +// functions that check the ages of files +// and track if the repo needs to be re-scanned + +import ( + "errors" + "os" + "path/filepath" + "time" + + "go.wit.com/log" +) + +func (repo *Repo) LastGitPull() (time.Time, error) { + return repo.oldMtime(".git/FETCH_HEAD") +} + +func (repo *Repo) GoSumAge() (time.Duration, error) { + var mtime time.Time + var err error + mtime, err = repo.oldMtime("go.sum") + if err == nil { + return time.Since(mtime), nil + } + mtime, err = repo.oldMtime("go.mod") + if err == nil { + return time.Since(mtime), nil + } + now := time.Now() + return time.Since(now), errors.New(repo.GoPath + " go.mod missing") +} + +func (repo *Repo) GitChanged() bool { + fullfile := filepath.Join(repo.FullPath, ".git/FETCH_HEAD") + lasttime, err := repo.LastGitPull() + if err == nil { + // if error, something is wrong, assume true + log.Info("gitpb:", fullfile, "changed") + return true + } + newtime := repo.LastPull.AsTime() + + if lasttime == newtime { + return false + } + log.Info("gitpb:", fullfile, "changed") + return true +} + +func (repo *Repo) GitPullAge() time.Duration { + lastpull, err := repo.LastGitPull() + if err == nil { + // if error, something is wrong, assume true + ltime := repo.LastPull.AsTime() + return time.Since(ltime) + } + + return time.Since(lastpull) +} + +func (repo *Repo) oldMtime(filename string) (time.Time, error) { + pathf := filepath.Join(repo.FullPath, filename) + statf, err := os.Stat(pathf) + if err == nil { + return statf.ModTime(), nil + } + log.Log(GITPBWARN, "Mtime() os.Stat() error", pathf, err) + return time.Now(), err +} diff --git a/changed.go b/changed.go new file mode 100644 index 0000000..d9d4bad --- /dev/null +++ b/changed.go @@ -0,0 +1,97 @@ +package gitpb + +// An app to submit patches for the 30 GO GUI repos + +import ( + "fmt" + "time" + + "go.wit.com/lib/gui/shell" + "go.wit.com/log" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func (repo *Repo) Mtime(fname string) *time.Time { + var fileTime *time.Time + tmp, err := repo.oldMtime(fname) + fileTime = &tmp + if err != nil { + log.Info("MTime got err", err) + return nil + } + return fileTime +} + +func (repo *Repo) changedDir() bool { + fname := ".git" + fileTime := repo.Mtime(fname) + if fileTime == nil { + // .git doesn't exist. something is wrong. rescan this repo + return true + } + pbtime := repo.Times.MtimeDir + mtime := timestamppb.New(*fileTime) + if (pbtime.Seconds == mtime.Seconds) && (pbtime.Nanos == mtime.Nanos) { + return false + } + dur := mtime.AsTime().Sub(pbtime.AsTime()) + repo.StateChange = fmt.Sprintf("%s changed %s", fname, shell.FormatDuration(dur)) + repo.Times.MtimeDir = mtime + return true +} + +func (repo *Repo) changedHead() bool { + fname := ".git/HEAD" + fileTime := repo.Mtime(fname) + if fileTime == nil { + // .git/HEAD doesn't exist. something is wrong. rescan this repo + return true + } + pbtime := repo.Times.MtimeHead + mtime := timestamppb.New(*fileTime) + if (pbtime.Seconds == mtime.Seconds) && (pbtime.Nanos == mtime.Nanos) { + return false + } + dur := mtime.AsTime().Sub(pbtime.AsTime()) + repo.StateChange = fmt.Sprintf("%s changed %s", fname, shell.FormatDuration(dur)) + repo.Times.MtimeHead = mtime + return true +} + +func (repo *Repo) changedIndex() bool { + fname := ".git/index" + fileTime := repo.Mtime(fname) + if fileTime == nil { + // .git/index doesn't exist. something is wrong. rescan this repo + return true + } + pbtime := repo.Times.MtimeIndex + mtime := timestamppb.New(*fileTime) + if (pbtime.Seconds == mtime.Seconds) && (pbtime.Nanos == mtime.Nanos) { + return false + } + dur := mtime.AsTime().Sub(pbtime.AsTime()) + repo.StateChange = fmt.Sprintf("%s changed %s", fname, shell.FormatDuration(dur)) + repo.Times.MtimeIndex = mtime + return true +} + +func (repo *Repo) RepoChanged() bool { + var changed bool + if repo.Times == nil { + repo.Times = new(GitTimes) + log.Info(repo.FullPath, "repo.Times were nil") + } + + if repo.changedHead() { + changed = true + } + if repo.changedIndex() { + changed = true + } + if repo.changedDir() { + changed = true + } + + return changed +} diff --git a/goDep.redoGoMod.go b/goDep.redoGoMod.go index 59845af..633f07f 100644 --- a/goDep.redoGoMod.go +++ b/goDep.redoGoMod.go @@ -4,7 +4,6 @@ package gitpb import ( "errors" - "time" ) // checks to see if the go.sum and go.mod files exist @@ -24,7 +23,7 @@ func (repo *Repo) ValidGoSum() error { } /* // todo: fix this - mtime, err := repo.mtime("go.mod") + mtime, err := repo.Mtime("go.mod") if err == nil { return err } @@ -38,7 +37,7 @@ func (repo *Repo) ValidGoSum() error { return errors.New("ValidGoSum() go.sum is missing") } /* - mtime, err := repo.mtime("go.sum") + mtime, err := repo.Mtime("go.sum") // todo: fix this if err == nil { return err @@ -56,22 +55,3 @@ func (repo *Repo) GoDepsLen() int { } return len(repo.GoDeps.GoDeps) } - -func (repo *Repo) LastGitPull() (time.Time, error) { - return repo.mtime(".git/FETCH_HEAD") -} - -func (repo *Repo) GoSumAge() (time.Duration, error) { - var mtime time.Time - var err error - mtime, err = repo.mtime("go.sum") - if err == nil { - return time.Since(mtime), nil - } - mtime, err = repo.mtime("go.mod") - if err == nil { - return time.Since(mtime), nil - } - now := time.Now() - return time.Since(now), errors.New(repo.GoPath + " go.mod missing") -} diff --git a/reload.go b/reload.go new file mode 100644 index 0000000..7a8c212 --- /dev/null +++ b/reload.go @@ -0,0 +1,39 @@ +package gitpb + +func (repo *Repo) Reload() error { + repo.Tags = new(GitTags) + repo.UpdateGitTags() + repo.GoDeps = new(GoDeps) + repo.ParseGoSum() + + if repo.GoInfo != nil { + repo.ReloadGo() + } + return nil +} + +func (repo *Repo) ReloadGo() error { + repo.GoPlugin = false + repo.GoProtobuf = false + repo.GoLibrary = false + repo.GoBinary = false + switch repo.goListRepoType() { + case "plugin": + repo.GoPlugin = true + case "protobuf": + repo.GoProtobuf = true + case "library": + repo.GoLibrary = true + case "binary": + repo.GoBinary = true + } + return nil +} + +func (repo *Repo) SetDevelBranchName(bname string) { + repo.DevelBranchName = bname +} + +func (repo *Repo) SetUserBranchName(bname string) { + repo.UserBranchName = bname +} diff --git a/repo.new.go b/repo.new.go index fa1d258..10b5fcb 100644 --- a/repo.new.go +++ b/repo.new.go @@ -2,12 +2,8 @@ package gitpb import ( "errors" - "os" - "path/filepath" - "time" "go.wit.com/log" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) // scans in a new git repo. If it detects the repo is a golang project, @@ -15,52 +11,24 @@ import ( // TODO: try adding python, rails, perl, rust, other language things? // I probably will never have time to try that, but I'd take patches for anyone // that might see this note and feel so inclined. -func (all *Repos) NewGoPath(basepath string, gopath string, url string) (*Repo, error) { +// todo: use Repos.Lock() ? +func (all *Repos) NewGoRepo(fullpath string, gopath string) (*Repo, error) { if gopath == "" { return nil, errors.New("blank gopath") } if r := all.FindByGoPath(gopath); r != nil { log.Info("gitpb.NewGoPath() already has gopath", r.GoPath) log.Info("gitpb.NewGoPath() already has FullPath", r.FullPath) - log.Info("gitpb.NewGoPath() already has URL", r.URL) // already had this gopath return r, errors.New("gitpb.NewGoPath() duplicate gopath " + gopath) } - log.Info("gitpb.NewGoPath() Attempting to add new path", basepath, gopath) - - // if .git doesn't exist, error out here - gitpath := filepath.Join(basepath, gopath, ".git") - _, err := os.Stat(gitpath) - if err != nil { - log.Warn("gitpb.NewGoPath() not a git directory", gitpath) - return nil, err - } // add a new one here newr := Repo{ - FullPath: filepath.Join(basepath, gopath), - GoPath: gopath, - URL: url, - } - newr.Tags = new(GitTags) - newr.UpdateGitTags() - newr.GoDeps = new(GoDeps) - - switch newr.goListRepoType() { - case "plugin": - newr.GoPlugin = true - case "protobuf": - newr.GoProtobuf = true - case "library": - newr.GoLibrary = true - case "binary": - newr.GoBinary = true - } - - lastpull, err := newr.LastGitPull() - if err == nil { - newr.LastPull = timestamppb.New(lastpull) + FullPath: fullpath, } + newr.GoInfo = new(GoInfo) + newr.GoInfo.GoPath = gopath if all.AppendUniqueGoPath(&newr) { // worked @@ -75,39 +43,3 @@ func (all *Repos) NewGoPath(basepath string, gopath string, url string) (*Repo, // todo: use Repos.Lock() return nil, errors.New("repo gitpb.NewGoPath() should never have gotten here " + gopath) } - -func (repo *Repo) SetDevelBranchName(bname string) { - repo.DevelBranchName = bname -} - -func (repo *Repo) SetUserBranchName(bname string) { - repo.UserBranchName = bname -} - -func (repo *Repo) GitChanged() bool { - fullfile := filepath.Join(repo.FullPath, ".git/FETCH_HEAD") - lasttime, err := repo.LastGitPull() - if err == nil { - // if error, something is wrong, assume true - log.Info("gitpb:", fullfile, "changed") - return true - } - newtime := repo.LastPull.AsTime() - - if lasttime == newtime { - return false - } - log.Info("gitpb:", fullfile, "changed") - return true -} - -func (repo *Repo) GitPullAge() time.Duration { - lastpull, err := repo.LastGitPull() - if err == nil { - // if error, something is wrong, assume true - ltime := repo.LastPull.AsTime() - return time.Since(ltime) - } - - return time.Since(lastpull) -} diff --git a/repo.proto b/repo.proto index 840804c..71f3010 100644 --- a/repo.proto +++ b/repo.proto @@ -34,6 +34,11 @@ message Repo { // `autogenpb:marshal` string desc = 20; // what is this repo? bytes goMod = 21; // the last go.mod file bytes goSum = 22; // the last go.sum file + google.protobuf.Timestamp mtimeGitDir = 23; // mtime for ./git + google.protobuf.Timestamp mtimeGitHead = 24; // mtime for ./git/HEAD // these two mtimes allow really fast checks to see if git has changed + GitTimes times = 25; // store all the mtime values here. these are temporary + GoInfo goInfo = 26; // put all the go specifcs here + string stateChange = 27; // reason for state change } message Repos { // `autogenpb:marshal` @@ -41,3 +46,29 @@ message Repos { // `autogenpb:marshal` string version = 2; // maybe can be used for protobuf schema change violations repeated Repo repos = 3; } + +// should it be done this way? +message GitTimes { + google.protobuf.Timestamp lastPull = 1; // last time a git pull was done + google.protobuf.Timestamp lastUpdate = 2; // when was ReloadGit() last done + google.protobuf.Timestamp lastDirty = 3; // last time CheckDirty() was run + google.protobuf.Timestamp mtimeDir = 4; // mtime for ./git // maybe useful to track + google.protobuf.Timestamp mtimeHead = 5; // mtime for ./git/HEAD // these two mtimes allow really fast checks to see if git has changed + google.protobuf.Timestamp mtimeIndex = 6; // mtime for ./git/HEAD // probably always in sync with HEAD + google.protobuf.Timestamp mtimeFetch = 7; // mtime for ./git/FETCH_HEAD // last time 'git fetch' or 'git pull' was run on current branch? +} + +// this is probably better. think about moving to this instead +message GoInfo { + string goPath = 1; // the logical path as used by golang: 'go.wit.com/apps/helloworld' + string desc = 2; // what is this repo? + bool goLibrary = 3; // is this a golang library? + bool goBinary = 4; // is this a golang binary? + bool goPrimitive = 5; // if this is a golang primitive (only has go.mod) + bool goPlugin = 6; // is this a golang plugin? + bool goProtobuf = 7; // autogen go files from .proto + GoDeps goDeps = 8; // what is in the go.sum file + GoDeps published = 9; // the last published go.mod/go.sum + bytes goMod = 10; // the last go.mod file + bytes goSum = 11; // the last go.sum file +} diff --git a/rill.go b/rill.go index 142b201..7b4bb87 100644 --- a/rill.go +++ b/rill.go @@ -102,3 +102,18 @@ func (all *Repos) RillGitPull(part1 int, part2 int) map[*Repo]cmd.Status { return allerr } + +func (repo *Repo) GitPullRealtime() cmd.Status { + currentName := repo.GetCurrentBranchName() + if repo.IsOnlyLocalTag(currentName) { + var result cmd.Status + result.Exit = 21 + result.Error = ErrorGitPullOnLocal + // log.Info("git pull skipped on local only branch", repo.GoPath) + return result + } + var cmd []string + cmd = append(cmd, "git", "pull") + r := repo.RunRealtime(cmd) + return r +} diff --git a/shell.go b/shell.go index efb682d..3d15eee 100644 --- a/shell.go +++ b/shell.go @@ -6,7 +6,6 @@ import ( "os" "path/filepath" "strings" - "time" "github.com/go-cmd/cmd" "go.wit.com/lib/gui/shell" @@ -109,13 +108,3 @@ func (repo *Repo) IsDirectory() bool { } return info.IsDir() } - -func (repo *Repo) mtime(filename string) (time.Time, error) { - pathf := filepath.Join(repo.FullPath, filename) - statf, err := os.Stat(pathf) - if err == nil { - return statf.ModTime(), nil - } - log.Log(GITPB, "mtime() os.Stat() error", pathf, err) - return time.Now(), err -}