package main import ( "errors" "fmt" "os" "path/filepath" "strings" "go.wit.com/dev/alexflint/arg" "go.wit.com/lib/protobuf/forgepb" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" ) // sent via -ldflags var VERSION string var BUILDTIME string var pp *arg.Parser var forge *forgepb.Forge var check *gitpb.Repo func main() { log.Info("go-mod-clean version", VERSION, "built on", BUILDTIME) pp = arg.MustParse(&argv) // load the ~/.config/forge/ config // this lets you configure repos you have read/write access too forge = forgepb.Init() // figure out what directory we are running in check = findPwdRepo() if check == nil { log.Info("this directory isn't in a golang project (not in ~/go/src nor a go.work file)") os.Exit(-1) } if err := check.ValidGoSum(); err == nil { okExit("go.mod and go.sum are already valid") } // skip restore if --force if !argv.Force { // try to restore from the git metadata if restoreFromGit(check) { okExit("go.mod was restored from the git notes") } } if check.GetMasterBranchName() != check.GetCurrentBranchName() { log.Info("") log.Info("You can only run go-mod-clean on a git master branch.") log.Info("Publishing go.mod & go.sum files must come from from git version tag") log.Info("Anything else doesn't make sense.") log.Info("") badExit(errors.New("not git master branch")) } // re-create go.sum and go.mod if _, err := redoGoMod(check); err != nil { badExit(err) } if argv.Trim { // try to trim junk if err := trimGoSum(check); err != nil { badExit(err) } } // check go.sum file if err := cleanGoDepsCheckOk(check); err != nil { log.Info("forge.FinalGoDepsCheck() failed. boo. :", check.GoPath) badExit(err) } // put the files in the notes section in git // this way, git commits are not messed up // with this autogenerated code if err := saveAsMetadata(check); err != nil { log.Info("save go.mod as git metadata failed", check.GoPath, err) badExit(err) } log.Info("forge.FinalGoDepsCheck() worked :", check.GoPath) okExit(check.GoPath + " go.sum seems clean") } func findPwdRepo() *gitpb.Repo { var check *gitpb.Repo // attempt to use the working directory // this is probably what happens most of the time pwd, _ := os.Getwd() if strings.HasPrefix(pwd, forge.GetGoSrc()) { gopath := strings.TrimPrefix(pwd, forge.GetGoSrc()) gopath = strings.Trim(gopath, "/") log.Info("findRepo() trying gopath", gopath) check = forge.Repos.FindByGoPath(gopath) if check != nil { log.Info("findRepo() worked", check.GoPath) return check } } return nil } func okExit(msg string) { log.Info("exit() go-mod-clean on", check.GetGoPath(), "ok") log.DaemonMode(true) log.Info(msg) os.Exit(0) } func badExit(err error) { log.DaemonMode(true) log.Info("go-mod-clean failed: ", err, forge.GetGoSrc()) os.Exit(-1) } // todo: do this the right way in git func saveAsMetadata(repo *gitpb.Repo) error { cname := check.GetCurrentBranchName() cmd := []string{"git", "notes", "remove", cname} if err := check.StrictRun(cmd); err != nil { return err } cmd = []string{"git", "notes", "add", "-m", "// `autogen:go.mod`", cname} if err := check.StrictRun(cmd); err != nil { return err } if check.GoPrimitive { cmd = []string{"git", "notes", "append", "-F", "go.mod", cname} if err := check.StrictRun(cmd); err != nil { return err } } else { cmd = []string{"git", "notes", "append", "-F", "go.mod", cname} if err := check.StrictRun(cmd); err != nil { return err } cmd = []string{"git", "notes", "append", "-m", "// `autogen:go.sum`", cname} if err := check.StrictRun(cmd); err != nil { return err } cmd = []string{"git", "notes", "append", "-F", "go.sum", cname} if err := check.StrictRun(cmd); err != nil { return err } } return nil } func restoreFromGit(repo *gitpb.Repo) bool { result := repo.Run([]string{"git", "notes", "show"}) if result.Exit != 0 { return false } if result.Error != nil { return false } if len(result.Stdout) == 0 { return false } var newf *os.File var body string for _, line := range result.Stdout { if strings.HasPrefix(line, "// `autogen:") { if newf != nil { fmt.Fprintln(newf, strings.TrimSpace(body)) newf.Close() newf = nil body = "" } fbase := strings.TrimPrefix(line, "// `autogen:") fbase = strings.TrimSpace(fbase) fbase = strings.TrimSuffix(fbase, "`") // if line == // `autogen:` , then the filename is blank if fbase != "" { fname := filepath.Join(filepath.Join(check.FullPath, fbase)) newf, _ = os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) } continue } body += line + "\n" } if newf != nil { fmt.Fprintln(newf, strings.TrimSpace(body)) newf.Close() } return true }