Skip to content

Plugin SDK

The Plugin SDK (github.com/GoSemantics/semrel-plugins) provides the generated Protobuf types and helper scaffolding for writing semrel plugins in Go. Plugins in other languages can use the raw .proto file from the main repository.

  • Go ≥ 1.24
  • Familiarity with gRPC and Protobuf
  1. Create a new module

    Terminal window
    mkdir my-semrel-plugin
    cd my-semrel-plugin
    go mod init github.com/myorg/my-semrel-plugin
    go get github.com/GoSemantics/semrel-plugins
  2. Implement the plugin interface

    Each plugin type corresponds to a gRPC service defined in semantic_release.proto.

    The following example implements a CommitAnalyzerPlugin that bumps minor for any feat: commit and patch for everything else:

    package main
    import (
    "context"
    "strings"
    semrelv1 "github.com/GoSemantics/semrel-plugins/gen/v1"
    )
    type myAnalyzer struct {
    semrelv1.UnimplementedCommitAnalyzerPluginServer
    }
    func (a *myAnalyzer) AnalyzeCommits(
    ctx context.Context,
    req *semrelv1.AnalyzeCommitsRequest,
    ) (*semrelv1.AnalyzeCommitsResponse, error) {
    bump := semrelv1.BumpLevel_BUMP_LEVEL_NONE
    for _, c := range req.Ctx.Commits {
    msg := c.RawMessage
    if strings.Contains(msg, "BREAKING CHANGE") {
    return &semrelv1.AnalyzeCommitsResponse{
    Bump: semrelv1.BumpLevel_BUMP_LEVEL_MAJOR,
    Reason: "breaking change detected",
    }, nil
    }
    if strings.HasPrefix(msg, "feat") {
    bump = semrelv1.BumpLevel_BUMP_LEVEL_MINOR
    } else if strings.HasPrefix(msg, "fix") && bump < semrelv1.BumpLevel_BUMP_LEVEL_MINOR {
    bump = semrelv1.BumpLevel_BUMP_LEVEL_PATCH
    }
    }
    return &semrelv1.AnalyzeCommitsResponse{Bump: bump}, nil
    }
  3. Serve via go-plugin

    Use plugin.Serve() — go-plugin manages the handshake protocol over stdout automatically. Use hclog for all logging so output goes to stderr:

    package main
    import (
    "os"
    hclog "github.com/hashicorp/go-hclog"
    goplugin "github.com/hashicorp/go-plugin"
    semrelv1 "github.com/GoSemantics/semrel-plugins/gen/v1"
    )
    func main() {
    logger := hclog.New(&hclog.LoggerOptions{
    Name: "my-analyzer",
    Output: os.Stderr, // MUST be stderr — stdout is reserved for go-plugin handshake
    Level: hclog.Info,
    })
    goplugin.Serve(&goplugin.ServeConfig{
    HandshakeConfig: semrelv1.HandshakeConfig,
    Plugins: map[string]goplugin.Plugin{
    "commit-analyzer": &semrelv1.CommitAnalyzerPlugin{Impl: &myAnalyzer{}},
    },
    GRPCServer: goplugin.DefaultGRPCServer,
    Logger: logger,
    })
    }
  4. Build and install the plugin

    Terminal window
    go build -o .semrel/my-analyzer .
  5. Register it in .semrel.yaml

    plugins:
    - name: my-analyzer
    path: ./.semrel/my-analyzer

A single plugin binary can register multiple gRPC services on the same server:

semrelv1.RegisterCommitAnalyzerPluginServer(srv, &myAnalyzer{})
semrelv1.RegisterChangelogGeneratorPluginServer(srv, &myChangelog{})

Every RPC receives a ReleaseContext in its request. It contains:

FieldTypeDescription
repo_owner / repo_namestringRepository identity
branchstringBranch being released
last_versionSemanticVersionPrevious release tag
next_versionSemanticVersionCalculated next version
commits[]CommitCommits since last release
dry_runboolWhen true, no side-effects should be applied
configmap<string, string>Plugin args from .semrel.yaml