Repository: goreleaser/goreleaser (go)

Issue:
[Bug]: announce: ensure correctness of the GitURL
### What happened?

If the repo is cloned via `SSH`, `.GitURL` will be the SSH URL, which will be consumed by `announce` pipe and send a notification to target. Eventually there will be an SSL URL instead of HTTP URL. [^1] [^2]



[^1]: https://github.com/goreleaser/goreleaser/issues/2622#issuecomment-958358917
[^2]: https://github.com/goreleaser/goreleaser/pull/2623#pullrequestreview-795989193

### How can we reproduce this?

1. Clone the repo using `ssh`
2. Set an announce on the template
3. `.GitURL` eventually will be SSH URL instead of HTTPS

### goreleaser version

47d0e827436f3c2ed8caac9d7f508b2a862c0e00

### GoReleaser Check

- [X] goreleaser check shows no errors

### Search

- [X] I did search for other open and closed issues before opening this.

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct

### Additional context

Some solutions to tackle this problem:
* We can mention that the `.GitURL` may not be an HTTP URL on the `template.md`. 
* Converting SSH URL to HTTPS URL in the announce stage?
* Separating to `.GitHTTPSURL` and `.GitSSHURL`?

cc: @developer-guy @erkanzileli 

Pull request:
feat: add message template support for git providers
Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com>
Co-authored-by: Furkan Türkal <furkan.turkal@trendyol.com>
Co-authored-by: Erkan Zileli <erkan.zileli@trendyol.com>

<!--

Hi, thanks for contributing!

Please make sure you read our CONTRIBUTING guide.

Also, add tests and the respective documentation changes as well.

-->


<!-- If applied, this commit will... -->

Fixes #2625 

<!-- Why is this change being made? -->

to support message templating while announcing for supported git providers such as GitHub, GitLab, Gitea



Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 44ccf875..0e44a1d7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -61,7 +61,7 @@ jobs:
             ${{ runner.os }}-go-
       -
         name: Setup Sigstore
-        uses: sigstore/cosign-installer@v1.2.1
+        uses: sigstore/cosign-installer@v1.3.0
       -
         name: build and test
         run: task ci
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 99cf970a..566b0eae 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -210,6 +210,8 @@ publishers:
     cmd: ./scripts/fury-upload.sh {{ .ArtifactName }}
 
 release:
+  extra_files:
+    - glob: ./cosign.pub
   footer: |
     ## What to do next?
 
diff --git a/Dockerfile b/Dockerfile
index 59b22dc5..0644c440 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.17.2-alpine
+FROM golang:1.17.3-alpine
 
 RUN apk add --no-cache bash \
 	curl \
