diff --git a/go.mod b/go.mod index 4edc1375869..d7103d05363 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index f95fc318340..758137b22d9 100644 --- a/go.sum +++ b/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= diff --git a/internal/pipe/announce/announce.go b/internal/pipe/announce/announce.go index cd61b5a296b..25d2f07af47 100644 --- a/internal/pipe/announce/announce.go +++ b/internal/pipe/announce/announce.go @@ -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{}, diff --git a/internal/pipe/mastodon/mastodon.go b/internal/pipe/mastodon/mastodon.go new file mode 100644 index 00000000000..6654926be92 --- /dev/null +++ b/internal/pipe/mastodon/mastodon.go @@ -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 +} diff --git a/internal/pipe/mastodon/mastodon_test.go b/internal/pipe/mastodon/mastodon_test.go new file mode 100644 index 00000000000..bb0d475eb9a --- /dev/null +++ b/internal/pipe/mastodon/mastodon_test.go @@ -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)) + }) +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 733cdad9451..da27d237923 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -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"` diff --git a/pkg/defaults/defaults.go b/pkg/defaults/defaults.go index 321f6420522..2a60f26b915 100644 --- a/pkg/defaults/defaults.go +++ b/pkg/defaults/defaults.go @@ -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{}, diff --git a/www/docs/customization/announce/mastodon.md b/www/docs/customization/announce/mastodon.md new file mode 100644 index 00000000000..8dac32e58ab --- /dev/null +++ b/www/docs/customization/announce/mastodon.md @@ -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/). diff --git a/www/mkdocs.yml b/www/mkdocs.yml index b036ef4081f..ca69c72f0e3 100644 --- a/www/mkdocs.yml +++ b/www/mkdocs.yml @@ -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