Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement atmos packages to install 3rd party tools #927

Open
osterman opened this issue Jan 10, 2025 · 22 comments
Open

Implement atmos packages to install 3rd party tools #927

osterman opened this issue Jan 10, 2025 · 22 comments
Assignees

Comments

@osterman
Copy link
Member

Describe the Feature

Atmos should automatically add the packages base-path as a priority search PATH when executing any commands.

Expected Behavior

Packages are automatically installed and available to atmos.

Use Case

  • Install versions of opentofu or terraform

  • Install helmfile

  • Install terraform-docs, tflint, etc.

  • Support multiple concurrent versions

  • Install any other binaries needed by workflows, custom commands, etc

Describe Ideal Solution

Atmos Commands

# Install all packages
atmos packages install
# Update packages 
atmos packages update

Atmos Configuration

# atmos.yaml
settings:
  packages:
    base-path: ./packages
    bin-path: ./packages/bin

# packages/
registry:
  #components: 
  # for examle only...

  # Configure the packages registries supported by Aqua
  packages:
  - name: aqua
    type: standard
    ref: v4.246.0 # renovate: depName=aquaproj/aqua-registry
  
  - name: foo
    type: github_content
    repo_owner: suzuki-shunsuke
    repo_name: private-aqua-registry
    ref: v0.1.0
    path: registry.yaml


# Define global packages not specific to any type component
# Use for subcommands
packages:
- name: hashicorp/[email protected]
- name: hashicorp/terraform
  version: v0.13.7
  command_aliases:
    - command: terraform
      alias: terraform-013


# Define packages required for a specific type of component
components:
  terraform:
    needs:
    - hashicorp/[email protected]
    - hashicorp/[email protected]

Stack Configuration

# stacks/_defaults.yaml
components:
  terraform:
    vpc:
      needs:
      - hashicorp/[email protected]

Alternatives Considered

No response

Additional Context

See https://github.com/suzuki-shunsuke/tfaction for example implementation

@suzuki-shunsuke
Copy link
Collaborator

suzuki-shunsuke commented Jan 10, 2025

There are several options:

  1. Use a version manager like aqua
  2. Run a version manager like aqua internally
  3. Call a version manager like aqua as a Go Module
  4. Develop the feature from scratch

The option 1 is simplest and easiest.
We don't need to develop anything.
We only need to encourage users to use a version manager.
But the drawback is that users need to install tools themselves.
By enabling atmos to manage tools, users don't need to install tools themselves.

The option 2 is not bad.
I think we should consider this option first.
I'm the author of aqua, which is a CLI version manager, so I use aqua here.
atmos can generate configuration files of aqua such as aqua.yaml, registry.yaml, aqua-checksums.json, and aqua-policy.yaml internally.
And atmos can execute aqua install internally to install tools.
By adding $(aqua root-dir)/bin to $PATH internally, users don't need to be aware of aqua.
aqua also provides a update command.
aqua provides Renovate config presets. We can utilize them or develop similar presets for Atmos.

This is an advanced topic, but one of problems is checksum verification in CI.
Checksum verification is a feature of aqua.
This feature is very important for security.
aqua recommends managing aqua-checksums.json by Git, otherwise checksums can be tampered.
If atmos hides aqua and doesn't manage aqua-checksums.json by Git, the checksum verification doesn't work enough.
But I'm aware that managing aqua-checksums.json by Git is a bit bothersome because we need to update checksums when we update tools.
Especially, when we update tools by Renovate, we also need to update checksums.
aqua provides a GitHub Action to update checksums automatically, but this requires a GitHub App.

The option 3 is good, but I guess most version managers are not Go Modules but CLI.
aqua is written in Go, and it doesn't use Go's internal packages, which means all packages are public.
But aqua doesn't intend that other tools depend on aqua's packages.
API isn't third party friendly, and API can be changed drastically, so I don't recommend using aqua as a library.

