publisher.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. os: "win-installer",
  160. arch: "amd64",
  161. urlPostfix: ".windows-amd64.msi",
  162. },
  163. }
  164. type artifactFilter struct {
  165. os string
  166. arch string
  167. }
  168. func filterBuildArtifacts(filters []artifactFilter) ([]buildArtifact, error) {
  169. var artifacts []buildArtifact
  170. for _, f := range filters {
  171. matched := false
  172. for _, a := range completeBuildArtifactConfigurations {
  173. if f.os == a.os && f.arch == a.arch {
  174. artifacts = append(artifacts, a)
  175. matched = true
  176. break
  177. }
  178. }
  179. if !matched {
  180. return nil, fmt.Errorf("No buildArtifact for os=%v, arch=%v", f.os, f.arch)
  181. }
  182. }
  183. return artifacts, nil
  184. }
  185. func newBuild(baseArchiveURL string, ba buildArtifact, version string, rt releaseType, sha256 string) build {
  186. return build{
  187. Os: ba.os,
  188. URL: ba.getURL(baseArchiveURL, version, rt),
  189. Sha256: sha256,
  190. Arch: ba.arch,
  191. }
  192. }
  193. func (p *publisher) apiURL(url string) string {
  194. return fmt.Sprintf("%s/%s%s", p.apiURI, p.product, url)
  195. }
  196. func (p *publisher) postRequest(url string, obj interface{}, desc string) error {
  197. jsonBytes, err := json.Marshal(obj)
  198. if err != nil {
  199. return err
  200. }
  201. if p.dryRun {
  202. log.Println(fmt.Sprintf("POST to %s:", p.apiURL(url)))
  203. log.Println(string(jsonBytes))
  204. return nil
  205. }
  206. req, err := http.NewRequest(http.MethodPost, p.apiURL(url), bytes.NewReader(jsonBytes))
  207. if err != nil {
  208. return err
  209. }
  210. req.Header.Add("Authorization", "Bearer "+p.apiKey)
  211. req.Header.Add("Content-Type", "application/json")
  212. res, err := http.DefaultClient.Do(req)
  213. if err != nil {
  214. return err
  215. }
  216. if res.StatusCode == http.StatusOK {
  217. log.Printf("Action: %s \t OK", desc)
  218. return nil
  219. }
  220. if res.Body != nil {
  221. defer res.Body.Close()
  222. body, err := ioutil.ReadAll(res.Body)
  223. if err != nil {
  224. return err
  225. }
  226. if strings.Contains(string(body), "already exists") || strings.Contains(string(body), "Nothing to update") {
  227. log.Printf("Action: %s \t Already exists", desc)
  228. } else {
  229. log.Printf("Action: %s \t Failed - Status: %v", desc, res.Status)
  230. log.Printf("Resp: %s", body)
  231. log.Fatalf("Quiting")
  232. }
  233. }
  234. return nil
  235. }
  236. type release struct {
  237. Version string `json:"version"`
  238. ReleaseDate time.Time `json:"releaseDate"`
  239. Stable bool `json:"stable"`
  240. Beta bool `json:"beta"`
  241. Nightly bool `json:"nightly"`
  242. WhatsNewURL string `json:"whatsNewUrl"`
  243. ReleaseNotesURL string `json:"releaseNotesUrl"`
  244. Builds []build `json:"-"`
  245. }
  246. type build struct {
  247. Os string `json:"os"`
  248. URL string `json:"url"`
  249. Sha256 string `json:"sha256"`
  250. Arch string `json:"arch"`
  251. }