1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-10 03:47:03 +02:00
goreleaser/internal/client/github.go

609 lines
16 KiB
Go
Raw Normal View History

2017-05-13 23:09:42 +02:00
package client
import (
2019-01-17 15:25:57 +02:00
"crypto/tls"
"errors"
"fmt"
2019-01-17 15:25:57 +02:00
"net/http"
2017-09-23 19:42:07 +02:00
"net/url"
2017-05-13 23:09:42 +02:00
"os"
2019-08-10 15:35:20 +02:00
"reflect"
feat: add gitlab for releases (#1038) * outlines gitlab client integration * makes client parameter more explicit * adds gitlab url to config * changes releaseID to string to adapt to gitlab * updates to latest gitlab client lib 0.18 * fixes copy paster in gitlab upload func * fixes gitlab typo in config * adds gitlab token to env and context * release now uses the client factory method * skips brew pipe if it is not a github release * add github tokentype to publish tests * skips scoop pipe if it is not a github release * corrects brew skip msg * adds gitlab token to main test * adds gitlab to release docs * validates config and errors accordingly * adapt release pipe name to include gitlab * fixes gitlab client after testing * moves not-configured brew and scoop pipe checks as first check * adds more debug to gitlab client * adapts changelog generation for gitlab markdown * adds debug log for gitlab changelog * env needs to run before changelog pipe * moves gitlab default download url to default pipe * moves multiple releases check to from config to release pipe * release differs now for github and gitlab * adds debug gitlab release update msgs * moves env pipe as second after before because it determines the token type other pipes depend on * adaptes error check on gitlab release creation * Revert "adaptes error check on gitlab release creation" This reverts commit 032024571c76140f8e2207ee01cc08088f37594b. * simplifies gitlab client logic. removes comments * skips tls verification for gitlab client if specified in config * updates the docs * adds clarification that brew and scoop are not supported if it is a gitlab release * fixes copy paster in release.md * adds missing blob pipe in defaults and publish due to missing in merge * updates comment in gitlab client
2019-06-29 16:02:40 +02:00
"strconv"
"strings"
"time"
2017-05-13 23:09:42 +02:00
"github.com/caarlos0/log"
2023-10-11 04:16:27 +02:00
"github.com/charmbracelet/x/exp/ordered"
"github.com/google/go-github/v59/github"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/tmpl"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
2017-05-13 23:09:42 +02:00
"golang.org/x/oauth2"
)
const DefaultGitHubDownloadURL = "https://github.com"
var (
_ Client = &githubClient{}
_ ReleaseNotesGenerator = &githubClient{}
_ PullRequestOpener = &githubClient{}
)
2017-05-13 23:09:42 +02:00
type githubClient struct {
client *github.Client
}
// NewGitHubReleaseNotesGenerator returns a GitHub client that can generate
// changelogs.
func NewGitHubReleaseNotesGenerator(ctx *context.Context, token string) (ReleaseNotesGenerator, error) {
return newGitHub(ctx, token)
}
// newGitHub returns a github client implementation.
func newGitHub(ctx *context.Context, token string) (*githubClient, error) {
2017-05-13 23:09:42 +02:00
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
2017-05-13 23:09:42 +02:00
)
feat: improve output and pipe skipping (#2480) * refactor: improve middleware Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: upload tests Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: twitter tests Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: source tests Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: snapshot tests Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * test: improved some tests Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: snapcraft skip Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip slack Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip sign Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip scoop Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip reddit Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip discord Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip publish Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip nfpm Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip milestone Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip custompublishers Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip checksums Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip changelog Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip brew Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip blob Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip before Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip artifactory Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip announce Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip defaults Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: cmds Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip docker Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * chore: todo Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: go.mod Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip release Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: remove old skip pipe errors Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip teams Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip brew Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix/test: skip smtp Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: lint issues Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip docker Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip brew and scoop Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip docker Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: skip http/artifactory Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * test: increase coverage Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * test: fix Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>
2021-09-18 15:21:29 +02:00
2019-01-17 15:25:57 +02:00
httpClient := oauth2.NewClient(ctx, ts)
2019-01-17 22:03:36 +02:00
base := httpClient.Transport.(*oauth2.Transport).Base
2019-08-10 15:35:20 +02:00
if base == nil || reflect.ValueOf(base).IsNil() {
2019-01-17 22:03:36 +02:00
base = http.DefaultTransport
}
2019-01-19 22:14:56 +02:00
// nolint: gosec
2019-01-17 22:03:36 +02:00
base.(*http.Transport).TLSClientConfig = &tls.Config{
2019-01-17 15:28:10 +02:00
InsecureSkipVerify: ctx.Config.GitHubURLs.SkipTLSVerify,
2019-01-17 15:25:57 +02:00
}
base.(*http.Transport).Proxy = http.ProxyFromEnvironment
2019-01-17 22:03:36 +02:00
httpClient.Transport.(*oauth2.Transport).Base = base
2019-01-17 15:25:57 +02:00
client := github.NewClient(httpClient)
err := overrideGitHubClientAPI(ctx, client)
if err != nil {
return &githubClient{}, err
2017-09-23 19:42:07 +02:00
}
2018-06-19 20:53:14 +02:00
return &githubClient{client: client}, nil
2017-05-13 23:09:42 +02:00
}
func (c *githubClient) checkRateLimit(ctx *context.Context) {
limits, _, err := c.client.RateLimits(ctx)
if err != nil {
log.Warn("could not check rate limits, hoping for the best...")
return
}
if limits.Core.Remaining > 100 { // 100 should be safe enough
return
}
sleep := limits.Core.Reset.UTC().Sub(time.Now().UTC())
if sleep <= 0 {
// it seems that sometimes, after the rate limit just reset, it might
// still get <100 remaining and a reset time in the past... in such
// cases we can probably sleep a bit more before trying again...
sleep = 15 * time.Second
}
log.Warnf("token too close to rate limiting, will sleep for %s before continuing...", sleep)
time.Sleep(sleep)
c.checkRateLimit(ctx)
}
func (c *githubClient) GenerateReleaseNotes(ctx *context.Context, repo Repo, prev, current string) (string, error) {
c.checkRateLimit(ctx)
notes, _, err := c.client.Repositories.GenerateReleaseNotes(ctx, repo.Owner, repo.Name, &github.GenerateNotesOptions{
TagName: current,
PreviousTagName: github.String(prev),
})
if err != nil {
return "", err
}
return notes.Body, err
}
func (c *githubClient) Changelog(ctx *context.Context, repo Repo, prev, current string) (string, error) {
c.checkRateLimit(ctx)
var log []string
opts := &github.ListOptions{PerPage: 100}
for {
result, resp, err := c.client.Repositories.CompareCommits(ctx, repo.Owner, repo.Name, prev, current, opts)
if err != nil {
return "", err
}
for _, commit := range result.Commits {
log = append(log, fmt.Sprintf(
"%s: %s (@%s)",
commit.GetSHA(),
strings.Split(commit.Commit.GetMessage(), "\n")[0],
commit.GetAuthor().GetLogin(),
))
}
if resp.NextPage == 0 {
break
}
opts.Page = resp.NextPage
}
return strings.Join(log, "\n"), nil
}
// getDefaultBranch returns the default branch of a github repo
func (c *githubClient) getDefaultBranch(ctx *context.Context, repo Repo) (string, error) {
c.checkRateLimit(ctx)
feat: allow default branch to be used in CreateFile (#2539) * feat: allow default branch to be used in gitlab * feat: add func to get default branch * fix: matching branch args * feat: branch customization * fix: fixing dummyclient naming * refactor: remove createbranch * feat: Adding gitlab branch customization * feat: testing for github and gitea defaultbranch * fix: remove dummy debug message * fix: removing note about gitea not being supported * feat: allow default branch to be used in gitlab * docs: links updat Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * chore: delete kodiak.yml Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * feat: add func to get default branch * fix: matching branch args * feat: branch customization * fix: docs: Missing mattermost docs on website (#2543) * docs: update CircleCI example (#2545) * fix: fixing dummyclient naming * docs: some little fixes on the webpage (#2547) * feat: some little fixes on the webpage Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com> * Update www/docs/overrides/home.html Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * refactor: remove unused not impl error (#2540) Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * feat: add template support for homebrew tap owner (#2544) * feat: add template support for homebrew tap name refs #2544 Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * refactor: remove createbranch * feat: Adding gitlab branch customization * feat: testing for github and gitea defaultbranch * fix: remove dummy debug message * fix: removing note about gitea not being supported Co-authored-by: Carlos Alexandro Becker <caarlos0@gmail.com> Co-authored-by: Engin Diri <engin.diri@mail.schwarz> Co-authored-by: Ricardo N Feliciano <FelicianoTech@gmail.com> Co-authored-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com> Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Co-authored-by: Erik Weber <terbolous@gmail.com>
2021-10-03 16:22:26 +02:00
p, res, err := c.client.Repositories.Get(ctx, repo.Owner, repo.Name)
if err != nil {
log := log.WithField("projectID", repo.String())
if res != nil {
log = log.WithField("statusCode", res.StatusCode)
}
log.
WithError(err).
Warn("error checking for default branch")
feat: allow default branch to be used in CreateFile (#2539) * feat: allow default branch to be used in gitlab * feat: add func to get default branch * fix: matching branch args * feat: branch customization * fix: fixing dummyclient naming * refactor: remove createbranch * feat: Adding gitlab branch customization * feat: testing for github and gitea defaultbranch * fix: remove dummy debug message * fix: removing note about gitea not being supported * feat: allow default branch to be used in gitlab * docs: links updat Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * chore: delete kodiak.yml Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * feat: add func to get default branch * fix: matching branch args * feat: branch customization * fix: docs: Missing mattermost docs on website (#2543) * docs: update CircleCI example (#2545) * fix: fixing dummyclient naming * docs: some little fixes on the webpage (#2547) * feat: some little fixes on the webpage Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com> * Update www/docs/overrides/home.html Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * refactor: remove unused not impl error (#2540) Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * feat: add template support for homebrew tap owner (#2544) * feat: add template support for homebrew tap name refs #2544 Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * refactor: remove createbranch * feat: Adding gitlab branch customization * feat: testing for github and gitea defaultbranch * fix: remove dummy debug message * fix: removing note about gitea not being supported Co-authored-by: Carlos Alexandro Becker <caarlos0@gmail.com> Co-authored-by: Engin Diri <engin.diri@mail.schwarz> Co-authored-by: Ricardo N Feliciano <FelicianoTech@gmail.com> Co-authored-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com> Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Co-authored-by: Erik Weber <terbolous@gmail.com>
2021-10-03 16:22:26 +02:00
return "", err
}
return p.GetDefaultBranch(), nil
}
// CloseMilestone closes a given milestone.
func (c *githubClient) CloseMilestone(ctx *context.Context, repo Repo, title string) error {
c.checkRateLimit(ctx)
milestone, err := c.getMilestoneByTitle(ctx, repo, title)
if err != nil {
return err
}
if milestone == nil {
return ErrNoMilestoneFound{Title: title}
}
closedState := "closed"
milestone.State = &closedState
_, _, err = c.client.Issues.EditMilestone(
ctx,
repo.Owner,
repo.Name,
*milestone.Number,
milestone,
)
return err
}
func headString(base, head Repo) string {
return strings.Join([]string{
2023-10-11 04:16:27 +02:00
ordered.First(head.Owner, base.Owner),
ordered.First(head.Name, base.Name),
ordered.First(head.Branch, base.Branch),
}, ":")
}
func (c *githubClient) getPRTemplate(ctx *context.Context, repo Repo) (string, error) {
content, _, _, err := c.client.Repositories.GetContents(
ctx, repo.Owner, repo.Name,
".github/PULL_REQUEST_TEMPLATE.md",
&github.RepositoryContentGetOptions{
Ref: repo.Branch,
},
)
if err != nil {
return "", err
}
return content.GetContent()
}
const prFooter = "###### Automated with [GoReleaser](https://goreleaser.com)"
func (c *githubClient) OpenPullRequest(
ctx *context.Context,
base, head Repo,
title string,
draft bool,
) error {
c.checkRateLimit(ctx)
2023-10-11 04:16:27 +02:00
base.Owner = ordered.First(base.Owner, head.Owner)
base.Name = ordered.First(base.Name, head.Name)
if base.Branch == "" {
def, err := c.getDefaultBranch(ctx, base)
if err != nil {
return err
}
base.Branch = def
}
tpl, err := c.getPRTemplate(ctx, base)
if err != nil {
log.WithError(err).Debug("no pull request template found...")
}
if len(tpl) > 0 {
log.Info("got a pr template")
}
log := log.
WithField("base", headString(base, Repo{})).
WithField("head", headString(base, head)).
WithField("draft", draft)
log.Info("opening pull request")
pr, res, err := c.client.PullRequests.Create(
ctx,
base.Owner,
base.Name,
&github.NewPullRequest{
Title: github.String(title),
Base: github.String(base.Branch),
Head: github.String(headString(base, head)),
Body: github.String(strings.Join([]string{tpl, prFooter}, "\n")),
Draft: github.Bool(draft),
},
)
if err != nil {
if res.StatusCode == http.StatusUnprocessableEntity {
log.WithError(err).Warn("pull request validation failed")
return nil
}
return fmt.Errorf("could not create pull request: %w", err)
}
log.WithField("url", pr.GetHTMLURL()).Info("pull request created")
return nil
}
2017-05-13 23:09:42 +02:00
func (c *githubClient) CreateFile(
ctx *context.Context,
commitAuthor config.CommitAuthor,
repo Repo,
content []byte,
path,
message string,
) error {
c.checkRateLimit(ctx)
defBranch, err := c.getDefaultBranch(ctx, repo)
if err != nil {
return fmt.Errorf("could not get default branch: %w", err)
}
branch := repo.Branch
if branch == "" {
branch = defBranch
feat: allow default branch to be used in CreateFile (#2539) * feat: allow default branch to be used in gitlab * feat: add func to get default branch * fix: matching branch args * feat: branch customization * fix: fixing dummyclient naming * refactor: remove createbranch * feat: Adding gitlab branch customization * feat: testing for github and gitea defaultbranch * fix: remove dummy debug message * fix: removing note about gitea not being supported * feat: allow default branch to be used in gitlab * docs: links updat Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * chore: delete kodiak.yml Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * feat: add func to get default branch * fix: matching branch args * feat: branch customization * fix: docs: Missing mattermost docs on website (#2543) * docs: update CircleCI example (#2545) * fix: fixing dummyclient naming * docs: some little fixes on the webpage (#2547) * feat: some little fixes on the webpage Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com> * Update www/docs/overrides/home.html Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * refactor: remove unused not impl error (#2540) Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * feat: add template support for homebrew tap owner (#2544) * feat: add template support for homebrew tap name refs #2544 Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * refactor: remove createbranch * feat: Adding gitlab branch customization * feat: testing for github and gitea defaultbranch * fix: remove dummy debug message * fix: removing note about gitea not being supported Co-authored-by: Carlos Alexandro Becker <caarlos0@gmail.com> Co-authored-by: Engin Diri <engin.diri@mail.schwarz> Co-authored-by: Ricardo N Feliciano <FelicianoTech@gmail.com> Co-authored-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com> Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Co-authored-by: Erik Weber <terbolous@gmail.com>
2021-10-03 16:22:26 +02:00
}
2017-05-13 23:09:42 +02:00
options := &github.RepositoryContentFileOptions{
Committer: &github.CommitAuthor{
Name: github.String(commitAuthor.Name),
Email: github.String(commitAuthor.Email),
2017-05-13 23:09:42 +02:00
},
Content: content,
Message: github.String(message),
2017-05-13 23:09:42 +02:00
}
feat: allow default branch to be used in CreateFile (#2539) * feat: allow default branch to be used in gitlab * feat: add func to get default branch * fix: matching branch args * feat: branch customization * fix: fixing dummyclient naming * refactor: remove createbranch * feat: Adding gitlab branch customization * feat: testing for github and gitea defaultbranch * fix: remove dummy debug message * fix: removing note about gitea not being supported * feat: allow default branch to be used in gitlab * docs: links updat Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * chore: delete kodiak.yml Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * feat: add func to get default branch * fix: matching branch args * feat: branch customization * fix: docs: Missing mattermost docs on website (#2543) * docs: update CircleCI example (#2545) * fix: fixing dummyclient naming * docs: some little fixes on the webpage (#2547) * feat: some little fixes on the webpage Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com> * Update www/docs/overrides/home.html Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * refactor: remove unused not impl error (#2540) Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * feat: add template support for homebrew tap owner (#2544) * feat: add template support for homebrew tap name refs #2544 Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * refactor: remove createbranch * feat: Adding gitlab branch customization * feat: testing for github and gitea defaultbranch * fix: remove dummy debug message * fix: removing note about gitea not being supported Co-authored-by: Carlos Alexandro Becker <caarlos0@gmail.com> Co-authored-by: Engin Diri <engin.diri@mail.schwarz> Co-authored-by: Ricardo N Feliciano <FelicianoTech@gmail.com> Co-authored-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com> Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Co-authored-by: Erik Weber <terbolous@gmail.com>
2021-10-03 16:22:26 +02:00
// Set the branch if we got it above...otherwise, just default to
// whatever the SDK does auto-magically
if branch != "" {
options.Branch = &branch
}
log.
WithField("repository", repo.String()).
WithField("branch", repo.Branch).
WithField("file", path).
Info("pushing")
if defBranch != branch && branch != "" {
2023-10-17 17:52:41 +02:00
_, res, err := c.client.Repositories.GetBranch(ctx, repo.Owner, repo.Name, branch, 100)
if err != nil && (res == nil || res.StatusCode != http.StatusNotFound) {
return fmt.Errorf("could not get branch %q: %w", branch, err)
}
if res.StatusCode == http.StatusNotFound {
defRef, _, err := c.client.Git.GetRef(ctx, repo.Owner, repo.Name, "refs/heads/"+defBranch)
if err != nil {
return fmt.Errorf("could not get ref %q: %w", "refs/heads/"+defBranch, err)
}
if _, _, err := c.client.Git.CreateRef(ctx, repo.Owner, repo.Name, &github.Reference{
Ref: github.String("refs/heads/" + branch),
Object: &github.GitObject{
SHA: defRef.Object.SHA,
},
}); err != nil {
rerr := new(github.ErrorResponse)
if !errors.As(err, &rerr) || rerr.Message != "Reference already exists" {
return fmt.Errorf("could not create ref %q from %q: %w", "refs/heads/"+branch, defRef.Object.GetSHA(), err)
}
}
}
}
2017-05-13 23:09:42 +02:00
file, _, res, err := c.client.Repositories.GetContents(
ctx,
repo.Owner,
repo.Name,
2017-05-13 23:09:42 +02:00
path,
&github.RepositoryContentGetOptions{
Ref: branch,
},
2017-05-13 23:09:42 +02:00
)
if err != nil && (res == nil || res.StatusCode != http.StatusNotFound) {
return fmt.Errorf("could not get %q: %w", path, err)
}
options.SHA = github.String(file.GetSHA())
if _, _, err := c.client.Repositories.UpdateFile(
ctx,
repo.Owner,
repo.Name,
path,
options,
); err != nil {
return fmt.Errorf("could not update %q: %w", path, err)
}
return nil
2017-05-13 23:09:42 +02:00
}
feat: add gitlab for releases (#1038) * outlines gitlab client integration * makes client parameter more explicit * adds gitlab url to config * changes releaseID to string to adapt to gitlab * updates to latest gitlab client lib 0.18 * fixes copy paster in gitlab upload func * fixes gitlab typo in config * adds gitlab token to env and context * release now uses the client factory method * skips brew pipe if it is not a github release * add github tokentype to publish tests * skips scoop pipe if it is not a github release * corrects brew skip msg * adds gitlab token to main test * adds gitlab to release docs * validates config and errors accordingly * adapt release pipe name to include gitlab * fixes gitlab client after testing * moves not-configured brew and scoop pipe checks as first check * adds more debug to gitlab client * adapts changelog generation for gitlab markdown * adds debug log for gitlab changelog * env needs to run before changelog pipe * moves gitlab default download url to default pipe * moves multiple releases check to from config to release pipe * release differs now for github and gitlab * adds debug gitlab release update msgs * moves env pipe as second after before because it determines the token type other pipes depend on * adaptes error check on gitlab release creation * Revert "adaptes error check on gitlab release creation" This reverts commit 032024571c76140f8e2207ee01cc08088f37594b. * simplifies gitlab client logic. removes comments * skips tls verification for gitlab client if specified in config * updates the docs * adds clarification that brew and scoop are not supported if it is a gitlab release * fixes copy paster in release.md * adds missing blob pipe in defaults and publish due to missing in merge * updates comment in gitlab client
2019-06-29 16:02:40 +02:00
func (c *githubClient) CreateRelease(ctx *context.Context, body string) (string, error) {
c.checkRateLimit(ctx)
title, err := tmpl.New(ctx).Apply(ctx.Config.Release.NameTemplate)
if err != nil {
feat: add gitlab for releases (#1038) * outlines gitlab client integration * makes client parameter more explicit * adds gitlab url to config * changes releaseID to string to adapt to gitlab * updates to latest gitlab client lib 0.18 * fixes copy paster in gitlab upload func * fixes gitlab typo in config * adds gitlab token to env and context * release now uses the client factory method * skips brew pipe if it is not a github release * add github tokentype to publish tests * skips scoop pipe if it is not a github release * corrects brew skip msg * adds gitlab token to main test * adds gitlab to release docs * validates config and errors accordingly * adapt release pipe name to include gitlab * fixes gitlab client after testing * moves not-configured brew and scoop pipe checks as first check * adds more debug to gitlab client * adapts changelog generation for gitlab markdown * adds debug log for gitlab changelog * env needs to run before changelog pipe * moves gitlab default download url to default pipe * moves multiple releases check to from config to release pipe * release differs now for github and gitlab * adds debug gitlab release update msgs * moves env pipe as second after before because it determines the token type other pipes depend on * adaptes error check on gitlab release creation * Revert "adaptes error check on gitlab release creation" This reverts commit 032024571c76140f8e2207ee01cc08088f37594b. * simplifies gitlab client logic. removes comments * skips tls verification for gitlab client if specified in config * updates the docs * adds clarification that brew and scoop are not supported if it is a gitlab release * fixes copy paster in release.md * adds missing blob pipe in defaults and publish due to missing in merge * updates comment in gitlab client
2019-06-29 16:02:40 +02:00
return "", err
}
if ctx.Config.Release.Draft && ctx.Config.Release.ReplaceExistingDraft {
2022-08-21 21:05:00 +02:00
if err := c.deleteExistingDraftRelease(ctx, title); err != nil {
return "", err
}
}
// Truncate the release notes if it's too long (github doesn't allow more than 125000 characters)
body = truncateReleaseBody(body)
data := &github.RepositoryRelease{
Name: github.String(title),
TagName: github.String(ctx.Git.CurrentTag),
Body: github.String(body),
// Always start with a draft release while uploading artifacts.
// PublishRelease will undraft it.
Draft: github.Bool(true),
Prerelease: github.Bool(ctx.PreRelease),
MakeLatest: github.String("true"),
2017-05-13 23:09:42 +02:00
}
if ctx.Config.Release.DiscussionCategoryName != "" {
data.DiscussionCategoryName = github.String(ctx.Config.Release.DiscussionCategoryName)
}
if target := ctx.Config.Release.TargetCommitish; target != "" {
target, err := tmpl.New(ctx).Apply(target)
if err != nil {
return "", err
}
if target != "" {
data.TargetCommitish = github.String(target)
}
}
if latest := strings.TrimSpace(ctx.Config.Release.MakeLatest); latest == "false" {
data.MakeLatest = github.String(latest)
}
release, err := c.createOrUpdateRelease(ctx, data, body)
if err != nil {
return "", fmt.Errorf("could not release: %w", err)
}
return strconv.FormatInt(release.GetID(), 10), nil
}
func (c *githubClient) PublishRelease(ctx *context.Context, releaseID string) (err error) {
releaseIDInt, err := strconv.ParseInt(releaseID, 10, 64)
if err != nil {
return fmt.Errorf("non-numeric release ID %q: %w", releaseID, err)
}
if _, err := c.updateRelease(ctx, releaseIDInt, &github.RepositoryRelease{
Draft: github.Bool(ctx.Config.Release.Draft),
}); err != nil {
return fmt.Errorf("could not update existing release: %w", err)
}
return nil
}
func (c *githubClient) createOrUpdateRelease(ctx *context.Context, data *github.RepositoryRelease, body string) (*github.RepositoryRelease, error) {
c.checkRateLimit(ctx)
release, _, err := c.client.Repositories.GetReleaseByTag(
ctx,
ctx.Config.Release.GitHub.Owner,
ctx.Config.Release.GitHub.Name,
data.GetTagName(),
)
2017-05-13 23:09:42 +02:00
if err != nil {
release, resp, err := c.client.Repositories.CreateRelease(
2017-05-13 23:09:42 +02:00
ctx,
ctx.Config.Release.GitHub.Owner,
ctx.Config.Release.GitHub.Name,
data,
)
if err == nil {
log.WithField("name", data.GetName()).
WithField("release-id", release.GetID()).
WithField("request-id", resp.Header.Get("X-GitHub-Request-Id")).
Info("release created")
}
return release, err
2022-04-11 14:47:14 +02:00
}
data.Body = github.String(getReleaseNotes(release.GetBody(), body, ctx.Config.Release.ReleaseNotesMode))
return c.updateRelease(ctx, release.GetID(), data)
}
func (c *githubClient) updateRelease(ctx *context.Context, id int64, data *github.RepositoryRelease) (*github.RepositoryRelease, error) {
c.checkRateLimit(ctx)
release, resp, err := c.client.Repositories.EditRelease(
ctx,
ctx.Config.Release.GitHub.Owner,
ctx.Config.Release.GitHub.Name,
id,
data,
)
if err == nil {
log.WithField("name", data.GetName()).
WithField("release-id", release.GetID()).
WithField("request-id", resp.Header.Get("X-GitHub-Request-Id")).
Info("release updated")
}
return release, err
2017-05-13 23:09:42 +02:00
}
func (c *githubClient) ReleaseURLTemplate(ctx *context.Context) (string, error) {
downloadURL, err := tmpl.New(ctx).Apply(ctx.Config.GitHubURLs.Download)
if err != nil {
return "", fmt.Errorf("templating GitHub download URL: %w", err)
}
return fmt.Sprintf(
"%s/%s/%s/releases/download/{{ .Tag }}/{{ .ArtifactName }}",
downloadURL,
ctx.Config.Release.GitHub.Owner,
ctx.Config.Release.GitHub.Name,
), nil
}
2017-05-13 23:09:42 +02:00
func (c *githubClient) Upload(
ctx *context.Context,
feat: add gitlab for releases (#1038) * outlines gitlab client integration * makes client parameter more explicit * adds gitlab url to config * changes releaseID to string to adapt to gitlab * updates to latest gitlab client lib 0.18 * fixes copy paster in gitlab upload func * fixes gitlab typo in config * adds gitlab token to env and context * release now uses the client factory method * skips brew pipe if it is not a github release * add github tokentype to publish tests * skips scoop pipe if it is not a github release * corrects brew skip msg * adds gitlab token to main test * adds gitlab to release docs * validates config and errors accordingly * adapt release pipe name to include gitlab * fixes gitlab client after testing * moves not-configured brew and scoop pipe checks as first check * adds more debug to gitlab client * adapts changelog generation for gitlab markdown * adds debug log for gitlab changelog * env needs to run before changelog pipe * moves gitlab default download url to default pipe * moves multiple releases check to from config to release pipe * release differs now for github and gitlab * adds debug gitlab release update msgs * moves env pipe as second after before because it determines the token type other pipes depend on * adaptes error check on gitlab release creation * Revert "adaptes error check on gitlab release creation" This reverts commit 032024571c76140f8e2207ee01cc08088f37594b. * simplifies gitlab client logic. removes comments * skips tls verification for gitlab client if specified in config * updates the docs * adds clarification that brew and scoop are not supported if it is a gitlab release * fixes copy paster in release.md * adds missing blob pipe in defaults and publish due to missing in merge * updates comment in gitlab client
2019-06-29 16:02:40 +02:00
releaseID string,
artifact *artifact.Artifact,
2017-05-13 23:09:42 +02:00
file *os.File,
) error {
c.checkRateLimit(ctx)
feat: add gitlab for releases (#1038) * outlines gitlab client integration * makes client parameter more explicit * adds gitlab url to config * changes releaseID to string to adapt to gitlab * updates to latest gitlab client lib 0.18 * fixes copy paster in gitlab upload func * fixes gitlab typo in config * adds gitlab token to env and context * release now uses the client factory method * skips brew pipe if it is not a github release * add github tokentype to publish tests * skips scoop pipe if it is not a github release * corrects brew skip msg * adds gitlab token to main test * adds gitlab to release docs * validates config and errors accordingly * adapt release pipe name to include gitlab * fixes gitlab client after testing * moves not-configured brew and scoop pipe checks as first check * adds more debug to gitlab client * adapts changelog generation for gitlab markdown * adds debug log for gitlab changelog * env needs to run before changelog pipe * moves gitlab default download url to default pipe * moves multiple releases check to from config to release pipe * release differs now for github and gitlab * adds debug gitlab release update msgs * moves env pipe as second after before because it determines the token type other pipes depend on * adaptes error check on gitlab release creation * Revert "adaptes error check on gitlab release creation" This reverts commit 032024571c76140f8e2207ee01cc08088f37594b. * simplifies gitlab client logic. removes comments * skips tls verification for gitlab client if specified in config * updates the docs * adds clarification that brew and scoop are not supported if it is a gitlab release * fixes copy paster in release.md * adds missing blob pipe in defaults and publish due to missing in merge * updates comment in gitlab client
2019-06-29 16:02:40 +02:00
githubReleaseID, err := strconv.ParseInt(releaseID, 10, 64)
if err != nil {
return err
}
_, resp, err := c.client.Repositories.UploadReleaseAsset(
2017-05-13 23:09:42 +02:00
ctx,
ctx.Config.Release.GitHub.Owner,
ctx.Config.Release.GitHub.Name,
feat: add gitlab for releases (#1038) * outlines gitlab client integration * makes client parameter more explicit * adds gitlab url to config * changes releaseID to string to adapt to gitlab * updates to latest gitlab client lib 0.18 * fixes copy paster in gitlab upload func * fixes gitlab typo in config * adds gitlab token to env and context * release now uses the client factory method * skips brew pipe if it is not a github release * add github tokentype to publish tests * skips scoop pipe if it is not a github release * corrects brew skip msg * adds gitlab token to main test * adds gitlab to release docs * validates config and errors accordingly * adapt release pipe name to include gitlab * fixes gitlab client after testing * moves not-configured brew and scoop pipe checks as first check * adds more debug to gitlab client * adapts changelog generation for gitlab markdown * adds debug log for gitlab changelog * env needs to run before changelog pipe * moves gitlab default download url to default pipe * moves multiple releases check to from config to release pipe * release differs now for github and gitlab * adds debug gitlab release update msgs * moves env pipe as second after before because it determines the token type other pipes depend on * adaptes error check on gitlab release creation * Revert "adaptes error check on gitlab release creation" This reverts commit 032024571c76140f8e2207ee01cc08088f37594b. * simplifies gitlab client logic. removes comments * skips tls verification for gitlab client if specified in config * updates the docs * adds clarification that brew and scoop are not supported if it is a gitlab release * fixes copy paster in release.md * adds missing blob pipe in defaults and publish due to missing in merge * updates comment in gitlab client
2019-06-29 16:02:40 +02:00
githubReleaseID,
2017-05-13 23:09:42 +02:00
&github.UploadOptions{
Name: artifact.Name,
2017-05-13 23:09:42 +02:00
},
file,
)
if err != nil {
requestID := ""
if resp != nil {
requestID = resp.Header.Get("X-GitHub-Request-Id")
}
log.WithField("name", artifact.Name).
WithField("release-id", releaseID).
WithField("request-id", requestID).
Warn("upload failed")
}
if err == nil {
return nil
}
if resp != nil && resp.StatusCode == http.StatusUnprocessableEntity {
return err
}
return RetriableError{err}
2017-05-13 23:09:42 +02:00
}
// getMilestoneByTitle returns a milestone by title.
func (c *githubClient) getMilestoneByTitle(ctx *context.Context, repo Repo, title string) (*github.Milestone, error) {
c.checkRateLimit(ctx)
// The GitHub API/SDK does not provide lookup by title functionality currently.
opts := &github.MilestoneListOptions{
ListOptions: github.ListOptions{PerPage: 100},
}
for {
milestones, resp, err := c.client.Issues.ListMilestones(
ctx,
repo.Owner,
repo.Name,
opts,
)
if err != nil {
return nil, err
}
for _, m := range milestones {
if m != nil && m.Title != nil && *m.Title == title {
return m, nil
}
}
if resp.NextPage == 0 {
break
}
opts.Page = resp.NextPage
}
return nil, nil
}
func overrideGitHubClientAPI(ctx *context.Context, client *github.Client) error {
if ctx.Config.GitHubURLs.API == "" {
return nil
}
apiURL, err := tmpl.New(ctx).Apply(ctx.Config.GitHubURLs.API)
if err != nil {
return fmt.Errorf("templating GitHub API URL: %w", err)
}
api, err := url.Parse(apiURL)
if err != nil {
return err
}
uploadURL, err := tmpl.New(ctx).Apply(ctx.Config.GitHubURLs.Upload)
if err != nil {
return fmt.Errorf("templating GitHub upload URL: %w", err)
}
upload, err := url.Parse(uploadURL)
if err != nil {
return err
}
client.BaseURL = api
client.UploadURL = upload
return nil
}
2022-08-21 21:05:00 +02:00
func (c *githubClient) deleteExistingDraftRelease(ctx *context.Context, name string) error {
c.checkRateLimit(ctx)
opt := github.ListOptions{PerPage: 50}
for {
releases, resp, err := c.client.Repositories.ListReleases(
ctx,
ctx.Config.Release.GitHub.Owner,
ctx.Config.Release.GitHub.Name,
&opt,
)
if err != nil {
return fmt.Errorf("could not delete existing drafts: %w", err)
}
for _, r := range releases {
if r.GetDraft() && r.GetName() == name {
if _, err := c.client.Repositories.DeleteRelease(
ctx,
ctx.Config.Release.GitHub.Owner,
ctx.Config.Release.GitHub.Name,
r.GetID(),
); err != nil {
return fmt.Errorf("could not delete previous draft release: %w", err)
}
log.WithField("commit", r.GetTargetCommitish()).
WithField("tag", r.GetTagName()).
WithField("name", r.GetName()).
Info("deleted previous draft release")
// in theory, there should be only 1 release matching, so we can just return
return nil
}
}
if resp.NextPage == 0 {
return nil
}
opt.Page = resp.NextPage
}
}