plugins.hooks.<plugin>@<version> now supports any json value/structure and nesting
this change also ensures hook plugin options are not mixed up if two plugins use the same key
changes to hook plugin Init function
Hook.Init function was removed from the interface and is checked at runtime through reflection to support variable arguments
the hooks.Server.Init callback validates the Hook.Init function of the plugin and parse the plugins.hooks.<plugin>@<version> json to the type of the first argument of HookImpl.Init
the signature of HookImpl.Init must be func (*<HookImpl>) Init(<any structure>) error or hooks.Server.Init will throw an error
the HookImpl.Init function accepts map[string]string, thus no changes to existing plugin code is required (plugin must be compiled with newer semantic-release tho; see drawbacks for details)
type MyHook struct {}
type MyHookOpts struct {
List []string
}
func (mh *MyHook) Init(opts MyHookOpts) error {
log.Printf("list: %v", opts.List)
return nil
}
// ... other function implementations of the Hook interface
drawbacks
This solution comes with some drawbacks I couldn't solve right now:
the Init function was removed from the Hooks interface, thus compiling plugins without or with invalid Init function will succeed
this is necessary if the options should be parsed automatically since the first argument to Init needs to accept different types
an alternative would be to use Init(interface{}) and let the plugin author handle the conversion/decoding of the options to its appropriate type
hook plugins compiled with the old Hook interface will stop work due to changes to the hooks.Server function. They won't produce errors but silently do nothing. Thus, every plugin has to be compiled against this change.
this could lead to problems with cached hook plugin installations (only if plugins aren't auto-updated which I dont know semantic-release does atm).
this also means custom hook plugins which are not maintained anymore will stop working
above issues may be preventable by introducing a breaking change version for semantic-release
to do
As this is a draft and I'm unsure if it gets picked up at the moment, there are a few things I will implement after a positive response:
[ ] add compatibility for older config structure (maybe useful if end users are unable or too lazy to change the config)
[ ] (optionally) revert to Init(interface{}) if the missing interface function is a huge problem/no-go
[ ] (optionally: if #162 is rejected) merge common config with the hook plugin config
This PR adds support for complex hook plugin configuration as requested in #163.
implementation
As this change is a little more advanced I'll describe the important details below.
changes to config structure
In order to support complex hook plugin configuration the config structure had to be changed a little:
plugins.hooks.<plugin>@<version>
now supports any json value/structure and nestingchanges to hook plugin
Init
functionHook.Init
function was removed from the interface and is checked at runtime through reflection to support variable argumentshooks.Server.Init
callback validates theHook.Init
function of the plugin and parse theplugins.hooks.<plugin>@<version>
json to the type of the first argument ofHookImpl.Init
HookImpl.Init
must befunc (*<HookImpl>) Init(<any structure>) error
orhooks.Server.Init
will throw an errorHookImpl.Init
function acceptsmap[string]string
, thus no changes to existing plugin code is required (plugin must be compiled with newer semantic-release tho; see drawbacks for details)drawbacks
This solution comes with some drawbacks I couldn't solve right now:
Init
function was removed from theHooks
interface, thus compiling plugins without or with invalidInit
function will succeedInit
needs to accept different typesInit(interface{})
and let the plugin author handle the conversion/decoding of the options to its appropriate typeHook
interface will stop work due to changes to thehooks.Server
function. They won't produce errors but silently do nothing. Thus, every plugin has to be compiled against this change.to do
As this is a draft and I'm unsure if it gets picked up at the moment, there are a few things I will implement after a positive response:
Init(interface{})
if the missing interface function is a huge problem/no-go