publisher.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. }
  78. func (t buildArtifact) getURL(baseArchiveURL, version string, releaseType releaseType) string {
  79. prefix := "-"
  80. rhelReleaseExtra := ""
  81. if t.os == "deb" {
  82. prefix = "_"
  83. }
  84. if releaseType.stable() && t.os == "rhel" {
  85. rhelReleaseExtra = "-1"
  86. }
  87. url := strings.Join([]string{baseArchiveURL, prefix, version, rhelReleaseExtra, t.urlPostfix}, "")
  88. return url
  89. }
  90. var completeBuildArtifactConfigurations = []buildArtifact{
  91. {
  92. os: "deb",
  93. arch: "arm64",
  94. urlPostfix: "_arm64.deb",
  95. },
  96. {
  97. os: "rhel",
  98. arch: "arm64",
  99. urlPostfix: ".aarch64.rpm",
  100. },
  101. {
  102. os: "linux",
  103. arch: "arm64",
  104. urlPostfix: ".linux-arm64.tar.gz",
  105. },
  106. {
  107. os: "deb",
  108. arch: "armv7",
  109. urlPostfix: "_armhf.deb",
  110. },
  111. {
  112. os: "deb",
  113. arch: "armv6",
  114. urlPostfix: "_armel.deb",
  115. },
  116. {
  117. os: "rhel",
  118. arch: "armv7",
  119. urlPostfix: ".armhfp.rpm",
  120. },
  121. {
  122. os: "linux",
  123. arch: "armv6",
  124. urlPostfix: ".linux-armv6.tar.gz",
  125. },
  126. {
  127. os: "linux",
  128. arch: "armv7",
  129. urlPostfix: ".linux-armv7.tar.gz",
  130. },
  131. {
  132. os: "darwin",
  133. arch: "amd64",
  134. urlPostfix: ".darwin-amd64.tar.gz",
  135. },
  136. {
  137. os: "deb",
  138. arch: "amd64",
  139. urlPostfix: "_amd64.deb",
  140. },
  141. {
  142. os: "rhel",
  143. arch: "amd64",
  144. urlPostfix: ".x86_64.rpm",
  145. },
  146. {
  147. os: "linux",
  148. arch: "amd64",
  149. urlPostfix: ".linux-amd64.tar.gz",
  150. },
  151. {
  152. os: "win",
  153. arch: "amd64",
  154. urlPostfix: ".windows-amd64.zip",
  155. },
  156. }
  157. type artifactFilter struct {
  158. os string
  159. arch string
  160. }
  161. func filterBuildArtifacts(filters []artifactFilter) ([]buildArtifact, error) {
  162. var artifacts []buildArtifact
  163. for _, f := range filters {
  164. matched := false
  165. for _, a := range completeBuildArtifactConfigurations {
  166. if f.os == a.os && f.arch == a.arch {
  167. artifacts = append(artifacts, a)
  168. matched = true
  169. break
  170. }
  171. }
  172. if !matched {
  173. return nil, fmt.Errorf("No buildArtifact for os=%v, arch=%v", f.os, f.arch)
  174. }
  175. }
  176. return artifacts, nil
  177. }
  178. func newBuild(baseArchiveURL string, ba buildArtifact, version string, rt releaseType, sha256 string) build {
  179. return build{
  180. Os: ba.os,
  181. URL: ba.getURL(baseArchiveURL, version, rt),
  182. Sha256: sha256,
  183. Arch: ba.arch,
  184. }
  185. }
  186. func (p *publisher) apiURL(url string) string {
  187. return fmt.Sprintf("%s/%s%s", p.apiURI, p.product, url)
  188. }
  189. func (p *publisher) postRequest(url string, obj interface{}, desc string) error {
  190. jsonBytes, err := json.Marshal(obj)
  191. if err != nil {
  192. return err
  193. }
  194. if p.dryRun {
  195. log.Println(fmt.Sprintf("POST to %s:", p.apiURL(url)))
  196. log.Println(string(jsonBytes))
  197. return nil
  198. }
  199. req, err := http.NewRequest(http.MethodPost, p.apiURL(url), bytes.NewReader(jsonBytes))
  200. if err != nil {
  201. return err
  202. }
  203. req.Header.Add("Authorization", "Bearer "+p.apiKey)
  204. req.Header.Add("Content-Type", "application/json")
  205. res, err := http.DefaultClient.Do(req)
  206. if err != nil {
  207. return err
  208. }
  209. if res.StatusCode == http.StatusOK {
  210. log.Printf("Action: %s \t OK", desc)
  211. return nil
  212. }
  213. if res.Body != nil {
  214. defer res.Body.Close()
  215. body, err := ioutil.ReadAll(res.Body)
  216. if err != nil {
  217. return err
  218. }
  219. if strings.Contains(string(body), "already exists") || strings.Contains(string(body), "Nothing to update") {
  220. log.Printf("Action: %s \t Already exists", desc)
  221. } else {
  222. log.Printf("Action: %s \t Failed - Status: %v", desc, res.Status)
  223. log.Printf("Resp: %s", body)
  224. log.Fatalf("Quiting")
  225. }
  226. }
  227. return nil
  228. }
  229. type release struct {
  230. Version string `json:"version"`
  231. ReleaseDate time.Time `json:"releaseDate"`
  232. Stable bool `json:"stable"`
  233. Beta bool `json:"beta"`
  234. Nightly bool `json:"nightly"`
  235. WhatsNewURL string `json:"whatsNewUrl"`
  236. ReleaseNotesURL string `json:"releaseNotesUrl"`
  237. Builds []build `json:"-"`
  238. }
  239. type build struct {
  240. Os string `json:"os"`
  241. URL string `json:"url"`
  242. Sha256 string `json:"sha256"`
  243. Arch string `json:"arch"`
  244. }