Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3633582
feat: Add explicit stack name override via stack manifest 'name' field
osterman Dec 4, 2025
a50a9d8
docs: Add stack naming documentation and update Terragrunt migration …
osterman Dec 5, 2025
370fc5b
docs: Improve stack naming section in Terragrunt migration guide
osterman Dec 5, 2025
0913fde
Merge branch 'main' into osterman/stack-manifest-name
osterman Dec 8, 2025
5b47c08
test: Add unit tests for BuildTerraformWorkspace stack name precedence
osterman Dec 8, 2025
4f849f0
Merge branch 'main' into osterman/stack-manifest-name
osterman Dec 8, 2025
6a5cebe
test: Add integration tests for describe_stacks stack name precedence
osterman Dec 8, 2025
d725a9c
fix(pty): Add delay for PTY output buffer flush in tests
osterman Dec 9, 2025
b9becf1
fix(auth): Increase context timeout in SSO test for Windows compatibi…
osterman Dec 9, 2025
585b747
Merge branch 'main' into osterman/stack-manifest-name
osterman Dec 9, 2025
cc44613
fix(pty): Use sh -c printf for more reliable PTY output in CI
osterman Dec 9, 2025
d67a86d
fix(pty): Use subprocess sleep to avoid PTY race condition
osterman Dec 10, 2025
7ae174d
Merge branch 'main' into osterman/stack-manifest-name
osterman Dec 10, 2025
15a945f
Merge branch 'main' into osterman/stack-manifest-name
aknysh Dec 10, 2025
dd84566
Merge branch 'main' into osterman/stack-manifest-name
aknysh Dec 10, 2025
2806ee9
Merge branch 'main' into osterman/stack-manifest-name
aknysh Dec 10, 2025
cc261c4
Merge branch 'main' into osterman/stack-manifest-name
aknysh Dec 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,30 +562,25 @@ Follow template (what/why/references).
**Blog Posts (CI Enforced):**
- PRs labeled `minor`/`major` MUST include blog post: `website/blog/YYYY-MM-DD-feature-name.mdx`
- Use `.mdx` with YAML front matter, `<!--truncate-->` after intro
- **ONLY use existing tags** - check `website/blog/*.mdx` for valid tags before writing
- Author: committer's GitHub username, add to `website/blog/authors.yml`
- **MUST read `website/blog/tags.yml`** - Only use tags defined there, never invent new tags
- **MUST read `website/blog/authors.yml`** - Use existing author or add new entry for committer

**Blog Template:**
```markdown
---
slug: descriptive-slug
title: "Clear Title"
authors: [username]
tags: [primary-tag, secondary-tag]
tags: [feature]
---
Brief intro.
<!--truncate-->
## What Changed / Why This Matters / How to Use It / Get Involved
```

**Existing Tags (use only these):**
- Primary: `feature`, `enhancement`, `bugfix`
- Secondary: `dx`, `security`, `documentation`, `core`, `breaking-change`

**Finding valid tags:**
```bash
grep -h "^ - " website/blog/*.mdx | sort | uniq -c | sort -rn
```
**Valid Tags (from `website/blog/tags.yml`):**
- User-facing: `feature`, `enhancement`, `bugfix`, `dx`, `breaking-change`, `security`, `documentation`, `deprecation`
- Internal: `core` (for contributor-only changes with zero user impact)

Use `no-release` label for docs-only changes.

Expand Down
6 changes: 3 additions & 3 deletions cmd/list/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ var componentsCmd = &cobra.Command{
}

if len(output) == 0 {
ui.Info("No components found")
_ = ui.Info("No components found")
return nil
}

