Skip to content

feat(stacks): support global-scope metadata defaults - #2808

Merged
Andriy Knysh (aknysh) merged 8 commits into
mainfrom
osterman/tags-labels-not-working
Jul 28, 2026
Merged

feat(stacks): support global-scope metadata defaults#2808
Andriy Knysh (aknysh) merged 8 commits into
mainfrom
osterman/tags-labels-not-working

Conversation

@osterman

@osterman Erik Osterman (Cloud Posse) (osterman) commented Jul 25, 2026

Copy link
Copy Markdown
Member

what

  • Adds support for a restricted allowlist of metadata fields (labels, tags, custom, enabled, locked, terraform_workspace_pattern) at the stack-manifest root, deep-merged into every component's own metadata as a stack-wide default.
  • Merge precedence, lowest to highest: global (stack-wide) → the metadata.inherits base-component chain → the component's own local metadata: block, which always wins.
  • Component-identity fields (component, inherits, type, name, terraform_workspace) remain component-only; setting one of these at global scope is now a hard validation error, both at runtime and via the manifest JSON Schema, instead of a silent no-op.
  • Updates metadata.mdx docs and adds a changelog post explaining the new global scope.

why

  • A stack-wide metadata: block (e.g. in _defaults.yaml) was previously accepted by the schema but never applied — metadata.labels/metadata.tags/etc. set there silently did nothing, which is worse than an error, since users had no signal their config wasn't taking effect.
  • Sharing labels, tags, or a stack-wide lock/enable flag across every component in a stack required copy-pasting the same metadata block into each component definition instead of declaring it once.

references

Summary by CodeRabbit

  • New Features

    • Added stack-root metadata: defaults that are deep-merged into each component’s metadata.
    • Enforced 3-tier precedence: stack defaults → metadata.inherits chain (when enabled) → component-local metadata.
  • Validation & Tests

    • Added schema allowlisting for stack-scope metadata (only labels, tags, custom, enabled, locked, terraform_workspace_pattern).
    • Expanded tests to cover precedence, custom component behavior, and new error paths for invalid global/identity fields.
  • Documentation

    • Updated component metadata docs and added a blog post explaining scope, allowed keys, and merge behavior.

Add a restricted allowlist of `metadata` fields (labels, tags, custom,
enabled, locked, terraform_workspace_pattern) that can be set once at
the stack-manifest root and deep-merged into every component's own
metadata, with the lowest precedence (global -> metadata.inherits base
chain -> component-local, which always wins).

Previously a top-level `metadata:` block was silently ignored; setting
a component-identity field (component/inherits/type/name/terraform_workspace)
at global scope is now a hard validation error instead of a silent no-op,
both at runtime and via the manifest JSON Schema.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@atmos-pro

atmos-pro Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@osterman Erik Osterman (Cloud Posse) (osterman) added the minor New features that do not break anything label Jul 25, 2026
@github-actions github-actions Bot added the size/m Medium size PR label Jul 25, 2026
@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown

Note

Release Documentation Complete ✅

  • Changelog: website/blog/2026-07-25-global-metadata-defaults.mdx
  • Roadmap: website/src/data/roadmap.js

Thank you!

@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

Add the stack-wide metadata defaults milestone to the discoverability
initiative, linked to PR #2808 and the global-metadata-defaults changelog post.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3c1c2b30-34a2-4f4a-a1fd-33288530d3ab

📥 Commits

Reviewing files that changed from the base of the PR and between 8928282 and 6656875.

📒 Files selected for processing (2)
  • internal/exec/stack_processor_process_stacks.go
  • internal/exec/stack_processor_process_stacks_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/exec/stack_processor_process_stacks.go
  • internal/exec/stack_processor_process_stacks_test.go

📝 Walkthrough

Walkthrough

Adds root-level stack metadata defaults with schema validation, allowlisted fields, propagation to built-in and custom components, precedence-aware merging, tests, and documentation.

Changes

Global metadata defaults