I don't recommend the option 4.
We should utilize existing tools rather than re-inventing the wheel.
We need to maintain something like aqua's registries ourselves.
aqua provides various features.
If other options isn't acceptable, we need to consider this option.

What do you think?

@osterman
Copy link
Member Author

@suzuki-shunsuke

  1. Use a version manager like aqua

My main concern is this: it defeats the purpose of the desired functionality, which is to avoid the need to manually install additional tools—including the tooling required to do so (besides Atmos itself). The goal is for everything to "just work" out of the box. I feel so strongly about this that I’d rather hold off until we find a solution that aligns with this principle.

  1. Run a version manager like aqua internally

Same response as (1).

  1. Call a version manager like aqua as a Go Module

Thank you for clarifying this point!

This was definitely my preferred approach. I was hoping this approach could work, as I noticed that the packages were not strictly internal.

However, I appreciate your warning about the unstable interface and its potential for changes without notice. This raises concerns and gives me pause. Let's see if there's a middle ground (see option 5).

  1. Develop the feature from scratch

I would prefer to rule this out as well.


I’d like to propose two additional options based on your feedback.

Option 5: Call Aqua (Preferred)

We have an upcoming PR that introduces support for editorconfig.

What’s notable about this implementation is that while many editorconfig packages are internal, we were able to call their public methods, achieving a near-native integration within Atmos.

What if we did something similar with Aqua?

In this model, if Aqua could expose a few stable APIs that provide command-line-like functionality from within Go, Atmos could call those directly. This way there's not a lot of work to maintain in Aqua, while providing other tooling the ability to leverage aqua. While this may sacrifice full flexibility, it offers the advantage of maintaining a native user experience within Atmos.

As part of this if we could programmatically pass a string containing aqua configuration, we can then work very well with parts of aqua. We could generate that configuration inside of atmos, so the package manafement can feel more integrated with things like stacks, workflows and custom commands.

Option 6: Go-based Aqua Bootstrapper (Less Preferred)

This is a riff on your #2.

If Aqua provided a method to bootstrap itself programmatically via Go, Atmos could invoke that as part of its process. This approach would enable similar functionality, but with a few limitations. Specifically, we’d be constrained to using Aqua’s configuration files without the ability to embed or extend those configurations directly within Atmos.

This limitation concerns me because we’re introducing radical improvements to Atmos configuration handling in an upcoming PR that adds support for remote configurations, local overrides, imports, and more. All of that provides a consistent configuration interface for atmos, including the underlying tooling. I’d like those improvements to be universally applicable across Atmos functionality.

@suzuki-shunsuke
Copy link
Collaborator

suzuki-shunsuke commented Jan 20, 2025

Sorry for late reply.
About the option 5,

  1. We need to add several public API to aqua.
  2. If we make atmos packages available to users directly, we need to develop something like aqua-proxy.
    We can't use aqua-proxy as aqua-proxy executes aqua exec and aqua can't use atmos configuration files.
    Please see How does Lazy Install work? too.
    We need to develop a tool like atmos-proxy and execute a command like atmos exec.
  3. I considered sharing the install directory with aqua, but it may cause trouble because aqua library atmos uses might not be compatible with aqua users use.
    aqua has API to change the install directory, so atmos should change it.
  4. Note that aqua uses logrus as logger.
    We can customize the setting of logrus, but probably the log format is different from the log format of atmos.
  5. About checksum verification, I'm wondering how atmos supports it. This feature is important for security, so I'd like to support this somehow. But if atmos creates a file like aqua-checksums.json, this has trade-off of user experience because users need to manage the file.
  6. About Renovate, we need to develop Renovate Config Preset like aqua-renovate-config. I guess this is not so difficult.

@suzuki-shunsuke
Copy link
Collaborator

This limitation concerns me because we’re introducing radical improvements to Atmos configuration handling in an upcoming PR that adds support for remote configurations, local overrides, imports, and more. All of that provides a consistent configuration interface for atmos, including the underlying tooling. I’d like those improvements to be universally applicable across Atmos functionality.

