github.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package feed
  2. import (
  3. "fmt"
  4. "log/slog"
  5. "net/http"
  6. "time"
  7. )
  8. type githubReleaseResponseJson struct {
  9. TagName string `json:"tag_name"`
  10. PublishedAt string `json:"published_at"`
  11. HtmlUrl string `json:"html_url"`
  12. Draft bool `json:"draft"`
  13. PreRelease bool `json:"prerelease"`
  14. Reactions struct {
  15. Downvotes int `json:"-1"`
  16. } `json:"reactions"`
  17. }
  18. func parseGithubTime(t string) time.Time {
  19. parsedTime, err := time.Parse("2006-01-02T15:04:05Z", t)
  20. if err != nil {
  21. return time.Now()
  22. }
  23. return parsedTime
  24. }
  25. func FetchLatestReleasesFromGithub(repositories []string, token string) (AppReleases, error) {
  26. appReleases := make(AppReleases, 0, len(repositories))
  27. if len(repositories) == 0 {
  28. return appReleases, nil
  29. }
  30. requests := make([]*http.Request, len(repositories))
  31. for i, repository := range repositories {
  32. request, _ := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/repos/%s/releases?per_page=10", repository), nil)
  33. if token != "" {
  34. request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
  35. }
  36. requests[i] = request
  37. }
  38. task := decodeJsonFromRequestTask[[]githubReleaseResponseJson](defaultClient)
  39. job := newJob(task, requests).withWorkers(15)
  40. responses, errs, err := workerPoolDo(job)
  41. if err != nil {
  42. return nil, err
  43. }
  44. var failed int
  45. for i := range responses {
  46. if errs[i] != nil {
  47. failed++
  48. slog.Error("Failed to fetch or parse github release", "error", errs[i], "url", requests[i].URL)
  49. continue
  50. }
  51. releases := responses[i]
  52. if len(releases) < 1 {
  53. failed++
  54. slog.Error("No releases found", "repository", repositories[i], "url", requests[i].URL)
  55. continue
  56. }
  57. var liveRelease *githubReleaseResponseJson
  58. for i := range releases {
  59. release := &releases[i]
  60. if !release.Draft && !release.PreRelease {
  61. liveRelease = release
  62. break
  63. }
  64. }
  65. if liveRelease == nil {
  66. slog.Error("No live release found", "repository", repositories[i], "url", requests[i].URL)
  67. continue
  68. }
  69. version := liveRelease.TagName
  70. if version[0] != 'v' {
  71. version = "v" + version
  72. }
  73. appReleases = append(appReleases, AppRelease{
  74. Name: repositories[i],
  75. Version: version,
  76. NotesUrl: liveRelease.HtmlUrl,
  77. TimeReleased: parseGithubTime(liveRelease.PublishedAt),
  78. Downvotes: liveRelease.Reactions.Downvotes,
  79. })
  80. }
  81. if len(appReleases) == 0 {
  82. return nil, ErrNoContent
  83. }
  84. appReleases.SortByNewest()
  85. if failed > 0 {
  86. return appReleases, fmt.Errorf("%w: could not get %d releases", ErrPartialContent, failed)
  87. }
  88. return appReleases, nil
  89. }