Skip to content
Permalink
Browse files
feat: add mastodon (#3567)
This PR adds [mastodon](https://joinmastodon.org/) as an announcement
pipeline. 馃コ


![mastodon-announce](https://user-images.githubusercontent.com/42128690/202544345-d90d8f10-0818-4bc2-bc35-3dbcba4254b0.png)


Resolves #3566

Signed-off-by: jolheiser <john.olheiser@gmail.com>
  • Loading branch information
jolheiser committed Nov 17, 2022
1 parent 8ef1d43 commit e65c53172e1836524cb570a7e727a0d72545b2ae
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 0 deletions.
2 go.mod
@@ -26,6 +26,7 @@ require (
github.com/invopop/jsonschema v0.7.0
github.com/jarcoal/httpmock v1.2.0
github.com/klauspost/pgzip v1.2.5
github.com/mattn/go-mastodon v0.0.6
github.com/mitchellh/go-homedir v1.1.0
github.com/muesli/mango-cobra v1.2.0
github.com/muesli/roff v0.1.0
@@ -147,6 +148,7 @@ require (
github.com/sergi/go-diff v1.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
go.opencensus.io v0.23.0 // indirect
4 go.sum
@@ -1145,6 +1145,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-mastodon v0.0.6 h1:lqU1sOeeIapaDsDUL6udDZIzMb2Wqapo347VZlaOzf0=
github.com/mattn/go-mastodon v0.0.6/go.mod h1:cg7RFk2pcUfHZw/IvKe1FUzmlq5KnLFqs7eV2PHplV8=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
@@ -1505,6 +1507,8 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y=
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
@@ -10,6 +10,7 @@ import (
"github.com/goreleaser/goreleaser/internal/middleware/skip"
"github.com/goreleaser/goreleaser/internal/pipe/discord"
"github.com/goreleaser/goreleaser/internal/pipe/linkedin"
"github.com/goreleaser/goreleaser/internal/pipe/mastodon"
"github.com/goreleaser/goreleaser/internal/pipe/mattermost"
"github.com/goreleaser/goreleaser/internal/pipe/reddit"
"github.com/goreleaser/goreleaser/internal/pipe/slack"
@@ -33,6 +34,7 @@ var announcers = []Announcer{
// XXX: keep asc sorting
discord.Pipe{},
linkedin.Pipe{},
mastodon.Pipe{},
mattermost.Pipe{},
reddit.Pipe{},
slack.Pipe{},
@@ -0,0 +1,59 @@
package mastodon

import (
"fmt"

"github.com/caarlos0/env/v6"
"github.com/caarlos0/log"
"github.com/goreleaser/goreleaser/internal/tmpl"
"github.com/goreleaser/goreleaser/pkg/context"
"github.com/mattn/go-mastodon"
)

const defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}`

type Pipe struct{}

func (Pipe) String() string { return "mastodon" }
func (Pipe) Skip(ctx *context.Context) bool { return !ctx.Config.Announce.Mastodon.Enabled }

type Config struct {
Server string `env:"MASTODON_SERVER,notEmpty"`
ClientID string `env:"MASTODON_CLIENT_ID,notEmpty"`
ClientSecret string `env:"MASTODON_CLIENT_SECRET,notEmpty"`
AccessToken string `env:"MASTODON_ACCESS_TOKEN,notEmpty"`
}

func (Pipe) Default(ctx *context.Context) error {
if ctx.Config.Announce.Mastodon.MessageTemplate == "" {
ctx.Config.Announce.Mastodon.MessageTemplate = defaultMessageTemplate
}
return nil
}

func (Pipe) Announce(ctx *context.Context) error {
msg, err := tmpl.New(ctx).Apply(ctx.Config.Announce.Mastodon.MessageTemplate)
if err != nil {
return fmt.Errorf("announce: failed to announce to mastodon: %w", err)
}

var cfg Config
if err := env.Parse(&cfg); err != nil {
return fmt.Errorf("announce: failed to announce to mastodon: %w", err)
}

client := mastodon.NewClient(&mastodon.Config{
Server: cfg.Server,
ClientID: cfg.ClientID,
ClientSecret: cfg.ClientSecret,
AccessToken: cfg.AccessToken,
})

log.Infof("posting: '%s'", msg)
if _, err := client.PostStatus(ctx, &mastodon.Toot{
Status: msg,
}); err != nil {
return fmt.Errorf("announce: failed to announce to mastodon: %w", err)
}
return nil
}
@@ -0,0 +1,57 @@
package mastodon

import (
"testing"

"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
"github.com/stretchr/testify/require"
)

func TestStringer(t *testing.T) {
require.Equal(t, Pipe{}.String(), "mastodon")
}

func TestDefault(t *testing.T) {
ctx := context.New(config.Project{})
require.NoError(t, Pipe{}.Default(ctx))
require.Equal(t, ctx.Config.Announce.Mastodon.MessageTemplate, defaultMessageTemplate)
}

func TestAnnounceInvalidTemplate(t *testing.T) {
ctx := context.New(config.Project{
Announce: config.Announce{
Mastodon: config.Mastodon{
MessageTemplate: "{{ .Foo }",
},
},
})
require.EqualError(t, Pipe{}.Announce(ctx), `announce: failed to announce to mastodon: template: tmpl:1: unexpected "}" in operand`)
}

func TestAnnounceMissingEnv(t *testing.T) {
ctx := context.New(config.Project{
Announce: config.Announce{
Mastodon: config.Mastodon{},
},
})
require.NoError(t, Pipe{}.Default(ctx))
require.EqualError(t, Pipe{}.Announce(ctx), `announce: failed to announce to mastodon: env: environment variable "MASTODON_SERVER" should not be empty; environment variable "MASTODON_CLIENT_ID" should not be empty; environment variable "MASTODON_CLIENT_SECRET" should not be empty; environment variable "MASTODON_ACCESS_TOKEN" should not be empty`)
}

func TestSkip(t *testing.T) {
t.Run("skip", func(t *testing.T) {
require.True(t, Pipe{}.Skip(context.New(config.Project{})))
})

t.Run("dont skip", func(t *testing.T) {
ctx := context.New(config.Project{
Announce: config.Announce{
Mastodon: config.Mastodon{
Enabled: true,
},
},
})
require.False(t, Pipe{}.Skip(ctx))
})
}
@@ -957,6 +957,7 @@ type GoMod struct {
type Announce struct {
Skip string `yaml:"skip,omitempty" json:"skip,omitempty" jsonschema:"oneof_type=string;boolean"`
Twitter Twitter `yaml:"twitter,omitempty" json:"twitter,omitempty"`
Mastodon Mastodon `yaml:"mastodon,omitempty" json:"mastodon,omitempty"`
Reddit Reddit `yaml:"reddit,omitempty" json:"reddit,omitempty"`
Slack Slack `yaml:"slack,omitempty" json:"slack,omitempty"`
Discord Discord `yaml:"discord,omitempty" json:"discord,omitempty"`
@@ -982,6 +983,11 @@ type Twitter struct {
MessageTemplate string `yaml:"message_template,omitempty" json:"message_template,omitempty"`
}

type Mastodon struct {
Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
MessageTemplate string `yaml:"message_template,omitempty" json:"message_template,omitempty"`
}

type Reddit struct {
Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
ApplicationID string `yaml:"application_id,omitempty" json:"application_id,omitempty"`
@@ -18,6 +18,7 @@ import (
"github.com/goreleaser/goreleaser/internal/pipe/gomod"
"github.com/goreleaser/goreleaser/internal/pipe/krew"
"github.com/goreleaser/goreleaser/internal/pipe/linkedin"
"github.com/goreleaser/goreleaser/internal/pipe/mastodon"
"github.com/goreleaser/goreleaser/internal/pipe/mattermost"
"github.com/goreleaser/goreleaser/internal/pipe/milestone"
"github.com/goreleaser/goreleaser/internal/pipe/nfpm"
@@ -80,6 +81,7 @@ var Defaulters = []Defaulter{
teams.Pipe{},
twitter.Pipe{},
smtp.Pipe{},
mastodon.Pipe{},
mattermost.Pipe{},
milestone.Pipe{},
linkedin.Pipe{},
@@ -0,0 +1,27 @@
# Mastodon

For it to work, you'll need to create a new Mastodon app `https://social.yourdomain.tld/settings/applications/new`, and set
some environment variables on your pipeline:

- `MASTODON_SERVER`
- `MASTODON_CLIENT_ID`
- `MASTODON_CLIENT_SECRET`
- `MASTODON_ACCESS_TOKEN`

Then, you can add something like the following to your `.goreleaser.yaml` config:

```yaml
# .goreleaser.yaml
announce:
mastodon:
# Whether its enabled or not.
# Defaults to false.
enabled: true
# Message template to use while publishing.
# Defaults to `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}`
message_template: 'Awesome project {{.Tag}} is out!'
```

!!! tip
Learn more about the [name template engine](/customization/templates/).
@@ -123,6 +123,7 @@ nav:
- About: customization/announce/index.md
- customization/announce/discord.md
- customization/announce/linkedin.md
- customization/announce/mastodon.md
- customization/announce/mattermost.md
- customization/announce/reddit.md
- customization/announce/slack.md

0 comments on commit e65c531

Please sign in to comment.