Probably I don't understand features remote configurations, local overrides, imports, and more completely,
but one idea is that Atmos generates aqua configuration based on those features and executes aqua.
This is like kustomize.

@osterman
Copy link
Member Author

So for a general purpose package manager, aqua is exceptional. For example, I like how we can define packages that install cleanly on a local workstation as well as in CI. The proxy installer is a brilliant feature, almost like lazy loading but for binaries. Never seen that before. I like the fact that only the binaries used are the ones installed, and that they still come from the vetted sources in the aqua configuration.

What we are trying to accomplish with this, as a core capability of atmos, is so that atmos can bootstrap everything required to get off the ground. It could mean also bootstrapping aqua itself.

When we started writing Atmos, for example, we were keen to use vendir. We in fact started with it, but felt overall it was overkill for the problem we were aiming to solve. We ended up using go-getter to implement something simpler and good enough (but not for @hans-d who went for Vendir anyways!)

I feel that this maybe the case here, that if the public APIs of aqua are not yet stable for embedding, and its capabilities are so rich, it may be overkill for the scope of this feature for our purposes.

Maybe all we need is a much simpler subset of functionality? Within atmos, we have a controlled environment. Things like the Lazy Install would be easier implemented in other ways, since each command can declare its dependencies in the schema.

The goal for this feature is to make running as command in atmos automatically install the versions of the tools it needs, without any other configuration of the host system.

This includes

  • helmfile commands, that can install helm and helmfile
  • terraform commands that can install any OpenTofu or terraform binary
  • custom commands that can install any command needed for that command to work
  • workflows like custom commands can declare their dependencies which get installed

For this reason, want to stay focused on finding a way to accomplish this for a near native feel in atmos. @suzuki-shunsuke if you have any more creative ideas for this simpler use-case, I am open for it!

@suzuki-shunsuke
Copy link
Collaborator

If we make atmos packages available to users directly, we need to develop something like aqua-proxy.

I'd like to clarify this point.

custom commands that can install any command needed for that command to work

For instance, when Atmos installs tflint, do you expect that users can execute tflint command directly?
If so, we need to add tflint command in $PATH.
Or do you expect users can execute tflint via Atmos subcommands?
If so, we don't need to add tflint command in $PATH. This means we don't need something like aqua-proxy.

@suzuki-shunsuke
Copy link
Collaborator

suzuki-shunsuke commented Jan 30, 2025

Hmm. Please give me some time to consider this more deeply.
I want to utilize aqua echosystem while integrating it with Atmos well.

For instance, when Atmos installs tflint, do you expect that users can execute tflint command directly?

Probably you would expect this.

@osterman
Copy link
Member Author

For instance, when Atmos installs tflint, do you expect that users can execute tflint command directly?

Tools like aqua, tenv, asdf, and many others follow a similar pattern. We're not trying to replace them, nor do Atmos users have to define their dependencies within Atmos if they prefer other solutions.

But the design goal of Atmos isn’t to be just another cog in an existing tool’s wheel—it’s to be the wheel itself. The goal is to establish a consistent automation environment, backed by configuration.

The Unix philosophy teaches us to "do one thing well," but the reality is that there are too many tools (too many cogs) that do one thing well—and no unified way to tie them together—the wheel.

That’s where Atmos comes in.

If your project depends on tflint (a cog), that's great. Install it with Atmos, then integrate it into your project’s configuration in a consistent, documented manner. No guesswork, no scattered tools—just a streamlined, repeatable workflow.

Developers don’t need to manually configure their shell, install 20 different tools by hand, or wrangle disparate dependencies. Instead, they install Atmos, and it all just works. Your team defines how the tools are used, and Atmos acts as the interface—making it feel as if Atmos itself implements everything.

Only, it doesn’t reinvent the wheel—it’s built on the shoulders of giants.

@osterman
Copy link
Member Author

For instance, when Atmos installs tflint, do you expect that users can execute tflint command directly?
Probably you would expect this.

