releases: Add support for gitlab

This commit is contained in:
Albin Parou 2024-07-03 08:17:51 +02:00
parent 39f40670d0
commit 7d1ede8c91
3 changed files with 127 additions and 3 deletions

View File

@ -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.

View File

@ -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 {

View File

@ -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