github.com/creativeprojects/go-selfupdate@v1.2.0/github_source.go (about) 1 package selfupdate 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 10 "github.com/google/go-github/v30/github" 11 "golang.org/x/oauth2" 12 ) 13 14 // GitHubConfig is an object to pass to NewGitHubSource 15 type GitHubConfig struct { 16 // APIToken represents GitHub API token. If it's not empty, it will be used for authentication of GitHub API 17 APIToken string 18 // EnterpriseBaseURL is a base URL of GitHub API. If you want to use this library with GitHub Enterprise, 19 // please set "https://{your-organization-address}/api/v3/" to this field. 20 EnterpriseBaseURL string 21 // EnterpriseUploadURL is a URL to upload stuffs to GitHub Enterprise instance. This is often the same as an API base URL. 22 // So if this field is not set and EnterpriseBaseURL is set, EnterpriseBaseURL is also set to this field. 23 EnterpriseUploadURL string 24 // Deprecated: Context option is no longer used 25 Context context.Context 26 } 27 28 // GitHubSource is used to load release information from GitHub 29 type GitHubSource struct { 30 api *github.Client 31 } 32 33 // NewGitHubSource creates a new GitHubSource from a config object. 34 // It initializes a GitHub API client. 35 // If you set your API token to the $GITHUB_TOKEN environment variable, the client will use it. 36 // You can pass an empty GitHubSource{} to use the default configuration 37 // The function will return an error if the GitHub Enterprise URLs in the config object cannot be parsed 38 func NewGitHubSource(config GitHubConfig) (*GitHubSource, error) { 39 token := config.APIToken 40 if token == "" { 41 // try the environment variable 42 token = os.Getenv("GITHUB_TOKEN") 43 } 44 hc := newHTTPClient(token) 45 46 if config.EnterpriseBaseURL == "" { 47 // public (or private) repository on standard GitHub offering 48 client := github.NewClient(hc) 49 return &GitHubSource{ 50 api: client, 51 }, nil 52 } 53 54 u := config.EnterpriseUploadURL 55 if u == "" { 56 u = config.EnterpriseBaseURL 57 } 58 client, err := github.NewEnterpriseClient(config.EnterpriseBaseURL, u, hc) 59 if err != nil { 60 return nil, fmt.Errorf("cannot parse GitHub enterprise URL: %w", err) 61 } 62 return &GitHubSource{ 63 api: client, 64 }, nil 65 } 66 67 // ListReleases returns all available releases 68 func (s *GitHubSource) ListReleases(ctx context.Context, repository Repository) ([]SourceRelease, error) { 69 owner, repo, err := repository.GetSlug() 70 if err != nil { 71 return nil, err 72 } 73 rels, res, err := s.api.Repositories.ListReleases(ctx, owner, repo, nil) 74 if err != nil { 75 if res != nil && res.StatusCode == 404 { 76 // 404 means repository not found or release not found. It's not an error here. 77 log.Print("Repository or release not found") 78 return nil, nil 79 } 80 log.Printf("API returned an error response: %s", err) 81 return nil, err 82 } 83 releases := make([]SourceRelease, len(rels)) 84 for i, rel := range rels { 85 releases[i] = NewGitHubRelease(rel) 86 } 87 return releases, nil 88 } 89 90 // DownloadReleaseAsset downloads an asset from a release. 91 // It returns an io.ReadCloser: it is your responsibility to Close it. 92 func (s *GitHubSource) DownloadReleaseAsset(ctx context.Context, rel *Release, assetID int64) (io.ReadCloser, error) { 93 if rel == nil { 94 return nil, ErrInvalidRelease 95 } 96 owner, repo, err := rel.repository.GetSlug() 97 if err != nil { 98 return nil, err 99 } 100 // create a new http client so the GitHub library can download the redirected file (if any) 101 client := http.DefaultClient 102 rc, _, err := s.api.Repositories.DownloadReleaseAsset(ctx, owner, repo, assetID, client) 103 if err != nil { 104 return nil, fmt.Errorf("failed to call GitHub Releases API for getting the asset ID %d on repository '%s/%s': %w", assetID, owner, repo, err) 105 } 106 return rc, nil 107 } 108 109 func newHTTPClient(token string) *http.Client { 110 if token == "" { 111 return http.DefaultClient 112 } 113 src := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) 114 return oauth2.NewClient(context.Background(), src) 115 } 116 117 // Verify interface 118 var _ Source = &GitHubSource{}