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