Skip to content

Commit 5bbf0b3

Browse files
committed
Merge origin/main: Support Atmos Toolchain for 3rd Party Tools (#1686)
Resolve conflicts: - cmd/auth/whoami.go: Add both data and flags imports - pkg/flags/global_registry_test.go: Merge both HEAD's NoOptDefVal tests and main's pager tests - tests/snapshots/TestCLICommands_atmos_auth_exec_--help.stdout.golden: Use main's wording - Delete obsolete cmd/auth_login_test.go and cmd/auth_shell_test.go
2 parents 5d12ef0 + 3292013 commit 5bbf0b3

File tree

471 files changed

+49417
-651
lines changed

Some content is hidden

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

471 files changed

+49417
-651
lines changed

.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,23 @@ tools/gomodcheck/.gomodcheck
9393
# Claude Code and Hive Mind development tools
9494
.claude-flow/
9595
.hive-mind/
96+
.claude/plans/
9697

9798
performance-optimization/
99+
node_modules
100+
101+
# Toolchain installed tools
102+
.tools/
103+
toolchain/.tools/
104+
toolchain/.tool-versions
98105

99106
# Merge conflict artifacts and backup files
100107
*.orig
101108
*.rej
102109
.scratch
103110
*.bak
111+
*.bak[0-9]
112+
*.bak[0-9][0-9]
104113
*.go.bak
105114
**/*.go.bak
106115

@@ -110,4 +119,10 @@ TEST_FAILURE_ANALYSIS.md
110119
scratch/
111120
COVERAGE_SUMMARY.md
112121
TESTABILITY_CHANGES_SUMMARY.md
122+
123+
# Conductor scratch/temporary files
124+
*.log
125+
*_progress.txt
126+
*_summary.txt
127+
reply_to_*.sh
113128
*.ansi

CLAUDE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,14 @@ ALWAYS build the website after documentation changes: `cd website && npm run bui
316316
### Git (MANDATORY)
317317
Don't commit: todos, research, scratch files. Do commit: code, tests, requested docs, schemas. Update `.gitignore` for patterns only.
318318

319+
**NEVER run destructive git commands without explicit user confirmation:**
320+
- `git reset HEAD` or `git reset --hard` - discards staged/committed changes
321+
- `git checkout HEAD -- .` or `git checkout -- .` - discards all working changes
322+
- `git clean -fd` - deletes untracked files
323+
- `git stash drop` - permanently deletes stashed changes
324+
325+
Always ask first: "This will discard uncommitted changes. Proceed? [y/N]"
326+
319327
### Test Coverage (MANDATORY)
320328
80% minimum (CodeCov enforced). All features need tests. `make testacc-coverage` for reports.
321329

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright 2020-2025 Cloud Posse, LLC
189+
Copyright 2020-2026 Cloud Posse, LLC
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

cmd/auth/whoami.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package auth
22

33
import (
44
"context"
5-
"encoding/json"
65
"fmt"
76
"os"
87
"strings"
@@ -17,6 +16,7 @@ import (
1716
authTypes "github.com/cloudposse/atmos/pkg/auth/types"
1817
cfg "github.com/cloudposse/atmos/pkg/config"
1918
"github.com/cloudposse/atmos/pkg/config/homedir"
19+
"github.com/cloudposse/atmos/pkg/data"
2020
"github.com/cloudposse/atmos/pkg/flags"
2121
log "github.com/cloudposse/atmos/pkg/logger"
2222
"github.com/cloudposse/atmos/pkg/perf"
@@ -222,13 +222,10 @@ func printWhoamiJSON(whoami *authTypes.WhoamiInfo) error {
222222
if whoami.Environment != nil {
223223
redactedWhoami.Environment = sanitizeEnvMap(whoami.Environment, homeDir)
224224
}
225-
jsonData, err := json.MarshalIndent(redactedWhoami, "", " ")
226-
if err != nil {
227-
errUtils.CheckErrorAndPrint(errUtils.ErrInvalidAuthConfig, "Failed to marshal JSON", "")
228-
return errUtils.ErrInvalidAuthConfig
229-
}
230-
fmt.Println(string(jsonData))
231-
return nil
225+
// Use data.WriteJSON() to write to the data channel (stdout) with proper I/O handling.
226+
// This ensures output goes through the I/O layer with automatic masking and respects
227+
// stream redirection in tests.
228+
return data.WriteJSON(redactedWhoami)
232229
}
233230

234231
func printWhoamiHuman(whoami *authTypes.WhoamiInfo, isValid bool) {

cmd/cmd_utils.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/cloudposse/atmos/pkg/auth/credentials"
2525
"github.com/cloudposse/atmos/pkg/auth/validation"
2626
cfg "github.com/cloudposse/atmos/pkg/config"
27+
"github.com/cloudposse/atmos/pkg/dependencies"
2728
envpkg "github.com/cloudposse/atmos/pkg/env"
2829
l "github.com/cloudposse/atmos/pkg/list"
2930
log "github.com/cloudposse/atmos/pkg/logger"
@@ -430,6 +431,26 @@ func executeCustomCommand(
430431
finalArgs = args
431432
}
432433

434+
// Resolve and install command dependencies
435+
resolver := dependencies.NewResolver(&atmosConfig)
436+
deps, err := resolver.ResolveCommandDependencies(commandConfig)
437+
if err != nil {
438+
errUtils.CheckErrorPrintAndExit(err, "", fmt.Sprintf("Failed to resolve dependencies for command '%s'", commandConfig.Name))
439+
}
440+
441+
if len(deps) > 0 {
442+
log.Debug("Installing command dependencies", "command", commandConfig.Name, "tools", deps)
443+
installer := dependencies.NewInstaller(&atmosConfig)
444+
if err := installer.EnsureTools(deps); err != nil {
445+
errUtils.CheckErrorPrintAndExit(err, "", fmt.Sprintf("Failed to install dependencies for command '%s'", commandConfig.Name))
446+
}
447+
448+
// Update PATH to include installed tools
449+
if err := dependencies.UpdatePathForTools(&atmosConfig, deps); err != nil {
450+
errUtils.CheckErrorPrintAndExit(err, "", fmt.Sprintf("Failed to update PATH for command '%s'", commandConfig.Name))
451+
}
452+
}
453+
433454
// Create auth manager if identity is specified for this custom command.
434455
// Check for --identity flag first (it overrides the config).
435456
var authManager auth.AuthManager
@@ -806,6 +827,31 @@ func isVersionCommand() bool {
806827
return len(os.Args) > 1 && (os.Args[1] == "version" || os.Args[1] == "--version")
807828
}
808829

830+
// isVersionManagementCommand checks if the current command is a version management command.
831+
// These commands should not trigger re-exec to avoid infinite loops.
832+
func isVersionManagementCommand(cmd *cobra.Command) bool {
833+
if cmd == nil {
834+
return false
835+
}
836+
837+
// Check the command hierarchy.
838+
cmdName := cmd.Name()
839+
840+
// Direct version subcommands that manage local installations (install, uninstall).
841+
// Note: "list" is excluded because it can reasonably work with --use-version
842+
// to list releases using a different Atmos version.
843+
if cmd.Parent() != nil && cmd.Parent().Name() == "version" {
844+
return cmdName == "install" || cmdName == "uninstall"
845+
}
846+
847+
// The version command itself (shows current version).
848+
if cmdName == "version" && cmd.Parent() != nil && cmd.Parent().Name() == "atmos" {
849+
return true
850+
}
851+
852+
return false
853+
}
854+
809855
// handleHelpRequest shows help content and exits only if the first argument is "help" or "--help" or "-h".
810856
func handleHelpRequest(cmd *cobra.Command, args []string) {
811857
if (len(args) > 0 && args[0] == "help") || Contains(args, "--help") || Contains(args, "-h") {

cmd/describe_affected_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ func TestDescribeAffected(t *testing.T) {
2626
t.Setenv("IDENTITY", "")
2727

2828
t.Chdir("../tests/fixtures/scenarios/basic")
29+
30+
// Disable authentication for this test to prevent validation errors.
31+
// Set both environment variable and viper value to ensure it's recognized.
32+
t.Setenv("ATMOS_IDENTITY", "false")
33+
viper.Set("identity", "false")
34+
2935
ctrl := gomock.NewController(t)
3036
defer ctrl.Finish()
3137

cmd/help_template.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,10 @@ func printDescription(w io.Writer, cmd *cobra.Command, styles *helpStyles) {
371371
// Use markdown rendering to respect terminal width and wrapping settings.
372372
// This ensures long descriptions wrap properly based on screen width.
373373
rendered := renderMarkdownDescription(desc)
374-
fmt.Fprintln(w, styles.commandDesc.Render(rendered))
374+
styled := styles.commandDesc.Render(rendered)
375+
// Lipgloss pads multi-line strings to uniform width. Trim trailing whitespace from each line.
376+
styled = ui.TrimLinesRight(styled)
377+
fmt.Fprintln(w, styled)
375378
fmt.Fprintln(w)
376379
}
377380

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
- Add a tool with version
2+
```
3+
$ atmos toolchain add <tool-name> <version>
4+
```
5+
6+
- Use a custom tool versions file
7+
```
8+
$ atmos toolchain add --file <path/to/.tool-versions> <tool-name> <version>
9+
```
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- Get the aliases configured
2+
```
3+
$ atmos toolchain aliases
4+
```
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- Delete everything from toolchain
2+
3+
```
4+
$ atmos toolchain clean
5+
```

0 commit comments

Comments
 (0)