github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/build/golang/golang.go (about) 1 package golang 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "os/exec" 11 "path/filepath" 12 13 "github.com/tickoalcantara12/micro/v3/service/build" 14 "github.com/tickoalcantara12/micro/v3/service/build/util/tar" 15 "github.com/tickoalcantara12/micro/v3/service/build/util/zip" 16 ) 17 18 // NewBuilder returns a golang build which can build a go binary given some source 19 func NewBuilder() (build.Builder, error) { 20 path, err := locateGo() 21 if err != nil { 22 return nil, fmt.Errorf("Error locating go binary: %v", err) 23 } 24 25 return &golang{ 26 cmdPath: path, 27 tmpDir: os.TempDir(), 28 }, nil 29 } 30 31 type golang struct { 32 cmdPath string 33 tmpDir string 34 } 35 36 // Build a binary using source. If an archive was used, e.g. tar, this should be specified in the 37 // options. If no archive option is passed, the build will treat the source as a single file. 38 func (g *golang) Build(src io.Reader, opts ...build.Option) (io.Reader, error) { 39 // parse the options 40 var options build.Options 41 for _, o := range opts { 42 o(&options) 43 } 44 45 // create a tmp dir to contain the source 46 dir, err := ioutil.TempDir(g.tmpDir, "src") 47 if err != nil { 48 return nil, err 49 } 50 defer os.RemoveAll(dir) 51 52 // decode the source and write to the tmp directory 53 switch options.Archive { 54 case "": 55 err = writeFile(src, dir) 56 case "tar": 57 err = tar.Unarchive(src, dir) 58 case "zip": 59 err = zip.Unarchive(src, dir) 60 default: 61 return nil, errors.New("Invalid Archive") 62 } 63 if err != nil { 64 return nil, err 65 } 66 67 // check for vendor directory before setting mod to vendor. the vendor directory wasn't uploaded 68 // in early v3 betas so this enables backwards compatability 69 args := []string{"build", "-o", "micro_build"} 70 if _, err := os.Stat(filepath.Join(dir, "vendor")); err == nil { 71 args = append(args, "-mod", "vendor") 72 } 73 74 // build the binary 75 cmd := exec.Command(g.cmdPath, append(args, ".")...) 76 cmd.Env = append(os.Environ(), "GO111MODULE=auto") 77 cmd.Dir = filepath.Join(dir, options.Entrypoint) 78 79 outp := bytes.NewBuffer(nil) 80 cmd.Stderr = outp 81 82 if err := cmd.Run(); err != nil { 83 return nil, fmt.Errorf("%v: %v", err, outp.String()) 84 } 85 86 // read the bytes from the file 87 dst, err := ioutil.ReadFile(filepath.Join(cmd.Dir, "micro_build")) 88 if err != nil { 89 return nil, err 90 } 91 92 return bytes.NewBuffer(dst), nil 93 } 94 95 // writeFile takes a single file to a directory 96 func writeFile(src io.Reader, dir string) error { 97 // copy the source to the temp file 98 bytes, err := ioutil.ReadAll(src) 99 if err != nil { 100 return err 101 } 102 103 // write the file, note: in order for the golang build to access the file, it cannot be 104 // os.ModeTemp. This is okay because we delete all the files in the tmp dir at the end of this 105 // function. 106 return ioutil.WriteFile(filepath.Join(dir, "main.go"), bytes, os.ModePerm) 107 } 108 109 // locateGo locates the go command 110 func locateGo() (string, error) { 111 if gr := os.Getenv("GOROOT"); len(gr) > 0 { 112 return filepath.Join(gr, "bin", "go"), nil 113 } 114 115 // check path 116 for _, p := range filepath.SplitList(os.Getenv("PATH")) { 117 bin := filepath.Join(p, "go") 118 if _, err := os.Stat(bin); err == nil { 119 return bin, nil 120 } 121 } 122 123 return exec.LookPath("go") 124 }