Layer / File(s) Summary
Metadata contract and schema
errors/errors.go, pkg/datafetcher/schema/atmos/manifest/1.0.json, pkg/datafetcher/schema_*, website/...
Defines allowed stack-level metadata, rejects unsupported identity fields, updates schema coverage, and documents merge precedence and supported defaults.
Validation and component wiring
internal/exec/stack_processor_process_stacks.go, internal/exec/stack_processor_process_stacks_helpers.go, internal/exec/stack_processor_process_stacks_test.go
Validates root metadata, passes it through built-in component options, merges it into custom components, and tests invalid and overriding configurations.
Metadata precedence and inheritance
internal/exec/stack_processor_merge.go, internal/exec/stack_processor_merge_test.go
Merges global, inherited, and local metadata layers and tests precedence with metadata inheritance enabled or disabled.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant StackConfig
  participant ProcessStackConfig
  participant ComponentProcessor
  StackConfig->>ProcessStackConfig: provide root metadata
  ProcessStackConfig->>ComponentProcessor: pass GlobalMetadata
  ComponentProcessor->>ComponentProcessor: merge global, inherited, and local metadata
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding support for stack-level global metadata defaults.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch osterman/tags-labels-not-working

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
website/docs/stacks/components/metadata.mdx (1)

39-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clarify: no terraform:/helmfile:/packer: metadata defaults feature exists.

