main.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "path"
  7. "strings"
  8. )
  9. // bunch of spaget but it does the job for now
  10. // TODO: tidy up and add a proper build system with CI
  11. const buildPath = "./build"
  12. const archivesPath = "./build/archives"
  13. const executableName = "glance"
  14. const ownerAndRepo = "glanceapp/glance"
  15. const moduleName = "github.com/" + ownerAndRepo
  16. type archiveType int
  17. const (
  18. archiveTypeTarGz archiveType = iota
  19. archiveTypeZip
  20. )
  21. type buildInfo struct {
  22. version string
  23. }
  24. type buildTarget struct {
  25. os string
  26. arch string
  27. armV int
  28. extension string
  29. archive archiveType
  30. }
  31. var buildTargets = []buildTarget{
  32. {
  33. os: "windows",
  34. arch: "amd64",
  35. extension: ".exe",
  36. archive: archiveTypeZip,
  37. },
  38. {
  39. os: "windows",
  40. arch: "arm64",
  41. extension: ".exe",
  42. archive: archiveTypeZip,
  43. },
  44. {
  45. os: "linux",
  46. arch: "amd64",
  47. },
  48. {
  49. os: "linux",
  50. arch: "arm64",
  51. },
  52. {
  53. os: "linux",
  54. arch: "arm",
  55. armV: 6,
  56. },
  57. {
  58. os: "linux",
  59. arch: "arm",
  60. armV: 7,
  61. },
  62. {
  63. os: "openbsd",
  64. arch: "amd64",
  65. },
  66. {
  67. os: "openbsd",
  68. arch: "386",
  69. },
  70. }
  71. func main() {
  72. cwd, err := os.Getwd()
  73. if err != nil {
  74. fmt.Println(err)
  75. os.Exit(1)
  76. }
  77. _, err = os.Stat(buildPath)
  78. if err == nil {
  79. fmt.Println("Cleaning up build path")
  80. os.RemoveAll(buildPath)
  81. }
  82. os.Mkdir(buildPath, 0755)
  83. os.Mkdir(archivesPath, 0755)
  84. version, err := getVersionFromGit()
  85. if err != nil {
  86. fmt.Println(version, err)
  87. os.Exit(1)
  88. }
  89. info := buildInfo{
  90. version: version,
  91. }
  92. for _, target := range buildTargets {
  93. fmt.Printf("Building for %s/%s\n", target.os, target.arch)
  94. if err := build(cwd, info, target); err != nil {
  95. fmt.Println(err)
  96. os.Exit(1)
  97. }
  98. }
  99. versionTag := fmt.Sprintf("%s:%s", ownerAndRepo, version)
  100. latestTag := fmt.Sprintf("%s:latest", ownerAndRepo)
  101. fmt.Println("Building docker image")
  102. output, err := exec.Command(
  103. "sudo", "docker", "build",
  104. "--platform=linux/amd64,linux/arm64,linux/arm/v7",
  105. "-t", versionTag,
  106. "-t", latestTag,
  107. ".",
  108. ).CombinedOutput()
  109. if err != nil {
  110. fmt.Println(string(output))
  111. fmt.Println(err)
  112. os.Exit(1)
  113. }
  114. var input string
  115. fmt.Print("Push docker image? [y/n]: ")
  116. fmt.Scanln(&input)
  117. if input != "y" {
  118. os.Exit(0)
  119. }
  120. output, err = exec.Command(
  121. "sudo", "docker", "push", versionTag,
  122. ).CombinedOutput()
  123. if err != nil {
  124. fmt.Printf("Failed pushing %s:\n", versionTag)
  125. fmt.Println(string(output))
  126. fmt.Println(err)
  127. os.Exit(1)
  128. }
  129. output, err = exec.Command(
  130. "sudo", "docker", "push", latestTag,
  131. ).CombinedOutput()
  132. if err != nil {
  133. fmt.Printf("Failed pushing %s:\n", latestTag)
  134. fmt.Println(string(output))
  135. fmt.Println(err)
  136. os.Exit(1)
  137. }
  138. }
  139. func getVersionFromGit() (string, error) {
  140. output, err := exec.Command("git", "describe", "--tags", "--abbrev=0").CombinedOutput()
  141. if err == nil {
  142. return strings.TrimSpace(string(output)), err
  143. }
  144. return string(output), err
  145. }
  146. func archiveFile(name string, target string, t archiveType) error {
  147. var output []byte
  148. var err error
  149. if t == archiveTypeZip {
  150. output, err = exec.Command("zip", "-j", path.Join(archivesPath, name+".zip"), target).CombinedOutput()
  151. } else if t == archiveTypeTarGz {
  152. output, err = exec.Command("tar", "-C", buildPath, "-czf", path.Join(archivesPath, name+".tar.gz"), name).CombinedOutput()
  153. }
  154. if err != nil {
  155. fmt.Println(string(output))
  156. return err
  157. }
  158. return nil
  159. }
  160. func build(workingDir string, info buildInfo, target buildTarget) error {
  161. var name string
  162. if target.arch != "arm" {
  163. name = fmt.Sprintf("%s-%s-%s%s", executableName, target.os, target.arch, target.extension)
  164. } else {
  165. name = fmt.Sprintf("%s-%s-%sv%d", executableName, target.os, target.arch, target.armV)
  166. }
  167. binaryPath := path.Join(buildPath, name)
  168. glancePackage := moduleName + "/internal/glance"
  169. flags := "-s -w"
  170. flags += fmt.Sprintf(" -X %s.buildVersion=%s", glancePackage, info.version)
  171. cmd := exec.Command(
  172. "go",
  173. "build",
  174. "--trimpath",
  175. "--ldflags",
  176. flags,
  177. "-o",
  178. binaryPath,
  179. )
  180. cmd.Dir = workingDir
  181. env := append(os.Environ(), "GOOS="+target.os, "GOARCH="+target.arch, "CGO_ENABLED=0")
  182. if target.arch == "arm" {
  183. env = append(env, fmt.Sprintf("GOARM=%d", target.armV))
  184. }
  185. cmd.Env = env
  186. output, err := cmd.CombinedOutput()
  187. if err != nil {
  188. fmt.Println(err)
  189. fmt.Println(string(output))
  190. return err
  191. }
  192. os.Chmod(binaryPath, 0755)
  193. fmt.Println("Creating archive")
  194. if err := archiveFile(name, binaryPath, target.archive); err != nil {
  195. return err
  196. }
  197. return nil
  198. }