Переглянути джерело

releases: Add support for gitlab

Albin Parou 1 рік тому
батько
коміт
7d1ede8c91
3 змінених файлів з 127 додано та 3 видалено
  1. 5 1
      docs/configuration.md
  2. 116 1
      internal/feed/git_forge.go
  3. 6 1
      internal/widget/releases.go

+ 5 - 1
docs/configuration.md

@@ -1008,6 +1008,7 @@ Preview:
 | token | string | no | |
 | limit | integer | no | 10 |
 | collapse-after | integer | no | 5 |
+| source | string | no | github |
 
 ##### `repositories`
 A list of repositores for which to fetch the latest release for. Only the name/repo is required, not the full URL.
@@ -1039,7 +1040,10 @@ This way you can safely check your `glance.yml` in version control without expos
 The maximum number of releases to show.
 
 #### `collapse-after`
-How many releases are visible before the "SHOW MORE" button appears. Set to `-1` to never collapse.
+how many releases are visible before the "show more" button appears. set to `-1` to never collapse.
+
+#### `source`
+Either `github` or `gitlab`. Wether to retrieve the releases from github repositories or gitlab repositories.
 
 ### Repository
 Display general information about a repository as well as a list of the latest open pull requests and issues.

+ 116 - 1
internal/feed/github.go → internal/feed/git_forge.go

@@ -1,9 +1,11 @@
 package feed
 
 import (
+	"errors"
 	"fmt"
 	"log/slog"
 	"net/http"
+	"net/url"
 	"sync"
 	"time"
 )
@@ -17,6 +19,19 @@ type githubReleaseLatestResponseJson struct {
 	} `json:"reactions"`
 }
 
+type gitlabReleaseResponseJson struct {
+	TagName     string `json:"tag_name"`
+	PublishedAt string `json:"created_at"`
+	Links       struct {
+		Self string `json:"self"`
+	} `json:"_links"`
+	Draft       bool   `json:"draft"`
+	PreRelease  bool   `json:"prerelease"`
+	Reactions   struct {
+		Downvotes int `json:"-1"`
+	} `json:"reactions"`
+}
+
 func parseGithubTime(t string) time.Time {
 	parsedTime, err := time.Parse("2006-01-02T15:04:05Z", t)
 
@@ -27,7 +42,107 @@ func parseGithubTime(t string) time.Time {
 	return parsedTime
 }
 
-func FetchLatestReleasesFromGithub(repositories []string, token string) (AppReleases, error) {
+func FetchLatestReleasesFromGitForge(repositories []string, token string, source string) (AppReleases, error) {
+	switch source {
+	case "github":
+		return fetchLatestReleasesFromGithub(repositories, token)
+	case "gitlab":
+		return fetchLatestReleasesFromGitlab(repositories, token)
+	default:
+		return nil, errors.New(fmt.Sprintf("Release source %s is invalid", source))
+	}
+}
+
+func fetchLatestReleasesFromGitlab(repositories []string, token string) (AppReleases, error) {
+	appReleases := make(AppReleases, 0, len(repositories))
+
+	if len(repositories) == 0 {
+		return appReleases, nil
+	}
+
+	requests := make([]*http.Request, len(repositories))
+
+	for i, repository := range repositories {
+		request, _ := http.NewRequest("GET", fmt.Sprintf("https://gitlab.com/api/v4/projects/%s/releases/", url.QueryEscape(repository)), nil)
+
+		if token != "" {
+			request.Header.Add("PRIVATE-TOKEN", token)
+		}
+
+		requests[i] = request
+	}
+
+	task := decodeJsonFromRequestTask[[]gitlabReleaseResponseJson](defaultClient)
+	job := newJob(task, requests).withWorkers(15)
+	responses, errs, err := workerPoolDo(job)
+
+	if err != nil {
+		return nil, err
+	}
+
+	var failed int
+
+	for i := range responses {
+		if errs[i] != nil {
+			failed++
+			slog.Error("Failed to fetch or parse gitlab release", "error", errs[i], "url", requests[i].URL)
+			continue
+		}
+
+		releases := responses[i]
+
+		if len(releases) < 1 {
+			failed++
+			slog.Error("No releases found", "repository", repositories[i], "url", requests[i].URL)
+			continue
+		}
+
+		var liveRelease *gitlabReleaseResponseJson
+
+		for i := range releases {
+			release := &releases[i]
+
+			if !release.Draft && !release.PreRelease {
+				liveRelease = release
+				break
+			}
+		}
+
+		if liveRelease == nil {
+			slog.Error("No live release found", "repository", repositories[i], "url", requests[i].URL)
+			continue
+		}
+
+		version := liveRelease.TagName
+
+		if version[0] != 'v' {
+			version = "v" + version
+		}
+
+		appReleases = append(appReleases, AppRelease{
+			Name:         repositories[i],
+			Version:      version,
+			NotesUrl:     liveRelease.Links.Self,
+			TimeReleased: parseGithubTime(liveRelease.PublishedAt),
+			Downvotes:    liveRelease.Reactions.Downvotes,
+		})
+	}
+
+	if len(appReleases) == 0 {
+		return nil, ErrNoContent
+	}
+
+	appReleases.SortByNewest()
+
+	if failed > 0 {
+		return appReleases, fmt.Errorf("%w: could not get %d releases", ErrPartialContent, failed)
+	}
+
+	return appReleases, nil
+}
+
+
+func fetchLatestReleasesFromGithub(repositories []string, token string) (AppReleases, error) {
 	appReleases := make(AppReleases, 0, len(repositories))
 
 	if len(repositories) == 0 {

+ 6 - 1
internal/widget/releases.go

@@ -16,6 +16,7 @@ type Releases struct {
 	Token         OptionalEnvString `yaml:"token"`
 	Limit         int               `yaml:"limit"`
 	CollapseAfter int               `yaml:"collapse-after"`
+	Source        string            `yaml:"source"`
 }
 
 func (widget *Releases) Initialize() error {
@@ -29,11 +30,15 @@ func (widget *Releases) Initialize() error {
 		widget.CollapseAfter = 5
 	}
 
+	if widget.Source == "" {
+		widget.Source = "github"
+	}
+
 	return nil
 }
 
 func (widget *Releases) Update(ctx context.Context) {
-	releases, err := feed.FetchLatestReleasesFromGithub(widget.Repositories, string(widget.Token))
+	releases, err := feed.FetchLatestReleasesFromGitForge(widget.Repositories, string(widget.Token), widget.Source)
 
 	if !widget.canContinueUpdateAfterHandlingErr(err) {
 		return