Conversation
…ession) wfctl bootstrap uses `provider: digitalocean` as the discriminator to identify which iac.provider module owns a given backend (e.g. `backend: spaces` for IaC state). v0.9.0's strict-contracts proto (PR #41) didn't declare `provider`, so configs carrying the canonical discriminator (BMW, core-dump, every existing iac.provider config) failed typed-config marshal with `protobuf error` because wfctl uses `protojson UnmarshalOptions{DiscardUnknown: false}` (workflow plugin/external/convert.go:65) which rejects unknown fields. The plugin itself doesn't use this field — manifest.iacProvider.name = digitalocean is the source of truth for provider identity — but the proto must accept it so strict-contracts unmarshal doesn't reject the config. Surfaced by core-dump's first deploy attempt against v0.9.0 (run 25277359916: `error: no iac.provider module found in config — add an iac.provider module to bootstrap remote state backends (backend="spaces")` after temporarily removing `provider:` to bypass the protobuf-error rejection). Bumps to v0.9.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes a v0.9.0 strict-contract regression where wfctl rejects IaC provider configs containing the canonical provider: digitalocean discriminator due to an undeclared proto field.
Changes:
- Adds
providertoIacProviderConfiginproto/digitalocean.prototo allow strict protojson unmarshalling. - Regenerates
proto/digitalocean.pb.goto include the new field accessor and descriptor updates. - Documents the fix in
CHANGELOG.mdunder Unreleased.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| proto/digitalocean.proto | Declares provider field on IacProviderConfig to accept wfctl’s discriminator field under strict contracts. |
| proto/digitalocean.pb.go | Regenerated Go bindings reflecting the new proto field. |
| CHANGELOG.md | Adds an Unreleased entry describing the regression and fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
1
to
5
| // Code generated by protoc-gen-go. DO NOT EDIT. | ||
| // versions: | ||
| // protoc-gen-go v1.36.11 | ||
| // protoc v3.21.12 | ||
| // protoc v7.34.1 | ||
| // source: proto/digitalocean.proto |
Comment on lines
+25
to
+35
| // provider is the wfctl bootstrap discriminator (e.g. "digitalocean") | ||
| // used to identify which iac.provider module owns a given backend | ||
| // (per cmd/wfctl/infra_bootstrap.go's lookup for backend="spaces" etc.). | ||
| // The plugin itself does not need this field — manifest.iacProvider.name | ||
| // already declares ownership — but the strict-contracts proto must | ||
| // accept it because protojson UnmarshalOptions{DiscardUnknown: false} | ||
| // (workflow plugin/external/convert.go:65) errors on unknown fields. | ||
| // Without this declaration, configs with `provider: digitalocean` | ||
| // (the canonical pattern across BMW, core-dump, etc.) fail v0.9.0+ | ||
| // strict-contract typed-config marshal. | ||
| string provider = 5; |
Comment on lines
+15
to
+19
| the bottom-most error, hiding the field name). Added `string provider | ||
| = 5` to the proto; the field is accepted but ignored by the plugin | ||
| itself (manifest.iacProvider.name = digitalocean is the source of | ||
| truth for provider identity). Surfaced by core-dump's first deploy | ||
| attempt against v0.9.0. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
v0.9.0 strict-contracts (PR #41) didn't declare a
providerfield onIacProviderConfig. wfctl usesprotojson UnmarshalOptions{DiscardUnknown: false}(plugin/external/convert.go:65) which rejects unknown fields, so configs carrying the canonicalprovider: digitaloceandiscriminator (BMW, core-dump, every existing iac.provider config) fail typed-config marshal.The error surfaces as
protobuf errorbecause wfctl'scmd/wfctl/main.go:206-209unwraps to the bottom-most error (google.golang.org/protobuf/internal/errors.Error = "protobuf error"), stripping all wrapping context. This required hours of source-diving to identify.Real-world impact
wfctl infra applyagainst any v0.9.0+ DO plugin withprovider:in the config fails at:Removing
provider:breaks bootstrap differently:Both failures observed in core-dump deploy runs
25276880916(withprovider:) and25277359916(without).Fix
Add
string provider = 5;toIacProviderConfigproto. The plugin itself doesn't use the field —manifest.iacProvider.name = digitaloceanis the source of truth for provider identity — but the proto must accept it so strict-contracts unmarshal doesn't reject configs with the canonical discriminator.Regenerated
proto/digitalocean.pb.goviaprotoc --go_out=..Bumps to v0.9.1.
Suggested follow-ups (separate PRs / repos)
cmd/wfctl/main.go:206-209unwraps to root, hiding the proto field name. Print the full chain or just the immediate wrapper.provider, etc.) that get stripped from typed-config payloads before marshal, so plugin protos don't have to enumerate them.Test plan
🤖 Generated with Claude Code