Repository navigation
feat: v0.18.6 — infra state persistence + remote backend loader + destroy/status/drift direct paths + bootstrap URL export - #471
Conversation
…tate loss Refactors applyWithProvider → applyWithProviderAndStore(ctx, provider, providerType, specs, current, store). After provider.Apply returns success, each ResourceOutput is written to the store; save failures log a loud warning but do not abort the apply. Delete actions trigger store.DeleteResource after the Apply call. Also introduces resolveStateStore(cfgFile) which reads the iac.state module config and instantiates the correct backend (filesystem or Spaces), rewriting loadCurrentState to delegate to it so that remote backends are supported beyond the previous filesystem-only path. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…odules Adds destroyInfraModules, statusInfraModules, and driftInfraModules that load state from the configured backend, group records by IaCProvider, and dispatch to provider.Destroy/Status/DetectDrift respectively. runInfraDestroy, runInfraStatus, and runInfraDrift now dispatch to the direct path when hasInfraModules() is true, falling back to the legacy pipeline path for platform.* configs. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
After bootstrapStateBackend creates or confirms a DO Spaces bucket, prints 'export DO_SPACES_BUCKET=<name>' to stdout for CI capture via $GITHUB_ENV, and calls writeBucketBackToConfig to bake the resolved bucket name into the on-disk YAML so downstream wfctl commands can read state without the env var being set again. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…atus/drift - Extract provider-loop bodies into closures (applyGroup, destroyGroup, statusGroup, driftGroup) to fix deferInLoop in all four files - Switch range-value loops to index loops for rangeValCopy (plan.Actions 136B, ResourceState 208B) in infra_apply, infra_destroy, infra_status_drift - Split combined err+len guard in resolveStateStore to satisfy nilerr; add //nolint:nilerr on the intentional err-swallow (noop on missing config) Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
⏱ Benchmark Results✅ No significant performance regressions detected. benchstat comparison (baseline → PR)
|
| } | ||
|
|
||
| return bootstrapDOSpacesBucketFn(ctx, bucket, region, accessKey, secretKey) | ||
| if err := bootstrapDOSpacesBucketFn(ctx, bucket, region, accessKey, secretKey); err != nil { |
There was a problem hiding this comment.
Why is there DO-specific bootstrapping here? This should be a hook system or similar, and the relevant provider bootstrap should load from that hook. DO logic should not be built into wfctl, same for AWS, Azure, etc, as I believe we have a provider plugin pattern for each, right?
There was a problem hiding this comment.
Agreed — this is an architectural leak. Refactoring now so bootstrap dispatches through the IaCProvider plugin interface (new method BootstrapStateBackend), with DO Spaces logic moving from wfctl core into workflow-plugin-digitalocean. wfctl will only be responsible for resolving the provider + delegating. See fix commits inbound.
There was a problem hiding this comment.
Fixed in 2d407fe. BootstrapStateBackend(ctx, cfg) (*BootstrapResult, error) is now a method on the IaCProvider interface — every cloud provider plugin implements it. All DO-specific S3/AWS SDK code has been removed from wfctl core; bootstrapStateBackend simply finds the first iac.provider module, calls resolveIaCProvider, and delegates. Self-contained backends (filesystem/memory/postgres) remain no-ops in wfctl. DO Spaces implementation will follow in workflow-plugin-digitalocean v0.7.4 (follow-up #31).
…re + follow-up TODOs bootstrapStateBackend now dispatches by iac.state.backend: - spaces → bootstrapStateBackendSpaces (DO endpoint URL constructed here) - s3/gcs/azure → clear not-yet-implemented errors with contribution guidance - filesystem/memory/postgres/"" → no-op bootstrapStateBackendSpaces: exports WFCTL_STATE_BUCKET (generic) + SPACES_BUCKET (backend-specific, no cloud-provider prefix). bootstrapDOSpacesBucket now accepts caller-supplied endpoint; creds error message genericised; log message updated to "created spaces state bucket". resolveStateStore: wires module.PostgresIaCStateStore for backend=postgres; s3/gcs/azure return clear errors instead of silent noop. Misc: - TODO(#28) added to deploy.go hardcoded credential checks - TODO(#29) added to providerCredentialSubKeys map - infra import --provider help text no longer lists specific providers - All bootstrapDOSpacesBucketFn test fakes updated for new endpoint param - CHANGELOG updated Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Move all cloud-provider-specific bucket-creation logic out of wfctl core and behind the IaCProvider plugin interface: - interfaces/iac_provider.go: add BootstrapStateBackend(ctx, cfg) (*BootstrapResult, error) to IaCProvider; add BootstrapResult struct (Bucket, Region, Endpoint, EnvVars) - infra_bootstrap.go: remove all DO/AWS SDK imports and S3-specific code; bootstrapStateBackend now finds the first iac.provider module, calls resolveIaCProvider, then delegates to provider.BootstrapStateBackend; self-contained backends (filesystem/memory/postgres/"") remain no-ops - deploy_providers.go: wire BootstrapStateBackend on remoteIaCProvider via InvokeService + anyToStruct, consistent with rest of bridge - All IaCProvider test stubs: add BootstrapStateBackend(nil, nil) stub - Delete infra_bootstrap_bucket_test.go: DO-specific S3 tests move to DO plugin (follow-up #31, workflow-plugin-digitalocean v0.7.4) - Rewrite infra_bootstrap_env_test.go + infra_bootstrap_url_export_test.go: replace bootstrapDOSpacesBucketFn overrides with resolveIaCProvider overrides using envBootstrapProvider / urlBootstrapProvider fakes - Add infra_bootstrap_provider_test.go: TestBootstrapStateBackend_DispatchesToProvider (full round-trip), SelfContainedBackendsSkipped, NoProviderModuleErrors Addresses review comment #3132352899 (intel352): DO-specific bootstrap should not live in wfctl core. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Summary
applyWithProviderAndStoresavesResourceStatefor each successfully provisioned resource; delete-action resources are removed from state. Previously state was never written, causing every run to re-create.resolveStateStoresupports filesystem and DO Spaces backends.loadCurrentStatenow delegates to it, enabling Spaces-backed state to be read by plan/apply.runInfraDestroydispatches todestroyInfraModules(loads state, callsprovider.Destroy, removes state records) instead of requiring a non-existentpipelines.destroypipeline.runInfraStatusandrunInfraDrift, callingprovider.Status/provider.DetectDriftdirectly on tracked records.export DO_SPACES_BUCKET=<name>for CI capture and writes the resolved name back to the on-disk config.Test plan
GOWORK=off go test -race -short ./cmd/wfctl/...— all passGOWORK=off go build ./...— cleango vet ./cmd/wfctl/...— cleangofmt -l ./cmd/wfctl/— no output🤖 Generated with Claude Code