go-latestver/internal/fetcher/github.go
Knut Ahlers 9255fc6898
Lint: Update linter config, fix all linter issues
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2023-03-18 17:57:13 +01:00

110 lines
3.1 KiB
Go

package fetcher
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
"github.com/pkg/errors"
"github.com/Luzifer/go-latestver/internal/database"
"github.com/Luzifer/go-latestver/internal/helpers"
"github.com/Luzifer/go_helpers/v2/fieldcollection"
)
/*
* @module github_release
* @module_desc Fetches the latest release from Github for a given repository not marked as pre-release
*/
const githubHTTPTimeout = 2 * time.Second
type (
// GithubReleaseFetcher implements the fetcher interface to monitor releases in a Github repository
GithubReleaseFetcher struct{}
githubRelease struct {
TagName string `json:"tag_name"`
PublishedAt time.Time `json:"published_at"`
Prerelease bool `json:"prerelease"`
}
)
func init() { registerFetcher("github_release", func() Fetcher { return &GithubReleaseFetcher{} }) }
// FetchVersion retrieves the latest version for the catalog entry
func (GithubReleaseFetcher) FetchVersion(ctx context.Context, attrs *fieldcollection.FieldCollection) (string, time.Time, error) {
ctx, cancel := context.WithTimeout(ctx, githubHTTPTimeout)
defer cancel()
req, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("https://api.github.com/repos/%s/releases", attrs.MustString("repository", nil)),
nil,
)
if err != nil {
return "", time.Time{}, errors.Wrap(err, "creating http request")
}
req.Header.Set("User-Agent", "Luzifer/go-latestver GithubReleaseFetcher")
if os.Getenv("GITHUB_CLIENT_ID") != "" && os.Getenv("GITHUB_CLIENT_SECRET") != "" {
req.SetBasicAuth(os.Getenv("GITHUB_CLIENT_ID"), os.Getenv("GITHUB_CLIENT_SECRET"))
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", time.Time{}, errors.Wrap(err, "executing request")
}
defer func() { helpers.LogIfErr(resp.Body.Close(), "closing response body after read") }()
if resp.StatusCode != http.StatusOK {
return "", time.Time{}, errors.Errorf("unexpected HTTP status %d", resp.StatusCode)
}
var payload []githubRelease
if err = json.NewDecoder(resp.Body).Decode(&payload); err != nil {
return "", time.Time{}, errors.Wrap(err, "decoding response")
}
var release *githubRelease
for i := range payload {
if payload[i].Prerelease {
continue
}
if release == nil || release.PublishedAt.Before(payload[i].PublishedAt) {
release = &payload[i]
}
}
if release == nil {
return "", time.Time{}, ErrNoVersionFound
}
return release.TagName, release.PublishedAt, nil
}
// Links retrieves a collection of links for the fetcher
func (GithubReleaseFetcher) Links(attrs *fieldcollection.FieldCollection) []database.CatalogLink {
return []database.CatalogLink{
{
IconClass: "fab fa-github",
Name: "Repository",
URL: fmt.Sprintf("https://github.com/%s", attrs.MustString("repository", nil)),
},
}
}
// Validate validates the configuration given to the fetcher
func (GithubReleaseFetcher) Validate(attrs *fieldcollection.FieldCollection) error {
// @attr repository required string "" Repository to fetch in form `owner/repo`
if v, err := attrs.String("repository"); err != nil || v == "" {
return errors.New("repository is expected to be non-empty string")
}
return nil
}