| package main |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "errors" |
| "fmt" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "runtime" |
| "strings" |
| ) |
| |
| type GoModDownloadResult struct { |
| Dir string |
| Sum string |
| Error string |
| } |
| |
| func findGoPath() string { |
| // Locate the go binary. If GOROOT is set, we'll use that one; otherwise, |
| // we'll use PATH. |
| goPath := "go" |
| if runtime.GOOS == "windows" { |
| goPath += ".exe" |
| } |
| if goroot, ok := os.LookupEnv("GOROOT"); ok { |
| goPath = filepath.Join(goroot, "bin", goPath) |
| } |
| return goPath |
| } |
| |
| func runGoModDownload(dl *GoModDownloadResult, dest string, importpath string, version string) error { |
| buf := bytes.NewBuffer(nil) |
| bufErr := bytes.NewBuffer(nil) |
| cmd := exec.Command(findGoPath(), "mod", "download", "-json", "-modcacherw") |
| cmd.Dir = dest |
| |
| if version != "" && importpath != "" { |
| cmd.Args = append(cmd.Args, importpath+"@"+version) |
| } |
| |
| cmd.Stdout = buf |
| cmd.Stderr = bufErr |
| dlErr := cmd.Run() |
| if dlErr != nil { |
| if _, ok := dlErr.(*exec.ExitError); !ok { |
| if bufErr.Len() > 0 { |
| return fmt.Errorf("go mod download exec error: %s %q: %s, %w", cmd.Path, strings.Join(cmd.Args, " "), bufErr.String(), dlErr) |
| } |
| return fmt.Errorf("go mod download exec error: %s %s: %v", cmd.Path, strings.Join(cmd.Args, " "), dlErr) |
| } |
| } |
| |
| // Parse the JSON output. |
| if err := json.Unmarshal(buf.Bytes(), &dl); err != nil { |
| if bufErr.Len() > 0 { |
| return fmt.Errorf("go mod download output format: `%s %s`: parsing JSON: %q stderr: %q error: %w", cmd.Path, strings.Join(cmd.Args, " "), buf.String(), bufErr.String(), err) |
| } |
| return fmt.Errorf("go mod download output format: `%s %s`: parsing JSON: %q error: %w", cmd.Path, strings.Join(cmd.Args, " "), buf.String(), err) |
| } |
| if dl.Error != "" { |
| return Join(errors.New(dl.Error), dlErr) |
| } |
| if dlErr != nil { |
| return dlErr |
| } |
| return nil |
| } |