Skip to content

Commit 8e2cc18

Browse files
ostermanclaudeautofix-ci[bot]aknysh
authored
feat: Atmos bugfixes and enhancements from vhs-demo-videos branch (#1954)
* feat: sync atmos bugfixes from vhs-demo-videos branch This includes non-demo changes from osterman/vhs-demo-videos: - List aliases subcommand - List query normalization and simplified syntax - Toolchain multi-tool install support - Markdown rendering fixes (inline code spacing) - Terraform output improvements - Auth factory and mock provider updates - Various documentation updates Excludes demo infrastructure: - tools/director/* - pkg/ffmpeg/* - pkg/vhs/* - demos/* - Makefile (has director references) - .claude/* (has director agent) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: remove vhs-demo-videos PRD This PRD belongs in the vhs-demo-videos branch, not the bugfixes branch. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: add blog post for list and toolchain enhancements Announces: - Simplified query syntax for atmos list components - New atmos list aliases subcommand - Multi-tool installation support for atmos toolchain install Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address CodeRabbit review feedback - Revert FeaturedDemoCarousel to LazyDemo (component pending in other PR) - Remove /demos navbar entry and demoId references from roadmap - Fix list aliases example (remove non-existent --format flag) - Use Packer config BasePath with fallback in describe_stacks - Fix table.go line endings to use utils.GetLineEnding() - Remove perf.Track from trivial ast.go String() method - Improve comment clarity in format.go - Add version documentation to custom_renderer.go - Register all markdown extensions in extendGlamourWithCustomExtensions - Refactor admonition colors to use lipgloss instead of hardcoded ANSI - Update admonition tests to check semantic content, not ANSI codes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add --format flag to list aliases command Add consistent --format support (json, yaml, csv, tsv, tree) to the list aliases command, matching other list commands. - Add WithFormatFlag to parser initialization - Add Format field to AliasesOptions - Implement renderAliasesWithFormat using list renderer infrastructure - Add aliasesToData conversion function for renderer compatibility - Update command examples to show format flag usage - Add tests for format flag and data conversion Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address CodeRabbit review comments for PR #1954 - .gitignore: fix conflicting .envrc patterns by removing duplicate entry - cmd/list/aliases.go: use buildConfigAndStacksInfo to populate global flags - pkg/ui/markdown/extensions/linkify.go: add early return to prevent duplicate nodesToReplace entries - website/src/components/Video/DemoVideo.tsx: add placeholder component for build compatibility - website/src/theme/Navbar/Content/usePriorityNavbar.ts: fix zero-width items handling in overflow calculation - internal/exec/describe_stacks.go: remove hardcoded Packer fallback for consistency with Terraform/Helmfile - pkg/ui/markdown/custom_renderer_test.go: add reflection stability test for getGlamourGoldmark Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add built-in aliases to list aliases command The `atmos list aliases` command now shows both: - Built-in aliases: Native Cobra command shortcuts (tf, hf, pk, etc.) - Configured aliases: User-defined aliases from atmos.yaml New features: - Recursively collects aliases from the entire command tree - Shows Type column to distinguish alias sources - Footer shows breakdown of built-in vs configured counts - Supports all output formats (table, json, yaml, csv, tsv) Built-in aliases are collected by traversing the Cobra command tree and extracting the Aliases field from each command. This makes it easy for users to discover all available shortcuts. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: sort aliases alphabetically by default Changed default sort order from "type first, then name" to pure alphabetical sorting by alias name. This makes it easier to find a specific alias when scanning the list. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: improve alias display with left-aligned columns and consistent paths - Changed from CreateThemedTable to CreateMinimalTable for left-aligned columns (CreateThemedTable right-aligns column 0 for status icons, not text) - Fixed nested alias paths to exclude root command name ("atmos") e.g., "describe dependants" instead of "atmos describe dependants" - Extracted helper functions to reduce nesting complexity - Updated tests to reflect the corrected alias path format Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address CodeRabbit review comments - DemoVideo.tsx: use void props pattern for unused parameters - usePriorityNavbar.ts: retry measurement until all items have widths - terraform-apply.mdx: remove placeholder DemoVideo until ready Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: remove placeholder DemoVideo from validate-stacks.mdx The DemoVideo component is a placeholder that returns null until the demo infrastructure is ready. Remove it from validate-stacks.mdx to prevent confusion and potential build issues. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: add list aliases milestone to roadmap Add shipped milestone for `atmos list aliases` showing built-in and configured aliases to the Discoverability initiative. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: correct ParseToolSpec method name and remove orphaned test - Fix case-sensitivity error: parseToolSpec → ParseToolSpec in toolchain/install.go:184 (fixes macOS/Windows build failures) - Remove cmd/list/components_query_test.go which tests non-existent functions (normalizeComponentQuery, filterComponentsWithQuery, isScalarExtractionQuery were never synced from source branch) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address CodeRabbit review comments - Add clarifying comment for highlightDelimiterProcessor explaining it's kept as a reference implementation (tested but not used in Extend()) - Fix components_test.go field names: kind→type for technology type, type→component_type for real/abstract status - Update toolchain/install.go comment to clarify reinstallFlag is unused (not "handled internally") - Remove unused @cloudflare/stream-react dependency from website Note: GetColorProfile() doesn't need perf.Track per CLAUDE.md exceptions for trivial getters. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use theme-aware colors in markdown extensions Address CodeRabbit review comments: 1. Fix missing trailing periods in test comments (godot linter) - custom_renderer_test.go lines 445, 455, 465 2. Replace hardcoded ANSI colors with theme-aware colors - admonition.go: Use theme.GetCurrentStyles() for admonition colors - badge.go: Use theme.GetCurrentStyles() for badge colors - Refactor theme/show.go to accept markdown renderer callback, breaking import cycle between theme and markdown packages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: resolve test failures from partial sync - Add multi-tool support to toolchain install command (Use, Args, runInstall) - Revert incorrect test field name change (kind -> type) in components_test.go - Update FormatToast empty message test expectation for cleaner renderer output Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address CodeRabbit review comments - Update list-pager-integration.md to reflect partial pager rollout - Add .git path guard in processMatch to prevent copying git internals - Update mock-aws to mock/aws in factory and provisioner tests - Add perf.Track to findVarsFromComponents and getComponentTypes - Fix comment formatting in highlight_utils.go (godot lint) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: regenerate pnpm-lock.yaml after removing @cloudflare/stream-react The previous commit (f9eca50) removed @cloudflare/stream-react from package.json but didn't regenerate the lockfile, causing CI to fail with ERR_PNPM_OUTDATED_LOCKFILE when running with --frozen-lockfile. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: resolve test failures from markdown rendering and badge parser - Fix badge parser panic by adding bounds check before slicing (pkg/ui/markdown/extensions/badge.go) - Fix NO_COLOR markdown rendering by skipping custom styles when color profile is Ascii - prevents malformed ANSI codes like [;1m (pkg/ui/markdown/custom_renderer.go) - Fix Toast trailing whitespace by stripping trailing empty lines that Glamour adds during word wrapping (pkg/ui/formatter.go) - Update test expectations to match cleaner output format (pkg/ui/formatter_test.go, toolchain/clean_test.go) - Regenerate golden snapshots after fixes Fixes: - TestExperimentalModeHandling (all 5 subtests) - TestCleanToolsAndCaches (all 5 subtests) - TestFormatterToastMethod and related formatter tests - TestCLICommands snapshot mismatches Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add direct reflection stability test for getGlamourGoldmark Address CodeRabbit review feedback by adding explicit test that: - Creates glamour.TermRenderer directly - Calls getGlamourGoldmark() and asserts non-nil result - Documents glamour v0.10.0 version dependency The direct test catches reflection breakage immediately rather than relying on indirect symptoms from extension registration failures. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add cleanup to restore color profile in Toast tests Address CodeRabbit review feedback by adding t.Cleanup to restore the global lipgloss color profile after TestFormatterToastMethod and TestFormatterToastfMethod. This prevents test isolation issues where subsequent tests could inherit the ASCII profile. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address CodeRabbit review comments - cmd/theme/show.go: Reword comment to fix godot linter issue - cmd/toolchain/install.go: Add warning when --default flag is used with multiple tools (flag is ignored for batch installs) - pkg/ui/formatter.go: Clarify comment about trailing newline stripping - pkg/utils/highlight_utils.go: Use canonical IsColorEnabled method for proper Color/NoColor field handling - website/docs: Fix DemoVideo id from config-describe-affected to describe-affected to match naming convention - website/src/theme/Navbar: Add visibility:hidden for accessibility, clamp itemRefs to totalItems to prevent endless re-measure loops Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: add perf.Track to GetColorProfile for consistency While GetColorProfile() is conceptually a getter, it makes external library calls (lipgloss.DefaultRenderer().ColorProfile()). Adding perf.Track for consistency with guidelines about functions making external calls. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * fix: restore TTY check for syntax highlighting in non-TTY environments The previous change to HighlightCodeWithConfig inadvertently enabled syntax highlighting in non-TTY environments. The IsColorEnabled method with Color=true (the default) returns true regardless of the isTTY parameter, causing ANSI color codes to be output even when stdout is piped. This fix restores explicit TTY gating: - Skip highlighting if not in a terminal AND color is not forced - ForceColor (ATMOS_FORCE_COLOR) explicitly enables highlighting without TTY - NoColor takes precedence over everything Fixes test failures in atmos_auth_env_--format_json_--identity_mock-identity and atmos_auth_env_--login_without_cached_credentials. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * [autofix.ci] apply automated fixes * fix: address CodeRabbit review comments and lint issues - Add perf.Track to createMarkdownRenderer in cmd/theme/show.go - Fix comment punctuation in pkg/ui/formatter.go for godot lint - Fix singleCaseSwitch lint in internal/exec/packer.go - Fix error-strings lint in internal/exec/packer_output.go Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address additional CodeRabbit review comments - Add trailing periods to inline comments in cmd/list/aliases.go - Wrap markdown renderer errors with ErrMarkdownRendererInit sentinel - Remove unused FFmpeg/VHS error sentinels from errors/errors.go Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use static error sentinels for config and markdown errors - Use ErrFailedToInitializeAtmosConfig in cmd/list/aliases.go - Add ErrMarkdownRender sentinel for render failures - Wrap markdown render errors in cmd/theme/show.go Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use static error sentinel for column selector creation Replace string literal error with ErrCreateColumnSelector sentinel in cmd/list/aliases.go to follow project error handling guidelines. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: use renderer abstraction for list aliases and update blog post - Refactor list aliases to use pkg/list/renderer for ALL output formats - Add --columns and --sort flags for consistency with other list commands - Update blog post to emphasize built-in + configured aliases distinction - Add WithAliasesColumnsFlag to flag_wrappers.go - Add helper functions: getAliasColumns, buildAliasSorters, buildAliasFooter - Remove obsolete custom table formatting functions - Update tests for new helper functions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: add trailing periods to comments in aliases.go Address CodeRabbit review comments: - Add trailing period to Type field inline comment - Combine multi-line comment into single line with period Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: reformat list-pager-integration PRD to standard format Add proper PRD structure including: - Executive Summary - Problem Statement with Challenges - Design Goals - Technical Specification with architecture diagram - Success Criteria - References and Revision History All original technical content preserved. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: regenerate toolchain test snapshots for branch output Regenerate golden snapshots to match actual code output: - registry_search: Uses plain text "20 tools" instead of bold - install_non-existent_tool: Error message format without tool name Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: apply deterministic sorting to complex types in table output Apply sortValueRecursive to maps and slices before JSON marshaling in formatValueForTable for consistent, deterministic output across runs. This matches the behavior of formatJSON and formatYAML formatters which already apply sorting for map key ordering. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * add tests * add tests * address comments * address comments --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: aknysh <andriy.knysh@gmail.com>
1 parent e299827 commit 8e2cc18

File tree

121 files changed

+6066
-305
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+6066
-305
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ jobs:
9797
- { os: "runs-on=${{github.run_id}}/runner=terraform/tag=atmos/extras=s3-cache/private=false", target: linux }
9898
- { os: "windows-latest", target: windows }
9999
- { os: "macos-latest", target: macos }
100-
timeout-minutes: 30
100+
timeout-minutes: 45
101101
runs-on: ${{ matrix.flavor.os }}
102102
steps:
103103
- uses: runs-on/action@v1
@@ -206,7 +206,7 @@ jobs:
206206
# }
207207

208208
- name: Acceptance tests
209-
timeout-minutes: 30
209+
timeout-minutes: 45
210210
if: ${{ ! ( matrix.flavor.target == 'windows' && github.event.pull_request.draft ) }}
211211
env:
212212
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
# Atmos binary (only in root directory)
2222
/atmos
2323

24+
# Demo binaries and large audio files
25+
tools/director/director
26+
demos/audio/
27+
2428
# Module directory
2529
.terraform
2630
**/.idea
@@ -38,9 +42,12 @@
3842
/bin
3943

4044
# Nix
41-
.envrc
4245
.direnv/
4346

47+
# Environment files (except .envrc which is tracked for Nix)
48+
.env*
49+
!.envrc
50+
4451
# Tool version managers
4552
.tool-versions
4653
!examples/toolchain/.tool-versions

cmd/list/aliases.go

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
package list
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
"github.com/spf13/viper"
10+
11+
errUtils "github.com/cloudposse/atmos/errors"
12+
"github.com/cloudposse/atmos/pkg/config"
13+
"github.com/cloudposse/atmos/pkg/flags"
14+
"github.com/cloudposse/atmos/pkg/flags/global"
15+
"github.com/cloudposse/atmos/pkg/list/column"
16+
"github.com/cloudposse/atmos/pkg/list/format"
17+
"github.com/cloudposse/atmos/pkg/list/renderer"
18+
listSort "github.com/cloudposse/atmos/pkg/list/sort"
19+
"github.com/cloudposse/atmos/pkg/schema"
20+
"github.com/cloudposse/atmos/pkg/terminal"
21+
"github.com/cloudposse/atmos/pkg/ui"
22+
"github.com/cloudposse/atmos/pkg/ui/theme"
23+
)
24+
25+
const (
26+
aliasTypeBuiltIn = "built-in"
27+
aliasTypeConfigured = "configured"
28+
)
29+
30+
// AliasInfo represents a command alias with its source type.
31+
type AliasInfo struct {
32+
Alias string
33+
Command string
34+
Type string // "built-in" or "configured".
35+
}
36+
37+
var aliasesParser *flags.StandardParser
38+
39+
// AliasesOptions contains parsed flags for the aliases command.
40+
type AliasesOptions struct {
41+
global.Flags
42+
Format string
43+
Columns []string
44+
Sort string
45+
}
46+
47+
// aliasesCmd lists configured command aliases.
48+
var aliasesCmd = &cobra.Command{
49+
Use: "aliases",
50+
Short: "List all command aliases (built-in and configured)",
51+
Long: `Display all command aliases including:
52+
- Built-in aliases: Native command shortcuts (e.g., tf → terraform)
53+
- Configured aliases: User-defined aliases from atmos.yaml`,
54+
Example: "atmos list aliases\n" +
55+
"atmos list aliases --format json\n" +
56+
"atmos list aliases --format yaml\n" +
57+
"atmos list aliases --columns alias,command\n" +
58+
"atmos list aliases --sort type:asc,alias:asc",
59+
Args: cobra.NoArgs,
60+
RunE: executeListAliases,
61+
}
62+
63+
func init() {
64+
// Create parser with aliases-specific flags using flag wrappers.
65+
aliasesParser = NewListParser(
66+
WithFormatFlag,
67+
WithAliasesColumnsFlag,
68+
WithSortFlag,
69+
)
70+
71+
// Register flags.
72+
aliasesParser.RegisterFlags(aliasesCmd)
73+
74+
// Bind flags to Viper for environment variable support.
75+
if err := aliasesParser.BindToViper(viper.GetViper()); err != nil {
76+
panic(err)
77+
}
78+
}
79+
80+
// executeListAliases runs the list aliases command.
81+
func executeListAliases(cmd *cobra.Command, args []string) error {
82+
// Parse flags using StandardParser with Viper precedence.
83+
v := viper.GetViper()
84+
if err := aliasesParser.BindFlagsToViper(cmd, v); err != nil {
85+
return err
86+
}
87+
88+
opts := &AliasesOptions{
89+
Flags: flags.ParseGlobalFlags(cmd, v),
90+
Format: v.GetString("format"),
91+
Columns: v.GetStringSlice("columns"),
92+
Sort: v.GetString("sort"),
93+
}
94+
95+
// Get root command to collect built-in aliases.
96+
rootCmd := cmd.Root()
97+
98+
return executeListAliasesWithOptions(opts, rootCmd)
99+
}
100+
101+
func executeListAliasesWithOptions(opts *AliasesOptions, rootCmd *cobra.Command) error {
102+
// Load atmos configuration with global flags.
103+
configAndStacksInfo := buildConfigAndStacksInfo(&opts.Flags)
104+
atmosConfig, err := config.InitCliConfig(configAndStacksInfo, false)
105+
if err != nil {
106+
return fmt.Errorf(errUtils.ErrWrapFormat, errUtils.ErrFailedToInitializeAtmosConfig, err)
107+
}
108+
109+
// Collect all aliases.
110+
allAliases := collectAllAliases(rootCmd, atmosConfig.CommandAliases)
111+
112+
if len(allAliases) == 0 {
113+
ui.Info("No aliases found")
114+
return nil
115+
}
116+
117+
// Convert aliases to []map[string]any for renderer.
118+
data := aliasesToData(allAliases)
119+
120+
// Build columns (use opts.Columns or default).
121+
columns := getAliasColumns(opts.Columns)
122+
123+
// Build column selector.
124+
selector, err := column.NewSelector(columns, column.BuildColumnFuncMap())
125+
if err != nil {
126+
return fmt.Errorf(errUtils.ErrWrapFormat, errUtils.ErrCreateColumnSelector, err)
127+
}
128+
129+
// Build sorters.
130+
sorters, err := buildAliasSorters(opts.Sort)
131+
if err != nil {
132+
return err
133+
}
134+
135+
// Determine output format.
136+
outputFormat := format.Format(opts.Format)
137+
138+
// Create renderer.
139+
r := renderer.New(nil, selector, sorters, outputFormat, "")
140+
141+
// For table format with TTY, use RenderToString and add footer.
142+
if outputFormat == "" || outputFormat == format.FormatTable {
143+
term := terminal.New()
144+
if term.IsTTY(terminal.Stdout) {
145+
// Render to string, add footer, then output.
146+
output, err := r.RenderToString(data)
147+
if err != nil {
148+
return err
149+
}
150+
footer := buildAliasFooter(allAliases)
151+
ui.Write(output + footer)
152+
return nil
153+
}
154+
}
155+
156+
// For all other cases, use standard render.
157+
return r.Render(data)
158+
}
159+
160+
// collectAllAliases gathers both built-in and configured aliases.
161+
func collectAllAliases(rootCmd *cobra.Command, configuredAliases schema.CommandAliases) []AliasInfo {
162+
var allAliases []AliasInfo
163+
164+
// Collect built-in aliases from command tree.
165+
builtInAliases := collectBuiltInAliases(rootCmd, "")
166+
allAliases = append(allAliases, builtInAliases...)
167+
168+
// Collect configured aliases.
169+
for alias, command := range configuredAliases {
170+
allAliases = append(allAliases, AliasInfo{
171+
Alias: alias,
172+
Command: command,
173+
Type: aliasTypeConfigured,
174+
})
175+
}
176+
177+
// Sort alphabetically by alias name for easy discovery.
178+
sort.Slice(allAliases, func(i, j int) bool {
179+
return allAliases[i].Alias < allAliases[j].Alias
180+
})
181+
182+
return allAliases
183+
}
184+
185+
// stripRootPrefix removes the root command name prefix from a path.
186+
// Example: "atmos describe dependents" -> "describe dependents".
187+
func stripRootPrefix(path, rootName string) string {
188+
prefix := rootName + " "
189+
if strings.HasPrefix(path, prefix) {
190+
return strings.TrimPrefix(path, prefix)
191+
}
192+
return path
193+
}
194+
195+
// collectCommandAliases collects aliases for a single command.
196+
func collectCommandAliases(cmd *cobra.Command, parentPath, cmdPath, rootName string) []AliasInfo {
197+
var aliases []AliasInfo
198+
199+
for _, alias := range cmd.Aliases {
200+
// Build the alias path (replace command name with alias in path).
201+
// For top-level commands: just the alias (e.g., "tf" for terraform).
202+
// For nested commands: parent path (without root) + alias (e.g., "describe dependants").
203+
aliasPath := alias
204+
if parentPath != rootName {
205+
parentPathWithoutRoot := stripRootPrefix(parentPath, rootName)
206+
aliasPath = parentPathWithoutRoot + " " + alias
207+
}
208+
209+
// Build command path without root command name for display.
210+
displayCmdPath := stripRootPrefix(cmdPath, rootName)
211+
212+
aliases = append(aliases, AliasInfo{
213+
Alias: aliasPath,
214+
Command: displayCmdPath,
215+
Type: aliasTypeBuiltIn,
216+
})
217+
}
218+
219+
return aliases
220+
}
221+
222+
// collectBuiltInAliases recursively collects Cobra command aliases from the command tree.
223+
// It skips the root command name when building paths so aliases like "tf" map to "terraform", not "atmos terraform".
224+
func collectBuiltInAliases(cmd *cobra.Command, parentPath string) []AliasInfo {
225+
var aliases []AliasInfo
226+
227+
// Build the full command path (excluding root command name for display).
228+
cmdPath := cmd.Name()
229+
if parentPath != "" {
230+
cmdPath = parentPath + " " + cmd.Name()
231+
}
232+
233+
// Collect aliases for this command (skip root command itself).
234+
if parentPath != "" {
235+
rootName := cmd.Root().Name()
236+
aliases = collectCommandAliases(cmd, parentPath, cmdPath, rootName)
237+
}
238+
239+
// Recursively collect from subcommands.
240+
for _, subCmd := range cmd.Commands() {
241+
// Skip help command and hidden commands.
242+
if subCmd.Name() == "help" || subCmd.Hidden {
243+
continue
244+
}
245+
subAliases := collectBuiltInAliases(subCmd, cmdPath)
246+
aliases = append(aliases, subAliases...)
247+
}
248+
249+
return aliases
250+
}
251+
252+
// aliasesToData converts AliasInfo slice to []map[string]any for the renderer.
253+
func aliasesToData(aliases []AliasInfo) []map[string]any {
254+
data := make([]map[string]any, 0, len(aliases))
255+
for _, alias := range aliases {
256+
data = append(data, map[string]any{
257+
"alias": alias.Alias,
258+
"command": alias.Command,
259+
"type": alias.Type,
260+
})
261+
}
262+
return data
263+
}
264+
265+
// getAliasColumns returns column configs, using custom columns if specified.
266+
func getAliasColumns(customColumns []string) []column.Config {
267+
// Default columns.
268+
defaultColumns := []column.Config{
269+
{Name: "Alias", Value: "{{ .alias }}"},
270+
{Name: "Command", Value: "{{ .command }}"},
271+
{Name: "Type", Value: "{{ .type }}"},
272+
}
273+
274+
if len(customColumns) == 0 {
275+
return defaultColumns
276+
}
277+
278+
// Map column names to configs.
279+
columnMap := map[string]column.Config{
280+
"alias": {Name: "Alias", Value: "{{ .alias }}"},
281+
"command": {Name: "Command", Value: "{{ .command }}"},
282+
"type": {Name: "Type", Value: "{{ .type }}"},
283+
}
284+
285+
var result []column.Config
286+
for _, name := range customColumns {
287+
if col, ok := columnMap[strings.ToLower(name)]; ok {
288+
result = append(result, col)
289+
}
290+
}
291+
292+
if len(result) == 0 {
293+
return defaultColumns
294+
}
295+
return result
296+
}
297+
298+
// buildAliasSorters builds sorters from sort specification.
299+
func buildAliasSorters(sortSpec string) ([]*listSort.Sorter, error) {
300+
if sortSpec == "" {
301+
// Default sort by alias name.
302+
return []*listSort.Sorter{listSort.NewSorter("Alias", listSort.Ascending)}, nil
303+
}
304+
return listSort.ParseSortSpec(sortSpec)
305+
}
306+
307+
// buildAliasFooter builds the footer showing alias counts.
308+
func buildAliasFooter(aliases []AliasInfo) string {
309+
styles := theme.GetCurrentStyles()
310+
311+
builtInCount := 0
312+
configuredCount := 0
313+
for _, alias := range aliases {
314+
if alias.Type == aliasTypeBuiltIn {
315+
builtInCount++
316+
} else {
317+
configuredCount++
318+
}
319+
}
320+
321+
footer := fmt.Sprintf("\n%d alias", len(aliases))
322+
if len(aliases) != 1 {
323+
footer += "es"
324+
}
325+
footer += fmt.Sprintf(" (%d built-in, %d configured)", builtInCount, configuredCount)
326+
327+
return styles.Footer.Render(footer) + "\n"
328+
}

0 commit comments

Comments
 (0)