build.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. // +build ignore
  2. package main
  3. import (
  4. "bytes"
  5. "crypto/md5"
  6. "crypto/sha256"
  7. "encoding/json"
  8. "flag"
  9. "fmt"
  10. "io"
  11. "io/ioutil"
  12. "log"
  13. "os"
  14. "os/exec"
  15. "path"
  16. "path/filepath"
  17. "runtime"
  18. "strconv"
  19. "strings"
  20. "time"
  21. )
  22. const (
  23. windows = "windows"
  24. linux = "linux"
  25. )
  26. var (
  27. //versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
  28. goarch string
  29. goos string
  30. gocc string
  31. cgo bool
  32. pkgArch string
  33. version string = "v1"
  34. // deb & rpm does not support semver so have to handle their version a little differently
  35. linuxPackageVersion string = "v1"
  36. linuxPackageIteration string = ""
  37. race bool
  38. phjsToRelease string
  39. workingDir string
  40. includeBuildId bool = true
  41. buildId string = "0"
  42. serverBinary string = "grafana-server"
  43. cliBinary string = "grafana-cli"
  44. binaries []string = []string{serverBinary, cliBinary}
  45. isDev bool = false
  46. enterprise bool = false
  47. skipRpmGen bool = false
  48. skipDebGen bool = false
  49. printGenVersion bool = false
  50. )
  51. func main() {
  52. log.SetOutput(os.Stdout)
  53. log.SetFlags(0)
  54. ensureGoPath()
  55. var buildIdRaw string
  56. flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH")
  57. flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS")
  58. flag.StringVar(&gocc, "cc", "", "CC")
  59. flag.BoolVar(&cgo, "cgo-enabled", cgo, "Enable cgo")
  60. flag.StringVar(&pkgArch, "pkg-arch", "", "PKG ARCH")
  61. flag.StringVar(&phjsToRelease, "phjs", "", "PhantomJS binary")
  62. flag.BoolVar(&race, "race", race, "Use race detector")
  63. flag.BoolVar(&includeBuildId, "includeBuildId", includeBuildId, "IncludeBuildId in package name")
  64. flag.BoolVar(&enterprise, "enterprise", enterprise, "Build enterprise version of Grafana")
  65. flag.StringVar(&buildIdRaw, "buildId", "0", "Build ID from CI system")
  66. flag.BoolVar(&isDev, "dev", isDev, "optimal for development, skips certain steps")
  67. flag.BoolVar(&skipRpmGen, "skipRpm", skipRpmGen, "skip rpm package generation (default: false)")
  68. flag.BoolVar(&skipDebGen, "skipDeb", skipDebGen, "skip deb package generation (default: false)")
  69. flag.BoolVar(&printGenVersion, "gen-version", printGenVersion, "generate Grafana version and output (default: false)")
  70. flag.Parse()
  71. buildId = shortenBuildId(buildIdRaw)
  72. readVersionFromPackageJson()
  73. if pkgArch == "" {
  74. pkgArch = goarch
  75. }
  76. if printGenVersion {
  77. printGeneratedVersion()
  78. return
  79. }
  80. log.Printf("Version: %s, Linux Version: %s, Package Iteration: %s\n", version, linuxPackageVersion, linuxPackageIteration)
  81. if flag.NArg() == 0 {
  82. log.Println("Usage: go run build.go build")
  83. return
  84. }
  85. workingDir, _ = os.Getwd()
  86. for _, cmd := range flag.Args() {
  87. switch cmd {
  88. case "setup":
  89. setup()
  90. case "build-srv":
  91. clean()
  92. build("grafana-server", "./pkg/cmd/grafana-server", []string{})
  93. case "build-cli":
  94. clean()
  95. build("grafana-cli", "./pkg/cmd/grafana-cli", []string{})
  96. case "build-server":
  97. clean()
  98. build("grafana-server", "./pkg/cmd/grafana-server", []string{})
  99. case "build":
  100. //clean()
  101. for _, binary := range binaries {
  102. build(binary, "./pkg/cmd/"+binary, []string{})
  103. }
  104. case "build-frontend":
  105. grunt(gruntBuildArg("build")...)
  106. case "test":
  107. test("./pkg/...")
  108. grunt("test")
  109. case "package":
  110. grunt(gruntBuildArg("build")...)
  111. grunt(gruntBuildArg("package")...)
  112. if goos == linux {
  113. createLinuxPackages()
  114. }
  115. case "package-only":
  116. grunt(gruntBuildArg("package")...)
  117. if goos == linux {
  118. createLinuxPackages()
  119. }
  120. case "pkg-archive":
  121. grunt(gruntBuildArg("package")...)
  122. case "pkg-rpm":
  123. grunt(gruntBuildArg("release")...)
  124. createRpmPackages()
  125. case "pkg-deb":
  126. grunt(gruntBuildArg("release")...)
  127. createDebPackages()
  128. case "sha-dist":
  129. shaFilesInDist()
  130. case "latest":
  131. makeLatestDistCopies()
  132. case "clean":
  133. clean()
  134. default:
  135. log.Fatalf("Unknown command %q", cmd)
  136. }
  137. }
  138. }
  139. func makeLatestDistCopies() {
  140. files, err := ioutil.ReadDir("dist")
  141. if err != nil {
  142. log.Fatalf("failed to create latest copies. Cannot read from /dist")
  143. }
  144. latestMapping := map[string]string{
  145. "_amd64.deb": "dist/grafana_latest_amd64.deb",
  146. ".x86_64.rpm": "dist/grafana-latest-1.x86_64.rpm",
  147. ".linux-amd64.tar.gz": "dist/grafana-latest.linux-x64.tar.gz",
  148. ".linux-armv7.tar.gz": "dist/grafana-latest.linux-armv7.tar.gz",
  149. ".linux-armv6.tar.gz": "dist/grafana-latest.linux-armv6.tar.gz",
  150. ".linux-arm64.tar.gz": "dist/grafana-latest.linux-arm64.tar.gz",
  151. }
  152. for _, file := range files {
  153. for extension, fullName := range latestMapping {
  154. if strings.HasSuffix(file.Name(), extension) {
  155. runError("cp", path.Join("dist", file.Name()), fullName)
  156. }
  157. }
  158. }
  159. }
  160. func readVersionFromPackageJson() {
  161. reader, err := os.Open("package.json")
  162. if err != nil {
  163. log.Fatal("Failed to open package.json")
  164. return
  165. }
  166. defer reader.Close()
  167. jsonObj := map[string]interface{}{}
  168. jsonParser := json.NewDecoder(reader)
  169. if err := jsonParser.Decode(&jsonObj); err != nil {
  170. log.Fatal("Failed to decode package.json")
  171. }
  172. version = jsonObj["version"].(string)
  173. linuxPackageVersion = version
  174. linuxPackageIteration = ""
  175. // handle pre version stuff (deb / rpm does not support semver)
  176. parts := strings.Split(version, "-")
  177. if len(parts) > 1 {
  178. linuxPackageVersion = parts[0]
  179. linuxPackageIteration = parts[1]
  180. }
  181. // add timestamp to iteration
  182. if includeBuildId {
  183. if buildId != "0" {
  184. linuxPackageIteration = fmt.Sprintf("%s%s", buildId, linuxPackageIteration)
  185. } else {
  186. linuxPackageIteration = fmt.Sprintf("%d%s", time.Now().Unix(), linuxPackageIteration)
  187. }
  188. }
  189. }
  190. type linuxPackageOptions struct {
  191. packageType string
  192. packageArch string
  193. homeDir string
  194. homeBinDir string
  195. binPath string
  196. serverBinPath string
  197. cliBinPath string
  198. configDir string
  199. ldapFilePath string
  200. etcDefaultPath string
  201. etcDefaultFilePath string
  202. initdScriptFilePath string
  203. systemdServiceFilePath string
  204. postinstSrc string
  205. initdScriptSrc string
  206. defaultFileSrc string
  207. systemdFileSrc string
  208. cliBinaryWrapperSrc string
  209. depends []string
  210. }
  211. func createDebPackages() {
  212. debPkgArch := pkgArch
  213. if pkgArch == "armv7" || pkgArch == "armv6" {
  214. debPkgArch = "armhf"
  215. }
  216. createPackage(linuxPackageOptions{
  217. packageType: "deb",
  218. packageArch: debPkgArch,
  219. homeDir: "/usr/share/grafana",
  220. homeBinDir: "/usr/share/grafana/bin",
  221. binPath: "/usr/sbin",
  222. configDir: "/etc/grafana",
  223. etcDefaultPath: "/etc/default",
  224. etcDefaultFilePath: "/etc/default/grafana-server",
  225. initdScriptFilePath: "/etc/init.d/grafana-server",
  226. systemdServiceFilePath: "/usr/lib/systemd/system/grafana-server.service",
  227. postinstSrc: "packaging/deb/control/postinst",
  228. initdScriptSrc: "packaging/deb/init.d/grafana-server",
  229. defaultFileSrc: "packaging/deb/default/grafana-server",
  230. systemdFileSrc: "packaging/deb/systemd/grafana-server.service",
  231. cliBinaryWrapperSrc: "packaging/wrappers/grafana-cli",
  232. depends: []string{"adduser", "libfontconfig1"},
  233. })
  234. }
  235. func createRpmPackages() {
  236. rpmPkgArch := pkgArch
  237. switch {
  238. case pkgArch == "armv7":
  239. rpmPkgArch = "armhfp"
  240. case pkgArch == "arm64":
  241. rpmPkgArch = "aarch64"
  242. }
  243. createPackage(linuxPackageOptions{
  244. packageType: "rpm",
  245. packageArch: rpmPkgArch,
  246. homeDir: "/usr/share/grafana",
  247. homeBinDir: "/usr/share/grafana/bin",
  248. binPath: "/usr/sbin",
  249. configDir: "/etc/grafana",
  250. etcDefaultPath: "/etc/sysconfig",
  251. etcDefaultFilePath: "/etc/sysconfig/grafana-server",
  252. initdScriptFilePath: "/etc/init.d/grafana-server",
  253. systemdServiceFilePath: "/usr/lib/systemd/system/grafana-server.service",
  254. postinstSrc: "packaging/rpm/control/postinst",
  255. initdScriptSrc: "packaging/rpm/init.d/grafana-server",
  256. defaultFileSrc: "packaging/rpm/sysconfig/grafana-server",
  257. systemdFileSrc: "packaging/rpm/systemd/grafana-server.service",
  258. cliBinaryWrapperSrc: "packaging/wrappers/grafana-cli",
  259. depends: []string{"/sbin/service", "fontconfig", "freetype", "urw-fonts"},
  260. })
  261. }
  262. func createLinuxPackages() {
  263. if !skipDebGen {
  264. createDebPackages()
  265. }
  266. if !skipRpmGen {
  267. createRpmPackages()
  268. }
  269. }
  270. func createPackage(options linuxPackageOptions) {
  271. packageRoot, _ := ioutil.TempDir("", "grafana-linux-pack")
  272. // create directories
  273. runPrint("mkdir", "-p", filepath.Join(packageRoot, options.homeDir))
  274. runPrint("mkdir", "-p", filepath.Join(packageRoot, options.configDir))
  275. runPrint("mkdir", "-p", filepath.Join(packageRoot, "/etc/init.d"))
  276. runPrint("mkdir", "-p", filepath.Join(packageRoot, options.etcDefaultPath))
  277. runPrint("mkdir", "-p", filepath.Join(packageRoot, "/usr/lib/systemd/system"))
  278. runPrint("mkdir", "-p", filepath.Join(packageRoot, "/usr/sbin"))
  279. // copy grafana-cli wrapper
  280. runPrint("cp", "-p", options.cliBinaryWrapperSrc, filepath.Join(packageRoot, "/usr/sbin/"+cliBinary))
  281. // copy grafana-server binary
  282. runPrint("cp", "-p", filepath.Join(workingDir, "tmp/bin/"+serverBinary), filepath.Join(packageRoot, "/usr/sbin/"+serverBinary))
  283. // copy init.d script
  284. runPrint("cp", "-p", options.initdScriptSrc, filepath.Join(packageRoot, options.initdScriptFilePath))
  285. // copy environment var file
  286. runPrint("cp", "-p", options.defaultFileSrc, filepath.Join(packageRoot, options.etcDefaultFilePath))
  287. // copy systemd file
  288. runPrint("cp", "-p", options.systemdFileSrc, filepath.Join(packageRoot, options.systemdServiceFilePath))
  289. // copy release files
  290. runPrint("cp", "-a", filepath.Join(workingDir, "tmp")+"/.", filepath.Join(packageRoot, options.homeDir))
  291. // remove bin path
  292. runPrint("rm", "-rf", filepath.Join(packageRoot, options.homeDir, "bin"))
  293. // create /bin within home
  294. runPrint("mkdir", "-p", filepath.Join(packageRoot, options.homeBinDir))
  295. // The grafana-cli binary is exposed through a wrapper to ensure a proper
  296. // configuration is in place. To enable that, we need to store the original
  297. // binary in a separate location to avoid conflicts.
  298. runPrint("cp", "-p", filepath.Join(workingDir, "tmp/bin/"+cliBinary), filepath.Join(packageRoot, options.homeBinDir, cliBinary))
  299. args := []string{
  300. "-s", "dir",
  301. "--description", "Grafana",
  302. "-C", packageRoot,
  303. "--url", "https://grafana.com",
  304. "--maintainer", "contact@grafana.com",
  305. "--config-files", options.initdScriptFilePath,
  306. "--config-files", options.etcDefaultFilePath,
  307. "--config-files", options.systemdServiceFilePath,
  308. "--after-install", options.postinstSrc,
  309. "--version", linuxPackageVersion,
  310. "-p", "./dist",
  311. }
  312. name := "grafana"
  313. if enterprise {
  314. name += "-enterprise"
  315. args = append(args, "--replaces", "grafana")
  316. }
  317. fmt.Printf("pkgArch is set to '%s', generated arch is '%s'\n", pkgArch, options.packageArch)
  318. if pkgArch == "armv6" {
  319. name += "-rpi"
  320. args = append(args, "--replaces", "grafana")
  321. }
  322. args = append(args, "--name", name)
  323. description := "Grafana"
  324. if enterprise {
  325. description += " Enterprise"
  326. }
  327. args = append(args, "--vendor", description)
  328. if !enterprise {
  329. args = append(args, "--license", "\"Apache 2.0\"")
  330. }
  331. if options.packageType == "rpm" {
  332. args = append(args, "--rpm-posttrans", "packaging/rpm/control/posttrans")
  333. }
  334. if options.packageType == "deb" {
  335. args = append(args, "--deb-no-default-config-files")
  336. }
  337. if options.packageArch != "" {
  338. args = append(args, "-a", options.packageArch)
  339. }
  340. if linuxPackageIteration != "" {
  341. args = append(args, "--iteration", linuxPackageIteration)
  342. }
  343. // add dependencies
  344. for _, dep := range options.depends {
  345. args = append(args, "--depends", dep)
  346. }
  347. args = append(args, ".")
  348. fmt.Println("Creating package: ", options.packageType)
  349. runPrint("fpm", append([]string{"-t", options.packageType}, args...)...)
  350. }
  351. func ensureGoPath() {
  352. if os.Getenv("GOPATH") == "" {
  353. cwd, err := os.Getwd()
  354. if err != nil {
  355. log.Fatal(err)
  356. }
  357. gopath := filepath.Clean(filepath.Join(cwd, "../../../../"))
  358. log.Println("GOPATH is", gopath)
  359. os.Setenv("GOPATH", gopath)
  360. }
  361. }
  362. func grunt(params ...string) {
  363. if runtime.GOOS == windows {
  364. runPrint(`.\node_modules\.bin\grunt`, params...)
  365. } else {
  366. runPrint("./node_modules/.bin/grunt", params...)
  367. }
  368. }
  369. func genPackageVersion() string {
  370. if includeBuildId {
  371. return fmt.Sprintf("%v-%v", linuxPackageVersion, linuxPackageIteration)
  372. } else {
  373. return version
  374. }
  375. }
  376. func gruntBuildArg(task string) []string {
  377. args := []string{task}
  378. args = append(args, fmt.Sprintf("--pkgVer=%v", genPackageVersion()))
  379. if pkgArch != "" {
  380. args = append(args, fmt.Sprintf("--arch=%v", pkgArch))
  381. }
  382. if phjsToRelease != "" {
  383. args = append(args, fmt.Sprintf("--phjsToRelease=%v", phjsToRelease))
  384. }
  385. if enterprise {
  386. args = append(args, "--enterprise")
  387. }
  388. args = append(args, fmt.Sprintf("--platform=%v", goos))
  389. return args
  390. }
  391. func setup() {
  392. runPrint("go", "install", "-v", "./pkg/cmd/grafana-server")
  393. }
  394. func printGeneratedVersion() {
  395. fmt.Print(genPackageVersion())
  396. }
  397. func test(pkg string) {
  398. setBuildEnv()
  399. runPrint("go", "test", "-short", "-timeout", "60s", pkg)
  400. }
  401. func build(binaryName, pkg string, tags []string) {
  402. binary := fmt.Sprintf("./bin/%s-%s/%s", goos, goarch, binaryName)
  403. if isDev {
  404. //don't include os and arch in output path in dev environment
  405. binary = fmt.Sprintf("./bin/%s", binaryName)
  406. }
  407. if goos == windows {
  408. binary += ".exe"
  409. }
  410. if !isDev {
  411. rmr(binary, binary+".md5")
  412. }
  413. args := []string{"build", "-ldflags", ldflags()}
  414. if len(tags) > 0 {
  415. args = append(args, "-tags", strings.Join(tags, ","))
  416. }
  417. if race {
  418. args = append(args, "-race")
  419. }
  420. args = append(args, "-o", binary)
  421. args = append(args, pkg)
  422. if !isDev {
  423. setBuildEnv()
  424. runPrint("go", "version")
  425. fmt.Printf("Targeting %s/%s\n", goos, goarch)
  426. }
  427. runPrint("go", args...)
  428. if !isDev {
  429. // Create an md5 checksum of the binary, to be included in the archive for
  430. // automatic upgrades.
  431. err := md5File(binary)
  432. if err != nil {
  433. log.Fatal(err)
  434. }
  435. }
  436. }
  437. func ldflags() string {
  438. var b bytes.Buffer
  439. b.WriteString("-w")
  440. b.WriteString(fmt.Sprintf(" -X main.version=%s", version))
  441. b.WriteString(fmt.Sprintf(" -X main.commit=%s", getGitSha()))
  442. b.WriteString(fmt.Sprintf(" -X main.buildstamp=%d", buildStamp()))
  443. b.WriteString(fmt.Sprintf(" -X main.buildBranch=%s", getGitBranch()))
  444. if v := os.Getenv("LDFLAGS"); v != "" {
  445. b.WriteString(fmt.Sprintf(" -extldflags=%s", v))
  446. }
  447. return b.String()
  448. }
  449. func rmr(paths ...string) {
  450. for _, path := range paths {
  451. log.Println("rm -r", path)
  452. os.RemoveAll(path)
  453. }
  454. }
  455. func clean() {
  456. if isDev {
  457. return
  458. }
  459. rmr("dist")
  460. rmr("tmp")
  461. rmr(filepath.Join(os.Getenv("GOPATH"), fmt.Sprintf("pkg/%s_%s/github.com/grafana", goos, goarch)))
  462. }
  463. func setBuildEnv() {
  464. os.Setenv("GOOS", goos)
  465. if goos == windows {
  466. // require windows >=7
  467. os.Setenv("CGO_CFLAGS", "-D_WIN32_WINNT=0x0601")
  468. }
  469. if goarch != "amd64" || goos != linux {
  470. // needed for all other archs
  471. cgo = true
  472. }
  473. if strings.HasPrefix(goarch, "armv") {
  474. os.Setenv("GOARCH", "arm")
  475. os.Setenv("GOARM", goarch[4:])
  476. } else {
  477. os.Setenv("GOARCH", goarch)
  478. }
  479. if goarch == "386" {
  480. os.Setenv("GO386", "387")
  481. }
  482. if cgo {
  483. os.Setenv("CGO_ENABLED", "1")
  484. }
  485. if gocc != "" {
  486. os.Setenv("CC", gocc)
  487. }
  488. }
  489. func getGitBranch() string {
  490. v, err := runError("git", "rev-parse", "--abbrev-ref", "HEAD")
  491. if err != nil {
  492. return "master"
  493. }
  494. return string(v)
  495. }
  496. func getGitSha() string {
  497. v, err := runError("git", "rev-parse", "--short", "HEAD")
  498. if err != nil {
  499. return "unknown-dev"
  500. }
  501. return string(v)
  502. }
  503. func buildStamp() int64 {
  504. // use SOURCE_DATE_EPOCH if set.
  505. if s, _ := strconv.ParseInt(os.Getenv("SOURCE_DATE_EPOCH"), 10, 64); s > 0 {
  506. return s
  507. }
  508. bs, err := runError("git", "show", "-s", "--format=%ct")
  509. if err != nil {
  510. return time.Now().Unix()
  511. }
  512. s, _ := strconv.ParseInt(string(bs), 10, 64)
  513. return s
  514. }
  515. func runError(cmd string, args ...string) ([]byte, error) {
  516. ecmd := exec.Command(cmd, args...)
  517. bs, err := ecmd.CombinedOutput()
  518. if err != nil {
  519. return nil, err
  520. }
  521. return bytes.TrimSpace(bs), nil
  522. }
  523. func runPrint(cmd string, args ...string) {
  524. log.Println(cmd, strings.Join(args, " "))
  525. ecmd := exec.Command(cmd, args...)
  526. ecmd.Stdout = os.Stdout
  527. ecmd.Stderr = os.Stderr
  528. err := ecmd.Run()
  529. if err != nil {
  530. log.Fatal(err)
  531. }
  532. }
  533. func md5File(file string) error {
  534. fd, err := os.Open(file)
  535. if err != nil {
  536. return err
  537. }
  538. defer fd.Close()
  539. h := md5.New()
  540. _, err = io.Copy(h, fd)
  541. if err != nil {
  542. return err
  543. }
  544. out, err := os.Create(file + ".md5")
  545. if err != nil {
  546. return err
  547. }
  548. _, err = fmt.Fprintf(out, "%x\n", h.Sum(nil))
  549. if err != nil {
  550. return err
  551. }
  552. return out.Close()
  553. }
  554. func shaFilesInDist() {
  555. filepath.Walk("./dist", func(path string, f os.FileInfo, err error) error {
  556. if path == "./dist" {
  557. return nil
  558. }
  559. if !strings.Contains(path, ".sha256") {
  560. err := shaFile(path)
  561. if err != nil {
  562. log.Printf("Failed to create sha file. error: %v\n", err)
  563. }
  564. }
  565. return nil
  566. })
  567. }
  568. func shaFile(file string) error {
  569. fd, err := os.Open(file)
  570. if err != nil {
  571. return err
  572. }
  573. defer fd.Close()
  574. h := sha256.New()
  575. _, err = io.Copy(h, fd)
  576. if err != nil {
  577. return err
  578. }
  579. out, err := os.Create(file + ".sha256")
  580. if err != nil {
  581. return err
  582. }
  583. _, err = fmt.Fprintf(out, "%x\n", h.Sum(nil))
  584. if err != nil {
  585. return err
  586. }
  587. return out.Close()
  588. }
  589. func shortenBuildId(buildId string) string {
  590. buildId = strings.Replace(buildId, "-", "", -1)
  591. if len(buildId) < 9 {
  592. return buildId
  593. }
  594. return buildId[0:8]
  595. }