Skip to content

Plugin Examples

Official plugins live in the GoSemantics/semrel-plugins repository (currently being set up).

github

ProviderPlugin for GitHub. Creates releases, uploads assets, and fetches commit history via the GitHub REST API.

conventional-commits

CommitAnalyzerPlugin that follows the Conventional Commits specification to determine the version bump.

markdown-changelog

ChangelogGeneratorPlugin that renders release notes as a Markdown CHANGELOG.md section.

go-mod-updater

FilesUpdaterPlugin that stamps the next version into Go source files via ldflags-compatible version variables.

A bare-bones commit analyser using go-plugin:

package main
import (
"context"
"os"
"strings"
hclog "github.com/hashicorp/go-hclog"
goplugin "github.com/hashicorp/go-plugin"
semrelv1 "github.com/GoSemantics/semrel-plugins/gen/v1"
)
type analyzer struct{ semrelv1.UnimplementedCommitAnalyzerPluginServer }
func (a *analyzer) AnalyzeCommits(
_ context.Context, req *semrelv1.AnalyzeCommitsRequest,
) (*semrelv1.AnalyzeCommitsResponse, error) {
bump := semrelv1.BumpLevel_BUMP_LEVEL_NONE
for _, c := range req.Ctx.Commits {
switch {
case strings.Contains(c.RawMessage, "BREAKING CHANGE"):
return &semrelv1.AnalyzeCommitsResponse{Bump: semrelv1.BumpLevel_BUMP_LEVEL_MAJOR}, nil
case strings.HasPrefix(c.RawMessage, "feat"):
bump = semrelv1.BumpLevel_BUMP_LEVEL_MINOR
case strings.HasPrefix(c.RawMessage, "fix") && bump == semrelv1.BumpLevel_BUMP_LEVEL_NONE:
bump = semrelv1.BumpLevel_BUMP_LEVEL_PATCH
}
}
return &semrelv1.AnalyzeCommitsResponse{Bump: bump}, nil
}
func main() {
// All logs MUST go to stderr. stdout is reserved for the go-plugin handshake.
logger := hclog.New(&hclog.LoggerOptions{Name: "my-analyzer", Output: os.Stderr})
goplugin.Serve(&goplugin.ServeConfig{
HandshakeConfig: semrelv1.HandshakeConfig,
Plugins: map[string]goplugin.Plugin{
"commit-analyzer": &semrelv1.CommitAnalyzerPlugin{Impl: &analyzer{}},
},
GRPCServer: goplugin.DefaultGRPCServer,
Logger: logger,
})
}

A Slack notification hook in Python. Note: Python plugins must use sys.stderr for all output — sys.stdout is reserved for the go-plugin handshake:

import sys, concurrent.futures
import grpc
from semrel_v1 import semantic_release_pb2, semantic_release_pb2_grpc
class SlackHook(semantic_release_pb2_grpc.HooksPluginServicer):
def OnSuccess(self, request, context):
webhook = request.ctx.config.get("webhook_url", "")
if webhook:
import urllib.request, json
data = json.dumps({"text": f"Released {request.ctx.next_version}!"})
urllib.request.urlopen(webhook, data.encode())
return semantic_release_pb2.OnSuccessResponse()
def OnFail(self, request, context):
return semantic_release_pb2.OnFailResponse()
if __name__ == "__main__":
# go-plugin writes the handshake line to stdout automatically.
# Our code must never write to stdout.
print("starting", file=sys.stderr) # safe: goes to stderr
server = grpc.server(concurrent.futures.ThreadPoolExecutor())
semantic_release_pb2_grpc.add_HooksPluginServicer_to_server(SlackHook(), server)
# go-plugin manages the port/socket and writes the handshake line.
# Use the go-plugin Python bridge (semrel-plugins/python) for the serve() call.
server.wait_for_termination()

Community-contributed plugins will be listed in the semrel registry once it launches. To have your plugin listed, open an issue or PR in the main repository.