Repository: goreleaser/goreleaser
Pull request #1547 (2020-05-22 04:53:07)

feat:  Gitea client add create file function
implement for brew and scoop support for Gitea-hosted repos

now[bot]: 
This pull request is being automatically deployed with Vercel ([learn more](https://zeit.ink/github-learn-more)).
To see the status of your deployment, click on the icon next to each commit.
	
6543: @caarlos0 I dont realy cnow what this will function will enable goreleaser - just noticed it while doing #1546

would be nice if you can test it throu :)
6543: and feel free to change the pull title ...
codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/1547?src=pr&el=h1) Report
> Merging [#1547](https://codecov.io/gh/goreleaser/goreleaser/pull/1547?src=pr&el=desc) into [master](https://codecov.io/gh/goreleaser/goreleaser/commit/5f41640978a306ea60c7478ebd65bd152ec3c467&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `59.37%`.

[![Impacted file tree graph](https://codecov.io/gh/goreleaser/goreleaser/pull/1547/graphs/tree.svg?width=650&height=150&src=pr&token=3bJvv6b7hN)](https://codecov.io/gh/goreleaser/goreleaser/pull/1547?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1547      +/-   ##
==========================================
- Coverage   85.58%   85.37%   -0.22%     
==========================================
  Files          70       70              
  Lines        3851     3883      +32     
==========================================
+ Hits         3296     3315      +19     
- Misses        459      470      +11     
- Partials       96       98       +2     
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/1547?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/client/gitea.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1547/diff?src=pr&el=tree#diff-aW50ZXJuYWwvY2xpZW50L2dpdGVhLmdv) | `90.44% <59.37%> (-9.56%)` | :arrow_down: |
| [internal/pipe/brew/brew.go](https://codecov.io/gh/goreleaser/goreleaser/pull/1547/diff?src=pr&el=tree#diff-aW50ZXJuYWwvcGlwZS9icmV3L2JyZXcuZ28=) | `88.23% <0.00%> (+0.06%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/1547?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/1547?src=pr&el=footer). Last update [5f41640...306d7c1](https://codecov.io/gh/goreleaser/goreleaser/pull/1547?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

6543: @caarlos0 I removed dummy test and add a real one now CI works

only problem is codecov now thinks this pull is bad :/
6543: @radeksimko avter you have looked at the commits, If I should squash them just tell
caarlos0: > @caarlos0 What are your thoughts on the deprecation?

I think it should be fine, yes... 
6543: @radeksimko @caarlos0 current state?
caarlos0: as @radeksimko mentioned, this pr alone does nothing... need the changes on brew/scoop pipe as well...
6543: hmm ok feel free to cherry-pick this :)
radeksimko: @6543 I have done some refactoring which should make it easier to enable gitea for both Homebrew and Scoop.

I assume it would be better to wait [until `v0.12.1` lands](https://gitea.com/gitea/go-sdk/releases) according to your comment above though?
6543: @radeksimko to wait for v0.12.1 is a good idear :)
6543: @radeksimko with your refactor and v0.12.1 release - what has to be done to finish this?
radeksimko: Good question @6543 

I think the main thing is checking the remaining docs which still claim gitea isn't supported, e.g. https://github.com/goreleaser/goreleaser/blob/b05c95a00f03fedc570a04465f3185c1b7546439/www/docs/customization/homebrew.md

>    NOTE: make sure the url_template, the token and given repo (github or gitlab) owner and name are from the
>    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

https://github.com/goreleaser/goreleaser/blob/b05c95a00f03fedc570a04465f3185c1b7546439/www/docs/customization/scoop.md

> Template for the url which is determined by the given Token (github or gitlab)
> Default for github is "https://github.com/<repo_owner>/<repo_name>/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
> Default for gitlab is "https://gitlab.com/<repo_owner>/<repo_name>/uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}"
> Gitea is not supported yet, but the support coming

and double check the relevant `brew` and `scoop` packages for any similar comments in the code.

Finally - have you tried this out with a real Homebrew tap or Scoop bucket hosted in Gitea? That's probably the last thing remaining I'd also like to check myself before merging this.
radeksimko: and this will need to be implemented too:

https://github.com/goreleaser/goreleaser/blob/01fd3e8c7b3b16e77996b0434fc49f62a033f775/internal/client/gitea.go#L197-L199
6543: give me some time :) and thanks for the hint
caarlos0: @6543 is this still valid?
6543: I'll rebase ... :)

vercel[bot]: @6543 is attempting to deploy a commit to the **Goreleaser** Team on [Vercel](https://vercel.com).

A member of the Team first needs to [authorize it](https://vercel.com/git/authorize?team=Goreleaser&type=github&job=%7B%22headInfo%22%3A%7B%22sha%22%3A%2253838225b3440791482f2060a9a8ad8afe2a1a97%22%7D%2C%22id%22%3A%22QmQYPtYnossxmV1e6xxomGCjDFzVz4FC7uMZC4ByLR7QZz%22%2C%22org%22%3A%22goreleaser%22%2C%22prId%22%3A1547%2C%22repo%22%3A%22goreleaser%22%7D).


6543: @caarlos0 I think it's valid again :)

NOTE: didn't test it out in the wild again ...
mavogel: @6543 I could help you out testing _out in the wild_. Could you rebase for me upfront :)
6543: @mavogel would be **great** ... sure :)
6543: @mavogel -> 98a3d3c
6543: looks like I need to change some things at this point ... WIP
mavogel: ok let me know when you're done :)
6543: @mavogel it's ready - it was just some changes missing in the test
mavogel: @6543 I was testing it locally due to my config for gitlab/gitea you can find [here](https://gitlab.com/mavogel/release-testing#testing-against-gitea) and also set it up locally. Unfortunately, there are some manual steps to do.

When I hover over one of the releases (e.g `release-testing_0.3.46_Linux_i386.tar.gz`) to get the download URL, which will later be used within the brew formula, I get `http://localhost:3000/attachments/e26a9054-85bc-4971-898d-f7cf17c56d21`. More investigation/debugging will be necessary to find out where we can get the hash during the upload process and then pass it through as we did already for GitLab with the `ArtifactUploadHash`. 

<img width="786" alt="Screenshot 2020-11-15 at 23 38 03" src="https://user-images.githubusercontent.com/8409778/99198961-27bcd400-279c-11eb-9d1f-403dd084dd99.png">

I'll get back to it tomorrow, but feel free to set it up locally as well. 

@caarlos0 would it make sense to add those local configs somewhere here in this repo, so people can set up their own dev env to verify those changes?

6543: @mavogel you dont need the uuid you can use the name too as it's done on github. the UI just use the uuid since you can upload artifacts with the same name ... this should be fixed, but I had no time to do so
6543: @mavogel  `http://localhost:3000/user/repo/releases/download/0.3.46/release-testing_0.3.46_Linux_i386.tar.gz` is also valide
mavogel: ok, thanks for the heads up. I'll restest later
mavogel: @6543 on publishing the homebrew taps I get an `json: cannot unmarshal array into Go value of type gitea.ContentsResponse`

I quick investigation seems like this method is causing it internally
```go
currentFile, resp, err := c.client.GetContents(repo.Owner, repo.Name, branchName, path)
// -> getParsedResponse -> json.Unmarshal(data, obj)
```
at https://gitea.com/gitea/go-sdk/src/branch/master/gitea/client.go#L242

AFAICS you're also the maintainer of the sdk :) 

mavogel: Update: 
- I got it fixed by creating an empty `Formula` folder in the `homebrew-tap` repository. 
- then it  was possible to install the binary via
```sh
brew tap mavogel/gitea-tools ssh://git@localhost:222/mavogel/homebrew-tap.git
brew install mavogel/gitea-tools/release-testing
```

So happy to have it successfully tested in the wild 🎉 
Should we open an issue anyway for the SDK function when trying to create a file in a non-existing path, should return another error than the one `json: cannot unmarshal`?
6543: @mavogel pleace do :) - I'm happy to follow up on that specific issue on a new created issue in the sdk repository :)
6543: > and this will need to be implemented too:
> 
> https://github.com/goreleaser/goreleaser/blob/01fd3e8c7b3b16e77996b0434fc49f62a033f775/internal/client/gitea.go#L197-L199

what is it doing?
6543: @radeksimko added
@caarlos0 think it is ready & tested now :)
6543: @radeksimko added
@caarlos0 think it is ready & tested now :)
6543: @caarlos0 do you squash-merge or should i swash it?
6543: @caarlos0 thanks for the advice - done
mavogel: @6543 : there you go: https://gitea.com/gitea/go-sdk/issues/459

