Skip to content

feat: persist plugin raw_options blob in the config file#18

Closed
liamcervante wants to merge 4 commits into
mainfrom
plugin-raw-options
Closed

feat: persist plugin raw_options blob in the config file#18
liamcervante wants to merge 4 commits into
mainfrom
plugin-raw-options

Conversation

@liamcervante

@liamcervante liamcervante commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Wires the plugin raw_options blob (added in infracost/proto#81) through config-file generation and makes plugins.<name> the canonical config format — additive and backwards-compatible. Three commits.

Commit 1 — capture + backfill (self-contained)

  • Thread the plugin-authored raw_options blob through identification: capture it from IdentifyProjects (directory-level seed) and IdentifyEnvironments (per environment), thread the seed into IdentifyEnvironments, and carry the resolved blob on the intermediate autodetect project.
  • For plugins that don't yet emit a blob, backfill it from the data config derived itself (attributed var files + resolved workspace). Isolated in autodetect/rawoptions_backfill.go, marked for removal once every plugin emits raw_options.

Commit 2 — emit + merge

  • Add a generic, plugin-keyed plugins section to the config file (keyed by consuming plugin, e.g. terraform), emitted per project under plugins.<type> as a native YAML map.
  • Add repo-level plugins defaults that deep-merge into each project's blob during normalization: nested maps merge recursively; every other value (scalars and lists) is atomic, with per-project winning over the global default. Schema-agnostic — config never interprets the blob.

Commit 3 — make plugins.<name> canonical

  • Generation now emits parse options only under plugins.<type>; the deprecated terraform.* / aws.* fields are no longer written.
  • On read, any hand-authored deprecated fields (and the repo-level terraform.source_map) are folded into the plugins blob so old and hand-written configs still present the new style to consumers. Structured values win over the blob. Isolated in config_plugins_compat.go, marked for removal once the deprecated fields are gone.
  • The CLI and runner (updated to this config version) read plugins.<name> and are responsible for building the blob sent to the plugin.

Notes

  • No config version bump. CurrentVersion is already 0.3 and parsing is strict (KnownFields(true)); bumping would make older parsers reject the file outright, which is less backwards-compatible than adding plugins: additively within 0.3.
  • Proto dependency is a branch pseudo-version (from feat: add plugin raw_options blob and typed generic options proto#81). Repoint go.mod at a tagged proto release before merging.

Testing

  • go build ./... and go test ./... pass. plugins_blob_test.go covers backfilled emission, the global→per-project merge, and folding deprecated fields; the shared generate-test assertions now read var files from the plugins blob.

Thread the plugin-authored raw_options blob through identification: capture
it from IdentifyProjects (directory-level seed) and IdentifyEnvironments
(per environment), thread the seed into IdentifyEnvironments, and carry the
resolved blob on the intermediate autodetect project.

For plugins that don't yet emit a raw_options blob, backfill it from the
data config derived itself (attributed var files + resolved workspace) so
the persisted blob matches what the callers historically built. The
backfill is isolated in autodetect/rawoptions_backfill.go and marked for
removal once every plugin emits raw_options.

The blob is captured but not yet emitted to the config file; that follows
in a subsequent commit.

Bumps github.com/infracost/proto to the commit introducing raw_options.
@liamcervante
liamcervante force-pushed the plugin-raw-options branch 3 times, most recently from 9c5e9b9 to 3085f9a Compare July 10, 2026 09:06
…level defaults

Add a generic, plugin-keyed `plugins` section to the config file, keyed by
the consuming plugin name (e.g. "terraform"). autodetect now emits each
project's raw_options blob under plugins.<type>, stored as a native YAML map
so it stays readable and editable. The existing terraform.* fields are still
emitted alongside as backwards-compatible sugar for consumers that don't yet
read the blob.

Also add repo-level plugin defaults (top-level `plugins`) that deep-merge
into each project's matching blob during normalization: nested maps merge
recursively, lists concatenate, and per-project values win over the global
default. The merge is schema-agnostic - config never interprets the blob.

The field is additive within config version 0.3 (no version bump, which would
make older parsers reject the file outright).
Generation now emits parse options only under plugins.<type> - the deprecated
terraform.* / aws.* fields are no longer written. On read, any hand-authored
deprecated fields (and the repo-level terraform.source_map) are folded into the
plugins blob so old and hand-written configs still present the new style to
consumers. The CLI and runner (updated to this config version) read
plugins.<name> and are responsible for building the blob sent to the plugin.

Folding is isolated in config_plugins_compat.go, marked for removal once the
deprecated fields are gone.
…to plugins

Previously autodetect backfilled a whole {tfVarsFiles, workspace} blob for
terraform-family projects when the plugin didn't author one. That duplicated
option schema config shouldn't own: workspace (and anything else) is the
plugin's to author via IdentifyEnvironments/IdentifyProjects.

Narrow config's responsibility to the one thing only it can compute:
tfVarsFiles, which depends on the cross-directory (sibling/pibling) attribution
that lives in config's tree passes. Instead of synthesising and replacing the
blob, sideloadTFVarsFiles now merges tfVarsFiles into whatever the plugin
authored (its per-env blob, the IdentifyProjects seed, or nothing), leaving
every other key untouched so the blob stays extensible. workspace is no longer
written by config - it comes from the plugin. Downstream this is safe: an unset
workspace and an empty one are identical in the CLI, runner, and parser (all
gate on == "" and the parser defaults to "default").

This is a deliberate, minimal special case expected to retire once
IdentifyAllProjects moves the multi-directory attribution plugin-side.

Test_Generate_EmitsPluginBlob's workspace assertion now depends on a terraform
plugin that emits raw_options from IdentifyEnvironments (proto >= v1.158.0).
installTestPlugins downloads the latest released plugin, which predates that, so
the assertion fails for now and starts passing once the new plugins are
released; the test is documented accordingly. tfVarsFiles and all other tests
pass.
@liamcervante

Copy link
Copy Markdown
Contributor Author

closed in favour of #19

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