diff --git a/USERS.md b/USERS.md
index e33aa757..fe686960 100644
--- a/USERS.md
+++ b/USERS.md
@@ -7,6 +7,7 @@ Currently, the following organizations are using GoReleaser:
 
 1. [Charm](https://charm.sh)
 1. [Hugo](https://gohugo.io/)
+1. [IRON Security](https://iron.security)
 1. [Ministry of Justice (UK)](https://mojdigital.blog.gov.uk/)
 1. [Numary](https://numary.com)
 1. [Schwarz IT](https://jobs.schwarz/)
diff --git a/internal/linux/arch.go b/internal/linux/arch.go
index 2a26dcad..b3304aad 100644
--- a/internal/linux/arch.go
+++ b/internal/linux/arch.go
@@ -20,7 +20,7 @@ func Arch(key string) string {
 	case "arm6": // GOARCH + GOARM
 		return "armhf"
 	case "arm7": // GOARCH + GOARM
-		return "armv7l"
+		return "armhf"
 	case "mips64le":
 		return "mips64el"
 	case "mipsle":
diff --git a/internal/pipe/git/git.go b/internal/pipe/git/git.go
index bf6d1cb3..e670e53b 100644
--- a/internal/pipe/git/git.go
+++ b/internal/pipe/git/git.go
@@ -55,7 +55,7 @@ func getInfo(ctx *context.Context) (context.GitInfo, error) {
 	if !git.IsRepo() {
 		return context.GitInfo{}, ErrNotRepository
 	}
-	info, err := getGitInfo()
+	info, err := getGitInfo(ctx)
 	if err != nil && ctx.Snapshot {
 		log.WithError(err).Warn("ignoring errors because this is a snapshot")
 		if info.Commit == "" {
@@ -66,7 +66,7 @@ func getInfo(ctx *context.Context) (context.GitInfo, error) {
 	return info, err
 }
 
-func getGitInfo() (context.GitInfo, error) {
+func getGitInfo(ctx *context.Context) (context.GitInfo, error) {
 	branch, err := getBranch()
 	if err != nil {
 		return context.GitInfo{}, fmt.Errorf("couldn't get current branch: %w", err)
@@ -88,6 +88,18 @@ func getGitInfo() (context.GitInfo, error) {
 		return context.GitInfo{}, fmt.Errorf("couldn't get remote URL: %w", err)
 	}
 
+	var tagErr error
+	tag, err := getTag()
+	if err != nil {
+		tagErr = err
+		tag = "v0.0.0"
+	}
+
+	releaseURL, err := getReleaseURL(ctx.TokenType, gitURL, tag)
+	if err != nil {
+		return context.GitInfo{}, fmt.Errorf("couldn't get release URL: %w", err)
+	}
+
 	if strings.HasPrefix(gitURL, "https://") {
 		u, err := url.Parse(gitURL)
 		if err != nil {
@@ -97,8 +109,7 @@ func getGitInfo() (context.GitInfo, error) {
 		gitURL = u.String()
 	}
 
-	tag, err := getTag()
-	if err != nil {
+	if tagErr != nil {
 		return context.GitInfo{
 			Branch:      branch,
 			Commit:      full,
@@ -106,7 +117,8 @@ func getGitInfo() (context.GitInfo, error) {
 			ShortCommit: short,
 			CommitDate:  date,
 			URL:         gitURL,
-			CurrentTag:  "v0.0.0",
+			ReleaseURL:  releaseURL,
+			CurrentTag:  tag,
 		}, ErrNoTag
 	}
 	return context.GitInfo{
@@ -117,6 +129,7 @@ func getGitInfo() (context.GitInfo, error) {
 		ShortCommit: short,
 		CommitDate:  date,
 		URL:         gitURL,
+		ReleaseURL:  releaseURL,
 	}, nil
 }
 
@@ -206,3 +219,16 @@ func getTag() (string, error) {
 func getURL() (string, error) {
 	return git.Clean(git.Run("ls-remote", "--get-url"))
 }
+
+func getReleaseURL(tokenType context.TokenType, url, tag string) (string, error) {
+	switch tokenType {
+	case context.TokenTypeGitHub:
+		return fmt.Sprintf("%s/releases/tag/%s", url, tag), nil
+	case context.TokenTypeGitLab:
+		return fmt.Sprintf("%s/-/releases/%s", url, tag), nil
+	case context.TokenTypeGitea:
+		return fmt.Sprintf("%s/releases/tag/%s", url, tag), nil
+	default:
+		return "", fmt.Errorf("invalid client token type: %q", tokenType)
+	}
+}
diff --git a/internal/pipe/mattermost/mattermost.go b/internal/pipe/mattermost/mattermost.go
index 4d9671c6..3271f612 100644
--- a/internal/pipe/mattermost/mattermost.go
+++ b/internal/pipe/mattermost/mattermost.go
@@ -18,7 +18,7 @@ import (
 const (
 	defaultColor           = "#2D313E"
 	defaultUsername        = `GoReleaser`
-	defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ trimsuffix .GitURL ".git" }}/releases/tag/{{ .Tag }}`
+	defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}`
 	defaultMessageTitle    = `{{ .ProjectName }} {{ .Tag }} is out!`
 )
 
@@ -35,6 +35,7 @@ func (Pipe) Default(ctx *context.Context) error {
 	if ctx.Config.Announce.Mattermost.MessageTemplate == "" {
 		ctx.Config.Announce.Mattermost.MessageTemplate = defaultMessageTemplate
 	}
+
 	if ctx.Config.Announce.Mattermost.TitleTemplate == "" {
 		ctx.Config.Announce.Mattermost.TitleTemplate = defaultMessageTitle
 	}
diff --git a/internal/pipe/reddit/reddit.go b/internal/pipe/reddit/reddit.go
index 4cea61c1..91c94fa5 100644
--- a/internal/pipe/reddit/reddit.go
+++ b/internal/pipe/reddit/reddit.go
@@ -12,7 +12,7 @@ import (
 
 const (
 	defaultTitleTemplate = `{{ .ProjectName }} {{ .Tag }} is out!`
-	defaultURLTemplate   = `{{ trimsuffix .GitURL ".git" }}/releases/tag/{{ .Tag }}`
+	defaultURLTemplate   = `{{ .ReleaseURL }}`
 )
 
 type Pipe struct{}
@@ -31,7 +31,7 @@ func (Pipe) Default(ctx *context.Context) error {
 	}
 
 	if ctx.Config.Announce.Reddit.URLTemplate == "" {
-		ctx.Config.Announce.Reddit.URLTemplate = defaultURLTemplate
+ 		ctx.Config.Announce.Reddit.URLTemplate = defaultURLTemplate
 	}
 
 	return nil
diff --git a/internal/pipe/release/release.go b/internal/pipe/release/release.go
index d915c007..661cbec7 100644
--- a/internal/pipe/release/release.go
+++ b/internal/pipe/release/release.go
@@ -22,7 +22,7 @@ var ErrMultipleReleases = errors.New("multiple releases are defined. Only one is
 // Pipe for github release.
 type Pipe struct{}
 
-func (Pipe) String() string                 { return "github/gitlab/gitea releases" }
+func (Pipe) String() string                 { return "scm releases" }
 func (Pipe) Skip(ctx *context.Context) bool { return ctx.Config.Release.Disable }
 
 // Default sets the pipe defaults.
@@ -45,41 +45,32 @@ func (Pipe) Default(ctx *context.Context) error {
 		ctx.Config.Release.NameTemplate = "{{.Tag}}"
 	}
 
-	// nolint: exhaustive
 	switch ctx.TokenType {
 	case context.TokenTypeGitLab:
-		{
-			if ctx.Config.Release.GitLab.Name == "" {
-				repo, err := git.ExtractRepoFromConfig()
-				if err != nil {
-					return err
-				}
-				ctx.Config.Release.GitLab = repo
+		if ctx.Config.Release.GitLab.Name == "" {
+			repo, err := git.ExtractRepoFromConfig()
+			if err != nil {
+				return err
 			}
-
-			return nil
+			ctx.Config.Release.GitLab = repo
 		}
 	case context.TokenTypeGitea:
-		{
-			if ctx.Config.Release.Gitea.Name == "" {
-				repo, err := git.ExtractRepoFromConfig()
-				if err != nil {
-					return err
-				}
-				ctx.Config.Release.Gitea = repo
+		if ctx.Config.Release.Gitea.Name == "" {
+			repo, err := git.ExtractRepoFromConfig()
+			if err != nil {
+				return err
 			}
-
-			return nil
+			ctx.Config.Release.Gitea = repo
 		}
-	}
-
-	// We keep github as default for now
-	if ctx.Config.Release.GitHub.Name == "" {
-		repo, err := git.ExtractRepoFromConfig()
-		if err != nil && !ctx.Snapshot {
-			return err
+	default:
+		// We keep github as default for now
+		if ctx.Config.Release.GitHub.Name == "" {
+			repo, err := git.ExtractRepoFromConfig()
+			if err != nil && !ctx.Snapshot {
+				return err
+			}
+			ctx.Config.Release.GitHub = repo
 		}
-		ctx.Config.Release.GitHub = repo
 	}
 
 	// Check if we have to check the git tag for an indicator to mark as pre release
diff --git a/internal/pipe/slack/slack.go b/internal/pipe/slack/slack.go
index 19075001..90310add 100644
--- a/internal/pipe/slack/slack.go
+++ b/internal/pipe/slack/slack.go
@@ -12,7 +12,7 @@ import (
 
 const (
 	defaultUsername        = `GoReleaser`
-	defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ trimsuffix .GitURL ".git" }}/releases/tag/{{ .Tag }}`
+	defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}`
 )
 
 type Pipe struct{}
diff --git a/internal/pipe/smtp/smtp.go b/internal/pipe/smtp/smtp.go
index 7b2a426e..ef5c7597 100644
--- a/internal/pipe/smtp/smtp.go
+++ b/internal/pipe/smtp/smtp.go
@@ -13,7 +13,7 @@ import (
 
 const (
 	defaultSubjectTemplate = `{{ .ProjectName }} {{ .Tag }} is out!`
-	defaultBodyTemplate    = `You can view details from: {{ trimsuffix .GitURL ".git" }}/releases/tag/{{ .Tag }}`
+	defaultBodyTemplate    = `You can view details from: {{ .ReleaseURL }}`
 )
 
 type Pipe struct{}
@@ -29,14 +29,14 @@ type Config struct {
 }
 
 func (Pipe) Default(ctx *context.Context) error {
-	if ctx.Config.Announce.SMTP.SubjectTemplate == "" {
-		ctx.Config.Announce.SMTP.SubjectTemplate = defaultSubjectTemplate
-	}
-
 	if ctx.Config.Announce.SMTP.BodyTemplate == "" {
 		ctx.Config.Announce.SMTP.BodyTemplate = defaultBodyTemplate
 	}
 
+	if ctx.Config.Announce.SMTP.SubjectTemplate == "" {
+		ctx.Config.Announce.SMTP.SubjectTemplate = defaultSubjectTemplate
+	}
+
 	return nil
 }
 
diff --git a/internal/pipe/teams/teams.go b/internal/pipe/teams/teams.go
index 9fee248b..727991aa 100644
--- a/internal/pipe/teams/teams.go
+++ b/internal/pipe/teams/teams.go
@@ -13,7 +13,7 @@ import (
 const (
 	defaultColor           = "#2D313E"
 	defaultIcon            = "https://goreleaser.com/static/avatar.png"
-	defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ trimsuffix .GitURL ".git" }}/releases/tag/{{ .Tag }}`
+	defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}`
 	defaultMessageTitle    = `{{ .ProjectName }} {{ .Tag }} is out!`
 )
 
diff --git a/internal/pipe/telegram/telegram.go b/internal/pipe/telegram/telegram.go
index 8e984924..7b273c60 100644
--- a/internal/pipe/telegram/telegram.go
+++ b/internal/pipe/telegram/telegram.go
@@ -10,7 +10,7 @@ import (
 	"github.com/goreleaser/goreleaser/pkg/context"
 )
 
-const defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ trimsuffix .GitURL ".git" }}/releases/tag/{{ .Tag }}`
+const defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}`
 
 type Pipe struct{}
 
diff --git a/internal/pipe/twitter/twitter.go b/internal/pipe/twitter/twitter.go
index 0dbf1734..96989a74 100644
--- a/internal/pipe/twitter/twitter.go
+++ b/internal/pipe/twitter/twitter.go
@@ -11,7 +11,7 @@ import (
 	"github.com/goreleaser/goreleaser/pkg/context"
 )
 
-const defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ trimsuffix .GitURL ".git" }}/releases/tag/{{ .Tag }}`
+const defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}`
 
 type Pipe struct{}
 
diff --git a/internal/tmpl/tmpl.go b/internal/tmpl/tmpl.go
index 287f73d8..56c1773c 100644
--- a/internal/tmpl/tmpl.go
+++ b/internal/tmpl/tmpl.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/Masterminds/semver/v3"
+
 	"github.com/goreleaser/goreleaser/internal/artifact"
 	"github.com/goreleaser/goreleaser/pkg/build"
 	"github.com/goreleaser/goreleaser/pkg/context"
@@ -37,6 +38,7 @@ const (
 	commitDate      = "CommitDate"
 	commitTimestamp = "CommitTimestamp"
 	gitURL          = "GitURL"
+	releaseURL      = "ReleaseURL"
 	major           = "Major"
 	minor           = "Minor"
 	patch           = "Patch"
@@ -83,6 +85,7 @@ func New(ctx *context.Context) *Template {
 			commitDate:      ctx.Git.CommitDate.UTC().Format(time.RFC3339),
 			commitTimestamp: ctx.Git.CommitDate.UTC().Unix(),
 			gitURL:          ctx.Git.URL,
+			releaseURL:      ctx.Git.ReleaseURL,
 			env:             ctx.Env,
 			date:            ctx.Date.UTC().Format(time.RFC3339),
 			timestamp:       ctx.Date.UTC().Unix(),
diff --git a/pkg/context/context.go b/pkg/context/context.go
index cc76b7a4..6c69a940 100644
--- a/pkg/context/context.go
+++ b/pkg/context/context.go
@@ -25,6 +25,7 @@ type GitInfo struct {
 	FullCommit  string
 	CommitDate  time.Time
 	URL         string
+	ReleaseURL  string
 }
 
 // Env is the environment variables.
diff --git a/www/docs/ci/azurepipelines.md b/www/docs/ci/azurepipelines.md
index 26dff273..7f8e356f 100644
--- a/www/docs/ci/azurepipelines.md
+++ b/www/docs/ci/azurepipelines.md
@@ -1,6 +1,61 @@
 # Azure Pipelines
 
-Generally there're two ways to define an [Azure Pipeline](https://azure.microsoft.com/en-us/services/devops/pipelines/):
+GoReleaser can also be used within our official [GoReleaser Extensions for Azure DevOps][goreleaser-extension]
+through [Visual Studio marketplace][marketplace].
+
+### Task definition
+
+````yaml
+- task: goreleaser@0
+  inputs:
+    version: 'latest'
+    distribution: 'goreleaser'
+    args: ''
+    workdir: '$(Build.SourcesDirectory)'
+````
+
+### Task inputs
+
+Following inputs can be used
+
+| Name             | Type    | Default      | Description                                                      |
+|------------------|---------|--------------|------------------------------------------------------------------|
+| `distribution`   | String  | `goreleaser` | GoReleaser distribution, either `goreleaser` or `goreleaser-pro` |
+| `version`**¹**   | String  | `latest`     | GoReleaser version                                               |
+| `args`           | String  |              | Arguments to pass to GoReleaser                                  |
+| `workdir`        | String  | `$(Build.SourcesDirectory)`          | Working directory (below repository root)                        |
+| `install-only`   | Bool    | `false`      | Just install GoReleaser                                          |
+
+> **¹** Can be a fixed version like `v0.132.0` or a max satisfying semver one like `~> 0.132`. In this case this will return `v0.132.1`.
+> For the `pro` version, add `-pro` to the string
+
+### Task environment variables
+
+```yaml
+...
+variables:
+- name: GORELEASER_KEY
+  value: xxx
+...
+
+or short:
+
+...
+variables:
+  GORELEASER_KEY: xxx
+...
+```
+
+Following environment variables can be used, as environment variable.
+
+| Name             | Description                           |
+|------------------|---------------------------------------|
+| `GITHUB_TOKEN`   | [GITHUB_TOKEN](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token) for e.g. `brew` or `gofish` |
+| `GORELEASER_KEY` | Your [GoReleaser Pro](https://goreleaser.com/pro) License Key, in case you are using the `goreleaser-pro` distribution                              |
+
+### Example pipeline
+
+Generally there are two ways to define an [Azure Pipeline](https://azure.microsoft.com/en-us/services/devops/pipelines/):
 Classic pipelines defined in the UI or YAML pipelines.
 
 Here is how to do it with YAML:
@@ -41,9 +96,16 @@ jobs:
           version: "$(GO_VERSION)"
         displayName: Install Go
 
-      - bash: curl -sL https://git.io/goreleaser | bash
-        displayName: Run Goreleaser
+      - task: goreleaser@0
+        inputs:
+          version: 'latest'
+          distribution: 'goreleaser'
+          args: ''
+          workdir: '$(Build.SourcesDirectory)'
 ```
 
-In this example a `Test` job is used to run `go test ./...` to first make sure that there're no failing tests.
-Only if that job succeeds and the pipeline was triggered from a tag (because of the defined `condition`) Goreleaser will be run.
+In this example a `Test` job is used to run `go test ./...` to first make sure that there're no failing tests. Only if
+that job succeeds and the pipeline was triggered from a tag (because of the defined `condition`) Goreleaser will be run.
+
+[goreleaser-extension]: https://marketplace.visualstudio.com/items?itemName=GoReleaser.goreleaser
+[marketplace]: https://marketplace.visualstudio.com/azuredevops
diff --git a/www/docs/users.md b/www/docs/users.md
index e33aa757..ca43189f 100644
--- a/www/docs/users.md
+++ b/www/docs/users.md
@@ -6,7 +6,9 @@ you are using GoReleaser.
 Currently, the following organizations are using GoReleaser:
 
 1. [Charm](https://charm.sh)
+1. [Compose Generator](https://www.compose-generator.com)
 1. [Hugo](https://gohugo.io/)
+1. [IRON Security](https://iron.security)
 1. [Ministry of Justice (UK)](https://mojdigital.blog.gov.uk/)
 1. [Numary](https://numary.com)
 1. [Schwarz IT](https://jobs.schwarz/)


Test patch:
diff --git a/internal/linux/arch_test.go b/internal/linux/arch_test.go
index 444599d8..591ca757 100644
--- a/internal/linux/arch_test.go
+++ b/internal/linux/arch_test.go
@@ -14,7 +14,7 @@ func TestArch(t *testing.T) {
 		"linuxarm64":             "arm64",
 		"linuxarm5":              "armel",
 		"linuxarm6":              "armhf",
-		"linuxarm7":              "armv7l",
+		"linuxarm7":              "armhf",
 		"linuxppc64":             "ppc64",
 		"linuxppc64le":           "ppc64le",
 		"linuxwhat":              "what",
diff --git a/internal/pipe/mattermost/mattermost_test.go b/internal/pipe/mattermost/mattermost_test.go
index 403ba042..53e5d4e0 100644
--- a/internal/pipe/mattermost/mattermost_test.go
+++ b/internal/pipe/mattermost/mattermost_test.go
@@ -89,6 +89,7 @@ func TestPostWebhook(t *testing.T) {
 	})
 
 	ctx.Git.CurrentTag = "v1.0.0"
+	ctx.Git.ReleaseURL = "https://github.com/honk/honk/releases/tag/v1.0.0"
 	ctx.Git.URL = "https://github.com/honk/honk"
 
 	os.Setenv("MATTERMOST_WEBHOOK", ts.URL)