package gitpb // does processing on the go.mod and go.sum files import ( "bufio" "errors" "os" "path/filepath" "strings" "go.wit.com/log" ) // poor name perhaps. It's because in most of these // repos you can also type "make redomod" to do the same thing // since it's a Makefile task that is also useful to be able to run // from the command line func (repo *Repo) RedoGoMod() (bool, error) { // unset the go development ENV var to generate release files os.Unsetenv("GO111MODULE") if ok, err := repo.strictRun([]string{"rm", "-f", "go.mod", "go.sum"}); !ok { log.Warn("rm go.mod go.sum failed", err) return ok, err } if ok, err := repo.strictRun([]string{"go", "mod", "init", repo.GoPath}); !ok { log.Warn("go mod init failed", err) return ok, err } if ok, err := repo.strictRun([]string{"go", "mod", "tidy"}); !ok { log.Warn("go mod tidy failed", err) return ok, err } // most things should build with golang after 1.20 if ok, err := repo.strictRun([]string{"go", "mod", "edit", "-go=1.20"}); !ok { log.Warn("go mod edit failed", err) return ok, err } // log.Info("MakeRedomod() worked", repo.GoPath) if repo.Exists("go.sum") { // return the attempt to parse go.mod & go.sum return repo.parseGoSum() } repo.GoDeps = new(GoDeps) repo.GoPrimitive = false ok, err := repo.isPrimativeGoMod() if err != nil { // this means this repo does not depend on any other package log.Info("PRIMATIVE repo error:", repo.GoPath, "err =", err) return false, err } if ok { // this means the repo is primitive so there is no go.sum repo.GoPrimitive = true return true, nil } // this should never happen return false, errors.New("MakeRedomod() logic failed") } // reads and parses the go.sum file func (repo *Repo) parseGoSum() (bool, error) { // empty out what was there before repo.GoDeps = nil tmp := filepath.Join(repo.FullPath, "go.sum") gosum, err := os.Open(tmp) if err != nil { log.Warn("missing go.sum", repo.FullPath) return false, err } defer gosum.Close() scanner := bufio.NewScanner(gosum) log.Info("gosum:", tmp) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) parts := strings.Split(line, " ") if len(parts) == 3 { godep := strings.TrimSpace(parts[0]) version := strings.TrimSpace(parts[1]) if strings.HasSuffix(version, "/go.mod") { version = strings.TrimSuffix(version, "/go.mod") } new1 := GoDep{ GoPath: godep, Version: version, } if repo.GoDeps == nil { repo.GoDeps = new(GoDeps) } repo.GoDeps.AppendUniqueGoPath(&new1) /* found := repo.FindGoDepByPath(godep) if found == nil { currentversion, ok := deps[godep] if ok { // only use the first value found in the file? // this shouldn't have been possible. this function should // only be called from MakeRedomod() // todo: make go things a seperate package so this function // isn't exported? if version != currentversion { log.Warn("\tgo.sum ", godep, "had both", version, currentversion) } } else { deps[godep] = version log.Info("\t", godep, "=", version) } */ } else { // I've never seen this happen yet panic(errors.New("go.sum invalid: " + line)) // return false, errors.New("go.sum invalid: " + line) } } if err := scanner.Err(); err != nil { repo.GoDeps = nil return false, err } return true, nil } func (repo *Repo) RepoType() string { if repo == nil { return "nil" } if repo.GetGoPlugin() { return "plugin" } if repo.GetGoBinary() { return "binary" } if ok, _, _ := repo.IsProtobuf(); ok { return "protobuf" } if repo.GetGoLibrary() { return "library" } return "" } func (repo *Repo) goListRepoType() string { os.Setenv("GO111MODULE", "off") cmd := []string{"go", "list", "-f", "'{{if eq .Name \"main\"}}binary{{else}}library{{end}}'"} // cmd := []string{"go", "list", "-f", "'{{.Name}}'"} // probably use this. this just prints out the package name // cmd := []string{"go", "list", "-f", "'{{.ImportPath}}'"} // returns go.wit.com/lib/protobuf/gitpb result := repo.RunQuiet(cmd) if result.Error != nil { log.Warn("go list binary detect failed", result.Error) return "" } output := strings.TrimSpace(strings.Join(result.Stdout, "\n")) output = strings.Trim(output, "'") return output } // reads and parses the go.sum file func (repo *Repo) UpdatePublished() (bool, error) { // empty out what was there before repo.Published = nil tmp := filepath.Join(repo.FullPath, "go.sum") gosum, err := os.Open(tmp) if err != nil { log.Warn("missing go.sum", repo.FullPath) return false, err } defer gosum.Close() scanner := bufio.NewScanner(gosum) log.Info("gosum:", tmp) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) parts := strings.Split(line, " ") if len(parts) == 3 { godep := strings.TrimSpace(parts[0]) version := strings.TrimSpace(parts[1]) if strings.HasSuffix(version, "/go.mod") { version = strings.TrimSuffix(version, "/go.mod") } new1 := GoDep{ GoPath: godep, Version: version, } if repo.Published == nil { repo.Published = new(GoDeps) } repo.Published.AppendUniqueGoPath(&new1) /* found := repo.FindGoDepByPath(godep) if found == nil { currentversion, ok := deps[godep] if ok { // only use the first value found in the file? // this shouldn't have been possible. this function should // only be called from MakeRedomod() // todo: make go things a seperate package so this function // isn't exported? if version != currentversion { log.Warn("\tgo.sum ", godep, "had both", version, currentversion) } } else { deps[godep] = version log.Info("\t", godep, "=", version) } */ } else { // I've never seen this happen yet panic(errors.New("go.sum invalid: " + line)) // return false, errors.New("go.sum invalid: " + line) } } if err := scanner.Err(); err != nil { repo.Published = nil return false, err } return true, nil } // returns true if the last published func (repo *Repo) GoDepsLen() int { if repo.GoDeps == nil { return 0 } return repo.GoDeps.Len() } // returns true if the last published func (repo *Repo) PublishedLen() int { if repo.Published == nil { return 0 } return repo.Published.Len() } // returns true if the last published func (all *Repos) GoDepsChanged(repo *Repo) (bool, error) { var upgrade bool = false if repo.GoDeps == nil { repo.RedoGoMod() } if repo.GoDeps.Len() == 0 { repo.RedoGoMod() } log.Printf("current repo %s go dependancy count: %d", repo.GetGoPath(), repo.GoDeps.Len()) deps := repo.GoDeps.SortByGoPath() for deps.Scan() { depRepo := deps.Next() if repo.Published == nil { return false, errors.New("repo published deps info is nil") } found := repo.Published.FindByGoPath(depRepo.GetGoPath()) if found == nil { log.Printf("dep %-50s %-10s vs %-10s", depRepo.GetGoPath(), depRepo.GetVersion(), "NEW") upgrade = true continue // return upgrade, errors.New("new repo added " + depRepo.GetGoPath()) } if depRepo.GetVersion() == found.GetVersion() { // log.Printf("deps %-50s %-10s vs %-10s", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetVersion()) } else { log.Printf("dep %-50s %-10s vs %-10s BROKEN", depRepo.GetGoPath(), depRepo.GetVersion(), found.GetVersion()) upgrade = true } } return upgrade, nil }