Actually, no. While we could optimize for that later, our real focus is on using the command within Atmos.

The goal of Atmos is that no one should need to write a shell script just to call Atmos—if they do, we've failed. No one should need to rely on Makefiles, Taskfiles, or other task runner tools—if they do, we've failed.

Atmos itself should be the interface that seamlessly integrates tools and workflows, providing a superior DX, eliminating the need for ad-hoc scripting or additional tooling just to glue things together.

@suzuki-shunsuke
Copy link
Collaborator

To enable users to execute installed tools in workflows, there are several options.

  1. Introduce proxy like aqua-proxy for changing tool versions dynamically
  2. Change environment variable $PATH for changing tool versions dynamically
  3. Install tools in the directory specific to each project

About point 2, this approach is entirely different from aqua, making it difficult to reuse aqua's resources. I’d like to avoid this.

Regarding point 3, it's simple and not necessarily a bad approach. However, we would need to install tools for each project and reinstall them every time tool versions change. This results in wasted time and storage.

@suzuki-shunsuke suzuki-shunsuke self-assigned this Feb 5, 2025
@suzuki-shunsuke
Copy link
Collaborator

suzuki-shunsuke commented Feb 5, 2025

How about this?

  1. Develop atmos-proxy like aqua-proxy https://github.com/suzuki-shunsuke/atmos-proxy
  2. Add the command atmos exec (Or atmos packages exec)
  3. atmos packages install reads atmos.yaml and stack configurations and install tools into $ATMOS_PKG_DIR. The path is configurable. It creates links to atmos-proxy in $ATMOS_PKG_DIR/bin. The mechanism is same with aqua
  4. atmos sub commands adds $ATMOS_PKG_DIR/bin to $PATH before executing commands
  5. When commands managed by atmos are executed, atmos-proxy executes atmos exec -- <command> [<arg> ...]. atmos exec reads configuration files and installs commands before executing them

@suzuki-shunsuke
Copy link
Collaborator

I've created atmos-proxy based on aqua-proxy. https://github.com/suzuki-shunsuke/atmos-proxy
And I'm working on implementing aqua packages install command.
I'm considering a Go package wrapping aqua.

atmos - aquaw (wrapper) -> aqua

The wrapper abstracts aqua.
I'm not sure if it works well, but this abstraction layer may be good for both atmos and aqua.

@osterman
Copy link
Member Author

osterman commented Feb 6, 2025

Develop atmos-proxy like aqua-proxy https://github.com/suzuki-shunsuke/atmos-proxy

  • Not clear how the proxy would handle multiple concurrent versions of some tool installed (e.g. opentofu, helm, helmfile, etc)

Add the command atmos exec (Or atmos packages exec)

  • This makes sense to me as a feature I hadn't considered

atmos packages install reads atmos.yaml and stack configurations and install tools into $ATMOS_PKG_DIR.

  • And supports multiple concurrent versions of tools

The path is configurable. It creates links to atmos-proxy in $ATMOS_PKG_DIR/bin.

  • My understanding is links don't work consistently across OS-boundaries
  • Links on Windows require administrative permissions
  • Windows is a growing demographic of Atmos users, especially in the enterprise

When commands managed by atmos are executed, atmos-proxy executes atmos exec -- <command> [<arg> ...].

  • How do you envision the version selection working?

@osterman
Copy link
Member Author

osterman commented Feb 6, 2025

@suzuki-shunsuke how would atmos-proxy get installed? I could understand if atmos could "cell-divide" so to say, but we don't want additional requirements to use atmos.

@suzuki-shunsuke
Copy link
Collaborator

suzuki-shunsuke commented Feb 6, 2025

Not clear how the proxy would handle multiple concurrent versions of some tool installed (e.g. opentofu, helm, helmfile, etc)

https://aquaproj.github.io/docs/guides/command-alias

aqua supports command aliases for this.

e.g.

