publisher.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "net/http"
  9. "strings"
  10. "time"
  11. )
  12. type publisher struct {
  13. apiKey string
  14. }
  15. func (p *publisher) doRelease(version string, whatsNewUrl string, releaseNotesUrl string, dryRun bool) error {
  16. currentRelease, err := newRelease(version, whatsNewUrl, releaseNotesUrl, buildArtifactConfigurations, getHttpContents{})
  17. if err != nil {
  18. return err
  19. }
  20. if dryRun {
  21. relJson, err := json.Marshal(currentRelease)
  22. if err != nil {
  23. return err
  24. }
  25. log.Println(string(relJson))
  26. for _, b := range currentRelease.Builds {
  27. artifactJson, err := json.Marshal(b)
  28. if err != nil {
  29. return err
  30. }
  31. log.Println(string(artifactJson))
  32. }
  33. } else {
  34. if err := p.postRelease(currentRelease); err != nil {
  35. return err
  36. }
  37. }
  38. return nil
  39. }
  40. func (p *publisher) postRelease(r *release) error {
  41. err := p.postRequest("/grafana/versions", r, fmt.Sprintf("Create Release %s", r.Version))
  42. if err != nil {
  43. return err
  44. }
  45. err = p.postRequest("/grafana/versions/"+r.Version, r, fmt.Sprintf("Update Release %s", r.Version))
  46. if err != nil {
  47. return err
  48. }
  49. for _, b := range r.Builds {
  50. err = p.postRequest(fmt.Sprintf("/grafana/versions/%s/packages", r.Version), b, fmt.Sprintf("Create Build %s %s", b.Os, b.Arch))
  51. if err != nil {
  52. return err
  53. }
  54. err = p.postRequest(fmt.Sprintf("/grafana/versions/%s/packages/%s/%s", r.Version, b.Arch, b.Os), b, fmt.Sprintf("Update Build %s %s", b.Os, b.Arch))
  55. if err != nil {
  56. return err
  57. }
  58. }
  59. return nil
  60. }
  61. const baseArhiveUrl = "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana"
  62. type buildArtifact struct {
  63. os string
  64. arch string
  65. urlPostfix string
  66. }
  67. func (t buildArtifact) getUrl(version string, isBeta bool) string {
  68. prefix := "-"
  69. rhelReleaseExtra := ""
  70. if t.os == "deb" {
  71. prefix = "_"
  72. }
  73. if !isBeta && t.os == "rhel" {
  74. rhelReleaseExtra = "-1"
  75. }
  76. url := strings.Join([]string{baseArhiveUrl, prefix, version, rhelReleaseExtra, t.urlPostfix}, "")
  77. return url
  78. }
  79. var buildArtifactConfigurations = []buildArtifact{
  80. {
  81. os: "deb",
  82. arch: "arm64",
  83. urlPostfix: "_arm64.deb",
  84. },
  85. {
  86. os: "rhel",
  87. arch: "arm64",
  88. urlPostfix: ".aarch64.rpm",
  89. },
  90. {
  91. os: "linux",
  92. arch: "arm64",
  93. urlPostfix: ".linux-arm64.tar.gz",
  94. },
  95. {
  96. os: "deb",
  97. arch: "armv7",
  98. urlPostfix: "_armhf.deb",
  99. },
  100. {
  101. os: "rhel",
  102. arch: "armv7",
  103. urlPostfix: ".armhfp.rpm",
  104. },
  105. {
  106. os: "linux",
  107. arch: "armv7",
  108. urlPostfix: ".linux-armv7.tar.gz",
  109. },
  110. {
  111. os: "darwin",
  112. arch: "amd64",
  113. urlPostfix: ".darwin-amd64.tar.gz",
  114. },
  115. {
  116. os: "deb",
  117. arch: "amd64",
  118. urlPostfix: "_amd64.deb",
  119. },
  120. {
  121. os: "rhel",
  122. arch: "amd64",
  123. urlPostfix: ".x86_64.rpm",
  124. },
  125. {
  126. os: "linux",
  127. arch: "amd64",
  128. urlPostfix: ".linux-amd64.tar.gz",
  129. },
  130. {
  131. os: "win",
  132. arch: "amd64",
  133. urlPostfix: ".windows-amd64.zip",
  134. },
  135. }
  136. func newRelease(rawVersion string, whatsNewUrl string, releaseNotesUrl string, artifactConfigurations []buildArtifact, getter urlGetter) (*release, error) {
  137. version := rawVersion[1:]
  138. now := time.Now()
  139. isBeta := strings.Contains(version, "beta")
  140. builds := []build{}
  141. for _, ba := range artifactConfigurations {
  142. sha256, err := getter.getContents(fmt.Sprintf("%s.sha256", ba.getUrl(version, isBeta)))
  143. if err != nil {
  144. return nil, err
  145. }
  146. builds = append(builds, newBuild(ba, version, isBeta, sha256))
  147. }
  148. r := release{
  149. Version: version,
  150. ReleaseDate: time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local),
  151. Stable: !isBeta,
  152. Beta: isBeta,
  153. Nightly: false,
  154. WhatsNewUrl: whatsNewUrl,
  155. ReleaseNotesUrl: releaseNotesUrl,
  156. Builds: builds,
  157. }
  158. return &r, nil
  159. }
  160. func newBuild(ba buildArtifact, version string, isBeta bool, sha256 string) build {
  161. return build{
  162. Os: ba.os,
  163. Url: ba.getUrl(version, isBeta),
  164. Sha256: sha256,
  165. Arch: ba.arch,
  166. }
  167. }
  168. func (p *publisher) postRequest(url string, obj interface{}, desc string) error {
  169. jsonBytes, err := json.Marshal(obj)
  170. if err != nil {
  171. return err
  172. }
  173. req, err := http.NewRequest(http.MethodPost, baseUri+url, bytes.NewReader(jsonBytes))
  174. if err != nil {
  175. return err
  176. }
  177. req.Header.Add("Authorization", "Bearer "+p.apiKey)
  178. req.Header.Add("Content-Type", "application/json")
  179. res, err := http.DefaultClient.Do(req)
  180. if err != nil {
  181. return err
  182. }
  183. if res.StatusCode == http.StatusOK {
  184. log.Printf("Action: %s \t OK", desc)
  185. return nil
  186. }
  187. if res.Body != nil {
  188. defer res.Body.Close()
  189. body, err := ioutil.ReadAll(res.Body)
  190. if err != nil {
  191. return err
  192. }
  193. if strings.Contains(string(body), "already exists") || strings.Contains(string(body), "Nothing to update") {
  194. log.Printf("Action: %s \t Already exists", desc)
  195. } else {
  196. log.Printf("Action: %s \t Failed - Status: %v", desc, res.Status)
  197. log.Printf("Resp: %s", body)
  198. log.Fatalf("Quiting")
  199. }
  200. }
  201. return nil
  202. }
  203. type release struct {
  204. Version string `json:"version"`
  205. ReleaseDate time.Time `json:"releaseDate"`
  206. Stable bool `json:"stable"`
  207. Beta bool `json:"beta"`
  208. Nightly bool `json:"nightly"`
  209. WhatsNewUrl string `json:"whatsNewUrl"`
  210. ReleaseNotesUrl string `json:"releaseNotesUrl"`
  211. Builds []build `json:"-"`
  212. }
  213. type build struct {
  214. Os string `json:"os"`
  215. Url string `json:"url"`
  216. Sha256 string `json:"sha256"`
  217. Arch string `json:"arch"`
  218. }
  219. type urlGetter interface {
  220. getContents(url string) (string, error)
  221. }
  222. type getHttpContents struct{}
  223. func (getHttpContents) getContents(url string) (string, error) {
  224. response, err := http.Get(url)
  225. if err != nil {
  226. return "", err
  227. }
  228. defer response.Body.Close()
  229. all, err := ioutil.ReadAll(response.Body)
  230. if err != nil {
  231. return "", err
  232. }
  233. return string(all), nil
  234. }