Plugin interface

Plugin defines the minimal interface that build plugins must implement.

Lifecycle hooks like PreBuild, Build and PostBuild are detected

automatically via reflection when present.

Methods:

Name


Returns:
  • string

ShouldRebuild


Parameters:
  • path string

Returns:
  • bool

Priority


Returns:
  • int

entry struct

Fields:

  • Plugin (Plugin)
  • cfg (json.RawMessage)

Info struct

Info reports the name and configuration of an active plugin.

Fields:

  • Name (string) - json:"name"
  • Config (json.RawMessage) - json:"config"

Active function

Active returns the list of configured plugins in execution order.

Returns:

  • []Info
Show/Hide Function Body
{
	out := make([]Info, len(active))
	for i, e := range active {
		out[i] = Info{Name: e.Plugin.Name(), Config: e.cfg}
	}
	return out
}

Register function

Register adds a plugin to the registry.

Parameters:

  • p Plugin

References:

Show/Hide Function Body
{ registry[p.Name()] = p }

Install function

Install builds and activates a single plugin by name.

Parameters:

  • name string
  • raw json.RawMessage

Returns:

  • error
Show/Hide Function Body
{
	if p, ok := registry[name]; ok {
		active = append(active, entry{Plugin: p, cfg: raw})
		return nil
	}
	return fmt.Errorf("plugin %s not found", name)
}

Configure function

Configure installs plugins listed in the provided configuration map.

Parameters:

  • cfg map[string]json.RawMessage

Returns:

  • error
Show/Hide Function Body
{
	active = active[:0]
	for name, raw := range cfg {
		if err := Install(name, raw); err != nil {
			return err
		}
	}
	sort.SliceStable(active, func(i, j int) bool {
		return active[i].Plugin.Priority() < active[j].Plugin.Priority()
	})
	return nil
}

invoke function

invoke executes the named lifecycle hook on the plugin if it exists.

Parameters:

  • e entry
  • name string

Returns:

  • error

References:

  • entry (cmd/rfw/plugins)
Show/Hide Function Body
{
	m := reflect.ValueOf(e.Plugin).MethodByName(name)
	if !m.IsValid() {
		return nil
	}
	typ := m.Type()
	if typ.NumIn() != 1 || typ.In(0) != reflect.TypeOf(json.RawMessage{}) || typ.NumOut() != 1 || typ.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
		return fmt.Errorf("%s has invalid signature for %s", e.Plugin.Name(), name)
	}
	out := m.Call([]reflect.Value{reflect.ValueOf(e.cfg)})
	if err, _ := out[0].Interface().(error); err != nil {
		return err
	}
	return nil
}

PreBuild function

Returns:

  • error
Show/Hide Function Body
{
	for _, e := range active {
		if err := invoke(e, "PreBuild"); err != nil {
			return fmt.Errorf("%s prebuild failed: %w", e.Plugin.Name(), err)
		}
	}
	return nil
}

Build function

Returns:

  • error
Show/Hide Function Body
{
	for _, e := range active {
		if err := invoke(e, "Build"); err != nil {
			return fmt.Errorf("%s build failed: %w", e.Plugin.Name(), err)
		}
	}
	return nil
}

PostBuild function

Returns:

  • error
Show/Hide Function Body
{
	for _, e := range active {
		if err := invoke(e, "PostBuild"); err != nil {
			return fmt.Errorf("%s postbuild failed: %w", e.Plugin.Name(), err)
		}
	}
	return nil
}

NeedsRebuild function

NeedsRebuild reports whether any active plugin requires a rebuild for the given path.

Parameters:

  • path string

Returns:

  • bool
Show/Hide Function Body
{
	for _, e := range active {
		if e.Plugin.ShouldRebuild(path) {
			return true
		}
	}
	return false
}

mockPlugin struct

mockPlugin is a simple implementation of the Plugin interface used for

testing the plugin registry and lifecycle.

Fields:

  • name (string)
  • priority (int)
  • rebuild (string)
  • pre (bool)
  • build (bool)
  • post (bool)

Methods:

Name


Returns:
  • string

Show/Hide Method Body
{ return m.name }

Priority


Returns:
  • int

Show/Hide Method Body
{ return m.priority }

ShouldRebuild


Parameters:
  • p string

Returns:
  • bool

Show/Hide Method Body
{ return p == m.rebuild }

PreBuild


Parameters:
  • json.RawMessage

Returns:
  • error

Show/Hide Method Body
{
	m.pre = true
	return nil
}

Build


Parameters:
  • json.RawMessage

Returns:
  • error

Show/Hide Method Body
{
	m.build = true
	return nil
}

PostBuild


Parameters:
  • json.RawMessage

Returns:
  • error

Show/Hide Method Body
{
	m.post = true
	return nil
}

TestLifecycle function

TestLifecycle verifies registration, ordering and lifecycle invocation of

plugins as well as the NeedsRebuild helper.

Parameters:

  • t *testing.T
Show/Hide Function Body
{
	// Reset global state.
	registry = map[string]Plugin{}
	active = nil

	p1 := &mockPlugin{name: "p1", priority: 1, rebuild: "a.go"}
	p0 := &mockPlugin{name: "p0", priority: 0}
	Register(p1)
	Register(p0)

	cfg := map[string]json.RawMessage{"p1": nil, "p0": nil}
	if err := Configure(cfg); err != nil {
		t.Fatalf("configure: %v", err)
	}
	if len(active) != 2 || active[0].Plugin != p0 || active[1].Plugin != p1 {
		t.Fatalf("plugins not sorted by priority: %#v", active)
	}

	if err := PreBuild(); err != nil {
		t.Fatalf("prebuild: %v", err)
	}
	if !p1.pre || !p0.pre {
		t.Fatalf("prebuild not invoked")
	}
	if err := Build(); err != nil {
		t.Fatalf("build: %v", err)
	}
	if !p1.build || !p0.build {
		t.Fatalf("build not invoked")
	}
	if err := PostBuild(); err != nil {
		t.Fatalf("postbuild: %v", err)
	}
	if !p1.post || !p0.post {
		t.Fatalf("postbuild not invoked")
	}

	if !NeedsRebuild("a.go") {
		t.Fatalf("expected rebuild for a.go")
	}
	if NeedsRebuild("b.go") {
		t.Fatalf("unexpected rebuild for b.go")
	}
}

TestActive function

Parameters:

  • t *testing.T
Show/Hide Function Body
{
	registry = map[string]Plugin{}
	active = nil

	p := &mockPlugin{name: "p", priority: 0}
	Register(p)

	cfg := map[string]json.RawMessage{"p": json.RawMessage("{\"x\":1}")}
	if err := Configure(cfg); err != nil {
		t.Fatalf("configure: %v", err)
	}
	list := Active()
	if len(list) != 1 || list[0].Name != "p" || string(list[0].Config) != "{\"x\":1}" {
		t.Fatalf("unexpected active list: %#v", list)
	}
}

encoding/json import

Import example:

import "encoding/json"

fmt import

Import example:

import "fmt"

reflect import

Import example:

import "reflect"

sort import

Import example:

import "sort"

encoding/json import

Import example:

import "encoding/json"

testing import

Import example:

import "testing"