build.go 11 KB

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