packages:
- name: hashicorp/[email protected]
- name: hashicorp/terraform
  version: v0.13.7
  command_aliases:
    - command: terraform
      alias: terraform-013
      # no_link: true

Links on Windows require administrative permissions

aqua uses hard links on Windows.

https://aquaproj.github.io/docs/reference/lazy-install#on-windows

How do you envision the version selection working?

Atmos can define packages in several locations, so we need to define the precedence order.

how would atmos-proxy get installed? I could understand if atmos could "cell-divide" so to say, but we don't want additional requirements to use atmos.

This is same with aqua-proxy.
atmos automatically installs atmos-proxy, so uses don't need to install them themselves.

@osterman
Copy link
Member Author

osterman commented Feb 6, 2025

Links on Windows require administrative permissions
aqua uses hard links on Windows.
https://aquaproj.github.io/docs/reference/lazy-install#on-windows

Without Developer Mode, my understanding is still that Admin is required.

Not clear how the proxy would handle multiple concurrent versions of some tool installed (e.g. opentofu, helm, helmfile, etc)
https://aquaproj.github.io/docs/guides/command-alias
aqua supports command aliases for this.

What I don't like about this is it requires changing the script to change the alias, rather than changing the version, of the commands. On the other hand, within a single script execution, if both versions are required, using the alias approach is good. But optimizing for the marginal case, doesn't seem ideal for me.

atmos automatically installs atmos-proxy, so uses don't need to install them themselves.

That works

@osterman
Copy link
Member Author

osterman commented Feb 6, 2025

@suzuki-shunsuke I neglected to respond to this comment.

This is a good one, because it highlights the strategic differences of what we're optimizing for in atmos vs aqua. As you know, it's hard to optimize for multiple things at the same time.

Aqua for good reason is optimizing for system-wide usage, installing packages so that the user running the commands directly in their terminal get the best experience.

Atmos is optimizing for running the commands are dependencies of workflows, subcommands, and components; they should install automatically, and ideally before starting any commands. We should have a visual cue on the screen (e.g. with a spinner), when dependencies are installed. Like with aqua, we want to lazy-install the commands, but we have the privilege of knowing what commands a workflow, component or subcommand depend on. Therefore, the proxy approach is suboptimal. From a UX perspective, we would prefer the installation to happen prior to running the workflows. For example, if there's any problem installing dependencies, we would rather know before we're halfway through the workflow. We want to ensure commands just work, and simplify upgrades.

To enable users to execute installed tools in workflows, there are several options.

  1. Introduce proxy like aqua-proxy for changing tool versions dynamically

Overall, I like this as a shim, automatically installed by atmos, as needed — enabling direct execution of the commands installed; however, it's secondary to first-class support within subcommands, workflows and components. It's a "bonus", rather than the primary interface.

  1. Change environment variable $PATH for changing tool versions dynamically

This is IMO the preferred route, because it allows the workflows, commands, and components to operate consistently, merely calling the intended command (e.g. helm or terraform, ecspresso, etc) without leveraging aliases. I see aliases as a good workaround for when within a single execution shell, both are needed; but this is an edge case.

  1. Install tools in the directory specific to each project

About point 2, this approach is entirely different from aqua, making it difficult to reuse aqua's resources. I’d like to avoid this.

I'm more concerned with the experience inside of one project working well, less concerned with the duplication of binaries (conservation of storage).

If we think about terraform providers, they are downloaded in each project. A provider cache directory can be specified with terraform; we could do something similar down the road. Alternatively, so long as binaries are stored in programmatically deterministic locations, multiple projects could update the same target directory and reuse binaries. Paths would need to take into account the source.

Regarding point 3, it's simple and not necessarily a bad approach. However, we would need to install tools for each project and reinstall them every time tool versions change. This results in wasted time and storage.

Just to reiterate, main priority is to get it working within a project. If we have a strategy for how that could work across projects, that can be implemented later.

@suzuki-shunsuke
Copy link
Collaborator

Without Developer Mode, my understanding is still that Admin is required.

