build.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. // +build ignore
  2. package main
  3. import (
  4. "bytes"
  5. "crypto/md5"
  6. "crypto/sha1"
  7. "encoding/json"
  8. "flag"
  9. "fmt"
  10. "io"
  11. "io/ioutil"
  12. "log"
  13. "os"
  14. "os/exec"
  15. "path/filepath"
  16. "regexp"
  17. "runtime"
  18. "strconv"
  19. "strings"
  20. "time"
  21. )
  22. var (
  23. versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
  24. goarch string
  25. goos string
  26. gocc string
  27. gocxx string
  28. cgo string
  29. pkgArch string
  30. version string = "v1"
  31. // deb & rpm does not support semver so have to handle their version a little differently
  32. linuxPackageVersion string = "v1"
  33. linuxPackageIteration string = ""
  34. race bool
  35. phjsToRelease string
  36. workingDir string
  37. includeBuildNumber bool = true
  38. binaries []string = []string{"grafana-server", "grafana-cli"}
  39. )
  40. const minGoVersion = 1.7
  41. func main() {
  42. log.SetOutput(os.Stdout)
  43. log.SetFlags(0)
  44. ensureGoPath()
  45. flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH")
  46. flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS")
  47. flag.StringVar(&gocc, "cc", "", "CC")
  48. flag.StringVar(&gocxx, "cxx", "", "CXX")
  49. flag.StringVar(&cgo, "cgo-enabled", "", "CGO_ENABLED")
  50. flag.StringVar(&pkgArch, "pkg-arch", "", "PKG ARCH")
  51. flag.StringVar(&phjsToRelease, "phjs", "", "PhantomJS binary")
  52. flag.BoolVar(&race, "race", race, "Use race detector")
  53. flag.BoolVar(&includeBuildNumber, "includeBuildNumber", includeBuildNumber, "IncludeBuildNumber in package name")
  54. flag.Parse()
  55. readVersionFromPackageJson()
  56. log.Printf("Version: %s, Linux Version: %s, Package Iteration: %s\n", version, linuxPackageVersion, linuxPackageIteration)
  57. if flag.NArg() == 0 {
  58. log.Println("Usage: go run build.go build")
  59. return
  60. }
  61. workingDir, _ = os.Getwd()
  62. for _, cmd := range flag.Args() {
  63. switch cmd {
  64. case "setup":
  65. setup()
  66. case "build-cli":
  67. clean()
  68. build("grafana-cli", "./pkg/cmd/grafana-cli", []string{})
  69. case "build":
  70. clean()
  71. for _, binary := range binaries {
  72. build(binary, "./pkg/cmd/"+binary, []string{})
  73. }
  74. case "test":
  75. test("./pkg/...")
  76. grunt("test")
  77. case "package":
  78. grunt(gruntBuildArg("release")...)
  79. createLinuxPackages()
  80. case "pkg-rpm":
  81. grunt(gruntBuildArg("release")...)
  82. createRpmPackages()
  83. case "pkg-deb":
  84. grunt(gruntBuildArg("release")...)
  85. createDebPackages()
  86. case "sha1-dist":
  87. sha1FilesInDist()
  88. case "latest":
  89. makeLatestDistCopies()
  90. case "clean":
  91. clean()
  92. default:
  93. log.Fatalf("Unknown command %q", cmd)
  94. }
  95. }
  96. }
  97. func makeLatestDistCopies() {
  98. rpmIteration := "-1"
  99. if linuxPackageIteration != "" {
  100. rpmIteration = linuxPackageIteration
  101. }
  102. runError("cp", fmt.Sprintf("dist/grafana_%v-%v_amd64.deb", linuxPackageVersion, linuxPackageIteration), "dist/grafana_latest_amd64.deb")
  103. runError("cp", fmt.Sprintf("dist/grafana-%v-%v.x86_64.rpm", linuxPackageVersion, rpmIteration), "dist/grafana-latest-1.x86_64.rpm")
  104. runError("cp", fmt.Sprintf("dist/grafana-%v-%v.linux-x64.tar.gz", linuxPackageVersion, linuxPackageIteration), "dist/grafana-latest.linux-x64.tar.gz")
  105. }
  106. func readVersionFromPackageJson() {
  107. reader, err := os.Open("package.json")
  108. if err != nil {
  109. log.Fatal("Failed to open package.json")
  110. return
  111. }
  112. defer reader.Close()
  113. jsonObj := map[string]interface{}{}
  114. jsonParser := json.NewDecoder(reader)
  115. if err := jsonParser.Decode(&jsonObj); err != nil {
  116. log.Fatal("Failed to decode package.json")
  117. }
  118. version = jsonObj["version"].(string)
  119. linuxPackageVersion = version
  120. linuxPackageIteration = ""
  121. // handle pre version stuff (deb / rpm does not support semver)
  122. parts := strings.Split(version, "-")
  123. if len(parts) > 1 {
  124. linuxPackageVersion = parts[0]
  125. linuxPackageIteration = parts[1]
  126. }
  127. // add timestamp to iteration
  128. if includeBuildNumber {
  129. linuxPackageIteration = fmt.Sprintf("%d%s", time.Now().Unix(), linuxPackageIteration)
  130. }
  131. }
  132. type linuxPackageOptions struct {
  133. packageType string
  134. homeDir string
  135. binPath string
  136. serverBinPath string
  137. cliBinPath string
  138. configDir string
  139. ldapFilePath string
  140. etcDefaultPath string
  141. etcDefaultFilePath string
  142. initdScriptFilePath string
  143. systemdServiceFilePath string
  144. postinstSrc string
  145. initdScriptSrc string
  146. defaultFileSrc string
  147. systemdFileSrc string
  148. depends []string
  149. }
  150. func createDebPackages() {
  151. createPackage(linuxPackageOptions{
  152. packageType: "deb",
  153. homeDir: "/usr/share/grafana",
  154. binPath: "/usr/sbin",
  155. configDir: "/etc/grafana",
  156. etcDefaultPath: "/etc/default",
  157. etcDefaultFilePath: "/etc/default/grafana-server",
  158. initdScriptFilePath: "/etc/init.d/grafana-server",
  159. systemdServiceFilePath: "/usr/lib/systemd/system/grafana-server.service",
  160. postinstSrc: "packaging/deb/control/postinst",
  161. initdScriptSrc: "packaging/deb/init.d/grafana-server",
  162. defaultFileSrc: "packaging/deb/default/grafana-server",
  163. systemdFileSrc: "packaging/deb/systemd/grafana-server.service",
  164. depends: []string{"adduser", "libfontconfig"},
  165. })
  166. }
  167. func createRpmPackages() {
  168. createPackage(linuxPackageOptions{
  169. packageType: "rpm",
  170. homeDir: "/usr/share/grafana",
  171. binPath: "/usr/sbin",
  172. configDir: "/etc/grafana",
  173. etcDefaultPath: "/etc/sysconfig",
  174. etcDefaultFilePath: "/etc/sysconfig/grafana-server",
  175. initdScriptFilePath: "/etc/init.d/grafana-server",
  176. systemdServiceFilePath: "/usr/lib/systemd/system/grafana-server.service",
  177. postinstSrc: "packaging/rpm/control/postinst",
  178. initdScriptSrc: "packaging/rpm/init.d/grafana-server",
  179. defaultFileSrc: "packaging/rpm/sysconfig/grafana-server",
  180. systemdFileSrc: "packaging/rpm/systemd/grafana-server.service",
  181. depends: []string{"/sbin/service", "fontconfig"},
  182. })
  183. }
  184. func createLinuxPackages() {
  185. createDebPackages()
  186. createRpmPackages()
  187. }
  188. func createPackage(options linuxPackageOptions) {
  189. packageRoot, _ := ioutil.TempDir("", "grafana-linux-pack")
  190. // create directories
  191. runPrint("mkdir", "-p", filepath.Join(packageRoot, options.homeDir))
  192. runPrint("mkdir", "-p", filepath.Join(packageRoot, options.configDir))
  193. runPrint("mkdir", "-p", filepath.Join(packageRoot, "/etc/init.d"))
  194. runPrint("mkdir", "-p", filepath.Join(packageRoot, options.etcDefaultPath))
  195. runPrint("mkdir", "-p", filepath.Join(packageRoot, "/usr/lib/systemd/system"))
  196. runPrint("mkdir", "-p", filepath.Join(packageRoot, "/usr/sbin"))
  197. // copy binary
  198. for _, binary := range binaries {
  199. runPrint("cp", "-p", filepath.Join(workingDir, "tmp/bin/"+binary), filepath.Join(packageRoot, "/usr/sbin/"+binary))
  200. }
  201. // copy init.d script
  202. runPrint("cp", "-p", options.initdScriptSrc, filepath.Join(packageRoot, options.initdScriptFilePath))
  203. // copy environment var file
  204. runPrint("cp", "-p", options.defaultFileSrc, filepath.Join(packageRoot, options.etcDefaultFilePath))
  205. // copy systemd file
  206. runPrint("cp", "-p", options.systemdFileSrc, filepath.Join(packageRoot, options.systemdServiceFilePath))
  207. // copy release files
  208. runPrint("cp", "-a", filepath.Join(workingDir, "tmp")+"/.", filepath.Join(packageRoot, options.homeDir))
  209. // remove bin path
  210. runPrint("rm", "-rf", filepath.Join(packageRoot, options.homeDir, "bin"))
  211. args := []string{
  212. "-s", "dir",
  213. "--description", "Grafana",
  214. "-C", packageRoot,
  215. "--vendor", "Grafana",
  216. "--url", "http://grafana.org",
  217. "--license", "\"Apache 2.0\"",
  218. "--maintainer", "contact@grafana.org",
  219. "--config-files", options.initdScriptFilePath,
  220. "--config-files", options.etcDefaultFilePath,
  221. "--config-files", options.systemdServiceFilePath,
  222. "--after-install", options.postinstSrc,
  223. "--name", "grafana",
  224. "--version", linuxPackageVersion,
  225. "-p", "./dist",
  226. }
  227. if pkgArch != "" {
  228. args = append(args, "-a", pkgArch)
  229. }
  230. if linuxPackageIteration != "" {
  231. args = append(args, "--iteration", linuxPackageIteration)
  232. }
  233. // add dependenciesj
  234. for _, dep := range options.depends {
  235. args = append(args, "--depends", dep)
  236. }
  237. args = append(args, ".")
  238. fmt.Println("Creating package: ", options.packageType)
  239. runPrint("fpm", append([]string{"-t", options.packageType}, args...)...)
  240. }
  241. func verifyGitRepoIsClean() {
  242. rs, err := runError("git", "ls-files", "--modified")
  243. if err != nil {
  244. log.Fatalf("Failed to check if git tree was clean, %v, %v\n", string(rs), err)
  245. return
  246. }
  247. count := len(string(rs))
  248. if count > 0 {
  249. log.Fatalf("Git repository has modified files, aborting")
  250. }
  251. log.Println("Git repository is clean")
  252. }
  253. func ensureGoPath() {
  254. if os.Getenv("GOPATH") == "" {
  255. cwd, err := os.Getwd()
  256. if err != nil {
  257. log.Fatal(err)
  258. }
  259. gopath := filepath.Clean(filepath.Join(cwd, "../../../../"))
  260. log.Println("GOPATH is", gopath)
  261. os.Setenv("GOPATH", gopath)
  262. }
  263. }
  264. func ChangeWorkingDir(dir string) {
  265. os.Chdir(dir)
  266. }
  267. func grunt(params ...string) {
  268. runPrint("./node_modules/.bin/grunt", params...)
  269. }
  270. func gruntBuildArg(task string) []string {
  271. var args []string
  272. if includeBuildNumber {
  273. args = append(args, fmt.Sprintf("--pkgVer=%v-%v", linuxPackageVersion, linuxPackageIteration))
  274. } else {
  275. args = append(args, fmt.Sprintf("--pkgVer=%v", linuxPackageVersion))
  276. }
  277. if pkgArch != "" {
  278. args = append(args, fmt.Sprintf("--arch=%v", pkgArch))
  279. }
  280. if phjsToRelease != "" {
  281. args = append(args, fmt.Sprintf("--phjsToRelease=%v", phjsToRelease))
  282. }
  283. return args
  284. }
  285. func setup() {
  286. runPrint("go", "get", "-v", "github.com/kardianos/govendor")
  287. runPrint("go", "install", "-v", "./pkg/cmd/grafana-server")
  288. }
  289. func test(pkg string) {
  290. setBuildEnv()
  291. runPrint("go", "test", "-short", "-timeout", "60s", pkg)
  292. }
  293. func build(binaryName, pkg string, tags []string) {
  294. binary := "./bin/" + binaryName
  295. if goos == "windows" {
  296. binary += ".exe"
  297. }
  298. rmr(binary, binary+".md5")
  299. args := []string{"build", "-ldflags", ldflags()}
  300. if len(tags) > 0 {
  301. args = append(args, "-tags", strings.Join(tags, ","))
  302. }
  303. if race {
  304. args = append(args, "-race")
  305. }
  306. args = append(args, "-o", binary)
  307. args = append(args, pkg)
  308. setBuildEnv()
  309. runPrint("go", "version")
  310. runPrint("go", args...)
  311. // Create an md5 checksum of the binary, to be included in the archive for
  312. // automatic upgrades.
  313. err := md5File(binary)
  314. if err != nil {
  315. log.Fatal(err)
  316. }
  317. }
  318. func ldflags() string {
  319. var b bytes.Buffer
  320. b.WriteString("-w")
  321. b.WriteString(fmt.Sprintf(" -X main.version=%s", version))
  322. b.WriteString(fmt.Sprintf(" -X main.commit=%s", getGitSha()))
  323. b.WriteString(fmt.Sprintf(" -X main.buildstamp=%d", buildStamp()))
  324. return b.String()
  325. }
  326. func rmr(paths ...string) {
  327. for _, path := range paths {
  328. log.Println("rm -r", path)
  329. os.RemoveAll(path)
  330. }
  331. }
  332. func clean() {
  333. rmr("dist")
  334. rmr("tmp")
  335. rmr(filepath.Join(os.Getenv("GOPATH"), fmt.Sprintf("pkg/%s_%s/github.com/grafana", goos, goarch)))
  336. }
  337. func setBuildEnv() {
  338. os.Setenv("GOOS", goos)
  339. if strings.HasPrefix(goarch, "armv") {
  340. os.Setenv("GOARCH", "arm")
  341. os.Setenv("GOARM", goarch[4:])
  342. } else {
  343. os.Setenv("GOARCH", goarch)
  344. }
  345. if goarch == "386" {
  346. os.Setenv("GO386", "387")
  347. }
  348. if cgo != "" {
  349. os.Setenv("CGO_ENABLED", cgo)
  350. }
  351. if gocc != "" {
  352. os.Setenv("CC", gocc)
  353. }
  354. if gocxx != "" {
  355. os.Setenv("CXX", gocxx)
  356. }
  357. }
  358. func getGitSha() string {
  359. v, err := runError("git", "rev-parse", "--short", "HEAD")
  360. if err != nil {
  361. return "unknown-dev"
  362. }
  363. return string(v)
  364. }
  365. func buildStamp() int64 {
  366. bs, err := runError("git", "show", "-s", "--format=%ct")
  367. if err != nil {
  368. return time.Now().Unix()
  369. }
  370. s, _ := strconv.ParseInt(string(bs), 10, 64)
  371. return s
  372. }
  373. func buildArch() string {
  374. os := goos
  375. if os == "darwin" {
  376. os = "macosx"
  377. }
  378. return fmt.Sprintf("%s-%s", os, goarch)
  379. }
  380. func run(cmd string, args ...string) []byte {
  381. bs, err := runError(cmd, args...)
  382. if err != nil {
  383. log.Println(cmd, strings.Join(args, " "))
  384. log.Println(string(bs))
  385. log.Fatal(err)
  386. }
  387. return bytes.TrimSpace(bs)
  388. }
  389. func runError(cmd string, args ...string) ([]byte, error) {
  390. ecmd := exec.Command(cmd, args...)
  391. bs, err := ecmd.CombinedOutput()
  392. if err != nil {
  393. return nil, err
  394. }
  395. return bytes.TrimSpace(bs), nil
  396. }
  397. func runPrint(cmd string, args ...string) {
  398. log.Println(cmd, strings.Join(args, " "))
  399. ecmd := exec.Command(cmd, args...)
  400. ecmd.Stdout = os.Stdout
  401. ecmd.Stderr = os.Stderr
  402. err := ecmd.Run()
  403. if err != nil {
  404. log.Fatal(err)
  405. }
  406. }
  407. func md5File(file string) error {
  408. fd, err := os.Open(file)
  409. if err != nil {
  410. return err
  411. }
  412. defer fd.Close()
  413. h := md5.New()
  414. _, err = io.Copy(h, fd)
  415. if err != nil {
  416. return err
  417. }
  418. out, err := os.Create(file + ".md5")
  419. if err != nil {
  420. return err
  421. }
  422. _, err = fmt.Fprintf(out, "%x\n", h.Sum(nil))
  423. if err != nil {
  424. return err
  425. }
  426. return out.Close()
  427. }
  428. func sha1FilesInDist() {
  429. filepath.Walk("./dist", func(path string, f os.FileInfo, err error) error {
  430. if path == "./dist" {
  431. return nil
  432. }
  433. if strings.Contains(path, ".sha1") == false {
  434. err := sha1File(path)
  435. if err != nil {
  436. log.Printf("Failed to create sha file. error: %v\n", err)
  437. }
  438. }
  439. return nil
  440. })
  441. }
  442. func sha1File(file string) error {
  443. fd, err := os.Open(file)
  444. if err != nil {
  445. return err
  446. }
  447. defer fd.Close()
  448. h := sha1.New()
  449. _, err = io.Copy(h, fd)
  450. if err != nil {
  451. return err
  452. }
  453. out, err := os.Create(file + ".sha1")
  454. if err != nil {
  455. return err
  456. }
  457. _, err = fmt.Fprintf(out, "%x\n", h.Sum(nil))
  458. if err != nil {
  459. return err
  460. }
  461. return out.Close()
  462. }