Relicta Plugin SDK
Official SDK for building Relicta plugins.
Overview
Relicta plugins extend the release workflow with custom functionality. Plugins can:
- Create releases on platforms (GitHub, GitLab, etc.)
- Send notifications (Slack, Discord, etc.)
- Update issue trackers (Jira, Linear, etc.)
- Upload artifacts
- Run custom automation
Quick Start
1. Create a new plugin
mkdir my-plugin && cd my-plugin
go mod init github.com/yourname/relicta-plugin-myplugin
go get github.com/relicta-tech/relicta-plugin-sdk
2. Implement the Plugin interface
package main
import (
"context"
"github.com/relicta-tech/relicta-plugin-sdk/plugin"
)
func main() {
plugin.Serve(&MyPlugin{})
}
type MyPlugin struct{}
func (p *MyPlugin) GetInfo() plugin.Info {
return plugin.Info{
Name: "my-plugin",
Version: "1.0.0",
Description: "My custom plugin",
Author: "Your Name",
Hooks: []plugin.Hook{plugin.HookPostPublish},
}
}
func (p *MyPlugin) Execute(ctx context.Context, req plugin.ExecuteRequest) (*plugin.ExecuteResponse, error) {
// Your plugin logic here
return &plugin.ExecuteResponse{
Success: true,
Message: "Done!",
}, nil
}
func (p *MyPlugin) Validate(ctx context.Context, config map[string]any) (*plugin.ValidateResponse, error) {
return &plugin.ValidateResponse{Valid: true}, nil
}
3. Build and install
go build -o my-plugin
relicta plugin install ./my-plugin
relicta plugin enable my-plugin
Available Hooks
Plugins can respond to these workflow hooks:
| Hook |
When it runs |
pre-init |
Before initialization |
post-init |
After initialization |
pre-plan |
Before analyzing changes |
post-plan |
After analyzing changes |
pre-version |
Before version bump |
post-version |
After version bump |
pre-notes |
Before generating notes |
post-notes |
After generating notes |
pre-approve |
Before approval |
post-approve |
After approval |
pre-publish |
Before publishing |
post-publish |
After publishing |
on-success |
When release succeeds |
on-error |
When release fails |
Configuration
Plugins receive configuration from relicta.config.yaml:
plugins:
- name: my-plugin
enabled: true
config:
api_key: ${MY_API_KEY}
webhook_url: https://example.com/webhook
Access configuration in your plugin:
func (p *MyPlugin) Execute(ctx context.Context, req plugin.ExecuteRequest) (*plugin.ExecuteResponse, error) {
cfg := helpers.NewConfigParser(req.Config)
// Get with environment variable fallback
apiKey := cfg.GetString("api_key", "MY_API_KEY", "")
// Get with default value
timeout := cfg.GetInt("timeout", 30)
// ...
}
Helper Utilities
The SDK includes helpers for common tasks:
Config Parser
import "github.com/relicta-tech/relicta-plugin-sdk/helpers"
cfg := helpers.NewConfigParser(req.Config)
// String with env fallback
token := cfg.GetString("token", "GITHUB_TOKEN", "")
// Boolean with default
draft := cfg.GetBool("draft", false)
// String slice
assets := cfg.GetStringSlice("assets", nil)
// Unmarshal to struct
var myConfig MyConfig
cfg.Unmarshal(&myConfig)
Validation Builder
func (p *MyPlugin) Validate(ctx context.Context, config map[string]any) (*plugin.ValidateResponse, error) {
v := helpers.NewValidationBuilder()
// Require fields (with optional env fallback)
v.RequireString(config, "api_key", "MY_API_KEY")
// Validate URL format
v.ValidateURL(config, "webhook_url")
// Validate against allowed values
v.ValidateOneOf(config, "level", []string{"info", "warn", "error"})
// Custom validation
if cfg, ok := config["max_retries"]; ok {
if n, ok := cfg.(float64); ok && n < 0 {
v.AddError("max_retries", "must be non-negative")
}
}
return v.Build(), nil
}
Asset Validation
// Validate file paths are safe
for _, path := range cfg.GetStringSlice("assets", nil) {
if err := helpers.ValidateAssetPath(path); err != nil {
return nil, err
}
}
// Expand glob patterns
files, err := helpers.ExpandGlob("dist/*.tar.gz")
Release Context
Plugins receive rich context about the release:
func (p *MyPlugin) Execute(ctx context.Context, req plugin.ExecuteRequest) (*plugin.ExecuteResponse, error) {
ctx := req.Context
// Version info
ctx.Version // "1.2.3"
ctx.PreviousVersion // "1.2.2"
ctx.TagName // "v1.2.3"
ctx.ReleaseType // "minor"
// Repository info
ctx.RepositoryOwner // "relicta-tech"
ctx.RepositoryName // "relicta"
ctx.Branch // "main"
ctx.CommitSHA // "abc123..."
// Generated content
ctx.Changelog // Full changelog markdown
ctx.ReleaseNotes // Public release notes
// Categorized changes
ctx.Changes.Features // []ConventionalCommit
ctx.Changes.Fixes
ctx.Changes.Breaking
// Filtered environment
ctx.Environment // Safe env vars
}
Dry Run Mode
Plugins should respect dry-run mode:
func (p *MyPlugin) Execute(ctx context.Context, req plugin.ExecuteRequest) (*plugin.ExecuteResponse, error) {
if req.DryRun {
return &plugin.ExecuteResponse{
Success: true,
Message: "Would create release (dry-run)",
}, nil
}
// Actual implementation...
}
Artifacts
Plugins can report created artifacts:
return &plugin.ExecuteResponse{
Success: true,
Artifacts: []plugin.Artifact{
{
Name: "release.tar.gz",
Path: "https://github.com/.../release.tar.gz",
Type: "url",
Size: 1024000,
Checksum: "sha256:abc123...",
},
},
}, nil
Testing
Use ServeTest for integration testing:
func TestMyPlugin(t *testing.T) {
reattach, cleanup := plugin.ServeTest(&MyPlugin{})
defer cleanup()
// Create client and test...
}
Examples
See the examples directory for complete plugin implementations:
- hello - Minimal example plugin
Protocol Version
The SDK uses protocol version 1. Plugins built with incompatible protocol versions will fail to load.
import "github.com/relicta-tech/relicta-plugin-sdk/plugin"
fmt.Println(plugin.ProtocolVersion) // 1
fmt.Println(plugin.Version) // "1.0.0"
License
MIT License - see LICENSE for details.