github.com/joselitofilho/goreleaser@v0.155.1-0.20210123221854-e4891856c593/internal/client/gitea.go (about)

     1  package client
     2  
     3  import (
     4  	"crypto/tls"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  	"os"
    10  	"strconv"
    11  
    12  	"code.gitea.io/sdk/gitea"
    13  	"github.com/apex/log"
    14  	"github.com/goreleaser/goreleaser/internal/artifact"
    15  	"github.com/goreleaser/goreleaser/internal/tmpl"
    16  	"github.com/goreleaser/goreleaser/pkg/config"
    17  	"github.com/goreleaser/goreleaser/pkg/context"
    18  )
    19  
    20  type giteaClient struct {
    21  	client *gitea.Client
    22  }
    23  
    24  func getInstanceURL(apiURL string) (string, error) {
    25  	u, err := url.Parse(apiURL)
    26  	if err != nil {
    27  		return "", err
    28  	}
    29  	u.Path = ""
    30  	rawurl := u.String()
    31  	if rawurl == "" {
    32  		return "", fmt.Errorf("invalid URL: %v", apiURL)
    33  	}
    34  	return rawurl, nil
    35  }
    36  
    37  // NewGitea returns a gitea client implementation.
    38  func NewGitea(ctx *context.Context, token string) (Client, error) {
    39  	instanceURL, err := getInstanceURL(ctx.Config.GiteaURLs.API)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	transport := &http.Transport{
    44  		Proxy: http.ProxyFromEnvironment,
    45  		TLSClientConfig: &tls.Config{
    46  			// nolint: gosec
    47  			InsecureSkipVerify: ctx.Config.GiteaURLs.SkipTLSVerify,
    48  		},
    49  	}
    50  	httpClient := &http.Client{Transport: transport}
    51  	client, err := gitea.NewClient(instanceURL,
    52  		gitea.SetToken(token),
    53  		gitea.SetHTTPClient(httpClient),
    54  	)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	if ctx != nil {
    59  		gitea.SetContext(ctx)(client)
    60  	}
    61  	return &giteaClient{client: client}, nil
    62  }
    63  
    64  // CloseMilestone closes a given milestone.
    65  func (c *giteaClient) CloseMilestone(ctx *context.Context, repo Repo, title string) error {
    66  	closedState := gitea.StateClosed
    67  	opts := gitea.EditMilestoneOption{
    68  		State: &closedState,
    69  		Title: title,
    70  	}
    71  
    72  	_, resp, err := c.client.EditMilestoneByName(repo.Owner, repo.Name, title, opts)
    73  	if resp != nil && resp.StatusCode == http.StatusNotFound {
    74  		return ErrNoMilestoneFound{Title: title}
    75  	}
    76  	return err
    77  }
    78  
    79  // CreateFile creates a file in the repository at a given path
    80  // or updates the file if it exists.
    81  func (c *giteaClient) CreateFile(
    82  	ctx *context.Context,
    83  	commitAuthor config.CommitAuthor,
    84  	repo Repo,
    85  	content []byte,
    86  	path,
    87  	message string,
    88  ) error {
    89  	// use default branch
    90  	branchName := ""
    91  
    92  	fileOptions := gitea.FileOptions{
    93  		Message:    message,
    94  		BranchName: branchName,
    95  		Author: gitea.Identity{
    96  			Name:  commitAuthor.Name,
    97  			Email: commitAuthor.Email,
    98  		},
    99  		Committer: gitea.Identity{
   100  			Name:  commitAuthor.Name,
   101  			Email: commitAuthor.Email,
   102  		},
   103  	}
   104  
   105  	currentFile, resp, err := c.client.GetContents(repo.Owner, repo.Name, branchName, path)
   106  	// file not exist, create it
   107  	if err != nil {
   108  		if resp == nil || resp.StatusCode != http.StatusNotFound {
   109  			return err
   110  		}
   111  		_, _, err = c.client.CreateFile(repo.Owner, repo.Name, path, gitea.CreateFileOptions{
   112  			FileOptions: fileOptions,
   113  			Content:     base64.StdEncoding.EncodeToString(content),
   114  		})
   115  		return err
   116  	}
   117  
   118  	// update file
   119  	_, _, err = c.client.UpdateFile(repo.Owner, repo.Name, path, gitea.UpdateFileOptions{
   120  		FileOptions: fileOptions,
   121  		SHA:         currentFile.SHA,
   122  		Content:     base64.StdEncoding.EncodeToString(content),
   123  	})
   124  	return err
   125  }
   126  
   127  func (c *giteaClient) createRelease(ctx *context.Context, title, body string) (*gitea.Release, error) {
   128  	releaseConfig := ctx.Config.Release
   129  	owner := releaseConfig.Gitea.Owner
   130  	repoName := releaseConfig.Gitea.Name
   131  	tag := ctx.Git.CurrentTag
   132  
   133  	opts := gitea.CreateReleaseOption{
   134  		TagName:      tag,
   135  		Target:       ctx.Git.Commit,
   136  		Title:        title,
   137  		Note:         body,
   138  		IsDraft:      releaseConfig.Draft,
   139  		IsPrerelease: ctx.PreRelease,
   140  	}
   141  	release, _, err := c.client.CreateRelease(owner, repoName, opts)
   142  	if err != nil {
   143  		log.WithFields(log.Fields{
   144  			"err": err.Error(),
   145  		}).Debug("error creating Gitea release")
   146  		return nil, err
   147  	}
   148  	log.WithField("id", release.ID).Info("Gitea release created")
   149  	return release, nil
   150  }
   151  
   152  func (c *giteaClient) getExistingRelease(owner, repoName, tagName string) (*gitea.Release, error) {
   153  	releases, _, err := c.client.ListReleases(owner, repoName, gitea.ListReleasesOptions{})
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	for _, release := range releases {
   159  		if release.TagName == tagName {
   160  			return release, nil
   161  		}
   162  	}
   163  
   164  	return nil, nil
   165  }
   166  
   167  func (c *giteaClient) updateRelease(ctx *context.Context, title, body string, id int64) (*gitea.Release, error) {
   168  	releaseConfig := ctx.Config.Release
   169  	owner := releaseConfig.Gitea.Owner
   170  	repoName := releaseConfig.Gitea.Name
   171  	tag := ctx.Git.CurrentTag
   172  
   173  	opts := gitea.EditReleaseOption{
   174  		TagName:      tag,
   175  		Target:       ctx.Git.Commit,
   176  		Title:        title,
   177  		Note:         body,
   178  		IsDraft:      &releaseConfig.Draft,
   179  		IsPrerelease: &ctx.PreRelease,
   180  	}
   181  
   182  	release, _, err := c.client.EditRelease(owner, repoName, id, opts)
   183  	if err != nil {
   184  		log.WithFields(log.Fields{
   185  			"err": err.Error(),
   186  		}).Debug("error updating Gitea release")
   187  		return nil, err
   188  	}
   189  	log.WithField("id", release.ID).Info("Gitea release updated")
   190  	return release, nil
   191  }
   192  
   193  // CreateRelease creates a new release or updates it by keeping
   194  // the release notes if it exists.
   195  func (c *giteaClient) CreateRelease(ctx *context.Context, body string) (string, error) {
   196  	var release *gitea.Release
   197  	var err error
   198  
   199  	releaseConfig := ctx.Config.Release
   200  
   201  	title, err := tmpl.New(ctx).Apply(releaseConfig.NameTemplate)
   202  	if err != nil {
   203  		return "", err
   204  	}
   205  
   206  	release, err = c.getExistingRelease(
   207  		releaseConfig.Gitea.Owner,
   208  		releaseConfig.Gitea.Name,
   209  		ctx.Git.CurrentTag,
   210  	)
   211  	if err != nil {
   212  		return "", err
   213  	}
   214  
   215  	if release != nil {
   216  		release, err = c.updateRelease(ctx, title, body, release.ID)
   217  		if err != nil {
   218  			return "", err
   219  		}
   220  	} else {
   221  		release, err = c.createRelease(ctx, title, body)
   222  		if err != nil {
   223  			return "", err
   224  		}
   225  	}
   226  
   227  	return strconv.FormatInt(release.ID, 10), nil
   228  }
   229  
   230  func (c *giteaClient) ReleaseURLTemplate(ctx *context.Context) (string, error) {
   231  	return fmt.Sprintf(
   232  		"%s/%s/%s/releases/download/{{ .Tag }}/{{ .ArtifactName }}",
   233  		ctx.Config.GiteaURLs.Download,
   234  		ctx.Config.Release.Gitea.Owner,
   235  		ctx.Config.Release.Gitea.Name,
   236  	), nil
   237  }
   238  
   239  // Upload uploads a file into a release repository.
   240  func (c *giteaClient) Upload(
   241  	ctx *context.Context,
   242  	releaseID string,
   243  	artifact *artifact.Artifact,
   244  	file *os.File,
   245  ) error {
   246  	giteaReleaseID, err := strconv.ParseInt(releaseID, 10, 64)
   247  	if err != nil {
   248  		return err
   249  	}
   250  	releaseConfig := ctx.Config.Release
   251  	owner := releaseConfig.Gitea.Owner
   252  	repoName := releaseConfig.Gitea.Name
   253  
   254  	_, _, err = c.client.CreateReleaseAttachment(owner, repoName, giteaReleaseID, file, artifact.Name)
   255  	if err != nil {
   256  		return RetriableError{err}
   257  	}
   258  	return nil
   259  }