github.com/goreleaser/goreleaser@v1.25.1/internal/client/client.go (about)

     1  // Package client contains the client implementations for several providers.
     2  package client
     3  
     4  import (
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/caarlos0/log"
     9  	"github.com/goreleaser/goreleaser/internal/artifact"
    10  	"github.com/goreleaser/goreleaser/internal/tmpl"
    11  	"github.com/goreleaser/goreleaser/pkg/config"
    12  	"github.com/goreleaser/goreleaser/pkg/context"
    13  )
    14  
    15  const (
    16  	// maxReleaseBodyLength defines the max characters size of the body
    17  	maxReleaseBodyLength = 125000
    18  	// ellipsis to be used when release notes body is too long
    19  	ellipsis = "..."
    20  )
    21  
    22  // ErrNotImplemented is returned when a client does not implement certain feature.
    23  var ErrNotImplemented = fmt.Errorf("not implemented")
    24  
    25  // ErrReleaseDisabled happens when a configuration tries to use the default
    26  // url_template even though the release is disabled.
    27  var ErrReleaseDisabled = fmt.Errorf("release is disabled, cannot use default url_template")
    28  
    29  // Info of the repository.
    30  type Info struct {
    31  	Description string
    32  	Homepage    string
    33  	URL         string
    34  }
    35  
    36  type Repo struct {
    37  	Owner         string
    38  	Name          string
    39  	Branch        string
    40  	GitURL        string
    41  	GitSSHCommand string
    42  	PrivateKey    string
    43  }
    44  
    45  func (r Repo) String() string {
    46  	if r.Owner == "" && r.Name == "" {
    47  		return ""
    48  	}
    49  	return r.Owner + "/" + r.Name
    50  }
    51  
    52  // Client interface.
    53  type Client interface {
    54  	CloseMilestone(ctx *context.Context, repo Repo, title string) (err error)
    55  	// Creates a release. It's marked as draft if possible (should call PublishRelease to finish publishing).
    56  	CreateRelease(ctx *context.Context, body string) (releaseID string, err error)
    57  	PublishRelease(ctx *context.Context, releaseID string) (err error)
    58  	Upload(ctx *context.Context, releaseID string, artifact *artifact.Artifact, file *os.File) (err error)
    59  	Changelog(ctx *context.Context, repo Repo, prev, current string) (string, error)
    60  	ReleaseURLTemplater
    61  	FileCreator
    62  }
    63  
    64  // ReleaseURLTemplater provides the release URL as a template, containing the
    65  // artifact name as well.
    66  type ReleaseURLTemplater interface {
    67  	ReleaseURLTemplate(ctx *context.Context) (string, error)
    68  }
    69  
    70  // RepoFile is a file to be created.
    71  type RepoFile struct {
    72  	Content    []byte
    73  	Path       string
    74  	Identifier string // for the use of the caller.
    75  }
    76  
    77  // FileCreator can create the given file to some code repository.
    78  type FileCreator interface {
    79  	CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo Repo, content []byte, path, message string) (err error)
    80  }
    81  
    82  // FilesCreator can create the multiple files in some repository and in a single commit.
    83  type FilesCreator interface {
    84  	FileCreator
    85  	CreateFiles(ctx *context.Context, commitAuthor config.CommitAuthor, repo Repo, message string, files []RepoFile) (err error)
    86  }
    87  
    88  // ReleaseNotesGenerator can generate release notes.
    89  type ReleaseNotesGenerator interface {
    90  	GenerateReleaseNotes(ctx *context.Context, repo Repo, prev, current string) (string, error)
    91  }
    92  
    93  // ForkSyncer can sync forks.
    94  type ForkSyncer interface {
    95  	SyncFork(ctx *context.Context, head, base Repo) error
    96  }
    97  
    98  // PullRequestOpener can open pull requests.
    99  type PullRequestOpener interface {
   100  	OpenPullRequest(ctx *context.Context, base, head Repo, title string, draft bool) error
   101  }
   102  
   103  // New creates a new client depending on the token type.
   104  func New(ctx *context.Context) (Client, error) {
   105  	return newWithToken(ctx, ctx.Token)
   106  }
   107  
   108  // NewReleaseClient returns a ReleaserURLTemplater, handling the possibility of
   109  // the release being disabled.
   110  func NewReleaseClient(ctx *context.Context) (ReleaseURLTemplater, error) {
   111  	disable, err := tmpl.New(ctx).Bool(ctx.Config.Release.Disable)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	if disable {
   116  		return errURLTemplater{}, nil
   117  	}
   118  	return New(ctx)
   119  }
   120  
   121  var _ ReleaseURLTemplater = errURLTemplater{}
   122  
   123  type errURLTemplater struct{}
   124  
   125  func (errURLTemplater) ReleaseURLTemplate(_ *context.Context) (string, error) {
   126  	return "", ErrReleaseDisabled
   127  }
   128  
   129  func newWithToken(ctx *context.Context, token string) (Client, error) {
   130  	log.WithField("type", ctx.TokenType).Debug("token type")
   131  	switch ctx.TokenType {
   132  	case context.TokenTypeGitHub:
   133  		return newGitHub(ctx, token)
   134  	case context.TokenTypeGitLab:
   135  		return newGitLab(ctx, token)
   136  	case context.TokenTypeGitea:
   137  		return newGitea(ctx, token)
   138  	default:
   139  		return nil, fmt.Errorf("invalid client token type: %q", ctx.TokenType)
   140  	}
   141  }
   142  
   143  func NewIfToken(ctx *context.Context, cli Client, token string) (Client, error) {
   144  	if token == "" {
   145  		return cli, nil
   146  	}
   147  	token, err := tmpl.New(ctx).ApplySingleEnvOnly(token)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	log.Debug("using custom token")
   152  	return newWithToken(ctx, token)
   153  }
   154  
   155  func truncateReleaseBody(body string) string {
   156  	if len(body) > maxReleaseBodyLength {
   157  		body = body[1:(maxReleaseBodyLength-len(ellipsis))] + ellipsis
   158  	}
   159  	return body
   160  }
   161  
   162  // ErrNoMilestoneFound is an error when no milestone is found.
   163  type ErrNoMilestoneFound struct {
   164  	Title string
   165  }
   166  
   167  func (e ErrNoMilestoneFound) Error() string {
   168  	return fmt.Sprintf("no milestone found: %s", e.Title)
   169  }
   170  
   171  // RetriableError is an error that will cause the action to be retried.
   172  type RetriableError struct {
   173  	Err error
   174  }
   175  
   176  func (e RetriableError) Error() string {
   177  	return e.Err.Error()
   178  }