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