# How the Main Entry Point of apple/container Directs Command Execution

> Discover how apple/container's Swift @main entry point directs command execution via Application.main and ArgumentParser, enabling dynamic plugin loading for flexible command handling.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: internals
- Published: 2026-07-06

---

**The `apple/container` CLI forwards execution from the Swift `@main` entry point to `Application.main()`, which uses Swift ArgumentParser to convert command-line arguments into concrete command types and executes their `run()` methods while supporting dynamic plugin loading for unrecognized commands.**

The `apple/container` repository provides a container platform for macOS built in Swift. Understanding how its main entry point directs command execution reveals a clean separation between bootstrapping logic and command implementation, leveraging type-safe argument parsing and plugin extension points.

## The `@main` Entry Point in ContainerCLI.swift

The binary entry point resides in [[`Sources/CLI/ContainerCLI.swift`](https://github.com/apple/container/blob/main/Sources/CLI/ContainerCLI.swift)](https://github.com/apple/container/blob/main/Sources/CLI/ContainerCLI.swift). The `ContainerCLI` struct is decorated with Swift’s **`@main`** attribute, making it the program’s entry point.

```swift
@main
public struct ContainerCLI: AsyncParsableCommand {
    public static func main() async throws {
        try await Application.main()
    }
}

```

When the binary launches, the Swift runtime invokes `ContainerCLI.main()`. This method immediately forwards to **`Application.main()`**, delegating all argument parsing and command dispatch to the core application logic.

## Application.main(): Parsing and Routing Commands

The real command dispatcher lives in [[`Sources/ContainerCommands/Application.swift`](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Application.swift)](https://github.com/apple/container/blob/main/Sources/ContainerCommands/Application.swift). The `main()` method there handles the complete execution flow:

```swift
public static func main() async throws {
    restoreCursorAtExit()
    let fullArgs = CommandLine.arguments
    let args = Array(fullArgs.dropFirst())
    var command = try Application.parseAsRoot(args)
    if var asyncCommand = command as? AsyncParsableCommand {
        try await asyncCommand.run()
    } else {
        try command.run()
    }
}

```

The process follows three distinct phases:

- **Argument capture** – The method reads `CommandLine.arguments` and drops the executable name to isolate user input.
- **Type resolution** – **`Application.parseAsRoot(args)`** (provided by **Swift ArgumentParser**) inspects the first argument and returns a concrete command instance (e.g., `container run` instantiates `ContainerRun`).
- **Execution dispatch** – The code checks if the command conforms to **`AsyncParsableCommand`**. If so, it `await`s the asynchronous `run()`; otherwise, it invokes the synchronous `run()` directly.

## Command Configuration and Subcommand Hierarchy

The `Application` struct defines a static **`CommandConfiguration`** that maps CLI inputs to Swift types. This configuration organizes commands into logical groups and specifies metadata:

```swift
public static let configuration = CommandConfiguration(
    commandName: "container",
    abstract: "A container platform for macOS",
    version: ReleaseVersion.singleLine(appName: "container CLI"),
    subcommands: [DefaultCommand.self, HelpCommand.self],
    groupedSubcommands: [
        CommandGroup(name: "Container", subcommands: [
            ContainerCopy.self, ContainerCreate.self, ContainerPrune.self
        ]),
        CommandGroup(name: "Image", subcommands: [
            BuildCommand.self, ImageCommand.self, RegistryCommand.self
        ])
    ],
    defaultSubcommand: DefaultCommand.self
)

```

**ArgumentParser** consumes this configuration to validate input and route to the appropriate subcommand. The `groupedSubcommands` array nests related operations (like `Container` or `Image` commands) under named headers in the help output.

## Plugin Discovery and Dynamic Command Loading

When users invoke an unrecognized subcommand, the CLI attempts to load external plugins before failing. The `Application` calls **`createPluginLoader()`** to search three specific locations:

1. User-specific plugins directory
2. Application bundle resources
3. System install root at `libexec/container/plugins`

The loader instantiates any factories implementing the **`PluginFactory`** protocol and registers their commands with the parser. If a plugin provides the requested command, the CLI re-executes the parse-and-run cycle with the newly available command type.

## Error Handling and Help Generation

The entry point provides unified handling for help requests and runtime errors. When users pass `--help` or `-h`, **ArgumentParser** raises an error before command instantiation. `Application.main()` catches this, detects the help flag, and prints dynamically-generated help text that includes discovered plugins.

All other errors funnel through **`Application.exit(withError:)`**, ensuring consistent exit codes and formatted error messages regardless of which subcommand failed.

## Practical Command Execution Examples

The following examples exercise the dispatch flow described above:

```bash

# Execute a synchronous container run command

container run --name hello alpine echo "Hello, world!"

# Execute an asynchronous command inside a running container

container exec my-container -- bash -c "ls /usr/bin"

# Build an image (routed to the Image command group)

container build -t myimage ./path/to/Dockerfile

# Invoke a plugin-provided command after installation

container my-plugin-command --option value

```

In each case, the CLI strips the first argument, matches it against the `CommandConfiguration` registry, instantiates the corresponding Swift type, and invokes its `run()` method.

## Summary

- **ContainerCLI.swift** provides the **`@main`** entry point that immediately delegates to `Application.main()`.
- **Application.main()`** parses arguments via Swift ArgumentParser, routes to concrete command types, and handles both synchronous and asynchronous execution paths.
- The **`CommandConfiguration`** defines a hierarchical command structure with grouped subcommands for containers, images, machines, and volumes.
- Unknown commands trigger **plugin discovery** across three directory locations, enabling extensibility without recompilation.
- **Unified error handling** ensures consistent help output and exit codes for both built-in and plugin-provided commands.

## Frequently Asked Questions

### What is the difference between `ContainerCLI` and `Application` in the entry point?

`ContainerCLI` is a thin `@main` struct that exists solely to satisfy Swift’s program entry point requirement. It immediately forwards to `Application.main()`, which contains the actual argument parsing logic, command routing, and plugin loading. This separation keeps the CLI module clean while concentrating execution logic in the `ContainerCommands` module.

### How does the entry point handle asynchronous vs. synchronous commands?

After parsing arguments, `Application.main()` checks if the resulting command conforms to `AsyncParsableCommand`. If it does, the method `await`s the command's `run()` method; otherwise, it calls the synchronous `run()` directly. This allows the CLI to support both blocking and async/await patterns without the caller needing to specify the execution model.

### Where does the container CLI search for plugins?

The plugin loader searches three locations: user-specific plugin directories, the application bundle’s resources, and the system install root at `libexec/container/plugins`. Any factory classes implementing the `PluginFactory` protocol found in these locations are instantiated and registered, making their commands available for subsequent parsing.

### How does the main entry point handle help requests and invalid input?

When users pass `--help` or `-h`, ArgumentParser throws a help-specific error before any subcommand is instantiated. `Application.main()` catches this error, detects the help flag, and prints usage information that dynamically includes any discovered plugins. For invalid commands that cannot be resolved to a type (and have no matching plugin), the error propagates through `Application.exit(withError:)` with a non-zero exit code.