# What Is the Role of runner.go in the Aqua Project?

> Discover the role of runner.go in the Aqua project. Learn how it manages CLI execution, sets up tool environments, and handles installations for seamless command invocation.

- Repository: [aquaproj/aqua](https://github.com/aquaproj/aqua)
- Tags: internals
- Published: 2026-02-25

---

**The [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) file in [`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go) implements the core execution logic for Aqua's CLI, bridging shell command invocation with version-managed tool environments by handling temporary PATH setup, lazy installation, and process spawning.**

The [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) component serves as the execution engine within the `aquaproj/aqua` repository, enabling the `aqua exec` command functionality. It transforms standard command-line invocations into reproducible executions by ensuring specified tool versions are installed and isolated in a temporary environment before the target process launches.

## Core Responsibilities of runner.go

Located in **[`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go)**, this file implements the **“run” sub-command** logic that powers Aqua's command execution capabilities. While [`pkg/cli/exec.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/exec.go) serves as the CLI entry point for argument parsing, the runner handles the actual process lifecycle management and environment orchestration.

### Command Execution and Process Spawning

The runner manages **command execution** by parsing user arguments and spawning child processes after verifying all prerequisites. When a user executes `aqua exec go -- version`, the runner extracts the tool name and arguments, then launches the process with the correct binary.

It preserves **exit code propagation**, ensuring that the status codes from executed commands return unchanged to the parent shell. This fidelity is critical for CI/CD pipelines and shell scripts that depend on accurate failure detection from underlying tools.

### Environment Preparation and PATH Management

A primary function of [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) is **environment preparation**. The component constructs a temporary `PATH` variable that prioritizes Aqua-managed binary directories over system-wide installations. This isolation prevents version conflicts and eliminates the need for global PATH modifications.

The runner injects these environment variables into the child process context, creating a sandboxed execution environment where the specified tool versions take precedence without affecting the host system configuration.

### Dependency Resolution and Lazy Installation

The runner performs **dependency resolution** by analyzing command arguments to identify required tools. When it detects a missing or outdated binary, it triggers the installation controller in **[`pkg/controller/install.go`](https://github.com/aquaproj/aqua/blob/main/pkg/controller/install.go)** to fetch the correct version on-the-fly.

This lazy installation mechanism consults **[`pkg/config/config.go`](https://github.com/aquaproj/aqua/blob/main/pkg/config/config.go)** to parse the [`aqua.yml`](https://github.com/aquaproj/aqua/blob/main/aqua.yml) configuration, supporting version-specific overrides via flags such as `--file`. Users avoid manual installation steps, as the runner resolves and installs dependencies transparently before execution.

### Telemetry and Error Handling

Beyond execution, [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) implements **structured logging** and telemetry collection. When enabled, it emits usage metrics about tool execution frequency and performance. The runner captures execution errors while preserving visibility into underlying tool output, creating a transparent debugging experience for developers.

## Practical Usage Examples

The following patterns demonstrate how [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) functions in real-world scenarios:

```bash

# Running a tool that Aqua installs on-the-fly

# runner.go parses the "go" argument, ensures the version is present,

# then executes "go version" with the Aqua-managed binary on PATH

aqua exec go -- go version

```

```bash

# Using a specific configuration file for version pinning

# runner.go reads aqua.yml, resolves the Terraform version,

# installs it if missing, and launches the command

aqua exec --file aqua.yml terraform -- version

```

```bash

# CI pipeline integration with Node.js

# The runner guarantees the exact Node version defined in config is used,

# then runs npm install with that isolated binary

aqua exec node -- npm install

```

## Key Integration Points

The runner operates within an architecture that includes several critical components:

- **[`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go)**: Contains the primary execution logic, environment setup, and process management.
- **[`pkg/controller/install.go`](https://github.com/aquaproj/aqua/blob/main/pkg/controller/install.go)**: Performs binary downloads and installations when the runner detects missing tools.
- **[`pkg/cli/exec.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/exec.go)**: Serves as the CLI entry point that wires the `exec` sub-command to the runner implementation.
- **[`pkg/config/config.go`](https://github.com/aquaproj/aqua/blob/main/pkg/config/config.go)**: Loads [`aqua.yml`](https://github.com/aquaproj/aqua/blob/main/aqua.yml) configurations that inform the runner's version resolution decisions.

Together, these files enable Aqua to function as a **runtime-aware version manager**, executing commands with precise version control without permanent system modification.

## Summary

- **[`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go)** implements the execution logic for Aqua's run command, located at [`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go).
- It constructs temporary **PATH environments** to isolate tool versions from the global system.
- The component triggers **lazy installation** via [`pkg/controller/install.go`](https://github.com/aquaproj/aqua/blob/main/pkg/controller/install.go) when required tools are absent.
- It **propagates exit codes** from child processes to maintain compatibility with scripts and CI systems.
- The runner supports **configuration-driven execution** by parsing [`aqua.yml`](https://github.com/aquaproj/aqua/blob/main/aqua.yml) through [`pkg/config/config.go`](https://github.com/aquaproj/aqua/blob/main/pkg/config/config.go).

## Frequently Asked Questions

### Where is runner.go located in the Aqua repository?

The [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) file resides in **[`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go)** within the `aquaproj/aqua` repository. This location places it in the CLI package alongside other command implementations, specifically serving as the backend execution engine while [`pkg/cli/exec.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/exec.go) handles the command-line interface wiring.

### How does runner.go handle missing tool versions?

When [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) detects that a required tool is not installed, it invokes the installation controller from **[`pkg/controller/install.go`](https://github.com/aquaproj/aqua/blob/main/pkg/controller/install.go)** to download and install the specific version defined in the [`aqua.yml`](https://github.com/aquaproj/aqua/blob/main/aqua.yml) configuration. This process occurs transparently before command execution, enabling on-demand tool management without separate installation steps.

### Does runner.go modify the system PATH permanently?

No, [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) creates a temporary PATH environment variable specifically for the child process it spawns. It prepends Aqua's managed binary directories to this temporary PATH, ensuring the correct tool versions are found during execution without altering the user's global shell environment or system configuration.

### What is the relationship between runner.go and the exec sub-command?

The [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) file implements the core "run" logic that powers the **`exec` sub-command**. The CLI entry point in **[`pkg/cli/exec.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/exec.go)** wires the `exec` command to the runner's methods, delegating process execution and environment setup to [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) while handling initial flag parsing and validation.