publisher.go 5.6 KB

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