Skip to main content

atmos describe affected

Purpose

Use this command to show a list of the affected Atmos components and stacks given two Git commits.


atmos describe affected --help

Description

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).

The second commit can be specified on the command line by using the --ref (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).

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 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

  • Deep merging all stack configurations for both the current working branch and the remote target branch

  • Looking for changes in the component directories

  • Comparing each section of the stack configuration 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

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.


Use our GitHub Action

Our affected stacks GitHub Action provides a ready-to-go way to run describe affected and produce a GitHub matrix.


Usage

atmos describe affected [options]

tip

Run atmos describe affected --help to see all the available options

Examples

atmos describe affected
atmos describe affected --verbose=true
atmos describe affected --ref refs/heads/main
atmos describe affected --ref refs/heads/my-new-branch --verbose=true
atmos describe affected --ref refs/heads/main --format json
atmos describe affected --ref refs/tags/v1.16.0 --file affected.yaml --format yaml
atmos describe affected --sha 3a5eafeab90426bd82bf5899896b28cc0bab3073 --file affected.json
atmos describe affected --sha 3a5eafeab90426bd82bf5899896b28cc0bab3073
atmos describe affected --ssh-key <path_to_ssh_key>
atmos describe affected --ssh-key <path_to_ssh_key> --ssh-key-password <password>
atmos describe affected --repo-path <path_to_already_cloned_repo>
atmos describe affected --include-spacelift-admin-stacks=true

Example Output

atmos describe affected --verbose=true

> atmos describe affected --verbose=true

Cloning repo 'https://github.com/cloudposse/atmos' into the temp dir '/var/folders/g5/lbvzy_ld2hx4mgrgyp19bvb00000gn/T/16710736261366892599'

Checking out the HEAD of the default branch ...

Enumerating objects: 4215, done.
Counting objects: 100% (1157/1157), done.
Compressing objects: 100% (576/576), done.
Total 4215 (delta 658), reused 911 (delta 511), pack-reused 3058

Checked out Git ref 'refs/heads/master'

Current working repo HEAD: 7d37c1e890514479fae404d13841a2754be70cbf refs/heads/describe-affected
Remote repo HEAD: 40210e8d365d3d88ac13c0778c0867b679bbba69 refs/heads/master

Changed files:

examples/tests/components/terraform/infra/vpc/main.tf
internal/exec/describe_affected.go
website/docs/cli/commands/describe/describe-affected.md

Affected components and stacks:

[
{
"component": "infra/vpc",
"component_type": "terraform",
"component_path": "components/terraform/infra/vpc",
"stack": "tenant1-ue2-dev",
"stack_slug": "tenant1-ue2-dev-infra-vpc",
"spacelift_stack": "tenant1-ue2-dev-infra-vpc",
"atlantis_project": "tenant1-ue2-dev-infra-vpc",
"affected": "component"
},
{
"component": "infra/vpc",
"component_type": "terraform",
"component_path": "components/terraform/infra/vpc",
"stack": "tenant1-ue2-prod",
"stack_slug": "tenant1-ue2-prod-infra-vpc",
"spacelift_stack": "tenant1-ue2-prod-infra-vpc",
"atlantis_project": "tenant1-ue2-prod-infra-vpc",
"affected": "component"
},
{
"component": "infra/vpc",
"component_type": "terraform",
"component_path": "components/terraform/infra/vpc",
"stack": "tenant1-ue2-staging",
"stack_slug": "tenant1-ue2-staging-infra-vpc",
"spacelift_stack": "tenant1-ue2-staging-infra-vpc",
"atlantis_project": "tenant1-ue2-staging-infra-vpc",
"affected": "component"
},
{
"component": "top-level-component3",
"component_type": "terraform",
"component_path": "components/terraform/top-level-component1",
"stack": "tenant1-ue2-test-1",
"stack_slug": "tenant1-ue2-test-1-top-level-component3",
"atlantis_project": "tenant1-ue2-test-1-top-level-component3",
"affected": "file",
"file": "examples/tests/components/terraform/mixins/introspection.mixin.tf"
},
{
"component": "top-level-component3",
"component_type": "terraform",
"component_path": "components/terraform/top-level-component1",
"stack": "tenant1-ue2-test-1",
"stack_slug": "tenant1-ue2-test-1-top-level-component3",
"atlantis_project": "tenant1-ue2-test-1-top-level-component3",
"affected": "folder",
"folder": "examples/tests/components/helmfile/infra/infra-server"
}
]

