Atmos Core: Component Type Development

SkillCloud & infra

This skill gives your AI a contributor guide for Atmos, the open source tool at github.com/cloudposse/atmos, focused on adding or changing component types in its Go codebase. Once added, your AI can extend Atmos with new component types such as terraform, helmfile, packer, ansible, or container, covering everything from the internal registry and commands to the schema and tests.

Available today. Use it from your connected AI after setup.

Tell your AI which component type you want to add or modify in Atmos, and it will follow the guide step by step through the registry, commands, schema, and tests.

Then ask your AI: use the Atmos Core: Component Type Development skill

What your AI can do with it

  • Add a new component type to Atmos, such as terraform, helmfile, packer, ansible, or container
  • Register component types in the internal registry and provider
  • Add command-line commands for a component type
  • Update which component types Atmos can list and describe
  • Implement inheritance and deep-merge behavior for custom components
  • Update the schema and write tests for component type changes

What this skill tells your AI

The instructions your AI receives, as published by cloudposse/atmos in .claude/skills/atmos-core-component-development/SKILL.md and read by ahel’s review.

Use this skill when developing Atmos itself — adding or modifying a component type (kind) in the Go codebase. This is contributor/core guidance, distinct from the user-facing atmos-components skill (which documents authoring terraform/helmfile/container components in stacks).

Start from docs/developing-component-plugins.md (the component-plugin development guide). The notes below capture the non-obvious wiring learned while adding the container component type.

The component provider (pkg/component)

A component type is a ComponentProvider (pkg/component/provider.go) registered via init() with component.Register(...) (pkg/component/registry.go). Reference impls:

  • pkg/component/ansible/ — typed-config built-in style.
  • pkg/component/mock/, pkg/component/custom/ — the Plugins-map plugin style.
  • pkg/component/container/ — provider + cmd/lifecycle split.

Layout per the guide: config.go (typed Config + parseConfig), <type>.go (provider + init), executor.go (verb implementations), <type>_test.go (>90% coverage). Wire a blank import into cmd/root.go so init() runs.

Reusable error sentinels live in errors/errors.go: ErrComponentExecutionFailed, ErrComponentConfigInvalid, ErrComponentValidationFailed, ErrComponentTypeEmpty.

First-class component config (NOT vars)

Per-instance config that is not arbitrary template data must be first-class top-level sections (siblings of metadata/env/composition), NOT nested under vars. For container, the config reuses the workflow container-step structs (schema.ContainerBuildStep/ContainerRunStep/ContainerMount/ ContainerPort in pkg/schema/workflow.go) for consistency. Decode a YAML-derived map[string]any into those structs with mapstructure using TagName: "yaml" so snake_case keys (build_args, read_only) map.

The CLI command group (cmd/)

Mirror cmd/ansible/: a base cobra.Command registered through the command registry (cmd/internal CommandProvider), persistent flags via flags.NewStandardParser() (NEVER viper.BindEnv/BindPFlag), one thin file per verb dispatching to component.MustGetProvider(<type>).Execute(&component.ExecutionContext{...}). Wire a blank import into cmd/root.go.

CRITICAL: the describe/list type whitelist

A new top-level components.<type> is dropped (stack renders {}, "component not found") unless the type is added to several hardcoded lists. Grep AnsibleSectionName / "ansible" across internal/exec + pkg/list/extract and mirror every hit:

  1. pkg/config/const.goXComponentType / XSectionName consts.
  2. internal/exec/describe_stacks_component_processor.go — the typeEntries list AND componentsSectionHasComponents.
  3. internal/exec/describe_stacks.gogetComponentBasePath switch.
  4. internal/exec/describe_component.go — the detectComponentType auto-detect order (a loop over [terraform, helmfile, packer, ansible, container]); a type missing here makes atmos describe component <name> fail even when the lifecycle works.
  5. pkg/list/extract/components.go — THREE hardcoded type lists (per-stack extractComponentType ×2, unique extractUniqueComponentType).

Verify with atmos describe stacks (stack with only the new type must be non-empty) and atmos describe component <name> -s <stack>.

Inheritance & deep-merge for custom types

Built-in types (terraform/helmfile/packer/ansible) get full inheritance via the processComponent pipeline. Other types ride the custom-component fallback in internal/exec/stack_processor_process_stacks.go. That fallback now resolves metadata.inherits and generic-deep-merges all top-level keys (resolveCustomComponentInheritance), so custom types honor catalog/abstract defaults. Gotchas:

  • Strip metadata.type/inherits/component from a base before merging, or an abstract base poisons the concrete component (sanitizeBaseForInheritance).
  • Reject metadata.type: abstract for execution and filter it from listings.
  • Use the native merge (pkg/merge) — it already incorporates the slice-truncation and permissive-type-mismatch fixes in docs/fixes/2026-03-19-* and docs/fixes/2026-03-24-*.
  • Component-level config sections (vars/settings/env/hooks/secrets/...) that need per-section inheritance still require the section whitelist plumbing (see docs/errors.md / the merge helpers).

Schema, docs, tests

  • JSON schema: pkg/datafetcher/schema/atmos/manifest/1.0.json — add <type>_components + <type>_component_manifest definitions and the components.<type> property.
  • Docs: website/docs/components/components-overview.mdx (Component Types table + directory diagram), website/docs/components/<type>.mdx, and website/docs/cli/commands/<type>/usage.mdx.
  • Tests: provider unit tests with a mockgen Runtime/dependency, cmd.NewTestKit for the command, inheritance + abstract + graceful-empty cases. Regenerate affected --help golden snapshots with -regenerate-snapshots (never hand-edit).
  • Gate: ./custom-gcl run --new-from-rev=origin/<base> ./pkg/component/<type>/... ./internal/exec/....

Signals

GitHub stars
1k
Forks
175
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
atmos-core-component-development
Source
github.com/cloudposse/atmos