Oh, really? I'll take a look.

we would prefer the installation to happen prior to running the workflows

I see. Atmos workflows define required packages and their versions, so we can achieve this.
Even if lazy install is disabled, proxy still has meaning to change versions dynamically.

On the other hand, within a single script execution, if both versions are required, using the alias approach is good.

Yes. Command alias is a solution for this.
If we need a single version in a context, aliases aren't necessary.
proxy can select a version from multiple versions according to the priority. This is same with aqua.

@osterman
Copy link
Member Author

osterman commented Feb 6, 2025

One more idea, symlinks can be an optional optimization, if it causes problems.

@suzuki-shunsuke
Copy link
Collaborator

suzuki-shunsuke commented Feb 7, 2025

Approach 2

This is the detail of the option 3 I mentioned before.

Install tools in the directory specific to each project

Directory structure:

PROJECT_ROOT_DIR/
  atmos.yaml
  .atmos/
    versions.json # package versions atmos copied to bin directory
    bin/
      <command>
      ...
  ...
~/.local/share/atmos/ (ATMOS_PKG_DIR)
  pkgs/ # Install tools (same with aqua)

atmos sub commands such as atmos packages install do the following things:

  1. Install packages in ATMOS_ROOT_DIR
  2. Copy commands to PROJECT_ROOT_DIR/.atmos/bin
  3. Sets package versions to PROJECT_ROOT_DIR/.atmos/versions.json
  4. Add PROJECT_ROOT_DIR/.atmos/bin to the environment variable $PATH

If commands don't exist in PROJECT_ROOT_DIR/.atmos/bin or versions.json are different from versions defined in atmos configuration files, atmos installs packages in ATMOS_ROOT_DIR and copy commands to PROJECT_ROOT_DIR/.atmos/bin.

Why is ATMOS_PKG_DIR required?

  1. Cache
  2. aqua's API restriction. aqua provides install API but it installs tools in a given directory, and we can't change the directory structure. We want to avoid re-inventing wheel, so we want to utilize this API

We can remove files from ATMOS_PKG_DIR after coping commands to PROJECT_ROOT_DIR/.atmos/bin if we want. This saves storage, but cache doesn't work.

Pros.

  1. This approach doesn't need proxy. It's simple and we can avoid issues related to proxy.
  2. This approach doesn't need links. We can avoid links specific issues (Especially on Windows).

Cons.

  1. This approach needs to install commands in project directories. Some people don't like it.
  2. Single binaries work well, but some tools don't work. For instance, tfenv doesn't work

@osterman
Copy link
Member Author

osterman commented Feb 7, 2025

Based on this, I'm concerned we haven't captured the requirement that within a project, multiple tool versions must be supported. Different versions of terraform components can require different versions of terraform/opentofu.

PROJECT_ROOT_DIR/
  atmos.yaml
  .atmos/
    versions.json # package versions atmos copied to bin directory
    bin/
      <command>
      ...

At a minimum, this would need to be:

  atmos.yaml
  .atmos/
    versions.json # package versions atmos copied to bin directory
    bin/
      <command = terraform>
        <version = 1.5.7>
          <executable = terraform>
      ...

Regarding ATMOS_PKG_DIR, I don't yet have a technical opinion on it.

@osterman
Copy link
Member Author

osterman commented Feb 7, 2025

Warning

⚠ Draft Proposal (still working on it)

I will remove this 👆 when proposal finished.

General Requirements

Must:

  • Install packages on-demand when workflows, subcommands or components need them
  • Installation happens before beginning execution of the workflow, subcommands, or components; we want to fail-fast
  • Support global tool versions that are used as defaults
  • Support a command to clean unused versions of tools

Nice to have:

  • Packages can define other packages they need (e.g. helmfile needs helm)
  • Ability to run commands conveniently outside of atmos
  • Support .tool-versions as an alternative way to specify default versions for a project
  • Compatibility with aqua
  • Some way to update the package versions automatically (how to initialize versions? or update to "latest")
  • Lockfile with versions installed
  • Signature validation (when tools support it)