Flags

FlagDescriptionRequired
--refGit Reference with which to compare the current working branchno
--shaGit commit SHA with which to compare the current working branchno
--fileIf specified, write the result to the fileno
--formatSpecify the output format: json or yaml (json is default)no
--ssh-keyPath to PEM-encoded private key to clone private repos using SSHno
--ssh-key-passwordEncryption password for the PEM-encoded private key if the key contains
a password-encrypted PEM block
no
--repo-pathPath to the already cloned target repository with which to compare the current branch.
Conflicts with --ref, --sha, --ssh-key and --ssh-key-password
no
--verbosePrint more detailed output when cloning and checking out the target
Git repository and processing the result
no
--include-spacelift-admin-stacksInclude the Spacelift admin stack of any stack
that is affected by config changes
no

Output

The command outputs a list of objects (in JSON or YAML format).

Each object has the following schema:

{
"component": "....",
"component_type": "....",
"component_path": "....",
"stack": "....",
"stack_slug": "....",
"spacelift_stack": ".....",
"atlantis_project": ".....",
"affected": ".....",
"file": ".....",
"folder": "....."
}

where:

  • component - the affected Atmos component

  • component_type - the type of the component (terraform or helmfile)

  • component_path - the filesystem path to the terraform or helmfile component

  • stack - the affected Atmos stack

  • stack_slug - the Atmos stack slug (concatenation of the Atmos stack and Atmos component)

  • spacelift_stack - the affected Spacelift stack. It will be included only if the Spacelift workspace is enabled for the Atmos component in the Atmos stack in the settings.spacelift.workspace_enabled section (either directly in the component's settings.spacelift.workspace_enabled section or via inheritance)

  • atlantis_project - the affected Atlantis project name. It will be included only if the Atlantis integration is configured in the settings.atlantis section in the stack config. Refer to Atlantis Integration for more details

  • file - if the Atmos component depends on an external file, and the file was changed (see affected.file below), the file attributes shows the modified file

  • folder - if the Atmos component depends on an external folder, and any file in the folder was changed (see affected.folder below), the folder attributes shows the modified folder

  • affected - shows what was changed for the component. The possible values are:

    • stack.vars - the vars component section in the stack config has been modified

    • stack.env - the env component section in the stack config has been modified

    • stack.settings - the settings component section in the stack config has been modified

    • stack.metadata - the metadata component section in the stack config has been modified

    • component - the Terraform or Helmfile component that the Atmos component provisions has been changed

    • component.module - the Terraform component is affected because it uses a local Terraform module (not from the Terraform registry, but from the local filesystem), and that local module has been changed.

      For example, let's suppose that we have a catalog of reusable Terraform modules in the modules folder (outside the components folder), and we have defined the following label Terraform module in modules/label:

      modules/label
        module "label" {
      source = "cloudposse/label/null"
      version = "0.25.0"
      context = module.this.context
      }

      output "label" {
      value = module.label
      description = "Label outputs"
      }

      We then use the Terraform module in the components/terraform/top-level-component1 component:

      components/terraform/top-level-component1
        module "service_2_label" {
      source = "../../../modules/label"
      context = module.this.context
      }

      output "service_2_id" {
      value = module.service_2_label.label.id
      description = "Service 2 ID"
      }

      The label module is not in the stack config of the top-level-component1 component (not in the YAML stack config files), but Atmos understands Terraform dependencies (using a Terraform parser from HashiCorp), and can automatically detect any changes to the module.

      For example, if you make changes to any files in the folder modules/label, Atmos will detect the module changes, and since the module is a Terraform dependency of the top-level-component1 component, Atmos will mark the component as affected with the affected attribute set to component.module:

        [
      {
      "component": "top-level-component1",
      "component_type": "terraform",
      "component_path": "examples/tests/components/terraform/top-level-component1",
      "stack": "tenant1-ue2-staging",
      "stack_slug": "tenant1-ue2-staging-top-level-component1",
      "spacelift_stack": "tenant1-ue2-staging-top-level-component1",
      "atlantis_project": "tenant1-ue2-staging-top-level-component1",
      "affected": "component.module"
      },
      {
      "component": "top-level-component1",
      "component_type": "terraform",
      "component_path": "examples/tests/components/terraform/top-level-component1",
      "stack": "tenant2-ue2-staging",
      "stack_slug": "tenant2-ue2-staging-top-level-component1",
      "spacelift_stack": "tenant2-ue2-staging-top-level-component1",
      "atlantis_project": "tenant2-ue2-staging-top-level-component1",
      "affected": "component.module"
      }
      ]

    • stack.settings.spacelift.admin_stack_selector - the Atmos component for the Spacelift admin stack. This will be included only if all the following is true:

      • The atmos describe affected is executed with the --include-spacelift-admin-stacks=true flag

      • Any of the affected Atmos components has configured the section settings.spacelift.admin_stack_selector pointing to the Spacelift admin stack that manages the components. For example:

        stacks/orgs/cp/tenant1/_defaults.yaml
        settings:
        spacelift:
        # 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
        # admin stack for each affected Atmos stack
        # when executing the command
        # `atmos describe affected --include-spacelift-admin-stacks=true`
        admin_stack_selector:
        component: infrastructure-tenant1
        tenant: tenant1
        environment: ue2
        stage: prod
      • The Spacelift admin stack is enabled by settings.spacelift.workdpace_enabled set to true. For example:

        stacks/catalog/terraform/spacelift/infrastructure-tenant1.yaml
        components:
        terraform:
        infrastructure-tenant1:
        metadata:
        component: spacelift
        inherits:
        - spacelift-defaults
        settings:
        spacelift:
        workspace_enabled: true
    • file - an external file on the local filesystem that the Atmos component depends on was changed.

      Dependencies on external files (not in the component's folder) are defined using the file attribute in the settings.depends_on map. For example:

      stacks/catalog/terraform/top-level-component3.yaml
      components:
      terraform:
      top-level-component3:
      metadata:
      component: "top-level-component1"
      settings:
      depends_on:
      1:
      file: "examples/tests/components/terraform/mixins/introspection.mixin.tf"

      In the configuration above, we specify that the Atmos component top-level-component3 depends on the file examples/tests/components/terraform/mixins/introspection.mixin.tf (which is not in the component's folder). If the file gets modified, the component top-level-component3 will be included in the atmos describe affected command output. For example:

        [
      {
      "component": "top-level-component3",
      "component_type": "terraform",
      "component_path": "components/terraform/top-level-component1",
      "stack": "tenant1-ue2-test-1",
      "stack_slug": "tenant1-ue2-test-1-top-level-component3",
      "atlantis_project": "tenant1-ue2-test-1-top-level-component3",
      "affected": "file",
      "file": "examples/tests/components/terraform/mixins/introspection.mixin.tf"
      }
      ]
    • folder - any file in an external folder that the Atmos component depends on was changed.

      Dependencies on external folders are defined using the folder attribute in the settings.depends_on map. For example:

      stacks/catalog/terraform/top-level-component3.yaml
      components:
      terraform:
      top-level-component3:
      metadata:
      component: "top-level-component1"
      settings:
      depends_on:
      1:
      file: "examples/tests/components/terraform/mixins/introspection.mixin.tf"
      2:
      folder: "examples/tests/components/helmfile/infra/infra-server"

      In the configuration above, we specify that the Atmos component top-level-component3 depends on the folder examples/tests/components/helmfile/infra/infra-server. If any file in the folder gets modified, the component top-level-component3 will be included in the atmos describe affected command output. For example:

        [
      {
      "component": "top-level-component3",
      "component_type": "terraform",
      "component_path": "components/terraform/top-level-component1",
      "stack": "tenant1-ue2-test-1",
      "stack_slug": "tenant1-ue2-test-1-top-level-component3",
      "atlantis_project": "tenant1-ue2-test-1-top-level-component3",
      "affected": "folder",
      "folder": "examples/tests/components/helmfile/infra/infra-server"
      }
      ]

note

Abstract Atmos components (metadata.type is set to abstract) are not included in the output since they serve as blueprints for other Atmos components and are not meant to be provisioned.

Output Example

atmos describe affected --include-spacelift-admin-stacks=true

[
{
"component": "infrastructure-tenant1",
"component_type": "terraform",
"component_path": "examples/tests/components/terraform/spacelift",
"stack": "tenant1-ue2-prod",
"stack_slug": "tenant1-ue2-prod-infrastructure-tenant1",
"spacelift_stack": "tenant1-ue2-prod-infrastructure-tenant1",
"atlantis_project": "tenant1-ue2-prod-infrastructure-tenant1",
"affected": "stack.settings.spacelift.admin_stack_selector"
},
{
"component": "infrastructure-tenant2",
"component_type": "terraform",
"component_path": "examples/tests/components/terraform/spacelift",
"stack": "tenant2-ue2-prod",
"stack_slug": "tenant2-ue2-prod-infrastructure-tenant2",
"spacelift_stack": "tenant2-ue2-prod-infrastructure-tenant2",
"atlantis_project": "tenant2-ue2-prod-infrastructure-tenant2",
"affected": "stack.settings.spacelift.admin_stack_selector"
},
{
"component": "test/test-component-override-2",
"component_type": "terraform",
"component_path": "components/terraform/test/test-component",
"stack": "tenant1-ue2-dev",
"stack_slug": "tenant1-ue2-dev-test-test-component-override-2",
"spacelift_stack": "tenant1-ue2-dev-new-component",
"atlantis_project": "tenant1-ue2-dev-new-component",
"affected": "stack.vars"
},
{
"component": "infra/vpc",
"component_type": "terraform",
"component_path": "components/terraform/infra/vpc",
"stack": "tenant2-ue2-staging",
"stack_slug": "tenant1-ue2-staging-infra-vpc",
"spacelift_stack": "tenant1-ue2-staging-infra-vpc",
"atlantis_project": "tenant1-ue2-staging-infra-vpc",
"affected": "component"
},
{
"component": "test/test-component-override-3",
"component_type": "terraform",
"component_path": "components/terraform/test/test-component",
"stack": "tenant1-ue2-prod",
"stack_slug": "tenant1-ue2-prod-test-test-component-override-3",
"atlantis_project": "tenant1-ue2-prod-test-test-component-override-3",
"affected": "stack.env"
},
{
"component": "top-level-component3",
"component_type": "terraform",
"component_path": "components/terraform/top-level-component1",
"stack": "tenant1-ue2-test-1",
"stack_slug": "tenant1-ue2-test-1-top-level-component3",
"atlantis_project": "tenant1-ue2-test-1-top-level-component3",
"affected": "file",
"file": "examples/tests/components/terraform/mixins/introspection.mixin.tf"
},
{
"component": "top-level-component3",
"component_type": "terraform",
"component_path": "components/terraform/top-level-component1",
"stack": "tenant1-ue2-test-1",
"stack_slug": "tenant1-ue2-test-1-top-level-component3",
"atlantis_project": "tenant1-ue2-test-1-top-level-component3",
"affected": "folder",
"folder": "examples/tests/components/helmfile/infra/infra-server"
}
]

Working with Private Repositories

There are a few ways to work with private repositories with which the current local branch is compared to detect the changed files and affected Atmos stacks and components:

  • Using the --ssh-key flag to specify the filesystem path to a PEM-encoded private key to clone private repos using SSH, and the --ssh-key-password flag to provide the encryption password for the PEM-encoded private key if the key contains a password-encrypted PEM block

  • Execute the atmos describe affected --repo-path <path_to_cloned_target_repo> command in a GitHub Action. For this to work, clone the remote private repository using the checkout GitHub action. Then use the --repo-path flag to specify the path to the already cloned target repository with which to compare the current branch

  • It should just also work with whatever SSH config/context has been already set up, for example, when using SSH agents. In this case, you don't need to use the --ssh-key, --ssh-key-password and --repo-path flags to clone private repositories

Using with GitHub Actions

If the atmos describe affected command is executed in a GitHub Action, and you don't want to store or generate a long-lived SSH private key on the server, you can do the following (NOTE: This is only required if the action is attempting to clone a private repo which is not itself):

  • Create a GitHub Personal Access Token (PAT) with scope permissions to clone private repos

  • Add the created PAT as a repository or GitHub organization secret

  • In your GitHub action, clone the remote repository using the checkout GitHub action

  • Execute atmos describe affected command with the --repo-path flag set to the cloned repository path using the GITHUB_WORKSPACE ENV variable (which points to the default working directory on the GitHub runner for steps, and the default location of the repository when using the checkout action). For example:

    atmos describe affected --repo-path $GITHUB_WORKSPACE