publisher.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. apiURI string
  15. product string
  16. dryRun bool
  17. enterprise bool
  18. baseArchiveURL string
  19. builder releaseBuilder
  20. }
  21. type releaseBuilder interface {
  22. prepareRelease(baseArchiveURL, whatsNewURL string, releaseNotesURL string, nightly bool) (*release, error)
  23. }
  24. func (p *publisher) doRelease(whatsNewURL string, releaseNotesURL string, nightly bool) error {
  25. currentRelease, err := p.builder.prepareRelease(p.baseArchiveURL, whatsNewURL, releaseNotesURL, nightly)
  26. if err != nil {
  27. return err
  28. }
  29. if err := p.postRelease(currentRelease); err != nil {
  30. return err
  31. }
  32. return nil
  33. }
  34. func (p *publisher) postRelease(r *release) error {
  35. err := p.postRequest("/versions", r, fmt.Sprintf("Create Release %s", r.Version))
  36. if err != nil {
  37. return err
  38. }
  39. err = p.postRequest("/versions/"+r.Version, r, fmt.Sprintf("Update Release %s", r.Version))
  40. if err != nil {
  41. return err
  42. }
  43. for _, b := range r.Builds {
  44. err = p.postRequest(fmt.Sprintf("/versions/%s/packages", r.Version), b, fmt.Sprintf("Create Build %s %s", b.Os, b.Arch))
  45. if err != nil {
  46. return err
  47. }
  48. err = p.postRequest(fmt.Sprintf("/versions/%s/packages/%s/%s", r.Version, b.Arch, b.Os), b, fmt.Sprintf("Update Build %s %s", b.Os, b.Arch))
  49. if err != nil {
  50. return err
  51. }
  52. }
  53. return nil
  54. }
  55. type releaseType int
  56. const (
  57. // STABLE is a release type constant
  58. STABLE releaseType = iota + 1
  59. // BETA is a release type constant
  60. BETA
  61. // NIGHTLY is a release type constant
  62. NIGHTLY
  63. )
  64. func (rt releaseType) beta() bool {
  65. return rt == BETA
  66. }
  67. func (rt releaseType) stable() bool {
  68. return rt == STABLE
  69. }
  70. func (rt releaseType) nightly() bool {
  71. return rt == NIGHTLY
  72. }
  73. type buildArtifact struct {
  74. os string
  75. arch string
  76. urlPostfix string
  77. packagePostfix string
  78. }
  79. func (t buildArtifact) getURL(baseArchiveURL, version string, releaseType releaseType) string {
  80. prefix := "-"
  81. rhelReleaseExtra := ""
  82. if t.os == "deb" {
  83. prefix = "_"
  84. }
  85. if releaseType.stable() && t.os == "rhel" {
  86. rhelReleaseExtra = "-1"
  87. }
  88. url := strings.Join([]string{baseArchiveURL, t.packagePostfix, prefix, version, rhelReleaseExtra, t.urlPostfix}, "")
  89. return url
  90. }
  91. var completeBuildArtifactConfigurations = []buildArtifact{
  92. {
  93. os: "deb",
  94. arch: "arm64",
  95. urlPostfix: "_arm64.deb",
  96. },
  97. {
  98. os: "rhel",
  99. arch: "arm64",
  100. urlPostfix: ".aarch64.rpm",
  101. },
  102. {
  103. os: "linux",
  104. arch: "arm64",
  105. urlPostfix: ".linux-arm64.tar.gz",
  106. },
  107. {
  108. os: "deb",
  109. arch: "armv7",
  110. urlPostfix: "_armhf.deb",
  111. },
  112. {
  113. os: "deb",
  114. arch: "armv6",
  115. packagePostfix: "-rpi",
  116. urlPostfix: "_armhf.deb",
  117. },
  118. {
  119. os: "rhel",
  120. arch: "armv7",
  121. urlPostfix: ".armhfp.rpm",
  122. },
  123. {
  124. os: "linux",
  125. arch: "armv6",
  126. urlPostfix: ".linux-armv6.tar.gz",
  127. },
  128. {
  129. os: "linux",
  130. arch: "armv7",
  131. urlPostfix: ".linux-armv7.tar.gz",
  132. },
  133. {
  134. os: "darwin",
  135. arch: "amd64",
  136. urlPostfix: ".darwin-amd64.tar.gz",
  137. },
  138. {
  139. os: "deb",
  140. arch: "amd64",
  141. urlPostfix: "_amd64.deb",
  142. },
  143. {
  144. os: "rhel",
  145. arch: "amd64",
  146. urlPostfix: ".x86_64.rpm",
  147. },
  148. {
  149. os: "linux",
  150. arch: "amd64",
  151. urlPostfix: ".linux-amd64.tar.gz",
  152. },
  153. {
  154. os: "win",
  155. arch: "amd64",
  156. urlPostfix: ".windows-amd64.zip",
  157. },
  158. }
  159. type artifactFilter struct {
  160. os string
  161. arch string
  162. }
  163. func filterBuildArtifacts(filters []artifactFilter) ([]buildArtifact, error) {
  164. var artifacts []buildArtifact
  165. for _, f := range filters {
  166. matched := false
  167. for _, a := range completeBuildArtifactConfigurations {
  168. if f.os == a.os && f.arch == a.arch {
  169. artifacts = append(artifacts, a)
  170. matched = true
  171. break
  172. }
  173. }
  174. if !matched {
  175. return nil, fmt.Errorf("No buildArtifact for os=%v, arch=%v", f.os, f.arch)
  176. }
  177. }
  178. return artifacts, nil
  179. }
  180. func newBuild(baseArchiveURL string, ba buildArtifact, version string, rt releaseType, sha256 string) build {
  181. return build{
  182. Os: ba.os,
  183. URL: ba.getURL(baseArchiveURL, version, rt),
  184. Sha256: sha256,
  185. Arch: ba.arch,
  186. }
  187. }
  188. func (p *publisher) apiURL(url string) string {
  189. return fmt.Sprintf("%s/%s%s", p.apiURI, p.product, url)
  190. }
  191. func (p *publisher) postRequest(url string, obj interface{}, desc string) error {
  192. jsonBytes, err := json.Marshal(obj)
  193. if err != nil {
  194. return err
  195. }
  196. if p.dryRun {
  197. log.Println(fmt.Sprintf("POST to %s:", p.apiURL(url)))
  198. log.Println(string(jsonBytes))
  199. return nil
  200. }
  201. req, err := http.NewRequest(http.MethodPost, p.apiURL(url), bytes.NewReader(jsonBytes))
  202. if err != nil {
  203. return err
  204. }
  205. req.Header.Add("Authorization", "Bearer "+p.apiKey)
  206. req.Header.Add("Content-Type", "application/json")
  207. res, err := http.DefaultClient.Do(req)
  208. if err != nil {
  209. return err
  210. }
  211. if res.StatusCode == http.StatusOK {
  212. log.Printf("Action: %s \t OK", desc)
  213. return nil
  214. }
  215. if res.Body != nil {
  216. defer res.Body.Close()
  217. body, err := ioutil.ReadAll(res.Body)
  218. if err != nil {
  219. return err
  220. }
  221. if strings.Contains(string(body), "already exists") || strings.Contains(string(body), "Nothing to update") {
  222. log.Printf("Action: %s \t Already exists", desc)
  223. } else {
  224. log.Printf("Action: %s \t Failed - Status: %v", desc, res.Status)
  225. log.Printf("Resp: %s", body)
  226. log.Fatalf("Quiting")
  227. }
  228. }
  229. return nil
  230. }
  231. type release struct {
  232. Version string `json:"version"`
  233. ReleaseDate time.Time `json:"releaseDate"`
  234. Stable bool `json:"stable"`
  235. Beta bool `json:"beta"`
  236. Nightly bool `json:"nightly"`
  237. WhatsNewURL string `json:"whatsNewUrl"`
  238. ReleaseNotesURL string `json:"releaseNotesUrl"`
  239. Builds []build `json:"-"`
  240. }
  241. type build struct {
  242. Os string `json:"os"`
  243. URL string `json:"url"`
  244. Sha256 string `json:"sha256"`
  245. Arch string `json:"arch"`
  246. }