github.com/creativeprojects/go-selfupdate@v1.2.0/gitea_source.go (about) 1 package selfupdate 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 10 "code.gitea.io/sdk/gitea" 11 ) 12 13 // GiteaConfig is an object to pass to NewGiteaSource 14 type GiteaConfig struct { 15 // APIToken represents Gitea API token. If it's not empty, it will be used for authentication for the API 16 APIToken string 17 // BaseURL is a base URL of your gitea instance. This parameter has NO default value. 18 BaseURL string 19 // Deprecated: Context option is no longer used 20 Context context.Context 21 } 22 23 // GiteaSource is used to load release information from Gitea 24 type GiteaSource struct { 25 api *gitea.Client 26 token string 27 baseURL string 28 } 29 30 // NewGiteaSource creates a new NewGiteaSource from a config object. 31 // It initializes a Gitea API Client. 32 // If you set your API token to the $GITEA_TOKEN environment variable, the client will use it. 33 // You can pass an empty GiteaSource{} to use the default configuration 34 func NewGiteaSource(config GiteaConfig) (*GiteaSource, error) { 35 token := config.APIToken 36 if token == "" { 37 // try the environment variable 38 token = os.Getenv("GITEA_TOKEN") 39 } 40 if config.BaseURL == "" { 41 return nil, fmt.Errorf("gitea base url must be set") 42 } 43 44 ctx := config.Context 45 if ctx == nil { 46 ctx = context.Background() 47 } 48 49 client, err := gitea.NewClient(config.BaseURL, gitea.SetContext(ctx), gitea.SetToken(token)) 50 if err != nil { 51 return nil, fmt.Errorf("error connecting to gitea: %w", err) 52 } 53 54 return &GiteaSource{ 55 api: client, 56 token: token, 57 baseURL: config.BaseURL, 58 }, nil 59 } 60 61 // ListReleases returns all available releases 62 func (s *GiteaSource) ListReleases(ctx context.Context, repository Repository) ([]SourceRelease, error) { 63 owner, repo, err := repository.GetSlug() 64 if err != nil { 65 return nil, err 66 } 67 68 s.api.SetContext(ctx) 69 rels, res, err := s.api.ListReleases(owner, repo, gitea.ListReleasesOptions{}) 70 if err != nil { 71 if res != nil && res.StatusCode == 404 { 72 // 404 means repository not found or release not found. It's not an error here. 73 log.Print("Repository or release not found") 74 return nil, nil 75 } 76 log.Printf("API returned an error response: %s", err) 77 return nil, err 78 } 79 releases := make([]SourceRelease, len(rels)) 80 for i, rel := range rels { 81 releases[i] = NewGiteaRelease(rel) 82 } 83 return releases, nil 84 } 85 86 // DownloadReleaseAsset downloads an asset from a release. 87 // It returns an io.ReadCloser: it is your responsibility to Close it. 88 func (s *GiteaSource) DownloadReleaseAsset(ctx context.Context, rel *Release, assetID int64) (io.ReadCloser, error) { 89 if rel == nil { 90 return nil, ErrInvalidRelease 91 } 92 owner, repo, err := rel.repository.GetSlug() 93 if err != nil { 94 return nil, err 95 } 96 s.api.SetContext(ctx) 97 attachment, _, err := s.api.GetReleaseAttachment(owner, repo, rel.ReleaseID, assetID) 98 if err != nil { 99 return nil, fmt.Errorf("failed to call Gitea Releases API for getting the asset ID %d on repository '%s/%s': %w", assetID, owner, repo, err) 100 } 101 102 client := http.DefaultClient 103 req, err := http.NewRequestWithContext(ctx, http.MethodGet, attachment.DownloadURL, http.NoBody) 104 if err != nil { 105 log.Print(err) 106 return nil, err 107 } 108 109 if s.token != "" { 110 // verify request is from same domain not to leak token 111 ok, err := canUseTokenForDomain(s.baseURL, attachment.DownloadURL) 112 if err != nil { 113 return nil, err 114 } 115 if ok { 116 req.Header.Set("Authorization", "token "+s.token) 117 } 118 } 119 response, err := client.Do(req) 120 121 if err != nil { 122 log.Print(err) 123 return nil, err 124 } 125 126 return response.Body, nil 127 } 128 129 // Verify interface 130 var _ Source = &GiteaSource{}