Repository: goreleaser/goreleaser
Pull request #2539 (2021-09-29 00:44:20)

Draft: feat: allow default branch to be used in Gitlab CreateFile
This commit will look up the default branch name in the Gitlab project, and if it can't find one, fall back to `master`, when using the CreateFile function

Previously, the branch used in CreateFile for Gitlab operations was hard coded to `master`. Now that Gitlab has switched over to `main` as the default branch, things like brew publishing breaks since the `master` branch doesn't exist. 

There isn't currently a test in place for any of the CreateFile stuff in `gitlab_test.rb`, so not sure what to do there...open to ideas though!

https://about.gitlab.com/blog/2021/03/10/new-git-default-branch-name/

Thanks for the consideration of this patch, and definitely open to changes/suggestions/etc!



codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2539?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) Report
> Merging [#2539](https://codecov.io/gh/goreleaser/goreleaser/pull/2539?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (f3c2797) into [master](https://codecov.io/gh/goreleaser/goreleaser/commit/1b6a2f5e1cdafe3ce254e619d5311402bda5573b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (1b6a2f5) will **decrease** coverage by `0.33%`.
> The diff coverage is `0.00%`.

[![Impacted file tree graph](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/graphs/tree.svg?width=650&height=150&src=pr&token=3bJvv6b7hN&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)](https://codecov.io/gh/goreleaser/goreleaser/pull/2539?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)

```diff
@@            Coverage Diff             @@
##           master    #2539      +/-   ##
==========================================
- Coverage   83.46%   83.13%   -0.34%     
==========================================
  Files          93       95       +2     
  Lines        6447     6741     +294     
==========================================
+ Hits         5381     5604     +223     
- Misses        910      966      +56     
- Partials      156      171      +15     
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/2539?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) | Coverage Δ | |
|---|---|---|
| [internal/client/gitlab.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvY2xpZW50L2dpdGxhYi5nbw==) | `37.53% <0.00%> (-2.15%)` | :arrow_down: |
| [pkg/config/config.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-cGtnL2NvbmZpZy9jb25maWcuZ28=) | `90.72% <0.00%> (ø)` | |
| [internal/pipe/publish/publish.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9wdWJsaXNoL3B1Ymxpc2guZ28=) | `80.00% <0.00%> (ø)` | |
| [internal/pipe/announce/announce.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9hbm5vdW5jZS9hbm5vdW5jZS5nbw==) | `100.00% <0.00%> (ø)` | |
| [internal/pipe/mattermost/mattermost.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9tYXR0ZXJtb3N0L21hdHRlcm1vc3QuZ28=) | `79.45% <0.00%> (ø)` | |
| [internal/pipe/gofish/gofish.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9nb2Zpc2gvZ29maXNoLmdv) | `80.00% <0.00%> (ø)` | |
| [internal/artifact/artifact.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvYXJ0aWZhY3QvYXJ0aWZhY3QuZ28=) | `98.02% <0.00%> (+0.02%)` | :arrow_up: |
| [internal/pipe/brew/brew.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9icmV3L2JyZXcuZ28=) | `83.20% <0.00%> (+0.78%)` | :arrow_up: |
| [internal/golden/golden.go](https://codecov.io/gh/goreleaser/goreleaser/pull/2539/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvZ29sZGVuL2dvbGRlbi5nbw==) | `76.66% <0.00%> (+2.59%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2539?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/2539?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser). Last update [1b6a2f5...f3c2797](https://codecov.io/gh/goreleaser/goreleaser/pull/2539?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser).

caarlos0: we have https://github.com/goreleaser/goreleaser/issues/2388 

I think we could allow to pass the branch, and get the default only if its empty. That's for all impls (gitlab, github and gitea)

wdyt?
drewstinnett: Ahhh I had not realized these were the same, good point! Yes, that makes a lot of sense here. I'd be happy to adjust the PR to use the behavior described above and in #2388! If it's cool with you, I'll adjust the MR later today (or maybe later this week, depending on time haha)
caarlos0: sure thing! thanks so much @drewstinnett 🙏

btw, let me know if you need help on anything, I'm usually on gophers slack and also on our discord server 👌
drewstinnett: Def will, ty @caarlos0, you rock! 
caarlos0: I would say that, to simplify, we could leave out the create branch thing...

- if config branch is empty, use default branch
- otherwise, use the branch provided, regardless of wether it exists or not

that way, if the user introduces a typo (e.g. `maste` instead of `master`), the error won't be "silenced"... 


also, to get the default branch on github: https://pkg.go.dev/github.com/google/go-github/v39/github#Repository.GetDefaultBranch

gitea seems to have it as well: https://pkg.go.dev/code.gitea.io/sdk/gitea#Repository
drewstinnett: Ahhh good call, will adjust!
drewstinnett: Okey doke, removed the `CreateBranch` stuff, and added the `GetDefaultBranch`, along with some tests to Github and Gitea (in addition to the one that's in there already for Gitlab)

Happy to adjust as needed, and ty for the consideration!
caarlos0: Amazing! Thanks you!

Patch:
diff --git a/internal/client/client.go b/internal/client/client.go
index 2ae483bb..36b9ee5b 100644
--- a/internal/client/client.go
+++ b/internal/client/client.go
@@ -20,8 +20,9 @@ type Info struct {
 }
 
 type Repo struct {
-	Owner string
-	Name  string
+	Owner  string
+	Name   string
+	Branch string
 }
 
 func (r Repo) String() string {
@@ -38,6 +39,7 @@ type Client interface {
 	ReleaseURLTemplate(ctx *context.Context) (string, error)
 	CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo Repo, content []byte, path, message string) (err error)
 	Upload(ctx *context.Context, releaseID string, artifact *artifact.Artifact, file *os.File) (err error)
+	GetDefaultBranch(ctx *context.Context, repo Repo) (string, error)
 }
 
 // New creates a new client depending on the token type.
diff --git a/internal/client/config.go b/internal/client/config.go
index 8cf34fc6..f36981a6 100644
--- a/internal/client/config.go
+++ b/internal/client/config.go
@@ -6,7 +6,8 @@ import (
 
 func RepoFromRef(ref config.RepoRef) Repo {
 	return Repo{
-		Owner: ref.Owner,
-		Name:  ref.Name,
+		Owner:  ref.Owner,
+		Name:   ref.Name,
+		Branch: ref.Branch,
 	}
 }
diff --git a/internal/client/gitea.go b/internal/client/gitea.go
index fabfdc31..4019cba5 100644
--- a/internal/client/gitea.go
+++ b/internal/client/gitea.go
@@ -81,6 +81,20 @@ func (c *giteaClient) CloseMilestone(ctx *context.Context, repo Repo, title stri
 	return err
 }
 
+func (c *giteaClient) GetDefaultBranch(ctx *context.Context, repo Repo) (string, error) {
+	projectID := repo.String()
+	p, res, err := c.client.GetRepo(repo.Owner, repo.Name)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"projectID":  projectID,
+			"statusCode": res.StatusCode,
+			"err":        err.Error(),
+		}).Warn("error checking for default branch")
+		return "", err
+	}
+	return p.DefaultBranch, nil
+}
+
 // CreateFile creates a file in the repository at a given path
 // or updates the file if it exists.
 func (c *giteaClient) CreateFile(
@@ -92,11 +106,27 @@ func (c *giteaClient) CreateFile(
 	message string,
 ) error {
 	// use default branch
-	branchName := ""
+	var branch string
+	var err error
+	if repo.Branch != "" {
+		branch = repo.Branch
+	} else {
+		branch, err = c.GetDefaultBranch(ctx, repo)
+		if err != nil {
+			// Fall back to 'master' 😭
+			log.WithFields(log.Fields{
+				"fileName":        path,
+				"projectID":       repo.String(),
+				"requestedBranch": branch,
+				"err":             err.Error(),
+			}).Warn("error checking for default branch, using master")
+		}
+
+	}
 
 	fileOptions := gitea.FileOptions{
 		Message:    message,
-		BranchName: branchName,
+		BranchName: branch,
 		Author: gitea.Identity{
 			Name:  commitAuthor.Name,
 			Email: commitAuthor.Email,
@@ -107,7 +137,7 @@ func (c *giteaClient) CreateFile(
 		},
 	}
 
-	currentFile, resp, err := c.client.GetContents(repo.Owner, repo.Name, branchName, path)
+	currentFile, resp, err := c.client.GetContents(repo.Owner, repo.Name, branch, path)
 	// file not exist, create it
 	if err != nil {
 		if resp == nil || resp.StatusCode != http.StatusNotFound {
diff --git a/internal/client/github.go b/internal/client/github.go
index a74c7b29..d38ea564 100644
--- a/internal/client/github.go
+++ b/internal/client/github.go
@@ -51,6 +51,20 @@ func NewGitHub(ctx *context.Context, token string) (Client, error) {
 	return &githubClient{client: client}, nil
 }
 
+// GetDefaultBranch returns the default branch of a github repo
+func (c *githubClient) GetDefaultBranch(ctx *context.Context, repo Repo) (string, error) {
+	p, res, err := c.client.Repositories.Get(ctx, repo.Owner, repo.Name)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"projectID":  repo.String(),
+			"statusCode": res.StatusCode,
+			"err":        err.Error(),
+		}).Warn("error checking for default branch")
+		return "", err
+	}
+	return p.GetDefaultBranch(), nil
+}
+
 // CloseMilestone closes a given milestone.
 func (c *githubClient) CloseMilestone(ctx *context.Context, repo Repo, title string) error {
 	milestone, err := c.getMilestoneByTitle(ctx, repo, title)
@@ -84,6 +98,22 @@ func (c *githubClient) CreateFile(
 	path,
 	message string,
 ) error {
+	var branch string
+	var err error
+	if repo.Branch != "" {
+		branch = repo.Branch
+	} else {
+		branch, err = c.GetDefaultBranch(ctx, repo)
+		if err != nil {
+			// Fall back to sdk default
+			log.WithFields(log.Fields{
+				"fileName":        path,
+				"projectID":       repo.String(),
+				"requestedBranch": branch,
+				"err":             err.Error(),
+			}).Warn("error checking for default branch, using master")
+		}
+	}
 	options := &github.RepositoryContentFileOptions{
 		Committer: &github.CommitAuthor{
 			Name:  github.String(commitAuthor.Name),
@@ -93,6 +123,12 @@ func (c *githubClient) CreateFile(
 		Message: github.String(message),
 	}
 
+	// Set the branch if we got it above...otherwise, just default to
+	// whatever the SDK does auto-magically
+	if branch != "" {
+		options.Branch = &branch
+	}
+
 	file, _, res, err := c.client.Repositories.GetContents(
 		ctx,
 		repo.Owner,
diff --git a/internal/client/gitlab.go b/internal/client/gitlab.go
index 27b95aac..bf98f400 100644
--- a/internal/client/gitlab.go
+++ b/internal/client/gitlab.go
@@ -49,6 +49,21 @@ func NewGitLab(ctx *context.Context, token string) (Client, error) {
 	return &gitlabClient{client: client}, nil
 }
 
+// GetDefaultBranch get the default branch
+func (c *gitlabClient) GetDefaultBranch(ctx *context.Context, repo Repo) (string, error) {
+	projectID := repo.String()
+	p, res, err := c.client.Projects.GetProject(projectID, nil)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"projectID":  projectID,
+			"statusCode": res.StatusCode,
+			"err":        err.Error(),
+		}).Warn("error checking for default branch")
+		return "", err
+	}
+	return p.DefaultBranch, nil
+}
+
 // CloseMilestone closes a given milestone.
 func (c *gitlabClient) CloseMilestone(ctx *context.Context, repo Repo, title string) error {
 	milestone, err := c.getMilestoneByTitle(repo, title)
@@ -90,19 +105,42 @@ func (c *gitlabClient) CreateFile(
 	message string, // the commit msg
 ) error {
 	fileName := path
-	// we assume having the formula in the master branch only
-	ref := "master"
-	branch := "master"
+	projectID := repo.String()
+
+	// Use the project default branch if we can get it...otherwise, just use
+	// 'master'
+	var branch, ref string
+	var err error
+	// Use the branch if given one
+	if repo.Branch != "" {
+		branch = repo.Branch
+	} else {
+		// Try to get the default branch from the Git provider
+		branch, err = c.GetDefaultBranch(ctx, repo)
+		if err != nil {
+			// Fall back to 'master' 😭
+			log.WithFields(log.Fields{
+				"fileName":        fileName,
+				"projectID":       repo.String(),
+				"requestedBranch": branch,
+				"err":             err.Error(),
+			}).Warn("error checking for default branch, using master")
+			ref = "master"
+			branch = "master"
+		}
+	}
+	ref = branch
 	opts := &gitlab.GetFileOptions{Ref: &ref}
 	castedContent := string(content)
-	projectID := repo.Owner + "/" + repo.Name
 
 	log.WithFields(log.Fields{
-		"owner": repo.Owner,
-		"name":  repo.Name,
+		"owner":  repo.Owner,
+		"name":   repo.Name,
+		"ref":    ref,
+		"branch": branch,
 	}).Debug("projectID at brew")
 
-	_, res, err := c.client.RepositoryFiles.GetFile(projectID, fileName, opts)
+	_, res, err := c.client.RepositoryFiles.GetFile(repo.String(), fileName, opts)
 	if err != nil && (res == nil || res.StatusCode != 404) {
 		log.WithFields(log.Fields{
 			"fileName":   fileName,
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 3bd7c4f8..4a02a575 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -47,9 +47,10 @@ type Repo struct {
 // also require separate authentication
 // e.g. Homebrew Tap, Scoop bucket.
 type RepoRef struct {
-	Owner string `yaml:",omitempty"`
-	Name  string `yaml:",omitempty"`
-	Token string `yaml:",omitempty"`
+	Owner  string `yaml:",omitempty"`
+	Name   string `yaml:",omitempty"`
+	Token  string `yaml:",omitempty"`
+	Branch string `yaml:",omitempty"`
 }
 
 // HomebrewDependency represents Homebrew dependency.
diff --git a/www/docs/customization/homebrew.md b/www/docs/customization/homebrew.md
index a9c3e755..0438a5a1 100644
--- a/www/docs/customization/homebrew.md
+++ b/www/docs/customization/homebrew.md
@@ -40,10 +40,12 @@ brews:
     # same kind. We will probably unify this in the next major version like it is done with scoop.
 
     # GitHub/GitLab repository to push the formula to
-    # Gitea is not supported yet, but the support coming
     tap:
       owner: repo-owner
       name: homebrew-tap
+      # Optionally a branch can be provided. If the branch does not exist, it
+      # will be created. If no branch is listed, the default branch will be used
+      branch: main
       # Optionally a token can be provided, if it differs from the token provided to GoReleaser
       token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
 


Test patch:
diff --git a/internal/client/gitea_test.go b/internal/client/gitea_test.go
index b1037c77..a2c74c18 100644
--- a/internal/client/gitea_test.go
+++ b/internal/client/gitea_test.go
@@ -3,6 +3,8 @@ package client
 import (
 	"fmt"
 	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
 	"os"
 	"strings"
 	"testing"
@@ -308,9 +310,11 @@ func (s *GiteaupdateReleaseSuite) TestError() {
 func (s *GiteaupdateReleaseSuite) TestGiteaCreateFile() {
 	t := s.T()
 	fileEndpoint := fmt.Sprintf("%s/api/v1/repos/%s/%s/contents/%s", s.url, s.owner, s.repoName, "file.txt")
+	projectEndpoint := fmt.Sprintf("%s/api/v1/repos/%s/%s", s.url, s.owner, s.repoName)
 
 	httpmock.RegisterResponder("GET", fmt.Sprintf("%s/api/v1/version", s.url), httpmock.NewStringResponder(200, "{\"version\":\"1.12.0\"}"))
 	httpmock.RegisterResponder("GET", fileEndpoint, httpmock.NewStringResponder(404, ""))
+	httpmock.RegisterResponder("GET", projectEndpoint, httpmock.NewStringResponder(200, ""))
 	httpmock.RegisterResponder("POST", fileEndpoint, httpmock.NewStringResponder(201, "{\n  \"content\": {\n    \"name\": \"test.file\",\n    \"path\": \"test.file\",\n    \"sha\": \"3b18e512dba79e4c8300dd08aeb37f8e728b8dad\",\n    \"type\": \"file\",\n    \"size\": 12,\n    \"encoding\": \"base64\",\n    \"content\": \"aGVsbG8gd29ybGQK\"\n  }\n}"))
 
 	author := config.CommitAuthor{Name: s.owner}
@@ -533,3 +537,67 @@ func TestGiteaReleaseURLTemplate(t *testing.T) {
 		})
 	}
 }
+
+func TestGiteaGetDefaultBranch(t *testing.T) {
+	totalRequests := 0
+	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		totalRequests++
+		defer r.Body.Close()
+
+		if strings.HasSuffix(r.URL.Path, "api/v1/version") {
+			w.WriteHeader(http.StatusOK)
+			fmt.Fprint(w, "{\"version\":\"1.12.0\"}")
+		} else {
+			w.WriteHeader(http.StatusOK)
+			fmt.Fprint(w, "{}")
+		}
+	}))
+	defer srv.Close()
+
+	ctx := context.New(config.Project{
+		GiteaURLs: config.GiteaURLs{
+			API: srv.URL,
+		},
+	})
+	client, err := NewGitea(ctx, "test-token")
+	require.NoError(t, err)
+	repo := Repo{
+		Owner:  "someone",
+		Name:   "something",
+		Branch: "somebranch",
+	}
+
+	_, err = client.GetDefaultBranch(ctx, repo)
+	require.NoError(t, err)
+	require.Equal(t, 2, totalRequests)
+}
+
+func TestGiteaGetDefaultBranchErr(t *testing.T) {
+	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		if strings.HasSuffix(r.URL.Path, "api/v1/version") {
+			w.WriteHeader(http.StatusOK)
+			fmt.Fprint(w, "{\"version\":\"1.12.0\"}")
+		} else {
+			w.WriteHeader(http.StatusNotFound)
+			fmt.Fprint(w, "{}")
+		}
+	}))
+	defer srv.Close()
+
+	ctx := context.New(config.Project{
+		GiteaURLs: config.GiteaURLs{
+			API: srv.URL,
+		},
+	})
+	client, err := NewGitea(ctx, "test-token")
+	require.NoError(t, err)
+	repo := Repo{
+		Owner:  "someone",
+		Name:   "something",
+		Branch: "somebranch",
+	}
+
+	_, err = client.GetDefaultBranch(ctx, repo)
+	require.Error(t, err)
+}
diff --git a/internal/client/github_test.go b/internal/client/github_test.go
index f0d3173d..531d864d 100644
--- a/internal/client/github_test.go
+++ b/internal/client/github_test.go
@@ -2,6 +2,8 @@ package client
 
 import (
 	"fmt"
+	"net/http"
+	"net/http/httptest"
 	"testing"
 	"text/template"
 
@@ -196,3 +198,60 @@ func TestGitHubCreateReleaseWrongNameTemplate(t *testing.T) {
 	require.Empty(t, str)
 	require.EqualError(t, err, `template: tmpl:1: unclosed action`)
 }
+
+func TestGithubGetDefaultBranch(t *testing.T) {
+	totalRequests := 0
+	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		totalRequests++
+		defer r.Body.Close()
+
+		// Assume the request to create a branch was good
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprint(w, "{}")
+	}))
+	defer srv.Close()
+
+	ctx := context.New(config.Project{
+		GitHubURLs: config.GitHubURLs{
+			API: srv.URL + "/",
+		},
+	})
+	client, err := NewGitHub(ctx, "test-token")
+	require.NoError(t, err)
+	repo := Repo{
+		Owner:  "someone",
+		Name:   "something",
+		Branch: "somebranch",
+	}
+
+	_, err = client.GetDefaultBranch(ctx, repo)
+	require.NoError(t, err)
+	require.Equal(t, 1, totalRequests)
+}
+
+func TestGithubGetDefaultBranchErr(t *testing.T) {
+	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+
+		// Assume the request to create a branch was good
+		w.WriteHeader(http.StatusNotImplemented)
+		fmt.Fprint(w, "{}")
+	}))
+	defer srv.Close()
+
+	ctx := context.New(config.Project{
+		GitHubURLs: config.GitHubURLs{
+			API: srv.URL + "/",
+		},
+	})
+	client, err := NewGitHub(ctx, "test-token")
+	require.NoError(t, err)
+	repo := Repo{
+		Owner:  "someone",
+		Name:   "something",
+		Branch: "somebranch",
+	}
+
+	_, err = client.GetDefaultBranch(ctx, repo)
+	require.Error(t, err)
+}
diff --git a/internal/client/gitlab_test.go b/internal/client/gitlab_test.go
index b15edf5d..0b48c372 100644
--- a/internal/client/gitlab_test.go
+++ b/internal/client/gitlab_test.go
@@ -323,3 +323,60 @@ func TestGitLabCreateReleaseUnkownHTTPError(t *testing.T) {
 	require.Error(t, err)
 	require.Equal(t, 2, totalRequests)
 }
+
+func TestGitlabGetDefaultBranch(t *testing.T) {
+	totalRequests := 0
+	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		totalRequests++
+		defer r.Body.Close()
+
+		// Assume the request to create a branch was good
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprint(w, "{}")
+	}))
+	defer srv.Close()
+
+	ctx := context.New(config.Project{
+		GitLabURLs: config.GitLabURLs{
+			API: srv.URL,
+		},
+	})
+	client, err := NewGitLab(ctx, "test-token")
+	require.NoError(t, err)
+	repo := Repo{
+		Owner:  "someone",
+		Name:   "something",
+		Branch: "somebranch",
+	}
+
+	_, err = client.GetDefaultBranch(ctx, repo)
+	require.NoError(t, err)
+	require.Equal(t, 2, totalRequests)
+}
+
+func TestGitlabGetDefaultBranchErr(t *testing.T) {
+	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+
+		// Assume the request to create a branch was good
+		w.WriteHeader(http.StatusNotImplemented)
+		fmt.Fprint(w, "{}")
+	}))
+	defer srv.Close()
+
+	ctx := context.New(config.Project{
+		GitLabURLs: config.GitLabURLs{
+			API: srv.URL,
+		},
+	})
+	client, err := NewGitLab(ctx, "test-token")
+	require.NoError(t, err)
+	repo := Repo{
+		Owner:  "someone",
+		Name:   "something",
+		Branch: "somebranch",
+	}
+
+	_, err = client.GetDefaultBranch(ctx, repo)
+	require.Error(t, err)
+}
diff --git a/internal/pipe/brew/brew_test.go b/internal/pipe/brew/brew_test.go
index bb853a6b..53f86676 100644
--- a/internal/pipe/brew/brew_test.go
+++ b/internal/pipe/brew/brew_test.go
@@ -1,6 +1,7 @@
 package brew
 
 import (
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -914,6 +915,10 @@ func (dc *DummyClient) CloseMilestone(ctx *context.Context, repo client.Repo, ti
 	return nil
 }
 
+func (dc *DummyClient) GetDefaultBranch(ctx *context.Context, repo client.Repo) (string, error) {
+	return "", errors.New("DummyClient does not yet implement GetDefaultBranch")
+}
+
 func (dc *DummyClient) CreateRelease(ctx *context.Context, body string) (releaseID string, err error) {
 	return
 }
diff --git a/internal/pipe/gofish/gofish_test.go b/internal/pipe/gofish/gofish_test.go
index f0515f79..8f5c8c2c 100644
--- a/internal/pipe/gofish/gofish_test.go
+++ b/internal/pipe/gofish/gofish_test.go
@@ -1,6 +1,7 @@
 package gofish
 
 import (
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -822,6 +823,10 @@ func (dc *DummyClient) ReleaseURLTemplate(ctx *context.Context) (string, error)
 	return "https://dummyhost/download/{{ .Tag }}/{{ .ArtifactName }}", nil
 }
 
+func (dc *DummyClient) GetDefaultBranch(ctx *context.Context, repo client.Repo) (string, error) {
+	return "", errors.New("DummyClient does not yet implement GetDefaultBranch")
+}
+
 func (dc *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo client.Repo, content []byte, path, msg string) (err error) {
 	dc.CreatedFile = true
 	dc.Content = string(content)
diff --git a/internal/pipe/milestone/milestone_test.go b/internal/pipe/milestone/milestone_test.go
index c4f7c12a..c7acc077 100644
--- a/internal/pipe/milestone/milestone_test.go
+++ b/internal/pipe/milestone/milestone_test.go
@@ -231,6 +231,10 @@ func (c *DummyClient) ReleaseURLTemplate(ctx *context.Context) (string, error) {
 	return "", nil
 }
 
+func (c *DummyClient) GetDefaultBranch(ctx *context.Context, repo client.Repo) (string, error) {
+	return "", errors.New("DummyClient does not yet implement GetDefaultBranch")
+}
+
 func (c *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo client.Repo, content []byte, path, msg string) error {
 	return nil
 }
diff --git a/internal/pipe/release/release_test.go b/internal/pipe/release/release_test.go
index e0b00e48..ec5b282c 100644
--- a/internal/pipe/release/release_test.go
+++ b/internal/pipe/release/release_test.go
@@ -555,6 +555,10 @@ func (c *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.Commi
 	return
 }
 
+func (c *DummyClient) GetDefaultBranch(ctx *context.Context, repo client.Repo) (string, error) {
+	return "", errors.New("DummyClient does not yet implement GetDefaultBranch")
+}
+
 func (c *DummyClient) Upload(ctx *context.Context, releaseID string, artifact *artifact.Artifact, file *os.File) error {
 	c.Lock.Lock()
 	defer c.Lock.Unlock()
diff --git a/internal/pipe/scoop/scoop_test.go b/internal/pipe/scoop/scoop_test.go
index dd60a832..3d42d094 100644
--- a/internal/pipe/scoop/scoop_test.go
+++ b/internal/pipe/scoop/scoop_test.go
@@ -2,6 +2,7 @@ package scoop
 
 import (
 	ctx "context"
+	"errors"
 	"os"
 	"path/filepath"
 	"testing"
@@ -1061,6 +1062,10 @@ func (dc *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.Comm
 	return
 }
 
+func (dc *DummyClient) GetDefaultBranch(ctx *context.Context, repo client.Repo) (string, error) {
+	return "", errors.New("DummyClient does not yet implement GetDefaultBranch")
+}
+
 func (dc *DummyClient) Upload(ctx *context.Context, releaseID string, artifact *artifact.Artifact, file *os.File) (err error) {
 	return
 }