1️⃣ Global Tool Manifest (atmos.yaml)

Defines all available versions and sets a default version per tool.

Example: atmos.yaml

packages:
  base-path: ./packages
  bin-path: ./packages/bin

  # Feature flag for symlink-based management
  use-symlinks: false

  registry:
    # defines where to find packages, with support for multype types of registries
    # Initially, we would support aqua
    - name: aqua-registry
      type: aqua
      # The !include is an atmos function
      settings: !include suzuki-aqua-registry.yaml
      

  # defines packages to install
  install:
    terraform:
      default: "1.5.7"  # Default version
      registry: aqua-registry
      versions:
        - "1.3.7"
        - "1.5.7"
    helm:
      default: "3.10.0"
      registry: aqua-registry
      versions:
        - "3.10.0"
        - "3.12.1"
    helmfile:
      default: "0.144.0"
      registry: aqua-registry
      needs:
        helm: 3.1.4
      versions:
        - "0.144.0"
        - "0.145.0"

Requirements:

  • Explicit versions only (no SemVer ranges, at this time).
  • The default version is used if no overrides are present.

2️⃣ Per-Stack and Per-Component Version Overrides

Each stack/component can specify which version to use. Only packages configured in the registry can be installed (otherwise, there's no way to find them)

Example: stacks/dev.yaml

components:
  terraform:
    vpc:
      needs:
        opentofu: "1.7.0"
          
    eks:
      needs:
        terraform: "1.5.7"
        helm: "3.12.1"

Requirements:

  • Overrides atmos.yaml defaults per stack/component.
  • Only explicit versions allowed.

3️⃣ Support .tool-versions (Project-Level Default)

Follows standard asdf format and only allows explicit versions.

Example: .tool-versions

opentofu 1.7.0
helm 3.12.1

Requirements:

  • Overrides atmos.yaml default versions.
  • File is only valid at the repository root (ATMOS_BASE_DIR)

4️⃣ Atmos Commands for Managing Versions

Install All Tools

Installs all tools and versions from atmos.yaml and what is defined in stacks, subcommands, and workflows.

atmos packages install

Update Installed Versions

Updates tools to match atmos.yaml definitions.

atmos packages update

Execute Commands with a Specific Tool Version

Runs Terraform 1.5.7, ignoring other defaults. If terraform not installed, it will error.

$ atmos exec --use [email protected] terraform version
Terraform v1.5.7
on darwin_arm64

If it's not installed, it would error:

$ atmos exec --use [email protected] terraform version
Package is not installed. Please use the `--install` flag or run `atmos packages install`.

Or if it's not configured:

$ atmos exec --use [email protected] terraform version
Package is not configured. Please configure it first.

Runs Terraform 1.5.7, ignoring other defaults. If terraform not installed, it will install it. A spinner conveys the installation process.

atmos exec --install --use [email protected] terraform version
⣻ Installing [email protected]
Terraform v1.5.7
on darwin_arm64

5️⃣ Directory Structure

🚀 Default: Duplicated Binaries (use-symlinks: false)

Binaries are copied to packages/bin/ (packages.base-path in the atmos.yaml) for the default version.

PROJECT_ROOT/
  atmos.yaml
  packages/
    bin/
      terraform   # Default Terraform version (copied)
      helm        # Default Helm version (copied)
    terraform/
      1.3.7/
        terraform
      1.5.7/
        terraform
    helm/
      3.10.0/
        helm
      3.12.1/
        helm

🚀 Alternative: Symlink-Based Management (use-symlinks: true) for Defaults

Symlinks are created inside packages/bin/ instead of duplicating binaries.

PROJECT_ROOT/
  packages/
    bin/
      terraform -> ../terraform/1.5.7/terraform  # Symlink to default version
      helm -> ../helm/3.10.0/helm               # Symlink to default version
    terraform/
      1.3.7/
        terraform
      1.5.7/
        terraform
    helm/
      3.10.0/
        helm
      3.12.1/
        helm

6️⃣ Dynamic $PATH Management

Since no wrapper scripts are used, $PATH dynamically ensures correct versions.

# Output the PATH for default tool versions
atmos path

# Set up the path for a particular stack and vpc
# Really only useful for debugging
atmos path --stack ue2-dev --component vpc

A command that outputs the correct $PATH based on:

  1. Explicitly requested versions (atmos exec --use)
  2. Per-stack/component versions (stacks/*.yaml)
  3. Project-wide .tool-versions (if present)
  4. Global default (atmos.yaml)

Example Usage:

Emit Correct $PATH for Active Tool Versions

Set the PATH to use the default tool versions for the project. This is not a primary use-case for Atmos.

$ export PATH=$(atmos path):$PATH
$ terraform version
Terraform v1.5.7
on darwin_arm64

Example Output

$ atmos path
/your/project/packages/bin:/your/project/packages/terraform/1.5.7:/your/project/packages/helm/3.12.1:/usr/local/bin:/usr/bin
  • Ensures Terraform 1.5.7 and Helm 3.12.1 are used first.
  • No need to modify scripts.

The Atmos Proxy provides a way to use binaries managed by Atmos outside of Atmos itself. This allows developers and CI/CD environments to run terraform, helm, helmfile, and other tools without manually configuring $PATH or invoking atmos exec.

Use Case

  • Allows users to run managed tools directly (terraform, helm, etc.) without needing atmos exec or export PATH=$(atmos path).
  • Ensures consistency across CLI usage inside and outside Atmos.
  • Reduces friction when using Atmos-managed tools in scripts, CI/CD, or interactive terminals.

7️⃣ How Packages Works with Atmos Workflows

Atmos workflows should automatically install and configure the correct tool versions without requiring atmos exec --use. Instead, workflows can declare required tool versions directly within the packages.install block, ensuring that the necessary tools are available before execution.

Example: Defining an Atmos Workflow with Automatic Package Installation

workflows:
  deploy-infra:
    description: "Deploy Terraform infrastructure"
    needs:
      terraform: 1.5.7
      helm: 3.1.2
    steps:
      - command: terraform init
      - command: terraform plan
      - command: terraform apply -auto-approve

8️⃣ How It Works with Atmos Custom Commands

# atmos.yaml
commands:
  plan:
    description: "Run Terraform plan"
    needs:
      terraform: 1.5.7
    steps:
      - command: terraform init
      - command: terraform plan

9️⃣ The Atmos Proxy (Optional)

The Atmos Proxy is inspired by the aqua proxy. It's not the primary use-case of packages for Atmos, but is available for users to use packages installed by atmos conveniently in their shell or shell-scripts.

How the Atmos Proxy Works

When the proxy is enabled:

  1. The proxy command (e.g. atmos-proxy is in the user's PATH)
  2. The proxy calls atmos exec which automatically installs proxy binaries (e.g. lazy loads) in a designated path (e.g., ~/.atmos/bin/ where where ever packages.base_path is set to), when not installed, otherwise calls the binary
  3. Any command (e.g., terraform) is intercepted by the proxy.
  4. The proxy only handles default versions of tools for the project
  5. The proxy forwards the command to the correct binary managed by Atmos, calling atmos exec.

Enabling the Atmos Proxy

To enable the proxy, users can define it in atmos.yaml:

packages:
  proxy:
    enabled: true

Then, to install the thin wrapper, would run:

atmos packages --install-proxy

Final Summary

✅ Explicit versions only—no SemVer ranges.
atmos.yaml defines available and default tool versions.
✅ Per-stack/component/workflow/subcommand settings override defaults.
.tool-versions provides an industry-standard alternative.
✅ Symlink support is optional (packages.use-symlinks: true | false).
✅ No wrapper scripts—relies purely on $PATH resolution
atmos path dynamically sets the correct environment (for scripts that call atmos)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants