Skip to content

fix(wfctl): load plugin state backends#709

Merged
intel352 merged 3 commits into
mainfrom
fix/iac-state-plugin-backend
May 18, 2026
Merged

fix(wfctl): load plugin state backends#709
intel352 merged 3 commits into
mainfrom
fix/iac-state-plugin-backend

Conversation

@intel352
Copy link
Copy Markdown
Contributor

Summary

  • resolves plugin-served iac.state backends (spaces, s3, gcs) through installed plugins for wfctl direct infra commands
  • adapts plugin IaCStateBackend clients to the direct-path infraStateStore interface
  • exports the existing gRPC state-store constructor for wfctl use

Verification

  • GOWORK=off go test ./cmd/wfctl -run 'TestResolveStateStore|TestInfraPlanAcceptsPluginDirFlag|TestDefaultResolveIaCProvider'
  • GOWORK=off go test ./cmd/wfctl ./module ./plugin/external
  • GOWORK=off go test ./cmd/wfctl -run TestResolveStateStore_SpacesUsesPluginLoader -count=1
  • git diff --check

Context

BMW deploy proved that Workflow v0.57.2 still hard-errors for iac.state backend: spaces even when workflow-plugin-digitalocean v1.1.0 is installed. This patch makes the direct-path infra commands load the plugin state backend instead of returning the legacy in-tree backend error.

Copilot AI review requested due to automatic review settings May 18, 2026 15:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables wfctl direct-path infra commands (apply/destroy/status/drift/refresh) to resolve plugin-served iac.state backends (spaces, s3, gcs) instead of returning a hard-coded "use plugin" error. Loads the appropriate external plugin from the configured plugin directory, retrieves the gRPC IaCStateBackend client, and adapts it to the CLI's infraStateStore interface.

Changes:

  • Replace the legacy hard-error returns for spaces/s3/gcs in resolveStateStore with a new resolvePluginStateStore helper that loads candidate plugins and configures the backend.
  • Add a pluginWfctlStateStore adapter that bridges module.IaCStateStore (ListStates/SaveState/DeleteState) to the wfctl infraStateStore interface (ListResources/SaveResource/DeleteResource).
  • Export module.NewGRPCIaCStateStore so wfctl can construct a state store around an IaCStateBackendClient without building a full engine.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
module/iac_state_grpc_client.go Adds an exported constructor wrapper around the internal newGRPCIaCStateStore for use by wfctl.
cmd/wfctl/infra_state_store.go Routes spaces/s3/gcs backends through a new plugin loader; adds adapter struct and candidate-discovery helper.
cmd/wfctl/infra_state_store_test.go Adds a test verifying the spaces backend goes through the plugin loader and no longer returns the legacy hard-coded error.
Comments suppressed due to low confidence (1)

cmd/wfctl/infra_state_store.go:224

  • For non-preferred plugin candidates this loop spawns each plugin as a subprocess just to ask whether it advertises the requested iac.state backend. In a typical install with many plugins (auth, http, broker, observability, etc.) this means starting and handshaking with every plugin in data/plugins on every resolveStateStore call — and resolveStateStore is invoked multiple times per command. Consider consulting each plugin's plugin.json manifest (IaCStateBackends) to filter candidates before invoking LoadPlugin, so we only spawn the plugin(s) that actually declare the backend.
func stateBackendPluginCandidates(backend string, entries []os.DirEntry) []string {
	seen := map[string]struct{}{}
	var candidates []string
	hasDir := func(name string) bool {
		for _, entry := range entries {
			if entry.IsDir() && entry.Name() == name {
				return true
			}
		}
		return false
	}
	add := func(name string) {
		if strings.TrimSpace(name) == "" {
			return
		}
		if _, ok := seen[name]; ok {
			return
		}
		seen[name] = struct{}{}
		candidates = append(candidates, name)
	}
	switch backend {
	case "spaces":
		if hasDir("digitalocean") {
			add("digitalocean")
		}
	case "s3":
		if hasDir("aws") {
			add("aws")
		}
	case "gcs":
		if hasDir("gcp") {
			add("gcp")
		}
	}
	for _, entry := range entries {
		if entry.IsDir() {
			add(entry.Name())
		}
	}
	return candidates
}