@caarlos0 : for the testing setup there was a lot of manual work to do until I discovered this recent [setup](https://gitea.com/gitea/go-sdk/src/branch/master/Makefile#L81) of gitea. IMHO it would make sense to add this and some additional steps (like homebrew-tap repo creation) to the docs. For `gitlab` as well, so Acc tests can be set-up quickly. What do you think? I could contribute this
caarlos0: > @caarlos0 : for the testing setup there was a lot of manual work to do until I discovered this recent [setup](https://gitea.com/gitea/go-sdk/src/branch/master/Makefile#L81) of gitea. IMHO it would make sense to add this and some additional steps (like homebrew-tap repo creation) to the docs. For `gitlab` as well, so Acc tests can be set-up quickly. What do you think? I could contribute this

you mean as documentation? 

I think it makes sense to put somewhere, or at least link to it somewhere, yes 🤔 
6543: something missing?

Patch:
diff --git a/internal/client/gitea.go b/internal/client/gitea.go
index 65c51e7d..3c20bc1f 100644
--- a/internal/client/gitea.go
+++ b/internal/client/gitea.go
@@ -2,6 +2,7 @@ package client
 
 import (
 	"crypto/tls"
+	"encoding/base64"
 	"fmt"
 	"net/http"
 	"net/url"
@@ -85,8 +86,42 @@ func (c *giteaClient) CreateFile(
 	path,
 	message string,
 ) error {
-	//TODO: implement for brew and scoop support for Gitea-hosted repos
-	return nil
+	// use default branch
+	branchName := ""
+
+	fileOptions := gitea.FileOptions{
+		Message:    message,
+		BranchName: branchName,
+		Author: gitea.Identity{
+			Name:  commitAuthor.Name,
+			Email: commitAuthor.Email,
+		},
+		Committer: gitea.Identity{
+			Name:  commitAuthor.Name,
+			Email: commitAuthor.Email,
+		},
+	}
+
+	currentFile, resp, err := c.client.GetContents(repo.Owner, repo.Name, branchName, path)
+	// file not exist, create it
+	if err != nil {
+		if resp == nil || resp.StatusCode != http.StatusNotFound {
+			return err
+		}
+		_, _, err = c.client.CreateFile(repo.Owner, repo.Name, path, gitea.CreateFileOptions{
+			FileOptions: fileOptions,
+			Content:     base64.StdEncoding.EncodeToString(content),
+		})
+		return err
+	}
+
+	// update file
+	_, _, err = c.client.UpdateFile(repo.Owner, repo.Name, path, gitea.UpdateFileOptions{
+		FileOptions: fileOptions,
+		SHA:         currentFile.SHA,
+		Content:     base64.StdEncoding.EncodeToString(content),
+	})
+	return err
 }
 
 func (c *giteaClient) createRelease(ctx *context.Context, title, body string) (*gitea.Release, error) {
@@ -193,7 +228,12 @@ func (c *giteaClient) CreateRelease(ctx *context.Context, body string) (string,
 }
 
 func (c *giteaClient) ReleaseURLTemplate(ctx *context.Context) (string, error) {
-	return "", NotImplementedError{TokenType: context.TokenTypeGitea}
+	return fmt.Sprintf(
+		"%s/%s/%s/releases/download/{{ .Tag }}/{{ .ArtifactName }}",
+		ctx.Config.GiteaURLs.Download,
+		ctx.Config.Release.Gitea.Owner,
+		ctx.Config.Release.Gitea.Name,
+	), nil
 }
 
 // Upload uploads a file into a release repository.
diff --git a/internal/pipe/defaults/defaults.go b/internal/pipe/defaults/defaults.go
index 5a0ea581..538700e2 100644
--- a/internal/pipe/defaults/defaults.go
+++ b/internal/pipe/defaults/defaults.go
@@ -3,6 +3,8 @@
 package defaults
 
 import (
+	"strings"
+
 	"github.com/goreleaser/goreleaser/internal/client"
 	"github.com/goreleaser/goreleaser/internal/middleware"
 	"github.com/goreleaser/goreleaser/pkg/context"
@@ -27,6 +29,9 @@ func (Pipe) Run(ctx *context.Context) error {
 	if ctx.Config.GitLabURLs.Download == "" {
 		ctx.Config.GitLabURLs.Download = client.DefaultGitLabDownloadURL
 	}
+	if ctx.Config.GiteaURLs.Download == "" {
+		ctx.Config.GiteaURLs.Download = strings.ReplaceAll(ctx.Config.GiteaURLs.API, "/api/v1", "")
+	}
 	for _, defaulter := range defaults.Defaulters {
 		if err := middleware.Logging(
 			defaulter.String(),
diff --git a/pkg/config/config.go b/pkg/config/config.go
index bd0ee746..c34110be 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -30,6 +30,7 @@ type GitLabURLs struct {
 // GiteaURLs holds the URLs to be used when using gitea.
 type GiteaURLs struct {
 	API           string `yaml:"api,omitempty"`
+	Download      string `yaml:"download,omitempty"`
 	SkipTLSVerify bool   `yaml:"skip_tls_verify,omitempty"`
 }
 
diff --git a/www/docs/environment.md b/www/docs/environment.md
index 8352fbc6..aba9f09e 100644
--- a/www/docs/environment.md
+++ b/www/docs/environment.md
@@ -74,6 +74,7 @@ the `.goreleaser.yml` configuration file:
 # .goreleaser.yml
 gitea_urls:
   api: https://gitea.myinstance.com/api/v1/
+  download: https://gitea.myinstance.com
   # set to true if you use a self-signed certificate
   skip_tls_verify: false
 ```


Test patch:
diff --git a/internal/client/client_test.go b/internal/client/client_test.go
index 42f9ad3e..1aa7356e 100644
--- a/internal/client/client_test.go
+++ b/internal/client/client_test.go
@@ -19,7 +19,9 @@ func TestClientNewGitea(t *testing.T) {
 	ctx := &context.Context{
 		Config: config.Project{
 			GiteaURLs: config.GiteaURLs{
-				API: "https://git.dtluna.net/api/v1",
+				// TODO: use a mocked http server to cover version api
+				API:      "https://gitea.com/api/v1",
+				Download: "https://gitea.com",
 			},
 		},
 		TokenType: context.TokenTypeGitea,
@@ -35,7 +37,7 @@ func TestClientNewGiteaInvalidURL(t *testing.T) {
 	ctx := &context.Context{
 		Config: config.Project{
 			GiteaURLs: config.GiteaURLs{
-				API: "://git.dtluna.net/api/v1",
+				API: "://gitea.com/api/v1",
 			},
 		},
 		TokenType: context.TokenTypeGitea,
diff --git a/internal/client/gitea_test.go b/internal/client/gitea_test.go
index 9e2d773f..7038c311 100644
--- a/internal/client/gitea_test.go
+++ b/internal/client/gitea_test.go
@@ -22,7 +22,7 @@ type GetInstanceURLSuite struct {
 
 func (s *GetInstanceURLSuite) TestWithScheme() {
 	t := s.T()
-	rootURL := "https://git.dtluna.net"
+	rootURL := "https://gitea.com"
 	result, err := getInstanceURL(rootURL + "/api/v1")
 	require.NoError(t, err)
 	require.Equal(t, rootURL, result)
@@ -30,7 +30,7 @@ func (s *GetInstanceURLSuite) TestWithScheme() {
 
 func (s *GetInstanceURLSuite) TestParseError() {
 	t := s.T()
-	host := "://.dtluna.net"
+	host := "://wrong.gitea.com"
 	result, err := getInstanceURL(host)
 	require.Error(t, err)
 	require.Empty(t, result)
@@ -38,7 +38,7 @@ func (s *GetInstanceURLSuite) TestParseError() {
 
 func (s *GetInstanceURLSuite) TestNoScheme() {
 	t := s.T()
-	host := "git.dtluna.net"
+	host := "gitea.com"
 	result, err := getInstanceURL(host)
 	require.Error(t, err)
 	require.Empty(t, result)
@@ -113,7 +113,7 @@ func (s *GiteaReleasesTestSuite) SetupTest() {
 			CurrentTag:  s.tag,
 			Commit:      s.commit,
 			ShortCommit: s.commit[0:2],
-			URL:         "https://git.dtluna.net/goreleaser/goreleaser.git",
+			URL:         "https://gitea.com/goreleaser/goreleaser.git",
 		},
 		PreRelease: s.isPrerelease,
 	}
@@ -240,20 +240,25 @@ func (s *GiteaupdateReleaseSuite) TestError() {
 	require.Nil(t, release)
 }
 
-func TestGiteaupdateReleaseSuite(t *testing.T) {
-	suite.Run(t, new(GiteaupdateReleaseSuite))
+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")
+
+	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("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}
+	repo := Repo{Owner: s.owner, Name: s.repoName}
+	content := []byte("hello world")
+	path := "file.txt"
+	message := "add hello world"
+	err := s.client.CreateFile(s.ctx, author, repo, content, path, message)
+	require.Nil(t, err)
 }
 
-func TestGiteaCreateFile(t *testing.T) {
-	client := giteaClient{}
-	ctx := context.Context{}
-	author := config.CommitAuthor{}
-	repo := Repo{}
-	content := []byte{}
-	path := ""
-	message := ""
-	file := client.CreateFile(&ctx, author, repo, content, path, message)
-	require.Nil(t, file)
+func TestGiteaupdateReleaseSuite(t *testing.T) {
+	suite.Run(t, new(GiteaupdateReleaseSuite))
 }
 
 type GiteaCreateReleaseSuite struct {
@@ -401,3 +406,26 @@ func (s *GiteaUploadSuite) TestSuccess() {
 func TestGiteaUploadSuite(t *testing.T) {
 	suite.Run(t, new(GiteaUploadSuite))
 }
+
+func TestGiteaReleaseURLTemplate(t *testing.T) {
+	var ctx = context.New(config.Project{
+		GiteaURLs: config.GiteaURLs{
+			API:      "https://gitea.com/api/v1",
+			Download: "https://gitea.com",
+		},
+		Release: config.Release{
+			Gitea: config.Repo{
+				Owner: "owner",
+				Name:  "name",
+			},
+		},
+	})
+	client, err := NewGitea(ctx, ctx.Token)
+	require.NoError(t, err)
+
+	urlTpl, err := client.ReleaseURLTemplate(ctx)
+	require.NoError(t, err)
+
+	expectedUrl := "https://gitea.com/owner/name/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
+	require.Equal(t, expectedUrl, urlTpl)
+}
diff --git a/internal/pipe/defaults/defaults_test.go b/internal/pipe/defaults/defaults_test.go
index f7f93c7a..5bb61be4 100644
--- a/internal/pipe/defaults/defaults_test.go
+++ b/internal/pipe/defaults/defaults_test.go
@@ -104,4 +104,16 @@ func TestFillPartial(t *testing.T) {
 	require.Empty(t, ctx.Config.Dockers[0].Goarm)
 	require.Equal(t, "disttt", ctx.Config.Dist)
 	require.NotEqual(t, "https://github.com", ctx.Config.GitHubURLs.Download)
+
+	ctx = &context.Context{
+		TokenType: context.TokenTypeGitea,
+
+		Config: config.Project{
+			GiteaURLs: config.GiteaURLs{
+				API: "https://gitea.com/api/v1",
+			},
+		},
+	}
+	require.NoError(t, Pipe{}.Run(ctx))
+	require.Equal(t, "https://gitea.com", ctx.Config.GiteaURLs.Download)
 }