Expand Down Expand Up @@ -84,7 +84,7 @@ func listComponentsWithOptions(cmd *cobra.Command, opts *ComponentsOptions) ([]s
configAndStacksInfo := schema.ConfigAndStacksInfo{}
atmosConfig, err := config.InitCliConfig(configAndStacksInfo, true)
if err != nil {
return nil, fmt.Errorf("error initializing CLI config: %v", err)
return nil, fmt.Errorf("error initializing CLI config: %w", err)
}

// Create AuthManager for authentication support.
Expand All @@ -95,7 +95,7 @@ func listComponentsWithOptions(cmd *cobra.Command, opts *ComponentsOptions) ([]s

stacksMap, err := e.ExecuteDescribeStacks(&atmosConfig, "", nil, nil, nil, false, false, false, false, nil, authManager)
if err != nil {
return nil, fmt.Errorf("error describing stacks: %v", err)
return nil, fmt.Errorf("error describing stacks: %w", err)
}

output, err := l.FilterAndListComponents(opts.Stack, stacksMap)
Expand Down
1 change: 0 additions & 1 deletion cmd/list/components_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//nolint:dupl // Test structure similarity is intentional for consistency
package list

import (
Expand Down
6 changes: 3 additions & 3 deletions cmd/list/stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ var stacksCmd = &cobra.Command{
}

if len(output) == 0 {
ui.Info("No stacks found")
_ = ui.Info("No stacks found")
return nil
}

Expand Down Expand Up @@ -85,7 +85,7 @@ func listStacksWithOptions(cmd *cobra.Command, opts *StacksOptions) ([]string, e

atmosConfig, err := config.InitCliConfig(configAndStacksInfo, true)
if err != nil {
return nil, fmt.Errorf("error initializing CLI config: %v", err)
return nil, fmt.Errorf("error initializing CLI config: %w", err)
}

// Create AuthManager for authentication support.
Expand All @@ -96,7 +96,7 @@ func listStacksWithOptions(cmd *cobra.Command, opts *StacksOptions) ([]string, e

stacksMap, err := e.ExecuteDescribeStacks(&atmosConfig, "", nil, nil, nil, false, false, false, false, nil, authManager)
if err != nil {
return nil, fmt.Errorf("error describing stacks: %v", err)
return nil, fmt.Errorf("error describing stacks: %w", err)
}

output, err := l.FilterAndListStacks(stacksMap, opts.Component)
Expand Down
31 changes: 19 additions & 12 deletions cmd/list/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ const (
ErrFmtWrapErr = "%w: %v" // Format for wrapping errors.
)

// Flag name constants.
const (
flagAbstract = "abstract"
flagProcessTemplates = "process-templates"
flagProcessFunctions = "process-functions"
)

var (
valuesParser *flags.StandardParser
varsParser *flags.StandardParser
Expand Down Expand Up @@ -169,14 +176,14 @@ var varsCmd = &cobra.Command{
func init() {
// Create parser for values command with all flags
valuesParser = newCommonListParser(
flags.WithBoolFlag("abstract", "", false, "Include abstract components"),
flags.WithBoolFlag(flagAbstract, "", false, "Include abstract components"),
flags.WithBoolFlag("vars", "", false, "Show only vars (equivalent to --query .vars)"),
flags.WithBoolFlag("process-templates", "", true, "Enable/disable Go template processing in Atmos stack manifests when executing the command"),
flags.WithBoolFlag("process-functions", "", true, "Enable/disable YAML functions processing in Atmos stack manifests when executing the command"),
flags.WithEnvVars("abstract", "ATMOS_LIST_ABSTRACT"),
flags.WithBoolFlag(flagProcessTemplates, "", true, "Enable/disable Go template processing in Atmos stack manifests when executing the command"),
flags.WithBoolFlag(flagProcessFunctions, "", true, "Enable/disable YAML functions processing in Atmos stack manifests when executing the command"),
flags.WithEnvVars(flagAbstract, "ATMOS_LIST_ABSTRACT"),
flags.WithEnvVars("vars", "ATMOS_LIST_VARS"),
flags.WithEnvVars("process-templates", "ATMOS_PROCESS_TEMPLATES"),
flags.WithEnvVars("process-functions", "ATMOS_PROCESS_FUNCTIONS"),
flags.WithEnvVars(flagProcessTemplates, "ATMOS_PROCESS_TEMPLATES"),
flags.WithEnvVars(flagProcessFunctions, "ATMOS_PROCESS_FUNCTIONS"),
)

// Register flags for values command
Expand All @@ -197,12 +204,12 @@ func init() {

// Create parser for vars command (no vars flag, as it's always .vars)
varsParser = newCommonListParser(
flags.WithBoolFlag("abstract", "", false, "Include abstract components"),
flags.WithBoolFlag("process-templates", "", true, "Enable/disable Go template processing in Atmos stack manifests when executing the command"),
flags.WithBoolFlag("process-functions", "", true, "Enable/disable YAML functions processing in Atmos stack manifests when executing the command"),
flags.WithEnvVars("abstract", "ATMOS_LIST_ABSTRACT"),
flags.WithEnvVars("process-templates", "ATMOS_PROCESS_TEMPLATES"),
flags.WithEnvVars("process-functions", "ATMOS_PROCESS_FUNCTIONS"),
flags.WithBoolFlag(flagAbstract, "", false, "Include abstract components"),
flags.WithBoolFlag(flagProcessTemplates, "", true, "Enable/disable Go template processing in Atmos stack manifests when executing the command"),
flags.WithBoolFlag(flagProcessFunctions, "", true, "Enable/disable YAML functions processing in Atmos stack manifests when executing the command"),
flags.WithEnvVars(flagAbstract, "ATMOS_LIST_ABSTRACT"),
flags.WithEnvVars(flagProcessTemplates, "ATMOS_PROCESS_TEMPLATES"),
flags.WithEnvVars(flagProcessFunctions, "ATMOS_PROCESS_FUNCTIONS"),
)

// Register flags for vars command
Expand Down
48 changes: 39 additions & 9 deletions internal/exec/describe_stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,22 @@ func ExecuteDescribeStacks(
var backendSection map[string]any
var backendTypeSection string
var stackName string
var stackManifestName string

for stackFileName, stackSection := range stacksMap {
var context schema.Context

// Delete the stack-wide imports.
delete(stackSection.(map[string]any), "imports")

// Extract the stack-level 'name' field (logical name override).
stackManifestName = ""
if nameValue, ok := stackSection.(map[string]any)[cfg.NameSectionName]; ok {
if name, ok := nameValue.(string); ok {
stackManifestName = name
}
}

// Check if the `components` section exists and has explicit components.
hasExplicitComponents := false
if componentsSection, ok := stackSection.(map[string]any)[cfg.ComponentsSectionName]; ok {
Expand Down Expand Up @@ -321,6 +330,7 @@ func ExecuteDescribeStacks(
configAndStacksInfo := schema.ConfigAndStacksInfo{
ComponentFromArg: componentName,
Stack: stackName,
StackManifestName: stackManifestName,
ComponentMetadataSection: metadataSection,
ComponentVarsSection: varsSection,
ComponentSettingsSection: settingsSection,
Expand Down Expand Up @@ -357,19 +367,25 @@ func ExecuteDescribeStacks(
configAndStacksInfo.ComponentSection[cfg.ComponentSectionName] = componentName
}

// Stack name.
if atmosConfig.Stacks.NameTemplate != "" {
// Stack name precedence: name (from manifest) > name_template > name_pattern > filename.
switch {
case stackManifestName != "":
stackName = stackManifestName
case atmosConfig.Stacks.NameTemplate != "":
stackName, err = ProcessTmpl(atmosConfig, "describe-stacks-name-template", atmosConfig.Stacks.NameTemplate, configAndStacksInfo.ComponentSection, false)
if err != nil {
return nil, err
}
} else {
case GetStackNamePattern(atmosConfig) != "":
context = cfg.GetContextFromVars(varsSection)
configAndStacksInfo.Context = context
stackName, err = cfg.GetContextPrefix(stackFileName, context, GetStackNamePattern(atmosConfig), stackFileName)
if err != nil {
return nil, err
}
default:
// Default: use stack filename when no name, template, or pattern is configured.
stackName = stackFileName
}

if filterByStack != "" && filterByStack != stackFileName && filterByStack != stackName {
Expand Down Expand Up @@ -563,6 +579,7 @@ func ExecuteDescribeStacks(
configAndStacksInfo := schema.ConfigAndStacksInfo{
ComponentFromArg: componentName,
Stack: stackName,
StackManifestName: stackManifestName,
ComponentMetadataSection: metadataSection,
ComponentVarsSection: varsSection,
ComponentSettingsSection: settingsSection,
Expand Down Expand Up @@ -599,19 +616,25 @@ func ExecuteDescribeStacks(
configAndStacksInfo.ComponentSection[cfg.ComponentSectionName] = componentName
}

// Stack name.
if atmosConfig.Stacks.NameTemplate != "" {
// Stack name precedence: name (from manifest) > name_template > name_pattern > filename.
switch {
case stackManifestName != "":
stackName = stackManifestName
case atmosConfig.Stacks.NameTemplate != "":
stackName, err = ProcessTmpl(atmosConfig, "describe-stacks-name-template", atmosConfig.Stacks.NameTemplate, configAndStacksInfo.ComponentSection, false)
if err != nil {
return nil, err
}
} else {
case GetStackNamePattern(atmosConfig) != "":
context = cfg.GetContextFromVars(varsSection)
configAndStacksInfo.Context = context
stackName, err = cfg.GetContextPrefix(stackFileName, context, GetStackNamePattern(atmosConfig), stackFileName)
if err != nil {
return nil, err
}
default:
// Default: use stack filename when no name, template, or pattern is configured.
stackName = stackFileName
}

if filterByStack != "" && filterByStack != stackFileName && filterByStack != stackName {
Expand Down Expand Up @@ -782,6 +805,7 @@ func ExecuteDescribeStacks(
configAndStacksInfo := schema.ConfigAndStacksInfo{
ComponentFromArg: componentName,
Stack: stackName,
StackManifestName: stackManifestName,
ComponentMetadataSection: metadataSection,
ComponentVarsSection: varsSection,
ComponentSettingsSection: settingsSection,
Expand Down Expand Up @@ -818,19 +842,25 @@ func ExecuteDescribeStacks(
configAndStacksInfo.ComponentSection[cfg.ComponentSectionName] = componentName
}

// Stack name.
if atmosConfig.Stacks.NameTemplate != "" {
// Stack name precedence: name (from manifest) > name_template > name_pattern > filename.
switch {
case stackManifestName != "":
stackName = stackManifestName
case atmosConfig.Stacks.NameTemplate != "":
stackName, err = ProcessTmpl(atmosConfig, "describe-stacks-name-template", atmosConfig.Stacks.NameTemplate, configAndStacksInfo.ComponentSection, false)
if err != nil {
return nil, err
}
} else {
case GetStackNamePattern(atmosConfig) != "":
context = cfg.GetContextFromVars(varsSection)
configAndStacksInfo.Context = context
stackName, err = cfg.GetContextPrefix(stackFileName, context, GetStackNamePattern(atmosConfig), stackFileName)
if err != nil {
return nil, err
}
default:
// Default: use stack filename when no name, template, or pattern is configured.
stackName = stackFileName
}

if filterByStack != "" && filterByStack != stackFileName && filterByStack != stackName {
Expand Down
Loading