Comment on lines +156 to +177
for _, pluginName := range stateBackendPluginCandidates(backend, entries) {
adapter, loadErr := mgr.LoadPlugin(pluginName)
if loadErr != nil {
mgr.Shutdown()
return nil, fmt.Errorf("load plugin %q for iac.state backend %q: %w", pluginName, backend, loadErr)
}
clients, clientsErr := adapter.IaCStateBackendClients()
if clientsErr != nil {
mgr.Shutdown()
return nil, fmt.Errorf("plugin %q iac.state backends: %w", pluginName, clientsErr)
}
client, ok := clients[backend]
if !ok {
continue
}
store := module.NewGRPCIaCStateStore(client)
if err := store.Configure(ctx, backend, cfg); err != nil {
mgr.Shutdown()
return nil, fmt.Errorf("configure plugin-served iac.state backend %q via plugin %q: %w", backend, pluginName, err)
}
return &pluginWfctlStateStore{inner: store, mgr: mgr}, nil
}
Comment on lines +133 to +139
func (s *pluginWfctlStateStore) Close() error {
if s.mgr == nil {
return nil
}
s.mgr.Shutdown()
return nil
}
@codecov
Copy link
Copy Markdown

codecov Bot commented May 18, 2026

Codecov Report

❌ Patch coverage is 73.80952% with 22 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/wfctl/infra_state_store.go 73.17% 15 Missing and 7 partials ⚠️

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 18, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:274: parsing iteration count: invalid syntax
baseline-bench.txt:323303: parsing iteration count: invalid syntax
baseline-bench.txt:632901: parsing iteration count: invalid syntax
baseline-bench.txt:980323: parsing iteration count: invalid syntax
baseline-bench.txt:1264775: parsing iteration count: invalid syntax
baseline-bench.txt:1561104: parsing iteration count: invalid syntax
benchmark-results.txt:274: parsing iteration count: invalid syntax
benchmark-results.txt:316022: parsing iteration count: invalid syntax
benchmark-results.txt:650328: parsing iteration count: invalid syntax
benchmark-results.txt:1169890: parsing iteration count: invalid syntax
benchmark-results.txt:1484402: parsing iteration count: invalid syntax
benchmark-results.txt:1808436: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 7763 64-Core Processor                
                            │ baseline-bench.txt │       benchmark-results.txt        │
                            │       sec/op       │    sec/op     vs base              │
InterpreterCreation-4               6.655m ± 58%   7.940m ± 61%       ~ (p=0.589 n=6)
ComponentLoad-4                     3.400m ±  2%   3.590m ±  7%  +5.61% (p=0.002 n=6)
ComponentExecute-4                  1.831µ ±  1%   1.925µ ±  2%  +5.16% (p=0.002 n=6)
PoolContention/workers-1-4          1.036µ ±  1%   1.082µ ±  3%  +4.44% (p=0.002 n=6)
PoolContention/workers-2-4          1.037µ ±  1%   1.087µ ±  1%  +4.77% (p=0.002 n=6)
PoolContention/workers-4-4          1.033µ ±  2%   1.080µ ±  1%  +4.55% (p=0.002 n=6)
PoolContention/workers-8-4          1.037µ ±  2%   1.085µ ±  1%  +4.68% (p=0.002 n=6)
PoolContention/workers-16-4         1.036µ ±  1%   1.084µ ±  1%  +4.68% (p=0.002 n=6)
ComponentLifecycle-4                3.419m ±  1%   3.619m ±  1%  +5.83% (p=0.002 n=6)
SourceValidation-4                  2.199µ ±  3%   2.302µ ±  0%  +4.68% (p=0.002 n=6)
RegistryConcurrent-4                741.4n ±  2%   777.7n ±  2%  +4.88% (p=0.002 n=6)
LoaderLoadFromString-4              3.395m ±  2%   3.634m ±  0%  +7.01% (p=0.002 n=6)
geomean                             17.68µ         18.78µ        +6.23%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               2.027Mi ± 0%   2.027Mi ± 0%       ~ (p=0.790 n=6)
ComponentLoad-4                     2.180Mi ± 0%   2.180Mi ± 0%       ~ (p=0.848 n=6)
ComponentExecute-4                  1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4         1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                2.183Mi ± 0%   2.183Mi ± 0%       ~ (p=0.290 n=6)
SourceValidation-4                  1.984Ki ± 0%   1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                1.133Ki ± 0%   1.133Ki ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4              2.182Mi ± 0%   2.182Mi ± 0%       ~ (p=0.853 n=6)
geomean                             15.25Ki        15.25Ki       +0.00%
¹ all samples are equal

                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │     allocs/op      │  allocs/op   vs base                │
InterpreterCreation-4                15.68k ± 0%   15.68k ± 0%       ~ (p=1.000 n=6)
ComponentLoad-4                      18.02k ± 0%   18.02k ± 0%       ~ (p=1.000 n=6)
ComponentExecute-4                    25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4           25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                 18.07k ± 0%   18.07k ± 0%       ~ (p=1.000 n=6) ¹
SourceValidation-4                    32.00 ± 0%    32.00 ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                  2.000 ± 0%    2.000 ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4               18.06k ± 0%   18.06k ± 0%       ~ (p=1.000 n=6) ¹
geomean                               183.3         183.3       +0.00%
¹ all samples are equal

pkg: github.com/GoCodeAlone/workflow/middleware
                                  │ baseline-bench.txt │       benchmark-results.txt       │
                                  │       sec/op       │   sec/op     vs base              │
CircuitBreakerDetection-4                 279.4n ± 12%   285.8n ± 6%       ~ (p=0.368 n=6)
CircuitBreakerExecution_Success-4         20.34n ±  1%   21.45n ± 0%  +5.41% (p=0.002 n=6)
CircuitBreakerExecution_Failure-4         63.04n ±  2%   66.30n ± 0%  +5.17% (p=0.002 n=6)
geomean                                   71.02n         74.07n       +4.29%

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │        B/op        │    B/op     vs base                │
CircuitBreakerDetection-4                 144.0 ± 0%     144.0 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │     allocs/op      │ allocs/op   vs base                │
CircuitBreakerDetection-4                 1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/module
                                 │ baseline-bench.txt │       benchmark-results.txt        │
                                 │       sec/op       │    sec/op     vs base              │
IaCStateBackend_InProcess-4              310.9n ± 28%   319.9n ±  2%       ~ (p=0.065 n=6)
IaCStateBackend_GRPC-4                   8.961m ± 13%   9.442m ± 21%  +5.36% (p=0.041 n=6)
JQTransform_Simple-4                     619.2n ± 36%   652.5n ± 40%       ~ (p=0.180 n=6)
JQTransform_ObjectConstruction-4         1.442µ ±  2%   1.513µ ±  0%  +4.92% (p=0.002 n=6)
JQTransform_ArraySelect-4                3.340µ ±  2%   3.446µ ±  1%  +3.16% (p=0.002 n=6)
JQTransform_Complex-4                    37.88µ ±  3%   39.12µ ±  0%  +3.29% (p=0.002 n=6)
JQTransform_Throughput-4                 1.764µ ±  2%   1.843µ ±  1%  +4.51% (p=0.002 n=6)
SSEPublishDelivery-4                     60.41n ±  1%   63.76n ±  0%  +5.54% (p=0.002 n=6)
geomean                                  3.679µ         3.840µ        +4.38%

                                 │ baseline-bench.txt │         benchmark-results.txt         │
                                 │        B/op        │     B/op       vs base                │
IaCStateBackend_InProcess-4             416.0 ±  0%       416.0 ±  0%       ~ (p=1.000 n=6) ¹
IaCStateBackend_GRPC-4                5.899Mi ± 10%     5.832Mi ± 10%       ~ (p=1.000 n=6)
JQTransform_Simple-4                  1.273Ki ±  0%     1.273Ki ±  0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4      1.773Ki ±  0%     1.773Ki ±  0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4             2.625Ki ±  0%     2.625Ki ±  0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                 16.22Ki ±  0%     16.22Ki ±  0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4              1.984Ki ±  0%     1.984Ki ±  0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                    0.000 ±  0%       0.000 ±  0%       ~ (p=1.000 n=6) ¹
geomean                                             ²                  -0.14%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                 │ baseline-bench.txt │        benchmark-results.txt        │
                                 │     allocs/op      │  allocs/op   vs base                │
