package gitpb // An app to submit patches for the 30 GO GUI repos import ( "errors" "fmt" "os" "path/filepath" "time" "go.wit.com/lib/cobol" "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.statMtime(fname) fileTime = &tmp if err != nil { log.Info("MTime got err", err) return nil } return fileTime } func (repo *Repo) statMtime(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(WARN, "Mtime() os.Stat() error", pathf, err) return time.Now(), err } 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 } mtime := timestamppb.New(*fileTime) pbtime := repo.Times.MtimeDir if pbtime == nil { // this can happen? repo.Times.MtimeDir = mtime return true } 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, cobol.FormatDuration(dur)) repo.Times.MtimeDir = mtime return true } func (repo *Repo) didFileChange(fname string, pbtime *timestamppb.Timestamp) bool { fileTime := repo.Mtime(fname) if fileTime == nil { repo.StateChange = fmt.Sprintf("%s missing", fname) return true } mtime := timestamppb.New(*fileTime) if pbtime == nil { repo.StateChange = fmt.Sprintf("%s mtime never recorded", fname) return true } if (pbtime.Seconds == mtime.Seconds) && (pbtime.Nanos == mtime.Nanos) { // it's the same! return false } dur := mtime.AsTime().Sub(pbtime.AsTime()) repo.StateChange = fmt.Sprintf("%s mtime changed %s", fname, cobol.FormatDuration(dur)) // need to reload from the filesystem return true } // boo. I'm not good at golang. this should use reflect. I'm bad. my code is bad. boo this man. you're cool, I'm outta here // make this work right someday func (repo *Repo) updateMtime(fname string, pbname string) bool { fileTime := repo.Mtime(fname) if fileTime == nil { // .git/HEAD doesn't exist. something is wrong. rescan this repo return true } mtime := timestamppb.New(*fileTime) pbtime := repo.Times.MtimeHead if pbtime == nil { // this can happen? repo.Times.MtimeHead = mtime return true } switch pbname { case "MtimeHead": if pbtime == nil { // this can happen? repo.Times.MtimeHead = mtime return true } default: } 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, cobol.FormatDuration(dur)) repo.Times.MtimeHead = 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 } mtime := timestamppb.New(*fileTime) pbtime := repo.Times.MtimeHead if pbtime == nil { // this can happen? repo.Times.MtimeHead = mtime return true } 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, cobol.FormatDuration(dur)) repo.Times.MtimeHead = mtime return true } // check the mtime of the .git/config file func (repo *Repo) changedConfig() bool { fname := ".git/config" fileTime := repo.Mtime(fname) if fileTime == nil { // .git/config doesn't exist. something is wrong! log.Info("gitpb .git/config is missing", repo.GetGoPath()) return false } mtime := timestamppb.New(*fileTime) pbtime := repo.Times.MtimeConfig if pbtime == nil { // this can happen? repo.Times.MtimeConfig = mtime return true } 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, cobol.FormatDuration(dur)) repo.Times.MtimeConfig = 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 } mtime := timestamppb.New(*fileTime) pbtime := repo.Times.MtimeIndex if pbtime == nil { // this can happen? repo.Times.MtimeIndex = mtime return true } 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, cobol.FormatDuration(dur)) repo.Times.MtimeIndex = mtime return true } func (repo *Repo) reloadMtimes() 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.changedConfig() { changed = true } if repo.changedDir() { // changed = true } return changed } // also checks .git which seems to change *a lot* // this might work immediately after git checkout func (repo *Repo) DidRepoChangeDir() error { if repo.Times == nil { repo.Times = new(GitTimes) } if repo.didFileChange(".git/HEAD", repo.Times.MtimeHead) { return log.Errorf(".git/HEAD changed") } if repo.didFileChange(".git/index", repo.Times.MtimeIndex) { repo.StateChange += "jwc" return log.Errorf(".git/index changed") } if repo.didFileChange(".git/config", repo.Times.MtimeConfig) { return log.Errorf(".git/config changed") } if repo.didFileChange(".git", repo.Times.MtimeDir) { // todo: do something with CheckDirty() return log.Errorf(".git changed") } if repo.Times.LastUpdate == nil { log.Info("repo.Reload() was never run") return log.Errorf("Reload() ran for the first time") } else { if repo.Times.LastUpdate.Seconds < repo.Times.MtimeHead.Seconds { log.Info("SHOULD RUN Reload() here", repo.Times.MtimeHead.Seconds-repo.Times.LastUpdate.Seconds, "secs diff") return log.Errorf("Reload() time skew on .git/HEAD") } } // log.Info("DidRepoChange() is false", repo.FullPath) return nil } func (repo *Repo) DidRepoChange() error { if repo.Times == nil { repo.Times = new(GitTimes) } if repo.didFileChange(".git/HEAD", repo.Times.MtimeHead) { return log.Errorf(".git/HEAD changed") } if repo.didFileChange(".git/index", repo.Times.MtimeIndex) { repo.StateChange += "jwc2" return log.Errorf(".git/index changed") } if repo.didFileChange(".git/config", repo.Times.MtimeConfig) { return log.Errorf(".git/config changed") } if repo.didFileChange(".git", repo.Times.MtimeDir) { // todo: do something with CheckDirty() // return true } if repo.Times.LastUpdate == nil { log.Info("repo.Reload() was never run") return log.Errorf("Reload() ran for the first time") } else { if repo.Times.LastUpdate.Seconds < repo.Times.MtimeHead.Seconds { log.Info("SHOULD RUN Reload() here", repo.Times.MtimeHead.Seconds-repo.Times.LastUpdate.Seconds, "secs diff") return log.Errorf("Reload() time skew on .git/HEAD") } } // log.Info("DidRepoChange() is false", repo.FullPath) return nil } func (all *Repos) ScanAllMtimesVerbose() (string, error) { var count int var anyerr error for r := range all.IterAll() { var err error var ok bool if count < 10 { // only show output for the first 10 repos ok, err = r.didRepoChangeVerbose() } else { err = r.DidRepoChange() } if err != nil { log.Info(r.Namespace, err) } if (err != nil) || ok { // means there was output anyerr = err count += 1 } } if anyerr != nil { all.Save() } return "looked into mtime changes", anyerr } func (r *Repo) didRepoChangeVerbose() (bool, error) { var output bool if r.Times == nil { r.Times = new(GitTimes) log.Info("repo.Reload() was never run") output = true // means this is going to populate the mtimes for the first time // todo: make sure the whole thing gets saved } else { if r.Times.LastUpdate != nil { lastupdate := r.Times.LastUpdate.AsTime() dur := time.Since(lastupdate) if dur < 5*time.Minute { log.Info("repo.ReloadMtime() was was last run", cobol.FormatDuration(dur), r.Namespace) output = true } } else { log.Info("repo.ReloadMtime() LastUpdate == nil", r.Namespace) output = true } } if r.didFileChange(".git/HEAD", r.Times.MtimeHead) { r.RunVerbose([]string{"ls", "-l", ".git/HEAD"}) output = true return output, errors.New(".git/HEAD changed") } if r.didFileChange(".git/index", r.Times.MtimeIndex) { r.StateChange += "jwc2" r.RunVerbose([]string{"ls", "-l", ".git/index"}) output = true return output, errors.New(".git/index changed") } if r.didFileChange(".git/config", r.Times.MtimeConfig) { r.RunVerbose([]string{"ls", "-l", ".git/config"}) output = true return output, errors.New(".git/config changed") } if r.didFileChange(".git", r.Times.MtimeDir) { if r.Times.MtimeDir != nil { lastupdate := r.Times.LastUpdate.AsTime() dur := time.Since(lastupdate) if dur < 5*time.Minute { log.Info("repo.ReloadMtime() MtimeDir last run", cobol.FormatDuration(dur), r.Namespace) r.RunVerbose([]string{"ls", "-l", ".git", "-d"}) output = true } } else { } // todo: do something with CheckDirty() // return true } if r.Times.LastUpdate == nil { log.Info("r.Reload() was never run") output = true return output, errors.New("Reload() ran for the first time") } else { if r.Times.LastUpdate.Seconds < r.Times.MtimeHead.Seconds { log.Info("SHOULD RUN Reload() here", r.Times.MtimeHead.Seconds-r.Times.LastUpdate.Seconds, "secs diff") output = true return output, errors.New("Reload() time skew on .git/HEAD") } } // nothing changed. All mtimes being monitored are old return output, nil }