Skip to content

Commit

Permalink
Merge branch 'main' into feature/dev-2821-atmos-terraform-help-should…
Browse files Browse the repository at this point in the history
…-also-show-terraform-help
  • Loading branch information
samtholiya authored Dec 16, 2024
2 parents f0b7b98 + b205a0d commit 922ba49
Show file tree
Hide file tree
Showing 26 changed files with 451 additions and 71 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@
# Nix
.envrc
.direnv/

.atmos/cache.yaml
6 changes: 6 additions & 0 deletions atmos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,9 @@ settings:
# If the source and destination lists have the same length, all items in the destination lists are
# deep-merged with all items in the source list.
list_merge_strategy: replace

version:
check:
enabled: true
timeout: 1000 # ms
frequency: 1h
63 changes: 53 additions & 10 deletions cmd/cmd_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path"
"strings"
"time"

"github.com/fatih/color"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -428,18 +429,60 @@ func printMessageForMissingAtmosConfig(cliConfig schema.CliConfiguration) {
u.PrintMessage("https://atmos.tools/quick-start\n")
}

// customHelpMessageToUpgradeToAtmosLatestRelease adds Atmos version info at the end of each help commnad
func customHelpMessageToUpgradeToAtmosLatestRelease(cmd *cobra.Command, args []string) {
originalHelpFunc(cmd, args)
// Check for the latest Atmos release on GitHub
// CheckForAtmosUpdateAndPrintMessage checks if a version update is needed and prints a message if a newer version is found.
// It loads the cache, decides if it's time to check for updates, compares the current version to the latest available release,
// and if newer, prints the update message. It also updates the cache's timestamp after printing.
func CheckForAtmosUpdateAndPrintMessage(cliConfig schema.CliConfiguration) {
// If version checking is disabled in the configuration, do nothing
if !cliConfig.Version.Check.Enabled {
return
}

// Load the cache
cacheCfg, err := cfg.LoadCache()
if err != nil {
u.LogWarning(cliConfig, fmt.Sprintf("Could not load cache: %s", err))
return
}

// Determine if it's time to check for updates based on frequency and last_checked
if !cfg.ShouldCheckForUpdates(cacheCfg.LastChecked, cliConfig.Version.Check.Frequency) {
// Not due for another check yet, so return without printing anything
return
}

// Get the latest Atmos release from GitHub
latestReleaseTag, err := u.GetLatestGitHubRepoRelease("cloudposse", "atmos")
if err == nil && latestReleaseTag != "" {
latestRelease := strings.TrimPrefix(latestReleaseTag, "v")
currentRelease := strings.TrimPrefix(version.Version, "v")
if latestRelease != currentRelease {
u.PrintMessageToUpgradeToAtmosLatestRelease(latestRelease)
}
if err != nil {
u.LogWarning(cliConfig, fmt.Sprintf("Failed to retrieve latest Atmos release info: %s", err))
return
}

if latestReleaseTag == "" {
u.LogWarning(cliConfig, "No release information available")
return
}

// Trim "v" prefix to compare versions
latestVersion := strings.TrimPrefix(latestReleaseTag, "v")
currentVersion := strings.TrimPrefix(version.Version, "v")

// If the versions differ, print the update message
if latestVersion != currentVersion {
u.PrintMessageToUpgradeToAtmosLatestRelease(latestVersion)
}

// Update the cache to mark the current timestamp
cacheCfg.LastChecked = time.Now().Unix()
if saveErr := cfg.SaveCache(cacheCfg); saveErr != nil {
u.LogWarning(cliConfig, fmt.Sprintf("Unable to save cache: %s", saveErr))

}
}

func customHelpMessageToUpgradeToAtmosLatestRelease(cmd *cobra.Command, args []string) {
originalHelpFunc(cmd, args)
CheckForAtmosUpdateAndPrintMessage(cliConfig)
}

// Check Atmos is version command
Expand Down
5 changes: 4 additions & 1 deletion cmd/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ var docsCmd = &cobra.Command{
u.LogErrorAndExit(schema.CliConfiguration{}, err)
}

fmt.Println(componentDocs)
if err := u.DisplayDocs(componentDocs, cliConfig.Settings.Docs.Pagination); err != nil {
u.LogErrorAndExit(schema.CliConfiguration{}, fmt.Errorf("failed to display documentation: %w", err))
}

return
}

Expand Down
15 changes: 2 additions & 13 deletions cmd/helmfile.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package cmd

import (
"strings"

"github.com/samber/lo"
"github.com/spf13/cobra"

e "github.com/cloudposse/atmos/internal/exec"
"github.com/cloudposse/atmos/pkg/schema"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/cloudposse/atmos/pkg/version"
)

// helmfileCmd represents the base command for all helmfile sub-commands
Expand All @@ -34,18 +31,10 @@ var helmfileCmd = &cobra.Command{
if err != nil {
u.LogErrorAndExit(schema.CliConfiguration{}, err)
}

// Check for the latest Atmos release on GitHub and print update message
latestReleaseTag, err := u.GetLatestGitHubRepoRelease("cloudposse", "atmos")
if err == nil && latestReleaseTag != "" {
latestRelease := strings.TrimPrefix(latestReleaseTag, "v")
currentRelease := strings.TrimPrefix(version.Version, "v")
if latestRelease != currentRelease {
u.PrintMessageToUpgradeToAtmosLatestRelease(latestRelease)
}
}
// Exit on help
if info.NeedHelp {
// Check for the latest Atmos release on GitHub and print update message
CheckForAtmosUpdateAndPrintMessage(cliConfig)
return
}
// Check Atmos configuration
Expand Down
30 changes: 16 additions & 14 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
u "github.com/cloudposse/atmos/pkg/utils"
)

var cliConfig schema.CliConfiguration

// originalHelpFunc holds Cobra's original help function to avoid recursion.
var originalHelpFunc func(*cobra.Command, []string)

Expand Down Expand Up @@ -72,14 +74,6 @@ func Execute() error {
Flags: cc.Bold,
})

// Save the original help function to prevent infinite recursion when overriding it.
// This allows us to call the original help functionality within our custom help function.
originalHelpFunc = RootCmd.HelpFunc()

// Override the help function with a custom one that adds an upgrade message after displaying help.
// This custom help function will call the original help function and then display the bordered message.
RootCmd.SetHelpFunc(customHelpMessageToUpgradeToAtmosLatestRelease)

// Check if the `help` flag is passed and print a styled Atmos logo to the terminal before printing the help
err := RootCmd.ParseFlags(os.Args)
if err != nil && errors.Is(err, pflag.ErrHelp) {
Expand All @@ -89,21 +83,29 @@ func Execute() error {
u.LogErrorAndExit(schema.CliConfiguration{}, err)
}
}

// InitCliConfig finds and merges CLI configurations in the following order:
// system dir, home dir, current dir, ENV vars, command-line arguments
// Here we need the custom commands from the config
cliConfig, err := cfg.InitCliConfig(schema.ConfigAndStacksInfo{}, false)
if err != nil && !errors.Is(err, cfg.NotFound) {
var initErr error
cliConfig, initErr = cfg.InitCliConfig(schema.ConfigAndStacksInfo{}, false)
if initErr != nil && !errors.Is(initErr, cfg.NotFound) {
if isVersionCommand() {
u.LogTrace(schema.CliConfiguration{}, fmt.Sprintf("warning: CLI configuration 'atmos.yaml' file not found. Error: %s", err))
u.LogTrace(schema.CliConfiguration{}, fmt.Sprintf("warning: CLI configuration 'atmos.yaml' file not found. Error: %s", initErr))
} else {
u.LogErrorAndExit(schema.CliConfiguration{}, err)
u.LogErrorAndExit(schema.CliConfiguration{}, initErr)
}
}

// Save the original help function to prevent infinite recursion when overriding it.
// This allows us to call the original help functionality within our custom help function.
originalHelpFunc = RootCmd.HelpFunc()

// Override the help function with a custom one that adds an upgrade message after displaying help.
// This custom help function will call the original help function and then display the bordered message.
RootCmd.SetHelpFunc(customHelpMessageToUpgradeToAtmosLatestRelease)

// If CLI configuration was found, process its custom commands and command aliases
if err == nil {
if initErr == nil {
err = processCustomCommands(cliConfig, cliConfig.Commands, RootCmd, true)
if err != nil {
u.LogErrorAndExit(schema.CliConfiguration{}, err)
Expand Down
15 changes: 3 additions & 12 deletions cmd/terraform.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package cmd

import (
"strings"

"github.com/samber/lo"
"github.com/spf13/cobra"

e "github.com/cloudposse/atmos/internal/exec"
"github.com/cloudposse/atmos/pkg/schema"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/cloudposse/atmos/pkg/version"
)

// terraformCmd represents the base command for all terraform sub-commands
Expand All @@ -35,17 +32,11 @@ var terraformCmd = &cobra.Command{
if err != nil {
u.LogErrorAndExit(schema.CliConfiguration{}, err)
}
// Check for the latest Atmos release on GitHub and print update message
latestReleaseTag, err := u.GetLatestGitHubRepoRelease("cloudposse", "atmos")
if err == nil && latestReleaseTag != "" {
latestRelease := strings.TrimPrefix(latestReleaseTag, "v")
currentRelease := strings.TrimPrefix(version.Version, "v")
if latestRelease != currentRelease {
u.PrintMessageToUpgradeToAtmosLatestRelease(latestRelease)
}
}

// Exit on help
if info.NeedHelp {
// Check for the latest Atmos release on GitHub and print update message
CheckForAtmosUpdateAndPrintMessage(cliConfig)
return
}
// Check Atmos configuration
Expand Down
31 changes: 24 additions & 7 deletions cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/cloudposse/atmos/pkg/version"
)

var checkFlag bool

var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the CLI version",
Expand All @@ -29,18 +31,33 @@ var versionCmd = &cobra.Command{
u.PrintMessage(fmt.Sprintf("\U0001F47D Atmos %s on %s/%s", version.Version, runtime.GOOS, runtime.GOARCH))
fmt.Println()

// Check for the latest Atmos release on GitHub
latestReleaseTag, err := u.GetLatestGitHubRepoRelease("cloudposse", "atmos")
if err == nil && latestReleaseTag != "" {
latestRelease := strings.TrimPrefix(latestReleaseTag, "v")
currentRelease := strings.TrimPrefix(version.Version, "v")
if latestRelease != currentRelease {
u.PrintMessageToUpgradeToAtmosLatestRelease(latestRelease)
if checkFlag {
// Check for the latest Atmos release on GitHub
latestReleaseTag, err := u.GetLatestGitHubRepoRelease("cloudposse", "atmos")
if err == nil && latestReleaseTag != "" {
if err != nil {
u.LogWarning(schema.CliConfiguration{}, fmt.Sprintf("Failed to check for updates: %v", err))
return
}
if latestReleaseTag == "" {
u.LogWarning(schema.CliConfiguration{}, "No release information available")
return
}
latestRelease := strings.TrimPrefix(latestReleaseTag, "v")
currentRelease := strings.TrimPrefix(version.Version, "v")
if latestRelease != currentRelease {
u.PrintMessageToUpgradeToAtmosLatestRelease(latestRelease)
}
}
return
}

// Check for the cache and print update message
CheckForAtmosUpdateAndPrintMessage(cliConfig)
},
}

func init() {
versionCmd.Flags().BoolVarP(&checkFlag, "check", "c", false, "Run additional checks after displaying version info")
RootCmd.AddCommand(versionCmd)
}
2 changes: 1 addition & 1 deletion examples/quick-start-advanced/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ARG GEODESIC_OS=debian
# https://atmos.tools/
# https://github.com/cloudposse/atmos
# https://github.com/cloudposse/atmos/releases
ARG ATMOS_VERSION=1.122.0
ARG ATMOS_VERSION=1.127.0

# Terraform: https://github.com/hashicorp/terraform/releases
ARG TF_VERSION=1.5.7
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/elewis787/boa v0.1.2
github.com/fatih/color v1.18.0
github.com/go-git/go-git/v5 v5.12.0
github.com/gofrs/flock v0.12.1
github.com/google/go-containerregistry v0.20.2
github.com/google/go-github/v59 v59.0.0
github.com/google/uuid v1.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,8 @@ github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
Expand Down
2 changes: 1 addition & 1 deletion internal/exec/atmos.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func ExecuteAtmosCmd() error {
if v2, ok := v.(map[string]any); ok {
if v3, ok := v2["components"].(map[string]any); ok {
if v4, ok := v3["terraform"].(map[string]any); ok {
return k, lo.Keys(v4)
return k, FilterAbstractComponents(v4)
}
// TODO: process 'helmfile' components and stacks.
// This will require checking the list of commands and filtering the stacks and components depending on the selected command.
Expand Down
30 changes: 30 additions & 0 deletions internal/exec/describe_component.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package exec

import (
"github.com/pkg/errors"
"github.com/samber/lo"
"github.com/spf13/cobra"

cfg "github.com/cloudposse/atmos/pkg/config"
Expand Down Expand Up @@ -83,3 +84,32 @@ func ExecuteDescribeComponent(

return configAndStacksInfo.ComponentSection, nil
}

// FilterAbstractComponents This function removes abstract components and returns the list of components
func FilterAbstractComponents(componentsMap map[string]any) []string {
if componentsMap == nil {
return []string{}
}
components := make([]string, 0)
for _, k := range lo.Keys(componentsMap) {
componentMap, ok := componentsMap[k].(map[string]any)
if !ok {
components = append(components, k)
continue
}

metadata, ok := componentMap["metadata"].(map[string]any)
if !ok {
components = append(components, k)
continue
}
if componentType, ok := metadata["type"].(string); ok && componentType == "abstract" {
continue
}
if componentEnabled, ok := metadata["enabled"].(bool); ok && !componentEnabled {
continue
}
components = append(components, k)
}
return components
}
4 changes: 4 additions & 0 deletions internal/exec/helmfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func ExecuteHelmfile(info schema.ConfigAndStacksInfo) error {
return nil
}

if info.SubCommand == "version" {
return ExecuteShellCommand(cliConfig, "helmfile", []string{info.SubCommand}, "", nil, false, info.RedirectStdErr)
}

info, err = ProcessStacks(cliConfig, info, true, true)
if err != nil {
return err
Expand Down
9 changes: 9 additions & 0 deletions internal/exec/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ func ExecuteTerraform(info schema.ConfigAndStacksInfo) error {
fmt.Println()
return nil
}
if info.SubCommand == "version" {
return ExecuteShellCommand(cliConfig,
"terraform",
[]string{info.SubCommand},
"",
nil,
false,
info.RedirectStdErr)
}

shouldProcessStacks := true
shouldCheckStack := true
Expand Down
Loading

0 comments on commit 922ba49

Please sign in to comment.