IaCStateBackend_InProcess-4              2.000 ± 0%      2.000 ± 0%       ~ (p=1.000 n=6) ¹
IaCStateBackend_GRPC-4                  6.835k ± 0%     6.834k ± 0%       ~ (p=0.569 n=6)
JQTransform_Simple-4                     10.00 ± 0%      10.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4         15.00 ± 0%      15.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4                30.00 ± 0%      30.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                    324.0 ± 0%      324.0 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4                 17.00 ± 0%      17.00 ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²                -0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/schema
                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │       sec/op       │    sec/op     vs base              │
SchemaValidation_Simple-4                   1.078µ ± 18%   1.098µ ± 13%       ~ (p=0.240 n=6)
SchemaValidation_AllFields-4                1.616µ ±  3%   1.656µ ±  3%  +2.44% (p=0.041 n=6)
SchemaValidation_FormatValidation-4         1.546µ ±  3%   1.584µ ±  1%  +2.39% (p=0.011 n=6)
SchemaValidation_ManySchemas-4              1.742µ ±  5%   1.800µ ±  3%  +3.39% (p=0.026 n=6)
geomean                                     1.472µ         1.509µ        +2.52%

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │        B/op        │    B/op     vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │     allocs/op      │ allocs/op   vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/store
                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │       sec/op       │    sec/op     vs base               │
EventStoreAppend_InMemory-4                1.091µ ± 17%   1.178µ ± 22%   +7.98% (p=0.041 n=6)
EventStoreAppend_SQLite-4                  1.096m ±  2%   1.433m ±  6%  +30.76% (p=0.002 n=6)
GetTimeline_InMemory/events-10-4           12.88µ ±  4%   13.47µ ±  4%   +4.60% (p=0.015 n=6)
GetTimeline_InMemory/events-50-4           66.16µ ± 14%   75.30µ ±  3%  +13.81% (p=0.002 n=6)
GetTimeline_InMemory/events-100-4          115.2µ ±  1%   152.6µ ±  2%  +32.42% (p=0.002 n=6)
GetTimeline_InMemory/events-500-4          596.4µ ±  2%   625.9µ ± 21%   +4.94% (p=0.002 n=6)
GetTimeline_InMemory/events-1000-4         1.217m ±  3%   1.272m ±  1%   +4.52% (p=0.002 n=6)
GetTimeline_SQLite/events-10-4             101.8µ ±  3%   105.9µ ±  1%   +4.04% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             233.5µ ±  1%   248.5µ ±  1%   +6.44% (p=0.002 n=6)
GetTimeline_SQLite/events-100-4            393.4µ ±  3%   423.5µ ±  1%   +7.65% (p=0.002 n=6)
GetTimeline_SQLite/events-500-4            1.687m ±  1%   1.792m ±  1%   +6.27% (p=0.002 n=6)
GetTimeline_SQLite/events-1000-4           3.345m ±  3%   3.493m ±  2%   +4.43% (p=0.002 n=6)
geomean                                    201.5µ         222.2µ        +10.26%

                                   │ baseline-bench.txt │        benchmark-results.txt         │
                                   │        B/op        │     B/op      vs base                │
EventStoreAppend_InMemory-4                 780.0 ± 11%     811.0 ± 6%       ~ (p=0.394 n=6)
EventStoreAppend_SQLite-4                 1.986Ki ±  1%   1.983Ki ± 3%       ~ (p=0.732 n=6)
GetTimeline_InMemory/events-10-4          7.953Ki ±  0%   7.953Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4          46.62Ki ±  0%   46.62Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4         94.48Ki ±  0%   94.48Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4         472.8Ki ±  0%   472.8Ki ± 0%       ~ (p=0.545 n=6)
GetTimeline_InMemory/events-1000-4        944.3Ki ±  0%   944.3Ki ± 0%       ~ (p=0.197 n=6)
GetTimeline_SQLite/events-10-4            16.74Ki ±  0%   16.74Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4            87.14Ki ±  0%   87.14Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4           175.4Ki ±  0%   175.4Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4           846.1Ki ±  0%   846.1Ki ± 0%       ~ (p=1.000 n=6)
GetTimeline_SQLite/events-1000-4          1.639Mi ±  0%   1.639Mi ± 0%       ~ (p=0.864 n=6)
geomean                                   67.28Ki         67.49Ki       +0.31%
¹ all samples are equal

                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │     allocs/op      │  allocs/op   vs base                │
EventStoreAppend_InMemory-4                  7.000 ± 0%    7.000 ± 0%       ~ (p=1.000 n=6) ¹
EventStoreAppend_SQLite-4                    53.00 ± 0%    53.00 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-10-4             125.0 ± 0%    125.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4             653.0 ± 0%    653.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4           1.306k ± 0%   1.306k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4           6.514k ± 0%   6.514k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-1000-4          13.02k ± 0%   13.02k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-10-4               382.0 ± 0%    382.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4              1.852k ± 0%   1.852k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4             3.681k ± 0%   3.681k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4             18.54k ± 0%   18.54k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-1000-4            37.29k ± 0%   37.29k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                     1.162k        1.162k       +0.00%
¹ all samples are equal

Benchmarks run with go test -bench=. -benchmem -count=6.
Regressions ≥ 20% are flagged. Results compared via benchstat.

Copilot AI review requested due to automatic review settings May 18, 2026 15:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

Comment on lines +134 to +140
func (s *pluginWfctlStateStore) Close() error {
if s.mgr == nil {
return nil
}
s.mgr.Shutdown()
return nil
}
Comment on lines +157 to +173
for _, pluginName := range stateBackendPluginCandidates(backend, entries) {
clients, clientsErr := loadPluginStateBackendClients(mgr, pluginName, backend)
if clientsErr != nil {
mgr.Shutdown()
return nil, clientsErr
}
client, ok := clients[backend]
if !ok {
continue
}
store := module.NewGRPCIaCStateStore(client)
if err := store.Configure(ctx, backend, cfg); err != nil {
mgr.Shutdown()
return nil, fmt.Errorf("configure plugin-served iac.state backend %q via plugin %q: %w", backend, pluginName, err)
}
return &pluginWfctlStateStore{inner: store, mgr: mgr}, nil
}
Comment on lines +156 to +177
mgr := external.NewExternalPluginManager(pluginDir, nil)
for _, pluginName := range stateBackendPluginCandidates(backend, entries) {
clients, clientsErr := loadPluginStateBackendClients(mgr, pluginName, backend)
if clientsErr != nil {
mgr.Shutdown()
return nil, clientsErr
}
client, ok := clients[backend]
if !ok {
continue
}
store := module.NewGRPCIaCStateStore(client)
if err := store.Configure(ctx, backend, cfg); err != nil {
mgr.Shutdown()
return nil, fmt.Errorf("configure plugin-served iac.state backend %q via plugin %q: %w", backend, pluginName, err)
}
return &pluginWfctlStateStore{inner: store, mgr: mgr}, nil
}

mgr.Shutdown()
return nil, fmt.Errorf("iac.state backend %q is plugin-served but no installed plugin in %s advertises it", backend, pluginDir)
}
Comment on lines 90 to +97
case "spaces":
return nil, fmt.Errorf("iac.state backend %q is now plugin-served by workflow-plugin-digitalocean v1.1.0; "+
"install and load the plugin to use the Spaces backend (wfctl direct-path commands no longer support in-tree spaces)", backend)
return resolvePluginStateStore(context.Background(), backend, cfg)

case "s3":
return nil, fmt.Errorf("iac.state backend %q is now plugin-served by workflow-plugin-aws v1.1.0; "+
"install and load the plugin to use the S3 backend (wfctl direct-path commands no longer support in-tree s3)", backend)
return resolvePluginStateStore(context.Background(), backend, cfg)

case "gcs":
return nil, fmt.Errorf("iac.state backend %q is now plugin-served by workflow-plugin-gcp v1.1.0; "+
"install and load the plugin to use the GCS backend (wfctl direct-path commands no longer support in-tree gcs)", backend)
return resolvePluginStateStore(context.Background(), backend, cfg)
// NewGRPCIaCStateStore wraps an IaCStateBackendClient as an IaCStateStore.
// It is exported for wfctl direct-path commands, which load plugin-served
// state backends without constructing a full engine.
func NewGRPCIaCStateStore(c pb.IaCStateBackendClient) *grpcIaCStateStore {
@intel352 intel352 merged commit 5bcb4df into main May 18, 2026
28 checks passed
@intel352 intel352 deleted the fix/iac-state-plugin-backend branch May 18, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants