mirror of
https://github.com/Luzifer/go-latestver.git
synced 2024-12-20 18:41:17 +00:00
110 lines
3.1 KiB
Go
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
|
|
}
|