From 2b85f6f2f692a9e9e8a0bd13e990d6054c9f1635 Mon Sep 17 00:00:00 2001 From: Andriy Knysh Date: Tue, 30 Apr 2024 12:54:14 -0400 Subject: [PATCH] Update `atmos describe affected` command. Update docs (#590) * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * updates * Apply suggestions from code review address comments Co-authored-by: Erik Osterman (CEO @ Cloud Posse) * Add Google Tag Manager --------- Co-authored-by: Erik Osterman (CEO @ Cloud Posse) --- cmd/atlantis_generate_repo_config.go | 6 + cmd/describe_affected.go | 5 +- examples/quick-start/Dockerfile | 4 +- .../atmos-manifest/1.0/atmos-manifest.json | 2 + examples/tests/atmos.yaml | 18 - .../rootfs/usr/local/etc/atmos/atmos.yaml | 18 - .../spacelift-and-backend-override-1.yaml | 2 +- examples/tests/stacks/orgs/cp/_defaults.yaml | 2 +- go.mod | 2 +- go.sum | 4 +- .../exec/atlantis_generate_repo_config.go | 25 +- internal/exec/describe_affected.go | 14 +- internal/exec/describe_affected_utils.go | 198 +++++++++-- internal/exec/describe_stacks.go | 16 +- internal/exec/utils.go | 6 +- .../atlantis_generate_repo_config_test.go | 1 + pkg/atlantis/atmos.yaml | 18 - pkg/aws/atmos.yaml | 18 - pkg/component/atmos.yaml | 18 - pkg/describe/atmos.yaml | 18 - pkg/describe/describe_affected_test.go | 4 +- pkg/generate/atmos.yaml | 18 - pkg/spacelift/atmos.yaml | 18 - pkg/utils/glob_utils.go | 1 - pkg/validate/atmos.yaml | 18 - pkg/vender/atmos.yaml | 18 - pkg/workflow/atmos.yaml | 18 - .../atlantis-generate-repo-config.mdx | 34 +- .../commands/describe/describe-affected.mdx | 90 +++-- .../components/remote-state-backend.md | 308 ++++++++++++++++++ .../core-concepts/components/remote-state.md | 14 +- .../docs/core-concepts/stacks/templating.md | 10 +- website/docs/integrations/atlantis.mdx | 2 +- .../github-actions/setup-atmos.md | 2 +- website/docs/integrations/terraform.md | 2 +- website/docusaurus.config.js | 8 +- website/package-lock.json | 43 +-- website/package.json | 9 +- .../atmos-manifest/1.0/atmos-manifest.json | 2 + 39 files changed, 671 insertions(+), 343 deletions(-) create mode 100644 website/docs/core-concepts/components/remote-state-backend.md diff --git a/cmd/atlantis_generate_repo_config.go b/cmd/atlantis_generate_repo_config.go index 9296b4726..7ce15081e 100644 --- a/cmd/atlantis_generate_repo_config.go +++ b/cmd/atlantis_generate_repo_config.go @@ -57,5 +57,11 @@ func init() { atlantisGenerateRepoConfigCmd.PersistentFlags().String("ssh-key", "", "Path to PEM-encoded private key to clone private repos using SSH: atmos atlantis generate repo-config --affected-only=true --ssh-key ") atlantisGenerateRepoConfigCmd.PersistentFlags().String("ssh-key-password", "", "Encryption password for the PEM-encoded private key if the key contains a password-encrypted PEM block: atmos atlantis generate repo-config --affected-only=true --ssh-key --ssh-key-password ") + atlantisGenerateCmd.PersistentFlags().Bool("clone-target-ref", false, "Clone the target reference with which to compare the current branch: "+ + "atmos atlantis generate repo-config --affected-only=true --clone-target-ref=true\n"+ + "The flag is only used when '--affected-only=true'\n"+ + "If set to 'false' (default), the target reference will be checked out instead\n"+ + "This requires that the target reference is already cloned by Git, and the information about it exists in the '.git' directory") + atlantisGenerateCmd.AddCommand(atlantisGenerateRepoConfigCmd) } diff --git a/cmd/describe_affected.go b/cmd/describe_affected.go index 7f83ecf7f..5fc4339d6 100644 --- a/cmd/describe_affected.go +++ b/cmd/describe_affected.go @@ -30,12 +30,15 @@ func init() { describeAffectedCmd.PersistentFlags().String("repo-path", "", "Filesystem path to the already cloned target repository with which to compare the current branch: atmos describe affected --repo-path ") describeAffectedCmd.PersistentFlags().String("ref", "", "Git reference with which to compare the current branch: atmos describe affected --ref refs/heads/main. Refer to https://git-scm.com/book/en/v2/Git-Internals-Git-References for more details") describeAffectedCmd.PersistentFlags().String("sha", "", "Git commit SHA with which to compare the current branch: atmos describe affected --sha 3a5eafeab90426bd82bf5899896b28cc0bab3073") - describeAffectedCmd.PersistentFlags().String("file", "", "Write the result to the file: atmos describe affected --ref refs/tags/v1.16.0 --file affected.json") + describeAffectedCmd.PersistentFlags().String("file", "", "Write the result to the file: atmos describe affected --ref refs/tags/v1.71.0 --file affected.json") describeAffectedCmd.PersistentFlags().String("format", "json", "The output format: atmos describe affected --format=json|yaml ('json' is default)") describeAffectedCmd.PersistentFlags().Bool("verbose", false, "Print more detailed output when cloning and checking out the Git repository: atmos describe affected --verbose=true") describeAffectedCmd.PersistentFlags().String("ssh-key", "", "Path to PEM-encoded private key to clone private repos using SSH: atmos describe affected --ssh-key ") describeAffectedCmd.PersistentFlags().String("ssh-key-password", "", "Encryption password for the PEM-encoded private key if the key contains a password-encrypted PEM block: atmos describe affected --ssh-key --ssh-key-password ") describeAffectedCmd.PersistentFlags().Bool("include-spacelift-admin-stacks", false, "Include the Spacelift admin stack of any stack that is affected by config changes: atmos describe affected --include-spacelift-admin-stacks=true") + describeAffectedCmd.PersistentFlags().Bool("clone-target-ref", false, "Clone the target reference with which to compare the current branch: atmos describe affected --clone-target-ref=true\n"+ + "If set to 'false' (default), the target reference will be checked out instead\n"+ + "This requires that the target reference is already cloned by Git, and the information about it exists in the '.git' directory") describeCmd.AddCommand(describeAffectedCmd) } diff --git a/examples/quick-start/Dockerfile b/examples/quick-start/Dockerfile index 6891f4e63..cabb8e746 100644 --- a/examples/quick-start/Dockerfile +++ b/examples/quick-start/Dockerfile @@ -6,10 +6,10 @@ ARG GEODESIC_OS=debian # https://atmos.tools/ # https://github.com/cloudposse/atmos # https://github.com/cloudposse/atmos/releases -ARG ATMOS_VERSION=1.70.0 +ARG ATMOS_VERSION=1.71.0 # Terraform: https://github.com/hashicorp/terraform/releases -ARG TF_VERSION=1.8.0 +ARG TF_VERSION=1.8.1 FROM cloudposse/geodesic:${GEODESIC_VERSION}-${GEODESIC_OS} diff --git a/examples/quick-start/stacks/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json b/examples/quick-start/stacks/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json index 445e9c7f2..c337407e0 100644 --- a/examples/quick-start/stacks/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json +++ b/examples/quick-start/stacks/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json @@ -437,6 +437,7 @@ "backend_type": { "type": "string", "enum": [ + "local", "s3", "remote", "vault", @@ -455,6 +456,7 @@ "remote_state_backend_type": { "type": "string", "enum": [ + "local", "s3", "remote", "vault", diff --git a/examples/tests/atmos.yaml b/examples/tests/atmos.yaml index 321c7a33c..ae550e158 100644 --- a/examples/tests/atmos.yaml +++ b/examples/tests/atmos.yaml @@ -125,24 +125,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/examples/tests/rootfs/usr/local/etc/atmos/atmos.yaml b/examples/tests/rootfs/usr/local/etc/atmos/atmos.yaml index fbe95303b..e10ebd063 100644 --- a/examples/tests/rootfs/usr/local/etc/atmos/atmos.yaml +++ b/examples/tests/rootfs/usr/local/etc/atmos/atmos.yaml @@ -118,24 +118,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/examples/tests/stacks/catalog/terraform/spacelift-and-backend-override-1.yaml b/examples/tests/stacks/catalog/terraform/spacelift-and-backend-override-1.yaml index f2a9fe15e..34b56a350 100644 --- a/examples/tests/stacks/catalog/terraform/spacelift-and-backend-override-1.yaml +++ b/examples/tests/stacks/catalog/terraform/spacelift-and-backend-override-1.yaml @@ -7,7 +7,7 @@ settings: terraform: vars: {} - backend_type: s3 # s3, remote, vault, static, azurerm, gcs, cloud + backend_type: s3 # s3, remote, vault, azurerm, gcs, cloud backend: s3: encrypt: true diff --git a/examples/tests/stacks/orgs/cp/_defaults.yaml b/examples/tests/stacks/orgs/cp/_defaults.yaml index fbee39c1f..a46aee6c4 100644 --- a/examples/tests/stacks/orgs/cp/_defaults.yaml +++ b/examples/tests/stacks/orgs/cp/_defaults.yaml @@ -12,7 +12,7 @@ terraform: region: "{{ .vars.region }}" terraform_workspace: "{{ .workspace }}" - backend_type: s3 # s3, remote, vault, static, azurerm, gcs, cloud + backend_type: s3 # s3, remote, vault, azurerm, gcs, cloud # https://developer.hashicorp.com/terraform/language/settings/backends/configuration backend: diff --git a/go.mod b/go.mod index 6c932a3d3..1718271a8 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/google/go-github/v59 v59.0.0 github.com/google/uuid v1.6.0 github.com/hairyhenderson/gomplate/v3 v3.11.7 - github.com/hashicorp/go-getter v1.7.3 + github.com/hashicorp/go-getter v1.7.4 github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl/v2 v2.20.1 github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72 diff --git a/go.sum b/go.sum index 507703aa5..d9125ad92 100644 --- a/go.sum +++ b/go.sum @@ -726,8 +726,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.3 h1:bN2+Fw9XPFvOCjB0UOevFIMICZ7G2XSQHzfvLUyOM5E= -github.com/hashicorp/go-getter v1.7.3/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-getter v1.7.4 h1:3yQjWuxICvSpYwqSayAdKRFcvBl1y/vogCxczWSmix0= +github.com/hashicorp/go-getter v1.7.4/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= diff --git a/internal/exec/atlantis_generate_repo_config.go b/internal/exec/atlantis_generate_repo_config.go index 5e4ed771e..90424f7b7 100644 --- a/internal/exec/atlantis_generate_repo_config.go +++ b/internal/exec/atlantis_generate_repo_config.go @@ -102,6 +102,11 @@ func ExecuteAtlantisGenerateRepoConfigCmd(cmd *cobra.Command, args []string) err // If the flag `--affected-only=true` is passed, find the affected components and stacks if affectedOnly { + cloneTargetRef, err := flags.GetBool("clone-target-ref") + if err != nil { + return err + } + return ExecuteAtlantisGenerateRepoConfigAffectedOnly( cliConfig, outputPath, @@ -113,6 +118,7 @@ func ExecuteAtlantisGenerateRepoConfigCmd(cmd *cobra.Command, args []string) err sshKeyPath, sshKeyPassword, verbose, + cloneTargetRef, ) } @@ -138,6 +144,7 @@ func ExecuteAtlantisGenerateRepoConfigAffectedOnly( sshKeyPath string, sshKeyPassword string, verbose bool, + cloneTargetRef bool, ) error { if repoPath != "" && (ref != "" || sha != "" || sshKeyPath != "" || sshKeyPassword != "") { return errors.New("if the '--repo-path' flag is specified, the '--ref', '--sha', '--ssh-key' and '--ssh-key-password' flags can't be used") @@ -146,10 +153,12 @@ func ExecuteAtlantisGenerateRepoConfigAffectedOnly( var affected []schema.Affected var err error - if repoPath == "" { - affected, err = ExecuteDescribeAffectedWithTargetRepoClone(cliConfig, ref, sha, sshKeyPath, sshKeyPassword, verbose, false) - } else { + if repoPath != "" { affected, err = ExecuteDescribeAffectedWithTargetRepoPath(cliConfig, repoPath, verbose, false) + } else if cloneTargetRef { + affected, err = ExecuteDescribeAffectedWithTargetRefClone(cliConfig, ref, sha, sshKeyPath, sshKeyPassword, verbose, false) + } else { + affected, err = ExecuteDescribeAffectedWithTargetRefCheckout(cliConfig, ref, sha, verbose, false) } if err != nil { @@ -319,6 +328,11 @@ func ExecuteAtlantisGenerateRepoConfig( return err } + // Base component is required to calculate terraform workspace for derived components + if terraformComponent != componentName { + context.BaseComponent = terraformComponent + } + configAndStacksInfo := schema.ConfigAndStacksInfo{ ComponentFromArg: componentName, Stack: stackConfigFileName, @@ -334,11 +348,6 @@ func ExecuteAtlantisGenerateRepoConfig( } // Calculate terraform workspace - // Base component is required to calculate terraform workspace for derived components - if terraformComponent != componentName { - context.BaseComponent = terraformComponent - } - workspace, err := BuildTerraformWorkspace(cliConfig, configAndStacksInfo) if err != nil { return err diff --git a/internal/exec/describe_affected.go b/internal/exec/describe_affected.go index 1f92a9e90..2ef7e5a8f 100644 --- a/internal/exec/describe_affected.go +++ b/internal/exec/describe_affected.go @@ -79,15 +79,23 @@ func ExecuteDescribeAffectedCmd(cmd *cobra.Command, args []string) error { return err } + cloneTargetRef, err := flags.GetBool("clone-target-ref") + if err != nil { + return err + } + if repoPath != "" && (ref != "" || sha != "" || sshKeyPath != "" || sshKeyPassword != "") { return errors.New("if the '--repo-path' flag is specified, the '--ref', '--sha', '--ssh-key' and '--ssh-key-password' flags can't be used") } var affected []schema.Affected - if repoPath == "" { - affected, err = ExecuteDescribeAffectedWithTargetRepoClone(cliConfig, ref, sha, sshKeyPath, sshKeyPassword, verbose, includeSpaceliftAdminStacks) - } else { + + if repoPath != "" { affected, err = ExecuteDescribeAffectedWithTargetRepoPath(cliConfig, repoPath, verbose, includeSpaceliftAdminStacks) + } else if cloneTargetRef { + affected, err = ExecuteDescribeAffectedWithTargetRefClone(cliConfig, ref, sha, sshKeyPath, sshKeyPassword, verbose, includeSpaceliftAdminStacks) + } else { + affected, err = ExecuteDescribeAffectedWithTargetRefCheckout(cliConfig, ref, sha, verbose, includeSpaceliftAdminStacks) } if err != nil { diff --git a/internal/exec/describe_affected_utils.go b/internal/exec/describe_affected_utils.go index 046729989..c4f8a89f7 100644 --- a/internal/exec/describe_affected_utils.go +++ b/internal/exec/describe_affected_utils.go @@ -1,6 +1,7 @@ package exec import ( + "errors" "fmt" "os" "path" @@ -15,7 +16,7 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport/ssh" "github.com/hashicorp/terraform-config-inspect/tfconfig" "github.com/mitchellh/mapstructure" - "github.com/pkg/errors" + cp "github.com/otiai10/copy" cfg "github.com/cloudposse/atmos/pkg/config" "github.com/cloudposse/atmos/pkg/schema" @@ -27,9 +28,9 @@ var ( remoteRepoIsNotGitRepoError = errors.New("the target remote repo is not a Git repository. Check that it was initialized and has '.git' folder") ) -// ExecuteDescribeAffectedWithTargetRepoClone clones the remote repo using `ref` or `sha`, and processes stack configs -// and returns a list of the affected Atmos components and stacks given two Git commits -func ExecuteDescribeAffectedWithTargetRepoClone( +// ExecuteDescribeAffectedWithTargetRefClone clones the remote reference, +// processes stack configs, and returns a list of the affected Atmos components and stacks given two Git commits +func ExecuteDescribeAffectedWithTargetRefClone( cliConfig schema.CliConfiguration, ref string, sha string, @@ -39,23 +40,29 @@ func ExecuteDescribeAffectedWithTargetRepoClone( includeSpaceliftAdminStacks bool, ) ([]schema.Affected, error) { - localRepo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{ + if verbose { + cliConfig.Logs.Level = u.LogLevelTrace + } + + localPath := "." + + localRepo, err := git.PlainOpenWithOptions(localPath, &git.PlainOpenOptions{ DetectDotGit: true, EnableDotGitCommonDir: false, }) if err != nil { - return nil, errors.Wrapf(err, "%v", localRepoIsNotGitRepoError) + return nil, errors.Join(err, localRepoIsNotGitRepoError) } // Get the Git config of the local repo localRepoConfig, err := localRepo.Config() if err != nil { - return nil, errors.Wrapf(err, "%v", localRepoIsNotGitRepoError) + return nil, errors.Join(err, localRepoIsNotGitRepoError) } localRepoWorktree, err := localRepo.Worktree() if err != nil { - return nil, errors.Wrapf(err, "%v", localRepoIsNotGitRepoError) + return nil, errors.Join(err, localRepoIsNotGitRepoError) } localRepoPath := localRepoWorktree.Filesystem.Root() @@ -109,9 +116,9 @@ func ExecuteDescribeAffectedWithTargetRepoClone( // If `ref` flag is not provided, it will clone the HEAD of the default branch if ref != "" { cloneOptions.ReferenceName = plumbing.ReferenceName(ref) - u.LogTrace(cliConfig, fmt.Sprintf("\nChecking out Git ref '%s' ...\n", ref)) + u.LogTrace(cliConfig, fmt.Sprintf("\nCloning Git ref '%s' ...\n", ref)) } else { - u.LogTrace(cliConfig, "\nChecking out the HEAD of the default branch ...\n") + u.LogTrace(cliConfig, "\nCloned the HEAD of the default branch ...\n") } if verbose { @@ -151,9 +158,9 @@ func ExecuteDescribeAffectedWithTargetRepoClone( } if ref != "" { - u.LogTrace(cliConfig, fmt.Sprintf("\nChecked out Git ref '%s'\n", ref)) + u.LogTrace(cliConfig, fmt.Sprintf("\nCloned Git ref '%s'\n", ref)) } else { - u.LogTrace(cliConfig, fmt.Sprintf("\nChecked out Git ref '%s'\n", remoteRepoHead.Name())) + u.LogTrace(cliConfig, fmt.Sprintf("\nCloned Git ref '%s'\n", remoteRepoHead.Name())) } // Check if a commit SHA was provided and checkout the repo at that commit SHA @@ -188,51 +195,196 @@ func ExecuteDescribeAffectedWithTargetRepoClone( return affected, nil } +// ExecuteDescribeAffectedWithTargetRefCheckout checks out the target reference, +// processes stack configs, and returns a list of the affected Atmos components and stacks given two Git commits +func ExecuteDescribeAffectedWithTargetRefCheckout( + cliConfig schema.CliConfiguration, + ref string, + sha string, + verbose bool, + includeSpaceliftAdminStacks bool, +) ([]schema.Affected, error) { + + if verbose { + cliConfig.Logs.Level = u.LogLevelTrace + } + + localPath := "." + + localRepo, err := git.PlainOpenWithOptions(localPath, &git.PlainOpenOptions{ + DetectDotGit: true, + EnableDotGitCommonDir: false, + }) + if err != nil { + return nil, errors.Join(err, localRepoIsNotGitRepoError) + } + + // Check the Git config of the local repo + _, err = localRepo.Config() + if err != nil { + return nil, errors.Join(err, localRepoIsNotGitRepoError) + } + + localRepoWorktree, err := localRepo.Worktree() + if err != nil { + return nil, errors.Join(err, localRepoIsNotGitRepoError) + } + + localRepoPath := localRepoWorktree.Filesystem.Root() + + // Create a temp dir for the target ref + tempDir, err := os.MkdirTemp("", strconv.FormatInt(time.Now().Unix(), 10)) + if err != nil { + return nil, err + } + + defer removeTempDir(cliConfig, tempDir) + + // Copy the local repo into the temp directory + u.LogTrace(cliConfig, fmt.Sprintf("\nCopying the local repo into the temp directory '%s' ...", tempDir)) + + copyOptions := cp.Options{ + PreserveTimes: false, + PreserveOwner: false, + // Skip specifies which files should be skipped + Skip: func(srcInfo os.FileInfo, src, dest string) (bool, error) { + if strings.Contains(src, "node_modules") { + return true, nil + } + return false, nil + }, + } + + if err = cp.Copy(localRepoPath, tempDir, copyOptions); err != nil { + return nil, err + } + + u.LogTrace(cliConfig, fmt.Sprintf("Copied the local repo into the temp directory '%s'\n", tempDir)) + + remoteRepo, err := git.PlainOpenWithOptions(tempDir, &git.PlainOpenOptions{ + DetectDotGit: false, + EnableDotGitCommonDir: false, + }) + if err != nil { + return nil, errors.Join(err, remoteRepoIsNotGitRepoError) + } + + // Check the Git config of the target ref + _, err = remoteRepo.Config() + if err != nil { + return nil, errors.Join(err, remoteRepoIsNotGitRepoError) + } + + if sha != "" { + u.LogTrace(cliConfig, fmt.Sprintf("\nChecking out commit SHA '%s' ...\n", sha)) + + w, err := remoteRepo.Worktree() + if err != nil { + return nil, err + } + + checkoutOptions := git.CheckoutOptions{ + Hash: plumbing.NewHash(sha), + Create: false, + Force: true, + Keep: false, + } + + err = w.Checkout(&checkoutOptions) + if err != nil { + return nil, err + } + + u.LogTrace(cliConfig, fmt.Sprintf("Checked out commit SHA '%s'\n", sha)) + } else { + // If `ref` is not provided, use the HEAD of the remote origin + if ref == "" { + ref = "refs/remotes/origin/HEAD" + } + + u.LogTrace(cliConfig, fmt.Sprintf("\nChecking out Git ref '%s' ...", ref)) + + w, err := remoteRepo.Worktree() + if err != nil { + return nil, err + } + + checkoutOptions := git.CheckoutOptions{ + Branch: plumbing.ReferenceName(ref), + Create: false, + Force: true, + Keep: false, + } + + err = w.Checkout(&checkoutOptions) + if err != nil { + if strings.Contains(err.Error(), "reference not found") { + errorMessage := fmt.Sprintf("the Git ref '%s' does not exist on the local filesystem"+ + "\nmake sure it's correct and was cloned by Git from the remote, or use the '--clone-target-ref=true' flag to clone it"+ + "\nrefer to https://atmos.tools/cli/commands/describe/affected for more details", ref) + err = errors.New(errorMessage) + } + return nil, err + } + + u.LogTrace(cliConfig, fmt.Sprintf("Checked out Git ref '%s'\n", ref)) + } + + affected, err := executeDescribeAffected(cliConfig, localRepoPath, tempDir, localRepo, remoteRepo, verbose, includeSpaceliftAdminStacks) + if err != nil { + return nil, err + } + + return affected, nil +} + // ExecuteDescribeAffectedWithTargetRepoPath uses `repo-path` to access the target repo, and processes stack configs // and returns a list of the affected Atmos components and stacks given two Git commits func ExecuteDescribeAffectedWithTargetRepoPath( cliConfig schema.CliConfiguration, - repoPath string, + targetRefPath string, verbose bool, includeSpaceliftAdminStacks bool, ) ([]schema.Affected, error) { - localRepo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{ + localPath := "." + + localRepo, err := git.PlainOpenWithOptions(localPath, &git.PlainOpenOptions{ DetectDotGit: true, EnableDotGitCommonDir: false, }) if err != nil { - return nil, errors.Wrapf(err, "%v", localRepoIsNotGitRepoError) + return nil, errors.Join(err, localRepoIsNotGitRepoError) } // Check the Git config of the local repo _, err = localRepo.Config() if err != nil { - return nil, errors.Wrapf(err, "%v", localRepoIsNotGitRepoError) + return nil, errors.Join(err, localRepoIsNotGitRepoError) } localRepoWorktree, err := localRepo.Worktree() if err != nil { - return nil, errors.Wrapf(err, "%v", localRepoIsNotGitRepoError) + return nil, errors.Join(err, localRepoIsNotGitRepoError) } localRepoPath := localRepoWorktree.Filesystem.Root() - remoteRepo, err := git.PlainOpenWithOptions(repoPath, &git.PlainOpenOptions{ + remoteRepo, err := git.PlainOpenWithOptions(targetRefPath, &git.PlainOpenOptions{ DetectDotGit: false, EnableDotGitCommonDir: false, }) if err != nil { - return nil, errors.Wrapf(err, "%v", remoteRepoIsNotGitRepoError) + return nil, errors.Join(err, remoteRepoIsNotGitRepoError) } // Check the Git config of the remote target repo _, err = remoteRepo.Config() if err != nil { - return nil, errors.Wrapf(err, "%v", remoteRepoIsNotGitRepoError) + return nil, errors.Join(err, remoteRepoIsNotGitRepoError) } - affected, err := executeDescribeAffected(cliConfig, localRepoPath, repoPath, localRepo, remoteRepo, verbose, includeSpaceliftAdminStacks) + affected, err := executeDescribeAffected(cliConfig, localRepoPath, targetRefPath, localRepo, remoteRepo, verbose, includeSpaceliftAdminStacks) if err != nil { return nil, err } @@ -357,13 +509,13 @@ func executeDescribeAffected( changedFiles = append(changedFiles, fileStat.Name) } - u.LogTrace(cliConfig, "") - affected, err := findAffected(currentStacks, remoteStacks, cliConfig, changedFiles, includeSpaceliftAdminStacks) if err != nil { return nil, err } + u.LogTrace(cliConfig, "\nAffected components and stacks:\n") + return affected, nil } diff --git a/internal/exec/describe_stacks.go b/internal/exec/describe_stacks.go index b32ab2c05..32fd30e3a 100644 --- a/internal/exec/describe_stacks.go +++ b/internal/exec/describe_stacks.go @@ -200,10 +200,6 @@ func ExecuteDescribeStacks( }, } - configAndStacksInfo.ComponentSection["atmos_component"] = componentName - configAndStacksInfo.ComponentSection["atmos_stack"] = stackName - configAndStacksInfo.ComponentSection["atmos_stack_file"] = stackFileName - if comp, ok := configAndStacksInfo.ComponentSection["component"].(string); !ok || comp == "" { configAndStacksInfo.ComponentSection["component"] = componentName } @@ -235,6 +231,10 @@ func ExecuteDescribeStacks( finalStacksMap[stackName] = make(map[string]any) } + configAndStacksInfo.ComponentSection["atmos_component"] = componentName + configAndStacksInfo.ComponentSection["atmos_stack"] = stackName + configAndStacksInfo.ComponentSection["atmos_stack_file"] = stackFileName + if len(components) == 0 || u.SliceContainsString(components, componentName) || u.SliceContainsString(derivedComponents, componentName) { if !u.MapKeyExists(finalStacksMap[stackName].(map[string]any), "components") { finalStacksMap[stackName].(map[string]any)["components"] = make(map[string]any) @@ -374,10 +374,6 @@ func ExecuteDescribeStacks( }, } - configAndStacksInfo.ComponentSection["atmos_component"] = componentName - configAndStacksInfo.ComponentSection["atmos_stack"] = stackName - configAndStacksInfo.ComponentSection["atmos_stack_file"] = stackFileName - if comp, ok := configAndStacksInfo.ComponentSection["component"].(string); !ok || comp == "" { configAndStacksInfo.ComponentSection["component"] = componentName } @@ -409,6 +405,10 @@ func ExecuteDescribeStacks( finalStacksMap[stackName] = make(map[string]any) } + configAndStacksInfo.ComponentSection["atmos_component"] = componentName + configAndStacksInfo.ComponentSection["atmos_stack"] = stackName + configAndStacksInfo.ComponentSection["atmos_stack_file"] = stackFileName + if len(components) == 0 || u.SliceContainsString(components, componentName) || u.SliceContainsString(derivedComponents, componentName) { if !u.MapKeyExists(finalStacksMap[stackName].(map[string]any), "components") { finalStacksMap[stackName].(map[string]any)["components"] = make(map[string]any) diff --git a/internal/exec/utils.go b/internal/exec/utils.go index e419db7ce..57eace027 100644 --- a/internal/exec/utils.go +++ b/internal/exec/utils.go @@ -311,7 +311,6 @@ func ProcessStacks( return configAndStacksInfo, err } - configAndStacksInfo.ComponentEnvList = u.ConvertEnvVars(configAndStacksInfo.ComponentEnvSection) configAndStacksInfo.StackFile = configAndStacksInfo.Stack // Process context @@ -345,8 +344,6 @@ func ProcessStacks( continue } - configAndStacksInfo.ComponentEnvList = u.ConvertEnvVars(configAndStacksInfo.ComponentEnvSection) - if cliConfig.Stacks.NameTemplate != "" { tmpl, err2 := u.ProcessTmpl("name-template", cliConfig.Stacks.NameTemplate, configAndStacksInfo.ComponentSection, false) if err2 != nil { @@ -602,6 +599,9 @@ func ProcessStacks( configAndStacksInfo.ComponentBackendType = i } + // Process the ENV variables from the `env` section + configAndStacksInfo.ComponentEnvList = u.ConvertEnvVars(configAndStacksInfo.ComponentEnvSection) + return configAndStacksInfo, nil } diff --git a/pkg/atlantis/atlantis_generate_repo_config_test.go b/pkg/atlantis/atlantis_generate_repo_config_test.go index ff99bcf50..ecafe72dd 100644 --- a/pkg/atlantis/atlantis_generate_repo_config_test.go +++ b/pkg/atlantis/atlantis_generate_repo_config_test.go @@ -87,6 +87,7 @@ func TestExecuteAtlantisGenerateRepoConfigAffectedOnly(t *testing.T) { "", "", "", + false, true, ) diff --git a/pkg/atlantis/atmos.yaml b/pkg/atlantis/atmos.yaml index b2de0b448..d497575b1 100644 --- a/pkg/atlantis/atmos.yaml +++ b/pkg/atlantis/atmos.yaml @@ -116,24 +116,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/pkg/aws/atmos.yaml b/pkg/aws/atmos.yaml index d350dcd70..21d2cc59a 100644 --- a/pkg/aws/atmos.yaml +++ b/pkg/aws/atmos.yaml @@ -116,24 +116,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/pkg/component/atmos.yaml b/pkg/component/atmos.yaml index b2de0b448..d497575b1 100644 --- a/pkg/component/atmos.yaml +++ b/pkg/component/atmos.yaml @@ -116,24 +116,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/pkg/describe/atmos.yaml b/pkg/describe/atmos.yaml index b2de0b448..d497575b1 100644 --- a/pkg/describe/atmos.yaml +++ b/pkg/describe/atmos.yaml @@ -116,24 +116,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/pkg/describe/describe_affected_test.go b/pkg/describe/describe_affected_test.go index 2913e9c12..988201c6a 100644 --- a/pkg/describe/describe_affected_test.go +++ b/pkg/describe/describe_affected_test.go @@ -12,7 +12,7 @@ import ( "github.com/cloudposse/atmos/pkg/schema" ) -func TestDescribeAffectedWithTargetRepoClone(t *testing.T) { +func TestDescribeAffectedWithTargetRefClone(t *testing.T) { configAndStacksInfo := schema.ConfigAndStacksInfo{} cliConfig, err := cfg.InitCliConfig(configAndStacksInfo, true) @@ -28,7 +28,7 @@ func TestDescribeAffectedWithTargetRepoClone(t *testing.T) { ref := "refs/heads/master" sha := "" - affected, err := e.ExecuteDescribeAffectedWithTargetRepoClone(cliConfig, ref, sha, "", "", true, true) + affected, err := e.ExecuteDescribeAffectedWithTargetRefClone(cliConfig, ref, sha, "", "", true, true) assert.Nil(t, err) affectedYaml, err := yaml.Marshal(affected) diff --git a/pkg/generate/atmos.yaml b/pkg/generate/atmos.yaml index b2de0b448..d497575b1 100644 --- a/pkg/generate/atmos.yaml +++ b/pkg/generate/atmos.yaml @@ -116,24 +116,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/pkg/spacelift/atmos.yaml b/pkg/spacelift/atmos.yaml index b2de0b448..d497575b1 100644 --- a/pkg/spacelift/atmos.yaml +++ b/pkg/spacelift/atmos.yaml @@ -116,24 +116,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/pkg/utils/glob_utils.go b/pkg/utils/glob_utils.go index 3b3ad925b..902a6a0c3 100644 --- a/pkg/utils/glob_utils.go +++ b/pkg/utils/glob_utils.go @@ -52,7 +52,6 @@ func GetGlobMatches(pattern string) ([]string, error) { // assumes that both `pattern` and `name` are using the system's path // separator. If you can't be sure of that, use filepath.ToSlash() on both // `pattern` and `name`, and then use the Match() function instead. -// func PathMatch(pattern, name string) (bool, error) { return doublestar.PathMatch(pattern, name) } diff --git a/pkg/validate/atmos.yaml b/pkg/validate/atmos.yaml index b2de0b448..d497575b1 100644 --- a/pkg/validate/atmos.yaml +++ b/pkg/validate/atmos.yaml @@ -116,24 +116,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/pkg/vender/atmos.yaml b/pkg/vender/atmos.yaml index b2de0b448..d497575b1 100644 --- a/pkg/vender/atmos.yaml +++ b/pkg/vender/atmos.yaml @@ -116,24 +116,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/pkg/workflow/atmos.yaml b/pkg/workflow/atmos.yaml index b2de0b448..d497575b1 100644 --- a/pkg/workflow/atmos.yaml +++ b/pkg/workflow/atmos.yaml @@ -116,24 +116,6 @@ commands: steps: - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK - - name: play - description: This command plays games - steps: - - echo Playing... - # subcommands - commands: - - name: hello - description: This command says Hello world - steps: - - echo Hello world - - name: ping - description: This command plays ping-pong - # If 'verbose' is set to 'true', atmos will output some info messages to the console before executing the command's steps - # If 'verbose' is not defined, it implicitly defaults to 'false' - verbose: true - steps: - - echo Playing ping-pong... - - echo pong - name: show description: Execute 'show' commands # subcommands diff --git a/website/docs/cli/commands/atlantis/atlantis-generate-repo-config.mdx b/website/docs/cli/commands/atlantis/atlantis-generate-repo-config.mdx index 95ce595f5..c1dc5b785 100644 --- a/website/docs/cli/commands/atlantis/atlantis-generate-repo-config.mdx +++ b/website/docs/cli/commands/atlantis/atlantis-generate-repo-config.mdx @@ -5,6 +5,7 @@ sidebar_class_name: command id: generate-repo-config description: Use this command to generate a repository configuration for Atlantis. --- + import Screengrab from '@site/src/components/Screengrab' :::info Purpose @@ -13,7 +14,7 @@ Use this command to generate a repository configuration for Atlantis.
- +

@@ -64,24 +65,27 @@ atmos atlantis generate repo-config --affected-only=true --ref refs/tags/v1.2.0 atmos atlantis generate repo-config --affected-only=true --ssh-key atmos atlantis generate repo-config --affected-only=true --ssh-key --ssh-key-password + +atmos atlantis generate repo-config --affected-only=true --clone-target-ref=true ``` ## Flags -| Flag | Description | Required | -|:---------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------| -| `--config-template` | Atlantis config template name | no | -| `--project-template` | Atlantis project template name | no | -| `--output-path` | Output path to write `atlantis.yaml` file | no | -| `--stacks` | Generate Atlantis projects for the specified stacks only (comma-separated values) | no | -| `--components` | Generate Atlantis projects for the specified components only (comma-separated values) | no | -| `--affected-only` | Generate Atlantis projects only for the Atmos components changed
between two Git commits | no | -| `--ref` | [Git Reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References) with which to compare the current working branch | no | -| `--sha` | Git commit SHA with which to compare the current working branch | no | -| `--ssh-key` | Path to PEM-encoded private key to clone private repos using SSH | no | -| `--ssh-key-password` | Encryption password for the PEM-encoded private key if the key contains
a password-encrypted PEM block | no | -| `--repo-path` | Path to the already cloned target repository with which to compare the current branch.
Conflicts with `--ref`, `--sha`, `--ssh-key` and `--ssh-key-password` | no | -| `--verbose` | Print more detailed output when cloning and checking out the target
Git repository and processing the result | no | +| Flag | Description | Required | +|:---------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------| +| `--config-template` | Atlantis config template name | no | +| `--project-template` | Atlantis project template name | no | +| `--output-path` | Output path to write `atlantis.yaml` file | no | +| `--stacks` | Generate Atlantis projects for the specified stacks only (comma-separated values) | no | +| `--components` | Generate Atlantis projects for the specified components only (comma-separated values) | no | +| `--affected-only` | Generate Atlantis projects only for the Atmos components changed
between two Git commits | no | +| `--ref` | [Git Reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References) with which to compare the current working branch | no | +| `--sha` | Git commit SHA with which to compare the current working branch | no | +| `--ssh-key` | Path to PEM-encoded private key to clone private repos using SSH | no | +| `--ssh-key-password` | Encryption password for the PEM-encoded private key if the key contains
a password-encrypted PEM block | no | +| `--repo-path` | Path to the already cloned target repository with which to compare the current branch.
Conflicts with `--ref`, `--sha`, `--ssh-key` and `--ssh-key-password` | no | +| `--verbose` | Print more detailed output when cloning and checking out the target
Git repository and processing the result | no | +| `--clone-target-ref` | Clone the target reference with which to compare the current branch.
`atmos atlantis generate repo-config --affected-only=true --clone-target-ref=true`

The flag is only used when `--affected-only=true`

If set to `false` (default), the target reference will be checked out instead
This requires that the target reference is already cloned by Git,
and the information about it exists in the `.git` directory | no |
diff --git a/website/docs/cli/commands/describe/describe-affected.mdx b/website/docs/cli/commands/describe/describe-affected.mdx index f96c94a57..9d7c057e6 100644 --- a/website/docs/cli/commands/describe/describe-affected.mdx +++ b/website/docs/cli/commands/describe/describe-affected.mdx @@ -20,46 +20,64 @@ Use this command to show a list of the affected Atmos components and stacks give The command uses two different Git commits to produce a list of affected Atmos components and stacks. -For the first commit, the command assumes that the current repo root is a Git checkout. An error will be thrown if the current repo is not a Git -repository (the `.git/` folder does not exist or is configured incorrectly). +For the first commit, the command assumes that the current repo root is a Git checkout. An error will be thrown if the +current repo is not a Git repository (the `.git/` folder does not exist or is configured incorrectly). The second commit can be specified on the command line by using the `--ref` ([Git References](https://git-scm.com/book/en/v2/Git-Internals-Git-References)) or `--sha` (commit SHA) flags. - -Either `--ref` or `--sha` should be used. If both flags are provided at the same time, the command will first clone the remote branch pointed to by -the `--ref` flag and then checkout the Git commit pointed to by the `--sha` flag (`--sha` flag overrides `--ref` flag). +The `--sha` takes precedence over the `--ref` flag. :::tip -If the flags are not provided, the `ref` will be set automatically to the reference to the default branch (e.g. `main`) and the commit SHA -will point to the `HEAD` of the branch. +If the flags are not provided, the `ref` will be set automatically to the reference to the default branch +(`refs/remotes/origin/HEAD` Git ref, usually the `main` branch). ::: -If you specify the `--repo-path` flag with the path to the already cloned repository, the command will not clone the target -repository, but instead will use the already cloned one to compare the current branch with. In this case, the `--ref`, `--sha`, `--ssh-key` -and `--ssh-key-password` flags are not used, and an error will be thrown if the `--repo-path` flag and any of the `--ref`, `--sha`, `--ssh-key` -or `--ssh-key-password` flags are provided at the same time. - ## How does it work? The command performs the following: -- Cloning the target branch (`--ref`) or checking out the commit (`--sha`) of the remote target branch, or using the already cloned target repository - specified by the `--repo-path` flag +- If the `--repo-path` flag is passed, the command uses it as the path to the already cloned target repo with which to + compare the current working branch. I this case, the command will not clone and checkout the + target reference, but instead will use the already cloned one to compare the current branch with. In this case, the + `--ref`, `--sha`, `--ssh-key` and `--ssh-key-password` flags are not used, and an error will be thrown if the `--repo-path` + flag and any of the `--ref`, `--sha`, `--ssh-key` or `--ssh-key-password` flags are provided at the same time + +- Otherwise, if the `--clone-target-ref=true` flag is specified, the command clones (into a temp directory) the remote + target with which to compare the current working branch. If the `--ref` flag or the commit SHA flag `--sha` are provided, + the command uses them to clone and checkout the remote target. Otherwise, the `HEAD` of the remote origin is + used (`refs/remotes/origin/HEAD` Git ref, usually the `main` branch) + +- Otherwise, (if the `--repo-path` and `--clone-target-ref=true` flags are not passed), the command does not clone anything + from the remote origin, but instead just copies the current repo into a temp directory and checks out the target + reference with which to compare the current working branch. + If the `--ref` flag or the commit SHA flag `--sha` are + provided, the command uses them to check out. Otherwise, the `HEAD` of the remote origin is used + (`refs/remotes/origin/HEAD` Git ref, usually the `main` branch). + This requires that the target reference is already cloned by Git, and the information about it exists in + the `.git` directory (in case of using a non-default branch as the target, Git deep clone needs to be executed instead + of a shallow clone). + This is the recommended way to execute the `atmos describe affected` command since it allows + [working with private repositories](#working-with-private-repositories) without providing the SSH credentials + (`--ssh-key` and `--ssh-key-password` flags), since in this case Atmos does not access the remote origin and instead + just checks out the target reference (which is already on the local file system) -- Deep merging all stack configurations for both the current working branch and the remote target branch +- The command deep-merges all stack configurations from both sources: the current working branch and the target reference -- Looking for changes in the component directories +- The command searches for changes in the component directories -- Comparing each section of the stack configuration looking for differences +- The command compares each stack manifest section of the stack configurations from both sources looking for differences -- Outputting a JSON or YAML document consisting of a list of the affected components and stacks and what caused it to be affected +- And finally, the command outputs a JSON or YAML document consisting of a list of the affected components and stacks + and what caused it to be affected -Since Atmos first checks the component folders for changes, if it finds any affected files, it will mark all related components and stacks as -affected. Atmos will then skip evaluating those stacks for differences since we already know that they are affected. +Since Atmos first checks the component folders for changes, if it finds any affected files, it will mark all related +components and stacks as affected. Atmos will then skip evaluating the stacks for differences since it already +knows that they are affected.
:::tip Use our GitHub Action -Our [affected stacks](/integrations/github-actions/affected-stacks) GitHub Action provides a ready-to-go way to run `describe affected` and produce a GitHub matrix. +Our [affected stacks](/integrations/github-actions/affected-stacks) GitHub Action provides a ready-to-go way to run +`describe affected` and produce a GitHub matrix. ::: # @@ -93,6 +111,7 @@ atmos describe affected --ssh-key atmos describe affected --ssh-key --ssh-key-password atmos describe affected --repo-path atmos describe affected --include-spacelift-admin-stacks=true +atmos describe affected --clone-target-ref=true ``` @@ -181,17 +200,18 @@ Affected components and stacks: ## Flags -| Flag | Description | Required | -|:-----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------| -| `--ref` | [Git Reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References) with which to compare the current working branch | no | -| `--sha` | Git commit SHA with which to compare the current working branch | no | -| `--file` | If specified, write the result to the file | no | -| `--format` | Specify the output format: `json` or `yaml` (`json` is default) | no | -| `--ssh-key` | Path to PEM-encoded private key to clone private repos using SSH | no | -| `--ssh-key-password` | Encryption password for the PEM-encoded private key if the key contains
a password-encrypted PEM block | no | -| `--repo-path` | Path to the already cloned target repository with which to compare the current branch.
Conflicts with `--ref`, `--sha`, `--ssh-key` and `--ssh-key-password` | no | -| `--verbose` | Print more detailed output when cloning and checking out the target
Git repository and processing the result | no | -| `--include-spacelift-admin-stacks` | Include the Spacelift admin stack of any stack
that is affected by config changes | no | +| Flag | Description | Required | +|:-----------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------| +| `--ref` | [Git Reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References) with which to compare the current working branch | no | +| `--sha` | Git commit SHA with which to compare the current working branch | no | +| `--file` | If specified, write the result to the file | no | +| `--format` | Specify the output format: `json` or `yaml` (`json` is default) | no | +| `--ssh-key` | Path to PEM-encoded private key to clone private repos using SSH | no | +| `--ssh-key-password` | Encryption password for the PEM-encoded private key if the key contains
a password-encrypted PEM block | no | +| `--repo-path` | Path to the already cloned target repository with which to compare the current branch.
Conflicts with `--ref`, `--sha`, `--ssh-key` and `--ssh-key-password` | no | +| `--verbose` | Print more detailed output when cloning and checking out the target
Git repository and processing the result | no | +| `--include-spacelift-admin-stacks` | Include the Spacelift admin stack of any stack
that is affected by config changes | no | +| `--clone-target-ref` | Clone the target reference with which to compare the current branch.
`atmos describe affected --clone-target-ref=true`

If set to `false` (default), the target reference will be checked out instead
This requires that the target reference is already cloned by Git,
and the information about it exists in the `.git` directory | no | ## Output @@ -328,11 +348,11 @@ where: ```yaml title="stacks/orgs/cp/tenant1/_defaults.yaml" settings: spacelift: - # All Spacelift child stacks for the `tenant1` tenant are managed by the + # All Spacelift child stacks for the `tenant1` tenant are managed by the # `tenant1-ue2-prod-infrastructure-tenant1` Spacelift admin stack. - # The `admin_stack_selector` attribute is used to find the affected Spacelift + # The `admin_stack_selector` attribute is used to find the affected Spacelift # admin stack for each affected Atmos stack - # when executing the command + # when executing the command # `atmos describe affected --include-spacelift-admin-stacks=true` admin_stack_selector: component: infrastructure-tenant1 diff --git a/website/docs/core-concepts/components/remote-state-backend.md b/website/docs/core-concepts/components/remote-state-backend.md new file mode 100644 index 000000000..ba4c13d52 --- /dev/null +++ b/website/docs/core-concepts/components/remote-state-backend.md @@ -0,0 +1,308 @@ +--- +title: Remote State Backend +sidebar_position: 16 +sidebar_label: Remote State Backend +id: remote-state-backend +--- + +Atmos supports configuring [Terraform Backends](/core-concepts/components/terraform-backends) to define where +Terraform stores its [state](https://developer.hashicorp.com/terraform/language/state), +and [Remote State](/core-concepts/components/remote-state) to get the outputs +of a [Terraform component](/core-concepts/components), provisioned in the same or a +different [Atmos stack](/core-concepts/stacks), and use +the outputs as inputs to another Atmos component + +Atmos also supports Remote State Backends (in the `remote_state_backend` section), which can be used to configure the +following: + +- Override [Terraform Backend](/core-concepts/components/terraform-backends) configuration to access the + remote state of a component (e.g. override the IAM role to assume, which in this case can be a read-only role) + +- Configure a remote state of type `static` which can be used to provide configurations for + [Brownfield development](https://en.wikipedia.org/wiki/Brownfield_(software_development)) + +## Override Terraform Backend Configuration to Access Remote State + +Atmos supports the `remote_state_backend` section which can be used to provide configuration to access the remote state +of components. + +To access the remote state of components, you can override +any [Terraform Backend](/core-concepts/components/terraform-backends) +configuration in the `backend` section using the `remote_state_backend` section. The `remote_state_backend` section +is a first-class section, and it can be defined globally at any scope (organization, tenant, account, region), or per +component, and then deep-merged using [Atmos Component Inheritance](/core-concepts/components/inheritance). + +For example, let's suppose we have the following S3 backend configuration for the entire organization +(refer to [AWS S3 Backend](/core-concepts/components/terraform-backends#aws-s3-backend) for more details): + +```yaml title="stacks/orgs/acme/_defaults.yaml" +terraform: + backend_type: s3 + backend: + s3: + acl: "bucket-owner-full-control" + encrypt: true + bucket: "your-s3-bucket-name" + dynamodb_table: "your-dynamodb-table-name" + key: "terraform.tfstate" + region: "your-aws-region" + role_arn: "arn:aws:iam::xxxxxxxx:role/terraform-backend-read-write" +``` + +
+ +Let's say we also have a read-only IAM role, and we want to use it to access the remote state instead of the read-write +role, because accessing remote state is a read-only operation, and we don't want to give the role more permissions than +it requires - this is the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege). + +We can add the `remote_state_backend` and `remote_state_backend_type` to override the required attributes from the +`backend` section: + +```yaml title="stacks/orgs/acme/_defaults.yaml" +terraform: + backend_type: s3 # s3, remote, vault, azurerm, gcs, cloud + backend: + s3: + acl: "bucket-owner-full-control" + encrypt: true + bucket: "your-s3-bucket-name" + dynamodb_table: "your-dynamodb-table-name" + key: "terraform.tfstate" + region: "your-aws-region" + role_arn: "arn:aws:iam::xxxxxxxx:role/terraform-backend-read-write" + + remote_state_backend_type: s3 # s3, remote, vault, azurerm, gcs, cloud, static + remote_state_backend: + s3: + role_arn: "arn:aws:iam::xxxxxxxx:role/terraform-backend-read-only" + # Override the other attributes from the `backend.s3` section as needed +``` + +
+ +In the example above, we've overridden the `role_arn` attribute for the `s3` backend to use the read-only role when +accessing the remote state of all components. All other attributes will be taken from the `backend` section (Atmos +deep-merges the `remote_state_backend` section with the `backend` section). + +When working with Terraform backends and writing/updating the state, the `terraform-backend-read-write` role will be +used. But when reading the remote state of components, the `terraform-backend-read-only` role will be used. + +## Brownfield Considerations + +The term "brownfield" comes from urban planning and refers to the redevelopment of land that was previously used and may need cleaning or modification. As it relates to infrastructure, [Brownfield development](https://en.wikipedia.org/wiki/Brownfield_(software_development)) describes development and deployment of new software +systems in the presence of existing (legacy) software applications/systems. Anytime this happens, new software architectures must take into account and coexist with the existing software. + +In the context of Atmos, brownfield development involves adopting some new techniques, practices, and integrating them into +established systems. This can be challenging due to several factors: + +- **Legacy Systems**: These are older systems that are still in use and may be harder to change. They might not even be managed with Terraform, or if they are, they might not provide outputs that are easily accessible in a conventional manner. + +- **Complex Integrations**: Existing systems might leverage entirely different toolchains, such as CloudFormation, CDK, or Pulumi. Managing integrations and dependencies across toolchains is complicated. + + +- **Technical Debt**: Over time, systems naturally accumulate technical debt, which includes outdated code, inconsistent conventions or lack of naming conventions, and suboptimal decisions that were previously made for expediency. Addressing this technical debt is crucial when adopting Atmos, as it introduces practices that ensure your system remains maintainable and scalable. + + +## Brownfield Development in Atmos + +Atmos is easier for new organizations or "greenfield" environments because you need to architect Terraform according to +our best practices to get all the benefits of Atmos. For example, when using +our [Terraform components](https://github.com/cloudposse/terraform-aws-components), we frequently use +[Terraform Remote State](/core-concepts/components/remote-state) to retrieve the outputs from other components. +This works well when you use our components but less so when you operate in a "brownfield" environment, for example, +with an existing VPC, S3 bucket, or IAM role. + +But what happens if those things weren't provisioned by Atmos or predates your infrastructure? For this reason, we +support something we refer to as the `static` remote state backend. Using the static remote state backend, you can +populate a virtual state backend with the outputs as though it had been provisioned with Terraform. You can use this +technique anytime you want to use the remote state functionality in Atmos, but when the remote state was provisioned +elsewhere. + +In Atmos, brownfield development describes the process of configuring Atmos components and stacks for the +existing (already provisioned) resources, and working on and updating existing infrastructure rather than creating a new +one from scratch (which is known as "greenfield" development). The process respects the existing systems' constraints +while progressively introducing improvements and modern practices. This can ultimately lead to more robust, flexible, +and efficient systems. + +Atmos supports brownfield configuration by using the remote state of type `static`. + +## `static` Remote State for Brownfield Development + +Suppose that we need to provision +the [`vpc`](https://github.com/cloudposse/atmos/tree/master/examples/quick-start/components/terraform/vpc) +Terraform component and, instead of provisioning an S3 bucket for VPC Flow Logs, we want to use an existing bucket. + +The `vpc` Terraform component needs the outputs from the `vpc-flow-logs-bucket` Terraform component to +configure [VPC Flow Logs](https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html). + +Let's redesign the example with the `vpc` and `vpc-flow-logs-bucket` components described in +[Terraform Component Remote State](/core-concepts/components/remote-state) and configure the `static` remote state for +the `vpc-flow-logs-bucket` component to use an existing S3 bucket. + +### Configure the `vpc-flow-logs-bucket` Component + +In the `stacks/catalog/vpc-flow-logs-bucket.yaml` file, add the following configuration for +the `vpc-flow-logs-bucket/defaults` Atmos component: + +```yaml title="stacks/catalog/vpc-flow-logs-bucket.yaml" +components: + terraform: + vpc-flow-logs-bucket/defaults: + metadata: + type: abstract + # Use `static` remote state to configure the attributes for an existing + # S3 bucket for VPC Flow Logs + remote_state_backend_type: static + remote_state_backend: + static: + # ARN of the existing S3 bucket + # `vpc_flow_logs_bucket_arn` is used as an input for the `vpc` component + vpc_flow_logs_bucket_arn: "arn:aws:s3::/my-vpc-flow-logs-bucket" +``` + +
+ +In the `stacks/ue2-dev.yaml` stack config file, add the following config for the `vpc-flow-logs-bucket-1` Atmos +component in the `ue2-dev` Atmos stack: + +```yaml title="stacks/ue2-dev.yaml" +# Import the base Atmos component configuration from the `catalog`. +# `import` supports POSIX-style Globs for file names/paths (double-star `**` is supported). +# File extensions are optional (if not specified, `.yaml` is used by default). +import: + - catalog/vpc-flow-logs-bucket + +components: + terraform: + vpc-flow-logs-bucket-1: + metadata: + # Point to the Terraform component in `components/terraform` folder + component: infra/vpc-flow-logs-bucket + inherits: + # Inherit all settings and variables from the + # `vpc-flow-logs-bucket/defaults` base Atmos component + - vpc-flow-logs-bucket/defaults +``` + +
+ +### Configure and Provision the `vpc` Component + +In the `components/terraform/infra/vpc/remote-state.tf` file, configure the +[remote-state](https://github.com/cloudposse/terraform-yaml-stack-config/tree/main/modules/remote-state) Terraform +module to obtain the remote state for the `vpc-flow-logs-bucket-1` Atmos component: + +```hcl title="components/terraform/infra/vpc/remote-state.tf" +module "vpc_flow_logs_bucket" { + count = local.vpc_flow_logs_enabled ? 1 : 0 + + source = "cloudposse/stack-config/yaml//modules/remote-state" + version = "1.5.0" + + # Specify the Atmos component name (defined in YAML stack config files) + # for which to get the remote state outputs + component = var.vpc_flow_logs_bucket_component_name + + # `context` input is a way to provide the information about the stack (using the context + # variables `namespace`, `tenant`, `environment`, `stage` defined in the stack config) + context = module.this.context +} +``` + +In the `components/terraform/infra/vpc/vpc-flow-logs.tf` file, configure the `aws_flow_log` resource for the `vpc` +Terraform component to use the remote state output `vpc_flow_logs_bucket_arn` from the `vpc-flow-logs-bucket-1` Atmos +component: + +```hcl title="components/terraform/infra/vpc/vpc-flow-logs.tf" +locals { + enabled = module.this.enabled + vpc_flow_logs_enabled = local.enabled && var.vpc_flow_logs_enabled +} + +resource "aws_flow_log" "default" { + count = local.vpc_flow_logs_enabled ? 1 : 0 + + # Use the remote state output `vpc_flow_logs_bucket_arn` of the `vpc_flow_logs_bucket` component + log_destination = module.vpc_flow_logs_bucket[0].outputs.vpc_flow_logs_bucket_arn + + log_destination_type = var.vpc_flow_logs_log_destination_type + traffic_type = var.vpc_flow_logs_traffic_type + vpc_id = module.vpc.vpc_id + + tags = module.this.tags +} +``` + +In the `stacks/catalog/vpc.yaml` file, add the following default config for the `vpc/defaults` Atmos component: + +```yaml title="stacks/catalog/vpc.yaml" +components: + terraform: + vpc/defaults: + metadata: + # `metadata.type: abstract` makes the component `abstract`, + # explicitly prohibiting the component from being deployed. + # `atmos terraform apply` will fail with an error. + # If `metadata.type` attribute is not specified, it defaults to `real`. + # `real` components can be provisioned by `atmos` and CI/CD like Spacelift and Atlantis. + type: abstract + # Default variables, which will be inherited and can be overridden in the derived components + vars: + public_subnets_enabled: false + nat_gateway_enabled: false + nat_instance_enabled: false + max_subnet_count: 3 + vpc_flow_logs_enabled: false + vpc_flow_logs_log_destination_type: s3 + vpc_flow_logs_traffic_type: "ALL" +``` + +
+ +In the `stacks/ue2-dev.yaml` stack config file, add the following config for the `vpc/1` Atmos component in +the `ue2-dev` stack: + +```yaml title="stacks/ue2-dev.yaml" +# Import the base component configuration from the `catalog`. +# `import` supports POSIX-style Globs for file names/paths (double-star `**` is supported). +# File extensions are optional (if not specified, `.yaml` is used by default). +import: + - catalog/vpc + +components: + terraform: + vpc/1: + metadata: + # Point to the Terraform component in `components/terraform` folder + component: infra/vpc + inherits: + # Inherit all settings and variables from the `vpc/defaults` base Atmos component + - vpc/defaults + vars: + # Define variables that are specific for this component + # and are not set in the base component + name: vpc-1 + ipv4_primary_cidr_block: 10.8.0.0/18 + # Override the default variables from the base component + vpc_flow_logs_enabled: true + vpc_flow_logs_traffic_type: "REJECT" + + # Specify the name of the Atmos component that provides configuration + # for the `infra/vpc-flow-logs-bucket` Terraform component + vpc_flow_logs_bucket_component_name: vpc-flow-logs-bucket-1 +``` + +
+ +Having the stacks configured as shown above, we can now provision the `vpc/1` Atmos component in the `ue2-dev` stack +by executing the following Atmos commands: + +```shell +atmos terraform plan vpc/1 -s ue2-dev +atmos terraform apply vpc/1 -s ue2-dev +``` + +When the commands are executed, the `vpc_flow_logs_bucket` remote-state module detects that the `vpc-flow-logs-bucket-1` +component has the `static` remote state configured, and instead of reading its remote state from the S3 state +bucket, it just returns the static values from the `remote_state_backend.static` section. +The `vpc_flow_logs_bucket_arn` is then used as an input for the `vpc` component. diff --git a/website/docs/core-concepts/components/remote-state.md b/website/docs/core-concepts/components/remote-state.md index 89e30cb46..b8e57fb34 100644 --- a/website/docs/core-concepts/components/remote-state.md +++ b/website/docs/core-concepts/components/remote-state.md @@ -1,11 +1,11 @@ --- title: Terraform Component Remote State -sidebar_position: 8 +sidebar_position: 14 sidebar_label: Remote State id: remote-state --- -The Terraform Component Remote State is used when we need to get the outputs of an [Terraform component](/core-concepts/components), +The Terraform Component Remote State is used when we need to get the outputs of a [Terraform component](/core-concepts/components), provisioned in the same or a different [Atmos stack](/core-concepts/stacks), and use the outputs as inputs to another Atmos component. :::info @@ -34,10 +34,10 @@ state for the component in the stack. - **Terraform Component** is a [Terraform Root Module](https://developer.hashicorp.com/terraform/language/modules#the-root-module) and stored typically in `components/terraform/$name` that consists of the resources defined in the `.tf` files in a working directory - (e.g. [components/terraform/infra/vpc](https://github.com/cloudposse/atmos/tree/master/examples/tests/components/terraform/infra/vpc)) + (e.g. [components/terraform/infra/vpc](https://github.com/cloudposse/atmos/tree/master/examples/quick-start/components/terraform/vpc)) -- **Atmos Component** provides configuration (variables and other settings) for a component and is defined in one or more YAML stack config - files (which are called [Atmos stacks](/core-concepts/stacks)) +- **Atmos Component** provides configuration (variables and other settings) for a component and is defined in one or more Atmos stack manifests + (a.k.a. stack conffig files) ::: @@ -268,7 +268,7 @@ atmos terraform apply vpc/1 -s ue2-dev Both the `atmos` [CLI](/cli) and [terraform-provider-utils](https://github.com/cloudposse/terraform-provider-utils) Terraform provider use the same `Go` code, which try to locate the [CLI config](/cli/configuration) `atmos.yaml` file before parsing and processing [Atmos stacks](/core-concepts/stacks). -This means that `atmos.yaml` file must be at a location in the file system where all processes can find it. +This means that `atmos.yaml` file must be at a location in the file system where all the processes can find it. While placing `atmos.yaml` at the root of the repository will work for Atmos, it will not work for the [terraform-provider-utils](https://github.com/cloudposse/terraform-provider-utils) Terraform provider because the provider gets executed from the @@ -330,7 +330,7 @@ For this to work for both the `atmos` CLI and the Terraform provider, we recomme the Atmos component name for which to get the remote state outputs - The module accepts the `context` input as a way to provide the information about the stack (using the context - variables `namespace`, `tenant`, `environment`, `stage` defined in the stack config) + variables `namespace`, `tenant`, `environment`, `stage` defined in the stack manifests) - If the Atmos component (for which we want to get the remote state outputs) is provisioned in a different Atmos stack (in a different AWS OU, or different AWS account, or different AWS region), we can override the context variables `tenant`, `stage` and `environment` to point the module to diff --git a/website/docs/core-concepts/stacks/templating.md b/website/docs/core-concepts/stacks/templating.md index 491d4f7e2..0dcc60086 100644 --- a/website/docs/core-concepts/stacks/templating.md +++ b/website/docs/core-concepts/stacks/templating.md @@ -126,9 +126,13 @@ For example, the `env` function has the same name in [Sprig](https://masterminds If you use the `env` function from one templating engine and enable both [Sprig](https://masterminds.github.io/sprig/) and [Gomplate](https://docs.gomplate.ca/functions/), it will be invalid in the other templating engine, and an error will be thrown. -For this reason, you can use the `templates.settings.sprig.enabled` and `templates.settings,gomplate.enabled` settings to selectively -enable/disable the [Sprig](https://masterminds.github.io/sprig/) and [Gomplate](https://docs.gomplate.ca/functions/) -functions. +To be able to use the `env` function from both templating engines, you can do one of the following: + +- Use the `env` function from one templating engine, and disable the other templating engine by using the + `templates.settings.sprig.enabled` and `templates.settings,gomplate.enabled` settings + +- Enable both engines and use the Gomplate's `env` function via its + [`getenv`](https://docs.gomplate.ca/functions/env/#examples) alias ::: diff --git a/website/docs/integrations/atlantis.mdx b/website/docs/integrations/atlantis.mdx index 33e43372b..0869aa893 100644 --- a/website/docs/integrations/atlantis.mdx +++ b/website/docs/integrations/atlantis.mdx @@ -686,7 +686,7 @@ on: branches: [ main ] env: - ATMOS_VERSION: 1.70.0 + ATMOS_VERSION: 1.71.0 ATMOS_CLI_CONFIG_PATH: ./ jobs: diff --git a/website/docs/integrations/github-actions/setup-atmos.md b/website/docs/integrations/github-actions/setup-atmos.md index 1adc73ce6..d8a31dce6 100644 --- a/website/docs/integrations/github-actions/setup-atmos.md +++ b/website/docs/integrations/github-actions/setup-atmos.md @@ -27,5 +27,5 @@ jobs: uses: cloudposse/github-action-setup-atmos with: # Make sure to pin to the latest version of atmos - atmos_version: 1.70.0 + atmos_version: 1.71.0 ``` diff --git a/website/docs/integrations/terraform.md b/website/docs/integrations/terraform.md index 6f528a23d..7cfc60b67 100644 --- a/website/docs/integrations/terraform.md +++ b/website/docs/integrations/terraform.md @@ -103,7 +103,7 @@ atmos terraform plan eks -s ue2-dev atmos terraform apply eks -s ue2-dev ``` -`terraform deploy` command executes `terraform apply -auto-approve` to provision components into stacks without user interaction: +`terraform deploy` command executes `terraform apply -auto-approve` to provision components in stacks without user interaction: ```console atmos terraform deploy eks -s ue2-dev diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 83f55759e..7b3ced105 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -57,7 +57,13 @@ const config = { }], [ 'custom-loaders', {} - ] + ], + [ + '@docusaurus/plugin-google-tag-manager', + { + containerId: 'GTM-KQ62MGX9', + }, + ], ], presets: [ diff --git a/website/package-lock.json b/website/package-lock.json index fa41cca6f..f7d11419f 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@docusaurus/core": "^3.2.1", "@docusaurus/plugin-client-redirects": "^3.2.1", + "@docusaurus/plugin-google-gtag": "^3.2.1", "@docusaurus/preset-classic": "^3.2.1", "@docusaurus/theme-mermaid": "^3.2.1", "@grnet/docusaurus-terminology": "^2.0.0-rc.1", @@ -19,12 +20,12 @@ "custom-loaders": "file:plugins/custom-loaders", "docusaurus-plugin-image-zoom": "^2.0.0", "html-loader": "^5.0.0", - "marked": "^12.0.1", + "marked": "^12.0.2", "prism-react-renderer": "^2.3.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-image-gallery": "^1.3.0", - "react-player": "^2.15.1", + "react-player": "^2.16.0", "react-table": "^7.8.0", "remark-html": "^16.0.1", "remark-parse": "^11.0.0", @@ -9234,9 +9235,9 @@ } }, "node_modules/marked": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.1.tgz", - "integrity": "sha512-Y1/V2yafOcOdWQCX0XpAKXzDakPOpn6U0YLxTJs3cww6VxOzZV1BTOOYWLvH3gX38cq+iLwljHHTnMtlDfg01Q==", + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", "bin": { "marked": "bin/marked.js" }, @@ -14344,9 +14345,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -14454,15 +14455,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-error-overlay": { @@ -14544,9 +14545,9 @@ } }, "node_modules/react-player": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.15.1.tgz", - "integrity": "sha512-ni1XFuYZuhIKKdeFII+KRLmIPcvCYlyXvtSMhNOgssdfnSovmakBtBTW2bxowPvmpKy5BTR4jC4CF79ucgHT+g==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz", + "integrity": "sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==", "dependencies": { "deepmerge": "^4.0.0", "load-script": "^1.0.0", @@ -15562,9 +15563,9 @@ "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } diff --git a/website/package.json b/website/package.json index ac92a467a..e5adf3df1 100644 --- a/website/package.json +++ b/website/package.json @@ -19,18 +19,19 @@ "@docusaurus/plugin-client-redirects": "^3.2.1", "@docusaurus/preset-classic": "^3.2.1", "@docusaurus/theme-mermaid": "^3.2.1", + "@docusaurus/plugin-google-tag-manager": "^3.2.1", "@grnet/docusaurus-terminology": "^2.0.0-rc.1", "@mdx-js/react": "^3.0.1", "clsx": "^2.1.0", "custom-loaders": "file:plugins/custom-loaders", "docusaurus-plugin-image-zoom": "^2.0.0", "html-loader": "^5.0.0", - "marked": "^12.0.1", + "marked": "^12.0.2", "prism-react-renderer": "^2.3.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-image-gallery": "^1.3.0", - "react-player": "^2.15.1", + "react-player": "^2.16.0", "react-table": "^7.8.0", "remark-html": "^16.0.1", "remark-parse": "^11.0.0", diff --git a/website/static/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json b/website/static/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json index 445e9c7f2..c337407e0 100644 --- a/website/static/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json +++ b/website/static/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json @@ -437,6 +437,7 @@ "backend_type": { "type": "string", "enum": [ + "local", "s3", "remote", "vault", @@ -455,6 +456,7 @@ "remote_state_backend_type": { "type": "string", "enum": [ + "local", "s3", "remote", "vault",