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