How the Aqua CLI Command Structure Is Organized
The aqua CLI command structure relies on the urfave/cli v3 framework, with a central Runner in pkg/cli/runner.go that assembles individual commands from dedicated packages under pkg/cli/, each exposing a New constructor returning a *cli.Command and receiving shared runtime parameters via util.Param and global arguments via cliargs.GlobalArgs.
The aquaproj/aqua repository implements a highly modular aqua CLI command structure designed for scalability and testing. By delegating each subcommand to its own package and standardizing the constructor pattern, the codebase enables developers to add or modify CLI functionality without affecting unrelated components.
Central Command Assembly in pkg/cli/runner.go
The entry point for all CLI operations resides in pkg/cli/runner.go, specifically within the Run function. This function acts as the composition root, instantiating shared dependencies before delegating to individual command packages.
First, Run creates a util.Param struct containing runtime essentials:
// pkg/cli/runner.go
param := &util.Param{
Stdin: env.Stdin,
Stdout: env.Stdout,
Stderr: env.Stderr,
Logger: logger,
Runtime: runtime.New(),
Version: env.Version,
}
It then initializes cliargs.GlobalArgs to capture flags available across all commands. These dependencies are passed to a commands() helper that invokes each package's New constructor:
return urfave.Command(env, &cli.Command{
Name: "aqua",
Usage: "Version Manager of CLI. https://aquaproj.github.io/",
Flags: cliargs.GlobalFlags(globalArgs),
Commands: commands(
param,
globalArgs,
initcmd.New,
install.New,
generate.New,
updateaqua.New,
upc.New,
update.New,
which.New,
info.New,
remove.New,
vacuum.New,
token.New,
cp.New,
cpolicy.New,
cpolicy.NewInitPolicy,
exec.New,
list.New,
genr.New,
root.New,
),
}).Run(ctx, env.Args)
This centralized registration pattern ensures the aqua CLI command structure remains explicit and discoverable, with all top-level commands enumerated in a single location.
Modular Command Packages Under pkg/cli/
Each subcommand lives in its own package beneath pkg/cli/, following a strict naming convention. The commands() function in runner.go accepts constructor functions that return *cli.Command instances. The current modular structure includes:
init(pkg/cli/initcmd): Initializes a newaqua.yamlconfigurationinstall(pkg/cli/install): Downloads tools and creates symlinksgenerate(pkg/cli/generate): Generates shell completion scriptsupdateaqua(pkg/cli/updateaqua): Self-updates the aqua binaryupc(pkg/cli/upc): Updates aqua configuration file(s)update(pkg/cli/update): Updates package versions and registrieswhich(pkg/cli/which): Displays the installed path of a commandinfo(pkg/cli/info): Prints detailed package informationremove(pkg/cli/remove): Removes installed toolsvacuum(pkg/cli/vacuum): Cleans up unused filestoken(pkg/cli/token): Manages GitHub token storagecp(pkg/cli/cp): Copies files from a tool's install directorypolicy(pkg/cli/cpolicy): Manages security policies with constructorsNewandNewInitPolicyexec(pkg/cli/exec): Executes commands within the tool-specific environmentlist(pkg/cli/list): Lists installed toolsgenr(pkg/cli/genr): Generates a JSON schema foraqua.yamlroot-dir(pkg/cli/root): Prints the Aqua root directory (AQUA_ROOT_DIR)
This package-per-command approach isolates business logic and allows independent testing of each CLI surface.
Standard Command Implementation Pattern
Every command package follows an identical four-step implementation pattern to maintain consistency across the aqua CLI command structure:
- Define an
Argsstruct embedding*cliargs.GlobalArgsto inherit global flags - Define a
commandstruct holding*util.Paramfor runtime access - Implement
New(r *util.Param, globalArgs *cliargs.GlobalArgs) *cli.Commandregistering flags, usage text, and the action function - Implement an
actionmethod handling profiling setup, parameter conversion viautil.SetParam, and controller invocation
The install command exemplifies this pattern in pkg/cli/install/command.go:
func New(r *util.Param, globalArgs *cliargs.GlobalArgs) *cli.Command {
args := &Args{GlobalArgs: globalArgs}
i := &command{r: r}
return &cli.Command{
Name: "install",
Aliases: []string{"i"},
Usage: "Install tools",
Action: func(ctx context.Context, _ *cli.Command) error {
return i.action(ctx, args)
},
Flags: []cli.Flag{ /* bool/string flags */ },
}
}
The action method typically initializes a controller from the pkg/controller layer and executes core logic, keeping the CLI adapter thin and focused on argument parsing and I/O handling.
Shared Infrastructure Across Commands
Two critical abstractions enable dependency injection throughout the aqua CLI command structure:
util.Param(defined inpkg/cli/util/param.go): A shared runtime context containingStdin,Stdout,Stderr,Logger,Runtime, andVersion. Every command receives this via itsNewconstructor, ensuring consistent I/O and logging without global state.cliargs.GlobalArgs(defined inpkg/cli/cliargs/global.go): A struct capturing flags applicable to all commands, such as configuration file paths or log levels. TheArgsstruct in each command embeds this to automatically inherit global flag parsing.
This design eliminates tight coupling between commands and the environment, facilitating unit testing through mock util.Param injection.
Key Files Defining the Command Structure
| Path | Responsibility |
|---|---|
pkg/cli/runner.go |
Central assembly point and entry point for the CLI; contains the Run function and command registration |
pkg/cli/<command>/command.go |
Individual command implementations (e.g., pkg/cli/install/command.go) |
pkg/cli/cliargs/global.go |
Definition of global flags and GlobalArgs struct |
pkg/cli/util/param.go |
Shared runtime parameters passed to every command constructor |
pkg/controller/* |
Business logic layer invoked by command action methods |
cmd/aqua/main.go |
Minimal wrapper that calls cli.Run with the environment |
Summary
- The aqua CLI command structure uses urfave/cli v3 as its foundational framework.
- Centralized registration occurs in
pkg/cli/runner.go, where theRunfunction assembles all commands via acommands()helper. - Modular packages under
pkg/cli/isolate each command, with directories likeinstall/,update/, andinfo/each exporting aNewconstructor. - Standardized patterns include an
Argsstruct embeddingGlobalArgs, acommandstruct holdingutil.Param, and anactionmethod delegating to controllers. - Shared infrastructure (
util.Paramandcliargs.GlobalArgs) provides consistent runtime context and global flag handling across all subcommands.
Frequently Asked Questions
What framework does the aqua CLI use for its command structure?
The aqua CLI is built on urfave/cli v3, a popular Go framework for building command-line interfaces. This framework provides the cli.Command struct and flag-parsing primitives that the aqua CLI command structure extends through its modular package organization.
How can I add a new command to the aqua CLI?
To add a new command, create a package under pkg/cli/<name>/ containing a command.go file. Implement the standard pattern: define an Args struct embedding *cliargs.GlobalArgs, a command struct with *util.Param, and a New function returning *cli.Command. Finally, add your package's New function to the commands() slice in pkg/cli/runner.go and rebuild the binary.
What is the purpose of util.Param in the command structure?
util.Param (located in pkg/cli/util/param.go) serves as a dependency injection container for runtime resources including standard I/O streams, the structured logger, runtime information, and version metadata. Every command receives this parameter through its constructor, enabling isolated testing and consistent resource access without global variables.
How does the aqua CLI handle global flags across all subcommands?
Global flags are defined in cliargs.GlobalArgs (within pkg/cli/cliargs/global.go) and exposed via cliargs.GlobalFlags(). Each command's local Args struct embeds *cliargs.GlobalArgs, automatically inheriting these flags. The New constructor in each package binds these arguments to the urfave command definition, ensuring uniform flag availability throughout the aqua CLI command structure.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →