Add a build package (#1926)

* Add a build package

* fix go mod

* package tar
This commit is contained in:
Asim Aslam
2020-08-11 16:51:58 +01:00
committed by GitHub
parent e162e6d505
commit fae4151027
13 changed files with 298 additions and 74 deletions

32
build/build.go Normal file
View File

@@ -0,0 +1,32 @@
// Package build is for building source into a package
package build
// Build is an interface for building packages
type Build interface {
// Package builds a package
Package(name string, src *Source) (*Package, error)
// Remove removes the package
Remove(*Package) error
}
// Source is the source of a build
type Source struct {
// Path to the source if local
Path string
// Language is the language of code
Language string
// Location of the source
Repository string
}
// Package is packaged format for source
type Package struct {
// Name of the package
Name string
// Location of the package
Path string
// Type of package e.g tarball, binary, docker
Type string
// Source of the package
Source *Source
}

96
build/docker/docker.go Normal file
View File

@@ -0,0 +1,96 @@
// Package docker builds docker images
package docker
import (
"archive/tar"
"bytes"
"io/ioutil"
"os"
"path/filepath"
docker "github.com/fsouza/go-dockerclient"
"github.com/micro/go-micro/v3/build"
"github.com/micro/go-micro/v3/logger"
)
type dockerBuild struct {
Options build.Options
Client *docker.Client
}
func (d *dockerBuild) Package(name string, s *build.Source) (*build.Package, error) {
image := name
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
defer tw.Close()
dockerFile := "Dockerfile"
// open docker file
f, err := os.Open(filepath.Join(s.Path, dockerFile))
if err != nil {
return nil, err
}
// read docker file
by, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
tarHeader := &tar.Header{
Name: dockerFile,
Size: int64(len(by)),
}
err = tw.WriteHeader(tarHeader)
if err != nil {
return nil, err
}
_, err = tw.Write(by)
if err != nil {
return nil, err
}
tr := bytes.NewReader(buf.Bytes())
err = d.Client.BuildImage(docker.BuildImageOptions{
Name: image,
Dockerfile: dockerFile,
InputStream: tr,
OutputStream: ioutil.Discard,
RmTmpContainer: true,
SuppressOutput: true,
})
if err != nil {
return nil, err
}
return &build.Package{
Name: image,
Path: image,
Type: "docker",
Source: s,
}, nil
}
func (d *dockerBuild) Remove(b *build.Package) error {
return d.Client.RemoveImage(b.Name)
}
func (d *dockerBuild) String() string {
return "docker"
}
func NewBuild(opts ...build.Option) build.Build {
options := build.Options{}
for _, o := range opts {
o(&options)
}
endpoint := "unix:///var/run/docker.sock"
client, err := docker.NewClient(endpoint)
if err != nil {
logger.Fatal(err)
}
return &dockerBuild{
Options: options,
Client: client,
}
}

75
build/golang/golang.go Normal file
View File

@@ -0,0 +1,75 @@
// Package golang is a go package manager
package golang
import (
"os"
"os/exec"
"path/filepath"
"github.com/micro/go-micro/v3/build"
)
type goBuild struct {
Options build.Options
Cmd string
Path string
}
// whichGo locates the go command
func whichGo() string {
// check GOROOT
if gr := os.Getenv("GOROOT"); len(gr) > 0 {
return filepath.Join(gr, "bin", "go")
}
// check path
for _, p := range filepath.SplitList(os.Getenv("PATH")) {
bin := filepath.Join(p, "go")
if _, err := os.Stat(bin); err == nil {
return bin
}
}
// best effort
return "go"
}
func (g *goBuild) Package(name string, src *build.Source) (*build.Package, error) {
binary := filepath.Join(g.Path, name)
source := src.Path
cmd := exec.Command(g.Cmd, "build", "-o", binary, source)
if err := cmd.Run(); err != nil {
return nil, err
}
return &build.Package{
Name: name,
Path: binary,
Type: g.String(),
Source: src,
}, nil
}
func (g *goBuild) Remove(b *build.Package) error {
binary := filepath.Join(b.Path, b.Name)
return os.Remove(binary)
}
func (g *goBuild) String() string {
return "golang"
}
func NewBuild(opts ...build.Option) build.Build {
options := build.Options{
Path: os.TempDir(),
}
for _, o := range opts {
o(&options)
}
return &goBuild{
Options: options,
Cmd: whichGo(),
Path: options.Path,
}
}

15
build/options.go Normal file
View File

@@ -0,0 +1,15 @@
package build
type Options struct {
// local path to download source
Path string
}
type Option func(o *Options)
// Local path for repository
func Path(p string) Option {
return func(o *Options) {
o.Path = p
}
}

45
build/tar/tar.go Normal file
View File

@@ -0,0 +1,45 @@
// Package tar basically tarballs source code
package tar
import (
"os"
"path/filepath"
"github.com/micro/go-micro/v3/build"
)
type tarBuild struct{}
func (t *tarBuild) Package(name string, src *build.Source) (*build.Package, error) {
pkg := name + ".tar.gz"
// path to the tarball
path := filepath.Join(os.TempDir(), src.Path, pkg)
// create a temp directory
if err := os.MkdirAll(filepath.Join(os.TempDir(), src.Path), 0755); err != nil {
return nil, err
}
if err := Compress(src.Path, path); err != nil {
return nil, err
}
return &build.Package{
Name: name,
Path: path,
Type: t.String(),
Source: src,
}, nil
}
func (t *tarBuild) Remove(b *build.Package) error {
return os.Remove(b.Path)
}
func (t *tarBuild) String() string {
return "tar.gz"
}
func NewBuild(opts ...build.Option) build.Build {
return new(tarBuild)
}

92
build/tar/util.go Normal file
View File

@@ -0,0 +1,92 @@
package tar
import (
"archive/tar"
"bytes"
"compress/gzip"
"io"
"os"
"path/filepath"
"strings"
)
func Compress(source, dest string) error {
// tar + gzip
var buf bytes.Buffer
_ = compress(source, &buf)
// write the .tar.gzip
fileToWrite, err := os.OpenFile(dest, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return err
}
_, err = io.Copy(fileToWrite, &buf)
return err
}
func compress(src string, buf io.Writer) error {
// tar > gzip > buf
zr := gzip.NewWriter(buf)
tw := tar.NewWriter(zr)
// walk through every file in the folder
filepath.Walk(src, func(file string, fi os.FileInfo, err error) error {
// generate tar header
header, err := tar.FileInfoHeader(fi, file)
if err != nil {
return err
}
// must provide real name
// (see https://golang.org/src/archive/tar/common.go?#L626)
srcWithSlash := src
if !strings.HasSuffix(src, string(filepath.Separator)) {
srcWithSlash = src + string(filepath.Separator)
}
header.Name = strings.ReplaceAll(file, srcWithSlash, "")
if header.Name == src || len(strings.TrimSpace(header.Name)) == 0 {
return nil
}
// @todo This is a quick hack to speed up whole repo uploads
// https://github.com/micro/micro/pull/956
if !fi.IsDir() && !strings.HasSuffix(header.Name, ".go") &&
!strings.HasSuffix(header.Name, ".mod") &&
!strings.HasSuffix(header.Name, ".sum") {
return nil
}
// write header
if err := tw.WriteHeader(header); err != nil {
return err
}
if fi.IsDir() {
return nil
}
// if not a dir, write file content
data, err := os.Open(file)
if err != nil {
return err
}
if _, err := io.Copy(tw, data); err != nil {
return err
}
return nil
})
// produce tar
if err := tw.Close(); err != nil {
return err
}
// produce gzip
if err := zr.Close(); err != nil {
return err
}
//
return nil
}