The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/build.go

478 lines
11 KiB

// +build ignore
package main
import (
"bytes"
"crypto/md5"
"crypto/sha256"
"encoding/json"
"flag"
"fmt"
"go/build"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
)
const (
windows = "windows"
linux = "linux"
)
var (
//versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
goarch string
goos string
gocc string
cgo bool
libc string
pkgArch string
version string = "v1"
buildTags []string
// deb & rpm does not support semver so have to handle their version a little differently
linuxPackageVersion string = "v1"
linuxPackageIteration string = ""
race bool
workingDir string
includeBuildId bool = true
buildId string = "0"
serverBinary string = "grafana-server"
cliBinary string = "grafana-cli"
binaries []string = []string{serverBinary, cliBinary}
isDev bool = false
enterprise bool = false
skipRpmGen bool = false
skipDebGen bool = false
printGenVersion bool = false
)
func main() {
log.SetOutput(os.Stdout)
log.SetFlags(0)
var buildIdRaw string
var buildTagsRaw string
flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH")
flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS")
flag.StringVar(&gocc, "cc", "", "CC")
flag.StringVar(&libc, "libc", "", "LIBC")
flag.StringVar(&buildTagsRaw, "build-tags", "", "Sets custom build tags")
flag.BoolVar(&cgo, "cgo-enabled", cgo, "Enable cgo")
flag.StringVar(&pkgArch, "pkg-arch", "", "PKG ARCH")
flag.BoolVar(&race, "race", race, "Use race detector")
flag.BoolVar(&includeBuildId, "includeBuildId", includeBuildId, "IncludeBuildId in package name")
flag.BoolVar(&enterprise, "enterprise", enterprise, "Build enterprise version of Grafana")
flag.StringVar(&buildIdRaw, "buildId", "0", "Build ID from CI system")
flag.BoolVar(&isDev, "dev", isDev, "optimal for development, skips certain steps")
flag.BoolVar(&skipRpmGen, "skipRpm", skipRpmGen, "skip rpm package generation (default: false)")
flag.BoolVar(&skipDebGen, "skipDeb", skipDebGen, "skip deb package generation (default: false)")
flag.BoolVar(&printGenVersion, "gen-version", printGenVersion, "generate Grafana version and output (default: false)")
flag.Parse()
buildId = shortenBuildId(buildIdRaw)
readVersionFromPackageJson()
if pkgArch == "" {
pkgArch = goarch
}
if printGenVersion {
printGeneratedVersion()
return
}
if len(buildTagsRaw) > 0 {
buildTags = strings.Split(buildTagsRaw, ",")
}
log.Printf("Version: %s, Linux Version: %s, Package Iteration: %s\n", version, linuxPackageVersion, linuxPackageIteration)
if flag.NArg() == 0 {
log.Println("Usage: go run build.go build")
return
}
workingDir, _ = os.Getwd()
for _, cmd := range flag.Args() {
switch cmd {
case "setup":
setup()
case "build-srv", "build-server":
clean()
doBuild("grafana-server", "./pkg/cmd/grafana-server", buildTags)
case "build-cli":
clean()
doBuild("grafana-cli", "./pkg/cmd/grafana-cli", buildTags)
case "build":
//clean()
for _, binary := range binaries {
doBuild(binary, "./pkg/cmd/"+binary, buildTags)
}
case "build-frontend":
yarn("build")
case "sha-dist":
shaFilesInDist()
case "latest":
makeLatestDistCopies()
case "clean":
clean()
default:
log.Fatalf("Unknown command %q", cmd)
}
}
}
func makeLatestDistCopies() {
files, err := ioutil.ReadDir("dist")
if err != nil {
log.Fatalf("failed to create latest copies. Cannot read from /dist")
}
latestMapping := map[string]string{
"_amd64.deb": "dist/grafana_latest_amd64.deb",
".x86_64.rpm": "dist/grafana-latest-1.x86_64.rpm",
".linux-amd64.tar.gz": "dist/grafana-latest.linux-x64.tar.gz",
".linux-amd64-musl.tar.gz": "dist/grafana-latest.linux-x64-musl.tar.gz",
".linux-armv7.tar.gz": "dist/grafana-latest.linux-armv7.tar.gz",
".linux-armv7-musl.tar.gz": "dist/grafana-latest.linux-armv7-musl.tar.gz",
".linux-armv6.tar.gz": "dist/grafana-latest.linux-armv6.tar.gz",
".linux-arm64.tar.gz": "dist/grafana-latest.linux-arm64.tar.gz",
".linux-arm64-musl.tar.gz": "dist/grafana-latest.linux-arm64-musl.tar.gz",
}
for _, file := range files {
for extension, fullName := range latestMapping {
if strings.HasSuffix(file.Name(), extension) {
runError("cp", path.Join("dist", file.Name()), fullName)
}
}
}
}
func readVersionFromPackageJson() {
reader, err := os.Open("package.json")
if err != nil {
log.Fatal("Failed to open package.json")
return
}
defer reader.Close()
jsonObj := map[string]interface{}{}
jsonParser := json.NewDecoder(reader)
if err := jsonParser.Decode(&jsonObj); err != nil {
log.Fatal("Failed to decode package.json")
}
version = jsonObj["version"].(string)
linuxPackageVersion = version
linuxPackageIteration = ""
// handle pre version stuff (deb / rpm does not support semver)
parts := strings.Split(version, "-")
if len(parts) > 1 {
linuxPackageVersion = parts[0]
linuxPackageIteration = parts[1]
}
// add timestamp to iteration
if includeBuildId {
if buildId != "0" {
linuxPackageIteration = fmt.Sprintf("%s%s", buildId, linuxPackageIteration)
} else {
linuxPackageIteration = fmt.Sprintf("%d%s", time.Now().Unix(), linuxPackageIteration)
}
}
}
func yarn(params ...string) {
runPrint(`yarn run`, params...)
}
func genPackageVersion() string {
if includeBuildId {
return fmt.Sprintf("%v-%v", linuxPackageVersion, linuxPackageIteration)
} else {
return version
}
}
func setup() {
args := []string{"install", "-v"}
if goos == windows {
args = append(args, "-buildmode=exe")
}
args = append(args, "./pkg/cmd/grafana-server")
runPrint("go", args...)
}
func printGeneratedVersion() {
fmt.Print(genPackageVersion())
}
func test(pkg string) {
setBuildEnv()
args := []string{"test", "-short", "-timeout", "60s"}
if goos == windows {
args = append(args, "-buildmode=exe")
}
args = append(args, pkg)
runPrint("go", args...)
}
func doBuild(binaryName, pkg string, tags []string) {
libcPart := ""
if libc != "" {
libcPart = fmt.Sprintf("-%s", libc)
}
binary := fmt.Sprintf("./bin/%s-%s%s/%s", goos, goarch, libcPart, binaryName)
if isDev {
//don't include os/arch/libc in output path in dev environment
binary = fmt.Sprintf("./bin/%s", binaryName)
}
if goos == windows {
binary += ".exe"
}
if !isDev {
rmr(binary, binary+".md5")
}
args := []string{"build", "-ldflags", ldflags()}
if goos == windows {
// Work around a linking error on Windows: "export ordinal too large"
args = append(args, "-buildmode=exe")
}
if len(tags) > 0 {
args = append(args, "-tags", strings.Join(tags, ","))
}
if race {
args = append(args, "-race")
}
args = append(args, "-o", binary)
args = append(args, pkg)
if !isDev {
setBuildEnv()
runPrint("go", "version")
libcPart := ""
if libc != "" {
libcPart = fmt.Sprintf("/%s", libc)
}
fmt.Printf("Targeting %s/%s%s\n", goos, goarch, libcPart)
}
runPrint("go", args...)
if !isDev {
// Create an md5 checksum of the binary, to be included in the archive for
// automatic upgrades.
err := md5File(binary)
if err != nil {
log.Fatal(err)
}
}
}
func ldflags() string {
var b bytes.Buffer
b.WriteString("-w")
b.WriteString(fmt.Sprintf(" -X main.version=%s", version))
b.WriteString(fmt.Sprintf(" -X main.commit=%s", getGitSha()))
b.WriteString(fmt.Sprintf(" -X main.buildstamp=%d", buildStamp()))
b.WriteString(fmt.Sprintf(" -X main.buildBranch=%s", getGitBranch()))
if v := os.Getenv("LDFLAGS"); v != "" {
b.WriteString(fmt.Sprintf(" -extldflags \"%s\"", v))
}
return b.String()
}
func rmr(paths ...string) {
for _, path := range paths {
log.Println("rm -r", path)
os.RemoveAll(path)
}
}
func clean() {
if isDev {
return
}
rmr("dist")
rmr("tmp")
rmr(filepath.Join(build.Default.GOPATH, fmt.Sprintf("pkg/%s_%s/github.com/grafana", goos, goarch)))
}
func setBuildEnv() {
os.Setenv("GOOS", goos)
if goos == windows {
// require windows >=7
os.Setenv("CGO_CFLAGS", "-D_WIN32_WINNT=0x0601")
}
if goarch != "amd64" || goos != linux {
// needed for all other archs
cgo = true
}
if strings.HasPrefix(goarch, "armv") {
os.Setenv("GOARCH", "arm")
os.Setenv("GOARM", goarch[4:])
} else {
os.Setenv("GOARCH", goarch)
}
if cgo {
os.Setenv("CGO_ENABLED", "1")
}
if gocc != "" {
os.Setenv("CC", gocc)
}
}
func getGitBranch() string {
v, err := runError("git", "rev-parse", "--abbrev-ref", "HEAD")
if err != nil {
return "main"
}
return string(v)
}
func getGitSha() string {
v, err := runError("git", "rev-parse", "--short", "HEAD")
if err != nil {
return "unknown-dev"
}
return string(v)
}
func buildStamp() int64 {
// use SOURCE_DATE_EPOCH if set.
if s, _ := strconv.ParseInt(os.Getenv("SOURCE_DATE_EPOCH"), 10, 64); s > 0 {
return s
}
bs, err := runError("git", "show", "-s", "--format=%ct")
if err != nil {
return time.Now().Unix()
}
s, _ := strconv.ParseInt(string(bs), 10, 64)
return s
}
func runError(cmd string, args ...string) ([]byte, error) {
ecmd := exec.Command(cmd, args...)
bs, err := ecmd.CombinedOutput()
if err != nil {
return nil, err
}
return bytes.TrimSpace(bs), nil
}
func runPrint(cmd string, args ...string) {
log.Println(cmd, strings.Join(args, " "))
ecmd := exec.Command(cmd, args...)
ecmd.Stdout = os.Stdout
ecmd.Stderr = os.Stderr
err := ecmd.Run()
if err != nil {
log.Fatal(err)
}
}
func md5File(file string) error {
fd, err := os.Open(file)
if err != nil {
return err
}
defer fd.Close()
h := md5.New()
_, err = io.Copy(h, fd)
if err != nil {
return err
}
out, err := os.Create(file + ".md5")
if err != nil {
return err
}
_, err = fmt.Fprintf(out, "%x\n", h.Sum(nil))
if err != nil {
return err
}
return out.Close()
}
func shaFilesInDist() {
filepath.Walk("./dist", func(path string, f os.FileInfo, err error) error {
if path == "./dist" {
return nil
}
if !strings.Contains(path, ".sha256") {
err := shaFile(path)
if err != nil {
log.Printf("Failed to create sha file. error: %v\n", err)
}
}
return nil
})
}
func shaFile(file string) error {
fd, err := os.Open(file)
if err != nil {
return err
}
defer fd.Close()
h := sha256.New()
_, err = io.Copy(h, fd)
if err != nil {
return err
}
out, err := os.Create(file + ".sha256")
if err != nil {
return err
}
_, err = fmt.Fprintf(out, "%x\n", h.Sum(nil))
if err != nil {
return err
}
return out.Close()
}
func shortenBuildId(buildId string) string {
buildId = strings.Replace(buildId, "-", "", -1)
if len(buildId) < 9 {
return buildId
}
return buildId[0:8]
}