atmos describe affected
Use this command to show a list of the affected Atmos components and stacks given two Git commits.
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.
The --sha takes precedence over the --ref flag.
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).
How does it work?​
The command performs the following:
-
If the
--repo-pathflag 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-keyand--ssh-key-passwordflags are not used, and an error will be thrown if the--repo-pathflag and any of the--ref,--sha,--ssh-keyor--ssh-key-passwordflags are provided at the same time -
Otherwise, if the
--clone-target-ref=trueflag is specified, the command clones (into a temp directory) the remote target with which to compare the current working branch. If the--refflag or the commit SHA flag--shaare provided, the command uses them to clone and checkout the remote target. Otherwise, theHEADof the remote origin is used (refs/remotes/origin/HEADGit ref, usually themainbranch) -
Otherwise, (if the
--repo-pathand--clone-target-ref=trueflags 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--refflag or the commit SHA flag--shaare provided, the command uses them to check out. Otherwise, theHEADof the remote origin is used (refs/remotes/origin/HEADGit ref, usually themainbranch). This requires that the target reference is already cloned by Git, and the information about it exists in the.gitdirectory (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 theatmos describe affectedcommand since it allows working with private repositories without providing the SSH credentials (--ssh-keyand--ssh-key-passwordflags), 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) -
The command deep-merges all stack configurations from both sources: the current working branch and the target reference
-
The command searches for changes in the component directories
-
The command compares each stack manifest section of the stack configurations from both sources looking for differences
-
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 the stacks for differences since it already knows that they are affected.
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]
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
atmos describe affected --clone-target-ref=true
atmos describe affected --include-dependents=true
atmos describe affected --include-settings=true
atmos describe affected --stack=plat-ue2-prod
atmos describe affected --upload=true
atmos describe affected --query <yq-expression>
atmos describe affected --process-templates=false
atmos describe affected --process-functions=false
atmos describe affected --skip=terraform.output
atmos describe affected --skip=terraform.output --skip=include
atmos describe affected --skip=include,eval
atmos describe affected --exclude-locked
Example Output
Flags​
--ref(optional)Git Reference with which to compare the current working branch
--sha(optional)Git commit SHA with which to compare the current working branch
--file(optional)If specified, write the result to the file
--format(optional)Specify the output format:
jsonoryaml(jsonis default)--ssh-key(optional)Path to PEM-encoded private key to clone private repos using SSH
--ssh-key-password(optional)Encryption password for the PEM-encoded private key if the key contains a password-encrypted PEM block
--repo-path(optional)Path to the already cloned target repository with which to compare the current branch. Conflicts with
--ref,--sha,--ssh-keyand--ssh-key-password--verbose(optional)Print more detailed output when cloning and checking out the target Git repository and processing the result
--include-spacelift-admin-stacks(optional)Include the Spacelift admin stack of any stack that is affected by config changes
--clone-target-ref(optional)Clone the target reference with which to compare the current branch.
atmos describe affected --clone-target-ref=trueIf 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.gitdirectory--stack(optional)Only show results for the specific stack.
atmos describe affected --stack=plat-ue2-prod--include-dependents(optional)Include the dependent components and stacks.
atmos describe affected --include-dependents=true--include-settings(optional)Include the
settingssection for each affected component.atmos describe affected --include-settings=true--query(optional)Query the results of the command using YQ expressions.
atmos describe affected --query=<yq-expression>For more details, refer to YQ - a lightweight and portable command-line YAML processor
--process-templates(optional)Enable/disable processing of
Gotemplates in Atmos stacks manifests when executing the command. If the flag is not provided, it's set totrueby default.atmos describe affected --process-templates=false--process-functions(optional)Enable/disable processing of Atmos YAML functions in Atmos stacks manifests when executing the command. If the flag is not provided, it's set to
trueby default.atmos describe affected --process-functions=false--skip(optional)Skip processing a specific Atmos YAML function in Atmos stacks manifests when executing the command. To specify more than one function, use multiple
--skipflags, or separate the functions with a comma:atmos describe affected --skip=terraform.output --skip=includeatmos describe affected --skip=terraform.output,include--exclude-locked(optional)Exclude the locked components (
metadata.locked: true) from the output.Refer to Locking Components with
metadata.lockedatmos describe affected --exclude-locked--upload(optional)Upload the affected components and stacks to a specified HTTP endpoint.
atmos describe affected --upload=trueAtmos will perform an HTTP POST request to the URL
${ATMOS_PRO_BASE_URL}/${ATMOS_PRO_ENDPOINT}, where the base URL is defined by theATMOS_PRO_BASE_URLenvironment variable, and the URL path is defined by theATMOS_PRO_ENDPOINTenvironment variable
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": ".....",
"affected_all": [],
"file": ".....",
"folder": ".....",
"dependents": [],
"included_in_dependents": "true | false",
"settings": {}
}
where:
componentThe affected Atmos component.
component_typeThe type of the component (
terraformorhelmfile).component_pathThe filesystem path to the
terraformorhelmfilecomponent.stackThe affected Atmos stack.
stack_slugThe Atmos stack slug (concatenation of the Atmos stack and Atmos component).
spacelift_stackThe 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_enabledsection (either directly in the component'ssettings.spacelift.workspace_enabledsection or via inheritance).atlantis_projectThe affected Atlantis project name. It will be included only if the Atlantis integration is configured in the
settings.atlantissection in the stack config. Refer to Atlantis Integration for more details.fileIf the Atmos component depends on an external file, and the file was changed, the
fileattributes shows the modified file.folderIf the Atmos component depends on an external folder, and any file in the folder was changed, the
folderattributes shows the modified folder.dependentsA list of components that depend on the current affected component. It will be populated only if the command-line flag
--include-dependents=trueis passed (to take dependencies into account) and there are other components that depend on the affected component in the stack. Refer toatmos describe dependentsfor more details. Thedependentsproperty is hierarchical - each component in the list will also contain adependentsproperty if that component has dependent components as well.settingsThe
settingssection of the component in the stack. It will be included only if the command-line flag--include-settings=trueis passed. Thesettingssections is a free-form map used to pass configuration information to integrations.included_in_dependentsA boolean flag indicating if the affected component in the stack is also present in any of the
dependentsproperties of the other affected components. It will be included only if the command-line flag--include-dependents=trueis passed. Ifincluded_in_dependentsis set totrue, it indicates that the affected component in the stack is also present in any of thedependentslists in the dependency hierarchy of the other affected components. This flag can be used to decide whether to plan/apply the affected component - you might skip planning/applying the component since it's also a dependency of another affected component and will be triggered in the dependency order of the other affected component.affectedShows the first (in the processing order) section that was changed. The possible values are:
stack.varsThe
varscomponent section in the stack config has been modified.stack.envThe
envcomponent section in the stack config has been modified.stack.settingsThe
settingscomponent section in the stack config has been modified.stack.metadataThe
metadatacomponent section in the stack config has been modified.componentThe Terraform or Helmfile component that the Atmos component provisions has been changed.
component.moduleThe 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
modulesfolder (outside thecomponentsfolder), and we have defined the followinglabelTerraform module inmodules/label:modules/labelmodule "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-component1component:components/terraform/top-level-component1module "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
labelmodule is not in the stack config of thetop-level-component1component (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 thetop-level-component1component, Atmos will mark the component as affected with theaffectedattribute set tocomponent.module:[
{
"component": "top-level-component1",
"component_type": "terraform",
"component_path": "tests/fixtures/scenarios/complete/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",
"affected_all": [
"component.module"
]
},
{
"component": "top-level-component1",
"component_type": "terraform",
"component_path": "tests/fixtures/scenarios/complete/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",
"affected_all": [
"component.module"
]
}
]stack.settings.spacelift.admin_stack_selectorThe Atmos component for the Spacelift admin stack.
This will be included only if all of the following is true:
-
The
atmos describe affectedis executed with the--include-spacelift-admin-stacks=trueflag -
Any of the affected Atmos components has configured the section
settings.spacelift.admin_stack_selectorpointing to the Spacelift admin stack that manages the components.For example:
stacks/orgs/cp/tenant1/_defaults.yamlsettings:
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.workspace_enabledset totrue.For example:
stacks/catalog/terraform/spacelift/infrastructure-tenant1.yamlcomponents:
terraform:
infrastructure-tenant1:
metadata:
component: spacelift
inherits:
- spacelift-defaults
settings:
spacelift:
workspace_enabled: true
-
fileAn 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
fileattribute in thesettings.depends_onmap.For example:
stacks/catalog/terraform/top-level-component3.yamlcomponents:
terraform:
top-level-component3:
metadata:
component: "top-level-component1"
settings:
depends_on:
1:
file: "tests/fixtures/scenarios/complete/components/terraform/mixins/introspection.mixin.tf"In the configuration above, we specify that the Atmos component
top-level-component3depends on the filetests/fixtures/scenarios/complete/components/terraform/mixins/introspection.mixin.tf(which is not in the component's folder). If the file gets modified, the componenttop-level-component3will be included in theatmos describe affectedcommand 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",
"affected_all": [
"file"
],
"file": "tests/fixtures/scenarios/complete/components/terraform/mixins/introspection.mixin.tf"
}
]folderAny file in an external folder that the Atmos component depends on was changed.
Dependencies on external folders are defined using the
folderattribute in thesettings.depends_onmap.For example:
stacks/catalog/terraform/top-level-component3.yamlcomponents:
terraform:
top-level-component3:
metadata:
component: "top-level-component1"
settings:
depends_on:
1:
file: "tests/fixtures/scenarios/complete/components/terraform/mixins/introspection.mixin.tf"
2:
folder: "tests/fixtures/scenarios/complete/components/helmfile/infra/infra-server"In the configuration above, we specify that the Atmos component
top-level-component3depends on the foldertests/fixtures/scenarios/complete/components/helmfile/infra/infra-server. If any file in the folder gets modified, the componenttop-level-component3will be included in theatmos describe affectedcommand 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",
"affected_all": [
"folder"
],
"folder": "tests/fixtures/scenarios/complete/components/helmfile/infra/infra-server"
}
]
affected_allShows all component sections and attributes that were changed.
For example, if you make changes to the
varsandsettingssections of the componentcomponent-1in thenonprodstack, and executeatmos describe affected, you will get the following result:[
{
"component": "component-1",
"component_type": "terraform",
"stack": "nonprod",
"stack_slug": "nonprod-component-1",
"affected": "stack.vars",
"affected_all": [
"stack.vars",
"stack.settings"
]
}
]If you create a new Terraform/Tofu component, configure a new Atmos component
component-1in thenonprodstack, and executeatmos describe affected, you will get the following result:[
{
"component": "component-1",
"component_type": "terraform",
"stack": "nonprod",
"stack_slug": "nonprod-component-1",
"affected": "stack.metadata",
"affected_all": [
"component",
"stack.metadata",
"stack.vars",
"stack.env",
"stack.settings"
]
}
]where:
affectedShows that the Atmos component's
metadatasection was changed (since the component is new and themetadatasection is the first section that Atmos processes).affected_allShows all the affected sections and attributes:
componentThe Terraform component (Terraform configuration) was affected (since it was just added).
stack.metadataThe Atmos component's
metadatasection was changed.stack.varsThe Atmos component's
varssection was changed.stack.envThe Atmos component's
envsection was changed.stack.settingsThe Atmos component's
settingssection was changed.
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.
Disabled Atmos components (metadata.enabled is set to false)
are also not included in the output since they are explicitly disabled.
Output Example​
Affected Components with Dependencies​
The output of the atmos describe affected command can include dependencies for the affected components.
If the command-line flag --include-dependents=true is passed to the atmos describe affected command, and there are
other components that depend on the affected components in the stack, the command will include a dependents
property (list) for each affected component. The dependents property is hierarchical - each component in the list will
also contain a dependents property if that component has dependent components as well.
For example, suppose that we have the following configuration for the Atmos components component-1, component-2 and
component-3 in the stack plat-ue2-dev:
stacks/orgs/acme/plat/dev/us-east-2.yaml
For more details on how to configure component dependencies, refer to atmos describe dependents
In the above configuration, component-3 depends on component-2, whereas component-2 depends on component-1.
If all the components are affected (modified) in the current working branch,
the atmos describe affected --include-dependents=true command will produce the following result:
The component-1 component does not depend on any other component, and therefore it has the included_in_dependents
attribute set to false. The component-2 and component-3 components depend on other components and are included in
the dependents property of the other components, and hence the included_in_dependents attribute is set to true.
When processing the above output, you might decide to not plan/apply the component-2 and component-3 components
since they are in the dependents property of the component-1 component. Instead, you might just
trigger component-1 and then component-2 and component-3 in the order of dependencies.
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-keyflag to specify the filesystem path to a PEM-encoded private key to clone private repos using SSH, and the--ssh-key-passwordflag 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-pathflag 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-passwordand--repo-pathflags 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 affectedcommand with the--repo-pathflag set to the cloned repository path using theGITHUB_WORKSPACEENV 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
Upload the affected components and stacks to an HTTP endpoint​
If the --upload=true command-line flag is passed, Atmos will upload the affected components and stacks to a
specified HTTP endpoint.
The endpoint can process the affected components and their dependencies in a CI/CD pipeline (e.g. execute
terraform apply on all the affected components in the stacks and all the dependencies).
Atmos will perform an HTTP POST request to the URL ${ATMOS_PRO_BASE_URL}/${ATMOS_PRO_ENDPOINT}, where the base URL
is defined by the ATMOS_PRO_BASE_URL environment variable, and the URL path is defined by the ATMOS_PRO_ENDPOINT
environment variable.
An Authorization header
Authorization: Bearer $ATMOS_PRO_TOKEN will be added to the HTTP request (if the ATMOS_PRO_TOKEN environment
variable is set) to provide credentials to authenticate with the server.
If the --upload=true command-line flag is passed, the --include-dependencies and --include-settings flags are
automatically set to true, so the affected components will be uploaded with their dependencies and settings
(if they are configured in Atmos stack manifests).
The payload of the HTTP POST request will be a JSON object with the following schema:
where:
base_shathe Git commit SHA of the base branch against which the changes in the current commit are compared
head_shathe SHA of the current Git commit
repo_urlthe URL of the current repository
repo_namethe name of the current repository
repo_ownerthe owner of the current repository
repo_hostthe host of the current repository
stacksa list of affected components and stacks with their dependencies and settings