The schema's terraform/helmfile/packer definitions don't expose a metadata property at all (additionalProperties: false, no metadata key), so this parenthetical implies a capability that isn't schema-supported rather than one that's merely restricted to identity fields.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@website/docs/stacks/components/metadata.mdx` at line 39, Update the
component-identity fields documentation to remove the unsupported claim about
setting metadata under terraform, helmfile, or packer defaults; describe only
the global-level restriction unless schema-supported nested metadata defaults
are actually available.
internal/exec/stack_processor_process_stacks.go (1)

1296-1308: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Shallow merge for custom-component metadata (no nested deep-merge).

Nested fields like metadata.labels will be fully overwritten by component-local values here rather than deep-merged, unlike the mergo-based merge for built-in component types. This matches the existing shallow-merge pattern already used for vars/settings/env in this same function, so it's consistent with established conventions here, just worth being aware of for custom component types.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/exec/stack_processor_process_stacks.go` around lines 1296 - 1308,
Preserve the existing shallow merge behavior in the custom-component metadata
handling around componentMetadata: copy global metadata first, then let
component-local metadata replace matching top-level keys, including entire
nested values such as labels. Do not introduce deep-merge behavior or alter the
established vars/settings/env merge conventions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@website/docs/stacks/components/metadata.mdx`:
- Line 285: Update the metadata.custom inheritance documentation in the manifest
schema referenced by the stack-config schema so it matches the implementation
and docs: remove the statement that custom is not inherited, or explicitly
include custom in the documented inherited metadata behavior. Preserve the
existing deep-merge and derived-component override semantics.

---

Nitpick comments:
In `@internal/exec/stack_processor_process_stacks.go`:
- Around line 1296-1308: Preserve the existing shallow merge behavior in the
custom-component metadata handling around componentMetadata: copy global
metadata first, then let component-local metadata replace matching top-level
keys, including entire nested values such as labels. Do not introduce deep-merge
behavior or alter the established vars/settings/env merge conventions.

In `@website/docs/stacks/components/metadata.mdx`:
- Line 39: Update the component-identity fields documentation to remove the
unsupported claim about setting metadata under terraform, helmfile, or packer
defaults; describe only the global-level restriction unless schema-supported
nested metadata defaults are actually available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2561841b-b2a6-4b15-8db1-b54897cf2c3a

📥 Commits

Reviewing files that changed from the base of the PR and between 1b965ac and 72c1dc7.

📒 Files selected for processing (11)
  • errors/errors.go
  • internal/exec/stack_processor_merge.go
  • internal/exec/stack_processor_merge_test.go
  • internal/exec/stack_processor_process_stacks.go
  • internal/exec/stack_processor_process_stacks_helpers.go
  • internal/exec/stack_processor_process_stacks_test.go
  • pkg/datafetcher/schema/atmos/manifest/1.0.json
  • pkg/datafetcher/schema_global_metadata_test.go
  • pkg/datafetcher/schema_section_coverage_test.go
  • website/blog/2026-07-25-global-metadata-defaults.mdx
  • website/docs/stacks/components/metadata.mdx

Comment thread website/docs/stacks/components/metadata.mdx
Reframe the intro and problem statement around the agreed-upon common
case (shared labels/tags) instead of asserting that locking every
component in a stack is a widely shared need; keep enabled/locked as a
secondary capability with a narrower, concrete example (decommissioning
a legacy stack).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The manifest schema's metadata.custom description claimed it was "not
inherited by derived components," but internal/exec/stack_processor_utils.go
only excludes 'inherits' and 'type' from the metadata.inherits base-component
merge — custom IS inherited, matching the docs and existing tests. Also
correct the same copy-pasted phrasing in the new global_metadata.custom
description to reflect actual precedence instead of the per-component wording
it was copied from.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 25, 2026
@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.23810% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.84%. Comparing base (edeff5e) to head (6656875).

Files with missing lines Patch % Lines
internal/exec/stack_processor_process_stacks.go 94.28% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #2808   +/-   ##
=======================================
  Coverage   81.84%   81.84%           
=======================================
  Files        1793     1793           
  Lines      173173   173207   +34     
=======================================
+ Hits       141725   141769   +44     
+ Misses      23649    23640    -9     
+ Partials     7799     7798    -1     
Flag Coverage Δ
unittests 81.84% <95.23%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
errors/errors.go 100.00% <ø> (ø)
internal/exec/stack_processor_merge.go 80.88% <100.00%> (-0.05%) ⬇️
...nal/exec/stack_processor_process_stacks_helpers.go 86.66% <ø> (ø)
internal/exec/stack_processor_process_stacks.go 82.47% <94.28%> (+0.48%) ⬆️

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The custom (non-built-in) component type passthrough loop merges
stack-root global metadata the same way built-in types do, but the
loop body that copies global metadata keys into a custom component's
metadata was never exercised by any existing test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/exec/stack_processor_process_stacks_test.go`:
- Around line 1331-1344: The test fixture and expectations around
custom-component metadata in the relevant stack-processing test do not verify
deep merging of nested labels. Add a non-conflicting global label and a
local-only label, then assert the merged result retains both labels while the
nested local org value overrides the global value; update the corresponding
expectation near the alternate referenced section as well.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 15148ec2-c83a-4592-aa79-80b2341d8a8e

📥 Commits

Reviewing files that changed from the base of the PR and between c5bbbe5 and 8928282.

📒 Files selected for processing (1)
  • internal/exec/stack_processor_process_stacks_test.go

Comment thread internal/exec/stack_processor_process_stacks_test.go
The custom (non-built-in) component type passthrough loop shallow-merged
stack-root global metadata into a component's own metadata, so nested maps
like metadata.labels were wholesale-replaced by a component's local value
instead of deep-merged. This was inconsistent with built-in component types
(terraform/helmfile/packer/ansible/kubernetes/helm), which use m.Merge and
retain non-conflicting keys from both sides.

Switch the custom-type metadata merge to m.Merge, matching the pattern
already used elsewhere in this file, and extend the existing test to assert
that non-conflicting global and local label keys both survive while the
conflicting key resolves to the local value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@aknysh
Andriy Knysh (aknysh) merged commit fc4960a into main Jul 28, 2026
84 checks passed
@aknysh
Andriy Knysh (aknysh) deleted the osterman/tags-labels-not-working branch July 28, 2026 18:12
@atmos-pro

atmos-pro Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@github-actions

Copy link
Copy Markdown

These changes were released in v1.225.0-rc.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor New features that do not break anything size/m Medium size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants